mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-04 04:07:42 +00:00
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:
@@ -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])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user