Files
QuanTAlib/python/tools/complete_bridge.py
T

1708 lines
73 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""Complete the Python bridge by adding all Exports.cs (manual) bindings and wrappers.
This script:
1. Rewrites _bridge.py with ALL bindings (Generated + Manual)
2. Appends missing wrappers to each category .py file
3. Rewrites indicators.py as thin re-export
4. Updates __init__.py
"""
import os
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PKG = os.path.join(ROOT, "quantalib")
def write(path: str, content: str) -> None:
with open(path, "w", encoding="utf-8", newline="\n") as f:
f.write(content)
print(f" wrote {os.path.relpath(path, ROOT)}")
def append(path: str, content: str) -> None:
with open(path, "a", encoding="utf-8", newline="\n") as f:
f.write(content)
print(f" appended to {os.path.relpath(path, ROOT)}")
def read(path: str) -> str:
with open(path, "r", encoding="utf-8") as f:
return f.read()
# ═══════════════════════════════════════════════════════════════════════════
# Step 1: Read existing _bridge.py and add missing manual bindings
# ═══════════════════════════════════════════════════════════════════════════
print("Step 1: Adding missing bindings to _bridge.py ...")
bridge_path = os.path.join(PKG, "_bridge.py")
bridge = read(bridge_path)
# Check which bindings already exist
MANUAL_BINDINGS = {
# ── Core ──
"qtl_avgprice": ["{dp}", "{dp}", "{dp}", "{dp}", "{ci}", "{dp}"],
"qtl_medprice": ["{dp}", "{dp}", "{ci}", "{dp}"],
"qtl_typprice": ["{dp}", "{dp}", "{dp}", "{ci}", "{dp}"],
"qtl_midbody": ["{dp}", "{dp}", "{ci}", "{dp}"],
# ── Momentum ──
"qtl_rsi": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_roc": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_mom": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_cmo": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_tsi": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}"],
"qtl_apo": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}"],
"qtl_bias": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_cfo": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_cfb": ["{dp}", "{ci}", "{dp}", "{ip}", "{ci}"],
"qtl_asi": ["{dp}", "{dp}", "{dp}", "{dp}", "{ci}", "{dp}", "{cd}"],
# ── Oscillators ──
"qtl_fisher": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_fisher04": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_dpo": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_trix": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_inertia": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_rsx": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_er": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_cti": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_reflex": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_trendflex": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_kri": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_psl": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_deco": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}"],
"qtl_dosc": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}", "{ci}", "{ci}"],
"qtl_dymoi": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}", "{ci}", "{ci}", "{ci}"],
"qtl_crsi": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}", "{ci}"],
"qtl_bbb": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_bbi": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}", "{ci}", "{ci}"],
"qtl_dem": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_brar": ["{dp}", "{dp}", "{dp}", "{dp}", "{ci}", "{dp}", "{dp}", "{ci}"],
# ── Trends FIR ──
"qtl_sma": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_wma": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_hma": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_trima": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_swma": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_dwma": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_blma": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_alma": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}", "{cd}"],
"qtl_lsma": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}", "{cd}"],
"qtl_sgma": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_sinema": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_hanma": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_parzen": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_tsf": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_conv": ["{dp}", "{ci}", "{dp}", "{dp}", "{ci}"],
"qtl_bwma": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}"],
"qtl_crma": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_sp15": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_tukey_w": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_rain": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_afirma": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}", "{ci}"],
# ── Trends IIR ──
"qtl_ema": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_ema_alpha": ["{dp}", "{ci}", "{dp}", "{cd}"],
"qtl_dema": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_dema_alpha":["{dp}", "{ci}", "{dp}", "{cd}"],
"qtl_tema": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_lema": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_hema": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_ahrens": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_decycler": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_dsma": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_gdema": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_coral": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_agc": ["{dp}", "{ci}", "{dp}", "{cd}"],
"qtl_ccyc": ["{dp}", "{ci}", "{dp}", "{cd}"],
# ── Channels ──
"qtl_bbands": ["{dp}", "{ci}", "{dp}", "{dp}", "{dp}", "{ci}", "{cd}"],
"qtl_aberr": ["{dp}", "{ci}", "{dp}", "{dp}", "{dp}", "{ci}", "{cd}"],
"qtl_atrbands": ["{dp}", "{dp}", "{dp}", "{ci}", "{dp}", "{dp}", "{dp}", "{ci}", "{cd}"],
"qtl_apchannel": ["{dp}", "{dp}", "{ci}", "{dp}", "{dp}", "{cd}"],
# ── Volatility ──
"qtl_tr": ["{dp}", "{dp}", "{dp}", "{ci}", "{dp}"],
"qtl_bbw": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_bbwn": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}", "{ci}"],
"qtl_bbwp": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}", "{ci}"],
"qtl_stddev": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_variance": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_etherm": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_ccv": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}"],
"qtl_cv": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}", "{cd}"],
"qtl_cvi": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}"],
"qtl_ewma": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}", "{ci}"],
# ── Volume ──
"qtl_obv": ["{dp}", "{dp}", "{ci}", "{dp}"],
"qtl_pvt": ["{dp}", "{dp}", "{ci}", "{dp}"],
"qtl_pvr": ["{dp}", "{dp}", "{ci}", "{dp}"],
"qtl_vf": ["{dp}", "{dp}", "{ci}", "{dp}"],
"qtl_nvi": ["{dp}", "{dp}", "{ci}", "{dp}"],
"qtl_pvi": ["{dp}", "{dp}", "{ci}", "{dp}"],
"qtl_tvi": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_pvd": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_vwma": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_evwma": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_efi": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_aobv": ["{dp}", "{dp}", "{ci}", "{dp}", "{dp}"],
"qtl_mfi": ["{dp}", "{dp}", "{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_cmf": ["{dp}", "{dp}", "{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_eom": ["{dp}", "{dp}", "{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_pvo": ["{dp}", "{ci}", "{dp}", "{dp}", "{dp}", "{ci}", "{ci}", "{ci}"],
# ── Statistics ──
"qtl_zscore": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_cma": ["{dp}", "{ci}", "{dp}"],
"qtl_entropy": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_correlation": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_covariance": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}", "{ci}"],
"qtl_cointegration": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
# ── Errors ──
"qtl_mse": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_rmse": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_mae": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_mape": ["{dp}", "{dp}", "{ci}", "{dp}", "{ci}"],
# ── Filters ──
"qtl_bessel": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_butter2": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_butter3": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_cheby1": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_cheby2": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_elliptic": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_edcf": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_bpf": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}"],
"qtl_alaguerre": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}"],
"qtl_bilateral": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}", "{cd}"],
"qtl_baxterking": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}", "{ci}"],
"qtl_cfitz": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}"],
# ── Cycles ──
"qtl_cg": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_dsp": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_ccor": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_ebsw": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}"],
"qtl_eacp": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}", "{ci}", "{ci}"],
# ── Numerics ──
"qtl_change": ["{dp}", "{ci}", "{dp}", "{ci}"],
"qtl_exptrans": ["{dp}", "{ci}", "{dp}"],
"qtl_betadist": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}", "{cd}"],
"qtl_expdist": ["{dp}", "{ci}", "{dp}", "{ci}", "{cd}"],
"qtl_binomdist":["{dp}", "{ci}", "{dp}", "{ci}", "{ci}", "{ci}"],
"qtl_cwt": ["{dp}", "{ci}", "{dp}", "{cd}", "{cd}"],
"qtl_dwt": ["{dp}", "{ci}", "{dp}", "{ci}", "{ci}"],
}
# Build binding lines
TYPE_MAP = {"{dp}": "_dp", "{ci}": "_ci", "{cd}": "_cd", "{ip}": "_ip"}
missing_bindings = {} # category -> list of lines
categories_order = [
("Core", ["qtl_avgprice","qtl_medprice","qtl_typprice","qtl_midbody"]),
("Momentum", ["qtl_rsi","qtl_roc","qtl_mom","qtl_cmo","qtl_tsi","qtl_apo","qtl_bias","qtl_cfo","qtl_cfb","qtl_asi"]),
("Oscillators", ["qtl_fisher","qtl_fisher04","qtl_dpo","qtl_trix","qtl_inertia","qtl_rsx","qtl_er","qtl_cti","qtl_reflex","qtl_trendflex","qtl_kri","qtl_psl","qtl_deco","qtl_dosc","qtl_dymoi","qtl_crsi","qtl_bbb","qtl_bbi","qtl_dem","qtl_brar"]),
("Trends — FIR", ["qtl_sma","qtl_wma","qtl_hma","qtl_trima","qtl_swma","qtl_dwma","qtl_blma","qtl_alma","qtl_lsma","qtl_sgma","qtl_sinema","qtl_hanma","qtl_parzen","qtl_tsf","qtl_conv","qtl_bwma","qtl_crma","qtl_sp15","qtl_tukey_w","qtl_rain","qtl_afirma"]),
("Trends — IIR", ["qtl_ema","qtl_ema_alpha","qtl_dema","qtl_dema_alpha","qtl_tema","qtl_lema","qtl_hema","qtl_ahrens","qtl_decycler","qtl_dsma","qtl_gdema","qtl_coral","qtl_agc","qtl_ccyc"]),
("Channels", ["qtl_bbands","qtl_aberr","qtl_atrbands","qtl_apchannel"]),
("Volatility", ["qtl_tr","qtl_bbw","qtl_bbwn","qtl_bbwp","qtl_stddev","qtl_variance","qtl_etherm","qtl_ccv","qtl_cv","qtl_cvi","qtl_ewma"]),
("Volume", ["qtl_obv","qtl_pvt","qtl_pvr","qtl_vf","qtl_nvi","qtl_pvi","qtl_tvi","qtl_pvd","qtl_vwma","qtl_evwma","qtl_efi","qtl_aobv","qtl_mfi","qtl_cmf","qtl_eom","qtl_pvo"]),
("Statistics", ["qtl_zscore","qtl_cma","qtl_entropy","qtl_correlation","qtl_covariance","qtl_cointegration"]),
("Errors", ["qtl_mse","qtl_rmse","qtl_mae","qtl_mape"]),
("Filters", ["qtl_bessel","qtl_butter2","qtl_butter3","qtl_cheby1","qtl_cheby2","qtl_elliptic","qtl_edcf","qtl_bpf","qtl_alaguerre","qtl_bilateral","qtl_baxterking","qtl_cfitz"]),
("Cycles", ["qtl_cg","qtl_dsp","qtl_ccor","qtl_ebsw","qtl_eacp"]),
("Numerics", ["qtl_change","qtl_exptrans","qtl_betadist","qtl_expdist","qtl_binomdist","qtl_cwt","qtl_dwt"]),
]
added_count = 0
new_lines = []
for cat, names in categories_order:
cat_lines = []
for name in names:
if f'"{name}"' in bridge:
continue # already bound
args = MANUAL_BINDINGS[name]
arg_str = ", ".join(TYPE_MAP[a] for a in args)
var = "HAS_" + name.replace("qtl_", "").upper()
cat_lines.append(f'{var} = _bind("{name}", [{arg_str}])')
added_count += 1
if cat_lines:
new_lines.append(f"\n# ── {cat} (Exports.cs — manual) ──")
new_lines.extend(cat_lines)
if new_lines:
# Append to end of _bridge.py
with open(bridge_path, "a", encoding="utf-8", newline="\n") as f:
f.write("\n")
f.write("\n".join(new_lines))
f.write("\n")
print(f" Added {added_count} bindings to _bridge.py")
else:
print(" All bindings already present in _bridge.py")
# ═══════════════════════════════════════════════════════════════════════════
# Step 2: Add missing wrappers to category .py files
# ═══════════════════════════════════════════════════════════════════════════
print("\nStep 2: Adding missing wrappers to category files ...")
# For each category file, check what's in __all__ and add missing funcs
# ── core.py ──
core_additions = '''
def avgprice(open: object, high: object, low: object, close: object,
offset: int = 0, **kwargs) -> object:
"""Average Price = (O+H+L+C)/4."""
offset = int(offset)
o, idx = _arr(open); h, _ = _arr(high); l, _ = _arr(low); c, _ = _arr(close)
n = len(o); dst = _out(n)
_check(_lib.qtl_avgprice(_ptr(o), _ptr(h), _ptr(l), _ptr(c), n, _ptr(dst)))
return _wrap(dst, idx, "AVGPRICE", "core", offset)
def medprice(high: object, low: object, offset: int = 0, **kwargs) -> object:
"""Median Price = (H+L)/2."""
h, idx = _arr(high); l, _ = _arr(low)
n = len(h); dst = _out(n)
_check(_lib.qtl_medprice(_ptr(h), _ptr(l), n, _ptr(dst)))
return _wrap(dst, idx, "MEDPRICE", "core", int(offset))
def typprice(open: object, high: object, low: object,
offset: int = 0, **kwargs) -> object:
"""Typical Price = (O+H+L)/3 (QuanTAlib variant)."""
o, idx = _arr(open); h, _ = _arr(high); l, _ = _arr(low)
n = len(o); dst = _out(n)
_check(_lib.qtl_typprice(_ptr(o), _ptr(h), _ptr(l), n, _ptr(dst)))
return _wrap(dst, idx, "TYPPRICE", "core", int(offset))
def midbody(open: object, close: object, offset: int = 0, **kwargs) -> object:
"""Mid Body = (O+C)/2."""
o, idx = _arr(open); c, _ = _arr(close)
n = len(o); dst = _out(n)
_check(_lib.qtl_midbody(_ptr(o), _ptr(c), n, _ptr(dst)))
return _wrap(dst, idx, "MIDBODY", "core", int(offset))
'''
# ── momentum.py ──
momentum_additions = '''
import ctypes
def rsi(close: object, length: int = 14, offset: int = 0, **kwargs) -> object:
"""Relative Strength Index."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_rsi(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"RSI_{length}", "momentum", offset)
def roc(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Rate of Change."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_roc(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"ROC_{length}", "momentum", offset)
def mom(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Momentum."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_mom(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"MOM_{length}", "momentum", offset)
def cmo(close: object, length: int = 14, offset: int = 0, **kwargs) -> object:
"""Chande Momentum Oscillator."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_cmo(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"CMO_{length}", "momentum", offset)
def tsi(close: object, long_period: int = 25, short_period: int = 13,
offset: int = 0, **kwargs) -> object:
"""True Strength Index."""
long_period = int(long_period); short_period = int(short_period); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_tsi(_ptr(src), n, _ptr(dst), long_period, short_period))
return _wrap(dst, idx, f"TSI_{long_period}_{short_period}", "momentum", offset)
def apo(close: object, fast: int = 12, slow: int = 26,
offset: int = 0, **kwargs) -> object:
"""Absolute Price Oscillator."""
fast = int(fast); slow = int(slow); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_apo(_ptr(src), n, _ptr(dst), fast, slow))
return _wrap(dst, idx, f"APO_{fast}_{slow}", "momentum", offset)
def bias(close: object, length: int = 26, offset: int = 0, **kwargs) -> object:
"""Bias."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_bias(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"BIAS_{length}", "momentum", offset)
def cfo(close: object, length: int = 14, offset: int = 0, **kwargs) -> object:
"""Chande Forecast Oscillator."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_cfo(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"CFO_{length}", "momentum", offset)
def cfb(close: object, lengths: list | None = None,
offset: int = 0, **kwargs) -> object:
"""Composite Fractal Behavior."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
if lengths:
arr_t = (ctypes.c_int * len(lengths))(*lengths)
_check(_lib.qtl_cfb(_ptr(src), n, _ptr(dst), arr_t, len(lengths)))
else:
_check(_lib.qtl_cfb(_ptr(src), n, _ptr(dst), None, 0))
return _wrap(dst, idx, "CFB", "momentum", offset)
def asi(open: object, high: object, low: object, close: object,
limit: float = 3.0, offset: int = 0, **kwargs) -> object:
"""Accumulative Swing Index."""
o, idx = _arr(open); h, _ = _arr(high); l, _ = _arr(low); c, _ = _arr(close)
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))
'''
# ── oscillators.py ──
oscillators_additions = '''
def fisher(close: object, length: int = 9, offset: int = 0, **kwargs) -> object:
"""Fisher Transform."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_fisher(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"FISHER_{length}", "oscillators", offset)
def fisher04(close: object, length: int = 9, offset: int = 0, **kwargs) -> object:
"""Fisher Transform (0.4 variant)."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_fisher04(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"FISHER04_{length}", "oscillators", offset)
def dpo(close: object, length: int = 20, offset: int = 0, **kwargs) -> object:
"""Detrended Price Oscillator."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_dpo(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"DPO_{length}", "oscillators", offset)
def trix(close: object, length: int = 18, offset: int = 0, **kwargs) -> object:
"""Triple EMA Rate of Change."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_trix(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"TRIX_{length}", "oscillators", offset)
def inertia(close: object, length: int = 20, offset: int = 0, **kwargs) -> object:
"""Inertia."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_inertia(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"INERTIA_{length}", "oscillators", offset)
def rsx(close: object, length: int = 14, offset: int = 0, **kwargs) -> object:
"""Relative Strength Xtra."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_rsx(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"RSX_{length}", "oscillators", offset)
def er(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Efficiency Ratio."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_er(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"ER_{length}", "oscillators", offset)
def cti(close: object, length: int = 12, offset: int = 0, **kwargs) -> object:
"""Correlation Trend Indicator."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_cti(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"CTI_{length}", "oscillators", offset)
def reflex(close: object, length: int = 20, offset: int = 0, **kwargs) -> object:
"""Reflex."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_reflex(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"REFLEX_{length}", "oscillators", offset)
def trendflex(close: object, length: int = 20, offset: int = 0, **kwargs) -> object:
"""Trendflex."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_trendflex(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"TRENDFLEX_{length}", "oscillators", offset)
def kri(close: object, length: int = 20, offset: int = 0, **kwargs) -> object:
"""Kairi Relative Index."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_kri(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"KRI_{length}", "oscillators", offset)
def psl(close: object, length: int = 12, offset: int = 0, **kwargs) -> object:
"""Psychological Line."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_psl(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"PSL_{length}", "oscillators", offset)
def deco(close: object, short_period: int = 30, long_period: int = 60,
offset: int = 0, **kwargs) -> object:
"""DECO."""
short_period = int(short_period); long_period = int(long_period); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_deco(_ptr(src), n, _ptr(dst), short_period, long_period))
return _wrap(dst, idx, f"DECO_{short_period}_{long_period}", "oscillators", offset)
def dosc(close: object, rsi_period: int = 14, ema1_period: int = 5,
ema2_period: int = 3, signal_period: int = 9,
offset: int = 0, **kwargs) -> object:
"""DeMarker Oscillator."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_dosc(_ptr(src), n, _ptr(dst),
int(rsi_period), int(ema1_period), int(ema2_period), int(signal_period)))
return _wrap(dst, idx, f"DOSC_{rsi_period}", "oscillators", offset)
def dymoi(close: object, base_period: int = 14, short_period: int = 5,
long_period: int = 10, min_period: int = 3, max_period: int = 30,
offset: int = 0, **kwargs) -> object:
"""Dynamic Momentum Index."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_dymoi(_ptr(src), n, _ptr(dst),
int(base_period), int(short_period), int(long_period),
int(min_period), int(max_period)))
return _wrap(dst, idx, "DYMOI", "oscillators", offset)
def crsi(close: object, rsi_period: int = 3, streak_period: int = 2,
rank_period: int = 100, offset: int = 0, **kwargs) -> object:
"""Connors RSI."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_crsi(_ptr(src), n, _ptr(dst),
int(rsi_period), int(streak_period), int(rank_period)))
return _wrap(dst, idx, f"CRSI_{rsi_period}", "oscillators", offset)
def bbb(close: object, length: int = 20, mult: float = 2.0,
offset: int = 0, **kwargs) -> object:
"""Bollinger Band Bounce."""
length = int(length); mult = float(mult); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_bbb(_ptr(src), n, _ptr(dst), length, mult))
return _wrap(dst, idx, f"BBB_{length}", "oscillators", offset)
def bbi(close: object, p1: int = 3, p2: int = 6, p3: int = 12, p4: int = 24,
offset: int = 0, **kwargs) -> object:
"""Bull Bear Index."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_bbi(_ptr(src), n, _ptr(dst), int(p1), int(p2), int(p3), int(p4)))
return _wrap(dst, idx, "BBI", "oscillators", offset)
def dem(high: object, low: object, length: int = 14,
offset: int = 0, **kwargs) -> object:
"""DeMarker."""
length = int(length)
h, idx = _arr(high); l, _ = _arr(low)
n = len(h); dst = _out(n)
_check(_lib.qtl_dem(_ptr(h), _ptr(l), n, _ptr(dst), length))
return _wrap(dst, idx, f"DEM_{length}", "oscillators", int(offset))
def brar(open: object, high: object, low: object, close: object,
length: int = 26, offset: int = 0, **kwargs) -> object:
"""Bull-Bear Ratio (BRAR)."""
length = int(length); offset = int(offset)
o, idx = _arr(open); h, _ = _arr(high); l, _ = _arr(low); c, _ = _arr(close)
n = len(o); br = _out(n); ar = _out(n)
_check(_lib.qtl_brar(_ptr(o), _ptr(h), _ptr(l), _ptr(c), n, _ptr(br), _ptr(ar), length))
return _wrap_multi({f"BR_{length}": br, f"AR_{length}": ar}, idx, "oscillators", offset)
'''
# ── trends_fir.py ──
trends_fir_additions = '''
import numpy as np
_F64 = np.float64
def sma(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Simple Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_sma(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"SMA_{length}", "trends_fir", offset)
def wma(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Weighted Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_wma(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"WMA_{length}", "trends_fir", offset)
def hma(close: object, length: int = 9, offset: int = 0, **kwargs) -> object:
"""Hull Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_hma(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"HMA_{length}", "trends_fir", offset)
def trima(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Triangular Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_trima(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"TRIMA_{length}", "trends_fir", offset)
def swma(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Symmetric Weighted Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_swma(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"SWMA_{length}", "trends_fir", offset)
def dwma(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Double Weighted Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_dwma(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"DWMA_{length}", "trends_fir", offset)
def blma(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Blackman Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_blma(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"BLMA_{length}", "trends_fir", offset)
def alma(close: object, length: int = 10, alma_offset: float = 0.85,
sigma: float = 6.0, offset: int = 0, **kwargs) -> object:
"""Arnaud Legoux Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_alma(_ptr(src), n, _ptr(dst), length, float(alma_offset), float(sigma)))
return _wrap(dst, idx, f"ALMA_{length}", "trends_fir", offset)
def lsma(close: object, length: int = 25, offset: int = 0, **kwargs) -> object:
"""Least Squares Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_lsma(_ptr(src), n, _ptr(dst), length, 0, 1.0))
return _wrap(dst, idx, f"LSMA_{length}", "trends_fir", offset)
def sgma(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Savitzky-Golay Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_sgma(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"SGMA_{length}", "trends_fir", offset)
def sinema(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Sine-weighted Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_sinema(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"SINEMA_{length}", "trends_fir", offset)
def hanma(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Hann-weighted Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_hanma(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"HANMA_{length}", "trends_fir", offset)
def parzen(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Parzen-weighted Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_parzen(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"PARZEN_{length}", "trends_fir", offset)
def tsf(close: object, length: int = 14, offset: int = 0, **kwargs) -> object:
"""Time Series Forecast."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_tsf(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"TSF_{length}", "trends_fir", offset)
def conv(close: object, kernel: list | None = None,
offset: int = 0, **kwargs) -> object:
"""Convolution with custom kernel."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
if kernel is None:
kernel = [1.0]
k = np.ascontiguousarray(kernel, dtype=_F64)
_check(_lib.qtl_conv(_ptr(src), n, _ptr(dst), _ptr(k), len(k)))
return _wrap(dst, idx, "CONV", "trends_fir", offset)
def bwma(close: object, length: int = 10, order: int = 0,
offset: int = 0, **kwargs) -> object:
"""Butterworth-weighted Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_bwma(_ptr(src), n, _ptr(dst), length, int(order)))
return _wrap(dst, idx, f"BWMA_{length}", "trends_fir", offset)
def crma(close: object, length: int = 10, volume_factor: float = 1.0,
offset: int = 0, **kwargs) -> object:
"""Cosine-Ramp Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_crma(_ptr(src), n, _ptr(dst), length, float(volume_factor)))
return _wrap(dst, idx, f"CRMA_{length}", "trends_fir", offset)
def sp15(close: object, length: int = 15, offset: int = 0, **kwargs) -> object:
"""SP-15 Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_sp15(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"SP15_{length}", "trends_fir", offset)
def tukey_w(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Tukey-windowed Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_tukey_w(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"TUKEY_{length}", "trends_fir", offset)
def rain(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""RAIN Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_rain(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"RAIN_{length}", "trends_fir", offset)
def afirma(close: object, length: int = 10, window_type: int = 0,
use_simd: bool = False, offset: int = 0, **kwargs) -> object:
"""Adaptive FIR Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_afirma(_ptr(src), n, _ptr(dst), length, int(window_type), int(use_simd)))
return _wrap(dst, idx, f"AFIRMA_{length}", "trends_fir", offset)
'''
# ── trends_iir.py ──
trends_iir_additions = '''
def ema(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Exponential Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_ema(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"EMA_{length}", "trends_iir", offset)
def ema_alpha(close: object, alpha: float = 0.1, offset: int = 0, **kwargs) -> object:
"""EMA with explicit alpha."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_ema_alpha(_ptr(src), n, _ptr(dst), float(alpha)))
return _wrap(dst, idx, f"EMA_a{alpha:.4f}", "trends_iir", offset)
def dema(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Double Exponential Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_dema(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"DEMA_{length}", "trends_iir", offset)
def dema_alpha(close: object, alpha: float = 0.1, offset: int = 0, **kwargs) -> object:
"""DEMA with explicit alpha."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_dema_alpha(_ptr(src), n, _ptr(dst), float(alpha)))
return _wrap(dst, idx, f"DEMA_a{alpha:.4f}", "trends_iir", offset)
def tema(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Triple Exponential Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_tema(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"TEMA_{length}", "trends_iir", offset)
def lema(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Laguerre-based EMA."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_lema(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"LEMA_{length}", "trends_iir", offset)
def hema(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Henderson EMA."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_hema(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"HEMA_{length}", "trends_iir", offset)
def ahrens(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Ahrens Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_ahrens(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"AHRENS_{length}", "trends_iir", offset)
def decycler(close: object, length: int = 20, offset: int = 0, **kwargs) -> object:
"""Simple Decycler."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_decycler(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"DECYCLER_{length}", "trends_iir", offset)
def dsma(close: object, length: int = 10, factor: float = 0.5,
offset: int = 0, **kwargs) -> object:
"""Deviation-Scaled Moving Average."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_dsma(_ptr(src), n, _ptr(dst), length, float(factor)))
return _wrap(dst, idx, f"DSMA_{length}", "trends_iir", offset)
def gdema(close: object, length: int = 10, vfactor: float = 1.0,
offset: int = 0, **kwargs) -> object:
"""Generalized DEMA."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_gdema(_ptr(src), n, _ptr(dst), length, float(vfactor)))
return _wrap(dst, idx, f"GDEMA_{length}", "trends_iir", offset)
def coral(close: object, length: int = 10, friction: float = 0.4,
offset: int = 0, **kwargs) -> object:
"""CORAL Trend."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_coral(_ptr(src), n, _ptr(dst), length, float(friction)))
return _wrap(dst, idx, f"CORAL_{length}", "trends_iir", offset)
def agc(close: object, alpha: float = 0.1, offset: int = 0, **kwargs) -> object:
"""Automatic Gain Control."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_agc(_ptr(src), n, _ptr(dst), float(alpha)))
return _wrap(dst, idx, f"AGC_a{alpha:.4f}", "trends_iir", offset)
def ccyc(close: object, alpha: float = 0.1, offset: int = 0, **kwargs) -> object:
"""Cyber Cycle."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_ccyc(_ptr(src), n, _ptr(dst), float(alpha)))
return _wrap(dst, idx, f"CCYC_a{alpha:.4f}", "trends_iir", offset)
'''
# ── channels.py ──
channels_additions = '''
def bbands(close: object, length: int = 20, std: float = 2.0,
offset: int = 0, **kwargs) -> object:
"""Bollinger Bands -> (upper, mid, lower) or DataFrame."""
length = int(length); std = float(std); offset = int(offset)
src, idx = _arr(close); n = len(src)
upper = _out(n); mid = _out(n); lower = _out(n)
_check(_lib.qtl_bbands(_ptr(src), n, _ptr(upper), _ptr(mid), _ptr(lower), length, std))
return _wrap_multi(
{f"BBU_{length}_{std}": upper, f"BBM_{length}_{std}": mid, f"BBL_{length}_{std}": lower},
idx, "channels", offset)
def aberr(close: object, length: int = 20, mult: float = 2.0,
offset: int = 0, **kwargs) -> object:
"""Aberration Bands -> (upper, mid, lower) or DataFrame."""
length = int(length); mult = float(mult); offset = int(offset)
src, idx = _arr(close); n = len(src)
upper = _out(n); mid = _out(n); lower = _out(n)
_check(_lib.qtl_aberr(_ptr(src), n, _ptr(mid), _ptr(upper), _ptr(lower), length, mult))
return _wrap_multi(
{f"ABERRU_{length}_{mult}": upper, f"ABERRM_{length}_{mult}": mid, f"ABERRL_{length}_{mult}": lower},
idx, "channels", offset)
def atrbands(high: object, low: object, close: object,
length: int = 14, mult: float = 2.0,
offset: int = 0, **kwargs) -> object:
"""ATR Bands -> (upper, mid, lower) or DataFrame."""
length = int(length); mult = float(mult); offset = int(offset)
h, idx = _arr(high); l, _ = _arr(low); c, _ = _arr(close)
n = len(h)
upper = _out(n); mid = _out(n); lower = _out(n)
_check(_lib.qtl_atrbands(_ptr(h), _ptr(l), _ptr(c), n, _ptr(upper), _ptr(mid), _ptr(lower), length, mult))
return _wrap_multi(
{f"ATRBU_{length}_{mult}": upper, f"ATRBM_{length}_{mult}": mid, f"ATRBL_{length}_{mult}": lower},
idx, "channels", offset)
def apchannel(high: object, low: object, length: int = 20,
offset: int = 0, **kwargs) -> object:
"""Average Price Channel -> (upper, lower) or DataFrame."""
length = int(length); offset = int(offset)
h, idx = _arr(high); l, _ = _arr(low)
n = len(h)
upper = _out(n); lower = _out(n)
_check(_lib.qtl_apchannel(_ptr(h), _ptr(l), n, _ptr(upper), _ptr(lower), float(length)))
return _wrap_multi({f"APCU_{length}": upper, f"APCL_{length}": lower}, idx, "channels", offset)
'''
# ── volatility.py ──
volatility_additions = '''
def tr(high: object, low: object, close: object, offset: int = 0, **kwargs) -> object:
"""True Range."""
h, idx = _arr(high); l, _ = _arr(low); c, _ = _arr(close)
n = len(h); dst = _out(n)
_check(_lib.qtl_tr(_ptr(h), _ptr(l), _ptr(c), n, _ptr(dst)))
return _wrap(dst, idx, "TR", "volatility", int(offset))
def bbw(close: object, length: int = 20, mult: float = 2.0,
offset: int = 0, **kwargs) -> object:
"""Bollinger Band Width."""
length = int(length); mult = float(mult); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_bbw(_ptr(src), n, _ptr(dst), length, mult))
return _wrap(dst, idx, f"BBW_{length}", "volatility", offset)
def bbwn(close: object, length: int = 20, mult: float = 2.0,
lookback: int = 252, offset: int = 0, **kwargs) -> object:
"""Bollinger Band Width Normalized."""
length = int(length); mult = float(mult); lookback = int(lookback); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_bbwn(_ptr(src), n, _ptr(dst), length, mult, lookback))
return _wrap(dst, idx, f"BBWN_{length}", "volatility", offset)
def bbwp(close: object, length: int = 20, mult: float = 2.0,
lookback: int = 252, offset: int = 0, **kwargs) -> object:
"""Bollinger Band Width Percentile."""
length = int(length); mult = float(mult); lookback = int(lookback); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_bbwp(_ptr(src), n, _ptr(dst), length, mult, lookback))
return _wrap(dst, idx, f"BBWP_{length}", "volatility", offset)
def stddev(close: object, length: int = 20, offset: int = 0, **kwargs) -> object:
"""Standard Deviation."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_stddev(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"STDDEV_{length}", "volatility", offset)
def variance(close: object, length: int = 20, offset: int = 0, **kwargs) -> object:
"""Variance."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_variance(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"VAR_{length}", "volatility", offset)
def etherm(high: object, low: object, length: int = 14,
offset: int = 0, **kwargs) -> object:
"""Elder Thermometer."""
length = int(length)
h, idx = _arr(high); l, _ = _arr(low)
n = len(h); dst = _out(n)
_check(_lib.qtl_etherm(_ptr(h), _ptr(l), n, _ptr(dst), length))
return _wrap(dst, idx, f"ETHERM_{length}", "volatility", int(offset))
def ccv(close: object, short_period: int = 20, long_period: int = 1,
offset: int = 0, **kwargs) -> object:
"""Close-to-Close Volatility."""
short_period = int(short_period); long_period = int(long_period); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_ccv(_ptr(src), n, _ptr(dst), short_period, long_period))
return _wrap(dst, idx, f"CCV_{short_period}", "volatility", offset)
def cv(close: object, length: int = 20, min_vol: float = 0.2,
max_vol: float = 0.7, offset: int = 0, **kwargs) -> object:
"""Coefficient of Variation."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_cv(_ptr(src), n, _ptr(dst), length, float(min_vol), float(max_vol)))
return _wrap(dst, idx, f"CV_{length}", "volatility", offset)
def cvi(close: object, ema_period: int = 10, roc_period: int = 10,
offset: int = 0, **kwargs) -> object:
"""Chaikin Volatility Index."""
ema_period = int(ema_period); roc_period = int(roc_period); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_cvi(_ptr(src), n, _ptr(dst), ema_period, roc_period))
return _wrap(dst, idx, f"CVI_{ema_period}", "volatility", offset)
def ewma(close: object, length: int = 20, is_pop: int = 1,
ann_factor: int = 252, offset: int = 0, **kwargs) -> object:
"""Exponentially Weighted Moving Average (volatility)."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_ewma(_ptr(src), n, _ptr(dst), length, int(is_pop), int(ann_factor)))
return _wrap(dst, idx, f"EWMA_{length}", "volatility", offset)
'''
# ── volume.py ──
volume_additions = '''
def obv(close: object, volume: object, offset: int = 0, **kwargs) -> object:
"""On-Balance Volume."""
offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c); dst = _out(n)
_check(_lib.qtl_obv(_ptr(c), _ptr(v), n, _ptr(dst)))
return _wrap(dst, idx, "OBV", "volume", offset)
def pvt(close: object, volume: object, offset: int = 0, **kwargs) -> object:
"""Price Volume Trend."""
offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c); dst = _out(n)
_check(_lib.qtl_pvt(_ptr(c), _ptr(v), n, _ptr(dst)))
return _wrap(dst, idx, "PVT", "volume", offset)
def pvr(close: object, volume: object, offset: int = 0, **kwargs) -> object:
"""Price Volume Rank."""
offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c); dst = _out(n)
_check(_lib.qtl_pvr(_ptr(c), _ptr(v), n, _ptr(dst)))
return _wrap(dst, idx, "PVR", "volume", offset)
def vf(close: object, volume: object, offset: int = 0, **kwargs) -> object:
"""Volume Flow."""
offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c); dst = _out(n)
_check(_lib.qtl_vf(_ptr(c), _ptr(v), n, _ptr(dst)))
return _wrap(dst, idx, "VF", "volume", offset)
def nvi(close: object, volume: object, offset: int = 0, **kwargs) -> object:
"""Negative Volume Index."""
offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c); dst = _out(n)
_check(_lib.qtl_nvi(_ptr(c), _ptr(v), n, _ptr(dst)))
return _wrap(dst, idx, "NVI", "volume", offset)
def pvi(close: object, volume: object, offset: int = 0, **kwargs) -> object:
"""Positive Volume Index."""
offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c); dst = _out(n)
_check(_lib.qtl_pvi(_ptr(c), _ptr(v), n, _ptr(dst)))
return _wrap(dst, idx, "PVI", "volume", offset)
def tvi(close: object, volume: object, length: int = 14,
offset: int = 0, **kwargs) -> object:
"""Trade Volume Index."""
length = int(length); offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c); dst = _out(n)
_check(_lib.qtl_tvi(_ptr(c), _ptr(v), n, _ptr(dst), length))
return _wrap(dst, idx, f"TVI_{length}", "volume", offset)
def pvd(close: object, volume: object, length: int = 14,
offset: int = 0, **kwargs) -> object:
"""Price Volume Divergence."""
length = int(length); offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c); dst = _out(n)
_check(_lib.qtl_pvd(_ptr(c), _ptr(v), n, _ptr(dst), length))
return _wrap(dst, idx, f"PVD_{length}", "volume", offset)
def vwma(close: object, volume: object, length: int = 20,
offset: int = 0, **kwargs) -> object:
"""Volume Weighted Moving Average."""
length = int(length); offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c); dst = _out(n)
_check(_lib.qtl_vwma(_ptr(c), _ptr(v), n, _ptr(dst), length))
return _wrap(dst, idx, f"VWMA_{length}", "volume", offset)
def evwma(close: object, volume: object, length: int = 20,
offset: int = 0, **kwargs) -> object:
"""Elastic Volume Weighted Moving Average."""
length = int(length); offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c); dst = _out(n)
_check(_lib.qtl_evwma(_ptr(c), _ptr(v), n, _ptr(dst), length))
return _wrap(dst, idx, f"EVWMA_{length}", "volume", offset)
def efi(close: object, volume: object, length: int = 13,
offset: int = 0, **kwargs) -> object:
"""Elder Force Index."""
length = int(length); offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c); dst = _out(n)
_check(_lib.qtl_efi(_ptr(c), _ptr(v), n, _ptr(dst), length))
return _wrap(dst, idx, f"EFI_{length}", "volume", offset)
def aobv(close: object, volume: object, offset: int = 0, **kwargs) -> object:
"""Archer OBV -> (fast, slow) or DataFrame."""
offset = int(offset)
c, idx = _arr(close); v, _ = _arr(volume)
n = len(c); obv_out = _out(n); sig = _out(n)
_check(_lib.qtl_aobv(_ptr(c), _ptr(v), n, _ptr(obv_out), _ptr(sig)))
return _wrap_multi({"AOBV": obv_out, "AOBV_SIG": sig}, idx, "volume", offset)
def mfi(high: object, low: object, close: object, volume: object,
length: int = 14, offset: int = 0, **kwargs) -> object:
"""Money Flow Index."""
length = int(length); offset = int(offset)
h, idx = _arr(high); l, _ = _arr(low); c, _ = _arr(close); v, _ = _arr(volume)
n = len(h); dst = _out(n)
_check(_lib.qtl_mfi(_ptr(h), _ptr(l), _ptr(c), _ptr(v), n, _ptr(dst), length))
return _wrap(dst, idx, f"MFI_{length}", "volume", offset)
def cmf(high: object, low: object, close: object, volume: object,
length: int = 20, offset: int = 0, **kwargs) -> object:
"""Chaikin Money Flow."""
length = int(length); offset = int(offset)
h, idx = _arr(high); l, _ = _arr(low); c, _ = _arr(close); v, _ = _arr(volume)
n = len(h); dst = _out(n)
_check(_lib.qtl_cmf(_ptr(h), _ptr(l), _ptr(c), _ptr(v), n, _ptr(dst), length))
return _wrap(dst, idx, f"CMF_{length}", "volume", offset)
def eom(high: object, low: object, volume: object,
length: int = 14, offset: int = 0, **kwargs) -> object:
"""Ease of Movement."""
length = int(length); offset = int(offset)
h, idx = _arr(high); l, _ = _arr(low); v, _ = _arr(volume)
n = len(h); dst = _out(n)
_check(_lib.qtl_eom(_ptr(h), _ptr(l), _ptr(v), n, _ptr(dst), length, 1e9))
return _wrap(dst, idx, f"EOM_{length}", "volume", offset)
def pvo(volume: object, fast: int = 12, slow: int = 26, signal: int = 9,
offset: int = 0, **kwargs) -> object:
"""Percentage Volume Oscillator -> (pvo, signal, histogram) or DataFrame."""
fast = int(fast); slow = int(slow); signal = int(signal); offset = int(offset)
v, idx = _arr(volume); n = len(v)
pvo_out = _out(n); sig = _out(n); hist = _out(n)
_check(_lib.qtl_pvo(_ptr(v), n, _ptr(pvo_out), _ptr(sig), _ptr(hist), fast, slow, signal))
return _wrap_multi(
{f"PVO_{fast}_{slow}_{signal}": pvo_out, f"PVOs_{fast}_{slow}_{signal}": sig, f"PVOh_{fast}_{slow}_{signal}": hist},
idx, "volume", offset)
'''
# ── statistics.py ──
statistics_additions = '''
def zscore(close: object, length: int = 20, offset: int = 0, **kwargs) -> object:
"""Z-Score."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_zscore(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"ZSCORE_{length}", "statistics", offset)
def cma(close: object, offset: int = 0, **kwargs) -> object:
"""Cumulative Moving Average."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_cma(_ptr(src), n, _ptr(dst)))
return _wrap(dst, idx, "CMA", "statistics", offset)
def entropy(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Shannon Entropy."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_entropy(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"ENTROPY_{length}", "statistics", offset)
def correlation(x: object, y: object, length: int = 20,
offset: int = 0, **kwargs) -> object:
"""Pearson Correlation."""
length = int(length); offset = int(offset)
xarr, idx = _arr(x); yarr, _ = _arr(y)
n = len(xarr); dst = _out(n)
_check(_lib.qtl_correlation(_ptr(xarr), _ptr(yarr), n, _ptr(dst), length))
return _wrap(dst, idx, f"CORR_{length}", "statistics", offset)
def covariance(x: object, y: object, length: int = 20,
is_sample: bool = True, offset: int = 0, **kwargs) -> object:
"""Covariance."""
length = int(length); offset = int(offset)
xarr, idx = _arr(x); yarr, _ = _arr(y)
n = len(xarr); dst = _out(n)
_check(_lib.qtl_covariance(_ptr(xarr), _ptr(yarr), n, _ptr(dst), length, int(is_sample)))
return _wrap(dst, idx, f"COV_{length}", "statistics", offset)
def cointegration(x: object, y: object, length: int = 20,
offset: int = 0, **kwargs) -> object:
"""Cointegration."""
length = int(length); offset = int(offset)
xarr, idx = _arr(x); yarr, _ = _arr(y)
n = len(xarr); dst = _out(n)
_check(_lib.qtl_cointegration(_ptr(xarr), _ptr(yarr), n, _ptr(dst), length))
return _wrap(dst, idx, f"COINT_{length}", "statistics", offset)
'''
# ── errors.py ──
errors_additions = '''
def mse(actual: object, predicted: object, length: int = 20,
offset: int = 0, **kwargs) -> object:
"""Mean Squared Error."""
length = int(length); offset = int(offset)
a, idx = _arr(actual); p, _ = _arr(predicted)
n = len(a); dst = _out(n)
_check(_lib.qtl_mse(_ptr(a), _ptr(p), n, _ptr(dst), length))
return _wrap(dst, idx, f"MSE_{length}", "errors", offset)
def rmse(actual: object, predicted: object, length: int = 20,
offset: int = 0, **kwargs) -> object:
"""Root Mean Squared Error."""
length = int(length); offset = int(offset)
a, idx = _arr(actual); p, _ = _arr(predicted)
n = len(a); dst = _out(n)
_check(_lib.qtl_rmse(_ptr(a), _ptr(p), n, _ptr(dst), length))
return _wrap(dst, idx, f"RMSE_{length}", "errors", offset)
def mae(actual: object, predicted: object, length: int = 20,
offset: int = 0, **kwargs) -> object:
"""Mean Absolute Error."""
length = int(length); offset = int(offset)
a, idx = _arr(actual); p, _ = _arr(predicted)
n = len(a); dst = _out(n)
_check(_lib.qtl_mae(_ptr(a), _ptr(p), n, _ptr(dst), length))
return _wrap(dst, idx, f"MAE_{length}", "errors", offset)
def mape(actual: object, predicted: object, length: int = 20,
offset: int = 0, **kwargs) -> object:
"""Mean Absolute Percentage Error."""
length = int(length); offset = int(offset)
a, idx = _arr(actual); p, _ = _arr(predicted)
n = len(a); dst = _out(n)
_check(_lib.qtl_mape(_ptr(a), _ptr(p), n, _ptr(dst), length))
return _wrap(dst, idx, f"MAPE_{length}", "errors", offset)
'''
# ── filters.py ──
filters_additions = '''
def bessel(close: object, length: int = 14, offset: int = 0, **kwargs) -> object:
"""Bessel Filter."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_bessel(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"BESSEL_{length}", "filters", offset)
def butter2(close: object, length: int = 14, gain: float = 1.0,
offset: int = 0, **kwargs) -> object:
"""2nd-order Butterworth."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_butter2(_ptr(src), n, _ptr(dst), length, float(gain)))
return _wrap(dst, idx, f"BUTTER2_{length}", "filters", offset)
def butter3(close: object, length: int = 14, gain: float = 1.0,
offset: int = 0, **kwargs) -> object:
"""3rd-order Butterworth."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_butter3(_ptr(src), n, _ptr(dst), length, float(gain)))
return _wrap(dst, idx, f"BUTTER3_{length}", "filters", offset)
def cheby1(close: object, length: int = 14, ripple: float = 0.5,
offset: int = 0, **kwargs) -> object:
"""Chebyshev Type I."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_cheby1(_ptr(src), n, _ptr(dst), length, float(ripple)))
return _wrap(dst, idx, f"CHEBY1_{length}", "filters", offset)
def cheby2(close: object, length: int = 14, ripple: float = 0.5,
offset: int = 0, **kwargs) -> object:
"""Chebyshev Type II."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_cheby2(_ptr(src), n, _ptr(dst), length, float(ripple)))
return _wrap(dst, idx, f"CHEBY2_{length}", "filters", offset)
def elliptic(close: object, length: int = 14, offset: int = 0, **kwargs) -> object:
"""Elliptic (Cauer) Filter."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_elliptic(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"ELLIPTIC_{length}", "filters", offset)
def edcf(close: object, length: int = 14, offset: int = 0, **kwargs) -> object:
"""Ehlers Distance Coefficient Filter."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_edcf(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"EDCF_{length}", "filters", offset)
def bpf(close: object, length: int = 14, bandwidth: int = 5,
offset: int = 0, **kwargs) -> object:
"""Bandpass Filter."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_bpf(_ptr(src), n, _ptr(dst), length, int(bandwidth)))
return _wrap(dst, idx, f"BPF_{length}", "filters", offset)
def alaguerre(close: object, length: int = 20, order: int = 5,
offset: int = 0, **kwargs) -> object:
"""Adaptive Laguerre Filter."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_alaguerre(_ptr(src), n, _ptr(dst), length, int(order)))
return _wrap(dst, idx, f"ALAGUERRE_{length}", "filters", offset)
def bilateral(close: object, length: int = 14, sigma_s: float = 0.5,
sigma_r: float = 1.0, offset: int = 0, **kwargs) -> object:
"""Bilateral Filter."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_bilateral(_ptr(src), n, _ptr(dst), length, float(sigma_s), float(sigma_r)))
return _wrap(dst, idx, f"BILATERAL_{length}", "filters", offset)
def baxterking(close: object, length: int = 12, min_period: int = 6,
max_period: int = 32, offset: int = 0, **kwargs) -> object:
"""Baxter-King Filter."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_baxterking(_ptr(src), n, _ptr(dst), length, int(min_period), int(max_period)))
return _wrap(dst, idx, f"BAXTERKING_{length}", "filters", offset)
def cfitz(close: object, length: int = 6, bw_period: int = 32,
offset: int = 0, **kwargs) -> object:
"""Christiano-Fitzgerald Filter."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_cfitz(_ptr(src), n, _ptr(dst), length, int(bw_period)))
return _wrap(dst, idx, f"CFITZ_{length}", "filters", offset)
'''
# ── cycles.py ──
cycles_additions = '''
def cg(close: object, length: int = 10, offset: int = 0, **kwargs) -> object:
"""Center of Gravity."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_cg(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"CG_{length}", "cycles", offset)
def dsp(close: object, length: int = 20, offset: int = 0, **kwargs) -> object:
"""Dominant Cycle Period (DSP)."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_dsp(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"DSP_{length}", "cycles", offset)
def ccor(close: object, length: int = 20, alpha: float = 0.07,
offset: int = 0, **kwargs) -> object:
"""Circular Correlation."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_ccor(_ptr(src), n, _ptr(dst), length, float(alpha)))
return _wrap(dst, idx, f"CCOR_{length}", "cycles", offset)
def ebsw(close: object, hp_length: int = 40, ssf_length: int = 10,
offset: int = 0, **kwargs) -> object:
"""Even Better Sinewave."""
hp_length = int(hp_length); ssf_length = int(ssf_length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_ebsw(_ptr(src), n, _ptr(dst), hp_length, ssf_length))
return _wrap(dst, idx, f"EBSW_{hp_length}", "cycles", offset)
def eacp(close: object, min_period: int = 8, max_period: int = 48,
avg_length: int = 3, enhance: int = 1,
offset: int = 0, **kwargs) -> object:
"""Ehlers Autocorrelation Periodogram."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_eacp(_ptr(src), n, _ptr(dst), int(min_period), int(max_period), int(avg_length), int(enhance)))
return _wrap(dst, idx, f"EACP_{min_period}_{max_period}", "cycles", offset)
'''
# ── numerics.py ──
numerics_additions = '''
def change(close: object, length: int = 1, offset: int = 0, **kwargs) -> object:
"""Price Change."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_change(_ptr(src), n, _ptr(dst), length))
return _wrap(dst, idx, f"CHANGE_{length}", "numerics", offset)
def exptrans(close: object, offset: int = 0, **kwargs) -> object:
"""Exponential Transform."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_exptrans(_ptr(src), n, _ptr(dst)))
return _wrap(dst, idx, "EXPTRANS", "numerics", offset)
def betadist(close: object, length: int = 50, alpha: float = 2.0,
beta: float = 2.0, offset: int = 0, **kwargs) -> object:
"""Beta Distribution."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_betadist(_ptr(src), n, _ptr(dst), length, float(alpha), float(beta)))
return _wrap(dst, idx, f"BETADIST_{length}", "numerics", offset)
def expdist(close: object, length: int = 50, lam: float = 3.0,
offset: int = 0, **kwargs) -> object:
"""Exponential Distribution."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_expdist(_ptr(src), n, _ptr(dst), length, float(lam)))
return _wrap(dst, idx, f"EXPDIST_{length}", "numerics", offset)
def binomdist(close: object, length: int = 50, trials: int = 20,
threshold: int = 10, offset: int = 0, **kwargs) -> object:
"""Binomial Distribution."""
length = int(length); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_binomdist(_ptr(src), n, _ptr(dst), length, int(trials), int(threshold)))
return _wrap(dst, idx, f"BINOMDIST_{length}", "numerics", offset)
def cwt(close: object, scale: float = 10.0, omega: float = 6.0,
offset: int = 0, **kwargs) -> object:
"""Continuous Wavelet Transform."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_cwt(_ptr(src), n, _ptr(dst), float(scale), float(omega)))
return _wrap(dst, idx, "CWT", "numerics", offset)
def dwt(close: object, length: int = 4, levels: int = 0,
offset: int = 0, **kwargs) -> object:
"""Discrete Wavelet Transform."""
length = int(length); levels = int(levels); offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
_check(_lib.qtl_dwt(_ptr(src), n, _ptr(dst), length, levels))
return _wrap(dst, idx, f"DWT_{length}", "numerics", offset)
'''
# Now apply all additions
additions = {
"core.py": (core_additions, ["avgprice", "medprice", "typprice", "midbody"]),
"momentum.py": (momentum_additions, ["rsi", "roc", "mom", "cmo", "tsi", "apo", "bias", "cfo", "cfb", "asi"]),
"oscillators.py": (oscillators_additions, ["fisher", "fisher04", "dpo", "trix", "inertia", "rsx", "er", "cti", "reflex", "trendflex", "kri", "psl", "deco", "dosc", "dymoi", "crsi", "bbb", "bbi", "dem", "brar"]),
"trends_fir.py": (trends_fir_additions, ["sma", "wma", "hma", "trima", "swma", "dwma", "blma", "alma", "lsma", "sgma", "sinema", "hanma", "parzen", "tsf", "conv", "bwma", "crma", "sp15", "tukey_w", "rain", "afirma"]),
"trends_iir.py": (trends_iir_additions, ["ema", "ema_alpha", "dema", "dema_alpha", "tema", "lema", "hema", "ahrens", "decycler", "dsma", "gdema", "coral", "agc", "ccyc"]),
"channels.py": (channels_additions, ["bbands", "aberr", "atrbands", "apchannel"]),
"volatility.py": (volatility_additions, ["tr", "bbw", "bbwn", "bbwp", "stddev", "variance", "etherm", "ccv", "cv", "cvi", "ewma"]),
"volume.py": (volume_additions, ["obv", "pvt", "pvr", "vf", "nvi", "pvi", "tvi", "pvd", "vwma", "evwma", "efi", "aobv", "mfi", "cmf", "eom", "pvo"]),
"statistics.py": (statistics_additions, ["zscore", "cma", "entropy", "correlation", "covariance", "cointegration"]),
"errors.py": (errors_additions, ["mse", "rmse", "mae", "mape"]),
"filters.py": (filters_additions, ["bessel", "butter2", "butter3", "cheby1", "cheby2", "elliptic", "edcf", "bpf", "alaguerre", "bilateral", "baxterking", "cfitz"]),
"cycles.py": (cycles_additions, ["cg", "dsp", "ccor", "ebsw", "eacp"]),
"numerics.py": (numerics_additions, ["change", "exptrans", "betadist", "expdist", "binomdist", "cwt", "dwt"]),
}
total_added = 0
for filename, (code, funcnames) in additions.items():
filepath = os.path.join(PKG, filename)
content = read(filepath)
# Check which functions are already defined
missing = [f for f in funcnames if f"\ndef {f}(" not in content]
if not missing:
print(f" {filename}: all {len(funcnames)} functions already present")
continue
# Update __all__ to include new functions
# Find __all__ closing bracket
import re
all_match = re.search(r'__all__\s*=\s*\[([^\]]*)\]', content, re.DOTALL)
if all_match:
existing_all = all_match.group(1)
existing_names = [s.strip().strip('"').strip("'") for s in existing_all.split(",") if s.strip().strip('"').strip("'")]
new_names = [f for f in funcnames if f not in existing_names]
if new_names:
all_entries = existing_names + new_names
new_all = "__all__ = [\n" + "".join(f' "{n}",\n' for n in all_entries) + "]"
content = content[:all_match.start()] + new_all + content[all_match.end():]
# Append the wrapper code
content += "\n" + code.strip() + "\n"
write(filepath, content)
total_added += len(missing)
print(f" {filename}: added {len(missing)} functions: {', '.join(missing)}")
print(f"\n Total wrappers added: {total_added}")
# ═══════════════════════════════════════════════════════════════════════════
# Step 3: Rewrite indicators.py as thin re-export
# ═══════════════════════════════════════════════════════════════════════════
print("\nStep 3: Rewriting indicators.py as re-export module ...")
CATEGORY_MODULES = [
"channels", "core", "cycles", "dynamics", "errors", "filters",
"momentum", "numerics", "oscillators", "reversals", "statistics",
"trends_fir", "trends_iir", "volatility", "volume",
]
indicators_content = '''"""High-level indicator wrappers for quantalib.
This module re-exports all indicator functions from per-category submodules.
Each function accepts numpy arrays (or pandas Series / DataFrame) and
returns the same type.
Category submodules:
quantalib.channels — Bollinger Bands, Keltner, Donchian, etc.
quantalib.core — Price transforms (avgprice, medprice, etc.)
quantalib.cycles — Hilbert, Sinewave, CG, DSP, etc.
quantalib.dynamics — ADX, Ichimoku, Supertrend, etc.
quantalib.errors — MSE, RMSE, MAE, MAPE, Huber, etc.
quantalib.filters — Butterworth, Chebyshev, Kalman, etc.
quantalib.momentum — RSI, MACD, ROC, MOM, etc.
quantalib.numerics — FFT, sigmoid, slope, distributions, etc.
quantalib.oscillators — Stochastic, Fisher, Williams %R, etc.
quantalib.reversals — Pivot points, PSAR, fractals, etc.
quantalib.statistics — Z-score, correlation, linreg, etc.
quantalib.trends_fir — SMA, WMA, HMA, ALMA, etc.
quantalib.trends_iir — EMA, DEMA, TEMA, JMA, KAMA, etc.
quantalib.volatility — ATR, TR, Bollinger Width, etc.
quantalib.volume — OBV, VWAP, MFI, CMF, etc.
"""
from __future__ import annotations
'''
for mod in CATEGORY_MODULES:
indicators_content += f"from .{mod} import * # noqa: F401, F403\n"
write(os.path.join(PKG, "indicators.py"), indicators_content)
# ═══════════════════════════════════════════════════════════════════════════
# Step 4: Update __init__.py
# ═══════════════════════════════════════════════════════════════════════════
print("\nStep 4: Updating __init__.py ...")
init_content = '''"""quantalib — Python wrapper for QuanTAlib NativeAOT exports.
Usage::
import quantalib as qtl
result = qtl.sma(close_array, length=14)
result = qtl.bbands(close_array, length=20, std=2.0)
"""
from __future__ import annotations
from pathlib import Path
from ._loader import load_native_library
from . import indicators
from .indicators import * # noqa: F401, F403 — re-export all indicator functions
# Re-export per-category submodules for direct access
from . import ( # noqa: F401
channels,
core,
cycles,
dynamics,
errors,
filters,
momentum,
numerics,
oscillators,
reversals,
statistics,
trends_fir,
trends_iir,
volatility,
volume,
)
from ._compat import ALIASES, get_compat
from ._bridge import (
QtlError,
QtlNullPointerError,
QtlInvalidLengthError,
QtlInvalidParamError,
QtlInternalError,
)
__all__ = [
"load_native_library",
"indicators",
"channels",
"core",
"cycles",
"dynamics",
"errors",
"filters",
"momentum",
"numerics",
"oscillators",
"reversals",
"statistics",
"trends_fir",
"trends_iir",
"volatility",
"volume",
"ALIASES",
"get_compat",
"QtlError",
"QtlNullPointerError",
"QtlInvalidLengthError",
"QtlInvalidParamError",
"QtlInternalError",
]
def _resolve_version() -> str:
version_file = Path(__file__).resolve().parents[2] / "lib" / "VERSION"
if version_file.exists():
version = version_file.read_text(encoding="utf-8").strip()
if version:
return version
return "0.0.0"
__version__ = _resolve_version()
'''
write(os.path.join(PKG, "__init__.py"), init_content)
# ═══════════════════════════════════════════════════════════════════════════
# Step 5: Count & verify
# ═══════════════════════════════════════════════════════════════════════════
print("\nStep 5: Verification counts ...")
import re as re2
total_funcs = 0
for mod in CATEGORY_MODULES:
filepath = os.path.join(PKG, f"{mod}.py")
content = read(filepath)
funcs = re2.findall(r'^def (\w+)\(', content, re2.MULTILINE)
# Exclude private helpers
public = [f for f in funcs if not f.startswith('_')]
total_funcs += len(public)
print(f" {mod:15s}: {len(public):3d} functions")
print(f" {'TOTAL':15s}: {total_funcs:3d} functions")
print("\nDone!")