mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-14 00:28:05 +00:00
Refactor IndicatorExtensions: Remove unused methods and optimize price retrieval
This commit is contained in:
@@ -37,52 +37,6 @@ public class IndicatorExtensionsTests
|
||||
Assert.NotEmpty(attr.Variants);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetInputValue_ReturnsCorrectValues_ForSourceTypes()
|
||||
{
|
||||
TestIndicator indicator = new();
|
||||
DateTime now = new(2024, 1, 1, 12, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
// Open=100, High=110, Low=90, Close=105, Volume=1000
|
||||
const double open = 100;
|
||||
const double high = 110;
|
||||
const double low = 90;
|
||||
const double close = 105;
|
||||
const double volume = 1000;
|
||||
|
||||
indicator.HistoricalData.AddBar(now, open, high, low, close, volume);
|
||||
|
||||
// Ensure Count is updated (mock implementation detail)
|
||||
// The mock HistoricalData.Count reflects added items.
|
||||
// Indicator.Count => HistoricalData.Count.
|
||||
|
||||
UpdateArgs args = new(UpdateReason.NewBar);
|
||||
|
||||
// Test each SourceType
|
||||
Assert.Equal(open, IndicatorExtensions.GetInputValue(indicator, args, SourceType.Open).Value);
|
||||
Assert.Equal(high, IndicatorExtensions.GetInputValue(indicator, args, SourceType.High).Value);
|
||||
Assert.Equal(low, IndicatorExtensions.GetInputValue(indicator, args, SourceType.Low).Value);
|
||||
Assert.Equal(close, IndicatorExtensions.GetInputValue(indicator, args, SourceType.Close).Value);
|
||||
|
||||
// HL2 = (110 + 90) / 2 = 100
|
||||
Assert.Equal(100, IndicatorExtensions.GetInputValue(indicator, args, SourceType.HL2).Value);
|
||||
|
||||
// OC2 = (100 + 105) / 2 = 102.5
|
||||
Assert.Equal(102.5, IndicatorExtensions.GetInputValue(indicator, args, SourceType.OC2).Value);
|
||||
|
||||
// OHL3 = (100 + 110 + 90) / 3 = 100
|
||||
Assert.Equal(100, IndicatorExtensions.GetInputValue(indicator, args, SourceType.OHL3).Value);
|
||||
|
||||
// HLC3 = (110 + 90 + 105) / 3 = 101.666...
|
||||
Assert.Equal(101.66666666666667, IndicatorExtensions.GetInputValue(indicator, args, SourceType.HLC3).Value, 5);
|
||||
|
||||
// OHLC4 = (100 + 110 + 90 + 105) / 4 = 101.25
|
||||
Assert.Equal(101.25, IndicatorExtensions.GetInputValue(indicator, args, SourceType.OHLC4).Value);
|
||||
|
||||
// HLCC4 = (110 + 90 + 105 + 105) / 4 = 102.5
|
||||
Assert.Equal(102.5, IndicatorExtensions.GetInputValue(indicator, args, SourceType.HLCC4).Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetInputBar_ReturnsCorrectBar()
|
||||
{
|
||||
@@ -130,11 +84,7 @@ public class IndicatorExtensionsTests
|
||||
|
||||
var clientRect = new Rectangle(0, 0, 100, 100);
|
||||
|
||||
// 1. Test GetHLineY
|
||||
int y = IndicatorExtensions.GetHLineY(converter, 50.0);
|
||||
Assert.Equal(50, y); // Since our mock returns value as Y
|
||||
|
||||
// 2. Test GetSmoothCurvePoints
|
||||
// Test GetSmoothCurvePoints
|
||||
var series = new LineSeries("Test", Color.Blue, 1, LineStyle.Solid);
|
||||
for (int i = 0; i < 20; i++) series.AddValue();
|
||||
for (int i = 0; i < 20; i++) series.SetValue(100 + i, i);
|
||||
@@ -145,34 +95,6 @@ public class IndicatorExtensionsTests
|
||||
// MockChart.BarsWidth defaults to something? Let's assume 0 or check logic.
|
||||
// In GetSmoothCurvePoints: barX + halfBarWidth.
|
||||
// Our mock GetChartX returns 10.
|
||||
|
||||
// 3. Test GetHistogramRectangles
|
||||
var histSeries = new LineSeries("Hist", Color.Blue, 1, LineStyle.Solid);
|
||||
for (int i = 0; i < 20; i++) histSeries.AddValue();
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
double val = (i % 2 == 0) ? 10.0 : -10.0;
|
||||
histSeries.SetValue(val, i);
|
||||
}
|
||||
|
||||
var rects = IndicatorExtensions.GetHistogramRectangles(indicator, converter, clientRect, histSeries);
|
||||
Assert.NotEmpty(rects);
|
||||
|
||||
// Check value at offset 9 (i=9 in setup loop)
|
||||
// i=9 is odd -> -10.0 (Negative)
|
||||
// Color should be Red (150, 255, 0, 0)
|
||||
var first = rects[0];
|
||||
Assert.Equal(Color.FromArgb(150, 255, 0, 0), first.Color);
|
||||
|
||||
// Verify geometry
|
||||
// Value is -10. GetChartY(-10) -> -10.
|
||||
// GetChartY(0) -> 0.
|
||||
// Height = Abs(0 - (-10)) = 10.
|
||||
// Y = 0 (since negative bars start at 0 and go down? No, GDI+ coords usually go down.
|
||||
// But here we are testing the logic in GetHistogramRectangles:
|
||||
// else new Rectangle(barX, barY0, ...) -> Y = barY0 = 0.
|
||||
Assert.Equal(0, first.Rect.Y);
|
||||
Assert.Equal(10, first.Rect.Height);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -225,10 +147,6 @@ public class IndicatorExtensionsTests
|
||||
indicator.CurrentChart.MainWindow.CoordinatesConverter = new TestCoordinatesConverter(validTime);
|
||||
|
||||
var args = new PaintChartEventArgs(graphics, new Rectangle(0, 0, 100, 100));
|
||||
using var pen = new Pen(Color.Red);
|
||||
|
||||
// Test PaintHLine
|
||||
IndicatorExtensions.PaintHLine(indicator, args, 100, pen);
|
||||
|
||||
// Test PaintSmoothCurve with different LineStyles and Warmup
|
||||
foreach (LineStyle style in Enum.GetValues(typeof(LineStyle)))
|
||||
@@ -242,40 +160,6 @@ public class IndicatorExtensionsTests
|
||||
|
||||
// Test without cold values
|
||||
IndicatorExtensions.PaintSmoothCurve(indicator, args, series, warmupPeriod: 5, showColdValues: false);
|
||||
|
||||
// Test PaintLine
|
||||
IndicatorExtensions.PaintLine(indicator, args, series, warmupPeriod: 5, showColdValues: true);
|
||||
}
|
||||
|
||||
// Test PaintHistogram with Positive and Negative values
|
||||
var histSeries = new LineSeries("Hist", Color.Blue, 1, LineStyle.Solid);
|
||||
for (int i = 0; i < 20; i++) histSeries.AddValue();
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
// Alternate positive and negative
|
||||
double val = (i % 2 == 0) ? 10.0 : -10.0;
|
||||
histSeries.SetValue(val, i);
|
||||
}
|
||||
IndicatorExtensions.PaintHistogram(indicator, args, histSeries, 0);
|
||||
|
||||
// Test DrawText
|
||||
IndicatorExtensions.DrawText(indicator, args, "Test Text");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetInputValue_DefaultCase_ReturnsClose()
|
||||
{
|
||||
TestIndicator indicator = new();
|
||||
DateTime now = new(2024, 1, 1, 12, 0, 0, DateTimeKind.Utc);
|
||||
indicator.HistoricalData.AddBar(now, 100, 110, 90, 105, 1000);
|
||||
UpdateArgs args = new(UpdateReason.NewBar);
|
||||
|
||||
// Cast to an invalid SourceType to trigger default case
|
||||
SourceType invalidType = (SourceType)999;
|
||||
|
||||
var result = IndicatorExtensions.GetInputValue(indicator, args, invalidType);
|
||||
|
||||
Assert.Equal(105, result.Value); // Should default to Close (105)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ using TradingPlatform.BusinessLayer;
|
||||
using TradingPlatform.BusinessLayer.Chart;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#nullable disable
|
||||
#pragma warning disable CA1416 // Validate platform compatibility
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
@@ -12,11 +14,6 @@ public enum SourceType
|
||||
Open, High, Low, Close, HL2, OC2, OHL3, HLC3, OHLC4, HLCC4
|
||||
}
|
||||
|
||||
public enum MaType
|
||||
{
|
||||
Alma, Dema, Dsma, Dwma, Ema, Epma, Frama, Fwma, Gma, Hma, Hwma, Jma, Kama, Maaf, Mgdi, MMa, Pwma, Rema, Rma, Sinema, Sma, Smma, T3, Tema, Trima, Vidya, Wma, Zlema
|
||||
}
|
||||
|
||||
public static class IndicatorExtensions
|
||||
{
|
||||
public static readonly Color Averages = Color.FromArgb(255, 255, 128); // #FFFF80 - Yellow
|
||||
@@ -47,14 +44,6 @@ public static class IndicatorExtensions
|
||||
{ }
|
||||
}
|
||||
|
||||
public static TValue GetInputValue(this Indicator indicator, UpdateArgs args, SourceType source)
|
||||
{
|
||||
var historicalData = indicator.HistoricalData;
|
||||
var item = historicalData[indicator.Count - 1, SeekOriginHistory.Begin];
|
||||
double price = item.GetPrice(source);
|
||||
return new TValue(item.TimeLeft.Ticks, price);
|
||||
}
|
||||
|
||||
public static TBar GetInputBar(this Indicator indicator, UpdateArgs args)
|
||||
{
|
||||
var historicalData = indicator.HistoricalData;
|
||||
@@ -68,125 +57,39 @@ public static class IndicatorExtensions
|
||||
);
|
||||
}
|
||||
|
||||
public static double GetPrice(this IHistoryItem item, SourceType source)
|
||||
public static Func<IHistoryItem, double> GetPriceSelector(this SourceType source)
|
||||
{
|
||||
return source switch
|
||||
{
|
||||
SourceType.Open => item[PriceType.Open],
|
||||
SourceType.High => item[PriceType.High],
|
||||
SourceType.Low => item[PriceType.Low],
|
||||
SourceType.Close => item[PriceType.Close],
|
||||
SourceType.HL2 => (item[PriceType.High] + item[PriceType.Low]) * 0.5,
|
||||
SourceType.OC2 => (item[PriceType.Open] + item[PriceType.Close]) * 0.5,
|
||||
SourceType.OHL3 => (item[PriceType.Open] + item[PriceType.High] + item[PriceType.Low]) * 0.333333333333333333,
|
||||
SourceType.HLC3 => (item[PriceType.High] + item[PriceType.Low] + item[PriceType.Close]) * 0.333333333333333333,
|
||||
SourceType.OHLC4 => (item[PriceType.Open] + item[PriceType.High] + item[PriceType.Low] + item[PriceType.Close]) * 0.25,
|
||||
SourceType.HLCC4 => (item[PriceType.High] + item[PriceType.Low] + item[PriceType.Close] + item[PriceType.Close]) * 0.25,
|
||||
_ => item[PriceType.Close]
|
||||
SourceType.Open => item => item[PriceType.Open],
|
||||
SourceType.High => item => item[PriceType.High],
|
||||
SourceType.Low => item => item[PriceType.Low],
|
||||
SourceType.Close => item => item[PriceType.Close],
|
||||
SourceType.HL2 => item => (item[PriceType.High] + item[PriceType.Low]) * 0.5,
|
||||
SourceType.OC2 => item => (item[PriceType.Open] + item[PriceType.Close]) * 0.5,
|
||||
SourceType.OHL3 => item => (item[PriceType.Open] + item[PriceType.High] + item[PriceType.Low]) * 0.333333333333333333,
|
||||
SourceType.HLC3 => item => (item[PriceType.High] + item[PriceType.Low] + item[PriceType.Close]) * 0.333333333333333333,
|
||||
SourceType.OHLC4 => item => (item[PriceType.Open] + item[PriceType.High] + item[PriceType.Low] + item[PriceType.Close]) * 0.25,
|
||||
SourceType.HLCC4 => item => (item[PriceType.High] + item[PriceType.Low] + item[PriceType.Close] + item[PriceType.Close]) * 0.25,
|
||||
_ => item => item[PriceType.Close]
|
||||
};
|
||||
}
|
||||
|
||||
public static void FillValues(this HistoricalData history, Span<double> destination, SourceType source)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsNewBar(this UpdateArgs args)
|
||||
{
|
||||
int count = Math.Min(history.Count, destination.Length);
|
||||
return args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
}
|
||||
|
||||
// Hoist switch to avoid per-iteration branching
|
||||
switch (source)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetValue(this LineSeries series, double value, bool isHot, bool showColdValues)
|
||||
{
|
||||
if (!showColdValues && !isHot)
|
||||
{
|
||||
case SourceType.Open:
|
||||
for (int i = 0; i < count; i++) destination[i] = history[i, SeekOriginHistory.Begin][PriceType.Open];
|
||||
break;
|
||||
case SourceType.High:
|
||||
for (int i = 0; i < count; i++) destination[i] = history[i, SeekOriginHistory.Begin][PriceType.High];
|
||||
break;
|
||||
case SourceType.Low:
|
||||
for (int i = 0; i < count; i++) destination[i] = history[i, SeekOriginHistory.Begin][PriceType.Low];
|
||||
break;
|
||||
case SourceType.Close:
|
||||
for (int i = 0; i < count; i++) destination[i] = history[i, SeekOriginHistory.Begin][PriceType.Close];
|
||||
break;
|
||||
case SourceType.HL2:
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var item = history[i, SeekOriginHistory.Begin];
|
||||
destination[i] = (item[PriceType.High] + item[PriceType.Low]) * 0.5;
|
||||
}
|
||||
break;
|
||||
case SourceType.OC2:
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var item = history[i, SeekOriginHistory.Begin];
|
||||
destination[i] = (item[PriceType.Open] + item[PriceType.Close]) * 0.5;
|
||||
}
|
||||
break;
|
||||
case SourceType.OHL3:
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var item = history[i, SeekOriginHistory.Begin];
|
||||
destination[i] = (item[PriceType.Open] + item[PriceType.High] + item[PriceType.Low]) * 0.333333333333333333;
|
||||
}
|
||||
break;
|
||||
case SourceType.HLC3:
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var item = history[i, SeekOriginHistory.Begin];
|
||||
destination[i] = (item[PriceType.High] + item[PriceType.Low] + item[PriceType.Close]) * 0.333333333333333333;
|
||||
}
|
||||
break;
|
||||
case SourceType.OHLC4:
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var item = history[i, SeekOriginHistory.Begin];
|
||||
destination[i] = (item[PriceType.Open] + item[PriceType.High] + item[PriceType.Low] + item[PriceType.Close]) * 0.25;
|
||||
}
|
||||
break;
|
||||
case SourceType.HLCC4:
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var item = history[i, SeekOriginHistory.Begin];
|
||||
destination[i] = (item[PriceType.High] + item[PriceType.Low] + item[PriceType.Close] + item[PriceType.Close]) * 0.25;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
for (int i = 0; i < count; i++) destination[i] = history[i, SeekOriginHistory.Begin][PriceType.Close];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetValues(this LineSeries series, ReadOnlySpan<double> values)
|
||||
{
|
||||
int count = values.Length;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
series.SetValue(values[i], i, SeekOriginHistory.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CA1416 // Validate platform compatibility
|
||||
public static int GetHLineY(IChartWindowCoordinatesConverter converter, double value)
|
||||
{
|
||||
return (int)converter.GetChartY(value);
|
||||
}
|
||||
|
||||
public static void PaintHLine(this Indicator indicator, PaintChartEventArgs args, double value, Pen pen)
|
||||
{
|
||||
if (indicator.CurrentChart == null)
|
||||
series.SetValue(double.NaN);
|
||||
return;
|
||||
|
||||
Graphics gr = args.Graphics;
|
||||
var mainWindow = indicator.CurrentChart.Windows[args.WindowIndex];
|
||||
var converter = mainWindow.CoordinatesConverter;
|
||||
var clientRect = mainWindow.ClientRectangle;
|
||||
|
||||
gr.SetClip(clientRect);
|
||||
int leftX = clientRect.Left;
|
||||
int rightX = clientRect.Right;
|
||||
int Y = GetHLineY(converter, value);
|
||||
|
||||
using (pen)
|
||||
{
|
||||
gr.DrawLine(pen, new Point(leftX, Y), new Point(rightX, Y));
|
||||
}
|
||||
series.SetValue(value);
|
||||
}
|
||||
|
||||
public static Point[] GetSmoothCurvePoints(Indicator indicator, IChartWindowCoordinatesConverter converter, Rectangle clientRect, LineSeries series)
|
||||
@@ -225,56 +128,7 @@ public static class IndicatorExtensions
|
||||
return allPoints;
|
||||
}
|
||||
|
||||
public static void PaintSmoothCurve(this Indicator indicator, PaintChartEventArgs args, LineSeries series, int warmupPeriod, bool showColdValues = true, double tension = 0.2)
|
||||
{
|
||||
if (!series.Visible || indicator.CurrentChart == null)
|
||||
return;
|
||||
|
||||
Graphics gr = args.Graphics;
|
||||
gr.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
var mainWindow = indicator.CurrentChart.Windows[args.WindowIndex];
|
||||
var converter = mainWindow.CoordinatesConverter;
|
||||
var clientRect = mainWindow.ClientRectangle;
|
||||
|
||||
gr.SetClip(clientRect);
|
||||
|
||||
Point[] allPoints = GetSmoothCurvePoints(indicator, converter, clientRect, series);
|
||||
|
||||
if (allPoints.Length > 1)
|
||||
{
|
||||
DateTime tRight = converter.GetTime(clientRect.Right);
|
||||
DateTime tZero = indicator.HistoricalData.Time(0);
|
||||
DateTime rightTime = tRight < tZero ? tRight : tZero;
|
||||
|
||||
int rightIndex = (int)indicator.HistoricalData.GetIndexByTime(rightTime.Ticks);
|
||||
|
||||
using Pen defaultPen = new(series.Color, series.Width) { DashStyle = ConvertLineStyleToDashStyle(series.Style) };
|
||||
using Pen coldPen = new(series.Color, series.Width) { DashStyle = DashStyle.Dot };
|
||||
|
||||
int hotCount = (warmupPeriod >= 0) ? (indicator.Count - warmupPeriod - rightIndex) : 0;
|
||||
|
||||
// Draw the hot part
|
||||
int hotSegments = Math.Min(hotCount, allPoints.Length - 1);
|
||||
if (hotSegments > 0)
|
||||
{
|
||||
gr.DrawCurve(defaultPen, allPoints, 0, hotSegments, (float)tension);
|
||||
}
|
||||
|
||||
// Draw the cold part
|
||||
if (showColdValues)
|
||||
{
|
||||
int coldStart = Math.Max(0, hotCount);
|
||||
int coldSegments = (allPoints.Length - 1) - coldStart;
|
||||
|
||||
if (coldSegments > 0)
|
||||
{
|
||||
gr.DrawCurve(coldPen, allPoints, coldStart, coldSegments, (float)tension);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void PaintLine(this Indicator indicator, PaintChartEventArgs args, LineSeries series, int warmupPeriod, bool showColdValues = true)
|
||||
public static void PaintSmoothCurve(this Indicator indicator, PaintChartEventArgs args, LineSeries series, int warmupPeriod, bool showColdValues = true, double tension = 0.5)
|
||||
{
|
||||
if (!series.Visible || indicator.CurrentChart == null)
|
||||
return;
|
||||
@@ -330,7 +184,7 @@ public static class IndicatorExtensions
|
||||
int hotSegments = Math.Min(hotCount, count - 1);
|
||||
if (hotSegments > 0)
|
||||
{
|
||||
gr.DrawCurve(defaultPen, allPoints, 0, hotSegments, tension: 0);
|
||||
gr.DrawCurve(defaultPen, allPoints, 0, hotSegments, (float)tension);
|
||||
}
|
||||
|
||||
// Draw the cold part
|
||||
@@ -341,7 +195,7 @@ public static class IndicatorExtensions
|
||||
|
||||
if (coldSegments > 0)
|
||||
{
|
||||
gr.DrawCurve(coldPen, allPoints, coldStart, coldSegments, tension: 0);
|
||||
gr.DrawCurve(coldPen, allPoints, coldStart, coldSegments, (float)tension);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,86 +206,6 @@ public static class IndicatorExtensions
|
||||
}
|
||||
}
|
||||
|
||||
public static List<(Rectangle Rect, Color Color)> GetHistogramRectangles(Indicator indicator, IChartWindowCoordinatesConverter converter, Rectangle clientRect, LineSeries series)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(indicator);
|
||||
ArgumentNullException.ThrowIfNull(converter);
|
||||
var data = indicator.HistoricalData;
|
||||
if (data == null) return new List<(Rectangle, Color)>();
|
||||
|
||||
var lastTime = data.Time(data.Count - 1);
|
||||
var firstTime = data.Time(0);
|
||||
|
||||
IChartWindowCoordinatesConverter safeConverter = converter!;
|
||||
DateTime tLeft = safeConverter.GetTime(clientRect.Left);
|
||||
DateTime leftTime = tLeft > lastTime ? tLeft : lastTime;
|
||||
|
||||
DateTime tRight = safeConverter.GetTime(clientRect.Right);
|
||||
DateTime rightTime = tRight < firstTime ? tRight : firstTime;
|
||||
|
||||
int leftIndex = (int)data.GetIndexByTime(leftTime.Ticks) + 1;
|
||||
int rightIndex = (int)data.GetIndexByTime(rightTime.Ticks);
|
||||
|
||||
var result = new List<(Rectangle, Color)>();
|
||||
|
||||
for (int i = rightIndex; i < leftIndex; i++)
|
||||
{
|
||||
int barX = (int)converter.GetChartX(data.Time(i));
|
||||
int barY = (int)converter.GetChartY(series[i]);
|
||||
int barY0 = (int)converter.GetChartY(0);
|
||||
int HistBarWidth = indicator.CurrentChart.BarsWidth - 2;
|
||||
|
||||
if (series[i] > 0)
|
||||
{
|
||||
result.Add((new Rectangle(barX, barY, HistBarWidth, Math.Abs(barY - barY0)), Color.FromArgb(150, 0, 255, 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add((new Rectangle(barX, barY0, HistBarWidth, Math.Abs(barY0 - barY)), Color.FromArgb(150, 255, 0, 0)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void PaintHistogram(this Indicator indicator, PaintChartEventArgs args, LineSeries series, int warmupPeriod, bool showColdValues = true)
|
||||
{
|
||||
if (!series.Visible || indicator.CurrentChart == null)
|
||||
return;
|
||||
|
||||
Graphics gr = args.Graphics;
|
||||
gr.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
var mainWindow = indicator.CurrentChart.Windows[args.WindowIndex];
|
||||
var converter = mainWindow.CoordinatesConverter;
|
||||
var clientRect = mainWindow.ClientRectangle;
|
||||
|
||||
gr.SetClip(clientRect);
|
||||
|
||||
var rects = GetHistogramRectangles(indicator, converter, clientRect, series);
|
||||
|
||||
foreach (var (rect, color) in rects)
|
||||
{
|
||||
using Brush hist = new SolidBrush(color);
|
||||
gr.FillRectangle(hist, rect);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawText(this Indicator indicator, PaintChartEventArgs args, string text)
|
||||
{
|
||||
if (indicator.CurrentChart == null)
|
||||
return;
|
||||
|
||||
Graphics gr = args.Graphics;
|
||||
var clientRect = indicator.CurrentChart.MainWindow.ClientRectangle;
|
||||
var font = new Font("Inter", 8);
|
||||
SizeF textSize = gr.MeasureString(text, font);
|
||||
var textRect = new RectangleF(clientRect.Left + 5,
|
||||
clientRect.Bottom - textSize.Height - 10,
|
||||
textSize.Width + 10, textSize.Height + 10);
|
||||
|
||||
gr.FillRectangle(Brushes.DarkBlue, textRect);
|
||||
gr.DrawString(text, font, Brushes.White, new PointF(textRect.X + 6, textRect.Y + 5));
|
||||
}
|
||||
|
||||
private static DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle)
|
||||
{
|
||||
return lineStyle switch
|
||||
|
||||
Reference in New Issue
Block a user