From 5c5ed130616c57bd6d99584f3a1860b07cefc754 Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 24 Jun 2025 19:34:26 -0400 Subject: [PATCH] Commit Latest --- .../Renderers/BollingerBandRenderer.cs | 9 + .../ViewModels/ScottPlotViewModel.cs | 285 ++++++++++++++++-- .../Views/BollingerBandView.axaml | 2 +- PortfolioManager/Views/ScottPlotView.axaml | 75 ++++- PortfolioManager/Views/ScottPlotView.axaml.cs | 17 +- 5 files changed, 345 insertions(+), 43 deletions(-) diff --git a/PortfolioManager/Renderers/BollingerBandRenderer.cs b/PortfolioManager/Renderers/BollingerBandRenderer.cs index 2feaa5e..4d0604a 100644 --- a/PortfolioManager/Renderers/BollingerBandRenderer.cs +++ b/PortfolioManager/Renderers/BollingerBandRenderer.cs @@ -88,6 +88,7 @@ namespace PortfolioManager.Renderers portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol); portfolioTradesLots = LotAggregator.CombineLots(portfolioTrades); + Plotter.Plot.Clear(); if (null != portfolioTrades && 0 != portfolioTrades.Count) { DateGenerator dateGenerator = new DateGenerator(); @@ -451,6 +452,14 @@ namespace PortfolioManager.Renderers // *********************************************************** P R O P E R T I E S ***************************************************** + public Prices Prices + { + get + { + return prices; + } + } + public bool SyncTradeToBand { get diff --git a/PortfolioManager/ViewModels/ScottPlotViewModel.cs b/PortfolioManager/ViewModels/ScottPlotViewModel.cs index 274ae6e..e2127aa 100644 --- a/PortfolioManager/ViewModels/ScottPlotViewModel.cs +++ b/PortfolioManager/ViewModels/ScottPlotViewModel.cs @@ -1,42 +1,261 @@ using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DynamicData; +using MarketData; +using MarketData.Cache; +using MarketData.DataAccess; +using MarketData.MarketDataModel; +using MarketData.Utils; using PortfolioManager.Renderers; +using PortfolioManager.UIUtils; using ScottPlot.Avalonia; namespace PortfolioManager.ViewModels { - - public partial class ScottPlotViewModel : PlotterWorkspaceViewModel + public partial class ScottPlotViewModel : PlotterWorkspaceViewModel + { +// private AvaPlot plotter = default; + private List watchListNames; + private String selectedWatchList; + private ObservableCollection symbols = new ObservableCollection(); + private List dayCounts = new int[] { 60, 90, 180, 360, 720, 1440, 3600 }.ToList(); + private int selectedDayCount = 90; + private bool isBusy = false; + private String selectedSymbol = default; + private String companyName = default; + BollingerBandRenderer bollingerBandRenderer = default; + + public ScottPlotViewModel() { - private AvaPlot plotter = default; - public ScottPlotViewModel() - { - OnPlotterLoadedEventHandler += PlotterLoadedEvent; - } - - public void PlotterLoadedEvent(object sender, PlotterLoadedEventArgs e) - { - // String selectedSymbol = "VST"; - String selectedSymbol = "CRS"; - int selectedDayCount = 180; - - 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() - { - return false; - } - - public override SaveParameters GetSaveParameters() - { - return null; - } - - public override void SetSaveParameters(SaveParameters saveParameters) - { - } + DisplayName = "Bollinger"; + Plotter = new AvaPlot(); + base.OnPropertyChanged("Plotter"); + OnPlotterLoadedEventHandler += PlotterLoadedEvent; + PropertyChanged += OnViewModelPropertyChanged; + Initialize(); } + + protected override void OnDispose() + { + MDTrace.WriteLine(LogLevel.DEBUG, $"Dispose BollingerBandViewModel"); + base.OnDispose(); + } + + private void Initialize(bool executePropertyChanged = true) + { + Task workerTask = Task.Factory.StartNew(() => + { + MDTrace.WriteLine(LogLevel.DEBUG, $"BollingerBandViewModel::Initialize()"); + watchListNames = WatchListDA.GetWatchLists(); + watchListNames.Insert(0, UIConstants.CONST_ALL); + selectedWatchList = watchListNames.Find(x => x.Equals("Valuations")); + symbols.AddRange(WatchListDA.GetWatchList(selectedWatchList)); + }); + workerTask.ContinueWith((continuation) => + { + if (executePropertyChanged) + { + base.OnPropertyChanged("Symbols"); + base.OnPropertyChanged("WatchListNames"); + base.OnPropertyChanged("SelectedWatchList"); + } + }); + } + + + // ******************************************************************************************************************************************* + /// + /// This event will be called the first time the view is rendered and each time the view comes back into focus (i.e.) Tab activated + /// + /// + /// + public void PlotterLoadedEvent(object sender, PlotterLoadedEventArgs e) + { + // Plotter = e.AvaPlot; + // Plotter.Plot.Clear(); + // Plotter.Refresh(); + // base.OnPropertyChanged("SelectedSymbol"); + // MDTrace.WriteLine(LogLevel.DEBUG,$"SelectedSymbol:{selectedSymbol}"); + // if (default == Plotter) + // { + // Plotter = e.AvaPlot; + // Plotter.Plot.Clear(); + // } + // else + // { + // Plotter = e.AvaPlot; + // Plotter.Plot.Clear(); + // base.OnPropertyChanged("SelectedSymbol"); + // } + } + + private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs) + { + + if ((eventArgs.PropertyName.Equals("SyncTradeToBand") || + eventArgs.PropertyName.Equals("ShowTradeLabels") || + eventArgs.PropertyName.Equals("SelectedSymbol") || + eventArgs.PropertyName.Equals("ShowRiskFree") || + eventArgs.PropertyName.Equals("LeastSquaresFit") || + eventArgs.PropertyName.Equals("SelectedDayCount")) + && !String.IsNullOrEmpty(selectedSymbol) + && default!=Plotter) + { + IsBusy = true; + Task workerTask = Task.Factory.StartNew(() => + { + companyName = PricingDA.GetNameForSymbol(selectedSymbol); + bollingerBandRenderer = new BollingerBandRenderer(Plotter); + bollingerBandRenderer.SetData(selectedSymbol, selectedDayCount); + bollingerBandRenderer.Render(); + }); + workerTask.ContinueWith((continuation) => + { + base.OnPropertyChanged("GraphTitle"); + IsBusy = false; + }); + } + } + + // ********************************************** P E R S I S T E N C E ************************* + public override bool CanPersist() + { + return false; + } + + public override SaveParameters GetSaveParameters() + { + return null; + } + + public override void SetSaveParameters(SaveParameters saveParameters) + { + } + + // ****************************************************** P R O P E R T I E S ************************************************ + + public AvaPlot Plotter { get; set; } = default; + + public String GraphTitle + { + get + { + if (null == companyName || null == bollingerBandRenderer || null == bollingerBandRenderer.Prices) return ""; + String displayCompanyName = companyName; + if (displayCompanyName.Length > 40) displayCompanyName = displayCompanyName.Substring(0, 40) + "..."; + StringBuilder sb = new StringBuilder(); + float change = float.NaN; + Prices prices2day = new Prices(bollingerBandRenderer.Prices.Take(2).ToList()); + if (2 == prices2day.Count) change = prices2day.GetReturns()[0]; + sb.Append(displayCompanyName); + sb.Append(" (").Append(selectedSymbol).Append(") "); + sb.Append(Utility.DateTimeToStringMMHDDHYYYY(bollingerBandRenderer.Prices[bollingerBandRenderer.Prices.Count - 1].Date)); + sb.Append(" Thru "); + sb.Append(Utility.DateTimeToStringMMHDDHYYYY(bollingerBandRenderer.Prices[0].Date)); + sb.Append(" (").Append(Utility.FormatCurrency(bollingerBandRenderer.Prices[0].Close)); + sb.Append("/").Append(Utility.FormatCurrency(bollingerBandRenderer.Prices[0].Low)); + if (!float.IsNaN(change)) + { + sb.Append(","); + sb.Append(change >= 0.00 ? "+" : "").Append(Utility.FormatPercent((double)change)); + } + sb.Append(")"); + return sb.ToString(); + } + } + + public List WatchListNames + { + get + { + return watchListNames; + } + } + + public String SelectedWatchList + { + get + { + return selectedWatchList; + } + set + { + selectedWatchList = value; + base.OnPropertyChanged("SelectedWatchList"); + } + } + + public ObservableCollection Symbols + { + get + { + return symbols; + } + } + + public int SelectedDayCount + { + get + { + return selectedDayCount; + } + set + { + selectedDayCount = value; + base.OnPropertyChanged("SelectedDayCount"); + } + } + + public List DayCounts + { + get + { + return dayCounts; + } + } + + public String SelectedSymbol + { + get + { + return selectedSymbol; + } + set + { + if (String.IsNullOrEmpty(value)) + { + return; + } + selectedSymbol = value; + base.OnPropertyChanged("SelectedSymbol"); + } + } + + public bool IsBusy + { + get + { + return isBusy; + } + set + { + isBusy = value; + base.OnPropertyChanged("IsBusy"); + } + } + + public override String Title + { + get + { + if (null == selectedSymbol) return DisplayName; + return "Bollinger " + "(" + selectedSymbol + ")"; + } + } + } } \ No newline at end of file diff --git a/PortfolioManager/Views/BollingerBandView.axaml b/PortfolioManager/Views/BollingerBandView.axaml index 6142eb8..1eb786c 100644 --- a/PortfolioManager/Views/BollingerBandView.axaml +++ b/PortfolioManager/Views/BollingerBandView.axaml @@ -22,7 +22,7 @@ - + diff --git a/PortfolioManager/Views/ScottPlotView.axaml b/PortfolioManager/Views/ScottPlotView.axaml index 4396bdb..3d0d065 100644 --- a/PortfolioManager/Views/ScottPlotView.axaml +++ b/PortfolioManager/Views/ScottPlotView.axaml @@ -11,5 +11,78 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:DataType="vm:ScottPlotViewModel" x:Class="PortfolioManager.Views.ScottPlotView"> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PortfolioManager/Views/ScottPlotView.axaml.cs b/PortfolioManager/Views/ScottPlotView.axaml.cs index 1ca6712..fbe6b15 100644 --- a/PortfolioManager/Views/ScottPlotView.axaml.cs +++ b/PortfolioManager/Views/ScottPlotView.axaml.cs @@ -19,13 +19,14 @@ public partial class ScottPlotView : UserControl private void AvaPlot_Loaded(object sender, RoutedEventArgs e) { // This code will execute when the AvaPlot is loaded - if (sender is ScottPlot.Avalonia.AvaPlot) - { - if (default == avaPlot) - { - PlotterWorkspaceViewModel viewModel = (DataContext as PlotterWorkspaceViewModel); - viewModel.OnPlotterLoaded(this.Find("AvaPlot")); - } - } + // if (sender is ScottPlot.Avalonia.AvaPlot) + // { + // if (default == avaPlot) + // { + // PlotterWorkspaceViewModel viewModel = (DataContext as PlotterWorkspaceViewModel); + // avaPlot = this.Find("AvaPlot"); + // viewModel.OnPlotterLoaded(avaPlot); + // } + // } } } \ No newline at end of file