feat: update Qodana configuration to disable failure conditions and improve build stability

refactor: enhance Bilateral and SMA indicators to handle edge cases and improve state management
refactor: clean up whitespace and formatting in various test files for consistency
This commit is contained in:
Miha Kralj
2025-12-28 16:22:45 -08:00
parent 5c3b3fbab4
commit 14c5f21d9e
50 changed files with 111 additions and 77 deletions
-1
View File
@@ -152,4 +152,3 @@ public sealed class AlmaValidationTests : IDisposable
_output.WriteLine("ALMA Batch validated successfully against Ooples");
}
}
@@ -58,4 +58,3 @@ public sealed class BesselValidationTests : IDisposable
_output.WriteLine("Bessel validated internally: Span vs TSeries are consistent.");
}
}
+12
View File
@@ -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];
@@ -113,7 +113,6 @@ public class BilateralIndicatorTests
Assert.True(double.IsFinite(secondValue));
}
[Fact]
public void BilateralIndicator_MultipleUpdates_ProducesCorrectSequence()
{
+31
View File
@@ -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()
@@ -191,4 +191,3 @@ public sealed class BilateralValidationTests : IDisposable
}
}
}
+14 -4
View File
@@ -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();
+2 -1
View File
@@ -63,11 +63,12 @@ public sealed class Blma : AbstractBase, IDisposable
public override void Prime(ReadOnlySpan<double> 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);
}
}
-1
View File
@@ -112,7 +112,6 @@ public class ConvIndicatorTests
Assert.True(double.IsFinite(secondValue));
}
[Fact]
public void ConvIndicator_MultipleUpdates_ProducesCorrectSequence()
{
+1 -3
View File
@@ -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");
}
}
}
-1
View File
@@ -195,4 +195,3 @@ public sealed class DemaValidationTests : IDisposable
_output.WriteLine("DEMA validated successfully against Ooples logic (2*EMA - EMA(EMA))");
}
}
-1
View File
@@ -186,4 +186,3 @@ public sealed class DwmaValidationTests : IDisposable
_output.WriteLine("DWMA validated against TA-Lib (Chained WMA)");
}
}
+1 -1
View File
@@ -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;
}
-1
View File
@@ -112,7 +112,6 @@ public class HmaIndicatorTests
Assert.True(double.IsFinite(secondValue));
}
[Fact]
public void HmaIndicator_MultipleUpdates_ProducesCorrectHmaSequence()
{
-1
View File
@@ -192,4 +192,3 @@ public sealed class HmaValidationTests : IDisposable
_output.WriteLine("HMA Batch(TSeries) validated successfully against Ooples");
}
}
+2 -2
View File
@@ -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();
-1
View File
@@ -113,7 +113,6 @@ public class JmaIndicatorTests
Assert.True(double.IsFinite(secondValue));
}
[Fact]
public void JmaIndicator_MultipleUpdates_ProducesCorrectSequence()
{
-1
View File
@@ -256,7 +256,6 @@ public class JmaTests
Assert.NotEqual(jmaPowerDefault.Last.Value, jmaPower2.Last.Value);
}
[Fact]
public void Jma_SpanCalc_HandlesNaN()
{
-1
View File
@@ -276,4 +276,3 @@ public sealed class KamaValidationTests : IDisposable
_output.WriteLine("KAMA validated successfully against Ooples");
}
}
-1
View File
@@ -112,7 +112,6 @@ public class LsmaIndicatorTests
Assert.True(double.IsFinite(secondValue));
}
[Fact]
public void LsmaIndicator_MultipleUpdates_ProducesCorrectSequence()
{
+1 -2
View File
@@ -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");
}
}
+7 -4
View File
@@ -262,8 +262,14 @@ public sealed class Mama : AbstractBase
return new TSeries(t, v);
}
/// <summary>
/// Primes the indicator with historical data.
/// </summary>
/// <param name="source">Historical price data</param>
/// <param name="step">Time step parameter (unused for this indicator but required by base signature)</param>
public override void Prime(ReadOnlySpan<double> 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;
+1 -2
View File
@@ -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);
}
}
-2
View File
@@ -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()
{
+14 -4
View File
@@ -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;
}
}
-1
View File
@@ -75,4 +75,3 @@ public sealed class SsfValidationTests : IDisposable
_output.WriteLine("SSF validated successfully against Ooples");
}
}
+1 -2
View File
@@ -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");
}
}