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
+8
View File
@@ -630,6 +630,7 @@ packages = ["quantalib"]
| sam | `Sam` | A | alpha, cutoff |
| tsi | `Tsi` | A | longPeriod, shortPeriod |
| vel | `Vel` | A | period |
| vwmacd | `Vwmacd` | multi | fastPeriod, slowPeriod, signalPeriod |
### 8.3 Oscillators (~35 indicators)
@@ -649,6 +650,7 @@ packages = ["quantalib"]
| deco | `Deco` | A | shortPeriod, longPeriod |
| dem | `Dem` | E (HLC) | period |
| dpo | `Dpo` | A | period |
| dstoch | `Dstoch` | E (HLC) | period |
| dosc | `Dosc` | A | rsiPeriod, ema1Period, ema2Period, sigPeriod |
| dymi | `Dymi` | A | basePeriod, shortPeriod, longPeriod... |
| er | `Er` | A | period |
@@ -661,6 +663,7 @@ packages = ["quantalib"]
| kri | `Kri` | A | period |
| kst | `Kst` | A | multiple roc/ma periods |
| marketfi | `Marketfi` | D (HL) + vol | — |
| bw_mfi | `BwMfi` | D (HL) + vol → 2 | — |
| mstoch | `Mstoch` | A | period |
| pgo | `Pgo` | E (HLC) | period |
| psl | `Psl` | A | period |
@@ -670,6 +673,7 @@ packages = ["quantalib"]
| rvgi | `Rvgi` | C (OHLC) | period |
| 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 |
| stc | `Stc` | A + enum | kPeriod, dPeriod, fastLen, slowLen, smoothing(int) |
| stoch | `Stoch` | I (HLC→2) | kPeriod, kSmooth, dSmooth |
| stochf | `Stochf` | I (HLC→2) | kPeriod, dPeriod |
@@ -774,6 +778,7 @@ All channel indicators output 3 spans: upper, middle, lower (Pattern I).
| dc | `Dc` | D (HL) | period |
| decaychannel | `Decaychannel` | B (HLC) | period |
| fcb | `Fcb` | D (HL) | period |
| hwc | `Hwc` | A→multi | period, multiplier |
| jbands | `Jbands` | A | period, phase, power |
| kc | `Kc` | B (HLC) | period, multiplier |
| maenv | `Maenv` | A | period, pct |
@@ -886,6 +891,7 @@ All channel indicators output 3 spans: upper, middle, lower (Pattern I).
| zscore | `Zscore` | A | period |
| ztest | `Ztest` | A | period, mu0 |
| cointegration | `Cointegration` | H | — |
| convexity | `Convexity` | multi | period |
### 8.10 Errors (~25 indicators)
@@ -996,6 +1002,7 @@ All channel indicators output 3 spans: upper, middle, lower (Pattern I).
| Indicator | C# Class | Pattern | Key params |
|-----------|----------|---------|------------|
| atrstop | `Atrstop` | E (HLC→1) | period, multiplier |
| chandelier | `Chandelier` | C (OHLC→2) | period, multiplier |
| ckstop | `Ckstop` | C (OHLC→2) | period, multiplier |
| fractals | `Fractals` | D (HL→2) | period |
@@ -1008,6 +1015,7 @@ All channel indicators output 3 spans: upper, middle, lower (Pattern I).
| sar | `Sar` | C (OHLC) | accelStart, accelMax |
| swings | `Swings` | D (HL→multi) | period |
| ttm_scalper | `TtmScalper` | D (HL→multi) | period |
| vstop | `Vstop` | E (HLC→1) | period, multiplier |
### 8.15 Numerics (~15 indicators)
+8
View File
@@ -130,6 +130,8 @@ HAS_IMI = _bind("qtl_imi", [_dp, _dp, _dp, _dp, _dp, _ci, _ci, _dp])
HAS_KDJ = _bind("qtl_kdj", [_dp, _dp, _dp, _dp, _dp, _dp, _ci, _ci, _ci])
HAS_KST = _bind("qtl_kst", [_dp, _dp, _dp, _ci, _ci, _ci, _ci, _ci, _ci, _ci, _ci, _ci, _ci])
HAS_MARKETFI = _bind("qtl_marketfi", [_dp, _dp, _dp, _dp, _ci])
HAS_BWMFI = _bind("qtl_bwmfi", [_dp, _dp, _dp, _dp, _dp, _ci])
HAS_DSTOCH = _bind("qtl_dstoch", [_dp, _dp, _dp, _dp, _ci, _ci])
HAS_MSTOCH = _bind("qtl_mstoch", [_dp, _dp, _ci, _ci, _ci, _ci])
HAS_PGO = _bind("qtl_pgo", [_dp, _dp, _dp, _dp, _ci, _ci])
HAS_QQE = _bind("qtl_qqe", [_dp, _dp, _ci, _ci, _ci, _cd])
@@ -137,6 +139,7 @@ HAS_REVERSEEMA = _bind("qtl_reverseema", [_dp, _dp, _ci, _ci])
HAS_RVGI = _bind("qtl_rvgi", [_dp, _dp, _dp, _dp, _dp, _dp, _ci, _ci])
HAS_SMI = _bind("qtl_smi", [_dp, _dp, _dp, _dp, _dp, _ci, _ci, _ci, _ci, _ci])
HAS_SQUEEZE = _bind("qtl_squeeze", [_dp, _dp, _dp, _dp, _dp, _ci, _ci, _cd, _cd])
HAS_SQUEEZE_PRO = _bind("qtl_squeeze_pro", [_dp, _dp, _dp, _dp, _dp, _ci, _ci, _cd, _cd, _cd, _cd, _ci, _ci, _ci])
HAS_STC = _bind("qtl_stc", [_dp, _dp, _ci, _ci, _ci, _ci, _ci, _ci])
HAS_STOCH = _bind("qtl_stoch", [_dp, _dp, _dp, _dp, _dp, _ci, _ci, _ci])
HAS_STOCHF = _bind("qtl_stochf", [_dp, _dp, _dp, _dp, _dp, _ci, _ci, _ci])
@@ -399,6 +402,7 @@ HAS_WEIBULLDIST = _bind("qtl_weibulldist", [_dp, _dp, _ci, _cd, _cd, _ci])
# ═══════════════════════════════════════════════════════════════════════════
# Reversals
# ═══════════════════════════════════════════════════════════════════════════
HAS_ATRSTOP = _bind("qtl_atrstop", [_dp, _dp, _dp, _dp, _ci, _ci, _cd])
HAS_CHANDELIER = _bind("qtl_chandelier", [_dp, _dp, _dp, _dp, _dp, _ci, _ci, _cd])
HAS_CKSTOP = _bind("qtl_ckstop", [_dp, _dp, _dp, _dp, _dp, _ci, _ci, _cd, _ci])
HAS_FRACTALS = _bind("qtl_fractals", [_dp, _dp, _dp, _dp, _ci])
@@ -411,6 +415,7 @@ HAS_PIVOTWOOD = _bind("qtl_pivotwood", [_dp, _dp, _dp, _dp, _ci])
HAS_SAR = _bind("qtl_sar", [_dp, _dp, _dp, _dp, _dp, _ci, _cd, _cd, _cd])
HAS_SWINGS = _bind("qtl_swings", [_dp, _dp, _dp, _dp, _ci, _ci])
HAS_TTMSCALPER = _bind("qtl_ttmscalper", [_dp, _dp, _dp, _dp, _dp, _ci, _ci])
HAS_VSTOP = _bind("qtl_vstop", [_dp, _dp, _dp, _dp, _ci, _ci, _cd])
# ── Momentum (Exports.cs — manual) ──
@@ -424,6 +429,7 @@ HAS_BIAS = _bind("qtl_bias", [_dp, _ci, _dp, _ci])
HAS_CFO = _bind("qtl_cfo", [_dp, _ci, _dp, _ci])
HAS_CFB = _bind("qtl_cfb", [_dp, _ci, _dp, _ip, _ci])
HAS_ASI = _bind("qtl_asi", [_dp, _dp, _dp, _dp, _ci, _dp, _cd])
HAS_VWMACD = _bind("qtl_vwmacd", [_dp, _dp, _ci, _dp, _dp, _dp, _ci, _ci, _ci])
# ── Oscillators (Exports.cs — manual) ──
HAS_FISHER = _bind("qtl_fisher", [_dp, _ci, _dp, _ci])
@@ -490,6 +496,7 @@ HAS_CCYC = _bind("qtl_ccyc", [_dp, _ci, _dp, _cd])
HAS_BBANDS = _bind("qtl_bbands", [_dp, _ci, _dp, _dp, _dp, _ci, _cd])
HAS_ATRBANDS = _bind("qtl_atrbands", [_dp, _dp, _dp, _ci, _dp, _dp, _dp, _ci, _cd])
HAS_APCHANNEL = _bind("qtl_apchannel", [_dp, _dp, _ci, _dp, _dp, _cd])
HAS_HWC = _bind("qtl_hwc", [_dp, _ci, _dp, _dp, _dp, _ci, _cd])
# ── Volatility (Exports.cs — manual) ──
HAS_TR = _bind("qtl_tr", [_dp, _dp, _dp, _ci, _dp])
@@ -529,6 +536,7 @@ HAS_ENTROPY = _bind("qtl_entropy", [_dp, _ci, _dp, _ci])
HAS_CORREL = _bind("qtl_correl", [_dp, _dp, _ci, _dp, _ci])
HAS_COVARIANCE = _bind("qtl_covariance", [_dp, _dp, _ci, _dp, _ci, _ci])
HAS_COINTEGRATION = _bind("qtl_cointegration", [_dp, _dp, _ci, _dp, _ci])
HAS_CONVEXITY = _bind("qtl_convexity", [_dp, _dp, _ci, _dp, _dp, _dp, _dp, _dp, _ci])
# ── Errors (Exports.cs — manual) ──
HAS_MSE = _bind("qtl_mse", [_dp, _dp, _ci, _dp, _ci])
+15
View File
@@ -17,6 +17,7 @@ __all__ = [
"dc",
"decaychannel",
"fcb",
"hwc",
"jbands",
"kc",
"maenv",
@@ -350,3 +351,17 @@ def apchannel(high: object, low: object, period: int = 20,
upper = _out(n); lower = _out(n)
_check(_lib.qtl_apchannel(_ptr(h), _ptr(l), n, _ptr(upper), _ptr(lower), float(period)))
return _wrap_multi({f"APCU_{period}": upper, f"APCL_{period}": lower}, idx, "channels", offset)
def hwc(close: object, period: int = 20, multiplier: float = 1.0,
offset: int = 0, **kwargs) -> object:
"""Holt-Winters Channel -> (upper, middle, lower) or DataFrame."""
period = int(kwargs.get("length", period)); offset = int(offset)
multiplier = float(kwargs.get("mult", multiplier))
arr, idx = _arr(close)
n = len(arr)
upper = _out(n); middle = _out(n); lower = _out(n)
_check(_lib.qtl_hwc(_ptr(arr), n, _ptr(upper), _ptr(middle), _ptr(lower), period, multiplier))
return _wrap_multi(
{f"HWCU_{period}": upper, f"HWCM_{period}": middle, f"HWCL_{period}": lower},
idx, "channels", offset)
+23
View File
@@ -28,6 +28,7 @@ __all__ = [
"cfo",
"cfb",
"asi",
"vwmacd",
]
@@ -231,3 +232,25 @@ def asi(open: object, high: object, low: object, close: object,
n = len(o); dst = _out(n)
_check(_lib.qtl_asi(_ptr(o), _ptr(h), _ptr(l), _ptr(c), n, _ptr(dst), float(limit)))
return _wrap(dst, idx, "ASI", "momentum", int(offset))
def vwmacd(close: object, volume: object, fastPeriod: int = 12,
slowPeriod: int = 26, signalPeriod: int = 9,
offset: int = 0, **kwargs) -> object:
"""Volume-Weighted MACD -> (vwmacd, signal, histogram) or DataFrame."""
fastPeriod = int(kwargs.get("fast", fastPeriod))
slowPeriod = int(kwargs.get("slow", slowPeriod))
signalPeriod = int(kwargs.get("signal", signalPeriod))
offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c)
d_vwmacd = _out(n); d_signal = _out(n); d_hist = _out(n)
_check(_lib.qtl_vwmacd(
_ptr(c), _ptr(v), n,
_ptr(d_vwmacd), _ptr(d_signal), _ptr(d_hist),
fastPeriod, slowPeriod, signalPeriod))
return _wrap_multi(
{f"VWMACD_{fastPeriod}_{slowPeriod}": d_vwmacd,
f"VWMACDs_{signalPeriod}": d_signal,
f"VWMACDh_{fastPeriod}_{slowPeriod}": d_hist},
idx, "momentum", offset)
+42
View File
@@ -11,6 +11,7 @@ __all__ = [
"ac",
"ao",
"bbs",
"bw_mfi",
"coppock",
"eri",
"fi",
@@ -200,6 +201,28 @@ def marketfi(high: object, low: object, volume: object, offset: int = 0, **kwarg
return _wrap(output, idx, "MARKETFI", "oscillators", offset)
def bw_mfi(high: object, low: object, volume: object, offset: int = 0, **kwargs) -> object:
"""Bill Williams Market Facilitation Index with 4-zone classification."""
offset = int(offset)
h, idx = _arr(high); l, _ = _arr(low); v, _ = _arr(volume)
n = len(h)
mfiOut = _out(n)
zoneOut = _out(n)
_check(_lib.qtl_bwmfi(_ptr(h), _ptr(l), _ptr(v), _ptr(mfiOut), _ptr(zoneOut), n))
return _wrap_multi({"mfiOut": mfiOut, "zoneOut": zoneOut}, idx, "oscillators", offset)
def dstoch(high: object, low: object, close: object, period: int = 21, offset: int = 0, **kwargs) -> object:
"""Double Stochastic (Bressert DSS)."""
period = int(kwargs.get("length", period))
offset = int(offset)
h, idx = _arr(high); l, _ = _arr(low); c, _ = _arr(close)
n = len(h)
output = _out(n)
_check(_lib.qtl_dstoch(_ptr(h), _ptr(l), _ptr(c), _ptr(output), n, period))
return _wrap(output, idx, f"DSTOCH_{period}", "oscillators", offset)
def mstoch(close: object, stochLength: int = 20, hpLength: int = 48, ssLength: int = 10, offset: int = 0, **kwargs) -> object:
"""Modified Stochastic."""
stochLength = int(stochLength)
@@ -289,6 +312,25 @@ def squeeze(high: object, low: object, close: object, period: int = 14, bbMult:
return _wrap_multi({"momOut": momOut, "sqOut": sqOut}, idx, "oscillators", offset)
def squeeze_pro(high: object, low: object, close: object, period: int = 20, bbMult: float = 2.0, kcMultWide: float = 2.0, kcMultNormal: float = 1.5, kcMultNarrow: float = 1.0, momLength: int = 12, momSmooth: int = 6, useSma: bool = True, offset: int = 0, **kwargs) -> object:
"""Squeeze Pro (LazyBear enhanced TTM Squeeze with 3 KC widths)."""
period = int(kwargs.get("length", period))
bbMult = float(bbMult)
kcMultWide = float(kcMultWide)
kcMultNormal = float(kcMultNormal)
kcMultNarrow = float(kcMultNarrow)
momLength = int(momLength)
momSmooth = int(momSmooth)
useSmaInt = int(bool(useSma))
offset = int(offset)
h, idx = _arr(high); l, _ = _arr(low); c, _ = _arr(close)
n = len(h)
momOut = _out(n)
sqOut = _out(n)
_check(_lib.qtl_squeeze_pro(_ptr(h), _ptr(l), _ptr(c), _ptr(momOut), _ptr(sqOut), n, period, bbMult, kcMultWide, kcMultNormal, kcMultNarrow, momLength, momSmooth, useSmaInt))
return _wrap_multi({"momOut": momOut, "sqOut": sqOut}, idx, "oscillators", offset)
def stc(close: object, kPeriod: int = 14, dPeriod: int = 3, fastLength: int = 23, slowLength: int = 50, smoothing: int = 10, offset: int = 0, **kwargs) -> object:
"""Schaff Trend Cycle."""
kPeriod = int(kPeriod)
+26
View File
@@ -8,6 +8,7 @@ from ._helpers import _arr, _ptr, _out, _wrap, _wrap_multi, _check, _lib
__all__ = [
"atrstop",
"chandelier",
"ckstop",
"fractals",
@@ -21,9 +22,22 @@ __all__ = [
"sarext",
"swings",
"ttm_scalper",
"vstop",
]
def atrstop(high: object, low: object, close: object, period: int = 21, multiplier: float = 3.0, offset: int = 0, **kwargs) -> object:
"""ATR Trailing Stop."""
period = int(kwargs.get("length", period))
multiplier = float(multiplier)
offset = int(offset)
h, idx = _arr(high); l, _ = _arr(low); c, _ = _arr(close)
n = len(h)
output = _out(n)
_check(_lib.qtl_atrstop(_ptr(h), _ptr(l), _ptr(c), _ptr(output), n, period, multiplier))
return _wrap(output, idx, f"ATRSTOP_{period}", "reversals", offset)
def chandelier(open: object, high: object, low: object, close: object, period: int = 14, multiplier: float = 2.0, offset: int = 0, **kwargs) -> object:
"""Chandelier Exit."""
period = int(kwargs.get("length", period))
@@ -173,3 +187,15 @@ def ttm_scalper(high: object, low: object, close: object, useCloses: int = 0, of
lowOutput = _out(n)
_check(_lib.qtl_ttmscalper(_ptr(h), _ptr(l), _ptr(c), _ptr(highOutput), _ptr(lowOutput), n, useCloses))
return _wrap_multi({"highOutput": highOutput, "lowOutput": lowOutput}, idx, "reversals", offset)
def vstop(high: object, low: object, close: object, period: int = 7, multiplier: float = 3.0, offset: int = 0, **kwargs) -> object:
"""Volatility Stop."""
period = int(kwargs.get("length", period))
multiplier = float(multiplier)
offset = int(offset)
h, idx = _arr(high); l, _ = _arr(low); c, _ = _arr(close)
n = len(h)
output = _out(n)
_check(_lib.qtl_vstop(_ptr(h), _ptr(l), _ptr(c), _ptr(output), n, period, multiplier))
return _wrap(output, idx, f"VSTOP_{period}", "reversals", offset)
+25
View File
@@ -41,6 +41,7 @@ __all__ = [
"correl",
"covariance",
"cointegration",
"convexity",
]
@@ -406,3 +407,27 @@ def cointegration(x: object, y: object, period: int = 20,
n = len(xarr); dst = _out(n)
_check(_lib.qtl_cointegration(_ptr(xarr), _ptr(yarr), n, _ptr(dst), period))
return _wrap(dst, idx, f"COINT_{period}", "statistics", offset)
def convexity(x: object, y: object, period: int = 20,
offset: int = 0, **kwargs) -> object:
"""Beta Convexity (up/down beta asymmetry).
Returns dict with keys: beta_std, beta_up, beta_down, ratio, convexity.
"""
period = int(kwargs.get("length", period)); offset = int(offset)
xarr, idx = _arr(x); yarr, _ = _arr(y)
n = len(xarr)
d_std = _out(n); d_up = _out(n); d_down = _out(n)
d_ratio = _out(n); d_cvx = _out(n)
_check(_lib.qtl_convexity(
_ptr(xarr), _ptr(yarr), n,
_ptr(d_std), _ptr(d_up), _ptr(d_down),
_ptr(d_ratio), _ptr(d_cvx), period))
return {
"beta_std": _wrap(d_std, idx, f"BETA_STD_{period}", "statistics", offset),
"beta_up": _wrap(d_up, idx, f"BETA_UP_{period}", "statistics", offset),
"beta_down": _wrap(d_down, idx, f"BETA_DOWN_{period}", "statistics", offset),
"ratio": _wrap(d_ratio, idx, f"RATIO_{period}", "statistics", offset),
"convexity": _wrap(d_cvx, idx, f"CONVEXITY_{period}", "statistics", offset),
}
+79
View File
@@ -308,6 +308,20 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
[UnmanagedCallersOnly(EntryPoint = "qtl_atrstop")]
public static int QtlAtrstop(double* high, double* low, double* close, double* output, int n, int period, double multiplier)
{
if (high == null || low == null || close == null || output == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
if (period <= 0) return StatusCodes.QTL_ERR_INVALID_PARAM;
try
{
Atrstop.Batch(Src(high, n), Src(low, n), Src(close, n), Dst(output, n), period, multiplier);
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
[UnmanagedCallersOnly(EntryPoint = "qtl_atr")]
public static int QtlAtr(double* sourceOpen, double* sourceHigh, double* sourceLow, double* sourceClose, double* sourceVolume, int period, int n, double* dst)
{
@@ -372,6 +386,44 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
[UnmanagedCallersOnly(EntryPoint = "qtl_bwmfi")]
public static int QtlBwMfi(double* high, double* low, double* volume, double* mfiOutput, double* zoneOutput, int n)
{
if (high == null || low == null || volume == null || mfiOutput == null || zoneOutput == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
try
{
var hi = Src(high, n);
var lo = Src(low, n);
var vol = Src(volume, n);
var mfiTmp = new double[n];
var zoneTmp = new int[n];
BwMfi.Batch(hi, lo, vol, mfiTmp.AsSpan(), zoneTmp.AsSpan());
mfiTmp.AsSpan().CopyTo(Dst(mfiOutput, n));
var zoneDst = Dst(zoneOutput, n);
for (int i = 0; i < n; i++)
{
zoneDst[i] = zoneTmp[i];
}
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
[UnmanagedCallersOnly(EntryPoint = "qtl_dstoch")]
public static int QtlDstoch(double* high, double* low, double* close, double* output, int n, int period)
{
if (high == null || low == null || close == null || output == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
if (period <= 0) return StatusCodes.QTL_ERR_INVALID_PARAM;
try
{
Dstoch.Batch(Src(high, n), Src(low, n), Src(close, n), Dst(output, n), period);
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
[UnmanagedCallersOnly(EntryPoint = "qtl_cci")]
public static int QtlCci(double* sourceOpen, double* sourceHigh, double* sourceLow, double* sourceClose, double* sourceVolume, int period, int n, double* dst)
{
@@ -2848,6 +2900,19 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
[UnmanagedCallersOnly(EntryPoint = "qtl_squeeze_pro")]
public static int QtlSqueezePro(double* high, double* low, double* close, double* momOut, double* sqOut, int n, int period, double bbMult, double kcMultWide, double kcMultNormal, double kcMultNarrow, int momLength, int momSmooth, int useSma)
{
if (high == null || low == null || close == null || momOut == null || sqOut == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
try
{
SqueezePro.Batch(Src(high, n), Src(low, n), Src(close, n), Dst(momOut, n), Dst(sqOut, n), period, bbMult, kcMultWide, kcMultNormal, kcMultNarrow, momLength, momSmooth, useSma != 0);
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
[UnmanagedCallersOnly(EntryPoint = "qtl_ssf2")]
public static int QtlSsf2(double* source, double* output, int n, int period)
{
@@ -3385,6 +3450,20 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
[UnmanagedCallersOnly(EntryPoint = "qtl_vstop")]
public static int QtlVstop(double* high, double* low, double* close, double* output, int n, int period, double multiplier)
{
if (high == null || low == null || close == null || output == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
if (period <= 0) return StatusCodes.QTL_ERR_INVALID_PARAM;
try
{
Vstop.Batch(Src(high, n), Src(low, n), Src(close, n), Dst(output, n), period, multiplier);
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
[UnmanagedCallersOnly(EntryPoint = "qtl_vov")]
public static int QtlVov(double* source, double* output, int n, int volatilityPeriod, int vovPeriod)
{
+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
// ═══════════════════════════════════════════════════════════════════════