feat(validation): add input validation for Bilateral and Blma constructors; enhance Butter and Mgdi calculations with NaN handling

This commit is contained in:
Miha Kralj
2025-12-25 20:53:56 -08:00
parent ac8b2dbb3f
commit 0d077c24d8
15 changed files with 195 additions and 66 deletions
+19
View File
@@ -237,6 +237,25 @@ public class JmaTests
Assert.NotEqual(jmaPhase0.Last.Value, jmaPhaseMinus100.Last.Value);
}
[Fact]
public void Jma_Power_AffectsResult()
{
var series = new TSeries();
var gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.1, seed: 42);
for (int i = 0; i < 100; i++)
{
var bar = gbm.Next(isNew: true);
series.Add(bar.Time, bar.Close);
}
var jmaPowerDefault = Jma.Batch(series, 10, power: 0.45);
var jmaPower1 = Jma.Batch(series, 10, power: 1.0);
var jmaPower2 = Jma.Batch(series, 10, power: 2.0);
Assert.NotEqual(jmaPowerDefault.Last.Value, jmaPower1.Last.Value);
Assert.NotEqual(jmaPowerDefault.Last.Value, jmaPower2.Last.Value);
}
[Fact]
public void Jma_SpanCalc_HandlesNaN()
+9 -3
View File
@@ -24,6 +24,7 @@ public sealed class Jma : AbstractBase
private readonly double _lengthDivider; // L'/(L'+2), L' = 0.9*L
private readonly double _logSqrtDivider; // Precomputed log(_sqrtDivider) for Exp optimization
private readonly double _logLengthDivider; // Precomputed log(_lengthDivider) for Exp optimization
private readonly double _power; // Jurik power parameter
// Constants for trimmed mean
private const int JurikTrimCount = 65; // canonical JMA: middle 65 of 128 samples
@@ -71,6 +72,8 @@ public sealed class Jma : AbstractBase
else
_phaseParam = (phase * 0.01) + 1.5;
_power = power;
// --- Length / log / divider parameters (from decompiled JMA) ---
// L_raw ~ (period - 1)/2, with a tiny lower bound to avoid log(0)
double lengthParam = period < 1.0000000002
@@ -144,7 +147,11 @@ public sealed class Jma : AbstractBase
// --- Handle NaN/inf: reuse last finite price ---
if (!double.IsFinite(value))
{
value = _state.Bars > 0 ? _state.LastPrice : 0.0;
if (_state.Bars == 0)
{
return double.NaN;
}
value = _state.LastPrice;
}
else
{
@@ -189,8 +196,7 @@ public sealed class Jma : AbstractBase
double ratio = absValue / refVolatility;
if (ratio < 0.0) ratio = 0.0;
double p = Math.Max(_logParam - 2.0, 0.5);
double d = Math.Pow(ratio, p);
double d = Math.Pow(ratio, _power);
if (d > _logParam) d = _logParam;
if (d < 1.0) d = 1.0;