From c644cf12ddf0544e664866cd557554f1b178ab4a Mon Sep 17 00:00:00 2001 From: Sean Date: Sat, 5 Jul 2025 21:55:47 -0400 Subject: [PATCH] Commit Latest --- .../Renderers/BollingerBandRenderer.cs | 37 ++++++--- .../ViewModels/BollingerBandViewModel.cs | 1 + .../ViewModels/MGSHMomentumViewModel.cs | 83 ++++++++++++++++++- .../Views/BollingerBandView.axaml.cs | 25 ++++++ 4 files changed, 133 insertions(+), 13 deletions(-) diff --git a/PortfolioManager/Renderers/BollingerBandRenderer.cs b/PortfolioManager/Renderers/BollingerBandRenderer.cs index fa5acb6..4d52def 100644 --- a/PortfolioManager/Renderers/BollingerBandRenderer.cs +++ b/PortfolioManager/Renderers/BollingerBandRenderer.cs @@ -35,7 +35,7 @@ namespace PortfolioManager.Renderers { public enum OffsetType { - VerticalOffset5PC, VerticalOffset3PC, VerticalOffset1PC, HorizontalOffset5PC, HorizontalOffset3PC, HorizontalOffset1PC, + VerticalOffset15PC, VerticalOffset10PC, VerticalOffset5PC, VerticalOffset3PC, VerticalOffset1PC, HorizontalOffset5PC, HorizontalOffset3PC, HorizontalOffset1PC, MinBollingerDate, MaxBollingerDate, MinBollingerValue, MaxBollingerValue }; @@ -107,7 +107,7 @@ namespace PortfolioManager.Renderers Plotter.Plot.Axes.Left.TickGenerator = new ScottPlot.TickGenerators.NumericAutomatic() { LabelFormatter = (double value) => value.ToString("C") // "C" format specifier formats as currency - }; + }; Plotter.Plot.Axes.DateTimeTicksBottom(); Plotter.Plot.Axes.AutoScale(); Plotter.Plot.XLabel("Date"); @@ -196,6 +196,8 @@ namespace PortfolioManager.Renderers offsets.Add(OffsetDictionary.OffsetType.VerticalOffset1PC,spreadVert * .01); offsets.Add(OffsetDictionary.OffsetType.VerticalOffset3PC,spreadVert *.03); offsets.Add(OffsetDictionary.OffsetType.VerticalOffset5PC,spreadVert * .05); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset10PC,spreadVert * .10); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset15PC,spreadVert * .15); } /// @@ -233,13 +235,13 @@ namespace PortfolioManager.Renderers coordinates = new Coordinates(dates[0].ToOADate(), values[0]); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image, SizeFactor.Normal); + if(!showTradeLabels)return; // 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); image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), FontFactor.FontSize); 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); @@ -271,6 +273,8 @@ namespace PortfolioManager.Renderers ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageStopLimitMarker, SizeFactor.Normal); } + if(!showTradeLabels)return; + // Add the text marker if (null != stopLimits) { @@ -287,7 +291,6 @@ namespace PortfolioManager.Renderers double percentOffsetFromLow = ((latestPrice.Low - limit.StopPrice) / limit.StopPrice); sb.Append(" (").Append(percentOffsetFromLow > 0 ? "+" : "").Append(Utility.FormatPercent(percentOffsetFromLow)).Append(")"); } -// Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), 155, 24, FontFactor.FontSize); Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), FontFactor.FontSize); Coordinates coordinates = new Coordinates(limit.EffectiveDate.ToOADate(), limit.StopPrice - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image); @@ -304,7 +307,6 @@ 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); Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), FontFactor.FontSize); 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); @@ -382,7 +384,7 @@ namespace PortfolioManager.Renderers { DateTime date = dates[index]; double value = values[index]; - coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); + coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset15PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageDisposed, SizeFactor.Small); } } @@ -393,7 +395,7 @@ namespace PortfolioManager.Renderers { DateTime date = dates[index]; double value = values[index]; - coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); + coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset15PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageDisposed, SizeFactor.Normal); } } @@ -404,7 +406,7 @@ namespace PortfolioManager.Renderers { DateTime date = dates[index]; double value = values[index]; - coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); + coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset15PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageDisposed, SizeFactor.Large); } } @@ -416,7 +418,7 @@ namespace PortfolioManager.Renderers { DateTime date = dates[index]; double value = values[index]; - coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); + coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset15PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageAcquired, SizeFactor.Small); } } @@ -427,7 +429,7 @@ namespace PortfolioManager.Renderers { DateTime date = dates[index]; double value = values[index]; - coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); + coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset15PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageAcquired, SizeFactor.Normal); } } @@ -438,7 +440,7 @@ namespace PortfolioManager.Renderers { DateTime date = dates[index]; double value = values[index]; - coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset5PC)); + coordinates = new Coordinates(date.ToOADate(),offsets.Offset(OffsetDictionary.OffsetType.MinBollingerValue) - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset15PC)); imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageAcquired, SizeFactor.Large); } } @@ -519,6 +521,19 @@ namespace PortfolioManager.Renderers // *********************************************************** P R O P E R T I E S ***************************************************** + public StopLimits ExternalStopLimits + { + get + { + return stopLimits; + } + set + { + stopLimits = value; + base.OnPropertyChanged("ExternalStopLimits"); + } + } + public Prices Prices { get diff --git a/PortfolioManager/ViewModels/BollingerBandViewModel.cs b/PortfolioManager/ViewModels/BollingerBandViewModel.cs index fd0598d..11c94b6 100644 --- a/PortfolioManager/ViewModels/BollingerBandViewModel.cs +++ b/PortfolioManager/ViewModels/BollingerBandViewModel.cs @@ -124,6 +124,7 @@ namespace PortfolioManager.ViewModels bollingerBandRenderer.SyncTradeToBand = syncTradeToBand; bollingerBandRenderer.ShowInsiderTransactions = showInsiderTransactions; bollingerBandRenderer.ShowTradeLabels = showTradeLabels; + bollingerBandRenderer.ExternalStopLimits = stopLimits; bollingerBandRenderer.SetData(selectedSymbol, selectedDayCount); bollingerBandRenderer.Render(); }); diff --git a/PortfolioManager/ViewModels/MGSHMomentumViewModel.cs b/PortfolioManager/ViewModels/MGSHMomentumViewModel.cs index 17f6834..edbddd5 100644 --- a/PortfolioManager/ViewModels/MGSHMomentumViewModel.cs +++ b/PortfolioManager/ViewModels/MGSHMomentumViewModel.cs @@ -21,6 +21,7 @@ using MarketData.MarketDataModel; using MarketData.Utils; using PortfolioManager.DataSeriesViewModels; using PortfolioManager.Dialogs; +using PortfolioManager.Extensions; using PortfolioManager.Models; using PortfolioManager.UIUtils; using StopLimit = MarketData.MarketDataModel.StopLimit; @@ -177,14 +178,92 @@ namespace PortfolioManager.ViewModels await ReloadTradeFile(); } + // public async Task ExecuteBollingerBands() + // { + // SaveParameters saveParams = SaveParameters.Parse("Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol," + selectedPosition.Symbol + ",SelectedWatchList,{All},SelectedDayCount,90"); + // saveParams.Referer=this; + // WorkspaceInstantiator.Invoke(saveParams); + // await Task.FromResult(true); + // } + public async Task ExecuteBollingerBands() { - SaveParameters saveParams = SaveParameters.Parse("Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol," + selectedPosition.Symbol + ",SelectedWatchList,{All},SelectedDayCount,90"); + MarketData.MarketDataModel.StopLimits stopLimits = GetHistoricalStopLimitsMarketDataModel(); + + StringBuilder sb = new StringBuilder(); + SaveParameters saveParams = null; + sb = new StringBuilder(); + sb.Append("Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,"); + sb.Append(selectedPosition.Symbol).Append(","); + sb.Append("SelectedWatchList,{All},SelectedDayCount,"); + sb.Append(GetDayCountSelectionForBollingerBands(selectedPosition, true)); + saveParams = SaveParameters.Parse(sb.ToString()); + SaveParameters stopLimitParams = StopLimitsExtensions.FromStopLimits(stopLimits); + saveParams.AddRange(stopLimitParams); saveParams.Referer=this; - WorkspaceInstantiator.Invoke(saveParams); + WorkspaceInstantiator.Invoke(saveParams); await Task.FromResult(true); } + // This getter returns non-model (MarketData.MarketDataModel) specific stop limits to pass along to the bollinger bands + private MarketData.MarketDataModel.StopLimits GetHistoricalStopLimitsMarketDataModel() + { + if (null == sessionParams || null == selectedPosition) return null; + DateGenerator dateGenerator = new DateGenerator(); + + MarketData.MarketDataModel.StopLimits marketDataModelStopLimits = new MarketData.MarketDataModel.StopLimits(); + + MarketData.Generator.Model.StopLimits stopLimits = + new MarketData.Generator.Model.StopLimits((from MarketData.Generator.Model.StopLimit stopLimit in sessionParams.StopLimits + where stopLimit.StopLimitId.Equals(selectedPosition.Symbol + Utility.DateTimeToStringYYYYMMDDMMSSTT(selectedPosition.PurchaseDate)) select stopLimit). + OrderByDescending(x => x.AnalysisDate).ToList()); + + MarketData.MarketDataModel.StopLimit initialStopLimit = new MarketData.MarketDataModel.StopLimit(); + initialStopLimit.Symbol = selectedPosition.Symbol; + initialStopLimit.Shares = 0; + initialStopLimit.StopPrice = selectedPosition.InitialStopLimit; + initialStopLimit.StopType = StopLimitConstants.STOP_QUOTE; + initialStopLimit.EffectiveDate = selectedPosition.PurchaseDate; + initialStopLimit.Active = 1; + marketDataModelStopLimits.Add(initialStopLimit); + + foreach (MarketData.Generator.Model.StopLimit stopLimit in stopLimits) + { + MarketData.MarketDataModel.StopLimit marketDataModelStopLimit = new MarketData.MarketDataModel.StopLimit(); + marketDataModelStopLimit.Symbol = stopLimit.Symbol; + marketDataModelStopLimit.Shares = 0; + marketDataModelStopLimit.StopPrice = stopLimit.NewStop; + marketDataModelStopLimit.StopType = StopLimitConstants.STOP_QUOTE; + marketDataModelStopLimit.EffectiveDate = stopLimit.AnalysisDate; + marketDataModelStopLimit.Active = 1; + marketDataModelStopLimits.Add(marketDataModelStopLimit); + } + + return marketDataModelStopLimits; + } + + private int GetDayCountSelectionForBollingerBands(MGSHPositionModel selectedPosition,bool ignoreActivePosition=false) + { + DateGenerator dateGenerator=new DateGenerator(); + DateTime maxDate = DateTime.Today; + + if(!ignoreActivePosition) + { + maxDate = PricingDA.GetLatestDate(selectedPosition.Symbol); + if(!selectedPosition.IsActivePosition) + { + maxDate = selectedPosition.SellDate; + } + } + int daysBetween=dateGenerator.DaysBetween(selectedPosition.PurchaseDate, maxDate); + if(daysBetween<90)return 90; + if(daysBetween<180)return 180; + if(daysBetween<360)return 360; + if(daysBetween<720)return 720; + if(daysBetween<1440)return 1440; + return 3600; + } + public async Task ReloadTradeFile() { LoadSessionFile(); diff --git a/PortfolioManager/Views/BollingerBandView.axaml.cs b/PortfolioManager/Views/BollingerBandView.axaml.cs index b488565..bc05871 100644 --- a/PortfolioManager/Views/BollingerBandView.axaml.cs +++ b/PortfolioManager/Views/BollingerBandView.axaml.cs @@ -1,10 +1,15 @@ using System; using Avalonia; using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; +using Eremex.AvaloniaUI.Controls.Utils; using PortfolioManager.ViewModels; +using ScottPlot; using ScottPlot.Avalonia; +using ScottPlot.DataSources; +using ScottPlot.Plottables; namespace PortfolioManager.Views; @@ -25,8 +30,28 @@ public partial class BollingerBandView : UserControl if (null!=viewModel && default == viewModel.Plotter) { viewModel.Plotter = new AvaPlot(); + viewModel.Plotter.PointerMoved+=OnPointerMoved; viewModel.OnPlotterLoaded(viewModel.Plotter); } } } + + private void OnPointerMoved(object sender, PointerEventArgs pointerEventArgs) + { + // AvaPlot avaPlot = (sender as AvaPlot); + // if(default == avaPlot)return; + // Point position = pointerEventArgs.GetPosition(this); + // avaPlot.Plot.Remove(); + // avaPlot.Plot.Add.Crosshair(position.X,position.Y); + // avaPlot.Refresh(); + + // Point clientMousePosition = avaPlot.PointToClient(new PixelPoint((int)position.X,(int)position.Y)); + // avaPlot.Plot.Add.Crosshair(clientMousePosition.X,clientMousePosition.Y); + // PointerPoint point = pointerEventArgs.GetCurrentPoint(null); + // Point position = point.Position; +// Point clientPoint = avaPlot.PointToClient(new PixelPoint((int)position.X,(int)position.Y)); +// Coordinates coordinates = avaPlot.Plot.GetCoordinates(position.X, position.Y); +// avaPlot.Plot.Remove(); + // avaPlot.Plot.Add.Crosshair(position.X,position.Y); + } } \ No newline at end of file