From 27881870f6d443b53516c3d267f5105395d9f632 Mon Sep 17 00:00:00 2001 From: "Sean Kessler (Europa)" Date: Wed, 25 Jun 2025 21:05:14 -0400 Subject: [PATCH] Commit Latest --- .../Renderers/BollingerBandRenderer.cs | 109 +++++++++++------- .../ViewModels/ScottPlotViewModel.cs | 96 +++++++-------- PortfolioManager/Views/ScottPlotView.axaml.cs | 2 +- PortfolioManager/saveparams.config | 1 + 4 files changed, 115 insertions(+), 93 deletions(-) diff --git a/PortfolioManager/Renderers/BollingerBandRenderer.cs b/PortfolioManager/Renderers/BollingerBandRenderer.cs index dee0acd..4bb0967 100644 --- a/PortfolioManager/Renderers/BollingerBandRenderer.cs +++ b/PortfolioManager/Renderers/BollingerBandRenderer.cs @@ -33,8 +33,30 @@ namespace PortfolioManager.Renderers public class OffsetDictionary { - enum OffsetType {VerticalOffset5PC, VerticalOffset3PC, VerticalOffset1PC,HorizontalOffset5PC,HorizontalOffset3PC,HorizontalOffset1PC}; - private Dictionary offsetDictionary; + public enum OffsetType + { + VerticalOffset5PC, VerticalOffset3PC, VerticalOffset1PC, HorizontalOffset5PC, HorizontalOffset3PC, HorizontalOffset1PC, + MinBollingerDate, MaxBollingerDate, MinBollingerValue, MaxBollingerValue + }; + + private Dictionary offsetDictionary = new Dictionary(); + + public void Add(OffsetType offsetType, double value) + { + if (offsetDictionary.ContainsKey(offsetType)) + { + offsetDictionary[offsetType] = value; + } + else + { + offsetDictionary.Add(offsetType, value); + } + } + + public double Offset(OffsetType offsetType) + { + return offsetDictionary[offsetType]; + } } @@ -58,14 +80,7 @@ namespace PortfolioManager.Renderers private BollingerBands bollingerBands; private InsiderTransactionSummaries insiderTransactionSummaries = null; - // double maxBollingerDate = 0.00; - // double minBollingerDate = 0.00; - //double spread = 0.00; - double percentShiftHorz3PC = 0.00; - double percentShiftHorz1PC = 0.00; - double percentShiftVert3PC = 0.00; - double percentShiftVert1PC = 0.00; - double percentShiftVert5PC = 0.00; + private OffsetDictionary offsets = new OffsetDictionary(); public BollingerBandRenderer(AvaPlot plotter) { @@ -91,10 +106,10 @@ namespace PortfolioManager.Renderers if (!ShowLegend) Plotter.Plot.HideLegend(); else Plotter.Plot.ShowLegend(); } - else if (eventArgs.PropertyName.Equals("ShowInsiderTransactions")) - { - SetData(selectedSymbol, selectedDayCount); - } + // else if (eventArgs.PropertyName.Equals("ShowInsiderTransactions")) + // { + // SetData(selectedSymbol, selectedDayCount); + // } } public void Render() @@ -152,20 +167,7 @@ namespace PortfolioManager.Renderers } } bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices); - - double maxBollingerDate = bollingerBands.Max(x => x.Date).ToOADate(); - double minBollingerDate = bollingerBands.Min(x => x.Date).ToOADate(); - double maxBollingerValue = bollingerBands.Max(x => x.K); - double minBollingerValue = bollingerBands.Min(x => x.L); - - double spreadHorz = (maxBollingerDate - minBollingerDate); - double spreadVert = (maxBollingerValue - minBollingerValue); - percentShiftHorz3PC = spreadHorz * .03; - percentShiftHorz1PC = spreadHorz * .01; - percentShiftVert3PC = spreadVert * .03; - percentShiftVert1PC = spreadVert * .01; - percentShiftVert5PC = spreadVert * .05; - + CalculateOffsets(); GenerateBollingerBands(); GenerateZeroPoint(zeroPrice); GenerateInsiderTransactions(); @@ -173,6 +175,28 @@ namespace PortfolioManager.Renderers GenerateTradePoints(); } + private void CalculateOffsets() + { + double maxBollingerDate = bollingerBands.Max(x => x.Date).ToOADate(); + double minBollingerDate = bollingerBands.Min(x => x.Date).ToOADate(); + double maxBollingerValue = bollingerBands.Max(x => x.K); + double minBollingerValue = bollingerBands.Min(x => x.L); + + offsets.Add(OffsetDictionary.OffsetType.MaxBollingerDate,maxBollingerDate); + offsets.Add(OffsetDictionary.OffsetType.MinBollingerDate,minBollingerDate); + offsets.Add(OffsetDictionary.OffsetType.MaxBollingerValue,maxBollingerValue); + offsets.Add(OffsetDictionary.OffsetType.MinBollingerValue,minBollingerValue); + + double spreadHorz = (maxBollingerDate - minBollingerDate); + double spreadVert = (maxBollingerValue - minBollingerValue); + offsets.Add(OffsetDictionary.OffsetType.HorizontalOffset1PC,spreadHorz * .01); + offsets.Add(OffsetDictionary.OffsetType.HorizontalOffset3PC,spreadHorz * .03); + offsets.Add(OffsetDictionary.OffsetType.HorizontalOffset5PC,spreadHorz * .05); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset1PC,spreadVert * .01); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset3PC,spreadVert *.03); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset5PC,spreadVert * .05); + } + /// /// Generate the ZeroPoint marker and text /// @@ -184,21 +208,21 @@ namespace PortfolioManager.Renderers 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)); + (DateTime[] dates, double[] values) = ZeroPoint.ToXYData(); // There is only a single value in this collection +// Place the triangle marker image = TextMarkerImageGenerator.ToSPImage(ImageCache.GetInstance().GetImage(ImageCache.ImageType.BlueTriangleUp)); coordinates = new Coordinates(dates[0].ToOADate(), values[0]); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image, SizeFactor.Normal); +// Place the text marker 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, FontFactor.FontSize); - coordinates = new Coordinates(dates[0].ToOADate(), values[0] - 5.00); + coordinates = new Coordinates(dates[0].ToOADate() - offsets.Offset(OffsetDictionary.OffsetType.HorizontalOffset3PC), values[0] - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image); } @@ -245,8 +269,7 @@ namespace PortfolioManager.Renderers sb.Append(" (").Append(percentOffsetFromLow > 0 ? "+" : "").Append(Utility.FormatPercent(percentOffsetFromLow)).Append(")"); } Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 155, 24, FontFactor.FontSize); - // Coordinates coordinates = new Coordinates(dates[0].ToOADate()-1, values[0] - 5.00); - Coordinates coordinates = new Coordinates(limit.EffectiveDate.ToOADate(), limit.StopPrice - 5.00); + Coordinates coordinates = new Coordinates(limit.EffectiveDate.ToOADate(), limit.StopPrice - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image); } } @@ -261,10 +284,8 @@ namespace PortfolioManager.Renderers sb.Append(stopLimit.StopType).Append(" "); sb.Append(Utility.FormatCurrency(stopLimit.StopPrice)); sb.Append(" (").Append(percentOffsetFromLow > 0 ? "+" : "").Append(Utility.FormatPercent(percentOffsetFromLow)).Append(")"); - Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 155, 24, FontFactor.FontSize); - // Coordinates coordinates = new Coordinates(latestPrice.Date.ToOADate()/(1+onePercent), stopLimit.StopPrice - 5.00); - Coordinates coordinates = new Coordinates(latestPrice.Date.ToOADate() - percentShiftHorz3PC, stopLimit.StopPrice - 5.00); + Coordinates coordinates = new Coordinates(latestPrice.Date.ToOADate() - offsets.Offset(OffsetDictionary.OffsetType.HorizontalOffset3PC), stopLimit.StopPrice - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image); } } @@ -295,7 +316,7 @@ namespace PortfolioManager.Renderers sb.Append(Utility.FormatCurrency(portfolioTrade.Price)); Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 150, 24, FontFactor.FontSize); - Coordinates coordinates = new Coordinates(portfolioTrade.TradeDate.ToOADate(), portfolioTrade.Price - percentShiftVert5PC); + Coordinates coordinates = new Coordinates(portfolioTrade.TradeDate.ToOADate(), portfolioTrade.Price - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image); } } @@ -337,7 +358,7 @@ namespace PortfolioManager.Renderers { DateTime date = dates[index]; double value = values[index]; - coordinates = new Coordinates(date.ToOADate(), value); + coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageDisposed, SizeFactor.Small); } } @@ -348,7 +369,7 @@ namespace PortfolioManager.Renderers { DateTime date = dates[index]; double value = values[index]; - coordinates = new Coordinates(date.ToOADate(), value); + coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageDisposed, SizeFactor.Normal); } } @@ -359,7 +380,7 @@ namespace PortfolioManager.Renderers { DateTime date = dates[index]; double value = values[index]; - coordinates = new Coordinates(date.ToOADate(), value); + coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageDisposed, SizeFactor.Large); } } @@ -371,7 +392,7 @@ namespace PortfolioManager.Renderers { DateTime date = dates[index]; double value = values[index]; - coordinates = new Coordinates(date.ToOADate(), value); + coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageAcquired, SizeFactor.Small); } } @@ -382,7 +403,7 @@ namespace PortfolioManager.Renderers { DateTime date = dates[index]; double value = values[index]; - coordinates = new Coordinates(date.ToOADate(), value); + coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageAcquired, SizeFactor.Normal); } } @@ -393,7 +414,7 @@ namespace PortfolioManager.Renderers { DateTime date = dates[index]; double value = values[index]; - coordinates = new Coordinates(date.ToOADate(), value); + coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageAcquired, SizeFactor.Large); } } diff --git a/PortfolioManager/ViewModels/ScottPlotViewModel.cs b/PortfolioManager/ViewModels/ScottPlotViewModel.cs index 5cf8643..6e0b6a8 100644 --- a/PortfolioManager/ViewModels/ScottPlotViewModel.cs +++ b/PortfolioManager/ViewModels/ScottPlotViewModel.cs @@ -20,53 +20,51 @@ namespace PortfolioManager.ViewModels { 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; + 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; + private BollingerBandRenderer bollingerBandRenderer = default; + private bool showInsiderTransactions = true; - public ScottPlotViewModel() - { - DisplayName = "Bollinger"; -// Plotter = new AvaPlot(); -// base.OnPropertyChanged("Plotter"); - OnPlotterLoadedEventHandler += PlotterLoadedEvent; - PropertyChanged += OnViewModelPropertyChanged; - Initialize(); - } + public ScottPlotViewModel() + { + DisplayName = "Bollinger"; + OnPlotterLoadedEventHandler += PlotterLoadedEvent; + PropertyChanged += OnViewModelPropertyChanged; + Initialize(); + } - protected override void OnDispose() - { - MDTrace.WriteLine(LogLevel.DEBUG, $"Dispose BollingerBandViewModel"); - base.OnDispose(); - } + 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"); - } - }); - } + 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"); + } + }); + } // ******************************************************************************************************************************************* @@ -78,7 +76,7 @@ namespace PortfolioManager.ViewModels public void PlotterLoadedEvent(object sender, PlotterLoadedEventArgs e) { base.OnPropertyChanged("Plotter"); - } + } private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs) { @@ -88,6 +86,7 @@ namespace PortfolioManager.ViewModels eventArgs.PropertyName.Equals("SelectedSymbol") || eventArgs.PropertyName.Equals("ShowRiskFree") || eventArgs.PropertyName.Equals("LeastSquaresFit") || + eventArgs.PropertyName.Equals("CheckBoxShowInsiderTransactions") || eventArgs.PropertyName.Equals("SelectedDayCount")) && !String.IsNullOrEmpty(selectedSymbol) && default != Plotter) @@ -97,6 +96,7 @@ namespace PortfolioManager.ViewModels { companyName = PricingDA.GetNameForSymbol(selectedSymbol); bollingerBandRenderer = new BollingerBandRenderer(Plotter); + bollingerBandRenderer.ShowInsiderTransactions=showInsiderTransactions; bollingerBandRenderer.SetData(selectedSymbol, selectedDayCount); bollingerBandRenderer.Render(); }); @@ -276,12 +276,12 @@ namespace PortfolioManager.ViewModels { get { - if (null == bollingerBandRenderer) return false; - return bollingerBandRenderer.ShowInsiderTransactions; + return showInsiderTransactions; } set { - bollingerBandRenderer.ShowInsiderTransactions = value; + showInsiderTransactions = value; + base.OnPropertyChanged("CheckBoxShowInsiderTransactions"); } } diff --git a/PortfolioManager/Views/ScottPlotView.axaml.cs b/PortfolioManager/Views/ScottPlotView.axaml.cs index 56bd04d..949f049 100644 --- a/PortfolioManager/Views/ScottPlotView.axaml.cs +++ b/PortfolioManager/Views/ScottPlotView.axaml.cs @@ -22,7 +22,7 @@ public partial class ScottPlotView : UserControl { ScottPlotView view = (sender as ScottPlotView); PlotterWorkspaceViewModel viewModel = (DataContext as PlotterWorkspaceViewModel); - if (default == viewModel.Plotter) + if (null!=viewModel && default == viewModel.Plotter) { viewModel.Plotter = new AvaPlot(); viewModel.OnPlotterLoaded(viewModel.Plotter); diff --git a/PortfolioManager/saveparams.config b/PortfolioManager/saveparams.config index e69de29..6f1cd46 100644 --- a/PortfolioManager/saveparams.config +++ b/PortfolioManager/saveparams.config @@ -0,0 +1 @@ +Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,KEP,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True