diff --git a/lib/averages/_index.md b/lib/averages/_index.md index d10eeae4..cb75879c 100644 --- a/lib/averages/_index.md +++ b/lib/averages/_index.md @@ -51,7 +51,7 @@ | SINEMA | Sine-weighted MA | | [SMA](sma/Sma.cs) | Simple MA | | SSF | Ehlers Super Smooth Filter | -| T3 | Tillson T3 MA | +| [T3](t3/T3.cs) | Tillson T3 MA | | [TEMA](tema/Tema.cs) | Triple Exponential MA | | [TRIMA](trima/Trima.cs) | Triangular MA | | USF | Ehlers Ultrasmooth Filter | diff --git a/lib/averages/hma/Hma.cs b/lib/averages/hma/Hma.cs index 93157a12..8dab6a4d 100644 --- a/lib/averages/hma/Hma.cs +++ b/lib/averages/hma/Hma.cs @@ -75,7 +75,7 @@ public sealed class Hma : ITValuePublisher public TSeries Update(TSeries source) { - if (source.Count == 0) return new TSeries(new List(), new List()); + if (source.Count == 0) return new TSeries([], new List()); int len = source.Count; var t = new List(len); diff --git a/lib/averages/t3/T3.cs b/lib/averages/t3/T3.cs index 36e79027..fe548422 100644 --- a/lib/averages/t3/T3.cs +++ b/lib/averages/t3/T3.cs @@ -35,10 +35,12 @@ public sealed class T3 : ITValuePublisher public override bool Equals(object? obj) => obj is State other && Equals(other); +#pragma warning disable S1244 // Do not check floating point equality with exact values public bool Equals(State other) => E1 == other.E1 && E2 == other.E2 && E3 == other.E3 && E4 == other.E4 && E5 == other.E5 && E6 == other.E6 && IsInitialized == other.IsInitialized; +#pragma warning restore S1244 // Do not check floating point equality with exact values public override int GetHashCode() => HashCode.Combine(E1, E2, E3, E4, E5, E6, IsInitialized); @@ -63,10 +65,12 @@ public sealed class T3 : ITValuePublisher public override bool Equals(object? obj) => obj is Parameters other && Equals(other); +#pragma warning disable S1244 // Do not check floating point equality with exact values public bool Equals(Parameters other) => Alpha == other.Alpha && C1 == other.C1 && C2 == other.C2 && C3 == other.C3 && C4 == other.C4; +#pragma warning restore S1244 // Do not check floating point equality with exact values public override int GetHashCode() => HashCode.Combine(Alpha, C1, C2, C3, C4); diff --git a/quantower/IndicatorExtensions.Tests.cs b/quantower/IndicatorExtensions.Tests.cs index 04797a2c..6a3658fa 100644 --- a/quantower/IndicatorExtensions.Tests.cs +++ b/quantower/IndicatorExtensions.Tests.cs @@ -111,9 +111,11 @@ public class IndicatorExtensionsTests [Fact] public void LogicMethods_CalculateCorrectly() { - var indicator = new TestIndicator(); - indicator.CurrentChart = new MockChart(); - + var indicator = new TestIndicator + { + CurrentChart = new MockChart() + }; + // Add some data var now = new DateTime(2024, 1, 1, 12, 0, 0, DateTimeKind.Utc); for (int i = 0; i < 20; i++) @@ -134,8 +136,8 @@ public class IndicatorExtensionsTests // 2. 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); + for (int i = 0; i < 20; i++) series.AddValue(); + for (int i = 0; i < 20; i++) series.SetValue(100 + i, i); var points = IndicatorExtensions.GetSmoothCurvePoints(indicator, converter, clientRect, series); Assert.NotEmpty(points); @@ -143,11 +145,11 @@ 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++) + for (int i = 0; i < 20; i++) { double val = (i % 2 == 0) ? 10.0 : -10.0; histSeries.SetValue(val, i); @@ -155,20 +157,20 @@ public class IndicatorExtensionsTests 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.First(); + 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. + // else new Rectangle(barX, barY0, ...) -> Y = barY0 = 0. Assert.Equal(0, first.Rect.Y); Assert.Equal(10, first.Rect.Height); } @@ -179,27 +181,38 @@ public class IndicatorExtensionsTests { // This test attempts to verify that paint methods don't crash. // It requires System.Drawing.Common to be functional. - + // On non-Windows, this might fail if libgdiplus is not installed. // We'll try-catch the PlatformNotSupportedException to allow the test to pass (but not cover) on those systems. - try + try { using var bitmap = new Bitmap(100, 100); using var graphics = Graphics.FromImage(bitmap); RunPaintTests(graphics); Assert.True(true); // Assertion to satisfy SonarCloud RSPEC-2699 } - catch (TypeInitializationException) { return; } // System.Drawing.Common not supported - catch (PlatformNotSupportedException) { return; } // GDI+ not available - catch (DllNotFoundException) { return; } // libgdiplus not found + catch (TypeInitializationException) + { + // System.Drawing.Common not supported on this platform + } + catch (PlatformNotSupportedException) + { + // GDI+ not available on this platform + } + catch (DllNotFoundException) + { + // libgdiplus not found on this platform + } } [System.Runtime.Versioning.SupportedOSPlatform("windows")] private void RunPaintTests(Graphics graphics) { - var indicator = new TestIndicator(); - indicator.CurrentChart = new MockChart(); - + var indicator = new TestIndicator + { + CurrentChart = new MockChart() + }; + // Add some data var now = new DateTime(2024, 1, 1, 12, 0, 0, DateTimeKind.Utc); for (int i = 0; i < 20; i++) @@ -213,7 +226,7 @@ public class IndicatorExtensionsTests 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); @@ -221,12 +234,12 @@ public class IndicatorExtensionsTests foreach (LineStyle style in Enum.GetValues(typeof(LineStyle))) { var series = new LineSeries("Test", Color.Blue, 1, style); - for (int i = 0; i < 20; i++) series.AddValue(); - for (int i = 0; i < 20; i++) series.SetValue(100 + i, i); - + for (int i = 0; i < 20; i++) series.AddValue(); + for (int i = 0; i < 20; i++) series.SetValue(100 + i, i); + // Test with warmup and cold values IndicatorExtensions.PaintSmoothCurve(indicator, args, series, warmupPeriod: 5, showColdValues: true); - + // Test without cold values IndicatorExtensions.PaintSmoothCurve(indicator, args, series, warmupPeriod: 5, showColdValues: false); } @@ -234,7 +247,7 @@ public class IndicatorExtensionsTests // 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++) + for (int i = 0; i < 20; i++) { // Alternate positive and negative double val = (i % 2 == 0) ? 10.0 : -10.0; @@ -256,9 +269,9 @@ public class IndicatorExtensionsTests // 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) } diff --git a/quantower/IndicatorExtensions.cs b/quantower/IndicatorExtensions.cs index 21b3fb37..b25a4584 100644 --- a/quantower/IndicatorExtensions.cs +++ b/quantower/IndicatorExtensions.cs @@ -170,24 +170,23 @@ public static class IndicatorExtensions DateTime rightTime = new[] { converter.GetTime(clientRect.Right), indicator.HistoricalData.Time(0) }.Min(); 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 }) + 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 = indicator.Count - warmupPeriod - rightIndex; + + // Draw the hot part + if (hotCount > 0) { - int hotCount = indicator.Count - warmupPeriod - rightIndex; + var hotPoints = allPoints.Take(Math.Min(hotCount + 1, allPoints.Count)).ToArray(); + gr.DrawCurve(defaultPen, hotPoints, 0, hotPoints.Length - 1, (float)tension); + } - // 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)tension); - } - - // 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)tension); - } + // 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)tension); } } }