diff --git a/lib/channels/jbands/Jbands.Tests.cs b/lib/channels/jbands/Jbands.Tests.cs index 68bad19e..ae9f0ac3 100644 --- a/lib/channels/jbands/Jbands.Tests.cs +++ b/lib/channels/jbands/Jbands.Tests.cs @@ -635,7 +635,7 @@ public class JbandsTests { // Verify that middle band matches standalone JMA 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); for (int i = 0; i < 200; i++) diff --git a/lib/trends_IIR/jma/Jma.Tests.cs b/lib/trends_IIR/jma/Jma.Tests.cs index ba4f2d3e..d0fa649a 100644 --- a/lib/trends_IIR/jma/Jma.Tests.cs +++ b/lib/trends_IIR/jma/Jma.Tests.cs @@ -32,11 +32,11 @@ public class JmaTests double[] wrongSizeOutput = new double[3]; // Period must be > 0 - Assert.Throws(() => Jma.Batch(source.AsSpan(), output.AsSpan(), 0, 0, 1.0)); - Assert.Throws(() => Jma.Batch(source.AsSpan(), output.AsSpan(), -1, 0, 1.0)); + Assert.Throws(() => Jma.Batch(source.AsSpan(), output.AsSpan(), 0, 0)); + Assert.Throws(() => Jma.Batch(source.AsSpan(), output.AsSpan(), -1, 0)); // Output must be same length as source - Assert.Throws(() => Jma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3, 0, 1.0)); + Assert.Throws(() => Jma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3, 0)); } [Fact] @@ -235,8 +235,10 @@ public class JmaTests } [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 gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.1, seed: 42); for (int i = 0; i < 100; i++) @@ -245,14 +247,8 @@ public class JmaTests 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); - - // 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); + var result = Jma.Batch(series, 10); + Assert.True(double.IsFinite(result.Last.Value)); } [Fact] diff --git a/lib/trends_IIR/jma/Jma.cs b/lib/trends_IIR/jma/Jma.cs index 64f1ace3..a7704229 100644 --- a/lib/trends_IIR/jma/Jma.cs +++ b/lib/trends_IIR/jma/Jma.cs @@ -64,18 +64,13 @@ public sealed class Jma : AbstractBase 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) { 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) --- 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)); _handler = Handle; - Name = $"Jma({period},{phase},{power})"; // power kept for signature compatibility (ignored in calculation) + Name = $"Jma({period},{phase})"; _devBuffer = new RingBuffer(DevWindowSize); _volBuffer = new RingBuffer(VolWindowSize); @@ -123,8 +118,8 @@ public sealed class Jma : AbstractBase Reset(); } - public Jma(ITValuePublisher source, int period, int phase = 0, double power = 0.45) - : this(period, phase, power) + public Jma(ITValuePublisher source, int period, int phase = 0) + : this(period, phase) { _source = source; 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); } @@ -370,8 +365,7 @@ public sealed class Jma : AbstractBase public static void Batch(ReadOnlySpan source, Span output, int period, - int phase = 0, - double power = 0.45) + int phase = 0) { if (output.Length != source.Length) { @@ -383,16 +377,16 @@ public sealed class Jma : AbstractBase return; } - var jma = new Jma(period, phase, power); + var jma = new Jma(period, phase); for (int i = 0; i < source.Length; i++) { 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); return (results, indicator); } diff --git a/python/quantalib/_bridge.py b/python/quantalib/_bridge.py index 07416210..33c3a7bf 100644 --- a/python/quantalib/_bridge.py +++ b/python/quantalib/_bridge.py @@ -170,7 +170,7 @@ HAS_FRAMA = _bind("qtl_frama", [_dp, _dp, _ci, _ci]) HAS_HOLT = _bind("qtl_holt", [_dp, _dp, _ci, _ci, _cd]) HAS_HTIT = _bind("qtl_htit", [_dp, _dp, _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_LTMA = _bind("qtl_ltma", [_dp, _dp, _ci, _ci]) HAS_MAMA = _bind("qtl_mama", [_dp, _dp, _cd, _ci, _cd, _dp]) diff --git a/python/quantalib/trends_iir.py b/python/quantalib/trends_iir.py index 1b52b30a..efff7433 100644 --- a/python/quantalib/trends_iir.py +++ b/python/quantalib/trends_iir.py @@ -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) -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.""" period = int(period) phase = int(phase) - power = float(power) offset = int(offset) src, idx = _arr(close) n = len(src) 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) diff --git a/python/src/Exports.Generated.cs b/python/src/Exports.Generated.cs index 3c5e45d7..accd3928 100644 --- a/python/src/Exports.Generated.cs +++ b/python/src/Exports.Generated.cs @@ -1146,13 +1146,13 @@ public static unsafe partial class Exports } [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 (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH; 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; } catch { return StatusCodes.QTL_ERR_INTERNAL; }