feat: add new indicators (Decay, Edecay, MinusDi, MinusDm, PlusDi, PlusDm, Maxindex, Minindex, Sarext) and update pine scripts, core libs, validation tests, and python bindings

This commit is contained in:
Miha Kralj
2026-03-09 13:45:46 -07:00
parent 8e43d62cbb
commit 031f1b5fe6
491 changed files with 6156 additions and 5590 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Accelerator Oscillator (AC)", "AC", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Awesome Oscillator (AO)", "AO", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Absolute Price Oscillator (APO)", "APO", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Bollinger %B", "BBB", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Bulls Bears Index (BBI)", "BBI", overlay=true)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Bollinger Band Squeeze (BBS)", "BBS", overlay=true)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Bull-Bear Power Ratio (BRAR)", "BRAR", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Chande Forecast Oscillator", "CFO", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Coppock Curve (COPPOCK)", "COPPOCK", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Connors RSI (CRSI)", "CRSI", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Correlation Trend Indicator (CTI)", "CTI", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Ehlers Decycler Oscillator (DECO)", "DECO", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("DeMarker Oscillator (DEM)", "DEM", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Derivative Oscillator (DOSC)", "DOSC", overlay=false, precision=4)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Detrended Price Oscillator (DPO)", "DPO", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Dynamic Momentum Index (DYMOI)", "DYMOI", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Efficiency Ratio (ER)", "ER", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Elder Ray Index (ERI)", "ERI", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Force Index (FI)", "FI", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Ehlers Fisher Transform (FISHER)", "FISHER", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Williams Gator Oscillator", "GATOR", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Intraday Momentum Index (IMI)", "IMI", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Inertia", "INERTIA", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("KDJ", "KDJ", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Kairi Relative Index (KRI)", "KRI", overlay=false, precision=4)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("KST Oscillator (KST)", "KST", overlay=false, precision=4)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
// Indicator algorithm (C) 2013 John F. Ehlers
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Pretty Good Oscillator", "PGO", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Psychological Line (PSL)", "PSL", overlay=false)
@@ -241,4 +241,33 @@ public sealed class QqeValidationTests
Assert.NotEqual(ind1.QqeValue, ind2.QqeValue);
_output.WriteLine($"QQE(14,5,4.236)={ind1.QqeValue:F6} QQE(7,3,2)={ind2.QqeValue:F6}");
}
[Fact]
public void Qqe_Correction_Recomputes()
{
var ind = new Qqe();
var t0 = DateTime.MinValue;
// Build state well past warmup (WarmupPeriod ≈ 37)
for (int i = 0; i < 60; i++)
{
ind.Update(new TValue(t0.AddSeconds(i), 100.0 + i * 0.5));
}
// Anchor bar
var anchorTime = t0.AddSeconds(60);
const double anchorPrice = 130.0;
ind.Update(new TValue(anchorTime, anchorPrice), isNew: true);
double anchorQqe = ind.QqeValue;
double anchorSignal = ind.Signal;
// Use large downward spike (÷10) to move RSI away from ceiling
ind.Update(new TValue(anchorTime, anchorPrice / 10), isNew: false);
Assert.NotEqual(anchorQqe, ind.QqeValue);
// Correction back to original — both outputs must restore exactly
ind.Update(new TValue(anchorTime, anchorPrice), isNew: false);
Assert.Equal(anchorQqe, ind.QqeValue, 1e-9);
Assert.Equal(anchorSignal, ind.Signal, 1e-9);
}
}
+4
View File
@@ -278,6 +278,10 @@ public sealed class Qqe : AbstractBase
}
/// <summary>Span-based batch calculation (QQE line only).</summary>
/// <remarks>
/// Computes only the QQE line (EMA-smoothed RSI). The dynamic trailing <see cref="Signal"/> line
/// is not available from this API; use <see cref="Update(TValue, bool)"/> streaming to access both outputs.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Batch(ReadOnlySpan<double> source, Span<double> output,
int rsiPeriod = DefaultRsiPeriod,
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Quantitative Qualitative Estimation (QQE)", "QQE", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
// Indicator algorithm (C) 2017 John F. Ehlers
//@version=6
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Relative Vigor Index (RVGI)", "RVGI", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Stochastic Momentum Index (SMI)", "SMI", overlay=false)
@@ -215,4 +215,35 @@ public sealed class SqueezeValidationTests
Assert.Equal(sqStream.Momentum, sqBatch.Momentum, precision: 8);
Assert.Equal(sqStream.SqueezeOn, sqBatch.SqueezeOn);
}
[Fact]
public void Squeeze_Correction_Recomputes()
{
var ind = new Squeeze(period: 20);
long t0 = TimeSpan.TicksPerSecond;
// Build state well past warmup (WarmupPeriod = 20)
for (int i = 0; i < 50; i++)
{
double p = 100.0 + i * 0.5;
ind.Update(new TBar(t0 + i * TimeSpan.TicksPerSecond, p, p + 1, p - 1, p, 1000), isNew: true);
}
// Anchor bar
long anchorTime = t0 + 50 * TimeSpan.TicksPerSecond;
var anchorBar = new TBar(anchorTime, 125.0, 126.0, 124.0, 125.0, 1000);
ind.Update(anchorBar, isNew: true);
double anchorMomentum = ind.Momentum;
bool anchorSqueezeOn = ind.SqueezeOn;
// Correction with dramatically different values — Momentum must change
var corruptBar = new TBar(anchorTime, 1250.0, 1260.0, 1240.0, 1250.0, 1000);
ind.Update(corruptBar, isNew: false);
Assert.NotEqual(anchorMomentum, ind.Momentum);
// Correction back to original — both outputs must restore exactly
ind.Update(anchorBar, isNew: false);
Assert.Equal(anchorMomentum, ind.Momentum, 1e-9);
Assert.Equal(anchorSqueezeOn, ind.SqueezeOn);
}
}
+5
View File
@@ -369,6 +369,11 @@ public sealed class Squeeze : ITValuePublisher
SqueezeOn = false;
}
/// <summary>
/// Span-based batch Squeeze calculation. Populates both the momentum and squeeze-state output spans.
/// </summary>
/// <param name="momOut">Output span for Squeeze momentum values (linear regression of detrended price).</param>
/// <param name="sqOut">Output span for squeeze-state values (positive when Bollinger Bands are inside Keltner Channel).</param>
public static void Batch(
ReadOnlySpan<double> high,
ReadOnlySpan<double> low,
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Squeeze Momentum (SQUEEZE)", "SQUEEZE", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Schaff Trend Cycle (STC)", "STC", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Stochastic Oscillator (STOCH)", "Stoch", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Stochastic Fast (STOCHF)", "STOCHF", overlay=false)
@@ -405,4 +405,33 @@ public sealed class StochrsiValidationTests : IDisposable
_output.WriteLine($"Skender span: {totalCompared} compared, {mismatches} mismatches ({mismatchRate:P2})");
Assert.True(mismatchRate < 0.05, $"Skender span mismatch rate {mismatchRate:P2} exceeds 5% ({mismatches}/{totalCompared})");
}
[Fact]
public void Stochrsi_Correction_Recomputes()
{
var ind = new Stochrsi();
var t0 = DateTime.MinValue;
// Build state well past warmup (WarmupPeriod ≈ 31)
for (int i = 0; i < 50; i++)
{
ind.Update(new TValue(t0.AddSeconds(i), 100.0 + i * 0.5));
}
// Anchor bar
var anchorTime = t0.AddSeconds(50);
const double anchorPrice = 125.0;
ind.Update(new TValue(anchorTime, anchorPrice), isNew: true);
double anchorK = ind.K;
double anchorD = ind.D;
// Use large downward spike (÷10) to move StochRSI away from ceiling
ind.Update(new TValue(anchorTime, anchorPrice / 10), isNew: false);
Assert.NotEqual(anchorK, ind.K);
// Correction back to original — both outputs must restore exactly
ind.Update(new TValue(anchorTime, anchorPrice), isNew: false);
Assert.Equal(anchorK, ind.K, 1e-9);
Assert.Equal(anchorD, ind.D, 1e-9);
}
}
+4
View File
@@ -348,6 +348,10 @@ public sealed class Stochrsi : AbstractBase
/// <summary>
/// High-performance span-based StochRSI %K calculation.
/// </summary>
/// <remarks>
/// Computes only the %K line (SMA-smoothed Stochastic RSI). The %D signal line (<see cref="D"/>)
/// is not available from this API; use <see cref="Update(TValue, bool)"/> streaming to access both outputs.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Batch(ReadOnlySpan<double> source, Span<double> output,
int rsiLength, int stochLength, int kSmooth, int dSmooth)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Stochastic RSI (STOCHRSI)", "StochRSI", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("TD Sequential (TD_SEQ)", "TD_SEQ", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
// Indicator algorithm (C) 2013 John F. Ehlers
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("TRIX", "TRIX", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Ultimate Oscillator (ULTOSC)", "ULTOSC", overlay=false)
+1 -1
View File
@@ -1,4 +1,4 @@
// The MIT License (MIT)
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Williams %R (WILLR)", "WILLR", overlay=false)