feat: add RRSI (Rocket RSI) — Ehlers TASC May 2018

Algorithm: SuperSmoother-filtered momentum → Ehlers RSI → Fisher Transform
- 2-pole Butterworth IIR pre-filter removes noise
- Ehlers RSI (raw summation, not Wilder) outputs [-1,1]
- arctanh produces Gaussian-distributed zero-mean oscillator

Files: Rrsi.cs, Rrsi.Quantower.cs, Rrsi.md, 31+7 tests
Integration: sidebar, indices, Python bridge (Exports, _bridge, oscillators, SPEC)
Build: 0 warnings, 0 errors | Tests: 15,963 passed, 0 failed
This commit is contained in:
Miha Kralj
2026-03-17 09:25:32 -07:00
parent 15f4bb90f3
commit eb9e41fc2e
12 changed files with 1028 additions and 0 deletions
+1
View File
@@ -671,6 +671,7 @@ packages = ["quantalib"]
| reflex | `Reflex` | A | period |
| reverseema | `ReverseEma` | A | period |
| rvgi | `Rvgi` | C (OHLC) | period |
| rrsi | `Rrsi` | A | smoothLength, rsiLength |
| smi | `Smi` | I (HLC→2+) | period, smoothK, smoothD |
| squeeze | `Squeeze` | I (HLC→multi) | bbPeriod, kcPeriod... |
| squeeze_pro | `SqueezePro` | I (HLC→multi) | period, bbMult, kcMultWide/Normal/Narrow |
+1
View File
@@ -438,6 +438,7 @@ HAS_DPO = _bind("qtl_dpo", [_dp, _ci, _dp, _ci])
HAS_TRIX = _bind("qtl_trix", [_dp, _ci, _dp, _ci])
HAS_INERTIA = _bind("qtl_inertia", [_dp, _ci, _dp, _ci])
HAS_RSX = _bind("qtl_rsx", [_dp, _ci, _dp, _ci])
HAS_RRSI = _bind("qtl_rrsi", [_dp, _ci, _dp, _ci, _ci])
HAS_ER = _bind("qtl_er", [_dp, _ci, _dp, _ci])
HAS_CTI = _bind("qtl_cti", [_dp, _ci, _dp, _ci])
HAS_REFLEX = _bind("qtl_reflex", [_dp, _ci, _dp, _ci])
+12
View File
@@ -25,6 +25,7 @@ __all__ = [
"qqe",
"reverseema",
"rvgi",
"rrsi",
"smi",
"squeeze",
"stc",
@@ -283,6 +284,17 @@ def rvgi(open: object, high: object, low: object, close: object, period: int = 1
return _wrap_multi({"rvgiOutput": rvgiOutput, "signalOutput": signalOutput}, idx, "oscillators", offset)
def rrsi(close: object, smoothLength: int = 10, rsiLength: int = 10, offset: int = 0, **kwargs) -> object:
"""Rocket RSI (Ehlers) — Fisher Transform of Super Smootherfiltered RSI."""
src = _to_np(close)
n = len(src)
out = _np.empty(n, dtype=_np.float64)
_bridge._check(_bridge._lib.qtl_rrsi(
src.ctypes.data_as(_bridge._dp), n,
out.ctypes.data_as(_bridge._dp), smoothLength, rsiLength))
return _shift(out, offset)
def smi(high: object, low: object, close: object, kPeriod: int = 14, kSmooth: int = 3, dSmooth: int = 3, blau: int = 3, offset: int = 0, **kwargs) -> object:
"""Stochastic Momentum Index."""
kPeriod = int(kPeriod)
+11
View File
@@ -327,6 +327,17 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
// Rrsi: Pattern A (dual period params)
[UnmanagedCallersOnly(EntryPoint = "qtl_rrsi")]
public static int QtlRrsi(double* src, int n, double* dst, int smoothLength, int rsiLength)
{
int v = Chk1(src, dst, n); if (v != 0) return v;
v = ChkPeriod(smoothLength); if (v != 0) return v;
v = ChkPeriod(rsiLength); if (v != 0) return v;
try { Rrsi.Batch(Src(src, n), Dst(dst, n), smoothLength, rsiLength); return StatusCodes.QTL_OK; }
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
// Er: Pattern A
[UnmanagedCallersOnly(EntryPoint = "qtl_er")]
public static int QtlEr(double* src, int n, double* dst, int period)