From f0025a97b47800bcde594625eb9ff56a69bdd24e Mon Sep 17 00:00:00 2001 From: Sean Date: Wed, 16 Jul 2025 18:34:00 -0400 Subject: [PATCH] Commit Latest --- .../Renderers/BollingerBandRenderer.cs | 340 +++++++++++------- .../Renderers/OffsetDictionary.cs | 41 +++ .../ViewModels/BollingerBandViewModel.cs | 27 +- .../ViewModels/PlotterWorkspaceViewModel.cs | 4 +- .../Views/BollingerBandView.axaml.cs | 2 + 5 files changed, 281 insertions(+), 133 deletions(-) diff --git a/PortfolioManager/Renderers/BollingerBandRenderer.cs b/PortfolioManager/Renderers/BollingerBandRenderer.cs index b84e4ac..160bcd0 100644 --- a/PortfolioManager/Renderers/BollingerBandRenderer.cs +++ b/PortfolioManager/Renderers/BollingerBandRenderer.cs @@ -20,69 +20,136 @@ using MarketData.Numerical; using MarketData; using Avalonia.Controls.Platform; using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace PortfolioManager.Renderers { // ******************************************************************************************************************************** - public class TextPlot + // public class TextPlot + // { + // public TextPlot() + // { + // } + // public TextPlot(String markerText,SKRect boundingRect,Pixel screenCoordinates) + // { + // BoundingRect = boundingRect; + // ScreenCoordinates = screenCoordinates; + // MarkerText = markerText; + // } + // public Pixel ScreenCoordinates { get; set; } + // public SKRect BoundingRect { get; set; } + // public String MarkerText { get; set; } + // } + + // public class TextPlots : List + // { + // public TextPlots() + // { + // } + + // /// + // /// The coordinate system is based on (0,0) in the upper left + // /// + // /// + // /// + // public bool PointInRects(String markerText,Pixel screenCoordinates,ref Pixel adjustedScreenCoordinates,float factor) + // { + // MDTrace.WriteLine(LogLevel.DEBUG,$"Searching for rectangles in X:{screenCoordinates.X}Y:{screenCoordinates.Y}"); + // foreach(TextPlot textPlot in this) + // { + // if(Math.Round(screenCoordinates.X,2) >= Math.Round(textPlot.BoundingRect.Left,2) && + // Math.Round(screenCoordinates.X,2) <= Math.Round(textPlot.BoundingRect.Right,2) && + // Math.Round(screenCoordinates.Y,2) >= Math.Round(textPlot.BoundingRect.Top,2) && + // Math.Round(screenCoordinates.Y,2) <= Math.Round(textPlot.BoundingRect.Bottom,2)) + // { + // MDTrace.WriteLine(LogLevel.DEBUG,$"The text {markerText} at Point:X:{screenCoordinates.X}:Y:{screenCoordinates.Y} is within Rect:L({textPlot.BoundingRect.Left}),T({textPlot.BoundingRect.Top}),R({textPlot.BoundingRect.Right}),B({textPlot.BoundingRect.Bottom}) with text {textPlot.MarkerText} which is plotted at Coordinates:X:{textPlot.ScreenCoordinates.X} Y:{textPlot.ScreenCoordinates.Y}"); + // SKRect lowestRect = FindLowestAdjacentRect(textPlot.BoundingRect); + // adjustedScreenCoordinates.Y = textPlot.ScreenCoordinates.Y - factor; + // adjustedScreenCoordinates.X = screenCoordinates.X; + // return true; + // } + // } + // return false; + // } + + // public void Summary() + // { + // MDTrace.WriteLine(LogLevel.DEBUG,$"TextPlots:{Count}"); + // foreach(TextPlot textPlot in this) + // { + // MDTrace.WriteLine(LogLevel.DEBUG,$"Rect:L({textPlot.BoundingRect.Left}),T({textPlot.BoundingRect.Top}),R({textPlot.BoundingRect.Right}),B({textPlot.BoundingRect.Bottom}) with text {textPlot.MarkerText} is plotted at Coordinates:X:{textPlot.ScreenCoordinates.X} Y:{textPlot.ScreenCoordinates.Y}"); + // } + // } + + // /// + // /// Find the Rectangle with the highest Bottom that lies within the X-plane + // /// + // /// + // private SKRect FindLowestAdjacentRect(SKRect rect) + // { + // List adjacentRects = this.Where(x=> rect.Left >= x.BoundingRect.Left && rect.Right <= x.BoundingRect.Right).Select(x=>x.BoundingRect).ToList(); + // if(null == adjacentRects || 0==adjacentRects.Count)return new SKRect(){Left=0,Top=0,Right=0,Bottom=0}; + // adjacentRects = adjacentRects.OrderByDescending(x=>x.Bottom).ToList(); + // return adjacentRects[0]; + // } + // } + + public class MarkerItem { - public TextPlot() + public MarkerItem(double markerDate, double markerPrice) { + MarkerDate = markerDate; + MarkerPrice = markerPrice; } - public TextPlot(String markerText,SKRect boundingRect,Pixel screenCoordinates) - { - BoundingRect = boundingRect; - ScreenCoordinates = screenCoordinates; - MarkerText = markerText; - } - public Pixel ScreenCoordinates { get; set; } - public SKRect BoundingRect { get; set; } - public String MarkerText { get; set; } + + public double MarkerDate { get; set; } + + public double MarkerPrice { get; set; } } - - public class TextPlots : List + + public class TextMarkerManager : List { - public TextPlots() + private DateGenerator dateGenerator = new DateGenerator(); + private const double DATE_SPREAD_PCNT = 2.0; // PERCENT + private const double PRICE_SPREAD_PCNT = 2.0; // PERCENT + + public Coordinates GetBestMarkerLocation(double markerDate, double markerPrice,OffsetDictionary offsetDictionary,double verticalAdjustmentFactor) { - } - - /// - /// The coordinate system is based on (0,0) in the upper left - /// - /// - /// - public bool PointInRects(String markerText,Pixel screenCoordinates,ref Pixel adjustedScreenCoordinates,float factor) - { - foreach(TextPlot textPlot in this) + if(!IsOverlapped(markerDate,markerPrice,offsetDictionary)) { - if(Math.Round(screenCoordinates.X,2) >= Math.Round(textPlot.BoundingRect.Left,2) && - Math.Round(screenCoordinates.X,2) <= Math.Round(textPlot.BoundingRect.Right,2) && - Math.Round(screenCoordinates.Y,2) >= Math.Round(textPlot.BoundingRect.Top,2) && - Math.Round(screenCoordinates.Y,2) <= Math.Round(textPlot.BoundingRect.Bottom,2)) - { - MDTrace.WriteLine(LogLevel.DEBUG,$"The text {markerText} at Point:X:{screenCoordinates.X}:Y:{screenCoordinates.Y} is within Rect:L({textPlot.BoundingRect.Left}),T({textPlot.BoundingRect.Top}),R({textPlot.BoundingRect.Right}),B({textPlot.BoundingRect.Bottom}) with text {textPlot.MarkerText} which is plotted at Coordinates:X:{textPlot.ScreenCoordinates.X} Y:{textPlot.ScreenCoordinates.Y}"); - SKRect lowestRect = FindLowestAdjacentRect(textPlot.BoundingRect); - adjustedScreenCoordinates.Y = textPlot.ScreenCoordinates.Y - factor; - adjustedScreenCoordinates.X = screenCoordinates.X; - return true; - } + Add(new MarkerItem(markerDate, markerPrice)); + return new Coordinates(){X=markerDate,Y=markerPrice}; + } + Add(new MarkerItem(markerDate, markerPrice)); + return new Coordinates(){X=markerDate,Y=markerPrice-verticalAdjustmentFactor}; + } + + private bool IsOverlapped(double markerDate, double markerPrice,OffsetDictionary offsetDictionary) + { + foreach(MarkerItem markerItem in this) + { + double markerItemDate = markerItem.MarkerDate; + double markerItemPrice = markerItem.MarkerPrice; + // double minDate = markerItemDate/(1.00+DATE_SPREAD_PCNT/100.0); + // double maxDate = markerItemDate*(1.00+DATE_SPREAD_PCNT/100.0); + // double minPrice = markerItemPrice/(1.00+PRICE_SPREAD_PCNT/100.0); + // double maxPrice = markerItemPrice*(1.00+PRICE_SPREAD_PCNT/100.0); + + double minDate = markerItemDate - offsetDictionary.HorizontalSpread*(DATE_SPREAD_PCNT/100.0); + double maxDate = markerItemDate + offsetDictionary.HorizontalSpread*(DATE_SPREAD_PCNT/100.0); + double minPrice = markerItemPrice - offsetDictionary.VerticalSpread*(PRICE_SPREAD_PCNT/100.0); + double maxPrice = markerItemPrice + offsetDictionary.VerticalSpread*(PRICE_SPREAD_PCNT/100.0); + + if(markerDate>=minDate && markerDate<=maxDate && markerPrice >=minPrice && markerPrice<=maxPrice) + { + return true; + } } return false; } - - /// - /// Find the Rectangle with the highest Bottom that lies within the X-plane - /// - /// - private SKRect FindLowestAdjacentRect(SKRect rect) - { - List adjacentRects = this.Where(x=> rect.Left >= x.BoundingRect.Left && rect.Right <= x.BoundingRect.Right).Select(x=>x.BoundingRect).ToList(); - if(null == adjacentRects || 0==adjacentRects.Count)return new SKRect(){Left=0,Top=0,Right=0,Bottom=0}; - adjacentRects = adjacentRects.OrderByDescending(x=>x.Bottom).ToList(); - return adjacentRects[0]; - } } + // ********************************************************************************************************************************************* @@ -105,7 +172,8 @@ namespace PortfolioManager.Renderers private Prices prices = default; private BollingerBands bollingerBands; private InsiderTransactionSummaries insiderTransactionSummaries = null; -// private TextPlots textPlots = new TextPlots(); + // private TextPlots textPlots = new TextPlots(); + private TextMarkerManager textMarkerManager = new TextMarkerManager(); private OffsetDictionary offsets = new OffsetDictionary(); @@ -131,78 +199,87 @@ namespace PortfolioManager.Renderers public void Render() { - MDTrace.WriteLine(LogLevel.DEBUG,$"[Render] ENTER"); - Plotter.Plot.Axes.Left.TickGenerator = new ScottPlot.TickGenerators.NumericAutomatic() + lock(Plotter.Plot.Sync) { - LabelFormatter = (double value) => value.ToString("C") // "C" format specifier formats as currency - }; - Plotter.Plot.Axes.DateTimeTicksBottom(); - Plotter.Plot.Axes.AutoScale(); - Plotter.Plot.XLabel("Date"); - Plotter.Plot.YLabel("Price"); - Plotter.Refresh(); - base.OnPropertyChanged("ShowLegend"); - MDTrace.WriteLine(LogLevel.DEBUG,$"[Render] LEAVE"); + MDTrace.WriteLine(LogLevel.DEBUG,$"[Render] ENTER"); + 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"); + Plotter.Plot.YLabel("Price"); + if (!ShowLegend) Plotter.Plot.HideLegend(); + else Plotter.Plot.ShowLegend(); + Plotter.Refresh(); + MDTrace.WriteLine(LogLevel.DEBUG,$"[Render] LEAVE"); + } } public void SetData(String selectedSymbol, int selectedDayCount) { - MDTrace.WriteLine(LogLevel.DEBUG,$"[SetData] ENTER"); - this.selectedSymbol = selectedSymbol; - this.selectedDayCount = selectedDayCount; - stopLimit = PortfolioDA.GetStopLimit(selectedSymbol); - portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol); - portfolioTradesLots = LotAggregator.CombineLots(portfolioTrades); - - Plotter.Plot.Clear(); - if (null != portfolioTrades && 0 != portfolioTrades.Count) + lock(Plotter.Plot.Sync) { - 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); + MDTrace.WriteLine(LogLevel.DEBUG,$"[SetData] ENTER"); + this.selectedSymbol = selectedSymbol; + this.selectedDayCount = selectedDayCount; + stopLimit = PortfolioDA.GetStopLimit(selectedSymbol); + portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol); + portfolioTradesLots = LotAggregator.CombineLots(portfolioTrades); - 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) + Plotter.Plot.Clear(); + 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); + // textPlots.Clear(); + textMarkerManager.Clear(); + CalculateOffsets(); + GenerateBollingerBands(); + GenerateLeastSquares(); + GenerateInsiderTransactions(); + GenerateStopLimits(); + GenerateTradePoints(); + GenerateZeroPoint(zeroPrice); + // textPlots.Summary(); + MDTrace.WriteLine(LogLevel.DEBUG,$"[SetData] LEAVE"); } - bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices); -// textPlots.Clear(); - CalculateOffsets(); - GenerateBollingerBands(); - GenerateLeastSquares(); - GenerateInsiderTransactions(); - GenerateStopLimits(); - GenerateTradePoints(); - GenerateZeroPoint(zeroPrice); - MDTrace.WriteLine(LogLevel.DEBUG,$"[SetData] LEAVE"); } /// @@ -210,29 +287,29 @@ namespace PortfolioManager.Renderers /// 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.MaxBollingerDate = bollingerBands.Max(x => x.Date).ToOADate(); + offsets.MinBollingerDate = bollingerBands.Min(x => x.Date).ToOADate(); + offsets.MaxBollingerValue = bollingerBands.Max(x => x.K); + offsets.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); + offsets.Add(OffsetDictionary.OffsetType.MaxBollingerDate,offsets.MaxBollingerDate); + offsets.Add(OffsetDictionary.OffsetType.MinBollingerDate,offsets.MinBollingerDate); + offsets.Add(OffsetDictionary.OffsetType.MaxBollingerValue,offsets.MaxBollingerValue); + offsets.Add(OffsetDictionary.OffsetType.MinBollingerValue,offsets.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); - offsets.Add(OffsetDictionary.OffsetType.VerticalOffset6PC,spreadVert * .06); - offsets.Add(OffsetDictionary.OffsetType.VerticalOffset6P5PC,spreadVert * .065); - offsets.Add(OffsetDictionary.OffsetType.VerticalOffset7PC,spreadVert * .07); - offsets.Add(OffsetDictionary.OffsetType.VerticalOffset10PC,spreadVert * .10); - offsets.Add(OffsetDictionary.OffsetType.VerticalOffset15PC,spreadVert * .15); + offsets.HorizontalSpread = (offsets.MaxBollingerDate - offsets.MinBollingerDate); + offsets.VerticalSpread = (offsets.MaxBollingerValue - offsets.MinBollingerValue); + offsets.Add(OffsetDictionary.OffsetType.HorizontalOffset1PC,offsets.HorizontalSpread * .01); + offsets.Add(OffsetDictionary.OffsetType.HorizontalOffset3PC,offsets.HorizontalSpread * .03); + offsets.Add(OffsetDictionary.OffsetType.HorizontalOffset5PC,offsets.HorizontalSpread * .05); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset1PC,offsets.VerticalSpread * .01); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset3PC,offsets.VerticalSpread *.03); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset5PC,offsets.VerticalSpread * .05); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset6PC,offsets.VerticalSpread * .06); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset6P5PC,offsets.VerticalSpread * .065); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset7PC,offsets.VerticalSpread * .07); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset10PC,offsets.VerticalSpread * .10); + offsets.Add(OffsetDictionary.OffsetType.VerticalOffset15PC,offsets.VerticalSpread * .15); } /// @@ -280,6 +357,9 @@ namespace PortfolioManager.Renderers image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), FontFactor.FontSize); coordinates = new Coordinates(dates[0].ToOADate() - offsets.Offset(OffsetDictionary.OffsetType.HorizontalOffset3PC), values[0] - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC)); + + + coordinates = textMarkerManager.GetBestMarkerLocation(coordinates.X, coordinates.Y, offsets, offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC)); // Pixel screenCoordinates = Plotter.Plot.GetPixel(coordinates); // Pixel adjScreenCoordinates = Plotter.Plot.GetPixel(coordinates); @@ -342,6 +422,7 @@ namespace PortfolioManager.Renderers Coordinates coordinates = new Coordinates(limit.EffectiveDate.ToOADate(), limit.StopPrice - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC)); + coordinates = textMarkerManager.GetBestMarkerLocation(coordinates.X, coordinates.Y,offsets, offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC)); // Pixel screenCoordinates = Plotter.Plot.GetPixel(coordinates); // Pixel adjScreenCoordinates = Plotter.Plot.GetPixel(coordinates); // if(textPlots.PointInRects(sb.ToString(),screenCoordinates,ref adjScreenCoordinates,(float)offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC))) @@ -370,7 +451,7 @@ namespace PortfolioManager.Renderers Coordinates coordinates = new Coordinates(latestPrice.Date.ToOADate() - offsets.Offset(OffsetDictionary.OffsetType.HorizontalOffset3PC), stopLimit.StopPrice - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC)); - + coordinates = textMarkerManager.GetBestMarkerLocation(coordinates.X, coordinates.Y,offsets, offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC)); // Pixel screenCoordinates = Plotter.Plot.GetPixel(coordinates); // Pixel adjScreenCoordinates = Plotter.Plot.GetPixel(coordinates); // if(textPlots.PointInRects(sb.ToString(),screenCoordinates,ref adjScreenCoordinates,(float)offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC))) @@ -416,6 +497,7 @@ namespace PortfolioManager.Renderers Coordinates coordinates = new Coordinates(portfolioTrade.TradeDate.ToOADate(), portfolioTrade.Price - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC)); + coordinates = textMarkerManager.GetBestMarkerLocation(coordinates.X, coordinates.Y,offsets, offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC)); // Pixel screenCoordinates = Plotter.Plot.GetPixel(coordinates); // Pixel adjScreenCoordinates = Plotter.Plot.GetPixel(coordinates); // if(textPlots.PointInRects(sb.ToString(),screenCoordinates,ref adjScreenCoordinates,(float)offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC))) diff --git a/PortfolioManager/Renderers/OffsetDictionary.cs b/PortfolioManager/Renderers/OffsetDictionary.cs index 37b8419..ecf2442 100644 --- a/PortfolioManager/Renderers/OffsetDictionary.cs +++ b/PortfolioManager/Renderers/OffsetDictionary.cs @@ -12,6 +12,42 @@ namespace PortfolioManager.Renderers private Dictionary offsetDictionary = new Dictionary(); + /// + /// Gets/Sets the Maximum bollinger date in AODate form + /// + public double MaxBollingerDate { get; set; } + + /// + /// Gets/Sets the Minumum bollinger date in AODate form + /// + public double MinBollingerDate { get; set; } + + /// + /// Gets/Sets the Maximum bollinger value + /// + public double MaxBollingerValue { get; set; } + + /// + /// Gets/Sets the Minimum bollinger value + /// + public double MinBollingerValue { get; set; } + + /// + /// Gets/Sets the Horizontal Spread (max-min) + /// + public double HorizontalSpread { get; set; } + + + /// + /// Gets/Sets the Vertical Spread (max-min) + /// + public double VerticalSpread { get; set; } + + /// + /// Add an offset and it's offset type + /// + /// + /// public void Add(OffsetType offsetType, double value) { if (offsetDictionary.ContainsKey(offsetType)) @@ -24,6 +60,11 @@ namespace PortfolioManager.Renderers } } + /// + /// Get the offset specified by offsetType + /// + /// + /// public double Offset(OffsetType offsetType) { return offsetDictionary[offsetType]; diff --git a/PortfolioManager/ViewModels/BollingerBandViewModel.cs b/PortfolioManager/ViewModels/BollingerBandViewModel.cs index 11c94b6..478ad27 100644 --- a/PortfolioManager/ViewModels/BollingerBandViewModel.cs +++ b/PortfolioManager/ViewModels/BollingerBandViewModel.cs @@ -104,8 +104,29 @@ namespace PortfolioManager.ViewModels private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs) { - - if ((eventArgs.PropertyName.Equals("SyncTradeToBand") || + if(eventArgs.PropertyName.Equals("SelectedWatchList")) + { + IsBusy=true; + + Task workerTask = Task.Factory.StartNew(()=> + { + symbols.Clear(); + if(UIConstants.CONST_ALL.Equals(selectedWatchList)) + { + symbols.AddRange(PricingDA.GetSymbols()); + } + else + { + symbols.AddRange(WatchListDA.GetWatchList(selectedWatchList)); + } + }); + workerTask.ContinueWith((continuation)=> + { + IsBusy=false; + base.OnPropertyChanged("Symbols"); + }); + } + else if ((eventArgs.PropertyName.Equals("SyncTradeToBand") || eventArgs.PropertyName.Equals("ShowTradeLabels") || eventArgs.PropertyName.Equals("SelectedSymbol") || eventArgs.PropertyName.Equals("ShowRiskFree") || @@ -127,6 +148,8 @@ namespace PortfolioManager.ViewModels bollingerBandRenderer.ExternalStopLimits = stopLimits; bollingerBandRenderer.SetData(selectedSymbol, selectedDayCount); bollingerBandRenderer.Render(); + // bollingerBandRenderer.SetData(selectedSymbol, selectedDayCount); // do it twice. The second render will figure out the text image overlaps + // bollingerBandRenderer.Render(); }); workerTask.ContinueWith((continuation) => { diff --git a/PortfolioManager/ViewModels/PlotterWorkspaceViewModel.cs b/PortfolioManager/ViewModels/PlotterWorkspaceViewModel.cs index 34ed1bf..5701ccf 100644 --- a/PortfolioManager/ViewModels/PlotterWorkspaceViewModel.cs +++ b/PortfolioManager/ViewModels/PlotterWorkspaceViewModel.cs @@ -7,11 +7,11 @@ namespace PortfolioManager.ViewModels { public AvaPlot AvaPlot { get; set; } = default; } - + public abstract class PlotterWorkspaceViewModel : WorkspaceViewModel { public AvaPlot Plotter { get; set; } - + public EventHandler OnPlotterLoadedEventHandler; public void OnPlotterLoaded(AvaPlot avaPlot) diff --git a/PortfolioManager/Views/BollingerBandView.axaml.cs b/PortfolioManager/Views/BollingerBandView.axaml.cs index 884c92b..20ebeaa 100644 --- a/PortfolioManager/Views/BollingerBandView.axaml.cs +++ b/PortfolioManager/Views/BollingerBandView.axaml.cs @@ -5,6 +5,7 @@ using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Eremex.AvaloniaUI.Controls.Utils; +using MarketData; using PortfolioManager.ViewModels; using ScottPlot; using ScottPlot.Avalonia; @@ -29,6 +30,7 @@ public partial class BollingerBandView : UserControl PlotterWorkspaceViewModel viewModel = (DataContext as PlotterWorkspaceViewModel); if (null!=viewModel && default == viewModel.Plotter) { + MDTrace.WriteLine(LogLevel.DEBUG,$"Create new plot."); viewModel.Plotter = new AvaPlot(); viewModel.OnPlotterLoaded(viewModel.Plotter); }