Files
Navigator/Navigator/Renderers/BollingerBandRenderer.cs
2025-04-08 11:11:01 -04:00

345 lines
18 KiB
C#

using DataDisplay.Common;
using DataDisplay.DataSource;
using DataDisplay.Graph;
using DataDisplay.Renderers;
using SkiaSharp;
using System;
using System.Linq;
using System.Collections.Generic;
using MarketData.Utils;
using MarketData.MarketDataModel;
namespace Navigator.Renderers
{
public class BollingerBandRenderer : IRenderer
{
private SKPaint paintK=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Green),StrokeWidth=5f};
private SKPaint paintL=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Green),StrokeWidth=5f};
private SKPaint paintKL1=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Green),StrokeWidth=3f};
private SKPaint paintLP1=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Green),StrokeWidth=3f};
private SKPaint paintHigh=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Blue),StrokeWidth=4f};
private SKPaint paintLow=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Red),StrokeWidth=4f};
private SKPaint paintClose=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Black),StrokeWidth=5f};
private SKPaint paintSMAN=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Purple),StrokeWidth=3f};
private SKPaint paintPointMarkerStroke=new SKPaint{Style=SKPaintStyle.Stroke,Color=new SKColor((uint)SKColors.Black),StrokeWidth=3f};
private SKPaint paintPointMarkerFill=new SKPaint{Style=SKPaintStyle.Fill,Color=new SKColor((uint)SKColors.Yellow),StrokeWidth=3f};
private SKPaint paintParityPricePointMarkerStroke=new SKPaint{Style=SKPaintStyle.Stroke,Color=new SKColor((uint)SKColors.Black),StrokeWidth=3f};
private SKPaint paintParityPricePointMarkerFill=new SKPaint{Style=SKPaintStyle.Fill,Color=new SKColor((uint)SKColors.DarkBlue),StrokeWidth=3f};
private SKPaint paintStopLimitMarkerStroke=new SKPaint{Style=SKPaintStyle.Stroke,Color=new SKColor((uint)SKColors.Black),StrokeWidth=3f};
private SKPaint paintStopLimitMarkerFill=new SKPaint{Style=SKPaintStyle.Fill,Color=new SKColor((uint)SKColors.Red),StrokeWidth=3f};
public enum Band{K=0,KL1=1,L=2,LP1=3,High=4,Low=5,Close=6,SMAN=7};
private Dictionary<int,LineGraph> bollingerBandGraphs=new Dictionary<int,LineGraph>();
private PortfolioTradesWithParityPrice portfolioTradesWithParityPrice;
private StopLimit stopLimit;
private DateGenerator dateGenerator=new DateGenerator();
private bool deviceIsTablet=false;
public BollingerBandRenderer()
{
}
public void SetPaint(SKPaint paint,Band band)
{
if(!bollingerBandGraphs.ContainsKey((int)band))return;
bollingerBandGraphs[(int)band].SetPaint(paint);
}
public bool DeviceIsTablet
{
get{return deviceIsTablet;}
set{deviceIsTablet=value;}
}
public StopLimit StopLimit
{
get{return stopLimit;}
set{stopLimit=value;}
}
public PortfolioTradesWithParityPrice PortfolioTradesWithParityPrice
{
get{return portfolioTradesWithParityPrice;}
set
{
portfolioTradesWithParityPrice=value;
if(null!=portfolioTradesWithParityPrice)PortfolioTrades=portfolioTradesWithParityPrice.Trades;
}
}
private PortfolioTrades PortfolioTrades{get;set;}
public CompositeDataSource K
{
set
{
if(bollingerBandGraphs.ContainsKey((int)Band.K))bollingerBandGraphs[(int)Band.K].SetDataSource(value);
else bollingerBandGraphs.Add((int)Band.K,new LineGraph(paintK,value));
}
}
public CompositeDataSource KL1
{
set
{
if(bollingerBandGraphs.ContainsKey((int)Band.KL1))bollingerBandGraphs[(int)Band.KL1].SetDataSource(value);
else bollingerBandGraphs.Add((int)Band.KL1,new LineGraph(paintKL1,value));
}
}
public CompositeDataSource L
{
set
{
if(bollingerBandGraphs.ContainsKey((int)Band.L))bollingerBandGraphs[(int)Band.L].SetDataSource(value);
else bollingerBandGraphs.Add((int)Band.L,new LineGraph(paintL,value));
}
}
public CompositeDataSource LP1
{
set
{
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))bollingerBandGraphs[(int)Band.LP1].SetDataSource(value);
else bollingerBandGraphs.Add((int)Band.LP1,new LineGraph(paintLP1,value));
}
}
public CompositeDataSource High
{
set
{
if(bollingerBandGraphs.ContainsKey((int)Band.High))bollingerBandGraphs[(int)Band.High].SetDataSource(value);
else bollingerBandGraphs.Add((int)Band.High,new LineGraph(paintHigh,value));
}
}
public CompositeDataSource Low
{
set
{
if(bollingerBandGraphs.ContainsKey((int)Band.Low))bollingerBandGraphs[(int)Band.Low].SetDataSource(value);
else bollingerBandGraphs.Add((int)Band.Low,new LineGraph(paintLow,value));
}
}
public CompositeDataSource Close
{
set
{
if(bollingerBandGraphs.ContainsKey((int)Band.Close))bollingerBandGraphs[(int)Band.Close].SetDataSource(value);
else bollingerBandGraphs.Add((int)Band.Close,new LineGraph(paintClose,value));
}
}
private Type GetXDataType()
{
return typeof(DateTime);
}
private Type GetYDataType()
{
return typeof(double);
}
//*********************************************************************************************************************************************************************
public CompositeDataSource SMAN
{
set
{
if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))bollingerBandGraphs[(int)Band.SMAN].SetDataSource(value);
else bollingerBandGraphs.Add((int)Band.SMAN,new LineGraph(paintSMAN,value));
}
}
public void Refresh()
{
RefreshRequested?.Invoke(this, EventArgs.Empty);
}
// ************************************************************************************************************************************
// ********************************************************* R E N D E R E R *********************************************************
// ************************************************************************************************************************************
public void PaintSurface(SKSurface surface,SKImageInfo imageInfo)
{
SKCanvas canvas = surface.Canvas;
canvas.Clear();
if(!AnyData())return;
int width=imageInfo.Width;
int height=imageInfo.Height;
XDataExtent=GetXDataExtent();
YDataExtent=GetYDataExtent();
XDataExtentMin=GetXDataExtentMin();
YDataExtentMin=GetYDataExtentMin();
PointMapping pointMapping=new PointMapping(width,height,XDataExtent,XDataExtentMin,YDataExtent,YDataExtentMin,80,50);
GridLines gridLines=new GridLines(10,10);
gridLines.AssignXDataType=GetXDataType;
gridLines.AssignGetYDataType=GetYDataType;
gridLines.Render(canvas,pointMapping);
if(bollingerBandGraphs.ContainsKey((int)Band.K))bollingerBandGraphs[(int)Band.K].Render(canvas,pointMapping);
if(bollingerBandGraphs.ContainsKey((int)Band.KL1))bollingerBandGraphs[(int)Band.KL1].Render(canvas,pointMapping);
if(bollingerBandGraphs.ContainsKey((int)Band.L))bollingerBandGraphs[(int)Band.L].Render(canvas,pointMapping);
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))bollingerBandGraphs[(int)Band.LP1].Render(canvas,pointMapping);
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))bollingerBandGraphs[(int)Band.LP1].Render(canvas,pointMapping);
if(bollingerBandGraphs.ContainsKey((int)Band.High))bollingerBandGraphs[(int)Band.High].Render(canvas,pointMapping);
if(bollingerBandGraphs.ContainsKey((int)Band.Low))bollingerBandGraphs[(int)Band.Low].Render(canvas,pointMapping);
if(bollingerBandGraphs.ContainsKey((int)Band.Close))bollingerBandGraphs[(int)Band.Close].Render(canvas,pointMapping);
if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))bollingerBandGraphs[(int)Band.SMAN].Render(canvas,pointMapping);
RenderPositionPointMarkers(canvas,pointMapping,paintPointMarkerStroke,paintPointMarkerFill);
RenderParityPricePointMarker(canvas,pointMapping,paintParityPricePointMarkerStroke,paintParityPricePointMarkerFill);
RenderStopLimitPointMarker(canvas,pointMapping,paintStopLimitMarkerStroke,paintStopLimitMarkerFill);
}
public void RenderPositionPointMarkers(SKCanvas canvas,PointMapping pointMapping,SKPaint paintPointMarkerStroke,SKPaint paintPointMarkerFill)
{
if(null==PortfolioTrades||0==PortfolioTrades.Count)return;
DateTime minVisibleDate=new DateTime((long)(pointMapping.XDataExtentMin*10000000000.0));
DateTime maxVisibleDate=new DateTime((long)(pointMapping.XDataExtent*10000000000.0));
minVisibleDate=dateGenerator.FindPrevBusinessDay(minVisibleDate); // subtract an extra business day to the extent to handle edge cases
maxVisibleDate=dateGenerator.FindNextBusinessDay(maxVisibleDate); // add an extra business day to the extent to handle edge cases.
List<PortfolioTrade> portfolioTrades=PortfolioTrades.Where(x=>x.TradeDate>=minVisibleDate && x.TradeDate<=maxVisibleDate).ToList();
foreach(PortfolioTrade portfolioTrade in portfolioTrades)
{
SKPoint tradePoint=new SKPoint((float)(portfolioTrade.TradeDate.Ticks/10000000000.0),(float)portfolioTrade.Price);
tradePoint=pointMapping.MapPoint(tradePoint);
DrawTriangle(canvas,tradePoint,paintPointMarkerStroke,paintPointMarkerFill);
}
}
public void RenderParityPricePointMarker(SKCanvas canvas,PointMapping pointMapping,SKPaint parityPricePointMarkerStroke,SKPaint parityPricePointMarkerFill)
{
if(null==PortfolioTradesWithParityPrice||null==PortfolioTradesWithParityPrice.ParityPrice)return;
SKPoint parityPoint=new SKPoint((float)(portfolioTradesWithParityPrice.ParityPrice.Date.Ticks/10000000000.0),(float)portfolioTradesWithParityPrice.ParityPrice.Close);
parityPoint=pointMapping.MapPoint(parityPoint);
DrawTriangle(canvas,parityPoint,parityPricePointMarkerStroke,parityPricePointMarkerFill);
}
public void RenderStopLimitPointMarker(SKCanvas canvas,PointMapping pointMapping,SKPaint stopLimitPointMarkerStroke,SKPaint stopLimitPointMarkerFill)
{
if(null==stopLimit)return;
SKPoint stopLimitPoint=new SKPoint((float)(pointMapping.XDataExtent),(float)stopLimit.StopPrice);
stopLimitPoint=pointMapping.MapPoint(stopLimitPoint);
DrawTriangle(canvas,stopLimitPoint,stopLimitPointMarkerStroke,stopLimitPointMarkerFill);
}
public void DrawTriangle(SKCanvas canvas,SKPoint point,SKPaint paintStrokeMarker,SKPaint paintFillMarker)
{
double baseLength = 50;
if(DeviceIsTablet)baseLength*=.45;
DrawingHelper.DrawIsoTriangle(canvas, point, (int)baseLength, paintStrokeMarker, paintFillMarker);
}
public double XDataExtent{get;set;}
public double XDataExtentMin{get;set;}
public double XRange{get{return XDataExtent-XDataExtentMin;}}
public double YDataExtent{get;set;}
public double YDataExtentMin{get;set;}
public double YRange{get{return YDataExtent-YDataExtentMin;}}
private bool AnyData()
{
if(bollingerBandGraphs.ContainsKey((int)Band.K))return true;
if(bollingerBandGraphs.ContainsKey((int)Band.KL1))return true;
if(bollingerBandGraphs.ContainsKey((int)Band.L))return true;
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))return true;
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))return true;
if(bollingerBandGraphs.ContainsKey((int)Band.High))return true;
if(bollingerBandGraphs.ContainsKey((int)Band.Low))return true;
if(bollingerBandGraphs.ContainsKey((int)Band.Close))return true;
if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))return true;
return false;
}
private double GetXDataExtent()
{
List<double> xExtents=new List<double>();
if(bollingerBandGraphs.ContainsKey((int)Band.K))xExtents.Add(bollingerBandGraphs[(int)Band.K].GetXExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.KL1))xExtents.Add(bollingerBandGraphs[(int)Band.KL1].GetXExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.L))xExtents.Add(bollingerBandGraphs[(int)Band.L].GetXExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))xExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetXExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))xExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetXExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.High))xExtents.Add(bollingerBandGraphs[(int)Band.High].GetXExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.Low))xExtents.Add(bollingerBandGraphs[(int)Band.Low].GetXExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.Close))xExtents.Add(bollingerBandGraphs[(int)Band.Close].GetXExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))xExtents.Add(bollingerBandGraphs[(int)Band.SMAN].GetXExtent());
return xExtents.Count>0?xExtents.Max(x=>x):0;
}
private double GetXDataExtentMin()
{
List<double> xExtents=new List<double>();
if(bollingerBandGraphs.ContainsKey((int)Band.K))xExtents.Add(bollingerBandGraphs[(int)Band.K].GetXExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.KL1))xExtents.Add(bollingerBandGraphs[(int)Band.KL1].GetXExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.L))xExtents.Add(bollingerBandGraphs[(int)Band.L].GetXExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))xExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetXExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))xExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetXExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.High))xExtents.Add(bollingerBandGraphs[(int)Band.High].GetXExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.Low))xExtents.Add(bollingerBandGraphs[(int)Band.Low].GetXExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.Close))xExtents.Add(bollingerBandGraphs[(int)Band.Close].GetXExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))xExtents.Add(bollingerBandGraphs[(int)Band.SMAN].GetXExtentMin());
return xExtents.Count>0?xExtents.Min(x=>x):0;
}
private double GetYDataExtent()
{
List<double> yExtents=new List<double>();
if(bollingerBandGraphs.ContainsKey((int)Band.K))yExtents.Add(bollingerBandGraphs[(int)Band.K].GetYExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.KL1))yExtents.Add(bollingerBandGraphs[(int)Band.KL1].GetYExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.L))yExtents.Add(bollingerBandGraphs[(int)Band.L].GetYExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))yExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetYExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))yExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetYExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.High))yExtents.Add(bollingerBandGraphs[(int)Band.High].GetYExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.Low))yExtents.Add(bollingerBandGraphs[(int)Band.Low].GetYExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.Close))yExtents.Add(bollingerBandGraphs[(int)Band.Close].GetYExtent());
if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))yExtents.Add(bollingerBandGraphs[(int)Band.SMAN].GetYExtent());
if(null!=portfolioTradesWithParityPrice&&null!=portfolioTradesWithParityPrice.ParityPrice)
{
yExtents.Add(portfolioTradesWithParityPrice.ParityPrice.Close);
}
if(null!=stopLimit)
{
yExtents.Add(stopLimit.StopPrice);
}
double maxDataExtent=yExtents.Count>0?yExtents.Max(x=>x):0;
return maxDataExtent;
}
private double GetYDataExtentMin()
{
List<double> yExtents=new List<double>();
if(bollingerBandGraphs.ContainsKey((int)Band.K))yExtents.Add(bollingerBandGraphs[(int)Band.K].GetYExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.KL1))yExtents.Add(bollingerBandGraphs[(int)Band.KL1].GetYExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.L))yExtents.Add(bollingerBandGraphs[(int)Band.L].GetYExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))yExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetYExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.LP1))yExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetYExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.High))yExtents.Add(bollingerBandGraphs[(int)Band.High].GetYExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.Low))yExtents.Add(bollingerBandGraphs[(int)Band.Low].GetYExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.Close))yExtents.Add(bollingerBandGraphs[(int)Band.Close].GetYExtentMin());
if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))yExtents.Add(bollingerBandGraphs[(int)Band.SMAN].GetYExtentMin());
if(null!=portfolioTradesWithParityPrice&&null!=portfolioTradesWithParityPrice.ParityPrice)
{
yExtents.Add(portfolioTradesWithParityPrice.ParityPrice.Close);
}
if(null!=stopLimit)
{
yExtents.Add(stopLimit.StopPrice);
}
double minDataExtent=yExtents.Count>0?yExtents.Min(x=>x):0;
return minDataExtent;
}
public event EventHandler RefreshRequested;
}
}