mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-28 09:47:43 +00:00
189 lines
6.0 KiB
C#
189 lines
6.0 KiB
C#
using System.Drawing;
|
|
using TradingPlatform.BusinessLayer;
|
|
using TradingPlatform.BusinessLayer.Chart;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Drawing.Drawing2D;
|
|
using QuanTAlib;
|
|
using System.Collections;
|
|
using TradingPlatform.BusinessLayer.TimeSync;
|
|
|
|
#pragma warning disable CA1416 // Validate platform compatibility
|
|
public abstract class IndicatorBase : Indicator, IWatchlistIndicator
|
|
{
|
|
|
|
[InputParameter("Data source", sortIndex: 17, variants: [
|
|
"Open", 1,
|
|
"High", 2,
|
|
"Low", 3,
|
|
"Close", 4,
|
|
"HL/2 (Median)", 5,
|
|
"OC/2 (Midpoint)", 6,
|
|
"OHL/3 (Mean)", 7,
|
|
"HLC/3 (Typical)", 8,
|
|
"OHLC/4 (Average)", 9,
|
|
"HLCC/4 (Weighted)", 10
|
|
])]
|
|
public int Source { get; set; } = 4;
|
|
|
|
[InputParameter("Show cold values", sortIndex: 20)]
|
|
public bool ShowColdValues { get; set; } = true;
|
|
public int MinHistoryDepths;
|
|
|
|
// LineSeries.LineSeries(string, Color, int, LineStyle)'
|
|
|
|
protected LineSeries? Series;
|
|
protected string SourceName;
|
|
protected abstract AbstractBase QuanTAlib { get; }
|
|
|
|
int IWatchlistIndicator.MinHistoryDepths => 0;
|
|
|
|
protected IndicatorBase() : base()
|
|
{
|
|
OnBackGround = true;
|
|
SeparateWindow = false;
|
|
SourceName = GetName(Source);
|
|
Series = new(name: $"{Name}", color: Color.RoyalBlue, width: 2, style: LineStyle.Solid);
|
|
|
|
AddLineSeries(Series);
|
|
InitIndicator();
|
|
}
|
|
|
|
protected virtual void InitIndicator()
|
|
{
|
|
SourceName = GetName(Source);
|
|
}
|
|
|
|
protected override void OnInit()
|
|
{
|
|
InitIndicator();
|
|
base.OnInit();
|
|
}
|
|
|
|
protected override void OnUpdate(UpdateArgs args)
|
|
{
|
|
TBar bar = new(Time: Time(),
|
|
Open: GetPrice(PriceType.Open),
|
|
High: GetPrice(PriceType.High),
|
|
Low: GetPrice(PriceType.Low),
|
|
Close: GetPrice(PriceType.Close),
|
|
Volume: GetPrice(PriceType.Volume),
|
|
IsNew: args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar);
|
|
|
|
double price = Source switch
|
|
{
|
|
1 => bar.Open,
|
|
2 => bar.High,
|
|
3 => bar.Low,
|
|
4 => bar.Close,
|
|
5 => bar.HL2,
|
|
6 => bar.OC2,
|
|
7 => bar.OHL3,
|
|
8 => bar.HLC3,
|
|
9 => bar.OHLC4,
|
|
10 => bar.HLCC4,
|
|
_ => bar.Close
|
|
};
|
|
|
|
TValue input = new TValue(bar.Time, price, bar.IsNew);
|
|
TValue result = QuanTAlib.Calc(input);
|
|
Series!.SetValue(result.Value);
|
|
Series!.SetMarker(0, Color.Transparent);
|
|
|
|
}
|
|
|
|
public override void OnPaintChart(PaintChartEventArgs args)
|
|
{
|
|
base.OnPaintChart(args);
|
|
List<Point> allPoints = new List<Point>();
|
|
if (CurrentChart == null) return;
|
|
|
|
Graphics gr = args.Graphics;
|
|
|
|
var mainWindow = this.CurrentChart.Windows[args.WindowIndex];
|
|
var converter = mainWindow.CoordinatesConverter;
|
|
var clientRect = mainWindow.ClientRectangle;
|
|
|
|
gr.SetClip(clientRect);
|
|
DateTime leftTime = new[] { converter.GetTime(clientRect.Left), Time(this.Count - 1) }.Max();
|
|
DateTime rightTime = new[] { converter.GetTime(clientRect.Right), Time(0) }.Min();
|
|
|
|
int leftIndex = (int)HistoricalData.GetIndexByTime(leftTime.Ticks) + 1;
|
|
int rightIndex = (int)HistoricalData.GetIndexByTime(rightTime.Ticks);
|
|
|
|
for (int i = rightIndex; i < leftIndex; i++)
|
|
{
|
|
int barX = (int)converter.GetChartX(Time(i));
|
|
int barY = (int)converter.GetChartY(Series![i]);
|
|
int halfBarWidth = CurrentChart.BarsWidth / 2;
|
|
Point point = new Point(barX + halfBarWidth, barY);
|
|
allPoints.Add(point);
|
|
}
|
|
|
|
if (allPoints.Count > 1)
|
|
{
|
|
DrawSmoothCombinedCurve(gr, allPoints, this.Count - QuanTAlib.WarmupPeriod - rightIndex);
|
|
}
|
|
}
|
|
|
|
private void DrawSmoothCombinedCurve(Graphics gr, List<Point> allPoints, int hotCount)
|
|
{
|
|
if (allPoints.Count < 2) return;
|
|
|
|
using (Pen defaultPen = new(Series!.Color, Series.Width) { DashStyle = ConvertLineStyleToDashStyle(Series.Style) })
|
|
using (Pen coldPen = new(Series!.Color, Series.Width) { DashStyle = DashStyle.Dot })
|
|
{
|
|
// Draw the hot part
|
|
if (hotCount > 0)
|
|
{
|
|
var hotPoints = allPoints.Take(Math.Min(hotCount + 1, allPoints.Count)).ToArray();
|
|
gr.DrawCurve(defaultPen, hotPoints, 0, hotPoints.Length - 1, (float)0.1);
|
|
}
|
|
|
|
// Draw the cold part
|
|
if (ShowColdValues && hotCount < allPoints.Count)
|
|
{
|
|
var coldPoints = allPoints.Skip(Math.Max(0, hotCount)).ToArray();
|
|
gr.DrawCurve(coldPen, coldPoints, 0, coldPoints.Length - 1, (float)0.1);
|
|
}
|
|
}
|
|
}
|
|
private DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle)
|
|
{
|
|
return lineStyle switch
|
|
{
|
|
LineStyle.Solid => DashStyle.Solid,
|
|
LineStyle.Dash => DashStyle.Dash,
|
|
LineStyle.Dot => DashStyle.Dot,
|
|
LineStyle.DashDot => DashStyle.DashDot,
|
|
_ => DashStyle.Solid,
|
|
};
|
|
}
|
|
protected void DrawText(Graphics gr, string text, Rectangle clientRect)
|
|
{
|
|
Font font = new Font("Inter", 8);
|
|
SizeF textSize = gr.MeasureString(text, font);
|
|
RectangleF textRect = new RectangleF(clientRect.Left + 5,
|
|
clientRect.Bottom - textSize.Height - 10,
|
|
textSize.Width + 10, textSize.Height + 10);
|
|
gr.FillRectangle(SystemBrushes.ControlDarkDark, textRect);
|
|
gr.DrawString(text, font, Brushes.White, new PointF(textRect.X + 6, textRect.Y + 5));
|
|
}
|
|
protected string GetName(int pType)
|
|
{
|
|
return pType switch
|
|
{
|
|
1 => "Open",
|
|
2 => "High",
|
|
3 => "Low",
|
|
4 => "Close",
|
|
5 => "Median",
|
|
6 => "Midpoint",
|
|
7 => "Mean",
|
|
8 => "Typical",
|
|
9 => "Average",
|
|
10 => "Weighted",
|
|
_ => "N/A"
|
|
};
|
|
}
|
|
|
|
} |