refactor: remove unused JMA power parameter from entire stack

This commit is contained in:
Miha Kralj
2026-03-01 22:14:30 -08:00
parent ce04e2792b
commit fa77882be7
6 changed files with 24 additions and 35 deletions
+1 -1
View File
@@ -635,7 +635,7 @@ public class JbandsTests
{ {
// Verify that middle band matches standalone JMA // Verify that middle band matches standalone JMA
var jbands = new Jbands(14, 0); var jbands = new Jbands(14, 0);
var jma = new Jma(14, 0, 0.45); var jma = new Jma(14, 0);
var gbm = new GBM(startPrice: 100, mu: 0.01, sigma: 0.1, seed: 999); var gbm = new GBM(startPrice: 100, mu: 0.01, sigma: 0.1, seed: 999);
for (int i = 0; i < 200; i++) for (int i = 0; i < 200; i++)
+8 -12
View File
@@ -32,11 +32,11 @@ public class JmaTests
double[] wrongSizeOutput = new double[3]; double[] wrongSizeOutput = new double[3];
// Period must be > 0 // Period must be > 0
Assert.Throws<ArgumentOutOfRangeException>(() => Jma.Batch(source.AsSpan(), output.AsSpan(), 0, 0, 1.0)); Assert.Throws<ArgumentOutOfRangeException>(() => Jma.Batch(source.AsSpan(), output.AsSpan(), 0, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => Jma.Batch(source.AsSpan(), output.AsSpan(), -1, 0, 1.0)); Assert.Throws<ArgumentOutOfRangeException>(() => Jma.Batch(source.AsSpan(), output.AsSpan(), -1, 0));
// Output must be same length as source // Output must be same length as source
Assert.Throws<ArgumentException>(() => Jma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3, 0, 1.0)); Assert.Throws<ArgumentException>(() => Jma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3, 0));
} }
[Fact] [Fact]
@@ -235,8 +235,10 @@ public class JmaTests
} }
[Fact] [Fact]
public void Jma_Power_AffectsResult() public void Jma_Power_RemovedFromApi()
{ {
// Power parameter was removed — it was never used in calculation.
// Verify the 2-parameter Batch still works correctly.
var series = new TSeries(); var series = new TSeries();
var gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.1, seed: 42); var gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.1, seed: 42);
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
@@ -245,14 +247,8 @@ public class JmaTests
series.Add(bar.Time, bar.Close); series.Add(bar.Time, bar.Close);
} }
var jmaPowerDefault = Jma.Batch(series, 10, power: 0.45); var result = Jma.Batch(series, 10);
var jmaPower1 = Jma.Batch(series, 10, power: 1.0); Assert.True(double.IsFinite(result.Last.Value));
var jmaPower2 = Jma.Batch(series, 10, power: 2.0);
// Power parameter is kept for API compatibility with Pine reference
// but does not affect output in this implementation
Assert.Equal(jmaPowerDefault.Last.Value, jmaPower1.Last.Value, 1e-10);
Assert.Equal(jmaPowerDefault.Last.Value, jmaPower2.Last.Value, 1e-10);
} }
[Fact] [Fact]
+10 -16
View File
@@ -64,18 +64,13 @@ public sealed class Jma : AbstractBase
public override bool IsHot => _state.Bars >= WarmupPeriod; public override bool IsHot => _state.Bars >= WarmupPeriod;
public Jma(int period, int phase = 0, double power = 0.45) public Jma(int period, int phase = 0)
{ {
if (period < 1) if (period < 1)
{ {
throw new ArgumentOutOfRangeException(nameof(period), "Period must be >= 1."); throw new ArgumentOutOfRangeException(nameof(period), "Period must be >= 1.");
} }
if (!double.IsFinite(power))
{
throw new ArgumentException("Power must be finite.", nameof(power));
}
// --- Phase parameter: maps -100..100 -> 0.5..2.5 (Jurik convention) --- // --- Phase parameter: maps -100..100 -> 0.5..2.5 (Jurik convention) ---
if (phase < -100) if (phase < -100)
{ {
@@ -115,7 +110,7 @@ public sealed class Jma : AbstractBase
WarmupPeriod = (int)Math.Ceiling(20.0 + 80.0 * Math.Pow(period, 0.36)); WarmupPeriod = (int)Math.Ceiling(20.0 + 80.0 * Math.Pow(period, 0.36));
_handler = Handle; _handler = Handle;
Name = $"Jma({period},{phase},{power})"; // power kept for signature compatibility (ignored in calculation) Name = $"Jma({period},{phase})";
_devBuffer = new RingBuffer(DevWindowSize); _devBuffer = new RingBuffer(DevWindowSize);
_volBuffer = new RingBuffer(VolWindowSize); _volBuffer = new RingBuffer(VolWindowSize);
@@ -123,8 +118,8 @@ public sealed class Jma : AbstractBase
Reset(); Reset();
} }
public Jma(ITValuePublisher source, int period, int phase = 0, double power = 0.45) public Jma(ITValuePublisher source, int period, int phase = 0)
: this(period, phase, power) : this(period, phase)
{ {
_source = source; _source = source;
source.Pub += _handler; source.Pub += _handler;
@@ -358,9 +353,9 @@ public sealed class Jma : AbstractBase
} }
} }
public static TSeries Batch(TSeries source, int period, int phase = 0, double power = 0.45) public static TSeries Batch(TSeries source, int period, int phase = 0)
{ {
var jma = new Jma(period, phase, power); var jma = new Jma(period, phase);
return jma.Update(source); return jma.Update(source);
} }
@@ -370,8 +365,7 @@ public sealed class Jma : AbstractBase
public static void Batch(ReadOnlySpan<double> source, public static void Batch(ReadOnlySpan<double> source,
Span<double> output, Span<double> output,
int period, int period,
int phase = 0, int phase = 0)
double power = 0.45)
{ {
if (output.Length != source.Length) if (output.Length != source.Length)
{ {
@@ -383,16 +377,16 @@ public sealed class Jma : AbstractBase
return; return;
} }
var jma = new Jma(period, phase, power); var jma = new Jma(period, phase);
for (int i = 0; i < source.Length; i++) for (int i = 0; i < source.Length; i++)
{ {
output[i] = jma.Step(source[i], isNew: true); output[i] = jma.Step(source[i], isNew: true);
} }
} }
public static (TSeries Results, Jma Indicator) Calculate(TSeries source, int period, int phase = 0, double power = 0.45) public static (TSeries Results, Jma Indicator) Calculate(TSeries source, int period, int phase = 0)
{ {
var indicator = new Jma(period, phase, power); var indicator = new Jma(period, phase);
TSeries results = indicator.Update(source); TSeries results = indicator.Update(source);
return (results, indicator); return (results, indicator);
} }
+1 -1
View File
@@ -170,7 +170,7 @@ HAS_FRAMA = _bind("qtl_frama", [_dp, _dp, _ci, _ci])
HAS_HOLT = _bind("qtl_holt", [_dp, _dp, _ci, _ci, _cd]) HAS_HOLT = _bind("qtl_holt", [_dp, _dp, _ci, _ci, _cd])
HAS_HTIT = _bind("qtl_htit", [_dp, _dp, _ci]) HAS_HTIT = _bind("qtl_htit", [_dp, _dp, _ci])
HAS_HWMA = _bind("qtl_hwma", [_dp, _dp, _ci, _ci]) HAS_HWMA = _bind("qtl_hwma", [_dp, _dp, _ci, _ci])
HAS_JMA = _bind("qtl_jma", [_dp, _dp, _ci, _ci, _ci, _cd]) HAS_JMA = _bind("qtl_jma", [_dp, _dp, _ci, _ci, _ci])
HAS_KAMA = _bind("qtl_kama", [_dp, _dp, _ci, _ci, _ci, _ci]) HAS_KAMA = _bind("qtl_kama", [_dp, _dp, _ci, _ci, _ci, _ci])
HAS_LTMA = _bind("qtl_ltma", [_dp, _dp, _ci, _ci]) HAS_LTMA = _bind("qtl_ltma", [_dp, _dp, _ci, _ci])
HAS_MAMA = _bind("qtl_mama", [_dp, _dp, _cd, _ci, _cd, _dp]) HAS_MAMA = _bind("qtl_mama", [_dp, _dp, _cd, _ci, _cd, _dp])
+2 -3
View File
@@ -107,16 +107,15 @@ def hwma(close: object, period: int = 14, offset: int = 0, **kwargs) -> object:
return _wrap(output, idx, f"HWMA_{period}", "trends_iir", offset) return _wrap(output, idx, f"HWMA_{period}", "trends_iir", offset)
def jma(close: object, period: int = 14, phase: int = 0, power: float = 0.45, offset: int = 0, **kwargs) -> object: def jma(close: object, period: int = 14, phase: int = 0, offset: int = 0, **kwargs) -> object:
"""Jurik Moving Average.""" """Jurik Moving Average."""
period = int(period) period = int(period)
phase = int(phase) phase = int(phase)
power = float(power)
offset = int(offset) offset = int(offset)
src, idx = _arr(close) src, idx = _arr(close)
n = len(src) n = len(src)
output = _out(n) output = _out(n)
_check(_lib.qtl_jma(_ptr(src), _ptr(output), n, period, phase, power)) _check(_lib.qtl_jma(_ptr(src), _ptr(output), n, period, phase))
return _wrap(output, idx, f"JMA_{period}", "trends_iir", offset) return _wrap(output, idx, f"JMA_{period}", "trends_iir", offset)
+2 -2
View File
@@ -1146,13 +1146,13 @@ public static unsafe partial class Exports
} }
[UnmanagedCallersOnly(EntryPoint = "qtl_jma")] [UnmanagedCallersOnly(EntryPoint = "qtl_jma")]
public static int QtlJma(double* source, double* output, int n, int period, int phase, double power) public static int QtlJma(double* source, double* output, int n, int period, int phase)
{ {
if (source == null || output == null) return StatusCodes.QTL_ERR_NULL_PTR; if (source == null || output == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH; if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
try try
{ {
Jma.Batch(Src(source, n), Dst(output, n), period, phase, power); Jma.Batch(Src(source, n), Dst(output, n), period, phase);
return StatusCodes.QTL_OK; return StatusCodes.QTL_OK;
} }
catch { return StatusCodes.QTL_ERR_INTERNAL; } catch { return StatusCodes.QTL_ERR_INTERNAL; }