From 981a873dee63b816346a6c883cf25682648d6cca Mon Sep 17 00:00:00 2001 From: Sean Date: Sat, 21 Jun 2025 18:53:21 -0400 Subject: [PATCH] Commit Latest --- .../Renderers/BollingerBandRenderer.cs | 295 ++++++++++++++++++ .../UIUtils/TextMarkerImageGenerator.cs | 46 +++ .../ViewModels/BollingerBandViewModel.cs | 54 ---- .../ViewModels/ScottPlotViewModel.cs | 180 +---------- PortfolioManager/portfolio_manager.log | 240 ++++++++++++++ 5 files changed, 586 insertions(+), 229 deletions(-) create mode 100644 PortfolioManager/Renderers/BollingerBandRenderer.cs create mode 100644 PortfolioManager/UIUtils/TextMarkerImageGenerator.cs diff --git a/PortfolioManager/Renderers/BollingerBandRenderer.cs b/PortfolioManager/Renderers/BollingerBandRenderer.cs new file mode 100644 index 0000000..9eadad1 --- /dev/null +++ b/PortfolioManager/Renderers/BollingerBandRenderer.cs @@ -0,0 +1,295 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; +using System.Linq; +using Eremex.AvaloniaUI.Charts; +using MarketData.DataAccess; +using MarketData.Generator; +using MarketData.MarketDataModel; +using MarketData.Utils; +using PortfolioManager.Cache; +using PortfolioManager.DataSeriesViewModels; +using PortfolioManager.Models; +using PortfolioManager.ViewModels; +using ScottPlot; +using ScottPlot.Avalonia; +using ScottPlot.Plottables; +using SkiaSharp; + +namespace PortfolioManager.Renderers +{ + public class BollingerBandRenderer : ModelBase + { + private Price latestPrice = default; + private Price zeroPrice = default; + private bool showLabels =true; + private bool showMarkers = true; + private bool showLegend = true; + private bool syncTradeToBand = true; + private StopLimit stopLimit = default; + private PortfolioTrades portfolioTrades = default; + private PortfolioTrades portfolioTradesLots = default; + private Prices prices = default; + private BollingerBands bollingerBands; + private InsiderTransactionSummaries insiderTransactionSummaries = null; + + public BollingerBandRenderer(AvaPlot plotter) + { + Plotter = plotter ?? throw new ArgumentNullException(nameof(plotter)); + PropertyChanged += OnBollingerBandRendererPropertyChanged; + } + + private void OnBollingerBandRendererPropertyChanged(Object sender, PropertyChangedEventArgs eventArgs) + { + if (eventArgs.PropertyName.Equals("ShowLabels")) + { + } + else if (eventArgs.PropertyName.Equals("ShowMarkers")) + { + } + else if (eventArgs.PropertyName.Equals("ShowLegend")) + { + if (!ShowLegend) Plotter.Plot.HideLegend(); + else Plotter.Plot.ShowLegend(); + } + } + + public void Render() + { + Plotter.Plot.Axes.AutoScale(); + Plotter.Refresh(); + base.OnPropertyChanged("ShowLegend"); + } + + public void SetData(String selectedSymbol, int selectedDayCount) + { + stopLimit = PortfolioDA.GetStopLimit(selectedSymbol); + portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol); + portfolioTradesLots = LotAggregator.CombineLots(portfolioTrades); + + if (null != portfolioTrades && 0 != portfolioTrades.Count) + { + DateGenerator dateGenerator = new DateGenerator(); + DateTime earliestTrade = portfolioTrades[0].TradeDate; + earliestTrade = earliestTrade.AddDays(-30); + int daysBetween = dateGenerator.DaysBetween(earliestTrade, DateTime.Now); + if (daysBetween < selectedDayCount || !syncTradeToBand) prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount); + else prices = PricingDA.GetPrices(selectedSymbol, earliestTrade); + + DateTime earliestInsiderTransactionDate = dateGenerator.GenerateFutureBusinessDate(prices[prices.Count - 1].Date, 30); + insiderTransactionSummaries = InsiderTransactionDA.GetInsiderTransactionSummaries(selectedSymbol, earliestInsiderTransactionDate); + + // calculate the break even price on the open trades for this symbol + PortfolioTrades openTrades = portfolioTrades.GetOpenTrades(); + DateTime latestPricingDate = PricingDA.GetLatestDate(selectedSymbol); + latestPrice = PricingDA.GetPrice(selectedSymbol, latestPricingDate); + zeroPrice = ParityGenerator.GenerateGainLossValue(openTrades, latestPrice); + + + if (!syncTradeToBand) + { + DateTime earliestPricingDate = prices[prices.Count - 1].Date; + earliestPricingDate = earliestPricingDate.AddDays(30); + IEnumerable tradesInRange = (from portfolioTrade in portfolioTradesLots where portfolioTrade.TradeDate >= earliestPricingDate select portfolioTrade); + portfolioTrades = new PortfolioTrades(); + foreach (PortfolioTrade portfolioTrade in tradesInRange) portfolioTrades.Add(portfolioTrade); + portfolioTradesLots = portfolioTrades; + } + } + else + { + prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount); + if (null != prices && 0 != prices.Count) + { + DateGenerator dateGenerator = new DateGenerator(); + DateTime earliestInsiderTransactionDate = dateGenerator.GenerateFutureBusinessDate(prices[prices.Count - 1].Date, 30); + insiderTransactionSummaries = InsiderTransactionDA.GetInsiderTransactionSummaries(selectedSymbol, earliestInsiderTransactionDate); + } + } + bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices); + GenerateCompositeDataSources(bollingerBands); + GenerateZeroPoint(zeroPrice); + } + + /// + /// Generate the ZeroPoint marker and text + /// + /// + private void GenerateZeroPoint(Price zeroPrice) + { + if (!ShowMarkers) return; + ImageMarker imageMarker = default; + Coordinates coordinates = default; + Image image = default; + ZeroPoint = GainLossModel.Price(zeroPrice); + (DateTime[] dates, double[] values) = ZeroPoint.ToXYData(); + + Scatter scatter = Plotter.Plot.Add.Scatter(dates, values, ScottPlot.Color.FromSKColor(SKColors.Blue)); + + image = TextMarkerImageGenerator.ToSPImage(ImageCache.GetInstance().GetImage(ImageCache.ImageType.BlueTriangleUp)); + coordinates = new Coordinates(dates[0].ToOADate(), values[0]); + imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image, 0.125f); + + StringBuilder sb = new StringBuilder(); + sb.Append("Even "); + sb.Append(Utility.FormatCurrency(zeroPrice.Close)); + double parityOffsetPercent = (latestPrice.Close - zeroPrice.Close) / zeroPrice.Close; + sb.Append("(").Append(parityOffsetPercent < 0 ? "" : "+").Append(Utility.FormatPercent(parityOffsetPercent)).Append(")"); + image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 130, 24, 11); + coordinates = new Coordinates(dates[0].ToOADate(), values[0]-5.00); + imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image); + } + + private void GenerateCompositeDataSources(BollingerBands bollingerBands) + { + K = BollingerBandModel.K(bollingerBands); + KL1 = BollingerBandModel.KL1(bollingerBands); + L = BollingerBandModel.L(bollingerBands); + LP1 = BollingerBandModel.LP1(bollingerBands); + High = BollingerBandModel.High(bollingerBands); + Low = BollingerBandModel.Low(bollingerBands); + Close = BollingerBandModel.Close(bollingerBands); + SMAN = BollingerBandModel.SMAN(bollingerBands); + Volume = BollingerBandModel.Volume(bollingerBands); + LeastSquares = BollingerBandModel.LeastSquares(bollingerBands); + + Scatter scatter = default; + { + (DateTime[] dates, double[] values) = K.ToXYData(); + scatter = Plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Green)); + scatter.LegendText = "K"; + scatter.LineWidth = 3; + } + + { + (DateTime[] dates, double[] values) = KL1.ToXYData(); + scatter = Plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Green)); + scatter.LegendText = "KL1"; + scatter.LineWidth = 2; + } + + { + (DateTime[] dates, double[] values) = L.ToXYData(); + scatter = Plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Green)); + scatter.LegendText = "L"; + scatter.LineWidth = 3; + } + + { + (DateTime[] dates, double[] values) = LP1.ToXYData(); + scatter = Plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Green)); + scatter.LegendText = "LP1"; + scatter.LineWidth = 2; + } + + { + (DateTime[] dates, double[] values) = High.ToXYData(); + scatter = Plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Blue)); + scatter.LegendText = "High"; + scatter.LineWidth = 2; + } + + { + (DateTime[] dates, double[] values) = Low.ToXYData(); + scatter = Plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Red)); + scatter.LegendText = "Low"; + scatter.LineWidth = 2; + } + + { + (DateTime[] dates, double[] values) = Close.ToXYData(); + scatter = Plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Black)); + scatter.LegendText = "Close"; + scatter.LineWidth = 2; + } + + { + (DateTime[] dates, double[] values) = LeastSquares.ToXYData(); + scatter = Plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Orange)); + scatter.LegendText = "LeastSquares"; + scatter.LineWidth = 2; + } + + { + (DateTime[] dates, double[] values) = SMAN.ToXYData(); + scatter = Plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Purple)); + scatter.LegendText = "SMAN"; + scatter.LineWidth = 2; + } + } + + public bool SyncTradeToBand + { + get + { + return syncTradeToBand; + } + set + { + syncTradeToBand = value; + base.OnPropertyChanged("SyncTradeToBand"); + } + } + + public bool ShowLabels + { + get + { + return showLabels; + } + set + { + showLabels = value; + base.OnPropertyChanged("ShowLabels"); + } + } + + public bool ShowMarkers + { + get + { + return showMarkers; + } + set + { + showMarkers = value; + base.OnPropertyChanged("ShowMarkers"); + } + } + + public bool ShowLegend + { + get + { + return showLegend; + } + set + { + showLegend = value; + base.OnPropertyChanged("ShowLegend"); + } + } + + public AvaPlot Plotter { get; private set; } + public CompositeDataSource K { get; private set; } = Empty(); + public CompositeDataSource KL1 { get; private set; } = Empty(); + public CompositeDataSource L { get; private set; } = Empty(); + public CompositeDataSource LP1 { get; private set; } = Empty(); + public CompositeDataSource High { get; private set; } = Empty(); + public CompositeDataSource Low { get; private set; } = Empty(); + public CompositeDataSource Close { get; private set; } = Empty(); + public CompositeDataSource SMAN { get; private set; } = Empty(); + public CompositeDataSource Volume { get; private set; } = Empty(); + public CompositeDataSource LeastSquares { get; private set; } = Empty(); + public CompositeDataSource ZeroPoint { get; private set; } = Empty(); + + private static CompositeDataSource Empty() + { + return new CompositeDataSource() + { + DataAdapter = new SortedDateTimeDataAdapter() + }; + } + } +} \ No newline at end of file diff --git a/PortfolioManager/UIUtils/TextMarkerImageGenerator.cs b/PortfolioManager/UIUtils/TextMarkerImageGenerator.cs new file mode 100644 index 0000000..1206708 --- /dev/null +++ b/PortfolioManager/UIUtils/TextMarkerImageGenerator.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using MarketData.CNNProcessing; +using ScottPlot; +using SkiaSharp; + +public static class TextMarkerImageGenerator + { + public static Image GenerateImage(String text,int width=130, int height=24, int fontSize=12) + { + ImageHelper imageHelper = new ImageHelper(); + imageHelper.CreateImage(width, height); //, new PointMapping(width,height,width,0,height,0)); + imageHelper.Fill(SKColors.White); + SKTextAlign align = SKTextAlign.Center; + SKFont font = new SKFont(SKTypeface.FromFamilyName("Arial",500,5,SKFontStyleSlant.Upright), fontSize); + imageHelper.DrawLine(SKColors.Black,1, new SKPoint(0,0), new SKPoint(width-1,0)); // bottom left to right + imageHelper.DrawLine(SKColors.Black,1, new SKPoint(width-1,0), new SKPoint(width-1,height-1)); // up lefthand side + imageHelper.DrawLine(SKColors.Black,1, new SKPoint(0,height-1), new SKPoint(width-1,height-1)); // top left to right + imageHelper.DrawLine(SKColors.Black,1, new SKPoint(0,height-1), new SKPoint(0,0)); // left hand side top to bottom + imageHelper.DrawText(text, new SKPoint(width / 2, height-8), SKColors.Black, align, font); + + using MemoryStream memoryStream = new MemoryStream(); + imageHelper.ToStream().CopyTo(memoryStream); + return new ScottPlot.Image(memoryStream.ToArray()); + } + + public static Image ToSPImage(IImage image) + { + Bitmap bitmap = (Bitmap)image; //ImageCache.GetInstance().GetImage(ImageCache.ImageType.BlueTriangleUp); + using MemoryStream memoryStream = new MemoryStream(); + bitmap.Save(memoryStream); + return new ScottPlot.Image(memoryStream.ToArray()); + } + + public static IImage GenerateImage(int width, int height, SKColor color) + { + ImageHelper imageHelper = new ImageHelper(); + imageHelper.CreateImage(width, height); + imageHelper.Fill(color); + Avalonia.Media.Imaging.Bitmap avBitmap = new Avalonia.Media.Imaging.Bitmap(imageHelper.ToStream()); + return avBitmap; + // avBitmap.Save("c:\\3\\mybitmap.jpg"); } + } + } diff --git a/PortfolioManager/ViewModels/BollingerBandViewModel.cs b/PortfolioManager/ViewModels/BollingerBandViewModel.cs index 84dea98..f31885c 100644 --- a/PortfolioManager/ViewModels/BollingerBandViewModel.cs +++ b/PortfolioManager/ViewModels/BollingerBandViewModel.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; -using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -11,7 +10,6 @@ using CommunityToolkit.Mvvm.Input; using DynamicData; using Eremex.AvaloniaUI.Charts; using MarketData; -using MarketData.CNNProcessing; using MarketData.DataAccess; using MarketData.Generator; using MarketData.MarketDataModel; @@ -22,61 +20,9 @@ using PortfolioManager.DataSeriesViewModels; using PortfolioManager.Extensions; using PortfolioManager.Models; using PortfolioManager.UIUtils; -using ScottPlot; -using SkiaSharp; namespace PortfolioManager.ViewModels { - public static class TextMarkerImageGenerator - { - public static Image GenerateImage(String text) - { - ImageHelper imageHelper = new ImageHelper(); - int fontSize = 12; - int width = 130; - int height = 24; - imageHelper.CreateImage(width, height); //, new PointMapping(width,height,width,0,height,0)); - imageHelper.Fill(SKColors.White); - SKTextAlign align = SKTextAlign.Center; -// SKFont font = new SKFont(SKTypeface.FromFamilyName("Arial",SKFontStyle.Normal), fontSize); - SKFont font = new SKFont(SKTypeface.FromFamilyName("Arial",500,5,SKFontStyleSlant.Upright), fontSize); -// SKFont font = new SKFont(SKTypeface.Default, fontSize); - imageHelper.DrawLine(SKColors.Black,1, new SKPoint(0,0), new SKPoint(width-1,0)); // bottom left to right - imageHelper.DrawLine(SKColors.Black,1, new SKPoint(width-1,0), new SKPoint(width-1,height-1)); // up lefthand side - imageHelper.DrawLine(SKColors.Black,1, new SKPoint(0,height-1), new SKPoint(width-1,height-1)); // top left to right - imageHelper.DrawLine(SKColors.Black,1, new SKPoint(0,height-1), new SKPoint(0,0)); // left hand side top to bottom - imageHelper.DrawText(text, new SKPoint(width / 2, height-8), SKColors.Black, align, font); - - using MemoryStream memoryStream = new MemoryStream(); - imageHelper.ToStream().CopyTo(memoryStream); - return new ScottPlot.Image(memoryStream.ToArray()); - } - - // public static IImage GenerateImage(String text) - // { - // ImageHelper imageHelper = new ImageHelper(); - // int fontSize = 36; - // int width = 130; - // imageHelper.CreateImage(width, fontSize); - // imageHelper.Fill(SKColors.White); - // SKTextAlign align = SKTextAlign.Center; - // SKFont font = new SKFont(SKTypeface.FromFamilyName("Helvetica"), fontSize); - // imageHelper.DrawText(text, new SKPoint(width / 2, fontSize - 2), SKColors.Black, align, font); - // Avalonia.Media.Imaging.Bitmap avBitmap = new Avalonia.Media.Imaging.Bitmap(imageHelper.ToStream()); - // return avBitmap; - // // avBitmap.Save("c:\\3\\mybitmap.jpg"); } - // } - - public static IImage GenerateImage(int width, int height, SKColor color) - { - ImageHelper imageHelper = new ImageHelper(); - imageHelper.CreateImage(width, height); - imageHelper.Fill(color); - Avalonia.Media.Imaging.Bitmap avBitmap = new Avalonia.Media.Imaging.Bitmap(imageHelper.ToStream()); - return avBitmap; - // avBitmap.Save("c:\\3\\mybitmap.jpg"); } - } - } public partial class BollingerBandViewModel : WorkspaceViewModel { diff --git a/PortfolioManager/ViewModels/ScottPlotViewModel.cs b/PortfolioManager/ViewModels/ScottPlotViewModel.cs index ccb7e6c..a3045fb 100644 --- a/PortfolioManager/ViewModels/ScottPlotViewModel.cs +++ b/PortfolioManager/ViewModels/ScottPlotViewModel.cs @@ -1,171 +1,9 @@ using System; -using Avalonia.Media; -using Eremex.AvaloniaUI.Charts; -using MarketData.DataAccess; -using MarketData.Generator; -using MarketData.MarketDataModel; -using PortfolioManager.DataSeriesViewModels; -using PortfolioManager.Models; -using ScottPlot; +using PortfolioManager.Renderers; using ScottPlot.Avalonia; -using ScottPlot.Plottables; -using ShimSkiaSharp; -using SkiaSharp; namespace PortfolioManager.ViewModels { - public class BollingerBandCompositeDataSourceGenerater - { - public BollingerBandCompositeDataSourceGenerater() - { - } - - public void Render(AvaPlot plotter) - { - plotter.Plot.Axes.AutoScale(); - plotter.Plot.HideLegend(); - plotter.Refresh(); - } - - public void SetData(String selectedSymbol, int selectedDayCount, AvaPlot plotter) - { - Prices prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount); - BollingerBands bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices); - - // calculate the break even price on the open trades for this symbol - PortfolioTrades portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol); - PortfolioTrades openTrades = portfolioTrades.GetOpenTrades(); - DateTime latestPricingDate = PricingDA.GetLatestDate(selectedSymbol); - Price latestPrice = PricingDA.GetPrice(selectedSymbol, latestPricingDate); - Price zeroPrice = ParityGenerator.GenerateGainLossValue(openTrades, latestPrice); - GenerateCompositeDataSources(plotter, bollingerBands); - GenerateZeroPoint(plotter, zeroPrice); - } - - private void GenerateZeroPoint(AvaPlot plotter, Price zeroPrice) - { - ZeroPoint = GainLossModel.Price(zeroPrice); - (DateTime[] dates, double[] values) = ZeroPoint.ToXYData(); - - Scatter scatter = plotter.Plot.Add.Scatter(dates, values, ScottPlot.Color.FromSKColor(SKColors.Blue)); - // Marker marker = plotter.Plot.Add.Marker(dates[0].ToOADate(), values[0] / 1.025); - // marker.MarkerFillColor = ScottPlot.Color.FromSKColor(SKColors.Blue); - // marker.MarkerStyle.Shape = MarkerShape.TriUp; - // marker.MarkerStyle.Size = 15; - - Image image = TextMarkerImageGenerator.GenerateImage("Even $52.14 (+.25%)"); - Coordinates coordinates = new Coordinates(dates[0].ToOADate(), values[0] / 1.0125); - ImageMarker marker = plotter.Plot.Add.ImageMarker(coordinates, image); - - - - - -// Marker marker=plotter.Plot.Add.Marker(K.DataAdapter.ItemCount-1,values[0]); - // Marker marker=plotter.Plot.Add.Marker(0,values[0]); - // marker.MarkerFillColor = ScottPlot.Color.FromSKColor(SKColors.Blue); - // marker.MarkerStyle.Shape = MarkerShape.TriUp; - // marker.MarkerStyle.Size = 15; - } - - private void GenerateCompositeDataSources(AvaPlot plotter, BollingerBands bollingerBands) - { - K = BollingerBandModel.K(bollingerBands); - KL1 = BollingerBandModel.KL1(bollingerBands); - L = BollingerBandModel.L(bollingerBands); - LP1 = BollingerBandModel.LP1(bollingerBands); - High = BollingerBandModel.High(bollingerBands); - Low = BollingerBandModel.Low(bollingerBands); - Close = BollingerBandModel.Close(bollingerBands); - SMAN = BollingerBandModel.SMAN(bollingerBands); - Volume = BollingerBandModel.Volume(bollingerBands); - LeastSquares = BollingerBandModel.LeastSquares(bollingerBands); - - Scatter scatter = default; - { - (DateTime[] dates, double[] values) = K.ToXYData(); - scatter = plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Green)); - scatter.LegendText = "K"; - scatter.LineWidth = 3; - } - - { - (DateTime[] dates, double[] values) = KL1.ToXYData(); - scatter = plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Green)); - scatter.LegendText = "KL1"; - scatter.LineWidth = 2; - } - - { - (DateTime[] dates, double[] values) = L.ToXYData(); - scatter = plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Green)); - scatter.LegendText = "L"; - scatter.LineWidth = 3; - } - - { - (DateTime[] dates, double[] values) = LP1.ToXYData(); - scatter = plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Green)); - scatter.LegendText = "LP1"; - scatter.LineWidth = 2; - } - - { - (DateTime[] dates, double[] values) = High.ToXYData(); - scatter = plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Blue)); - scatter.LegendText = "High"; - scatter.LineWidth = 2; - } - - { - (DateTime[] dates, double[] values) = Low.ToXYData(); - scatter = plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Red)); - scatter.LegendText = "Low"; - scatter.LineWidth = 2; - } - - { - (DateTime[] dates, double[] values) = Close.ToXYData(); - scatter = plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Black)); - scatter.LegendText = "Close"; - scatter.LineWidth = 2; - } - - { - (DateTime[] dates, double[] values) = LeastSquares.ToXYData(); - scatter = plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Orange)); - scatter.LegendText = "LeastSquares"; - scatter.LineWidth = 2; - } - - { - (DateTime[] dates, double[] values) = SMAN.ToXYData(); - scatter = plotter.Plot.Add.ScatterLine(dates, values, ScottPlot.Color.FromSKColor(SKColors.Purple)); - scatter.LegendText = "SMAN"; - scatter.LineWidth = 2; - } - } - - public CompositeDataSource K { get; private set; } = Empty(); - public CompositeDataSource KL1 { get; private set; } = Empty(); - public CompositeDataSource L { get; private set; } = Empty(); - public CompositeDataSource LP1 { get; private set; } = Empty(); - public CompositeDataSource High { get; private set; } = Empty(); - public CompositeDataSource Low { get; private set; } = Empty(); - public CompositeDataSource Close { get; private set; } = Empty(); - public CompositeDataSource SMAN { get; private set; } = Empty(); - public CompositeDataSource Volume { get; private set; } = Empty(); - public CompositeDataSource LeastSquares { get; private set; } = Empty(); - public CompositeDataSource ZeroPoint { get; private set; } = Empty(); - - private static CompositeDataSource Empty() - { - return new CompositeDataSource() - { - DataAdapter = new SortedDateTimeDataAdapter() - }; - } - } public partial class ScottPlotViewModel : PlotterWorkspaceViewModel { @@ -180,18 +18,10 @@ namespace PortfolioManager.ViewModels String selectedSymbol = "CRS"; int selectedDayCount = 180; - plotter = e.AvaPlot; // we should store this somewhere - // Prices prices = PricingDA.GetPrices("SPY", 180); - // BollingerBands bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices); - - BollingerBandCompositeDataSourceGenerater bollingerBandCompositeDataSourceGenerater = new BollingerBandCompositeDataSourceGenerater(); - bollingerBandCompositeDataSourceGenerater.SetData(selectedSymbol, selectedDayCount, plotter); -//public void SetData(String selectedSymbol, int selectedDayCount, AvaPlot plotter) - -// bollingerBandCompositeDataSourceGenerater.GenerateCompositeDataSources(plotter, bollingerBands); - bollingerBandCompositeDataSourceGenerater.Render(plotter); - // plotter.Plot.Axes.AutoScale(); - // plotter.Refresh(); + plotter = e.AvaPlot; + BollingerBandRenderer bollingerBandRenderer = new BollingerBandRenderer(plotter); + bollingerBandRenderer.SetData(selectedSymbol, selectedDayCount); + bollingerBandRenderer.Render(); } // ********************************************** P E R S I S T E N C E ************************* public override bool CanPersist() diff --git a/PortfolioManager/portfolio_manager.log b/PortfolioManager/portfolio_manager.log index af320db..f7ed3f5 100644 --- a/PortfolioManager/portfolio_manager.log +++ b/PortfolioManager/portfolio_manager.log @@ -428,3 +428,243 @@ [Thread=1][TRACE.DEBUG][6/19/2025 09:31:19 PM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE [Thread=1][TRACE.VERBOSE][6/19/2025 09:31:19 PM] [PortfolioManager.Program::Main(args)]There were 36 threads still running at application shutdown. [Thread=1][TRACE.VERBOSE][6/19/2025 09:31:19 PM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:06:28 AM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:06:28 AM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:06:28 AM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:06:28 AM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 8:06:31 AM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 8:06:33 AM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 8:06:33 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:06:33 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=14][TRACE.VERBOSE][6/21/2025 8:06:33 AM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 8:06:33 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=1][TRACE.VERBOSE][6/21/2025 8:06:33 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'WaitSleepJoin'. Joining main thread... +[Thread=15][TRACE.VERBOSE][6/21/2025 8:06:34 AM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:06:34 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 8:06:34 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 8:06:34 AM] [PortfolioManager.Program::Main(args)]There were 47 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:06:34 AM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:37 AM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:37 AM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:37 AM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:37 AM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 8:08:40 AM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 8:08:44 AM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 8:08:44 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:44 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=14][TRACE.VERBOSE][6/21/2025 8:08:44 AM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:44 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:44 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=15][TRACE.VERBOSE][6/21/2025 8:08:44 AM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:44 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 8:08:44 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:45 AM] [PortfolioManager.Program::Main(args)]There were 46 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:45 AM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:56 AM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:56 AM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:56 AM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:08:56 AM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 8:08:59 AM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 8:09:05 AM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 8:09:05 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:09:05 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=14][TRACE.VERBOSE][6/21/2025 8:09:05 AM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 8:09:05 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=15][TRACE.VERBOSE][6/21/2025 8:09:05 AM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:09:05 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=1][TRACE.VERBOSE][6/21/2025 8:09:05 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 8:09:05 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 8:09:05 AM] [PortfolioManager.Program::Main(args)]There were 46 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:09:05 AM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:04 AM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:04 AM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:04 AM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:04 AM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 8:10:07 AM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 8:10:31 AM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 8:10:31 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:31 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=14][TRACE.VERBOSE][6/21/2025 8:10:31 AM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:31 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=15][TRACE.VERBOSE][6/21/2025 8:10:31 AM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:31 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:31 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 8:10:31 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:31 AM] [PortfolioManager.Program::Main(args)]There were 43 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:31 AM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:44 AM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:44 AM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:44 AM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:10:44 AM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 8:10:47 AM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 8:11:10 AM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 8:11:10 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:11:10 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=14][TRACE.VERBOSE][6/21/2025 8:11:10 AM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 8:11:10 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=15][TRACE.VERBOSE][6/21/2025 8:11:10 AM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:11:10 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=1][TRACE.VERBOSE][6/21/2025 8:11:10 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 8:11:10 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 8:11:10 AM] [PortfolioManager.Program::Main(args)]There were 42 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:11:10 AM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:12:58 AM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:12:58 AM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:12:58 AM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:12:58 AM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 8:13:03 AM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 8:13:10 AM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 8:13:10 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:13:10 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=14][TRACE.VERBOSE][6/21/2025 8:13:10 AM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 8:13:10 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=15][TRACE.VERBOSE][6/21/2025 8:13:10 AM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:13:10 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=1][TRACE.VERBOSE][6/21/2025 8:13:10 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 8:13:10 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 8:13:10 AM] [PortfolioManager.Program::Main(args)]There were 46 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:13:10 AM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:14:13 AM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:14:13 AM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:14:13 AM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:14:13 AM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 8:14:17 AM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 8:14:31 AM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 8:14:31 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:14:31 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=14][TRACE.VERBOSE][6/21/2025 8:14:31 AM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 8:14:31 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=15][TRACE.VERBOSE][6/21/2025 8:14:31 AM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:14:31 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=1][TRACE.VERBOSE][6/21/2025 8:14:31 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 8:14:31 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 8:14:31 AM] [PortfolioManager.Program::Main(args)]There were 45 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:14:31 AM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:15:08 AM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:15:09 AM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:15:09 AM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:15:09 AM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 8:15:13 AM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 8:15:18 AM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 8:15:18 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:15:18 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=13][TRACE.VERBOSE][6/21/2025 8:15:18 AM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 8:15:18 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=1][TRACE.VERBOSE][6/21/2025 8:15:18 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'WaitSleepJoin'. Joining main thread... +[Thread=14][TRACE.VERBOSE][6/21/2025 8:15:19 AM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:15:19 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 8:15:19 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 8:15:19 AM] [PortfolioManager.Program::Main(args)]There were 45 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:15:19 AM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:16:26 AM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:16:26 AM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:16:26 AM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:16:26 AM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 8:16:32 AM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 8:17:22 AM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 8:17:22 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:17:22 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=16][TRACE.VERBOSE][6/21/2025 8:17:22 AM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 8:17:22 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=17][TRACE.VERBOSE][6/21/2025 8:17:22 AM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:17:22 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=1][TRACE.VERBOSE][6/21/2025 8:17:22 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 8:17:22 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 8:17:22 AM] [PortfolioManager.Program::Main(args)]There were 42 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:17:22 AM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:17:43 AM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:17:43 AM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:17:43 AM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:17:43 AM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 8:17:49 AM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 8:18:01 AM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 8:18:01 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:18:01 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=14][TRACE.VERBOSE][6/21/2025 8:18:01 AM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 8:18:01 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=15][TRACE.VERBOSE][6/21/2025 8:18:01 AM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:18:01 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=1][TRACE.VERBOSE][6/21/2025 8:18:01 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 8:18:01 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 8:18:01 AM] [PortfolioManager.Program::Main(args)]There were 46 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:18:01 AM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:18 AM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:18 AM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:18 AM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:18 AM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 8:34:21 AM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 8:34:21 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:21 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=6][TRACE.VERBOSE][6/21/2025 8:34:21 AM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:21 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=7][TRACE.VERBOSE][6/21/2025 8:34:21 AM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:21 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:21 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 8:34:21 AM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 8:34:21 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:21 AM] [PortfolioManager.Program::Main(args)]There were 36 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:21 AM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:56 AM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:56 AM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:56 AM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 8:34:56 AM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 8:35:00 AM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 8:35:13 AM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 8:35:13 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 8:35:13 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=14][TRACE.VERBOSE][6/21/2025 8:35:13 AM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 8:35:13 AM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=1][TRACE.VERBOSE][6/21/2025 8:35:13 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=15][TRACE.VERBOSE][6/21/2025 8:35:13 AM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:35:13 AM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 8:35:13 AM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 8:35:13 AM] [PortfolioManager.Program::Main(args)]There were 45 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 8:35:13 AM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 1:37:22 PM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 1:37:22 PM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 1:37:22 PM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 1:37:22 PM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 1:37:33 PM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 1:38:00 PM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 1:38:00 PM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 1:38:00 PM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=14][TRACE.VERBOSE][6/21/2025 1:38:00 PM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 1:38:00 PM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=1][TRACE.VERBOSE][6/21/2025 1:38:00 PM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'WaitSleepJoin'. Joining main thread... +[Thread=15][TRACE.VERBOSE][6/21/2025 1:38:01 PM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 1:38:01 PM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 1:38:01 PM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 1:38:01 PM] [PortfolioManager.Program::Main(args)]There were 44 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 1:38:01 PM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 2:49:51 PM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 2:49:51 PM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 2:49:51 PM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 2:49:51 PM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 2:49:55 PM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 2:50:09 PM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 2:50:09 PM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=1][TRACE.VERBOSE][6/21/2025 2:50:09 PM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=14][TRACE.VERBOSE][6/21/2025 2:50:09 PM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 2:50:09 PM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=15][TRACE.VERBOSE][6/21/2025 2:50:09 PM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 2:50:09 PM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=1][TRACE.VERBOSE][6/21/2025 2:50:09 PM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 2:50:09 PM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 2:50:09 PM] [PortfolioManager.Program::Main(args)]There were 45 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 2:50:09 PM] [PortfolioManager.Program::Main(args)][MAIN:EXIT] +[Thread=1][TRACE.VERBOSE][6/21/2025 6:45:51 PM] [PortfolioManager.Program::Main(args)][MAIN:STARTING] +[Thread=1][TRACE.VERBOSE][6/21/2025 6:45:51 PM] [PortfolioManager.Program::Main(args)]Using portfolio_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 6:45:51 PM] [PortfolioManager.Program::Main(args)]Using market_data at Adrastea +[Thread=1][TRACE.VERBOSE][6/21/2025 6:45:51 PM] [PortfolioManager.Program::Main(args)]Using user_data at Adrastea +[Thread=1][TRACE.DEBUG][6/21/2025 6:45:54 PM] [PortfolioManager.Cache.ImageCache::.ctor()]Reading assets from C:\Avalonia\PortfolioManager/Assets +[Thread=1][TRACE.DEBUG][6/21/2025 6:46:06 PM] [PortfolioManager.App+<>c__DisplayClass1_1::b__1(,)]App: Received ClosingHandler event +[Thread=1][TRACE.DEBUG][6/21/2025 6:46:06 PM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] +[Thread=14][TRACE.VERBOSE][6/21/2025 6:46:06 PM] [MarketData.Cache.LocalPriceCache::ThreadProc()][LocalPriceCache:ThreadProc] Thread ended. Items in cache:0 +[Thread=1][TRACE.VERBOSE][6/21/2025 6:46:06 PM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=1][TRACE.VERBOSE][6/21/2025 6:46:06 PM] [MarketData.Cache.LocalPriceCache::Dispose()][LocalPriceCache:Dispose] End +[Thread=15][TRACE.VERBOSE][6/21/2025 6:46:06 PM] [MarketData.Cache.GBPriceCache::ThreadProc()][GBPriceCache:ThreadProc]Thread ended. +[Thread=1][TRACE.VERBOSE][6/21/2025 6:46:06 PM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose]Thread state is 'Running'. Joining main thread... +[Thread=1][TRACE.VERBOSE][6/21/2025 6:46:06 PM] [MarketData.Cache.GBPriceCache::Dispose()][GBPriceCache:Dispose] End. +[Thread=1][TRACE.DEBUG][6/21/2025 6:46:06 PM] [PortfolioManager.ViewModels.MainWindowViewModel::OnDispose()][MainWindowViewModel:OnDispose] LEAVE +[Thread=1][TRACE.VERBOSE][6/21/2025 6:46:06 PM] [PortfolioManager.Program::Main(args)]There were 46 threads still running at application shutdown. +[Thread=1][TRACE.VERBOSE][6/21/2025 6:46:06 PM] [PortfolioManager.Program::Main(args)][MAIN:EXIT]