Files
Avalonia/PortfolioManager/Renderers/BollingerBandRenderer.cs
2025-06-21 18:53:21 -04:00

295 lines
10 KiB
C#

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<PortfolioTrade> 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);
}
/// <summary>
/// Generate the ZeroPoint marker and text
/// </summary>
/// <param name="zeroPrice"></param>
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()
};
}
}
}