feat: add LPF - Ehlers Linear Predictive Filter (TASC Jan 2025)

Implements Ehlers' Linear Predictive Filter for dominant cycle detection:
- Roofing filter (HP + SuperSmoother) → AGC → Griffiths adaptive predictor
- DFT spectrum from predictor coefficients → Center of Gravity dominant cycle
- Outputs: DominantCycle, Signal (AGC-normalized), Predict (one-bar-ahead)

Files added:
- lib/cycles/lpf/Lpf.cs (core implementation, sealed class)
- lib/cycles/lpf/Lpf.Quantower.cs (3 LineSeries: Cycle, Signal, Predict)
- lib/cycles/lpf/Lpf.md (canonical template v3 documentation)
- lib/cycles/lpf/lpf.pine (PineScript v6 reference)
- lib/cycles/lpf/tests/Lpf.Tests.cs (38 unit tests)
- lib/cycles/lpf/tests/Lpf.Quantower.Tests.cs (22 adapter tests)

Updated: index files, Python bridge (Exports.cs, _bridge.py, cycles.py)
This commit is contained in:
Miha Kralj
2026-03-17 20:24:54 -07:00
parent 547367790b
commit 6ac30d37e6
14 changed files with 1607 additions and 2 deletions
+10
View File
@@ -1546,6 +1546,16 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
// Lpf: Pattern A (source → output, int lowerBound, int upperBound, int dataLength)
[UnmanagedCallersOnly(EntryPoint = "qtl_lpf")]
public static int QtlLpf(double* src, int n, double* dst, int lowerBound, int upperBound, int dataLength)
{
int v = Chk1(src, dst, n); if (v != 0) return v;
v = ChkPeriod(lowerBound); if (v != 0) return v;
try { Lpf.Batch(Src(src, n), Dst(dst, n), lowerBound, upperBound, dataLength); return StatusCodes.QTL_OK; }
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
// ═══════════════════════════════════════════════════════════════════════
// §8.14 Numerics / transforms
// ═══════════════════════════════════════════════════════════════════════