295 lines
10 KiB
C#
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()
|
|
};
|
|
}
|
|
}
|
|
} |