feat: add 8 new indicators with full integration

New indicators:
- HWC (Holt-Winters Channel) — channels, 27 tests
- VWMACD (Volume-Weighted MACD) — momentum, 38 tests
- Squeeze Pro — oscillators, 69 tests
- BW_MFI (Bill Williams MFI) — oscillators
- DSTOCH (Double Stochastic) — oscillators
- ATRSTOP (ATR Trailing Stop) — reversals
- VSTOP (Volatility Stop) — reversals
- Convexity (Beta Convexity) — statistics, 23 tests

Integration:
- Python bridge: Exports.cs, _bridge.py, wrapper modules
- Documentation: _sidebar.md, _index.md pages, SPEC.md
- All analyzer warnings fixed (MA0074, xUnit2013, S2699)

Build: 0 warnings, 0 errors | Tests: 15,933 passed, 0 failed
This commit is contained in:
Miha Kralj
2026-03-17 08:35:29 -07:00
parent 6f0a339c9b
commit 15f4bb90f3
71 changed files with 10194 additions and 44 deletions
+63
View File
@@ -242,6 +242,27 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
// Vwmacd: multi-output (close, volume → vwmacd, signal, histogram)
[UnmanagedCallersOnly(EntryPoint = "qtl_vwmacd")]
public static int QtlVwmacd(double* close, double* volume, int n,
double* dstVwmacd, double* dstSignal, double* dstHist,
int fastPeriod, int slowPeriod, int signalPeriod)
{
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
if (close == null || volume == null ||
dstVwmacd == null || dstSignal == null || dstHist == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (fastPeriod < 1 || slowPeriod < 1 || signalPeriod < 1) return StatusCodes.QTL_ERR_INVALID_PARAM;
try
{
Vwmacd.Batch(
Src(close, n), Src(volume, n),
Dst(dstVwmacd, n), Dst(dstSignal, n), Dst(dstHist, n),
fastPeriod, slowPeriod, signalPeriod);
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
// ═══════════════════════════════════════════════════════════════════════
// §8.3 Oscillators
// ═══════════════════════════════════════════════════════════════════════
@@ -841,6 +862,26 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
// Hwc: multi-output (src → upper, middle, lower; int period, double multiplier)
[UnmanagedCallersOnly(EntryPoint = "qtl_hwc")]
public static int QtlHwc(double* src, int n,
double* dstUpper, double* dstMiddle, double* dstLower,
int period, double multiplier)
{
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
if (src == null || dstUpper == null || dstMiddle == null || dstLower == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (period < 1) return StatusCodes.QTL_ERR_INVALID_PARAM;
try
{
Hwc.Batch(
Src(src, n),
Dst(dstUpper, n), Dst(dstMiddle, n), Dst(dstLower, n),
period, multiplier);
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
// ═══════════════════════════════════════════════════════════════════════
// §8.7 Volatility
// ═══════════════════════════════════════════════════════════════════════
@@ -1179,6 +1220,28 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
// Convexity: multi-output (asset, market → betaStd, betaUp, betaDown, ratio, convexity)
[UnmanagedCallersOnly(EntryPoint = "qtl_convexity")]
public static int QtlConvexity(double* asset, double* market, int n,
double* dstBetaStd, double* dstBetaUp, double* dstBetaDown,
double* dstRatio, double* dstConvexity, int period)
{
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
if (asset == null || market == null ||
dstBetaStd == null || dstBetaUp == null || dstBetaDown == null ||
dstRatio == null || dstConvexity == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (period < 1) return StatusCodes.QTL_ERR_INVALID_PARAM;
try
{
Convexity.Batch(
Src(asset, n), Src(market, n),
Dst(dstBetaStd, n), Dst(dstBetaUp, n), Dst(dstBetaDown, n),
Dst(dstRatio, n), Dst(dstConvexity, n), period);
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
// ═══════════════════════════════════════════════════════════════════════
// §8.10 Error Metrics
// ═══════════════════════════════════════════════════════════════════════