Add Blazor Stocks PWA
|
@ -0,0 +1,350 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29521.150
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorFinancePortfolio.Server", "Server\BlazorFinancePortfolio.Server.csproj", "{98239239-B3F1-48CE-870F-AF18D5667417}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorFinancePortfolio.Client", "Client\BlazorFinancePortfolio.Client.csproj", "{38F39FB3-AA4B-4D48-B07B-CEC28603B931}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorFinancePortfolio.Shared", "Shared\BlazorFinancePortfolio.Shared.csproj", "{6EC3914B-B550-4BA6-A22E-12D8BC5A9624}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{98239239-B3F1-48CE-870F-AF18D5667417}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{98239239-B3F1-48CE-870F-AF18D5667417}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{98239239-B3F1-48CE-870F-AF18D5667417}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{98239239-B3F1-48CE-870F-AF18D5667417}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{38F39FB3-AA4B-4D48-B07B-CEC28603B931}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{38F39FB3-AA4B-4D48-B07B-CEC28603B931}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{38F39FB3-AA4B-4D48-B07B-CEC28603B931}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{38F39FB3-AA4B-4D48-B07B-CEC28603B931}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6EC3914B-B550-4BA6-A22E-12D8BC5A9624}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6EC3914B-B550-4BA6-A22E-12D8BC5A9624}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6EC3914B-B550-4BA6-A22E-12D8BC5A9624}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6EC3914B-B550-4BA6-A22E-12D8BC5A9624}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {CE15B802-C89C-483C-96AB-8E3F2F626994}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,10 @@
|
|||
<Router AppAssembly="@typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
|
||||
</Found>
|
||||
<NotFound>
|
||||
<LayoutView Layout="@typeof(MainLayout)">
|
||||
<p>Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
|
@ -0,0 +1,46 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<BlazorLinkOnBuild>false</BlazorLinkOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ManifestShortName>My Stocks</ManifestShortName>
|
||||
<ManifestLongName>My Blazor Stocks PWA</ManifestLongName>
|
||||
<ServiceWorkerForce>true</ServiceWorkerForce>
|
||||
<ServiceWorkerCacheVersion>21</ServiceWorkerCacheVersion>
|
||||
<ServiceWorkerRegisterInstallableType>installable-blazor</ServiceWorkerRegisterInstallableType>
|
||||
<ServiceWorkerBlazorAssembly>BlazorFinancePortfolio.Client</ServiceWorkerBlazorAssembly>
|
||||
<ServiceWorkerBlazorInstallMethod>InstallPwaPrompt</ServiceWorkerBlazorInstallMethod>
|
||||
<ServiceWorkerBaseUrl>/blazor-stocks-portfolio/</ServiceWorkerBaseUrl>
|
||||
<ManifestBaseUrl>/blazor-stocks-portfolio/</ManifestBaseUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="CopyStyles" AfterTargets="BeforeCompile">
|
||||
<Copy SourceFiles="$(MSBuildProjectDirectory)\Styles\styles.min.css" DestinationFolder="$(MSBuildProjectDirectory)\wwwroot\css" />
|
||||
<Message Importance="high" Text="Copying the minified builld styles to WWWROOT" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Styles\styles.scss" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BlazorPWA.MSBuild" Version="1.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="BuildWebCompiler" Version="1.12.405" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Blazor" Version="3.1.0-preview4.19579.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Blazor.Build" Version="3.1.0-preview4.19579.2" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Blazor.HttpClient" Version="3.1.0-preview4.19579.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Blazor.DevServer" Version="3.1.0-preview4.19579.2" PrivateAssets="all" />
|
||||
<PackageReference Include="Telerik.UI.for.Blazor.Trial" Version="2.6.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shared\BlazorFinancePortfolio.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,17 @@
|
|||
<div class="k-button-group">
|
||||
<NavLink href=""
|
||||
Match="@NavLinkMatch.All"
|
||||
ActiveClass="@ActiveClass"
|
||||
class="k-button k-group-start">
|
||||
Result List
|
||||
</NavLink>
|
||||
<NavLink href="real-time"
|
||||
Match="@NavLinkMatch.All"
|
||||
ActiveClass="@ActiveClass"
|
||||
class="k-button k-group-end">
|
||||
Data Virtualization
|
||||
</NavLink>
|
||||
</div>
|
||||
@code{
|
||||
string ActiveClass = "k-state-active k-primary";
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<TelerikDatePicker Value="@StartDate" ValueChanged="@( (DateTime d) => StartChanged(d) )"
|
||||
Min="@( MinDate )"
|
||||
Max="@GetHigherDate()"
|
||||
Width="50%" Format="dd MMM yyyy" />
|
||||
<span style="display: inline-block; width: 5px;"></span>
|
||||
<TelerikDatePicker Value="@EndDate" ValueChanged="@( (DateTime d) => EndChanged(d) )"
|
||||
Min="@GetLowerDate()"
|
||||
Max="@( MaxDate )"
|
||||
Width="50%" Format="dd MMM yyyy" />
|
||||
@if (ShowErrorMessage)
|
||||
{
|
||||
<div class="k-bg-error">@WrongRangeMessage</div>
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlazorFinancePortfolio.Client.Components.StocksChart
|
||||
{
|
||||
public partial class DateRangePicker
|
||||
{
|
||||
[Parameter] public DateTime StartDate { get; set; } = DateTime.Now.Date;
|
||||
[Parameter] public DateTime EndDate { get; set; } = DateTime.Now.Date;
|
||||
[Parameter] public DateTime MinDate { get; set; } = DateTime.Now.AddMonths(-2).Date;
|
||||
[Parameter] public DateTime MaxDate { get; set; } = DateTime.Now.Date;
|
||||
[Parameter] public EventCallback<Tuple<DateTime, DateTime>> SelectionChanged { get; set; }
|
||||
string WrongRangeMessage = "Start date must be before end date. We reset the selection, try again.";
|
||||
bool ShowErrorMessage { get; set; }
|
||||
|
||||
protected override Task OnParametersSetAsync()
|
||||
{
|
||||
StartDate = GetLowerDate();
|
||||
return base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
bool IsValid()
|
||||
{
|
||||
if (StartDate <= EndDate)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async Task FlashErrorMessage()
|
||||
{
|
||||
ShowErrorMessage = true;
|
||||
await Task.Delay(3000);
|
||||
ShowErrorMessage = false;
|
||||
}
|
||||
|
||||
async void Update()
|
||||
{
|
||||
if (IsValid())
|
||||
{
|
||||
ShowErrorMessage = false;
|
||||
await SelectionChanged.InvokeAsync(new Tuple<DateTime, DateTime>(StartDate, EndDate));
|
||||
}
|
||||
}
|
||||
|
||||
async void StartChanged(DateTime userChoice)
|
||||
{
|
||||
if (userChoice > GetHigherDate())
|
||||
{
|
||||
await FlashErrorMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartDate = userChoice;
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
async void EndChanged(DateTime userChoice)
|
||||
{
|
||||
if (userChoice < GetLowerDate())
|
||||
{
|
||||
await FlashErrorMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
EndDate = userChoice;
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
DateTime GetLowerDate()
|
||||
{
|
||||
return StartDate <= EndDate ? StartDate : EndDate;
|
||||
}
|
||||
|
||||
DateTime GetHigherDate()
|
||||
{
|
||||
return StartDate >= EndDate ? StartDate : EndDate;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
@implements IDisposable
|
||||
|
||||
@using BlazorFinancePortfolio.Models
|
||||
@using BlazorFinancePortfolio.Helpers
|
||||
|
||||
<TelerikChart Transitions="true" RenderAs="@RenderingMode.SVG" @ref="@ChartRef">
|
||||
<ChartSeriesItems>
|
||||
<ChartSeries Type="@MainChartType" Data="@ChartData"
|
||||
Field="@( nameof(StockIntervalDetails.Close) )"
|
||||
CategoryField="@( nameof(StockIntervalDetails.Date) )"
|
||||
Style="@ChartSeriesStyle.Smooth"
|
||||
Color="@( MainChartType == ChartSeriesType.Line ? "#2D73F5" : "#007BFF" )">
|
||||
<ChartSeriesLine Style="@ChartSeriesLineStyle.Smooth" />
|
||||
</ChartSeries>
|
||||
<ChartSeries Type="@ChartSeriesType.Column" Data="@ChartData"
|
||||
Field="@( nameof(StockIntervalDetails.Volume) )"
|
||||
CategoryField="@( nameof(StockIntervalDetails.Date) )"
|
||||
ColorField="@( nameof(StockIntervalDetails.ColumnColor) )"
|
||||
Aggregate="@ChartSeriesAggregate.Sum"
|
||||
Gap="0.75"
|
||||
Axis="Volume"></ChartSeries>
|
||||
</ChartSeriesItems>
|
||||
<ChartCategoryAxes>
|
||||
<ChartCategoryAxis Type="@ChartCategoryAxisType.Date"
|
||||
BaseUnit="@IntervalFilter.Interval.Unit"
|
||||
BaseUnitStep="@IntervalFilter.Interval.Step"
|
||||
MaxDateGroups="20">
|
||||
<ChartCategoryAxisLabels Format="dd/yyyy">
|
||||
<ChartCategoryAxisLabels Step="@LabelStep"></ChartCategoryAxisLabels>
|
||||
</ChartCategoryAxisLabels>
|
||||
<ChartCategoryAxisMajorGridLines Visible="true" />
|
||||
<ChartCategoryAxisMinorGridLines Visible="false" />
|
||||
</ChartCategoryAxis>
|
||||
</ChartCategoryAxes>
|
||||
<ChartValueAxes>
|
||||
<ChartValueAxis>
|
||||
<ChartValueAxisLabels Format="@( SelectedCurrency.Sign + "{0}" )" />
|
||||
<ChartValueAxisMinorGridLines Visible="false" />
|
||||
</ChartValueAxis>
|
||||
<ChartValueAxis Name="Volume" Min="0" Max="@VolumeValueAxisMax()" Visible="false"></ChartValueAxis>
|
||||
</ChartValueAxes>
|
||||
<ChartLegend Visible="false" />
|
||||
</TelerikChart>
|
||||
|
||||
@code{
|
||||
[Parameter] public ChartSeriesType MainChartType { get; set; }
|
||||
[Parameter] public List<StockIntervalDetails> ChartData { get; set; }
|
||||
[Parameter] public int LabelStep { get; set; }
|
||||
[Parameter] public IntervalFilter IntervalFilter { get; set; } = new IntervalFilter() { Duration = Constants.MS_1_HOUR, Interval = new Interval { Step = 60, Unit = ChartCategoryAxisBaseUnit.Minutes } };
|
||||
[CascadingParameter] public Currency SelectedCurrency { get; set; }
|
||||
TelerikChart ChartRef { get; set; }
|
||||
long VolumeValueAxisMax()
|
||||
{
|
||||
if (ChartData == null || ChartData.Count == 0)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
return (long)Math.Round(ChartData.Max(d => d.Volume) * 6);
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
WindowResizeDispatcher.WindowResize += ResizeChart;
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
AdjustLbelStep();
|
||||
|
||||
base.OnParametersSet();
|
||||
}
|
||||
|
||||
async Task ResizeChart()
|
||||
{
|
||||
if (ChartRef != null)
|
||||
{
|
||||
AdjustLbelStep();
|
||||
ChartRef.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void AdjustLbelStep()
|
||||
{
|
||||
if (ChartData.Any())
|
||||
{
|
||||
var chartDataTimeRangeInMs = (long)(ChartData.Last().Date - ChartData.First().Date).TotalMilliseconds;
|
||||
int currStep = (int)(chartDataTimeRangeInMs / IntervalFilter.Duration / Constants.MaxLabelStepsInStocksChart);
|
||||
if (WindowResizeDispatcher.WindowWidth != null && WindowResizeDispatcher.WindowWidth < 768)
|
||||
{
|
||||
currStep = currStep * 5;
|
||||
}
|
||||
LabelStep = currStep;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
WindowResizeDispatcher.WindowResize -= ResizeChart;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
@using BlazorFinancePortfolio.Models
|
||||
|
||||
<div class="row py-4 d-flex justify-content-between align-items-center">
|
||||
<div class="col col-4 d-flex daterange-input-wrap">
|
||||
<DateRangePicker StartDate="@Start"
|
||||
EndDate="@End"
|
||||
MinDate="@MinDate"
|
||||
MaxDate="@MaxDate"
|
||||
SelectionChanged="@( (Tuple<DateTime, DateTime> d) => DatesChanged(d) )" />
|
||||
</div>
|
||||
|
||||
<ul class="k-reset d-flex col-12 col-sm-8 col-md-6 col-lg-3 justify-content-center justify-content-sm-start">
|
||||
@foreach (TimeFilter filter in TimeFilters)
|
||||
{
|
||||
<li class="ml-3">
|
||||
<span class="list-item time-filter-item @( ActiveTimeFilterDuration == filter.Duration ? " active" : "" )"
|
||||
@onclick="@( () => OnTimeFilterClick(filter.Duration) )">
|
||||
@filter.Name
|
||||
</span>
|
||||
</li>
|
||||
|
||||
}
|
||||
</ul>
|
||||
|
||||
<div class="col-12 col-sm-4 col-md-6 col-lg-4 text-center text-sm-right mt-3 mt-sm-0">
|
||||
<TelerikDropDownList Data="@IntervalFilters" TextField="Name" ValueField="Duration"
|
||||
TItem="IntervalFilter" TValue="long" Value="SelectedFilterInterval"
|
||||
ValueChanged="@( (long v) => OnIntervalChange(v) )"
|
||||
Width="140px" PopupWidth="135px" PopupHeight="auto" Class="dropdown-list-selection interval ddl-no-bg">
|
||||
<ValueTemplate>
|
||||
<span class="service-category"> Interval: @( (context as IntervalFilter).Name)</span>
|
||||
</ValueTemplate>
|
||||
</TelerikDropDownList>
|
||||
|
||||
<TelerikDropDownList @bind-Value="@MainChartType" Data="@AvailableChartTypes" Width="140px" PopupWidth="140px" PopupHeight="auto" Class="ddl-no-bg">
|
||||
<ValueTemplate>
|
||||
@{
|
||||
ChartTypeList dataItem = context as ChartTypeList;
|
||||
<span class="chart-category selected d-flex align-items-center">
|
||||
<img src="@( $" images/{@dataItem.Text.ToLowerInvariant()}.png" )" />
|
||||
@dataItem.Text
|
||||
</span>
|
||||
}
|
||||
</ValueTemplate>
|
||||
<ItemTemplate>
|
||||
@{
|
||||
ChartTypeList dataItem = context as ChartTypeList;
|
||||
<span class="chart-category d-flex align-items-center">
|
||||
<img src="@( $" images/{@dataItem.Text.ToLowerInvariant()}.png" )" />
|
||||
@dataItem.Text
|
||||
</span>
|
||||
}
|
||||
</ItemTemplate>
|
||||
</TelerikDropDownList>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
@if (CurrentChartData != null)
|
||||
{
|
||||
<MainChart ChartData="@CurrentChartData" IntervalFilter="@SelectedInterval" MainChartType="@MainChartType" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
Select a symbol from the list to see its details. If there are no symbols, add a new one.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,142 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorFinancePortfolio.Models;
|
||||
using BlazorFinancePortfolio.Helpers;
|
||||
using BlazorFinancePortfolio.Services;
|
||||
using Telerik.Blazor;
|
||||
|
||||
namespace BlazorFinancePortfolio.Client.Components.StocksChart
|
||||
{
|
||||
public partial class TopAreaContainer
|
||||
{
|
||||
[Inject] internal StocksListService stocksService { get; set; }
|
||||
[Parameter] public Stock SelectedStock { get; set; }
|
||||
|
||||
public DateTime MinDate { get; set; } = DateTime.Now.AddMonths(-2).Date;
|
||||
public DateTime MaxDate { get; set; } = DateTime.Now.Date;
|
||||
|
||||
//parameters
|
||||
List<StockIntervalDetails> CurrentChartData { get; set; }
|
||||
DateTime Start { get; set; }
|
||||
DateTime End { get; set; } = DateTime.Now;
|
||||
public IntervalFilter SelectedInterval { get; set; }
|
||||
ChartSeriesType MainChartType { get; set; }
|
||||
long? ActiveTimeFilterDuration { get; set; }
|
||||
long SelectedFilterInterval { get; set; }
|
||||
|
||||
//data sources
|
||||
List<ChartTypeList> AvailableChartTypes { get; set; } = ChartTypeList.GetAvailableSeriesTypes();
|
||||
List<TimeFilter> TimeFilters { get; set; } = TimeFilter.GetFilters();
|
||||
public List<IntervalFilter> IntervalFilters { get; set; } = IntervalFilter.GetIntervalFilters();
|
||||
|
||||
IDictionary<long, long> TimeFilterDefaultIntervalsMapping { get; set; } =
|
||||
new Dictionary<long, long>
|
||||
{
|
||||
{Constants.MS_1_HOUR, Constants.MS_5_MINUTES },
|
||||
{Constants.MS_4_HOURS, Constants.MS_15_MINUTES },
|
||||
{Constants.MS_12_HOURS, Constants.MS_30_MINUTES },
|
||||
{Constants.MS_1_DAY, Constants.MS_30_MINUTES },
|
||||
{Constants.MS_4_DAYS, Constants.MS_1_HOUR },
|
||||
{Constants.MS_1_WEEK, Constants.MS_4_HOURS },
|
||||
};
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
SelectedInterval = IntervalFilter.GetIntervalFilters()[3];
|
||||
SelectedFilterInterval = IntervalFilter.GetIntervalFilters()[3].Duration;
|
||||
ActiveTimeFilterDuration = TimeFilter.GetFilters()[3].Duration;
|
||||
|
||||
Start = End.AddDays(-4);
|
||||
FilterIntervals(ActiveTimeFilterDuration.Value);
|
||||
|
||||
TimeFilters.Add(new TimeFilter() { Name = "MAX", Duration = (long)(MaxDate - MinDate).TotalMilliseconds });
|
||||
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
FilterCurrentChartData(SelectedFilterInterval);
|
||||
|
||||
base.OnParametersSet();
|
||||
}
|
||||
|
||||
void DatesChanged(Tuple<DateTime, DateTime> dates)
|
||||
{
|
||||
Start = dates.Item1;
|
||||
End = dates.Item2;
|
||||
|
||||
var dateRangeIntervalInMs = (long)(End - Start).TotalMilliseconds;
|
||||
|
||||
ActiveTimeFilterDuration = null;
|
||||
|
||||
FilterIntervals(dateRangeIntervalInMs);
|
||||
SetDefaultInterval(dateRangeIntervalInMs);
|
||||
|
||||
FilterCurrentChartData(SelectedFilterInterval);
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
void OnTimeFilterClick(long FilterDuration)
|
||||
{
|
||||
if (this.ActiveTimeFilterDuration == FilterDuration)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ActiveTimeFilterDuration = FilterDuration;
|
||||
|
||||
End = MaxDate;
|
||||
Start = End.AddMilliseconds(-ActiveTimeFilterDuration.Value);
|
||||
|
||||
FilterIntervals(FilterDuration);
|
||||
SetDefaultInterval(FilterDuration);
|
||||
|
||||
FilterCurrentChartData(SelectedFilterInterval);
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
void FilterCurrentChartData(long intervalInMilliseconds)
|
||||
{
|
||||
var intervalInMinutes = intervalInMilliseconds / (1000 * 60);
|
||||
|
||||
if (SelectedStock != null)
|
||||
{
|
||||
CurrentChartData = stocksService.GenerateStockIntervals(SelectedStock, (int)intervalInMinutes, Start, End);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentChartData = null;
|
||||
}
|
||||
}
|
||||
|
||||
void OnIntervalChange(long IntervalDuration)
|
||||
{
|
||||
SelectedInterval = IntervalFilter.GetIntervalFilters().Where(i => i.Duration == IntervalDuration).FirstOrDefault();
|
||||
SelectedFilterInterval = IntervalDuration;
|
||||
|
||||
FilterCurrentChartData(IntervalDuration);
|
||||
}
|
||||
|
||||
void FilterIntervals(long filterDuration)
|
||||
{
|
||||
IntervalFilters = IntervalFilter.GetIntervalFilters().Where(i => i.Duration < filterDuration && filterDuration / i.Duration < 200).ToList();
|
||||
}
|
||||
|
||||
void SetDefaultInterval(long filterDuration)
|
||||
{
|
||||
if (!IntervalFilters.Any(i => i.Duration == SelectedFilterInterval))
|
||||
{
|
||||
SelectedFilterInterval = TimeFilterDefaultIntervalsMapping.ContainsKey(filterDuration)
|
||||
? TimeFilterDefaultIntervalsMapping[filterDuration]
|
||||
: Constants.MS_1_DAY;
|
||||
SelectedInterval = IntervalFilters.Where(i => i.Duration == SelectedFilterInterval).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<TelerikButton Icon="@IconName.Delete" Class="text-danger" OnClick="@( _ => WindowVisible = true )" Enabled="@Enabled">
|
||||
Remove
|
||||
</TelerikButton>
|
||||
|
||||
<TelerikWindow @bind-Visible="@WindowVisible" Modal="true">
|
||||
<WindowTitle>
|
||||
<strong>Confirm deleting</strong>
|
||||
</WindowTitle>
|
||||
<WindowContent>
|
||||
Are you sure you want to delete this?
|
||||
<TelerikButton OnClick="@(() => WindowVisible = false)">Cancel</TelerikButton>
|
||||
<TelerikButton OnClick="@RaiseConfirm" Primary="true">Confirm</TelerikButton>
|
||||
</WindowContent>
|
||||
</TelerikWindow>
|
||||
|
||||
@code {
|
||||
[Parameter] public bool Enabled { get; set; }
|
||||
[Parameter] public EventCallback OnConfirm { get; set; }
|
||||
bool WindowVisible { get; set; }
|
||||
async void RaiseConfirm()
|
||||
{
|
||||
WindowVisible = false;
|
||||
await OnConfirm.InvokeAsync(null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
@using BlazorFinancePortfolio.Models
|
||||
|
||||
<TelerikChart Width="100%" Height="60px" Transitions="false" RenderAs="@RenderingMode.SVG">
|
||||
<ChartSeriesItems>
|
||||
<ChartSeries Type="ChartSeriesType.Area"
|
||||
Data="@(StockData.IntraDayChart.Cast<object>())"
|
||||
Color="@(StockData.DayChange >= 0 ? "#5CB85C" : "#D9534F")" />
|
||||
<ChartLegend Visible="false" />
|
||||
<ChartValueAxes>
|
||||
<ChartValueAxis Visible="false">
|
||||
<ChartValueAxisLabels Visible="false" />
|
||||
<ChartValueAxisMajorGridLines Visible="false" />
|
||||
<ChartValueAxisMinorGridLines Visible="false" />
|
||||
</ChartValueAxis>
|
||||
</ChartValueAxes>
|
||||
<ChartCategoryAxes>
|
||||
<ChartCategoryAxis Visible="false">
|
||||
<ChartCategoryAxisLabels Visible="false" />
|
||||
<ChartCategoryAxisMajorGridLines Visible="false" />
|
||||
<ChartCategoryAxisMinorGridLines Visible="false" />
|
||||
</ChartCategoryAxis>
|
||||
</ChartCategoryAxes>
|
||||
</ChartSeriesItems>
|
||||
</TelerikChart>
|
||||
|
||||
@code {
|
||||
[Parameter] public Stock StockData { get; set; }
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
@using BlazorFinancePortfolio.Models
|
||||
@using BlazorFinancePortfolio.Helpers
|
||||
|
||||
<div class="row py-4 d-flex justify-content-between stocks-grid-buttons">
|
||||
<div class="col d-flex justify-content-start px-0">
|
||||
@if (UncategorizedStocks != null)
|
||||
{
|
||||
<TelerikDropDownList Data="@UncategorizedStocks"
|
||||
Value="@SelectedValue"
|
||||
ValueChanged="@((string v) =>OnAdd(v))"
|
||||
TextField="Name"
|
||||
ValueField="Symbol"
|
||||
DefaultItem="@DefailtUncategorizedStock"
|
||||
PopupWidth="270px"
|
||||
Width="165px"
|
||||
Class="ddl-no-bg">
|
||||
</TelerikDropDownList>
|
||||
}
|
||||
|
||||
<ConfirmButton Enabled="@( SelectedStock != null )" OnConfirm="@OnRemoveConfirm" />
|
||||
</div>
|
||||
<div class="col text-center mt-4 mt-md-0">
|
||||
<GridSwitcher />
|
||||
</div>
|
||||
<div class="col d-none d-md-block"></div>
|
||||
</div>
|
||||
<div class="row stocks-grid">
|
||||
<TelerikGrid Data="@Data" Sortable="true" SelectionMode="GridSelectionMode.Single" SelectedItems="@SelectedStocks" SelectedItemsChanged="@((IEnumerable<Stock> stocks) => OnSelect(stocks))">
|
||||
<GridColumns>
|
||||
<GridCheckboxColumn SelectAll="false" Width="40px" />
|
||||
<GridColumn Field="Symbol" Title="Symbol" />
|
||||
<GridColumn Field="Name" Title="Name" />
|
||||
<GridColumn Field="Price" Title="Price">
|
||||
<Template>
|
||||
@{
|
||||
var stock = context as Stock;
|
||||
<strong>@(SelectedCurrency?.Sign + stock.Price)</strong>
|
||||
}
|
||||
</Template>
|
||||
</GridColumn>
|
||||
@if (LargerThanPhone)
|
||||
{
|
||||
<GridColumn Field="DayChange" Title="Day Change">
|
||||
<Template>
|
||||
@{
|
||||
var stock = context as Stock;
|
||||
<span class="@NumbersHelper.GetChangeNumberClass(stock.DayChange)">@NumbersHelper.FormatDecimal(stock.DayChange * SelectedCurrency.Multiplier)</span>
|
||||
}
|
||||
</Template>
|
||||
</GridColumn>
|
||||
@*<GridColumn Field="ChangePercentage" Title="Change Percentage">
|
||||
<Template>
|
||||
@{
|
||||
var stock = context as Stock;
|
||||
<span class="@NumbersHelper.GetChangeNumberClass(stock.ChangePercentage)">@(stock.ChangePercentage)%</span>
|
||||
}
|
||||
</Template>
|
||||
</GridColumn>*@
|
||||
<GridColumn Field="Volume" Title="Volume">
|
||||
<Template>
|
||||
@{
|
||||
var stock = context as Stock;
|
||||
<span>@NumbersHelper.FormatDecimal(stock.Volume * SelectedCurrency.Multiplier)</span>
|
||||
}
|
||||
</Template>
|
||||
</GridColumn>
|
||||
@*<GridColumn Field="VolumeAvg" Title="Avg. Volume">
|
||||
<Template>
|
||||
@{
|
||||
var stock = context as Stock;
|
||||
<span>@NumbersHelper.FormatDecimal(stock.VolumeAvg * SelectedCurrency.Multiplier)</span>
|
||||
}
|
||||
</Template>
|
||||
</GridColumn>*@
|
||||
@if (LargerThanTablet)
|
||||
{
|
||||
<GridColumn Field="MarketCap" Title="Market Cap">
|
||||
<Template>
|
||||
@{
|
||||
var stock = context as Stock;
|
||||
<span>@NumbersHelper.FormatDecimal(stock.MarketCap * SelectedCurrency.Multiplier)</span>
|
||||
}
|
||||
</Template>
|
||||
</GridColumn>
|
||||
<GridColumn Field="PricePerEarningRatio" Title="PE Ratio">
|
||||
<Template>
|
||||
@{
|
||||
var stock = context as Stock;
|
||||
<span>
|
||||
@(stock.PricePerEarningRatio != null
|
||||
? NumbersHelper.FormatDecimal(stock.PricePerEarningRatio.Value * SelectedCurrency.Multiplier)
|
||||
: null)
|
||||
</span>
|
||||
}
|
||||
</Template>
|
||||
</GridColumn>
|
||||
<GridColumn Title="Last Day Chart">
|
||||
<Template>
|
||||
<SparklineChart StockData="@( context as Stock )" />
|
||||
</Template>
|
||||
</GridColumn>
|
||||
}
|
||||
}
|
||||
</GridColumns>
|
||||
</TelerikGrid>
|
||||
|
||||
</div>
|
|
@ -0,0 +1,123 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorFinancePortfolio.Models;
|
||||
using BlazorFinancePortfolio.Helpers;
|
||||
using BlazorFinancePortfolio.Services;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BlazorFinancePortfolio.Client.Components.StocksGrid
|
||||
{
|
||||
public partial class StocksGrid : IDisposable
|
||||
{
|
||||
[Inject] StocksListService StocksListService { get; set; }
|
||||
[Inject] IJSRuntime JSRuntime { get; set; }
|
||||
[Parameter] public Stock SelectedStock { get; set; }
|
||||
IEnumerable<Stock> SelectedStocks
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SelectedStock != null)
|
||||
{
|
||||
return new List<Stock>() { SelectedStock };
|
||||
}
|
||||
return Enumerable.Empty<Stock>();
|
||||
}
|
||||
}
|
||||
[Parameter] public EventCallback<Stock> SelectedStockChanged { get; set; }
|
||||
[Parameter] public List<Stock> Data { get; set; }
|
||||
[Parameter] public List<Stock> UncategorizedStocks { get; set; }
|
||||
[CascadingParameter] public Currency SelectedCurrency { get; set; }
|
||||
|
||||
string SelectedValue { get; set; }
|
||||
Stock DefailtUncategorizedStock { get; set; } = new Stock() { Name = "+ Add new stock", Symbol = null };
|
||||
|
||||
bool LargerThanPhone { get; set; }
|
||||
bool LargerThanTablet { get; set; }
|
||||
|
||||
async Task OnSelect(IEnumerable<Stock> selectedStocks)
|
||||
{
|
||||
SelectedStock = selectedStocks.FirstOrDefault();
|
||||
await SelectedStockChanged.InvokeAsync(SelectedStock);
|
||||
}
|
||||
|
||||
async Task OnAdd(string symbol)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(symbol))
|
||||
{
|
||||
var stock = UncategorizedStocks.FirstOrDefault(s => s.Symbol == symbol);
|
||||
if (stock != null && Data.All(s => s.Symbol != stock.Symbol))
|
||||
{
|
||||
Stock addedStock = await StocksListService.AddStock(stock);
|
||||
Data = await StocksListService.GetStocks(true);
|
||||
|
||||
UncategorizedStocks.Remove(stock);
|
||||
var newUncategorizedStocks = new List<Stock>(UncategorizedStocks);
|
||||
UncategorizedStocks = newUncategorizedStocks;
|
||||
|
||||
SelectedValue = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async Task OnRemoveConfirm()
|
||||
{
|
||||
if (SelectedStock == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var stockForRemove = Data.FirstOrDefault(c => c.Symbol == SelectedStock.Symbol);
|
||||
await StocksListService.RemoveStock(stockForRemove);
|
||||
Data = await StocksListService.GetStocks(true);
|
||||
UncategorizedStocks = await StocksListService.GetStocks(false);
|
||||
|
||||
SelectedStock = Data.FirstOrDefault();
|
||||
await SelectedStockChanged.InvokeAsync(SelectedStock);
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await ToggleColumns();
|
||||
|
||||
Data = await StocksListService.GetStocks(true);
|
||||
|
||||
UncategorizedStocks = await StocksListService.GetStocks(false);
|
||||
|
||||
SelectedStock = Data.FirstOrDefault();
|
||||
await SelectedStockChanged.InvokeAsync(SelectedStock);
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
WindowResizeDispatcher.WindowResize += ToggleColumns;
|
||||
}
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
async Task ToggleColumns()
|
||||
{
|
||||
if (WindowResizeDispatcher.WindowWidth == null)
|
||||
{
|
||||
WindowResizeDispatcher.WindowWidth = await JSRuntime.InvokeAsync<int>("getWindowWidth");
|
||||
}
|
||||
|
||||
LargerThanPhone = WindowResizeDispatcher.WindowWidth > 768;
|
||||
LargerThanTablet = WindowResizeDispatcher.WindowWidth > 992;
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
WindowResizeDispatcher.WindowResize -= ToggleColumns;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
@page "/"
|
||||
|
||||
@using BlazorFinancePortfolio.Models
|
||||
@using BlazorFinancePortfolio.Services
|
||||
@using BlazorFinancePortfolio.Client.Components.StocksChart
|
||||
@using BlazorFinancePortfolio.Client.Components.StocksGrid
|
||||
|
||||
@inject StocksListService StocksListService
|
||||
|
||||
|
||||
<div class="container">
|
||||
@if (ShowChart)
|
||||
{
|
||||
<TopAreaContainer SelectedStock="@SelectedStock" />
|
||||
}
|
||||
|
||||
<div class="text-primary text-right mt-2">
|
||||
<span class="collapse-expand-button" @onclick="@( () => ShowChart = !ShowChart )">
|
||||
@( ShowChart ? "Hide Chart" : "Show Chart" )
|
||||
<TelerikIcon Icon="@( ShowChart ? IconName.ArrowChevronUp : IconName.ArrowChevronDown )" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="d-flex justify-content-center mx-0">
|
||||
<div class="row">
|
||||
@if (SelectedStock != null)
|
||||
{
|
||||
<div class="selected-stock-badge d-flex align-items-center badge-@( SelectedStock.ChangePercentage > 0 ? "positive" : "negative" )-value">
|
||||
<span>@SelectedStock.Symbol </span><span>@SelectedStock.Price</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<StocksGrid @bind-SelectedStock="@SelectedStock"></StocksGrid>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
Stock SelectedStock { get; set; }
|
||||
bool ShowChart { get; set; } = true;
|
||||
|
||||
[CascadingParameter] public bool ShowNavigation { get; set; }
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
ShowNavigation = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
@page "/real-time"
|
||||
|
||||
@using BlazorFinancePortfolio.Models
|
||||
@using BlazorFinancePortfolio.Client.Components
|
||||
|
||||
<div class="container" style="padding-bottom: 100px;">
|
||||
<div class="d-flex justify-content-center py-5 align-items-center">
|
||||
<GridSwitcher />
|
||||
</div>
|
||||
<div class="row d-flex justify-content-center real-time-data">
|
||||
<TelerikGrid Data=@GridData
|
||||
ScrollMode="@GridScrollMode.Virtual"
|
||||
Height="600px" RowHeight="60" PageSize="48" Width="100%">
|
||||
<GridColumns>
|
||||
<GridColumn Field="@nameof(RealTimeData.Symbol)" Width="80px" Title="Symbol" />
|
||||
<GridColumn Field="@nameof(RealTimeData.Name)" Width="130px" Title="Name" />
|
||||
<GridColumn Field="@nameof(RealTimeData.Currency)" Width="80px" Title="Currency">
|
||||
<Template>
|
||||
@SelectedCurrency.Symbol
|
||||
</Template>
|
||||
</GridColumn>
|
||||
<GridColumn Field="@nameof(RealTimeData.Price)" Width="100px" Title="Price">
|
||||
<Template>
|
||||
@{
|
||||
RealTimeData dataItem = context as RealTimeData;
|
||||
<span class="grid-price-cell p-0 @( GetPriceChangeClass(dataItem.Change) )">
|
||||
@string.Format(SelectedCurrency.Sign + "{0:0.00}", dataItem.Price)
|
||||
</span>
|
||||
}
|
||||
</Template>
|
||||
</GridColumn>
|
||||
<GridColumn Field="@nameof(RealTimeData.Change)" Width="100px" Title="Change">
|
||||
<Template>
|
||||
@{
|
||||
RealTimeData dataItem = context as RealTimeData;
|
||||
string sign = dataItem.Change > 0 ? "+" : "";
|
||||
<span class="grid-price-cell p-0 @( GetPriceChangeClass(dataItem.Change) )">
|
||||
@string.Format(sign + "{0:0.00}%", dataItem.Change)
|
||||
</span>
|
||||
}
|
||||
</Template>
|
||||
</GridColumn>
|
||||
<GridColumn Field="@nameof(RealTimeData.StockExchangeLong)" Width="230px" Title="Stock Exchange" />
|
||||
<GridColumn Field="@nameof(RealTimeData.StockExchangeShort)" Width="100px" Title="SE Short" />
|
||||
<GridColumn Field="@nameof(RealTimeData.TimeZone)" Width="100px" Title="Time Zone" />
|
||||
<GridColumn Field="@nameof(RealTimeData.TimeZoneName)" Width="180px" Title="Time Zone Name" />
|
||||
@if (ShowAllColumns)
|
||||
{
|
||||
<GridColumn Field="@nameof(RealTimeData.YearLow)" Width="80px" Title="Year Low">
|
||||
<Template>
|
||||
@string.Format("{0:0.00}", (context as RealTimeData).YearLow)
|
||||
</Template>
|
||||
</GridColumn>
|
||||
<GridColumn Field="@nameof(RealTimeData.YearHigh)" Width="90px" Title="Year High">
|
||||
<Template>
|
||||
@string.Format("{0:0.00}", (context as RealTimeData).YearHigh)
|
||||
</Template>
|
||||
</GridColumn>
|
||||
<GridColumn Field="@nameof(RealTimeData.Volume)" Width="130px" Title="Volume">
|
||||
<Template>
|
||||
@string.Format("{0:0}", (context as RealTimeData).Volume)
|
||||
</Template>
|
||||
</GridColumn>
|
||||
<GridColumn Field="@nameof(RealTimeData.MarketCap)" Width="150px" Title="Market Cap">
|
||||
<Template>
|
||||
@string.Format("{0:0}", (context as RealTimeData).MarketCap)
|
||||
</Template>
|
||||
</GridColumn>
|
||||
}
|
||||
</GridColumns>
|
||||
</TelerikGrid>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,93 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using BlazorFinancePortfolio.Models;
|
||||
using BlazorFinancePortfolio.Services;
|
||||
using BlazorFinancePortfolio.Helpers;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BlazorFinancePortfolio.Client.Pages
|
||||
{
|
||||
public partial class RealTime : IDisposable
|
||||
{
|
||||
[Inject] RealTimeDataService RealTimeDataService { get; set; }
|
||||
[Inject] IJSRuntime JSRuntime { get; set; }
|
||||
[CascadingParameter] public Currency SelectedCurrency { get; set; }
|
||||
bool ShowAllColumns { get; set; }
|
||||
int LoadDataInterval { get; set; } = 1500;
|
||||
List<RealTimeData> GridData { get; set; }
|
||||
CancellationTokenSource CancelToken;
|
||||
Random rnd = new Random();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await ToggleColumns();
|
||||
WindowResizeDispatcher.WindowResize += ToggleColumns;
|
||||
CancelToken = new CancellationTokenSource();
|
||||
GridData = await RealTimeDataService.GetInitialData(SelectedCurrency.Symbol);
|
||||
await IntervalDataUpdate();
|
||||
}
|
||||
|
||||
async Task IntervalDataUpdate()
|
||||
{
|
||||
while (CancelToken.Token != null)
|
||||
{
|
||||
await Task.Delay(LoadDataInterval, CancelToken.Token);
|
||||
await RefreshData();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void StopTimer()
|
||||
{
|
||||
if (CancelToken != null)
|
||||
{
|
||||
CancelToken.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task ToggleColumns()
|
||||
{
|
||||
if (WindowResizeDispatcher.WindowWidth == null)
|
||||
{
|
||||
WindowResizeDispatcher.WindowWidth = await JSRuntime.InvokeAsync<int>("getWindowWidth");
|
||||
}
|
||||
|
||||
if (WindowResizeDispatcher.WindowWidth < 992)
|
||||
{
|
||||
ShowAllColumns = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowAllColumns = true;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
WindowResizeDispatcher.WindowResize -= ToggleColumns;
|
||||
StopTimer();
|
||||
}
|
||||
|
||||
async Task RefreshData()
|
||||
{
|
||||
foreach (RealTimeData item in GridData)
|
||||
{
|
||||
decimal change = RealTimeDataService.GetRandomChange();
|
||||
item.Change = change;
|
||||
item.Price = item.Price + change;
|
||||
}
|
||||
}
|
||||
|
||||
string GetPriceChangeClass(decimal change)
|
||||
{
|
||||
if (change > 0) return "price-up";
|
||||
if (change < 0) return "price-down";
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
@page "/profile"
|
||||
|
||||
@using BlazorFinancePortfolio.Models
|
||||
@using BlazorFinancePortfolio.Helpers
|
||||
|
||||
<TelerikWindow Visible="@IsProfileVisible" State="WindowState.Maximized">
|
||||
<WindowContent>
|
||||
<div class="container py-5">
|
||||
<div class="col d-flex justify-content-between align-items-center py-5">
|
||||
<h1>My Portfolio</h1>
|
||||
|
||||
<span @onclick="@CloseProfile" class="k-icon icon-close"></span>
|
||||
</div>
|
||||
<div class=" row d-flex justify-content-between pt-5">
|
||||
<div class="col col-3">
|
||||
<div class="user-profile-wrapper d-flex flex-column align-self-end">
|
||||
<div class="profile-image"></div>
|
||||
<h3 class="pt-4 pb-5">Collin Johnson</h3>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<td>CURRENT VALUE:</td>
|
||||
<td class="current-value">@(SelectedCurrency.Sign)@NumbersHelper.FormatDecimal(1694.88m * SelectedCurrency.Multiplier) </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>24H CHANGE:</td>
|
||||
<td class="green">@(SelectedCurrency.Sign)@NumbersHelper.FormatDecimal(20m * SelectedCurrency.Multiplier)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>% CHANGE:</td>
|
||||
<td class="green">+1.2%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TOTAL COST:</td>
|
||||
<td>@(SelectedCurrency.Sign)@NumbersHelper.FormatDecimal(9.185m * SelectedCurrency.Multiplier) </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TOTAL PROFIT:</td>
|
||||
<td class="red">@(SelectedCurrency.Sign)@NumbersHelper.FormatDecimal(-2.638m * SelectedCurrency.Multiplier) </td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col user-data">
|
||||
<TelerikGrid Data="@Stocks">
|
||||
<GridColumns>
|
||||
<GridColumn Field="Symbol" Title="Symbol" Width="100px">
|
||||
<Template>
|
||||
<span class="symbol-col">@( (context as Stock).Symbol )</span>
|
||||
</Template>
|
||||
</GridColumn>
|
||||
<GridColumn Field="Name" Title="Name" />
|
||||
<GridColumn Field="Price" Title="Price" Width="100px">
|
||||
<Template>
|
||||
@{
|
||||
var stock = context as Stock;
|
||||
<strong>@(SelectedCurrency?.Sign + stock.Price)</strong>
|
||||
}
|
||||
</Template>
|
||||
</GridColumn>
|
||||
</GridColumns>
|
||||
</TelerikGrid>
|
||||
</div>
|
||||
<div class="col col-5 pb-5">
|
||||
<TelerikChart Width="100%" Height="100%" @ref="@ChartRef">
|
||||
<ChartSeriesItems>
|
||||
<ChartSeries Type="ChartSeriesType.Pie" Data="@Stocks"
|
||||
Field="@nameof(Stock.Price)" CategoryField="@nameof(Stock.Symbol)">
|
||||
<ChartSeriesLabels Template="@( $"#=category#\n{SelectedCurrency.Sign}#=value#" )" Visible="true" />
|
||||
</ChartSeries>
|
||||
</ChartSeriesItems>
|
||||
|
||||
<ChartLegend Position="ChartLegendPosition.Bottom" Visible="@ChartLegendVisible">
|
||||
</ChartLegend>
|
||||
</TelerikChart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</WindowContent>
|
||||
</TelerikWindow>
|
|
@ -0,0 +1,90 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorFinancePortfolio.Models;
|
||||
using BlazorFinancePortfolio.Helpers;
|
||||
using BlazorFinancePortfolio.Services;
|
||||
using Telerik.Blazor.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BlazorFinancePortfolio.Client.Pages
|
||||
{
|
||||
public partial class UserProfile : IDisposable
|
||||
{
|
||||
[Inject] NavigationManager NavManager { get; set; }
|
||||
[Inject] StocksListService StocksListService {get;set;}
|
||||
[Inject] IJSRuntime JSRuntime { get; set; }
|
||||
[CascadingParameter] public Currency SelectedCurrency { get; set; }
|
||||
|
||||
[Parameter] public bool IsProfileVisible { get; set; }
|
||||
[Parameter] public EventCallback<bool> IsProfileVisibleChanged { get; set; }
|
||||
|
||||
public List<Stock> Stocks { get; set; }
|
||||
TelerikChart ChartRef { get; set; }
|
||||
bool ChartLegendVisible { get; set; } = true;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (IsCurrentPageProfile())
|
||||
{
|
||||
IsProfileVisible = true;
|
||||
}
|
||||
|
||||
Stocks = await StocksListService.GetStocks(true);
|
||||
|
||||
await ResizeChart();
|
||||
WindowResizeDispatcher.WindowResize += ResizeChart;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
async void CloseProfile()
|
||||
{
|
||||
if (IsCurrentPageProfile())
|
||||
{
|
||||
NavManager.NavigateTo("");
|
||||
}
|
||||
else
|
||||
{
|
||||
IsProfileVisible = false;
|
||||
await IsProfileVisibleChanged.InvokeAsync(IsProfileVisible);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsCurrentPageProfile()
|
||||
{
|
||||
string currPage = NavManager.Uri.Substring(Math.Min(NavManager.Uri.Length, NavManager.BaseUri.Length)).ToLowerInvariant();
|
||||
return currPage.StartsWith("profile");
|
||||
}
|
||||
|
||||
async Task ResizeChart()
|
||||
{
|
||||
if (WindowResizeDispatcher.WindowWidth == null)
|
||||
{
|
||||
WindowResizeDispatcher.WindowWidth = await JSRuntime.InvokeAsync<int>("getWindowWidth");
|
||||
}
|
||||
|
||||
if (WindowResizeDispatcher.WindowWidth <= 992)
|
||||
{
|
||||
ChartLegendVisible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ChartLegendVisible = true;
|
||||
}
|
||||
StateHasChanged();
|
||||
|
||||
if (ChartRef != null)
|
||||
{
|
||||
ChartRef.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
WindowResizeDispatcher.WindowResize -= ResizeChart;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using Microsoft.AspNetCore.Blazor.Hosting;
|
||||
|
||||
namespace BlazorFinancePortfolio.Client
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IWebAssemblyHostBuilder CreateHostBuilder(string[] args) =>
|
||||
BlazorWebAssemblyHost.CreateDefaultBuilder()
|
||||
.UseBlazorStartup<Startup>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:65065/blazor-stocks-portfolio/",
|
||||
"sslPort": 44340
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"BlazorFinancePortfolio.Client": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:5001/blazor-stocks-portfolio/;http://localhost:5000/blazor-stocks-portfolio/"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
@inherits LayoutComponentBase
|
||||
|
||||
@using BlazorFinancePortfolio.Services
|
||||
@using BlazorFinancePortfolio.Models
|
||||
@using BlazorFinancePortfolio.Client.Pages
|
||||
|
||||
@inject CurrenciesService currenciesService
|
||||
|
||||
<TelerikRootComponent>
|
||||
<nav class="navbar navbar-expand-lg py-3 header">
|
||||
<div class="container d-flex justify-content-between">
|
||||
<div class="title-wrap">
|
||||
<h1 class="header-title">My Stocks Portfolio</h1>
|
||||
|
||||
<div class="d-flex flex-wrap justify-content-between">
|
||||
<TelerikDropDownList Data="@Currencies"
|
||||
Value="@SelectedCurrencyName"
|
||||
TValue="string"
|
||||
TItem="Currency"
|
||||
ValueField="Symbol"
|
||||
TextField="Name"
|
||||
PopupWidth="200px"
|
||||
PopupHeight="auto"
|
||||
Width="200px"
|
||||
ValueChanged="@OnCurrencySelect"
|
||||
Class="ddl-no-bg">
|
||||
|
||||
</TelerikDropDownList>
|
||||
|
||||
<a href="https://github.com/telerik/blazor-stocks" class="source-code-link text-white-50 d-flex align-items-center ">
|
||||
<svg class="navbar-nav-svg mr-1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" focusable="false"><title>GitHub</title><path d="M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z" fill="currentColor" fill-rule="evenodd"></path></svg>
|
||||
Source Code
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div @onclick="@( () => ProfileVisible = true )" class="profile-wrapper d-flex flex-column align-self-center"
|
||||
tabindex="0">
|
||||
<div class="profile-image">
|
||||
|
||||
</div>
|
||||
<div class="text-white-50">Collin Johnson</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container-fluid px-0">
|
||||
|
||||
<div class="content px-4">
|
||||
|
||||
@if (Installable)
|
||||
{
|
||||
<div class="fixed-bottom d-flex align-items-center justify-content-between install-prompt">
|
||||
<span>Install this app?</span>
|
||||
<span>
|
||||
<button class="btn btn-light btn-sm mx-2" @onclick="@InstallClicked">Install</button>
|
||||
<button class="btn btn-outline-light btn-sm" @onclick="@(()=>Installable=false)">Cancel </button>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<CascadingValue Value="@SelectedCurrency">
|
||||
@if (!ProfileVisible)
|
||||
{
|
||||
@Body
|
||||
}
|
||||
else
|
||||
{
|
||||
<UserProfile @bind-IsProfileVisible="@ProfileVisible" />
|
||||
}
|
||||
</CascadingValue>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="container-fluid footer text-center d-flex align-items-center">
|
||||
<div class="w-100">
|
||||
<span class="footer-copyright text-center">
|
||||
Copyright ©2019-@DateTime.Now.Year Progress Software
|
||||
Corporation and/or its subsidiaries or affiliates.
|
||||
</span><span class="progress-logo d-inline-flex"></span>
|
||||
</div>
|
||||
</footer>
|
||||
</TelerikRootComponent>
|
||||
|
||||
|
||||
@code{
|
||||
public Currency SelectedCurrency { get; set; }
|
||||
bool ProfileVisible { get; set; }
|
||||
List<Currency> Currencies { get; set; }
|
||||
public string SelectedCurrencyName { get; set; } = "USD";
|
||||
|
||||
void OnCurrencySelect(string selectedCurrencySymbol)
|
||||
{
|
||||
SelectedCurrency = Currencies.FirstOrDefault(c => c.Symbol == selectedCurrencySymbol);
|
||||
SelectedCurrencyName = selectedCurrencySymbol;
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
ml = () => InvokeAsync(StateHasChanged);// PWA infrastructure
|
||||
|
||||
Currencies = await currenciesService.GetCurrenciesAsync();
|
||||
|
||||
SelectedCurrency = Currencies.FirstOrDefault();
|
||||
}
|
||||
|
||||
// PWA infrastructure
|
||||
[Inject] IJSRuntime JSRuntime { get; set; } // needs to be here not as a @inject directive
|
||||
static bool Installable = false;
|
||||
static Action ml;
|
||||
|
||||
[JSInvokable("InstallPwaPrompt")]
|
||||
//a named method so that it is easy to call from JS Interop, used in the config in the .csproj file
|
||||
public static Task InstallPwaPrompt()
|
||||
{
|
||||
Installable = true;
|
||||
ml.Invoke();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
async void InstallClicked(MouseEventArgs args)
|
||||
{
|
||||
Installable = false;
|
||||
await JSRuntime.InvokeAsync<object>("BlazorPWA.installPWA");//this name comes from the nuget package that generates the pwa code, don't change it unless you change the generated code
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using BlazorFinancePortfolio.Services;
|
||||
using Microsoft.AspNetCore.Components.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BlazorFinancePortfolio.Client
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddTelerikBlazor();
|
||||
services.AddScoped<CurrenciesService>();
|
||||
services.AddScoped<StocksListService>();
|
||||
services.AddScoped<RealTimeDataService>();
|
||||
}
|
||||
|
||||
public void Configure(IComponentsApplicationBuilder app)
|
||||
{
|
||||
app.AddComponent<App>("app");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,657 @@
|
|||
// typography
|
||||
$roboto: 'Roboto', Helvetica, Arial, sans-serif;
|
||||
$ubunto: 'Ubuntu', sans-serif;
|
||||
|
||||
$font-weight-200: 200;
|
||||
|
||||
// colors
|
||||
$white: #fff;
|
||||
$black: #000;
|
||||
$white-50: rgba(255, 255, 255, .5);
|
||||
$text-base: #373A3C;
|
||||
$border-base: #ddd;
|
||||
$active-state-base: #1C7CD5;
|
||||
$hover-bg: #e6e6e6;
|
||||
$negative-value: #D9534F;
|
||||
$positive-value: #5CB85C;
|
||||
$body-base-bg: rgba(236, 238, 239, 0.5);
|
||||
|
||||
// margins
|
||||
$margin-sm: 4px;
|
||||
$margin-m: 2 * $margin-sm;
|
||||
|
||||
// fonts
|
||||
$font-size-default: 16px;
|
||||
|
||||
// header
|
||||
$header-height: 140px;
|
||||
$header-title-padding: .5rem;
|
||||
$profile-image-width: 58px;
|
||||
$profile-image-height: 58px;
|
||||
$profile-image-margin-top: 2rem;
|
||||
|
||||
// footer
|
||||
$footer-padding: 10px;
|
||||
$footer-height: 43px;
|
||||
$footer-font-size: $font-size-default;
|
||||
$progress-logo-height: 20px;
|
||||
$progress-logo-width: 100px;
|
||||
|
||||
// dropdownlist
|
||||
$dropdownlist-font-size: $font-size-default;
|
||||
$dropdownlist-item-selected-bg: #E9ECEF;
|
||||
$dropdownlist-item-selected-text: $active-state-base;
|
||||
|
||||
$dropownlist-add-new-padding: 8px;
|
||||
$dropownlist-add-new-width: 125px;
|
||||
$dropownlist-add-new-height: 40px;
|
||||
|
||||
// splitter
|
||||
$splitter-bar-boxshadow: 5px 5px 5px rgba(#000, 0.2);
|
||||
|
||||
// grid
|
||||
$grid-header-subtitle: 13px;
|
||||
$grid-row-selection-bg: #007BFF;
|
||||
$grid-cell-positive-color: $positive-value;
|
||||
$grid-cell-negative-color: $negative-value;
|
||||
$grid-sorting-icon-margin: $margin-sm;
|
||||
$grid-sorting-icon-right-position: 20px;
|
||||
|
||||
// buttons
|
||||
$nav-button-width: 150px;
|
||||
$nav-button-active-bg: $active-state-base;
|
||||
|
||||
// badge
|
||||
$badge-padding: 10px;
|
||||
$badge-height: 22px;
|
||||
|
||||
// heatmap
|
||||
$heatmap-base-bg: $body-base-bg;
|
||||
$heatmap-size-container-mb: -35px;
|
||||
$heatmap-scale-wrap-width: 385px;
|
||||
$heatmap-scape-block-width: 35px;
|
||||
$heatmap-scale-block-height: 18px;
|
||||
|
||||
// tooltip
|
||||
$tooltip-content-padding-x: 6px;
|
||||
$tooltip-content-padding-y: 2 * $tooltip-content-padding-x;
|
||||
|
||||
// user profile
|
||||
$profile-font-size: 1.8em;
|
||||
$profile-current-value-font-size: 1em;
|
||||
$profile-table-padding: 8px 12px 8px 0;
|
||||
$profile-table-cell-width: 180px;
|
||||
$profile-icon-size: 38px;
|
||||
$profile-user-wrapper: 210px;
|
||||
|
||||
// charts
|
||||
$chart-navigation-drag-handle-bg: #ECEEEF;
|
||||
$chart-navigation-drag-handle-border: #818A91;
|
||||
|
||||
// dateinputs
|
||||
$dateinput-width: 140px;
|
||||
|
||||
/* Blazor app loading sign */
|
||||
.loading-section {
|
||||
text-align: center;
|
||||
height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.loading-section h2 {
|
||||
color: $active-state-base;
|
||||
}
|
||||
|
||||
.loader-dot {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: $active-state-base;
|
||||
display: inline-block;
|
||||
-webkit-animation: grow 2.1s infinite ease-in-out both;
|
||||
animation: grow 2.1s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.loader-dot.dot1 {
|
||||
-webkit-animation-delay: -0.96s;
|
||||
animation-delay: -0.96s;
|
||||
}
|
||||
|
||||
.loader-dot.dot2 {
|
||||
-webkit-animation-delay: -0.48s;
|
||||
animation-delay: -0.48s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes grow {
|
||||
0%, 80%, 100% {
|
||||
-webkit-transform: scale(0)
|
||||
}
|
||||
|
||||
40% {
|
||||
-webkit-transform: scale(1.0)
|
||||
}
|
||||
}
|
||||
/* End Blazor app loading sign */
|
||||
|
||||
/* Symbol badge */
|
||||
.selected-stock-badge {
|
||||
height: $badge-height;
|
||||
color: $white;
|
||||
padding: $badge-padding;
|
||||
border-radius: 2 * $badge-padding;
|
||||
|
||||
span:first-child {
|
||||
margin-right: 2 * $margin-sm;
|
||||
}
|
||||
|
||||
&.badge-positive-value {
|
||||
background: $positive-value;
|
||||
}
|
||||
|
||||
&.badge-negative-value {
|
||||
background: $negative-value;
|
||||
}
|
||||
}
|
||||
/* End symbol badge */
|
||||
|
||||
/* Header */
|
||||
.header {
|
||||
height: $header-height;
|
||||
background: url('../images/header-bg.svg');
|
||||
background-position: bottom;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
h1 {
|
||||
padding-top: $header-title-padding;
|
||||
font-family: $ubunto;
|
||||
font-weight: $font-weight-200;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.profile-wrapper {
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.profile-image {
|
||||
align-self: center;
|
||||
width: $profile-image-width;
|
||||
height: $profile-image-height;
|
||||
border-radius: 50%;
|
||||
background-image: url('../images/user.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.title-wrap {
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
.source-code-link {
|
||||
padding: .375rem .75rem;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navbar-nav-svg {
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.k-dropdown {
|
||||
.k-dropdown-wrap .k-input {
|
||||
font-size: $dropdownlist-font-size;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.k-popup {
|
||||
padding: 0;
|
||||
|
||||
.k-list {
|
||||
.k-item.k-state-selected {
|
||||
background: $dropdownlist-item-selected-bg;
|
||||
color: $dropdownlist-item-selected-text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Stock list */
|
||||
.dropdownlist-add-new {
|
||||
padding-left: 0;
|
||||
width: auto;
|
||||
|
||||
.k-dropdown-wrap {
|
||||
border: 1px solid $border-base;
|
||||
background: $white;
|
||||
color: $text-base;
|
||||
padding: $dropownlist-add-new-padding;
|
||||
width: $dropownlist-add-new-width;
|
||||
height: $dropownlist-add-new-height;
|
||||
align-items: center;
|
||||
|
||||
.k-select {
|
||||
.k-icon {
|
||||
color: $text-base;
|
||||
|
||||
&.k-i-arrow-s {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.k-input {
|
||||
.k-icon {
|
||||
&.k-i-plus {
|
||||
margin-right: 2 * $margin-sm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $hover-bg;
|
||||
background-color: $hover-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-remove-stock.k-button.k-flat {
|
||||
color: $negative-value;
|
||||
|
||||
&:focus:after {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.k-dialog {
|
||||
.k-header {
|
||||
&, .k-icon {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.k-icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.k-content.k-dialog-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.k-dialog-buttongroup {
|
||||
.k-button {
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-list-selection {
|
||||
align-items: center;
|
||||
|
||||
.k-dropdown-wrap .service-category {
|
||||
font-weight: bold;
|
||||
margin-left: $margin-sm;
|
||||
}
|
||||
}
|
||||
|
||||
.k-grid {
|
||||
margin-bottom: 100px;
|
||||
border-width: 0;
|
||||
|
||||
td:first-child {
|
||||
text-overflow: unset;
|
||||
}
|
||||
|
||||
.k-grid-header .k-header {
|
||||
vertical-align: middle;
|
||||
|
||||
.grid-header-subtitle {
|
||||
display: block;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
font-size: $grid-header-subtitle;
|
||||
}
|
||||
|
||||
.k-link > .k-icon.k-i-sort-asc-sm,
|
||||
.k-link > .k-icon.k-i-sort-desc-sm {
|
||||
margin-left: $grid-sorting-icon-margin;
|
||||
}
|
||||
|
||||
.grid-header-subtitle + .k-icon.k-i-sort-asc-sm,
|
||||
.grid-header-subtitle + .k-i-sort-desc-sm {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: - #{$grid-sorting-icon-right-position / 2};
|
||||
right: $grid-sorting-icon-right-position;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.k-grid-content tr td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
tr.k-state-selected > td {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
tr.k-state-selected > td.symbol-col {
|
||||
color: $grid-row-selection-bg;
|
||||
}
|
||||
|
||||
.symbol-col {
|
||||
color: $grid-row-selection-bg;
|
||||
}
|
||||
|
||||
th,
|
||||
.symbol-col,
|
||||
.price-col {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.grid-cell-positive {
|
||||
color: $grid-cell-positive-color;
|
||||
}
|
||||
|
||||
.grid-cell-negative {
|
||||
color: $grid-cell-negative-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Stock chart */
|
||||
kendo-dateinput {
|
||||
width: $dateinput-width;
|
||||
margin-right: $margin-m;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
&.time-filter-item {
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
border-bottom: 3px solid $active-state-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chart-category {
|
||||
&.selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-right: $margin-m;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdownlist-service-selector.interval {
|
||||
margin-right: $margin-m;
|
||||
}
|
||||
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
padding: $footer-padding;
|
||||
background: url('../images/footer-bg.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-color: blue;
|
||||
z-index: 999;
|
||||
font-size: $footer-font-size;
|
||||
color: $white;
|
||||
|
||||
.progress-logo {
|
||||
width: $progress-logo-width;
|
||||
height: $progress-logo-height;
|
||||
vertical-align: middle;
|
||||
background-image: url('../images/progress-logo.svg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
/* PWA Install Prompt */
|
||||
.install-prompt {
|
||||
width: 360px;
|
||||
height: 60px;
|
||||
padding: 15px 20px;
|
||||
box-sizing: border-box;
|
||||
bottom: 63px;
|
||||
left: auto;
|
||||
right: 20px;
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
font-family: $roboto;
|
||||
font-size: 14px;
|
||||
font-weight: lighter;
|
||||
color: #FFFFFF;
|
||||
background-color: #818A91;
|
||||
z-index: 11000;
|
||||
}
|
||||
|
||||
/* Profile */
|
||||
h1 {
|
||||
font-family: $ubunto;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: $profile-table-padding;
|
||||
}
|
||||
|
||||
.user-data {
|
||||
.k-grid tbody td {
|
||||
line-height: 20px;
|
||||
padding: .4rem;
|
||||
}
|
||||
}
|
||||
|
||||
td:last-of-type {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.green {
|
||||
color: $positive-value;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: $negative-value;
|
||||
}
|
||||
|
||||
.current-value {
|
||||
font-size: $profile-current-value-font-size;
|
||||
}
|
||||
|
||||
.symbol-col {
|
||||
color: $grid-row-selection-bg;
|
||||
}
|
||||
|
||||
.k-icon {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
|
||||
&.icon-close {
|
||||
background: url('../images/cross-out.svg') center no-repeat;
|
||||
width: $profile-icon-size;
|
||||
height: $profile-icon-size;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-stocks-grid {
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.user-profile-wrapper {
|
||||
max-width: $profile-user-wrapper;
|
||||
|
||||
.profile-image {
|
||||
align-self: center;
|
||||
width: 2 * $profile-image-width;
|
||||
height: 2 * $profile-image-height;
|
||||
border-radius: 50%;
|
||||
background-image: url('../images/user.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: $profile-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
/*end profile*/
|
||||
|
||||
/*error ui*/
|
||||
|
||||
#blazor-error-ui {
|
||||
display: none;
|
||||
position: fixed;
|
||||
width: 30vw;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 999999;
|
||||
}
|
||||
|
||||
/*end error ui*/
|
||||
|
||||
/* collapse and exand button for the chart area */
|
||||
.collapse-expand-button {
|
||||
font-size: .875em;
|
||||
font-weight: bold;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
/* end collapse-expand buttons */
|
||||
|
||||
/* real-time data */
|
||||
.real-time-data {
|
||||
.k-grid {
|
||||
max-width: 100%;
|
||||
|
||||
tbody td {
|
||||
white-space: nowrap;
|
||||
line-height: 20px;
|
||||
padding: 0.4rem 0.4rem;
|
||||
}
|
||||
|
||||
.grid-price-cell {
|
||||
padding: 4px 12px;
|
||||
}
|
||||
|
||||
.price-up {
|
||||
color: $positive-value;
|
||||
}
|
||||
|
||||
.price-down {
|
||||
color: $negative-value;
|
||||
}
|
||||
}
|
||||
|
||||
.price-down,
|
||||
.price-up {
|
||||
animation-duration: 700ms;
|
||||
animation-name: blink;
|
||||
animation-iteration-count: 1;
|
||||
animation-direction: alternate;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: .4;
|
||||
}
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
font-size: 2em;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
/* end real-time data */
|
||||
|
||||
/* drpdowns background */
|
||||
|
||||
.ddl-no-bg.k-dropdown .k-dropdown-wrap {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.header .ddl-no-bg.k-dropdown .k-dropdown-wrap {
|
||||
color: rgba($white, .5);
|
||||
}
|
||||
|
||||
.stocks-grid .k-grid td:last-child {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.stocks-grid .k-grid td, .stocks-grid .k-grid th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
stocks-grid .k-grid .k-grid-header th.k-header {
|
||||
overflow: visible;
|
||||
white-space: normal;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.stocks-grid-buttons .k-widget.k-dropdown .k-dropdown-wrap {
|
||||
.k-select {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/* responsive layouts */
|
||||
@media screen and (max-width: 992px) {
|
||||
.daterange-input-wrap, .scale-blocks, .scale-wrap, .user-data {
|
||||
display: none !important
|
||||
}
|
||||
|
||||
app-navigation {
|
||||
-webkit-box-flex: 0 !important;
|
||||
flex-grow: 0 !important
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
.k-button-group, .profile-wrapper {
|
||||
display: none
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 1.8rem
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 769px) {
|
||||
.dropdownlist-add-new {
|
||||
padding-left: 16px !important
|
||||
}
|
||||
|
||||
.k-button-group {
|
||||
margin-top: 10px
|
||||
}
|
||||
|
||||
.profile-wrapper div:last-child {
|
||||
display: none
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
@using System.Net.Http
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.JSInterop
|
||||
@using BlazorFinancePortfolio.Client
|
||||
@using BlazorFinancePortfolio.Client.Shared
|
||||
@using Telerik.Blazor
|
||||
@using Telerik.Blazor.Components
|
|
@ -0,0 +1,13 @@
|
|||
[
|
||||
{
|
||||
"outputFile": "Styles/styles.css",
|
||||
"inputFile": "Styles/styles.scss",
|
||||
"minify": {
|
||||
"enabled": true
|
||||
},
|
||||
"includeInProject": true,
|
||||
"options": {
|
||||
"sourceMap": true
|
||||
}
|
||||
}
|
||||
]
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"defaultProvider": "unpkg",
|
||||
"libraries": [
|
||||
{
|
||||
"library": "@progress/kendo-theme-bootstrap@latest",
|
||||
"destination": "wwwroot/telerik-themes/bootstrap",
|
||||
"files": [
|
||||
"dist/all.css"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
const baseURL = '/blazor-stocks-portfolio/';
|
||||
const indexURL = '/blazor-stocks-portfolio/index.html';
|
||||
const networkFetchEvent = 'fetch';
|
||||
const swInstallEvent = 'install';
|
||||
const swInstalledEvent = 'installed';
|
||||
const swActivateEvent = 'activate';
|
||||
const staticCachePrefix = 'blazor-cache-v';
|
||||
const staticCacheName = 'blazor-cache-v21';
|
||||
const requiredFiles = [
|
||||
"/blazor-stocks-portfolio/_framework/blazor.boot.json",
|
||||
"/blazor-stocks-portfolio/_framework/blazor.webassembly.js",
|
||||
"/blazor-stocks-portfolio/_framework/wasm/mono.js",
|
||||
"/blazor-stocks-portfolio/_framework/wasm/mono.wasm",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/BlazorFinancePortfolio.Client.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/BlazorFinancePortfolio.Client.pdb",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/BlazorFinancePortfolio.Shared.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/BlazorFinancePortfolio.Shared.pdb",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Microsoft.AspNetCore.Blazor.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Microsoft.AspNetCore.Components.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Microsoft.AspNetCore.Components.Forms.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Microsoft.AspNetCore.Components.Web.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Microsoft.Bcl.AsyncInterfaces.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Microsoft.CSharp.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Microsoft.Extensions.DependencyInjection.Abstractions.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Microsoft.Extensions.DependencyInjection.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Microsoft.Extensions.Logging.Abstractions.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Microsoft.Extensions.Options.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Microsoft.Extensions.Primitives.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Microsoft.JSInterop.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Mono.Security.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Mono.WebAssembly.Interop.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/mscorlib.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/netstandard.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Newtonsoft.Json.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Buffers.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.ComponentModel.Annotations.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.ComponentModel.Composition.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.ComponentModel.DataAnnotations.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Core.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Data.DataSetExtensions.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Data.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Drawing.Common.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.IO.Compression.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.IO.Compression.FileSystem.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Memory.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Net.Http.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Numerics.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Numerics.Vectors.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Runtime.CompilerServices.Unsafe.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Runtime.Loader.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Runtime.Serialization.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.ServiceModel.Internals.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Text.Encodings.Web.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Text.Json.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Threading.Tasks.Extensions.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Transactions.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Xml.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/System.Xml.Linq.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Telerik.Blazor.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/Telerik.DataSource.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/WebAssembly.Bindings.dll",
|
||||
"/blazor-stocks-portfolio/_framework/_bin/WebAssembly.Net.Http.dll",
|
||||
"/blazor-stocks-portfolio/css/bootstrap/bootstrap.min.css",
|
||||
"/blazor-stocks-portfolio/css/bootstrap/bootstrap.min.css.map",
|
||||
"/blazor-stocks-portfolio/css/styles.min.css",
|
||||
"/blazor-stocks-portfolio/default-icon-192x192.png",
|
||||
"/blazor-stocks-portfolio/default-icon-512x512.png",
|
||||
"/blazor-stocks-portfolio/favicon.ico",
|
||||
"/blazor-stocks-portfolio/images/area.png",
|
||||
"/blazor-stocks-portfolio/images/candle.png",
|
||||
"/blazor-stocks-portfolio/images/cross-out.svg",
|
||||
"/blazor-stocks-portfolio/images/footer-bg.svg",
|
||||
"/blazor-stocks-portfolio/images/header-bg.svg",
|
||||
"/blazor-stocks-portfolio/images/line.png",
|
||||
"/blazor-stocks-portfolio/images/progress-logo.svg",
|
||||
"/blazor-stocks-portfolio/images/user.jpg",
|
||||
"/blazor-stocks-portfolio/index.html",
|
||||
"/blazor-stocks-portfolio/telerik-themes/bootstrap/dist/all.css",
|
||||
"/blazor-stocks-portfolio/windowResizeHandler.js",
|
||||
"/blazor-stocks-portfolio/ServiceWorkerRegister.js",
|
||||
"/blazor-stocks-portfolio/manifest.json"
|
||||
];
|
||||
// * listen for the install event and pre-cache anything in filesToCache * //
|
||||
self.addEventListener(swInstallEvent, event => {
|
||||
self.skipWaiting();
|
||||
event.waitUntil(
|
||||
caches.open(staticCacheName)
|
||||
.then(cache => {
|
||||
return cache.addAll(requiredFiles);
|
||||
})
|
||||
);
|
||||
});
|
||||
self.addEventListener(swActivateEvent, function (event) {
|
||||
event.waitUntil(
|
||||
caches.keys().then(function (cacheNames) {
|
||||
return Promise.all(
|
||||
cacheNames.map(function (cacheName) {
|
||||
if (staticCacheName !== cacheName && cacheName.startsWith(staticCachePrefix)) {
|
||||
return caches.delete(cacheName);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
self.addEventListener(networkFetchEvent, event => {
|
||||
const requestUrl = new URL(event.request.url);
|
||||
if (requestUrl.origin === location.origin) {
|
||||
if (requestUrl.pathname === baseURL) {
|
||||
event.respondWith(caches.match(indexURL));
|
||||
return;
|
||||
}
|
||||
}
|
||||
event.respondWith(
|
||||
caches.match(event.request)
|
||||
.then(response => {
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
return fetch(event.request)
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
if (requestUrl.origin === location.origin) {
|
||||
caches.open(staticCacheName).then(cache => {
|
||||
cache.put(event.request.url, response);
|
||||
});
|
||||
}
|
||||
}
|
||||
return response.clone();
|
||||
});
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
})
|
||||
);
|
||||
});
|
|
@ -0,0 +1,68 @@
|
|||
const serviceWorkerFileName = '/blazor-stocks-portfolio/ServiceWorker.js';
|
||||
const swInstalledEvent = 'installed';
|
||||
const staticCachePrefix = 'blazor-cache-v';
|
||||
const updateAlertMessage = 'Update available. Reload the page when convenient.';
|
||||
const blazorAssembly = 'BlazorFinancePortfolio.Client';
|
||||
const blazorInstallMethod = 'InstallPwaPrompt';
|
||||
window.updateAvailable = new Promise(function (resolve, reject) {
|
||||
var { hostname } = window.location;
|
||||
if (typeof ignoreHosts !== 'undefined') {
|
||||
if (ignoreHosts.includes(hostname)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register(serviceWorkerFileName)
|
||||
.then(function (registration) {
|
||||
console.log('Registration successful, scope is:', registration.scope);
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
installingWorker.onstatechange = () => {
|
||||
switch (installingWorker.state) {
|
||||
case swInstalledEvent:
|
||||
if (navigator.serviceWorker.controller) {
|
||||
resolve(true);
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error =>
|
||||
console.log('Service worker registration failed, error:', error));
|
||||
}
|
||||
});
|
||||
window['updateAvailable']
|
||||
.then(isAvailable => {
|
||||
if (isAvailable) {
|
||||
alert(updateAlertMessage);
|
||||
}
|
||||
});
|
||||
function showAddToHomeScreen() {
|
||||
DotNet.invokeMethodAsync(blazorAssembly, blazorInstallMethod)
|
||||
.then(function () { }, function (er) { setTimeout(showAddToHomeScreen, 1000); });
|
||||
}
|
||||
|
||||
window.BlazorPWA = {
|
||||
installPWA: function () {
|
||||
if (window.PWADeferredPrompt) {
|
||||
window.PWADeferredPrompt.prompt();
|
||||
window.PWADeferredPrompt.userChoice
|
||||
.then(function (choiceResult) {
|
||||
window.PWADeferredPrompt = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
window.addEventListener('beforeinstallprompt', function (e) {
|
||||
// Prevent Chrome 67 and earlier from automatically showing the prompt
|
||||
e.preventDefault();
|
||||
// Stash the event so it can be triggered later.
|
||||
window.PWADeferredPrompt = e;
|
||||
|
||||
showAddToHomeScreen();
|
||||
|
||||
});
|
После Ширина: | Высота: | Размер: 8.8 KiB |
После Ширина: | Высота: | Размер: 22 KiB |
После Ширина: | Высота: | Размер: 3.2 KiB |
После Ширина: | Высота: | Размер: 410 B |
После Ширина: | Высота: | Размер: 391 B |
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 224.512 224.512" style="enable-background:new 0 0 224.512 224.512;" xml:space="preserve">
|
||||
<g>
|
||||
<polygon style="fill:#010002;" points="224.507,6.997 217.521,0 112.256,105.258 6.998,0 0.005,6.997 105.263,112.254
|
||||
0.005,217.512 6.998,224.512 112.256,119.24 217.521,224.512 224.507,217.512 119.249,112.254 "/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 735 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1920" height="43" viewBox="0 0 1920 43"><defs><clipPath id="a"><rect width="1920" height="43" fill="#8d8d8d" style="mix-blend-mode:multiply;isolation:isolate"/></clipPath><linearGradient id="b" x1="0.546" y1="-0.009" x2="0.5" y2="0.968" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#1d2aa3"/><stop offset="1" stop-color="#425ad2"/></linearGradient><linearGradient id="c" x1="0.509" y1="-0.491" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#00b5dc" stop-opacity="0.58"/><stop offset="1" stop-color="#00b5dc" stop-opacity="0.141"/></linearGradient></defs><g clip-path="url(#a)"><g transform="translate(-52.239 -161.611)"><rect width="2108.063" height="132.002" transform="translate(43.918 116)" fill="url(#b)"/><g transform="translate(-169 -106.778)"><rect width="2415.489" height="875.609" fill="none"/><g transform="translate(22.014 99.518)"><path d="M122.273,222.71l331.388,86.9c36.969,9.695,78.433.593,101.458-22.269L722.827,120.821c26.134-25.949,75.317-33.715,114.275-18.045l223.589,89.936c22.977,9.242,50.537,10.609,75.076,3.725l252.566-70.857a120.42,120.42,0,0,1,66.847.81l564.346,174.317c31.328,9.677,67.65,5.491,93.153-10.738L2543.386,15.891c41.681-26.524,106.891-18.885,134.59,15.765l22.671,28.361c17.01,21.277,15.194,47.692-4.649,67.67L2191.2,635.935c-25.036,25.208-71.962,33.637-110.657,19.88l-225.292-80.1a119.822,119.822,0,0,0-74.595-1.192L1474.93,671.3a120.014,120.014,0,0,1-72.622-.513L935.132,514.945a120.236,120.236,0,0,0-60.237-3.664L630.451,559.063c-21.893,4.279-45.251,2.327-65.177-5.45L45.8,350.891C11.575,337.534-6.26,309.626,2,282.353l4.219-13.926c11.3-37.289,66-58.84,116.051-45.717" transform="translate(0 0)" fill="rgba(0,181,220,0.08)"/><path d="M2166.294,65.676a110.173,110.173,0,0,0-28.3,8.1L1800.385,223.556c-24.439,10.843-54.744,12.437-81.133,4.267l-239.419-74.118c-27.124-8.4-58.326-6.466-83.036,5.135L1107.168,294.832c-27.136,12.742-61.874,13.734-90.341,2.58L553.5,115.886c-22.877-8.963-50.135-10.185-74.395-3.335L57.045,231.718C26.832,240.249,5.66,260.017,2,283.119l-1.427,9C-4.8,325.941,28.411,356.68,75.49,361.468l476.135,48.419a108.679,108.679,0,0,1,41.18,12.4L868.417,573.966c26.433,14.547,62.09,17.125,92.053,6.658l366.846-128.152c25.7-8.979,55.9-8.439,80.936,1.447l306.671,121.066c39.473,15.583,89.018,7.245,114.658-19.3l230.014-238.108c18.5-19.151,50.266-29.415,82.565-26.68l433.87,36.75c48.844,4.138,92.915-21.243,97.733-56.284L2701.578,69.07c5.809-42.251-47.331-76.4-105.381-67.711Z" transform="translate(49.835 51.733)" fill="url(#c)"/></g></g></g></g></svg>
|
После Ширина: | Высота: | Размер: 2.6 KiB |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1920" height="160" viewBox="0 0 1920 160"><defs><style>.a{fill:#1159a7;}.b{clip-path:url(#a);}.c{fill:url(#b);}.d{fill:none;}.e{fill:rgba(0,181,220,0.08);}.f{fill:url(#c);}</style><clipPath id="a"><rect class="a" width="1920" height="160"/></clipPath><linearGradient id="b" x1="0.454" y1="-0.009" x2="0.5" y2="0.968" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#121b95"/><stop offset="1" stop-color="#425ad2"/></linearGradient><linearGradient id="c" x1="0.509" y1="-0.491" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#00b5dc" stop-opacity="0.58"/><stop offset="1" stop-color="#00b5dc" stop-opacity="0.141"/></linearGradient></defs><g class="b"><rect class="c" width="2108.063" height="1168.002" transform="translate(-179.742 -161.611)"/><g transform="translate(-443.25 -140.389)"><rect class="d" width="2415.489" height="875.609"/><g transform="translate(-167.986 49.518)"><path class="e" d="M122.273,222.71l331.388,86.9c36.969,9.695,78.433.593,101.458-22.269L722.827,120.821c26.134-25.949,75.317-33.715,114.275-18.045l223.589,89.936c22.977,9.242,50.537,10.609,75.076,3.725l252.566-70.857a120.42,120.42,0,0,1,66.847.81l564.346,174.317c31.328,9.677,67.65,5.491,93.153-10.738L2543.386,15.891c41.681-26.524,106.891-18.885,134.59,15.765l22.671,28.361c17.01,21.277,15.194,47.692-4.649,67.67L2191.2,635.935c-25.036,25.208-71.962,33.637-110.657,19.88l-225.292-80.1a119.822,119.822,0,0,0-74.595-1.192L1474.93,671.3a120.014,120.014,0,0,1-72.622-.513L935.132,514.945a120.236,120.236,0,0,0-60.237-3.664L630.451,559.063c-21.893,4.279-45.251,2.327-65.177-5.45L45.8,350.891C11.575,337.534-6.26,309.626,2,282.353l4.219-13.926c11.3-37.289,66-58.84,116.051-45.717" transform="translate(0 0)"/><path class="f" d="M2166.294,65.676a110.173,110.173,0,0,0-28.3,8.1L1800.385,223.556c-24.439,10.843-54.744,12.437-81.133,4.267l-239.419-74.118c-27.124-8.4-58.326-6.466-83.036,5.135L1107.168,294.832c-27.136,12.742-61.874,13.734-90.341,2.58L553.5,115.886c-22.877-8.963-50.135-10.185-74.395-3.335L57.045,231.718C26.832,240.249,5.66,260.017,2,283.119l-1.427,9C-4.8,325.941,28.411,356.68,75.49,361.468l476.135,48.419a108.679,108.679,0,0,1,41.18,12.4L868.417,573.966c26.433,14.547,62.09,17.125,92.053,6.658l366.846-128.152c25.7-8.979,55.9-8.439,80.936,1.447l306.671,121.066c39.473,15.583,89.018,7.245,114.658-19.3l230.014-238.108c18.5-19.151,50.266-29.415,82.565-26.68l433.87,36.75c48.844,4.138,92.915-21.243,97.733-56.284L2701.578,69.07c5.809-42.251-47.331-76.4-105.381-67.711Z" transform="translate(49.835 51.733)"/></g></g></g></svg>
|
После Ширина: | Высота: | Размер: 2.6 KiB |
После Ширина: | Высота: | Размер: 522 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="89.433" height="20.85" viewBox="0 0 89.433 20.85"><defs><style>.a{fill:#5ce500;}.b{fill:#fff;}</style></defs><path class="a" d="M16.722,13.973a.677.677,0,0,1-.312.536L12.075,17V7.378L3.715,2.563,8.051.068a.685.685,0,0,1,.624,0L16.722,4.7ZM10.217,8.45l-5.725-3.3a.706.706,0,0,0-.624,0L0,7.378l6.037,3.477V17.81L9.905,15.58a.7.7,0,0,0,.312-.539ZM0,14.333l4.176,2.406V11.923Z" transform="translate(0 0.007)"/><g transform="translate(21.164 4.675)"><path class="b" d="M66.218,13.62H61.66V25.958h2.021V21.117h2.551c2.759,0,4.28-1.348,4.28-3.8,0-1.113-.416-3.7-4.294-3.7m2.242,3.733c0,1.39-.721,1.958-2.492,1.958H63.684V15.443h2.551c1.5,0,2.225.624,2.225,1.91m8.072-.381.152.073-.343,1.823-.236-.073a2,2,0,0,0-.586-.09c-1.889,0-2.041,1.5-2.041,3.324v3.93H71.607V16.9h1.757V17.99a2.415,2.415,0,0,1,1.816-1.206,2.518,2.518,0,0,1,1.352.187m4.6-.256a4,4,0,0,0-3.414,1.622,5.452,5.452,0,0,0-.915,3.105c0,2.863,1.7,4.713,4.329,4.713,3.2,0,4.329-2.575,4.329-4.779a5.074,5.074,0,0,0-1.057-3.241,4.088,4.088,0,0,0-3.272-1.421m0,7.77c-1.466,0-2.374-1.165-2.374-3.04,0-1.9.911-3.074,2.374-3.074s2.391,1.192,2.391,3.04c0,1.9-.915,3.074-2.391,3.074M101.2,16.971l.152.073-.343,1.823-.236-.073a2,2,0,0,0-.582-.09c-1.889,0-2.041,1.5-2.041,3.324v3.93H96.276V16.9h1.757V17.99a2.415,2.415,0,0,1,1.816-1.206,2.51,2.51,0,0,1,1.348.187m6.724,6.082-.035.1a1.993,1.993,0,0,1-2.041,1.348,2.345,2.345,0,0,1-2.416-2.468h6.4l.024-.177a5.531,5.531,0,0,0,.021-.721c0-.059,0-.111,0-.163a4.057,4.057,0,0,0-4.114-4.239,3.907,3.907,0,0,0-3.279,1.573,5.391,5.391,0,0,0-.97,3.157c0,2.8,1.74,4.682,4.332,4.682a3.72,3.72,0,0,0,3.889-2.866l.059-.222h-1.872Zm-4.429-2.644a2.176,2.176,0,0,1,2.239-2.052,2.132,2.132,0,0,1,2.194,2.052Zm-10.63-2.752a3.2,3.2,0,0,0-2.409-.943c-2.748,0-4,2.416-4,4.661,0,2.3,1.237,4.63,4,4.63a3.2,3.2,0,0,0,2.336-.95c-.007.4-.017.78-.028.943a2.023,2.023,0,0,1-2.26,2.19c-.783,0-1.681-.277-1.854-1.057l-.038-.173H86.766l.031.246c.194,1.6,1.594,2.589,3.66,2.589a3.8,3.8,0,0,0,3.726-1.972,6.371,6.371,0,0,0,.454-2.693V16.9H92.866v.759Zm-2.291,6.679c-.655,0-2.177-.3-2.177-3.04,0-1.8.853-2.925,2.225-2.925,1.061,0,2.194.769,2.194,2.925,0,1.9-.839,3.04-2.242,3.04m27.955-1.106c0,1.442-1.2,2.894-3.868,2.894-2.364,0-3.771-1.022-3.958-2.88l-.021-.191h1.844l.028.139c.225,1.116,1.289,1.352,2.142,1.352.936,0,1.934-.3,1.934-1.147,0-.426-.277-.731-.828-.9-.326-.1-.724-.208-1.151-.329-.728-.2-1.483-.416-1.948-.575a2.382,2.382,0,0,1-1.809-2.2c0-1.826,1.778-2.644,3.539-2.644,2.357,0,3.577.887,3.729,2.714l.014.187h-1.816l-.024-.142c-.184-1.05-1.227-1.206-1.833-1.206-.523,0-1.743.094-1.743.963,0,.4.336.686,1.033.887.208.059.516.139.87.229.738.191,1.66.43,2.125.589a2.276,2.276,0,0,1,1.743,2.263m8.647,0c0,1.442-1.2,2.894-3.868,2.894-2.364,0-3.771-1.022-3.958-2.88l-.021-.191h1.844l.028.139c.225,1.116,1.289,1.352,2.142,1.352.936,0,1.934-.3,1.934-1.147,0-.426-.277-.731-.828-.9-.326-.1-.728-.208-1.151-.329-.728-.2-1.483-.416-1.944-.575a2.382,2.382,0,0,1-1.809-2.2c0-1.826,1.778-2.644,3.539-2.644,2.357,0,3.577.887,3.729,2.714l.017.187h-1.816l-.024-.142c-.184-1.05-1.227-1.206-1.833-1.206-.523,0-1.743.094-1.743.963,0,.4.336.686,1.033.887.208.059.516.139.873.229.738.191,1.66.43,2.125.589a2.278,2.278,0,0,1,1.733,2.263" transform="translate(-61.66 -13.62)"/><path class="b" d="M252.555,21.019a1.289,1.289,0,1,1,1.275-1.289,1.266,1.266,0,0,1-1.275,1.289m0-2.36a1.071,1.071,0,1,0,1.043,1.071,1.034,1.034,0,0,0-1.043-1.071m-.149,1.31v.43h-.381V19.043h.634a.452.452,0,0,1,.5.471.4.4,0,0,1-.26.409l.308.471h-.423l-.26-.43h-.118Zm.211-.607h-.211v.3h.211c.114,0,.177-.055.177-.149s-.062-.152-.177-.152" transform="translate(-185.562 -16.769)"/></g></svg>
|
После Ширина: | Высота: | Размер: 3.6 KiB |
После Ширина: | Высота: | Размер: 134 KiB |
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Telerik Blazor Stocks Portfolio</title>
|
||||
<base href="/blazor-stocks-portfolio/" />
|
||||
<link href="favicon.ico" rel="icon" type="image/x-icon" />
|
||||
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="css/styles.min.css" rel="stylesheet" />
|
||||
<link href="telerik-themes/bootstrap/dist/all.css" rel="stylesheet" />
|
||||
<script src="_content/telerik.ui.for.blazor.trial/js/telerik-blazor.js" defer></script>
|
||||
<script src="ServiceWorkerRegister.js"></script>
|
||||
<link href="manifest.json" rel="manifest" />
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Ubuntu&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<app>
|
||||
<div class="loading-section">
|
||||
<h2>Telerik Blazor Stocks Portfolio</h2>
|
||||
<div class="short-description">You can review and manage your stocks and their performance. Please wait while the app initializes.</div>
|
||||
<div class="loader mt-5">
|
||||
<div class="loader-dot dot1"></div>
|
||||
<div class="loader-dot dot2"></div>
|
||||
<div class="loader-dot dot3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</app>
|
||||
|
||||
<div id="blazor-error-ui" class="alert alert-warning text-center py-3">
|
||||
An error has occurred. This application may no longer respond until reloaded.
|
||||
<div class="pt-3">
|
||||
<a href class="reload btn-warning p-2"><span class="oi oi-reload"></span> Reload</a>
|
||||
</div>
|
||||
</div>
|
||||
<script src="_framework/blazor.webassembly.js"></script>
|
||||
<script src="windowResizeHandler.js" defer></script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"short_name": "My Stocks",
|
||||
"name": "My Blazor Stocks PWA",
|
||||
"start_url": "/blazor-stocks-portfolio/",
|
||||
"display": "standalone",
|
||||
"icons": [
|
||||
{
|
||||
"src":"/blazor-stocks-portfolio/default-icon-192x192.png",
|
||||
"type":"image/png",
|
||||
"sizes":"192x192"
|
||||
},
|
||||
{
|
||||
"src":"/blazor-stocks-portfolio/default-icon-512x512.png",
|
||||
"type":"image/png",
|
||||
"sizes":"512x512"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
function raiseResizeEvent() {
|
||||
var namespace = 'BlazorFinancePortfolio.Shared';
|
||||
var method = 'RaiseWindowResizeEvent';
|
||||
DotNet.invokeMethodAsync(namespace, method, Math.floor(window.innerWidth), Math.floor(window.innerHeight))
|
||||
.then(function () { },
|
||||
function (er) { /* .net dispatches is probably not initialized yet, or the namespace/method name are wrong */ });
|
||||
}
|
||||
|
||||
//throttle resize event, taken from https://stackoverflow.com/a/668185/812369
|
||||
var timeout = false;
|
||||
window.addEventListener("resize", function () {
|
||||
if (timeout !== false)
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(raiseResizeEvent, 200);
|
||||
});
|
||||
|
||||
function getWindowWidth() {
|
||||
return window.innerWidth;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Blazor.Server" Version="3.1.0-preview4.19579.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Client\BlazorFinancePortfolio.Client.csproj" />
|
||||
<ProjectReference Include="..\Shared\BlazorFinancePortfolio.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,22 @@
|
|||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace BlazorFinancePortfolio.Server
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
BuildWebHost(args).Run();
|
||||
}
|
||||
|
||||
public static IWebHost BuildWebHost(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseConfiguration(new ConfigurationBuilder()
|
||||
.AddCommandLine(args)
|
||||
.Build())
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:65063/blazor-stocks-portfolio/",
|
||||
"sslPort": 44359
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"BlazorFinancePortfolio.Server": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:5001/blazor-stocks-portfolio/;http://localhost:5000/blazor-stocks-portfolio/"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.ResponseCompression;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System.Linq;
|
||||
|
||||
namespace BlazorFinancePortfolio.Server
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc();
|
||||
services.AddResponseCompression(opts =>
|
||||
{
|
||||
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
|
||||
new[] { "application/octet-stream" });
|
||||
});
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
app.UseResponseCompression();
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseBlazorDebugging();
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseClientSideBlazorFiles<Client.Startup>();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapDefaultControllerRoute();
|
||||
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Telerik.UI.for.Blazor.Trial" Version="2.6.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,18 @@
|
|||
namespace BlazorFinancePortfolio.Helpers
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public const int MS_1_DAY = 86400000;
|
||||
|
||||
public const int MS_5_MINUTES = MS_1_DAY / 24 / 12;
|
||||
public const int MS_15_MINUTES = MS_1_DAY / 24 / 4;
|
||||
public const int MS_30_MINUTES = MS_1_DAY / 24 / 2;
|
||||
public const int MS_1_HOUR = MS_1_DAY / 24;
|
||||
public const int MS_4_HOURS = MS_1_DAY / 6;
|
||||
public const int MS_12_HOURS = MS_1_DAY / 2;
|
||||
public const int MS_4_DAYS = MS_1_DAY * 4;
|
||||
public const int MS_1_WEEK = MS_1_DAY * 7;
|
||||
|
||||
public const int MaxLabelStepsInStocksChart = 15;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
|
||||
namespace BlazorFinancePortfolio.Helpers
|
||||
{
|
||||
public class NumbersHelper
|
||||
{
|
||||
public static string FormatDecimal(decimal num)
|
||||
{
|
||||
// Ensure number has max 3 significant digits (no rounding up can happen)
|
||||
long i = (long)Math.Pow(10, (int)Math.Max(0, Math.Log10(Convert.ToDouble(num)) - 2));
|
||||
|
||||
if (i != 0)
|
||||
{
|
||||
num = num / i * i;
|
||||
}
|
||||
|
||||
if (num >= 1000000000)
|
||||
return (num / 1000000000).ToString("0.##") + "B";
|
||||
if (num >= 1000000)
|
||||
return (num / 1000000).ToString("0.##") + "M";
|
||||
if (num >= 1000)
|
||||
return (num / 1000).ToString("0.##") + "K";
|
||||
|
||||
return num.ToString("0.##");
|
||||
}
|
||||
|
||||
public static string GetChangeNumberClass(decimal value)
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
return "grid-cell-positive";
|
||||
}
|
||||
else if (value < 0)
|
||||
{
|
||||
return "grid-cell-negative";
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BlazorFinancePortfolio.Helpers
|
||||
{
|
||||
public static class WindowResizeDispatcher
|
||||
{
|
||||
public static event Func<Task> WindowResize;
|
||||
public static int? WindowWidth { get; set; }
|
||||
public static int? WindowHeight { get; set; }
|
||||
|
||||
[JSInvokable]
|
||||
public static async Task RaiseWindowResizeEvent(int width, int height)
|
||||
{
|
||||
WindowWidth = width;
|
||||
WindowHeight = height;
|
||||
try
|
||||
{
|
||||
await WindowResize?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//something went wrong with the handlers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Telerik.Blazor;
|
||||
|
||||
namespace BlazorFinancePortfolio.Models
|
||||
{
|
||||
public class ChartTypeList
|
||||
{
|
||||
public ChartSeriesType Value { get; set; }
|
||||
public string Text { get { return Value.ToString(); } }
|
||||
|
||||
public static List<ChartTypeList> GetAvailableSeriesTypes()
|
||||
{
|
||||
return new List<ChartTypeList>()
|
||||
{
|
||||
new ChartTypeList { Value = ChartSeriesType.Area },
|
||||
new ChartTypeList { Value = ChartSeriesType.Line }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
namespace BlazorFinancePortfolio.Models
|
||||
{
|
||||
public class Currency
|
||||
{
|
||||
public string Symbol { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public decimal Multiplier { get; set; }
|
||||
|
||||
public string Sign { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using Telerik.Blazor;
|
||||
|
||||
namespace BlazorFinancePortfolio.Models
|
||||
{
|
||||
public class Interval
|
||||
{
|
||||
public ChartCategoryAxisBaseUnit Unit { get; set; }
|
||||
|
||||
public int Step { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using BlazorFinancePortfolio.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Telerik.Blazor;
|
||||
|
||||
namespace BlazorFinancePortfolio.Models
|
||||
{
|
||||
public class IntervalFilter
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Interval Interval { get; set; }
|
||||
public long Duration { get; set; }
|
||||
|
||||
public static List<IntervalFilter> GetIntervalFilters()
|
||||
{
|
||||
return new List<IntervalFilter>()
|
||||
{
|
||||
new IntervalFilter { Name = "5M", Interval = new Interval { Unit = ChartCategoryAxisBaseUnit.Minutes, Step = 5 }, Duration = Constants.MS_5_MINUTES },
|
||||
new IntervalFilter { Name = "15M", Interval = new Interval { Unit = ChartCategoryAxisBaseUnit.Minutes, Step = 15 }, Duration = Constants.MS_15_MINUTES },
|
||||
new IntervalFilter { Name = "30M", Interval = new Interval { Unit = ChartCategoryAxisBaseUnit.Minutes, Step = 30 }, Duration = Constants.MS_30_MINUTES },
|
||||
new IntervalFilter { Name = "1H", Interval = new Interval { Unit = ChartCategoryAxisBaseUnit.Hours, Step = 1 }, Duration = Constants.MS_1_HOUR },
|
||||
new IntervalFilter { Name = "4H", Interval = new Interval { Unit = ChartCategoryAxisBaseUnit.Hours, Step = 4 }, Duration = Constants.MS_4_HOURS },
|
||||
new IntervalFilter { Name = "1D", Interval = new Interval { Unit = ChartCategoryAxisBaseUnit.Days, Step = 1 }, Duration = Constants.MS_1_DAY },
|
||||
new IntervalFilter { Name = "1W", Interval = new Interval { Unit = ChartCategoryAxisBaseUnit.Weeks, Step = 1 }, Duration = Constants.MS_1_WEEK }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlazorFinancePortfolio.Models
|
||||
{
|
||||
public class RealTimeData
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Symbol { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public decimal Change { get; set; }
|
||||
public string StockExchangeLong { get; set; }
|
||||
public string StockExchangeShort { get; set; }
|
||||
public string TimeZone { get; set; }
|
||||
public string TimeZoneName { get; set; }
|
||||
public decimal YearHigh { get; set; }
|
||||
public decimal YearLow { get; set; }
|
||||
public decimal Volume { get; set; }
|
||||
public decimal MarketCap { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BlazorFinancePortfolio.Models
|
||||
{
|
||||
public class Stock
|
||||
{
|
||||
public string Symbol { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public decimal Price { get; set; }
|
||||
|
||||
public decimal DayChange { get; set; }
|
||||
|
||||
public decimal ChangePercentage { get; set; }
|
||||
|
||||
public decimal Volume { get; set; }
|
||||
|
||||
public decimal VolumeAvg { get; set; }
|
||||
|
||||
public decimal MarketCap { get; set; }
|
||||
|
||||
public decimal? PricePerEarningRatio { get; set; }
|
||||
|
||||
public bool IsCategorized { get; set; }
|
||||
|
||||
public IEnumerable<decimal> IntraDayChart { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
|
||||
namespace BlazorFinancePortfolio.Models
|
||||
{
|
||||
public class StockIntervalDetails
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
public decimal Open { get; set; }
|
||||
|
||||
public decimal Close { get; set; }
|
||||
|
||||
public decimal High { get; set; }
|
||||
|
||||
public decimal Low { get; set; }
|
||||
|
||||
public decimal Volume { get; set; }
|
||||
|
||||
public string ColumnColor { get; set; } // return currentLargerThenPrev ? '#5CB85C' : '#FF6358';
|
||||
|
||||
public StockIntervalDetails Clone()
|
||||
{
|
||||
return new StockIntervalDetails
|
||||
{
|
||||
Date = new DateTime(this.Date.Ticks),
|
||||
Close = this.Close,
|
||||
ColumnColor = this.ColumnColor,
|
||||
High = this.High,
|
||||
Low = this.Low,
|
||||
Open = this.Open,
|
||||
Volume = this.Volume
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlazorFinancePortfolio.Models
|
||||
{
|
||||
public class TimeFilter
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public long Duration { get; set; }
|
||||
|
||||
private static int MS_PER_DAY = 86400000;
|
||||
|
||||
public static List<TimeFilter> GetFilters()
|
||||
{
|
||||
return new List<TimeFilter>()
|
||||
{
|
||||
new TimeFilter { Name = "1H", Duration = MS_PER_DAY / 24 },
|
||||
new TimeFilter { Name = "4H", Duration = MS_PER_DAY / 6 },
|
||||
new TimeFilter { Name = "12H",Duration = MS_PER_DAY / 2 },
|
||||
new TimeFilter { Name = "1D", Duration = MS_PER_DAY },
|
||||
new TimeFilter { Name = "4D", Duration = MS_PER_DAY * 4 },
|
||||
new TimeFilter { Name = "1W", Duration = MS_PER_DAY * 7 }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using BlazorFinancePortfolio.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlazorFinancePortfolio.Services
|
||||
{
|
||||
public class CurrenciesService
|
||||
{
|
||||
public Task<List<Currency>> GetCurrenciesAsync()
|
||||
{
|
||||
var data = new List<Currency>();
|
||||
data.Add(new Currency { Multiplier = 1, Name = "United States Dollar", Symbol = "USD", Sign = "$" });
|
||||
data.Add(new Currency { Multiplier = 0.9m, Name = "Euro", Symbol = "EUR", Sign = "€" });
|
||||
data.Add(new Currency { Multiplier = 0.76m, Name = "Pound sterling", Symbol = "GBP", Sign = "£" });
|
||||
|
||||
return Task.FromResult(data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorFinancePortfolio.Models;
|
||||
|
||||
namespace BlazorFinancePortfolio.Services
|
||||
{
|
||||
public class RealTimeDataService
|
||||
{
|
||||
private static Random Rnd { get; set; } = new Random();
|
||||
|
||||
public decimal GetRandomChange()
|
||||
{
|
||||
double currRandom = Rnd.NextDouble();
|
||||
double change = currRandom > 0.5 ? currRandom > 0.75 ? -Rnd.NextDouble() * 2 : Rnd.NextDouble() * 2 : 0;
|
||||
return (decimal)change;
|
||||
}
|
||||
|
||||
private string GetSymbol()
|
||||
{
|
||||
string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
string symbol = string.Empty;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
symbol += letters[(int)Math.Floor(Rnd.NextDouble() * 26)];
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
public Task<List<RealTimeData>> GetInitialData(string currency)
|
||||
{
|
||||
List<RealTimeData> data = new List<RealTimeData>();
|
||||
decimal price = (decimal)(Rnd.NextDouble() * 100 + 10);
|
||||
|
||||
for (int i = 1; i < 10001; i++)
|
||||
{
|
||||
string symbol = GetSymbol();
|
||||
data.Add(new RealTimeData
|
||||
{
|
||||
Id = i,
|
||||
Symbol = symbol,
|
||||
Name = symbol + " Inc.",
|
||||
Currency = currency,
|
||||
Price = price,
|
||||
Change = GetRandomChange(),
|
||||
StockExchangeLong = "New York Stock Exchange",
|
||||
StockExchangeShort = "NYSE",
|
||||
TimeZone = "EDT",
|
||||
TimeZoneName = "America/New_York",
|
||||
YearHigh = price + price / 3,
|
||||
YearLow = price - price / 3,
|
||||
Volume = (decimal)(21774241 * Rnd.NextDouble() * 50),
|
||||
MarketCap = (decimal)(229956956 * Rnd.NextDouble() * 50)
|
||||
});
|
||||
}
|
||||
|
||||
return Task.FromResult(data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,665 @@
|
|||
using BlazorFinancePortfolio.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlazorFinancePortfolio.Services
|
||||
{
|
||||
public class StocksListService
|
||||
{
|
||||
private List<Stock> UserStocks { get; set; }
|
||||
|
||||
public Task<List<Stock>>GetStocks(bool isCategorized)
|
||||
{
|
||||
if (UserStocks == null)
|
||||
{
|
||||
UserStocks = GetInitialStocks();
|
||||
}
|
||||
|
||||
var categorizedStocks = UserStocks.Where(s => s.IsCategorized == isCategorized).ToList();
|
||||
|
||||
return Task.FromResult(categorizedStocks);
|
||||
}
|
||||
|
||||
public List<StockIntervalDetails> GenerateStockIntervals(Stock stock, int intervalInMinutes, DateTime start, DateTime end)
|
||||
{
|
||||
var stocks = new List<StockIntervalDetails>
|
||||
{
|
||||
new StockIntervalDetails
|
||||
{
|
||||
Open = stock.Price - 0.1m,
|
||||
Close = stock.Price,
|
||||
ColumnColor = "#5CB85C",
|
||||
Date = start,
|
||||
High = stock.Price + 1.1m,
|
||||
Low = stock.Price - 2.2m,
|
||||
Volume = stock.Price * 10000
|
||||
}
|
||||
};
|
||||
var startIterator = start.AddMinutes(intervalInMinutes);
|
||||
|
||||
var random = new Random();
|
||||
|
||||
var counter = 1;
|
||||
while (startIterator < end)
|
||||
{
|
||||
var prevInterval = stocks[counter - 1];
|
||||
// get random volatility from -3% to 3% for each N(intervalInMinutes) minutes
|
||||
var randomVolatility = random.Next(-30, 30) * 0.001m;
|
||||
|
||||
var randomVolume = random.Next(100, 10000);
|
||||
var randomHighPercentage = random.Next(100, 130) * 0.01m;
|
||||
var randomLowPercentage = random.Next(70, 100) * 0.01m;
|
||||
|
||||
var change = prevInterval.Close * randomVolatility;
|
||||
var newPrice = prevInterval.Close + change;
|
||||
var high = newPrice * randomHighPercentage;
|
||||
var low = newPrice * randomLowPercentage;
|
||||
var stockToAdd = new StockIntervalDetails
|
||||
{
|
||||
Close = newPrice,
|
||||
Date = startIterator,
|
||||
Open = prevInterval.Close,
|
||||
High = high,
|
||||
Low = low,
|
||||
Volume = newPrice * randomVolume
|
||||
};
|
||||
|
||||
stockToAdd.ColumnColor = stockToAdd.Volume >= prevInterval.Volume ? "#5CB85C" : "#FF6358";
|
||||
stocks.Add(stockToAdd);
|
||||
|
||||
counter++;
|
||||
startIterator = startIterator.AddMinutes(intervalInMinutes);
|
||||
}
|
||||
|
||||
return stocks;
|
||||
}
|
||||
|
||||
public async Task<Stock> RemoveStock(Stock stockToRemove)
|
||||
{
|
||||
var matchingItem = UserStocks.FirstOrDefault(s => s.Symbol == stockToRemove.Symbol);
|
||||
if (matchingItem != null)
|
||||
{
|
||||
matchingItem.IsCategorized = false;
|
||||
return await Task.FromResult(matchingItem);
|
||||
}
|
||||
|
||||
return await Task.FromResult(matchingItem);
|
||||
}
|
||||
|
||||
public async Task<Stock> AddStock(Stock stockToAdd)
|
||||
{
|
||||
var matchingItem = UserStocks.FirstOrDefault(s => s.Symbol == stockToAdd.Symbol);
|
||||
if (matchingItem != null)
|
||||
{
|
||||
matchingItem.IsCategorized = true;
|
||||
}
|
||||
|
||||
return await Task.FromResult(matchingItem);
|
||||
}
|
||||
|
||||
private List<Stock> GetInitialStocks()
|
||||
{
|
||||
var stockList = new List<Stock>();
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "AAN",
|
||||
Name = "Aaron's, Inc.",
|
||||
Price = 76.61m,
|
||||
DayChange = -1.18m,
|
||||
ChangePercentage = -1.52m,
|
||||
Volume = 710442,
|
||||
VolumeAvg = 837114,
|
||||
MarketCap = 5174814208,
|
||||
IsCategorized = true,
|
||||
PricePerEarningRatio = 25.94m,
|
||||
IntraDayChart = new List<decimal> { 77.77m, 77.48m, 77.47m, 77.22m, 77.29m, 76.9m, 76.69m, 76.65m, 76.69m, 76.82m, 76.75m, 76.81m, 76.87m, 76.84m, 76.7m, 76.65m, 76.44m, 76.29m, 76.42m, 76.32m, 76.2m, 75.94m, 75.87m, 75.8m, 75.99m, 76.09m, 75.8m, 75.82m, 75.91m, 75.84m, 75.85m, 76.01m, 75.99m, 75.92m, 75.99m, 76.06m, 76.06m, 76.11m, 76.17m, 76.18m, 76.17m, 76.13m, 76.04m, 75.88m, 75.72m, 75.69m, 75.92m, 75.99m, 76.04m, 76.07m, 76.03m, 75.95m, 75.75m, 75.58m, 75.8m, 75.87m, 75.93m, 76.08m, 75.95m, 76.01m, 76.05m, 76.07m, 76.16m, 76.21m, 76.2m, 76.38m, 76.41m, 76.43m, 76.38m, 76.51m, 76.7m, 76.65m, 76.71m, 76.68m, 76.65m, 76.46m, 76.53m, 76.59m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "AAPL",
|
||||
Name = "Apple Inc.",
|
||||
Price = 246.58m,
|
||||
DayChange = 2.49m,
|
||||
ChangePercentage = 1.02m,
|
||||
Volume = 15827692,
|
||||
VolumeAvg = 20028962,
|
||||
MarketCap = 1114344259584,
|
||||
IsCategorized = true,
|
||||
PricePerEarningRatio = 20.94m,
|
||||
IntraDayChart = new List<decimal> { 243.75m, 243.54m, 243.32m, 243.47m, 243.74m, 243.43m, 243.34m, 243.39m, 243.47m, 243.66m, 243.68m, 244.43m, 244.53m, 244.25m, 244.16m, 243.93m, 244.41m, 244.69m, 244.52m, 244.52m, 244.62m, 244.88m, 245.07m, 245.7m, 245.31m, 245.34m, 245.48m, 245.54m, 245.28m, 245.43m, 245.41m, 245.2m, 245.33m, 245.31m, 245.34m, 245.56m, 245.59m, 245.47m, 245.1m, 245.18m, 245.29m, 245.24m, 245.35m, 245.26m, 245.16m, 245.38m, 245.31m, 245.3m, 245.3m, 245.25m, 245.39m, 245.45m, 245.38m, 245.37m, 245.25m, 244.81m, 245.05m, 245.07m, 245.1m, 245.2m, 245.18m, 245.13m, 245.18m, 245.35m, 245.34m, 245.31m, 245.39m, 245.46m, 245.57m, 245.65m, 245.67m, 245.76m, 245.68m, 245.77m, 245.79m, 245.86m, 245.9m, 246.24m }
|
||||
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
|
||||
Symbol = "ACN",
|
||||
Name = "Accenture plc",
|
||||
Price = 183.07m,
|
||||
DayChange = -0.77m,
|
||||
ChangePercentage = -0.42m,
|
||||
Volume = 1369124,
|
||||
VolumeAvg = 1892150,
|
||||
MarketCap = 116597284864,
|
||||
IsCategorized = true,
|
||||
PricePerEarningRatio = 24.87m,
|
||||
IntraDayChart = new List<decimal> { 184.08m, 184.36m, 183.49m, 183.77m, 183.74m, 183.57m, 183.62m, 183.76m, 184.13m, 183.95m, 183.99m, 184.16m, 184.05m, 183.85m, 183.81m, 183.84m, 184.4m, 184.34m, 184.4m, 184.32m, 184.24m, 184.45m, 184.5m, 184.54m, 184.52m, 184.55m, 184.58m, 184.71m, 184.63m, 184.74m, 184.58m, 184.29m, 184.13m, 184.12m, 184.1m, 184.11m, 184.21m, 184.21m, 184.11m, 184.05m, 184.06m, 184m, 183.94m, 183.9m, 183.89m, 183.9m, 183.94m, 183.83m, 183.9m, 183.75m, 183.73m, 183.78m, 183.78m, 183.92m, 183.9m, 183.75m, 183.87m, 183.87m, 183.83m, 183.81m, 183.7m, 183.4m, 183.42m, 183.53m, 183.53m, 183.6m, 183.64m, 183.5m, 183.46m, 183.51m, 183.54m, 183.62m, 183.59m, 183.66m, 183.54m, 183.4m, 183.24m, 183.31m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "ADBE",
|
||||
Name = "Adobe Inc.",
|
||||
Price = 270.98m,
|
||||
DayChange = 2.93m,
|
||||
ChangePercentage = 1.09m,
|
||||
Volume = 1511852,
|
||||
VolumeAvg = 3342325,
|
||||
MarketCap = 131175735296,
|
||||
IsCategorized = true,
|
||||
PricePerEarningRatio = 48.22m,
|
||||
IntraDayChart = new List<decimal> { 267.66m, 267.8m, 268.63m, 269.04m, 269.44m, 269.13m, 269.3m, 269.95m, 270.13m, 269.81m, 269.9m, 270.2m, 270.35m, 270.2m, 270.14m, 270.33m, 271.13m, 270.73m, 270.55m, 270.31m, 270.46m, 271.06m, 271.54m, 271.31m, 270.86m, 270.97m, 271.18m, 271.33m, 271.16m, 271.29m, 271.11m, 270.73m, 270.61m, 270.82m, 271.02m, 270.95m, 271.18m, 271.07m, 270.96m, 271.03m, 271.02m, 270.95m, 270.91m, 270.93m, 270.88m, 271.06m, 271.03m, 270.89m, 271.02m, 270.95m, 271.04m, 270.74m, 270.86m, 270.57m, 270.85m, 270.68m, 270.63m, 270.59m, 270.59m, 270.7m, 270.82m, 270.35m, 270.53m, 270.73m, 270.76m, 271m, 271.06m, 270.64m, 270.65m, 270.79m, 270.88m, 270.88m, 270.9m, 271.02m, 270.77m, 270.72m, 270.68m, 270.59m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "AGM",
|
||||
Name = "Federal Agricultural Mortgage Corporation",
|
||||
Price = 84.57m,
|
||||
DayChange = 0.17m,
|
||||
ChangePercentage = 0.2m,
|
||||
Volume = 22444,
|
||||
VolumeAvg = 22114,
|
||||
MarketCap = 890445952,
|
||||
IsCategorized = true,
|
||||
PricePerEarningRatio = 9.46m,
|
||||
IntraDayChart = new List<decimal> { 84.42m, 84.87m, 84.87m, 84.02m, 84.02m, 84.18m, 84.11m, 83.5m, 82.7m, 82.7m, 82.97m, 82.97m, 82.97m, 83.08m, 83.08m, 83.29m, 83.29m, 83.01m, 83.01m, 83.21m, 83.21m, 83.18m, 83.38m, 83.08m, 83.08m, 83.34m, 83.34m, 83.39m, 83.39m, 83.35m, 83.35m, 83.32m, 83.32m, 83.28m, 83.28m, 83.28m, 83.58m, 83.58m, 83.58m, 83.26m, 83.26m, 83.49m, 83.49m, 84.52m, 84.52m, 84.1m, 84.1m, 84.1m, 83.41m, 84.77m, 83.53m, 83.53m, 83.92m, 83.92m, 83.76m, 83.76m, 84.44m, 84.44m, 84.44m, 84.33m, 84.72m, 84.59m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "AMZN",
|
||||
Name = "Amazon.com, Inc.",
|
||||
Price = 1779.99m,
|
||||
DayChange = 17.78m,
|
||||
ChangePercentage = 1.01m,
|
||||
Volume = 2173743,
|
||||
VolumeAvg = 3771314,
|
||||
MarketCap = 882513674240,
|
||||
IsCategorized = true,
|
||||
PricePerEarningRatio = 78.87m,
|
||||
IntraDayChart = new List<decimal> { 1762.22m, 1762m, 1763.11m, 1768.61m, 1768.02m, 1766.44m, 1764.64m, 1766.46m, 1767.14m, 1768.17m, 1767.75m, 1769.02m, 1768.68m, 1771.99m, 1771.46m, 1774.6m, 1778.5m, 1778.76m, 1776.4m, 1773.73m, 1774.49m, 1771.65m, 1772.55m, 1773.22m, 1773.5m, 1770.94m, 1769.19m, 1770.69m, 1771.39m, 1772.2m, 1770.41m, 1771.65m, 1769.62m, 1769.5m, 1769.54m, 1768.72m, 1768.31m, 1767.99m, 1767.33m, 1766.14m, 1765.66m, 1765.45m, 1765.79m, 1765.99m, 1767.82m, 1767.14m, 1768.4m, 1768.29m, 1767.83m, 1767.51m, 1769.12m, 1767.93m, 1768.07m, 1768.5m, 1769.81m, 1769.46m, 1775.4m, 1774.97m, 1772.7m, 1771m, 1768.94m, 1769.56m, 1774.53m, 1775.34m, 1778.57m, 1779.69m, 1780.34m, 1779.24m, 1778.32m, 1780m, 1781m, 1779.16m, 1778.02m, 1777.22m, 1777.04m, 1778.57m, 1780.24m, 1780m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "ASML",
|
||||
Name = "ASML Holding N.V.",
|
||||
Price = 263.99m,
|
||||
DayChange = 1.26m,
|
||||
ChangePercentage = 0.48m,
|
||||
Volume = 549797,
|
||||
VolumeAvg = 1164687,
|
||||
MarketCap = 110834614272,
|
||||
IsCategorized = true,
|
||||
PricePerEarningRatio = 37.94m,
|
||||
IntraDayChart = new List<decimal> { 262.8m, 262.63m, 262.18m, 261.98m, 262.34m, 262.23m, 262.23m, 262.21m, 262.78m, 262.62m, 262.75m, 262.74m, 262.88m, 262.85m, 263.11m, 263m, 263.38m, 263.45m, 263.63m, 263.23m, 263.45m, 263.16m, 263.29m, 263.48m, 263.52m, 263.89m, 264.08m, 264.5m, 264.54m, 264.38m, 263.95m, 263.82m, 263.78m, 263.93m, 264.13m, 264.16m, 263.97m, 263.94m, 264m, 263.84m, 263.98m, 263.95m, 264.11m, 264.15m, 264.26m, 264.06m, 264.49m, 264.02m, 264.08m, 264.6m, 264.65m, 264.27m, 264.36m, 263.92m, 263.75m, 264.29m, 264.09m, 264.24m, 264.33m, 263.92m, 263.77m, 263.74m, 264.02m, 263.88m, 263.82m, 263.79m, 263.6m, 263.43m, 263.58m, 263.57m, 263.61m, 263.55m, 263.57m, 263.41m, 264.06m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "AVGO",
|
||||
Name = "Broadcom Inc.",
|
||||
Price = 289.82m,
|
||||
DayChange = 5.87m,
|
||||
ChangePercentage = 2.07m,
|
||||
Volume = 1987976,
|
||||
VolumeAvg = 1691400,
|
||||
MarketCap = 114963193856,
|
||||
IsCategorized = true,
|
||||
PricePerEarningRatio = 40.79m,
|
||||
IntraDayChart = new List<decimal> { 283.88m, 284.32m, 284.93m, 284.65m, 285.68m, 285.99m, 286.07m, 285.96m, 286.29m, 286.21m, 286.29m, 286.64m, 286.62m, 286.22m, 286.17m, 285.92m, 286.8m, 286.83m, 287.17m, 287.14m, 287.66m, 287.87m, 287.92m, 288.42m, 288.4m, 288.42m, 288.8m, 288.9m, 289.24m, 289.27m, 288.96m, 288.16m, 288.27m, 288.4m, 288.8m, 288.8m, 289.16m, 288.92m, 288.97m, 289.09m, 289.16m, 288.75m, 288.88m, 289.1m, 289.4m, 290.08m, 290.01m, 290.26m, 290.67m, 290.22m, 290.47m, 290.91m, 290.79m, 291.15m, 291.27m, 290.16m, 290.1m, 289.79m, 290.02m, 290.05m, 290.03m, 289.9m, 289.92m, 290.29m, 290.22m, 290.28m, 290.24m, 290.46m, 290.64m, 290.62m, 290.67m, 290.61m, 290.63m, 290.19m, 290.01m, 290.2m, 289.89m, 289.75m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "BNPQY",
|
||||
Name = "BNP Paribas SA",
|
||||
Price = 26.43m,
|
||||
DayChange = 0.43m,
|
||||
ChangePercentage = 1.65m,
|
||||
Volume = 103645,
|
||||
VolumeAvg = 193571,
|
||||
MarketCap = 66021871616,
|
||||
IsCategorized = true,
|
||||
PricePerEarningRatio = null,
|
||||
IntraDayChart = new List<decimal> { 26m, 25.97m, 25.97m, 25.98m, 25.95m, 25.95m, 25.89m, 25.91m, 25.89m, 25.85m, 25.91m, 25.93m, 25.89m, 25.94m, 25.94m, 25.94m, 25.94m, 25.91m, 25.9m, 25.89m, 25.92m, 25.97m, 25.97m, 25.94m, 25.94m, 25.94m, 25.94m, 25.99m, 26.07m, 26.07m, 26.01m, 26.01m, 26.04m, 26.03m, 26.03m, 26.03m, 25.99m, 25.99m, 26.08m, 26.08m, 26.06m, 26.05m, 26.05m, 26.05m, 25.99m, 25.99m, 25.99m, 26.06m, 25.99m, 26.02m, 26.05m, 26.05m, 26m, 26m, 26m, 25.98m, 26m, 26m, 26.03m, 26.11m, 26.22m, 26.26m, 26.36m, 26.41m, 26.41m, 26.29m, 26.41m, 26.4m, 26.35m, 26.35m, 26.42m, 26.43m, 26.43m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "CACC",
|
||||
Name = "Credit Acceptance Corporation",
|
||||
Price = 439.2m,
|
||||
DayChange = -0.69m,
|
||||
ChangePercentage = -0.16m,
|
||||
Volume = 57324,
|
||||
VolumeAvg = 84857,
|
||||
MarketCap = 8255554560,
|
||||
IsCategorized = true,
|
||||
PricePerEarningRatio = 13.4m,
|
||||
IntraDayChart = new List<decimal> { 439.4m, 438.08m, 438.08m, 438.57m, 438.57m, 439.86m, 439.86m, 440.89m, 440.89m, 439.95m, 439.95m, 440m, 440.8m, 440.58m, 439.52m, 439.03m, 438.46m, 437.69m, 437.29m, 438.59m, 437.05m, 437.25m, 437.34m, 438.89m, 438.89m, 438.89m, 440m, 438.22m, 437.41m, 438.13m, 438.14m, 437.57m, 437.14m, 436.77m, 436.77m, 437.89m, 437.31m, 437.31m, 437.09m, 437.09m, 436.91m, 436.91m, 437.43m, 437.43m, 437.22m, 437.22m, 436.83m, 436.47m, 436.47m, 436.82m, 436.82m, 438.84m, 438.84m, 437.4m, 437.4m, 438.35m, 438.35m, 438.2m, 439.57m, 440.49m, 438.99m, 438.67m, 440.14m, 439.22m, 439.77m, 439.77m, 439.25m, 439.43m, 438.9m, 439.24m, 438.69m, 438.71m }
|
||||
});
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "CAI",
|
||||
Name = "CAI International, Inc.",
|
||||
Price = 23.77m,
|
||||
DayChange = -1.23m,
|
||||
ChangePercentage = -4.92m,
|
||||
Volume = 184691,
|
||||
VolumeAvg = 114114,
|
||||
MarketCap = 414063904,
|
||||
PricePerEarningRatio = 12.99m,
|
||||
IsCategorized = false,
|
||||
IntraDayChart = new List<decimal> { 25m, 25.5m, 24.95m, 24.99m, 25.19m, 25m, 24.69m, 24.55m, 24.31m, 24.32m, 24.22m, 24.15m, 23.97m, 23.77m, 24.13m, 24m, 24m, 24.07m, 23.91m, 24.06m, 23.91m, 23.71m, 23.59m, 23.57m, 23.72m, 23.56m, 23.47m, 23.32m, 23.68m, 23.68m, 23.65m, 23.65m, 23.65m, 23.63m, 23.67m, 23.67m, 23.62m, 23.77m, 23.67m, 23.56m, 23.44m, 23.69m, 23.69m, 23.85m, 23.84m, 23.79m, 23.79m, 23.58m, 23.58m, 23.58m, 23.78m, 23.84m, 23.66m, 23.67m, 24.15m, 23.98m, 23.98m, 23.81m, 23.8m, 23.8m, 23.77m, 23.91m, 23.91m, 23.83m, 23.98m, 23.95m, 23.88m, 23.91m, 23.84m, 23.78m, 23.92m, 23.7m, 23.93m, 23.76m, 23.7m, 23.76m, 23.76m, 23.77m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "CBTX",
|
||||
Name = "CBTX, Inc.",
|
||||
Price = 28.71m,
|
||||
DayChange = 0.04m,
|
||||
ChangePercentage = 0.14m,
|
||||
Volume = 43506,
|
||||
VolumeAvg = 26514,
|
||||
MarketCap = 746933696,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 13.87m,
|
||||
IntraDayChart = new List<decimal> { 28.58m, 28.58m, 28.55m, 28.55m, 28.55m, 28.43m, 28.43m, 28.33m, 28.33m, 28.43m, 28.43m, 28.43m, 28.45m, 28.45m, 28.46m, 28.46m, 28.75m, 28.64m, 28.7m, 28.7m, 28.55m, 28.55m, 28.55m, 28.55m, 28.55m, 28.49m, 28.49m, 28.46m, 28.46m, 28.47m, 28.43m, 28.43m, 28.41m, 28.39m, 28.39m, 28.5m, 28.51m, 28.48m, 28.23m, 28.23m, 28.41m, 28.41m, 28.45m, 28.45m, 28.55m, 28.5m, 28.55m, 28.55m, 28.75m, 28.75m, 28.74m, 28.74m, 28.9m, 28.9m, 28.82m, 28.82m, 28.79m, 28.85m, 28.62m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "CMA",
|
||||
Name = "Comerica Incorporated",
|
||||
Price = 66.52m,
|
||||
DayChange = 0.28m,
|
||||
ChangePercentage = 0.42m,
|
||||
Volume = 1407582,
|
||||
VolumeAvg = 1229371,
|
||||
MarketCap = 9587926016,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 8.42m,
|
||||
IntraDayChart = new List<decimal> { 66.25m, 66.08m, 66.16m, 66.01m, 65.99m, 65.83m, 65.75m, 65.89m, 65.83m, 65.75m, 65.76m, 65.8m, 65.67m, 65.61m, 65.69m, 65.9m, 66.02m, 65.95m, 65.96m, 65.91m, 65.93m, 65.99m, 65.9m, 65.88m, 65.79m, 65.9m, 65.86m, 65.92m, 65.91m, 65.95m, 65.97m, 66.1m, 66.06m, 65.92m, 65.96m, 65.98m, 66.11m, 66.15m, 66.2m, 66.15m, 66.24m, 66.31m, 66.35m, 66.3m, 66.25m, 66.26m, 66.23m, 66.18m, 66.22m, 66.22m, 66.22m, 66.13m, 66.2m, 66.25m, 66.49m, 66.36m, 66.53m, 66.59m, 66.56m, 66.63m, 66.63m, 66.92m, 67.06m, 67.06m, 67.1m, 67.23m, 67.23m, 66.94m, 66.89m, 66.81m, 66.9m, 66.84m, 66.78m, 66.71m, 66.63m, 66.47m, 66.63m, 66.53m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "CRM",
|
||||
Name = "salesforce.com, inc.",
|
||||
Price = 150.49m,
|
||||
DayChange = 3.21m,
|
||||
ChangePercentage = 2.18m,
|
||||
Volume = 4814264,
|
||||
VolumeAvg = 5236950,
|
||||
MarketCap = 131979730944,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 124.99m,
|
||||
IntraDayChart = new List<decimal> { 147.19m, 147.16m, 147.29m, 147.91m, 147.87m, 147.83m, 147.8m, 147.78m, 148.08m, 148.2m, 148.04m, 148.39m, 148.23m, 148.01m, 147.88m, 148.18m, 148.77m, 148.51m, 148.4m, 148.32m, 148.33m, 148.69m, 148.79m, 148.83m, 148.79m, 148.89m, 148.89m, 148.89m, 148.95m, 149.04m, 148.96m, 148.76m, 148.61m, 148.61m, 148.69m, 148.63m, 148.73m, 148.79m, 148.69m, 148.72m, 148.71m, 148.79m, 148.75m, 148.79m, 148.79m, 149.03m, 149.08m, 149.15m, 149.2m, 149.11m, 149.34m, 149.55m, 149.63m, 149.58m, 149.63m, 149.64m, 149.78m, 149.89m, 149.93m, 149.95m, 149.97m, 149.6m, 149.74m, 149.73m, 149.94m, 149.99m, 149.96m, 149.98m, 150.05m, 150.1m, 150.17m, 150.23m, 150.24m, 150.41m, 150.32m, 150.4m, 150.23m, 150.4m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "CSCO",
|
||||
Name = "Cisco Systems, Inc.",
|
||||
Price = 46.9m,
|
||||
DayChange = 0.31m,
|
||||
ChangePercentage = 0.67m,
|
||||
Volume = 12554651,
|
||||
VolumeAvg = 16144737,
|
||||
MarketCap = 196328095744,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 17.97m,
|
||||
IntraDayChart = new List<decimal> { 46.59m, 46.67m, 46.74m, 46.77m, 46.79m, 46.76m, 46.79m, 46.78m, 46.78m, 46.82m, 46.77m, 46.79m, 46.82m, 46.79m, 46.74m, 46.67m, 46.76m, 46.77m, 46.74m, 46.74m, 46.74m, 46.8m, 46.88m, 46.96m, 46.96m, 46.94m, 46.95m, 46.96m, 46.94m, 46.95m, 46.91m, 46.86m, 46.88m, 46.87m, 46.88m, 46.86m, 46.9m, 46.94m, 46.92m, 46.93m, 46.94m, 46.92m, 46.92m, 46.89m, 46.87m, 46.91m, 46.89m, 46.88m, 46.88m, 46.85m, 46.89m, 46.9m, 46.85m, 46.87m, 46.83m, 46.86m, 46.86m, 46.78m, 46.8m, 46.81m, 46.78m, 46.72m, 46.72m, 46.78m, 46.8m, 46.83m, 46.86m, 46.87m, 46.88m, 46.86m, 46.88m, 46.92m, 46.96m, 46.94m, 46.94m, 46.94m, 46.88m, 46.9m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "ECPG",
|
||||
Name = "Encore Capital Group, Inc.",
|
||||
Price = 33.79m,
|
||||
DayChange = -0.76m,
|
||||
ChangePercentage = -2.2m,
|
||||
Volume = 233014,
|
||||
VolumeAvg = 189114,
|
||||
MarketCap = 1049429568,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 6.82m,
|
||||
IntraDayChart = new List<decimal> { 34.56m, 34.45m, 34.45m, 34.53m, 34.38m, 34.26m, 34.17m, 34.17m, 34.13m, 34.19m, 34.21m, 34.27m, 34.27m, 34.17m, 34.17m, 34.17m, 34.12m, 34.17m, 34.18m, 34.18m, 34.14m, 34.1m, 34.12m, 34.09m, 34.09m, 34.03m, 33.99m, 33.97m, 33.98m, 33.97m, 34.02m, 33.99m, 33.99m, 34m, 33.96m, 34.01m, 34.01m, 34.03m, 34.05m, 34.1m, 34.04m, 34.06m, 34.01m, 34.01m, 34.01m, 33.9m, 33.88m, 33.88m, 33.85m, 33.85m, 33.86m, 33.88m, 33.88m, 33.89m, 33.92m, 33.96m, 33.94m, 33.94m, 33.91m, 33.88m, 33.89m, 33.87m, 33.9m, 33.9m, 33.87m, 33.87m, 33.89m, 33.86m, 33.85m, 33.87m, 33.76m, 33.85m, 33.79m, 33.81m, 33.9m, 33.86m, 33.8m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "EPRT",
|
||||
Name = "Essential Properties Realty Trust, Inc.",
|
||||
Price = 25.58m,
|
||||
DayChange = 0.04m,
|
||||
ChangePercentage = 0.16m,
|
||||
Volume = 894983,
|
||||
VolumeAvg = 909457,
|
||||
MarketCap = 1952475392,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 49.57m,
|
||||
IntraDayChart = new List<decimal> { 25.54m, 25.43m, 25.4m, 25.29m, 25.21m, 25.24m, 25.26m, 25.32m, 25.3m, 25.23m, 25.33m, 25.39m, 25.41m, 25.3m, 25.37m, 25.36m, 25.36m, 25.43m, 25.45m, 25.43m, 25.37m, 25.46m, 25.48m, 25.49m, 25.48m, 25.5m, 25.51m, 25.55m, 25.53m, 25.54m, 25.56m, 25.6m, 25.53m, 25.51m, 25.5m, 25.53m, 25.51m, 25.54m, 25.56m, 25.55m, 25.55m, 25.5m, 25.47m, 25.47m, 25.45m, 25.47m, 25.5m, 25.53m, 25.48m, 25.49m, 25.43m, 25.38m, 25.45m, 25.46m, 25.4m, 25.42m, 25.34m, 25.37m, 25.36m, 25.34m, 25.35m, 25.33m, 25.42m, 25.41m, 25.48m, 25.44m, 25.46m, 25.48m, 25.5m, 25.49m, 25.49m, 25.45m, 25.52m, 25.56m, 25.57m, 25.58m, 25.59m, 25.58m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "FB",
|
||||
Name = "Facebook, Inc.",
|
||||
Price = 187.89m,
|
||||
DayChange = 2.09m,
|
||||
ChangePercentage = 1.12m,
|
||||
Volume = 6979273,
|
||||
VolumeAvg = 12925912,
|
||||
MarketCap = 536040767488,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 31.78m,
|
||||
IntraDayChart = new List<decimal> { 185.81m, 185.25m, 185.73m, 185.74m, 185.93m, 186.33m, 186.08m, 186.03m, 186.39m, 186.33m, 186.44m, 186.87m, 187.51m, 187.11m, 187.29m, 187.43m, 187.83m, 187.96m, 188.27m, 188.29m, 188.25m, 188.5m, 188.76m, 188.93m, 188.54m, 188.44m, 188.52m, 188.55m, 188.33m, 188.35m, 188.21m, 187.93m, 188.05m, 188.29m, 188.36m, 188.5m, 188.57m, 188.47m, 188.43m, 188.63m, 188.66m, 188.66m, 188.57m, 188.57m, 188.43m, 188.57m, 188.52m, 188.4m, 188.29m, 188.26m, 188.44m, 188.24m, 188.28m, 188.36m, 188.24m, 187.91m, 188.05m, 188.12m, 187.77m, 187.85m, 187.83m, 187.62m, 187.74m, 187.93m, 187.96m, 187.96m, 188.04m, 188.17m, 188.18m, 188.18m, 188.05m, 188.19m, 188.1m, 188.19m, 188.07m, 188.02m, 187.93m, 187.9m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "FOR",
|
||||
Name = "Forestar Group Inc.",
|
||||
Price = 18.5m,
|
||||
DayChange = -0.32m,
|
||||
ChangePercentage = -1.7m,
|
||||
Volume = 77837,
|
||||
VolumeAvg = 88428,
|
||||
MarketCap = 887951872,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 6.72m,
|
||||
IntraDayChart = new List<decimal> { 18.8m, 18.8m, 18.43m, 18.43m, 18.48m, 18.48m, 18.38m, 18.38m, 18.43m, 18.43m, 18.35m, 18.4m, 18.32m, 18.32m, 18.33m, 18.33m, 18.3m, 18.26m, 18.26m, 18.21m, 18.21m, 18.12m, 18.13m, 18.13m, 18.12m, 18.13m, 18.13m, 18.19m, 18.17m, 18.16m, 18.16m, 18.17m, 18.2m, 18.17m, 18.16m, 18.16m, 18.14m, 18.16m, 18.19m, 18.19m, 18.16m, 18.16m, 18.16m, 18.17m, 18.17m, 18.19m, 18.19m, 18.17m, 18.17m, 18.17m, 18.18m, 18.18m, 18.16m, 18.18m, 18.17m, 18.17m, 18.17m, 18.17m, 18.16m, 18.16m, 18.2m, 18.29m, 18.29m, 18.35m, 18.35m, 18.35m, 18.39m, 18.36m, 18.5m, 18.49m, 18.45m, 18.47m, 18.5m, 18.48m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "GATX",
|
||||
Name = "GATX Corporation",
|
||||
Price = 80.2m,
|
||||
DayChange = -1.39m,
|
||||
ChangePercentage = -1.7m,
|
||||
Volume = 144502,
|
||||
VolumeAvg = 283171,
|
||||
MarketCap = 2815019776,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 14.51m,
|
||||
IntraDayChart = new List<decimal> { 81.6m, 81.53m, 81.64m, 81.64m, 81.26m, 81.26m, 80.63m, 80.89m, 80.91m, 80.98m, 81.06m, 81.09m, 80.93m, 80.89m, 80.8m, 80.85m, 80.65m, 80.68m, 80.58m, 80.46m, 80.6m, 80.38m, 80.24m, 80.01m, 79.79m, 80.02m, 80.01m, 80.08m, 80.3m, 80.4m, 80.46m, 80.6m, 80.6m, 80.32m, 80.37m, 80.44m, 80.61m, 80.49m, 80.49m, 80.58m, 80.58m, 80.59m, 80.64m, 80.52m, 80.49m, 80.3m, 80.45m, 80.36m, 80.29m, 80.35m, 80.45m, 80.36m, 80.41m, 80.4m, 79.86m, 79.41m, 79.56m, 79.64m, 80.03m, 80.16m, 80.17m, 80.22m, 80.18m, 80.25m, 80.46m, 80.56m, 80.55m, 80.59m, 80.47m, 80.42m, 80.57m, 80.41m, 80.39m, 80.24m, 80.23m, 80.09m, 80.26m, 80.21m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "GOOGL",
|
||||
Name = "Alphabet Inc.",
|
||||
Price = 1264.3m,
|
||||
DayChange = 12.77m,
|
||||
ChangePercentage = 1.02m,
|
||||
Volume = 1243991,
|
||||
VolumeAvg = 1160087,
|
||||
MarketCap = 877319290880,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 25.52m,
|
||||
IntraDayChart = new List<decimal> { 1250.64m, 1251.31m, 1254.55m, 1256.5m, 1255.41m, 1254.68m, 1252.91m, 1254.09m, 1256.22m, 1256.61m, 1257.85m, 1260.42m, 1260.15m, 1259.65m, 1261.1m, 1261.26m, 1263.52m, 1264m, 1264.86m, 1264.52m, 1264.04m, 1264.98m, 1266.85m, 1267.93m, 1265.79m, 1266.83m, 1266.93m, 1267.49m, 1266.84m, 1266.75m, 1265.67m, 1264.12m, 1263.36m, 1264.1m, 1263.71m, 1264.6m, 1265m, 1265m, 1264.28m, 1264.64m, 1265.52m, 1265.67m, 1265.46m, 1265.36m, 1265.49m, 1265.6m, 1265.38m, 1265.37m, 1264.68m, 1263.98m, 1264.75m, 1265.1m, 1266m, 1266.34m, 1266.22m, 1264.06m, 1263.03m, 1263.04m, 1261.88m, 1262.14m, 1261.77m, 1261m, 1260.89m, 1260.72m, 1263.04m, 1263.1m, 1262.93m, 1262.81m, 1263.68m, 1263.31m, 1262.4m, 1263.43m, 1263.6m, 1264.01m, 1264.16m, 1263.89m, 1263.45m, 1263.41m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "IBM",
|
||||
Name = "International Business Machines Corporation",
|
||||
Price = 135.44m,
|
||||
DayChange = 0.91m,
|
||||
ChangePercentage = 0.68m,
|
||||
Volume = 2543592,
|
||||
VolumeAvg = 5996562,
|
||||
MarketCap = 119982915584,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 14.23m,
|
||||
IntraDayChart = new List<decimal> { 134.52m, 134.76m, 134.94m, 134.59m, 134.44m, 134.35m, 134.45m, 134.42m, 134.39m, 134.52m, 134.84m, 135.26m, 134.93m, 134.77m, 134.82m, 134.88m, 135.34m, 135.29m, 135.45m, 135.43m, 135.36m, 135.7m, 135.72m, 135.89m, 135.9m, 135.75m, 135.72m, 135.82m, 135.74m, 135.76m, 135.65m, 135.5m, 135.46m, 135.55m, 135.64m, 135.59m, 135.55m, 135.59m, 135.51m, 135.4m, 135.46m, 135.44m, 135.46m, 135.49m, 135.48m, 135.49m, 135.48m, 135.37m, 135.37m, 135.3m, 135.3m, 135.32m, 135.31m, 135.35m, 135.36m, 135.18m, 135.25m, 135.43m, 135.55m, 135.5m, 135.43m, 135.32m, 135.29m, 135.28m, 135.28m, 135.4m, 135.4m, 135.34m, 135.38m, 135.37m, 135.35m, 135.39m, 135.44m, 135.43m, 135.48m, 135.43m, 135.41m, 135.43m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "INTC",
|
||||
Name = "Intel Corporation",
|
||||
Price = 56.46m,
|
||||
DayChange = 1.99m,
|
||||
ChangePercentage = 3.64m,
|
||||
Volume = 56704514,
|
||||
VolumeAvg = 16469900,
|
||||
MarketCap = 252583968768,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 13.11m,
|
||||
IntraDayChart = new List<decimal> { 54.61m, 55.48m, 55.38m, 55.4m, 55.68m, 55.71m, 55.87m, 55.93m, 55.85m, 55.9m, 55.96m, 55.94m, 55.94m, 55.94m, 55.85m, 55.58m, 55.92m, 55.9m, 55.99m, 55.98m, 55.93m, 56.11m, 56.15m, 56.4m, 56.33m, 56.2m, 55.97m, 56.17m, 56.34m, 56.17m, 56.21m, 56.18m, 56.14m, 56.14m, 56.22m, 56.19m, 56.25m, 56.07m, 56.1m, 56.19m, 56.14m, 56.09m, 56.03m, 56.08m, 56.13m, 56.22m, 56.24m, 56.22m, 56.23m, 56.15m, 56.26m, 56.37m, 56.31m, 56.31m, 56.23m, 56.19m, 56.22m, 56.29m, 56.34m, 56.33m, 56.35m, 56.25m, 56.22m, 56.18m, 56.19m, 56.22m, 56.19m, 56.25m, 56.28m, 56.39m, 56.41m, 56.46m, 56.53m, 56.45m, 56.43m, 56.37m, 56.4m, 56.6m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "INTU",
|
||||
Name = "Intuit Inc.",
|
||||
Price = 257.67m,
|
||||
DayChange = -1.99m,
|
||||
ChangePercentage = -0.77m,
|
||||
Volume = 1038001,
|
||||
VolumeAvg = 1170162,
|
||||
MarketCap = 67013271552,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 43.75m,
|
||||
IntraDayChart = new List<decimal> { 259.8m, 258.72m, 258.6m, 259.23m, 259.13m, 258.44m, 258.75m, 259.33m, 259.71m, 259.47m, 259.68m, 259.95m, 259.79m, 259.05m, 259.1m, 258.85m, 259.25m, 258.8m, 258.9m, 258.83m, 258.99m, 259.42m, 259.41m, 259.01m, 258.94m, 258.98m, 259.07m, 258.93m, 258.87m, 258.76m, 258.64m, 258.04m, 257.73m, 257.94m, 258.08m, 257.92m, 258.15m, 258.01m, 257.96m, 258.01m, 258.18m, 258.07m, 258.07m, 257.97m, 257.9m, 258.01m, 258.01m, 257.83m, 257.77m, 257.46m, 257.64m, 257.78m, 257.74m, 257.58m, 257.5m, 257.19m, 257.26m, 257.2m, 257.13m, 257.06m, 257.22m, 256.83m, 256.94m, 257.04m, 256.81m, 257.05m, 257.08m, 256.97m, 256.86m, 257.14m, 257.15m, 257.2m, 257.29m, 257.47m, 257.39m, 257.56m, 257.4m, 257.81m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "JPM",
|
||||
Name = "JPMorgan Chase & Co.",
|
||||
Price = 125.73m,
|
||||
DayChange = -0.11m,
|
||||
ChangePercentage = -0.09m,
|
||||
Volume = 7792212,
|
||||
VolumeAvg = 10052585,
|
||||
MarketCap = 394352164864,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 12.41m,
|
||||
IntraDayChart = new List<decimal> { 125.86m, 125.75m, 126.07m, 125.8m, 125.74m, 125.78m, 125.52m, 125.68m, 125.78m, 125.78m, 125.69m, 125.68m, 125.47m, 125.29m, 125.35m, 125.44m, 125.52m, 125.57m, 125.6m, 125.57m, 125.56m, 125.52m, 125.29m, 125.31m, 125.22m, 125.34m, 125.24m, 125.05m, 124.98m, 125.07m, 125.08m, 125.11m, 125.07m, 125.14m, 125.26m, 125.36m, 125.45m, 125.35m, 125.45m, 125.44m, 125.44m, 125.54m, 125.64m, 125.51m, 125.43m, 125.4m, 125.49m, 125.46m, 125.48m, 125.49m, 125.44m, 125.39m, 125.49m, 125.45m, 125.57m, 125.28m, 125.44m, 125.46m, 125.59m, 125.62m, 125.65m, 125.74m, 126m, 126.05m, 126.08m, 126.03m, 125.96m, 125.79m, 125.75m, 125.69m, 125.93m, 125.82m, 125.82m, 125.73m, 125.67m, 125.51m, 125.72m, 125.75m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "MSFT",
|
||||
Name = "Microsoft Corporation",
|
||||
Price = 140.73m,
|
||||
DayChange = 1.04m,
|
||||
ChangePercentage = 0.75m,
|
||||
Volume = 20464765,
|
||||
VolumeAvg = 26070562,
|
||||
MarketCap = 1094723174400,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 26.55m,
|
||||
IntraDayChart = new List<decimal> { 139.48m, 139.56m, 139.57m, 139.89m, 140.3m, 140.12m, 140.35m, 140.58m, 140.98m, 140.71m, 140.77m, 140.77m, 140.69m, 140.61m, 140.65m, 140.62m, 140.82m, 140.82m, 140.82m, 140.84m, 140.62m, 140.92m, 141.1m, 141.08m, 140.78m, 140.83m, 141.07m, 141.01m, 140.97m, 141.02m, 140.84m, 140.72m, 140.69m, 140.7m, 140.77m, 140.71m, 140.73m, 140.62m, 140.55m, 140.65m, 140.69m, 140.66m, 140.68m, 140.66m, 140.68m, 140.79m, 140.84m, 140.71m, 140.57m, 140.51m, 140.58m, 140.65m, 140.66m, 140.68m, 140.59m, 140.51m, 140.45m, 140.48m, 140.46m, 140.44m, 140.39m, 140.26m, 140.29m, 140.26m, 140.25m, 140.36m, 140.41m, 140.35m, 140.41m, 140.38m, 140.47m, 140.54m, 140.49m, 140.52m, 140.43m, 140.42m, 140.38m, 140.52m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "NVDA",
|
||||
Name = "NVIDIA Corporation",
|
||||
Price = 204.54m,
|
||||
DayChange = 3,
|
||||
ChangePercentage = 1.49m,
|
||||
Volume = 10357677,
|
||||
VolumeAvg = 8645212,
|
||||
MarketCap = 122178895872,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 46.12m,
|
||||
IntraDayChart = new List<decimal> { 201.49m, 202.51m, 202.97m, 201.91m, 202.82m, 202.28m, 201.87m, 201.42m, 202.09m, 202.28m, 201.57m, 202.62m, 202.68m, 202.38m, 202.45m, 202.53m, 203.34m, 203.21m, 203.85m, 204.02m, 204.29m, 204.48m, 204.57m, 204.88m, 204.4m, 204.41m, 204.4m, 204.63m, 204.89m, 205.22m, 204.87m, 204.64m, 204.23m, 204.36m, 204.67m, 204.61m, 204.59m, 204.07m, 204.16m, 204.2m, 204.29m, 203.85m, 203.76m, 203.75m, 204m, 204.4m, 204.45m, 204.57m, 204.75m, 204.55m, 204.51m, 204.9m, 204.9m, 204.62m, 204.52m, 203.82m, 203.72m, 203.82m, 203.87m, 203.81m, 203.77m, 203.35m, 203.47m, 203.75m, 203.87m, 203.93m, 203.73m, 203.69m, 203.86m, 203.84m, 204.38m, 204.71m, 204.8m, 204.66m, 204.52m, 204.57m, 204.33m, 204.49m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "ORCL",
|
||||
Name = "Oracle Corporation",
|
||||
Price = 54.17m,
|
||||
DayChange = 0.09m,
|
||||
ChangePercentage = 0.17m,
|
||||
Volume = 5741404,
|
||||
VolumeAvg = 9189612,
|
||||
MarketCap = 177814110208,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 17.73m,
|
||||
IntraDayChart = new List<decimal> { 54.04m, 54.27m, 54.25m, 54.19m, 54.23m, 54.24m, 54.24m, 54.29m, 54.35m, 54.34m, 54.35m, 54.36m, 54.28m, 54.24m, 54.18m, 54.13m, 54.22m, 54.21m, 54.21m, 54.21m, 54.18m, 54.28m, 54.34m, 54.38m, 54.38m, 54.36m, 54.38m, 54.38m, 54.38m, 54.42m, 54.39m, 54.28m, 54.3m, 54.28m, 54.27m, 54.26m, 54.27m, 54.23m, 54.22m, 54.23m, 54.25m, 54.2m, 54.2m, 54.26m, 54.27m, 54.28m, 54.26m, 54.26m, 54.24m, 54.19m, 54.22m, 54.22m, 54.22m, 54.2m, 54.2m, 54.13m, 54.16m, 54.15m, 54.14m, 54.1m, 54.08m, 54.01m, 54.04m, 54.05m, 54.04m, 54.1m, 54.12m, 54.1m, 54.12m, 54.12m, 54.13m, 54.16m, 54.18m, 54.19m, 54.15m, 54.15m, 54.13m, 54.13m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "PRGS",
|
||||
Name = "Progress Software Corporation",
|
||||
Price = 40.31m,
|
||||
DayChange = 0.43m,
|
||||
ChangePercentage = 1.08m,
|
||||
Volume = 119189,
|
||||
VolumeAvg = 166171,
|
||||
MarketCap = 1805307648,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 33.76m,
|
||||
IntraDayChart = new List<decimal> { 39.86m, 39.69m, 39.69m, 39.72m, 39.74m, 39.68m, 39.5m, 39.45m, 39.55m, 39.55m, 39.65m, 39.65m, 39.63m, 39.76m, 39.79m, 39.89m, 39.87m, 39.86m, 39.85m, 39.85m, 39.85m, 39.85m, 39.85m, 39.86m, 39.87m, 39.92m, 39.88m, 39.86m, 39.83m, 39.82m, 39.83m, 39.83m, 39.95m, 39.94m, 39.9m, 39.94m, 39.94m, 39.94m, 39.95m, 39.97m, 39.96m, 39.94m, 39.93m, 39.91m, 39.89m, 39.92m, 39.93m, 39.96m, 39.95m, 39.93m, 39.92m, 39.93m, 39.9m, 39.96m, 39.88m, 39.9m, 39.88m, 39.94m, 39.98m, 39.95m, 39.91m, 39.9m, 39.93m, 39.97m, 40.18m, 40.18m, 40.19m, 40.19m, 40.19m, 40.24m, 40.35m, 40.28m, 40.3m, 40.27m, 40.33m, 40.3m, 40.33m, 40.29m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "QCOM",
|
||||
Name = "QUALCOMM Incorporated",
|
||||
Price = 80.17m,
|
||||
DayChange = 0.94m,
|
||||
ChangePercentage = 1.19m,
|
||||
Volume = 5950509,
|
||||
VolumeAvg = 5573175,
|
||||
MarketCap = 97459462144,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 29.34m,
|
||||
IntraDayChart = new List<decimal> { 79.2m, 79.12m, 79.34m, 79.07m, 79.09m, 79.15m, 79.1m, 79.08m, 79.15m, 79.21m, 79.22m, 79.32m, 79.35m, 79.28m, 79.24m, 79.13m, 79.31m, 79.33m, 79.39m, 79.43m, 79.55m, 79.55m, 79.6m, 79.82m, 79.83m, 79.89m, 79.91m, 79.96m, 80m, 80.08m, 80.06m, 80.02m, 79.98m, 80m, 80.03m, 79.98m, 80.01m, 79.87m, 79.95m, 79.97m, 79.99m, 79.91m, 79.94m, 79.9m, 79.92m, 79.98m, 79.99m, 79.98m, 79.99m, 79.9m, 79.91m, 79.98m, 79.92m, 79.92m, 79.95m, 79.85m, 80.01m, 80.1m, 80.15m, 80.11m, 80.07m, 79.93m, 79.84m, 79.84m, 79.82m, 79.95m, 79.93m, 79.83m, 79.82m, 79.95m, 79.98m, 80.14m, 80.16m, 80.1m, 80.1m, 80.12m, 80.14m, 80.14m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "SAP",
|
||||
Name = "SAP SE",
|
||||
Price = 131.87m,
|
||||
DayChange = 0.34m,
|
||||
ChangePercentage = 0.26m,
|
||||
Volume = 558142,
|
||||
VolumeAvg = 1046450,
|
||||
MarketCap = 162669559808,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 31.86m,
|
||||
IntraDayChart = new List<decimal> { 131.43m, 131.37m, 131.06m, 131.03m, 131.23m, 131.25m, 131.32m, 131.33m, 131.59m, 131.67m, 131.72m, 131.82m, 131.79m, 131.81m, 131.76m, 131.78m, 131.85m, 131.84m, 131.95m, 131.79m, 131.77m, 131.83m, 131.92m, 132.02m, 131.95m, 132.08m, 131.99m, 132.05m, 131.96m, 131.94m, 131.75m, 131.73m, 131.73m, 131.72m, 131.71m, 131.78m, 131.73m, 131.67m, 131.73m, 131.74m, 131.82m, 131.79m, 131.83m, 131.83m, 131.82m, 131.79m, 131.77m, 131.75m, 131.7m, 131.72m, 131.77m, 131.74m, 131.73m, 131.67m, 131.65m, 131.61m, 131.62m, 131.65m, 131.62m, 131.63m, 131.55m, 131.56m, 131.6m, 131.58m, 131.69m, 131.69m, 131.65m, 131.61m, 131.65m, 131.66m, 131.7m, 131.7m, 131.71m, 131.68m, 131.66m, 131.67m, 131.77m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "SNAP",
|
||||
Name = "Snap Inc.",
|
||||
Price = 13.52m,
|
||||
DayChange = 0.29m,
|
||||
ChangePercentage = 2.19m,
|
||||
Volume = 54048642,
|
||||
VolumeAvg = 44871025,
|
||||
MarketCap = 18647865344,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = null,
|
||||
IntraDayChart = new List<decimal> { 13.22m, 12.93m, 12.94m, 12.94m, 13.03m, 13.06m, 13.06m, 13.02m, 13.06m, 13.08m, 13.07m, 13.09m, 13.1m, 13.15m, 13.16m, 13.27m, 13.2m, 13.33m, 13.44m, 13.52m, 13.58m, 13.6m, 13.48m, 13.54m, 13.53m, 13.6m, 13.55m, 13.56m, 13.58m, 13.65m, 13.58m, 13.6m, 13.58m, 13.51m, 13.54m, 13.58m, 13.54m, 13.54m, 13.55m, 13.54m, 13.56m, 13.61m, 13.61m, 13.55m, 13.55m, 13.52m, 13.48m, 13.47m, 13.47m, 13.38m, 13.42m, 13.44m, 13.43m, 13.39m, 13.42m, 13.41m, 13.4m, 13.41m, 13.38m, 13.4m, 13.39m, 13.41m, 13.44m, 13.43m, 13.49m, 13.51m, 13.51m, 13.54m, 13.56m, 13.59m, 13.58m, 13.58m, 13.57m, 13.53m, 13.51m, 13.49m, 13.48m, 13.51m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "SNE",
|
||||
Name = "Sony Corporation",
|
||||
Price = 58.51m,
|
||||
DayChange = 0.47m,
|
||||
ChangePercentage = 0.81m,
|
||||
Volume = 670753,
|
||||
VolumeAvg = 757475,
|
||||
MarketCap = 71632035840,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 12.3m,
|
||||
IntraDayChart = new List<decimal> { 58.11m, 58.09m, 58.15m, 58.11m, 58.19m, 58.17m, 58.16m, 58.2m, 58.27m, 58.24m, 58.24m, 58.25m, 58.29m, 58.25m, 58.28m, 58.34m, 58.41m, 58.38m, 58.4m, 58.35m, 58.35m, 58.38m, 58.47m, 58.48m, 58.47m, 58.52m, 58.53m, 58.59m, 58.58m, 58.57m, 58.52m, 58.5m, 58.48m, 58.48m, 58.48m, 58.45m, 58.47m, 58.39m, 58.38m, 58.39m, 58.42m, 58.43m, 58.43m, 58.39m, 58.42m, 58.4m, 58.39m, 58.43m, 58.46m, 58.42m, 58.47m, 58.45m, 58.43m, 58.46m, 58.47m, 58.4m, 58.45m, 58.46m, 58.5m, 58.5m, 58.47m, 58.44m, 58.46m, 58.48m, 58.5m, 58.49m, 58.5m, 58.52m, 58.52m, 58.55m, 58.53m, 58.54m, 58.54m, 58.58m, 58.57m, 58.58m, 58.56m, 58.58m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "TSM",
|
||||
Name = "Taiwan Semiconductor Manufacturing Company Limited",
|
||||
Price = 51.13m,
|
||||
DayChange = 0.36m,
|
||||
ChangePercentage = 0.71m,
|
||||
Volume = 5965678,
|
||||
VolumeAvg = 9484987,
|
||||
MarketCap = 250063552512,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 22.93m,
|
||||
IntraDayChart = new List<decimal> { 50.79m, 50.85m, 50.92m, 50.9m, 50.92m, 50.91m, 50.86m, 50.78m, 50.73m, 50.82m, 50.85m, 50.87m, 50.85m, 50.88m, 50.97m, 50.95m, 51.05m, 51.06m, 50.99m, 51.01m, 51.03m, 50.93m, 50.92m, 51m, 51m, 50.92m, 50.97m, 50.98m, 50.97m, 50.94m, 50.93m, 50.9m, 50.92m, 50.92m, 50.96m, 50.99m, 51m, 50.97m, 51.04m, 51.03m, 51.01m, 51.05m, 51.05m, 51.06m, 51.06m, 51.06m, 51.05m, 51.03m, 51.03m, 51.01m, 50.99m, 51.03m, 51.04m, 51.04m, 51.04m, 50.97m, 50.96m, 50.96m, 50.99m, 51m, 51m, 50.97m, 50.99m, 51.01m, 51.03m, 51.06m, 51.04m, 51.03m, 51.07m, 51.03m, 51.04m, 51.12m, 51.14m, 51.1m, 51.11m, 51.09m, 51.1m, 51.15m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "TWTR",
|
||||
Name = "Twitter, Inc.",
|
||||
Price = 30.75m,
|
||||
DayChange = -0.64m,
|
||||
ChangePercentage = -2.07m,
|
||||
Volume = 105075360,
|
||||
VolumeAvg = 10571275,
|
||||
MarketCap = 23770302464,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 10.16m,
|
||||
IntraDayChart = new List<decimal> { 30.95m, 30.53m, 30.56m, 30.64m, 30.53m, 30.39m, 30.26m, 30.13m, 30.03m, 29.99m, 30.11m, 30.16m, 30.14m, 30.02m, 30.01m, 30m, 30.11m, 30.16m, 30.22m, 30.17m, 30.15m, 30.2m, 30.23m, 30.33m, 30.35m, 30.3m, 30.39m, 30.44m, 30.48m, 30.55m, 30.34m, 30.47m, 30.37m, 30.42m, 30.44m, 30.47m, 30.51m, 30.56m, 30.48m, 30.52m, 30.53m, 30.63m, 30.58m, 30.51m, 30.45m, 30.52m, 30.52m, 30.49m, 30.33m, 30.34m, 30.29m, 30.3m, 30.31m, 30.26m, 30.31m, 30.2m, 30.25m, 30.15m, 30.1m, 30.07m, 30.08m, 30.06m, 30.07m, 30.14m, 30.17m, 30.13m, 30.14m, 30.13m, 30.15m, 30.18m, 30.2m, 30.24m, 30.21m, 30.32m, 30.31m, 30.33m, 30.33m, 30.31m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "TXN",
|
||||
Name = "Texas Instruments Incorporated",
|
||||
Price = 120.51m,
|
||||
DayChange = 1.21m,
|
||||
ChangePercentage = 1.01m,
|
||||
Volume = 4106890,
|
||||
VolumeAvg = 6018987,
|
||||
MarketCap = 112698302464,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 22.37m,
|
||||
IntraDayChart = new List<decimal> { 119.32m, 119.08m, 119.2m, 118.7m, 118.93m, 119.07m, 118.95m, 118.92m, 118.99m, 118.98m, 119.1m, 118.98m, 118.92m, 118.8m, 118.84m, 118.81m, 119.13m, 119.21m, 119.36m, 119.32m, 119.32m, 119.34m, 119.31m, 119.61m, 119.58m, 119.79m, 119.85m, 119.97m, 120m, 119.92m, 119.78m, 119.71m, 119.78m, 119.97m, 120.08m, 120.07m, 120.22m, 120.14m, 120.18m, 120.25m, 120.25m, 120.13m, 120.28m, 120.32m, 120.38m, 120.46m, 120.55m, 120.52m, 120.59m, 120.37m, 120.39m, 120.56m, 120.43m, 120.51m, 120.43m, 120.24m, 120.29m, 120.23m, 120.21m, 120.22m, 120.24m, 120.03m, 120.12m, 120.17m, 120.24m, 120.28m, 120.32m, 120.33m, 120.25m, 120.29m, 120.35m, 120.5m, 120.5m, 120.5m, 120.39m, 120.46m, 120.37m, 120.53m }
|
||||
});
|
||||
|
||||
stockList.Add(new Stock
|
||||
{
|
||||
Symbol = "XOM",
|
||||
Name = "Exxon Mobil Corporation",
|
||||
Price = 67.72m,
|
||||
DayChange = -0.54m,
|
||||
ChangePercentage = -0.79m,
|
||||
Volume = 11995831,
|
||||
VolumeAvg = 10012842,
|
||||
MarketCap = 286530764800,
|
||||
IsCategorized = false,
|
||||
PricePerEarningRatio = 16.32m,
|
||||
IntraDayChart = new List<decimal> { 68.28m, 68.18m, 68.1m, 68.04m, 68.04m, 68.03m, 68m, 68.1m, 68.11m, 68.02m, 68.07m, 68.01m, 68m, 68.07m, 67.97m, 68m, 67.98m, 67.99m, 67.93m, 67.88m, 67.85m, 67.82m, 67.81m, 67.82m, 67.77m, 67.81m, 67.77m, 67.72m, 67.71m, 67.64m, 67.71m, 67.78m, 67.71m, 67.71m, 67.68m, 67.67m, 67.7m, 67.61m, 67.62m, 67.59m, 67.62m, 67.61m, 67.7m, 67.71m, 67.7m, 67.62m, 67.6m, 67.63m, 67.6m, 67.57m, 67.57m, 67.5m, 67.47m, 67.5m, 67.46m, 67.36m, 67.41m, 67.4m, 67.43m, 67.51m, 67.39m, 67.31m, 67.3m, 67.36m, 67.43m, 67.5m, 67.55m, 67.54m, 67.54m, 67.53m, 67.54m, 67.56m, 67.53m, 67.5m, 67.51m, 67.49m, 67.64m, 67.74m }
|
||||
});
|
||||
|
||||
return stockList;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
# Blazor PWA App - Stocks Portfolio
|
||||
|
||||
This sample application demonstrates one way to implement a PWA ([Progressive Web Application](https://developers.google.com/web/progressive-web-apps)) functionality in a Blazor WASM app.
|
||||
|
||||
To generate the needed PWA assets, the [Blazor.PWA.MSBuild by SQL-MisterMagoo](https://github.com/SQL-MisterMagoo/Blazor.PWA.MSBuild) is used in this example. You can write them manually, use a different tool, or even see if Microsoft will prepare a ready-made PWA project template for Blazor.
|
||||
|
||||
The SASS styles are built to CSS through the [WebCopmiler package by madskristensen ](https://github.com/madskristensen/WebCompiler), then a build task in the `csproj` file copies the output to the `wwwroot` folder. This requires that you build through Visual Studio, a command-line build may throw an exception like `Access to the path '7z.dll' is denied`, depending on the machine setup and permissions.
|
||||
|
||||
The Telerik theme is fetched as a [local dependency](https://docs.telerik.com/blazor-ui/themes/overview#optional-dependency-management) to allow for easier caching for offline use of the PWA.
|
||||
|
||||
Data is generated in services for simplicity in this sample. They do not provide full offline capabilities (such as [offline detection](https://stackoverflow.com/questions/44756154/progressive-web-app-how-to-detect-and-handle-when-connection-is-up-again) and caching changes that can be synced later) as this is beyond the scope of this example.
|