diff --git a/.github/workflows/Publish.yml b/.github/workflows/Publish.yml index fa9b429e..02cc9df1 100644 --- a/.github/workflows/Publish.yml +++ b/.github/workflows/Publish.yml @@ -213,13 +213,15 @@ jobs: - name: Qodana Scan uses: JetBrains/qodana-action@v2025.3 + continue-on-error: true env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} with: - args: --coverage-dir,.qodana/code-coverage - fail-threshold: -1 + args: --coverage-dir,.qodana/code-coverage,--fail-threshold,999999 + fail-threshold: 999999 - name: Upload Qodana SARIF Artifacts + if: always() uses: actions/upload-artifact@v4 with: name: qodana-sarif-reports @@ -230,6 +232,7 @@ jobs: # ============================================================================== Codacy_Qodana_Upload: needs: Qodana_Scan + if: always() runs-on: ubuntu-latest permissions: contents: read @@ -239,6 +242,7 @@ jobs: fetch-depth: 0 - name: Download Qodana SARIF Reports + continue-on-error: true uses: actions/download-artifact@v4 with: name: qodana-sarif-reports diff --git a/Directory.Build.props b/Directory.Build.props index 91e53206..a0040729 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -53,7 +53,7 @@ - $(NoWarn);S1144;S1944;S2053;S2245;S2259;S2583;S2589;S3329;S3655;S3776;S3949;S3966;S4158;S4347;S5773;S6781;MA0048;MA0051 + $(NoWarn);S1144;S1944;S2053;S2245;S2259;S2583;S2589;S3329;S3655;S3776;S3949;S3966;S4158;S4347;S5773;S6781;MA0048;MA0051;RCS1159 diff --git a/lib/core/simd/SimdExtensions.cs b/lib/core/simd/SimdExtensions.cs index fa24c948..de3c3b6e 100644 --- a/lib/core/simd/SimdExtensions.cs +++ b/lib/core/simd/SimdExtensions.cs @@ -709,5 +709,4 @@ public static class SimdExtensions return sum; } - } diff --git a/lib/momentum/adx/Adx.Quantower.Tests.cs b/lib/momentum/adx/Adx.Quantower.Tests.cs index 4d39ba52..f29cddb3 100644 --- a/lib/momentum/adx/Adx.Quantower.Tests.cs +++ b/lib/momentum/adx/Adx.Quantower.Tests.cs @@ -28,7 +28,6 @@ public class AdxIndicatorTests Assert.Equal(0, watchlistIndicator.MinHistoryDepths); } - [Fact] public void AdxIndicator_Initialize_CreatesInternalAdx() { diff --git a/lib/momentum/adxr/Adxr.Quantower.Tests.cs b/lib/momentum/adxr/Adxr.Quantower.Tests.cs index f071c0d5..4a856552 100644 --- a/lib/momentum/adxr/Adxr.Quantower.Tests.cs +++ b/lib/momentum/adxr/Adxr.Quantower.Tests.cs @@ -28,7 +28,6 @@ public class AdxrIndicatorTests Assert.Equal(0, watchlistIndicator.MinHistoryDepths); } - [Fact] public void AdxrIndicator_Initialize_CreatesInternalAdxr() { diff --git a/lib/momentum/adxr/Adxr.cs b/lib/momentum/adxr/Adxr.cs index 9832ac79..87aa51ce 100644 --- a/lib/momentum/adxr/Adxr.cs +++ b/lib/momentum/adxr/Adxr.cs @@ -233,4 +233,3 @@ public sealed class Adxr : ITValuePublisher return new TSeries(tList, [.. v]); } } - diff --git a/lib/momentum/ao/Ao.cs b/lib/momentum/ao/Ao.cs index c9ea590b..9bee1092 100644 --- a/lib/momentum/ao/Ao.cs +++ b/lib/momentum/ao/Ao.cs @@ -99,7 +99,7 @@ public sealed class Ao : ITValuePublisher double ao = sFast.Value - sSlow.Value; Last = new TValue(input.Time, ao); - Pub?.Invoke(this, new TValueEventArgs { Value = Last, IsNew = true }); + Pub?.Invoke(this, new TValueEventArgs { Value = Last, IsNew = isNew }); return Last; } @@ -117,7 +117,7 @@ public sealed class Ao : ITValuePublisher double ao = sFast.Value - sSlow.Value; Last = new TValue(input.Time, ao); - Pub?.Invoke(this, new TValueEventArgs { Value = Last, IsNew = true }); + Pub?.Invoke(this, new TValueEventArgs { Value = Last, IsNew = isNew }); return Last; } diff --git a/lib/momentum/aroon/Aroon.Validation.Tests.cs b/lib/momentum/aroon/Aroon.Validation.Tests.cs index a62969a2..e918647b 100644 --- a/lib/momentum/aroon/Aroon.Validation.Tests.cs +++ b/lib/momentum/aroon/Aroon.Validation.Tests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Skender.Stock.Indicators; @@ -136,5 +136,4 @@ public sealed class AroonValidationTests : IDisposable // Verify Oscillator ValidationHelper.VerifyData(results, tulipOsc, lookback: 14); } - } diff --git a/lib/momentum/aroon/Aroon.cs b/lib/momentum/aroon/Aroon.cs index 56652c46..38e37278 100644 --- a/lib/momentum/aroon/Aroon.cs +++ b/lib/momentum/aroon/Aroon.cs @@ -255,4 +255,3 @@ public sealed class Aroon : ITValuePublisher return new TSeries(tList, [.. v]); } } - diff --git a/lib/momentum/aroonosc/AroonOsc.Validation.Tests.cs b/lib/momentum/aroonosc/AroonOsc.Validation.Tests.cs index e1a12586..aa2bf73e 100644 --- a/lib/momentum/aroonosc/AroonOsc.Validation.Tests.cs +++ b/lib/momentum/aroonosc/AroonOsc.Validation.Tests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Skender.Stock.Indicators; @@ -96,5 +96,4 @@ public sealed class AroonOscValidationTests : IDisposable // Verify Oscillator ValidationHelper.VerifyData(results, tulipOsc, lookback: 14); } - } diff --git a/lib/momentum/aroonosc/AroonOsc.cs b/lib/momentum/aroonosc/AroonOsc.cs index 91e86c1c..fc8d559f 100644 --- a/lib/momentum/aroonosc/AroonOsc.cs +++ b/lib/momentum/aroonosc/AroonOsc.cs @@ -241,4 +241,3 @@ public sealed class AroonOsc : ITValuePublisher return new TSeries(tList, [.. v]); } } - diff --git a/lib/momentum/bop/Bop.cs b/lib/momentum/bop/Bop.cs index fd91a37d..05153b1e 100644 --- a/lib/momentum/bop/Bop.cs +++ b/lib/momentum/bop/Bop.cs @@ -180,4 +180,3 @@ public sealed class Bop : ITValuePublisher return new TSeries(t, v); } } - diff --git a/lib/momentum/cfb/Cfb.cs b/lib/momentum/cfb/Cfb.cs index 0db66820..2b7fa9fc 100644 --- a/lib/momentum/cfb/Cfb.cs +++ b/lib/momentum/cfb/Cfb.cs @@ -188,7 +188,6 @@ public sealed class Cfb : ITValuePublisher double ratio = netMove / _runningSums[i]; - if (ratio >= 0.25) { sumWeightedLen += L * ratio; diff --git a/lib/momentum/macd/Macd.Validation.Tests.cs b/lib/momentum/macd/Macd.Validation.Tests.cs index a10d1869..b947f77a 100644 --- a/lib/momentum/macd/Macd.Validation.Tests.cs +++ b/lib/momentum/macd/Macd.Validation.Tests.cs @@ -245,4 +245,3 @@ public sealed class MacdValidationTests : IDisposable _output.WriteLine("MACD Streaming validated successfully against Tulip"); } } - diff --git a/lib/momentum/rsi/Rsi.Tests.cs b/lib/momentum/rsi/Rsi.Tests.cs index f007a525..a811bc6d 100644 --- a/lib/momentum/rsi/Rsi.Tests.cs +++ b/lib/momentum/rsi/Rsi.Tests.cs @@ -377,7 +377,7 @@ public class RsiTests // Should not crash and produce finite results for (int i = 0; i < output.Length; i++) { - Assert.True(double.IsFinite(output[i]) || output[i] == 0); + Assert.True(double.IsFinite(output[i]) || Math.Abs(output[i]) < 1e-10); } } diff --git a/lib/momentum/rsi/Rsi.Validation.Tests.cs b/lib/momentum/rsi/Rsi.Validation.Tests.cs index fbe1cfd3..da78f795 100644 --- a/lib/momentum/rsi/Rsi.Validation.Tests.cs +++ b/lib/momentum/rsi/Rsi.Validation.Tests.cs @@ -269,4 +269,3 @@ public sealed class RsiValidationTests : IDisposable _output.WriteLine("RSI validated successfully against Ooples"); } } - diff --git a/lib/statistics/linreg/LinReg.Validation.Tests.cs b/lib/statistics/linreg/LinReg.Validation.Tests.cs index a685b66e..26b835e6 100644 --- a/lib/statistics/linreg/LinReg.Validation.Tests.cs +++ b/lib/statistics/linreg/LinReg.Validation.Tests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Runtime.CompilerServices; using Xunit; @@ -58,5 +58,4 @@ public sealed class LinRegValidationTests : IDisposable ValidationHelper.VerifyData(r2Series, skender, x => x.RSquared, tolerance: ValidationHelper.DefaultTolerance); } - } diff --git a/lib/statistics/stddev/StdDev.Validation.Tests.cs b/lib/statistics/stddev/StdDev.Validation.Tests.cs index 280bbac5..3e9536d6 100644 --- a/lib/statistics/stddev/StdDev.Validation.Tests.cs +++ b/lib/statistics/stddev/StdDev.Validation.Tests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Xunit; using QuanTAlib; @@ -127,5 +127,4 @@ public class StdDevValidationTests } } } - } diff --git a/lib/statistics/variance/Variance.Validation.Tests.cs b/lib/statistics/variance/Variance.Validation.Tests.cs index d14454d4..b1f58ed1 100644 --- a/lib/statistics/variance/Variance.Validation.Tests.cs +++ b/lib/statistics/variance/Variance.Validation.Tests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Xunit; using QuanTAlib; @@ -131,5 +131,4 @@ public class VarianceValidationTests } } } - } diff --git a/lib/trends/alma/Alma.Validation.Tests.cs b/lib/trends/alma/Alma.Validation.Tests.cs index 3c1c4c24..92540ba7 100644 --- a/lib/trends/alma/Alma.Validation.Tests.cs +++ b/lib/trends/alma/Alma.Validation.Tests.cs @@ -152,4 +152,3 @@ public sealed class AlmaValidationTests : IDisposable _output.WriteLine("ALMA Batch validated successfully against Ooples"); } } - diff --git a/lib/trends/bessel/Bessel.Validation.Tests.cs b/lib/trends/bessel/Bessel.Validation.Tests.cs index 9768beda..6d2882cb 100644 --- a/lib/trends/bessel/Bessel.Validation.Tests.cs +++ b/lib/trends/bessel/Bessel.Validation.Tests.cs @@ -58,4 +58,3 @@ public sealed class BesselValidationTests : IDisposable _output.WriteLine("Bessel validated internally: Span vs TSeries are consistent."); } } - diff --git a/lib/trends/bessel/Bessel.cs b/lib/trends/bessel/Bessel.cs index d8bf2c62..644942eb 100644 --- a/lib/trends/bessel/Bessel.cs +++ b/lib/trends/bessel/Bessel.cs @@ -113,6 +113,18 @@ public sealed class Bessel : AbstractBase, IDisposable } } + // Handle case where all inputs are NaN + if (_state.Count == 0) + { + _state.LastValidValue = double.NaN; + _state.F1 = double.NaN; + _state.F2 = double.NaN; + _state.IsHot = false; + Last = new TValue(DateTime.MinValue, double.NaN); + _p_state = _state; + return; + } + for (; i < len; i++) { double val = source[i]; diff --git a/lib/trends/bilateral/Bilateral.Quantower.Tests.cs b/lib/trends/bilateral/Bilateral.Quantower.Tests.cs index 652b7ce5..fe8b9030 100644 --- a/lib/trends/bilateral/Bilateral.Quantower.Tests.cs +++ b/lib/trends/bilateral/Bilateral.Quantower.Tests.cs @@ -113,7 +113,6 @@ public class BilateralIndicatorTests Assert.True(double.IsFinite(secondValue)); } - [Fact] public void BilateralIndicator_MultipleUpdates_ProducesCorrectSequence() { diff --git a/lib/trends/bilateral/Bilateral.Tests.cs b/lib/trends/bilateral/Bilateral.Tests.cs index a42c9d3e..74440c24 100644 --- a/lib/trends/bilateral/Bilateral.Tests.cs +++ b/lib/trends/bilateral/Bilateral.Tests.cs @@ -106,6 +106,37 @@ public class BilateralTests Assert.False(indicator.IsHot); Assert.Equal(1, indicator.Update(new TValue(DateTime.UtcNow, 1)).Value); // Center val 1, weights 0? No, center val is returned if weights 0. } + + [Fact] + public void Update_IsNew_False_OnEmptyBuffer_DoesNotCrash() + { + // Test edge case: calling Update with isNew:false before any isNew:true + var indicator = new Bilateral(3); + + // This should not crash - buffer is empty, so we treat it as first value + var result = indicator.Update(new TValue(DateTime.UtcNow, 5.0), isNew: false); + + // Should have added the value to the buffer + Assert.True(double.IsFinite(result.Value)); + Assert.Equal(5.0, result.Value); // Single value, so result is that value + } + + [Fact] + public void Update_IsNew_False_AfterReset_DoesNotCrash() + { + // Test edge case: calling Update with isNew:false after Reset + var indicator = new Bilateral(3); + + indicator.Update(new TValue(DateTime.UtcNow, 1)); + indicator.Update(new TValue(DateTime.UtcNow, 2)); + indicator.Reset(); + + // Buffer is now empty, isNew:false should not crash + var result = indicator.Update(new TValue(DateTime.UtcNow, 7.0), isNew: false); + + Assert.True(double.IsFinite(result.Value)); + Assert.Equal(7.0, result.Value); + } [Fact] public void AllModes_ProduceSameResult() diff --git a/lib/trends/bilateral/Bilateral.Validation.Tests.cs b/lib/trends/bilateral/Bilateral.Validation.Tests.cs index 42ac81da..533e4549 100644 --- a/lib/trends/bilateral/Bilateral.Validation.Tests.cs +++ b/lib/trends/bilateral/Bilateral.Validation.Tests.cs @@ -191,4 +191,3 @@ public sealed class BilateralValidationTests : IDisposable } } } - diff --git a/lib/trends/bilateral/Bilateral.cs b/lib/trends/bilateral/Bilateral.cs index 136fd4e2..86f618fd 100644 --- a/lib/trends/bilateral/Bilateral.cs +++ b/lib/trends/bilateral/Bilateral.cs @@ -168,11 +168,21 @@ public sealed class Bilateral : AbstractBase _state.SumSq = currentSumSq; double val = GetValidValue(input.Value); - double oldNewest = _buffer.Newest; // Get current newest before overwriting - _buffer.UpdateNewest(val); - _state.SumSq -= (oldNewest * oldNewest); - _state.SumSq += (val * val); + // Defensive check: if buffer is empty, treat as first value + if (_buffer.Count == 0) + { + _buffer.Add(val); + _state.SumSq += (val * val); + } + else + { + double oldNewest = _buffer.Newest; // Get current newest before overwriting + _buffer.UpdateNewest(val); + + _state.SumSq -= (oldNewest * oldNewest); + _state.SumSq += (val * val); + } } double result = CalculateBilateral(); diff --git a/lib/trends/blma/Blma.cs b/lib/trends/blma/Blma.cs index 57748103..834c2be5 100644 --- a/lib/trends/blma/Blma.cs +++ b/lib/trends/blma/Blma.cs @@ -63,11 +63,12 @@ public sealed class Blma : AbstractBase, IDisposable public override void Prime(ReadOnlySpan source, TimeSpan? step = null) { + TimeSpan increment = step ?? TimeSpan.FromMilliseconds(1); DateTime time = DateTime.UtcNow; foreach (var value in source) { Update(new TValue(time, value)); - time = time.AddMilliseconds(1); + time = time.Add(increment); } } diff --git a/lib/trends/conv/Conv.Quantower.Tests.cs b/lib/trends/conv/Conv.Quantower.Tests.cs index 52e33068..bb8a727c 100644 --- a/lib/trends/conv/Conv.Quantower.Tests.cs +++ b/lib/trends/conv/Conv.Quantower.Tests.cs @@ -112,7 +112,6 @@ public class ConvIndicatorTests Assert.True(double.IsFinite(secondValue)); } - [Fact] public void ConvIndicator_MultipleUpdates_ProducesCorrectSequence() { diff --git a/lib/trends/dema/Dema.Quantower.Tests.cs b/lib/trends/dema/Dema.Quantower.Tests.cs index 248fc0c5..a0e1030d 100644 --- a/lib/trends/dema/Dema.Quantower.Tests.cs +++ b/lib/trends/dema/Dema.Quantower.Tests.cs @@ -1,4 +1,4 @@ -using Xunit; +using Xunit; using TradingPlatform.BusinessLayer; namespace QuanTAlib.Tests; @@ -111,7 +111,6 @@ public class DemaIndicatorTests Assert.True(double.IsFinite(secondValue)); } - [Fact] public void DemaIndicator_MultipleUpdates_ProducesCorrectDemaSequence() { @@ -153,5 +152,4 @@ public class DemaIndicatorTests $"Source {source} should produce finite value"); } } - } diff --git a/lib/trends/dema/Dema.Validation.Tests.cs b/lib/trends/dema/Dema.Validation.Tests.cs index c54836a4..23be6d91 100644 --- a/lib/trends/dema/Dema.Validation.Tests.cs +++ b/lib/trends/dema/Dema.Validation.Tests.cs @@ -195,4 +195,3 @@ public sealed class DemaValidationTests : IDisposable _output.WriteLine("DEMA validated successfully against Ooples logic (2*EMA - EMA(EMA))"); } } - diff --git a/lib/trends/dwma/Dwma.Validation.Tests.cs b/lib/trends/dwma/Dwma.Validation.Tests.cs index 1101706d..7dcdef2e 100644 --- a/lib/trends/dwma/Dwma.Validation.Tests.cs +++ b/lib/trends/dwma/Dwma.Validation.Tests.cs @@ -186,4 +186,3 @@ public sealed class DwmaValidationTests : IDisposable _output.WriteLine("DWMA validated against TA-Lib (Chained WMA)"); } } - diff --git a/lib/trends/ema/Ema.cs b/lib/trends/ema/Ema.cs index 2510c1da..f393a7f1 100644 --- a/lib/trends/ema/Ema.cs +++ b/lib/trends/ema/Ema.cs @@ -215,7 +215,7 @@ public sealed class Ema : AbstractBase double val = GetValidValue(input.Value); val = Compute(val, _alpha, _decay, ref _state); Last = new TValue(input.Time, val); - PubEvent(Last); + PubEvent(Last, isNew); return Last; } diff --git a/lib/trends/hma/Hma.Quantower.Tests.cs b/lib/trends/hma/Hma.Quantower.Tests.cs index 16aedfb8..0049e907 100644 --- a/lib/trends/hma/Hma.Quantower.Tests.cs +++ b/lib/trends/hma/Hma.Quantower.Tests.cs @@ -112,7 +112,6 @@ public class HmaIndicatorTests Assert.True(double.IsFinite(secondValue)); } - [Fact] public void HmaIndicator_MultipleUpdates_ProducesCorrectHmaSequence() { diff --git a/lib/trends/hma/Hma.Validation.Tests.cs b/lib/trends/hma/Hma.Validation.Tests.cs index 31e995f6..ba08326b 100644 --- a/lib/trends/hma/Hma.Validation.Tests.cs +++ b/lib/trends/hma/Hma.Validation.Tests.cs @@ -192,4 +192,3 @@ public sealed class HmaValidationTests : IDisposable _output.WriteLine("HMA Batch(TSeries) validated successfully against Ooples"); } } - diff --git a/lib/trends/htit/Htit.cs b/lib/trends/htit/Htit.cs index 398ed3cb..868b2788 100644 --- a/lib/trends/htit/Htit.cs +++ b/lib/trends/htit/Htit.cs @@ -79,8 +79,8 @@ public sealed class Htit : AbstractBase public override void Reset() { - _state = default; - _p_state = default; + _state = new State(); + _p_state = new State(); _priceBuffer.Clear(); _smoothBuffer.Clear(); diff --git a/lib/trends/jma/Jma.Quantower.Tests.cs b/lib/trends/jma/Jma.Quantower.Tests.cs index b4962b3d..998c0b87 100644 --- a/lib/trends/jma/Jma.Quantower.Tests.cs +++ b/lib/trends/jma/Jma.Quantower.Tests.cs @@ -113,7 +113,6 @@ public class JmaIndicatorTests Assert.True(double.IsFinite(secondValue)); } - [Fact] public void JmaIndicator_MultipleUpdates_ProducesCorrectSequence() { diff --git a/lib/trends/jma/Jma.Tests.cs b/lib/trends/jma/Jma.Tests.cs index cf2f9af1..f9faae85 100644 --- a/lib/trends/jma/Jma.Tests.cs +++ b/lib/trends/jma/Jma.Tests.cs @@ -256,7 +256,6 @@ public class JmaTests Assert.NotEqual(jmaPowerDefault.Last.Value, jmaPower2.Last.Value); } - [Fact] public void Jma_SpanCalc_HandlesNaN() { diff --git a/lib/trends/kama/Kama.Validation.Tests.cs b/lib/trends/kama/Kama.Validation.Tests.cs index b168e56c..88a0bb79 100644 --- a/lib/trends/kama/Kama.Validation.Tests.cs +++ b/lib/trends/kama/Kama.Validation.Tests.cs @@ -276,4 +276,3 @@ public sealed class KamaValidationTests : IDisposable _output.WriteLine("KAMA validated successfully against Ooples"); } } - diff --git a/lib/trends/lsma/Lsma.Quantower.Tests.cs b/lib/trends/lsma/Lsma.Quantower.Tests.cs index 9fada4be..5261f1f3 100644 --- a/lib/trends/lsma/Lsma.Quantower.Tests.cs +++ b/lib/trends/lsma/Lsma.Quantower.Tests.cs @@ -112,7 +112,6 @@ public class LsmaIndicatorTests Assert.True(double.IsFinite(secondValue)); } - [Fact] public void LsmaIndicator_MultipleUpdates_ProducesCorrectSequence() { diff --git a/lib/trends/lsma/Lsma.Validation.Tests.cs b/lib/trends/lsma/Lsma.Validation.Tests.cs index a2568db6..21389db0 100644 --- a/lib/trends/lsma/Lsma.Validation.Tests.cs +++ b/lib/trends/lsma/Lsma.Validation.Tests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using OoplesFinance.StockIndicators; @@ -83,5 +83,4 @@ public class LsmaValidationTests } _output.WriteLine("LSMA Span validated successfully against Skender"); } - } diff --git a/lib/trends/mama/Mama.cs b/lib/trends/mama/Mama.cs index f45d12b0..ab6aeac7 100644 --- a/lib/trends/mama/Mama.cs +++ b/lib/trends/mama/Mama.cs @@ -262,8 +262,14 @@ public sealed class Mama : AbstractBase return new TSeries(t, v); } + /// + /// Primes the indicator with historical data. + /// + /// Historical price data + /// Time step parameter (unused for this indicator but required by base signature) public override void Prime(ReadOnlySpan source, TimeSpan? step = null) { + _ = step; // Parameter required by base signature but not used by MAMA foreach (var value in source) { Step(value, true); @@ -447,11 +453,8 @@ public sealed class Mama : AbstractBase // Set initial p_state p_mama = avg; p_fama = avg; - p_period = 0; // Initial period state + p_period = 0; p_phase = 0; - - // Initialize other state variables if needed for next iteration logic? - // Actually they just stay 0/default until we hit count > 6 } output[i] = mama; diff --git a/lib/trends/pwma/Pwma.Quantower.cs b/lib/trends/pwma/Pwma.Quantower.cs index 26b64747..a0595e9a 100644 --- a/lib/trends/pwma/Pwma.Quantower.cs +++ b/lib/trends/pwma/Pwma.Quantower.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Drawing; using System.Runtime.CompilerServices; using TradingPlatform.BusinessLayer; @@ -54,5 +54,4 @@ public sealed class PwmaIndicator : Indicator, IWatchlistIndicator double value = _pwma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew).Value; _series!.SetValue(value, _pwma.IsHot, ShowColdValues); } - } diff --git a/lib/trends/sma/Sma.Quantower.Tests.cs b/lib/trends/sma/Sma.Quantower.Tests.cs index 3a8bf0c3..8efd32f9 100644 --- a/lib/trends/sma/Sma.Quantower.Tests.cs +++ b/lib/trends/sma/Sma.Quantower.Tests.cs @@ -36,7 +36,6 @@ public class SmaIndicatorTests Assert.Contains("15", indicator.ShortName, StringComparison.Ordinal); } - [Fact] public void SmaIndicator_Initialize_CreatesInternalSma() { @@ -103,7 +102,6 @@ public class SmaIndicatorTests Assert.True(double.IsFinite(secondValue)); } - [Fact] public void SmaIndicator_MultipleUpdates_ProducesCorrectSmaSequence() { diff --git a/lib/trends/sma/Sma.cs b/lib/trends/sma/Sma.cs index 73bcd504..e8ba982c 100644 --- a/lib/trends/sma/Sma.cs +++ b/lib/trends/sma/Sma.cs @@ -36,6 +36,7 @@ public sealed class Sma : AbstractBase private record struct State(double Sum, double LastInput, double LastValidValue, int TickCount); private State _state; private State _p_state; + private double _currentBarValue; // Value added during isNew=true, survives state restore private const int ResyncInterval = 1000; @@ -186,19 +187,27 @@ public sealed class Sma : AbstractBase { if (isNew) { + // Capture previous state BEFORE any mutation + _p_state = _state; + double val = GetValidValue(input.Value); UpdateState(val); _state.LastInput = val; - - _p_state = _state; + _currentBarValue = val; // Store the value added for this bar } else { + // Restore scalar state to pre-mutation values _state = _p_state; + double val = GetValidValue(input.Value); - - _state.Sum = _state.Sum - _state.LastInput + val; + // Update sum: remove the value that was added during isNew=true, add the new correction value + _state.Sum = _state.Sum - _currentBarValue + val; + + // Update the buffer's newest value and sync its internal sum with our state sum _buffer.UpdateNewest(val); + _state.Sum = _buffer.RecalculateSum(); // Ensure sums stay in sync + // DO NOT update _currentBarValue here - it must remain the original value from isNew=true } double result = _buffer.Count > 0 ? _state.Sum / _buffer.Count : double.NaN; @@ -597,6 +606,7 @@ public sealed class Sma : AbstractBase _buffer.Clear(); _state = default; _p_state = default; + _currentBarValue = default; Last = default; } } diff --git a/lib/trends/ssf/Ssf.Validation.Tests.cs b/lib/trends/ssf/Ssf.Validation.Tests.cs index 43dbe5f9..75a456d0 100644 --- a/lib/trends/ssf/Ssf.Validation.Tests.cs +++ b/lib/trends/ssf/Ssf.Validation.Tests.cs @@ -75,4 +75,3 @@ public sealed class SsfValidationTests : IDisposable _output.WriteLine("SSF validated successfully against Ooples"); } } - diff --git a/lib/trends/trima/Trima.Validation.Tests.cs b/lib/trends/trima/Trima.Validation.Tests.cs index b5cadda5..a2eecb21 100644 --- a/lib/trends/trima/Trima.Validation.Tests.cs +++ b/lib/trends/trima/Trima.Validation.Tests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Skender.Stock.Indicators; @@ -139,5 +139,4 @@ public class TrimaValidationTests } _output.WriteLine("TRIMA Span validated successfully against TA-Lib"); } - } diff --git a/lib/volatility/atr/Atr.Validation.Tests.cs b/lib/volatility/atr/Atr.Validation.Tests.cs index b19d1ac6..e3ef9797 100644 --- a/lib/volatility/atr/Atr.Validation.Tests.cs +++ b/lib/volatility/atr/Atr.Validation.Tests.cs @@ -254,4 +254,3 @@ public sealed class AtrValidationTests : IDisposable _output.WriteLine("ATR Batch(TSeries) validated successfully against Ooples"); } } - diff --git a/lib/volume/adl/Adl.cs b/lib/volume/adl/Adl.cs index 5e9948b8..49e696c3 100644 --- a/lib/volume/adl/Adl.cs +++ b/lib/volume/adl/Adl.cs @@ -197,4 +197,3 @@ public sealed class Adl : ITValuePublisher } } } - diff --git a/lib/volume/adosc/Adosc.cs b/lib/volume/adosc/Adosc.cs index e810310e..f88fabd7 100644 --- a/lib/volume/adosc/Adosc.cs +++ b/lib/volume/adosc/Adosc.cs @@ -176,4 +176,3 @@ public sealed class Adosc : ITValuePublisher SimdExtensions.Subtract(fastEma, slowEma, output); } } - diff --git a/qodana.yaml b/qodana.yaml index e2ca9c70..52a504e4 100644 --- a/qodana.yaml +++ b/qodana.yaml @@ -13,13 +13,15 @@ dotnet: frameworks: "net10.0" configuration: Release -failureConditions: - severityThresholds: - critical: -1 - high: -1 - moderate: -1 - low: -1 - info: -1 +# Disabled failure conditions - Qodana should never fail the build +# Issues are reported for informational purposes only +# failureConditions: +# severityThresholds: +# critical: -1 +# high: -1 +# moderate: -1 +# low: -1 +# info: -1 exclude: - name: All