from __future__ import annotations
import json
import math
from pathlib import Path
import numpy as np
import talib
N = 400
SEED = 20260603
OUT_PATH = Path(__file__).resolve().parents[1] / "tests/fixtures/talib_parity.json"
def build_series() -> dict[str, np.ndarray]:
rng = np.random.default_rng(SEED)
rets = rng.normal(0.0005, 0.012, N)
close = 100.0 * np.exp(np.cumsum(rets))
spread = np.abs(rng.normal(0.0, 0.006, N)) * close
open_ = close * (1.0 + rng.normal(0.0, 0.004, N))
high = np.maximum(open_, close) + spread
low = np.minimum(open_, close) - spread
volume = rng.uniform(1.0e5, 5.0e5, N)
market_rets = 0.7 * rets + 0.3 * rng.normal(0.0005, 0.012, N)
close2 = 100.0 * np.exp(np.cumsum(market_rets))
return {
"open": open_,
"high": high,
"low": low,
"close": close,
"volume": volume,
"close2": close2,
}
def clean(arr: np.ndarray) -> list:
out: list = []
for value in arr:
if value is None or (isinstance(value, float) and not math.isfinite(value)):
out.append(None)
elif isinstance(value, (np.floating, float)):
out.append(float(value))
else:
out.append(int(value))
return out
def main() -> None:
s = build_series()
o, h, low, c, v, c2 = s["open"], s["high"], s["low"], s["close"], s["volume"], s["close2"]
expected: dict[str, list] = {}
expected["ht_dcperiod"] = clean(talib.HT_DCPERIOD(c))
expected["ht_dcphase"] = clean(talib.HT_DCPHASE(c))
inphase, quadrature = talib.HT_PHASOR(c)
expected["ht_phasor_inphase"] = clean(inphase)
expected["ht_phasor_quadrature"] = clean(quadrature)
sine, leadsine = talib.HT_SINE(c)
expected["ht_sine"] = clean(sine)
expected["ht_leadsine"] = clean(leadsine)
expected["ht_trendmode"] = clean(talib.HT_TRENDMODE(c))
expected["ht_trendline"] = clean(talib.HT_TRENDLINE(c))
mama, fama = talib.MAMA(c, fastlimit=0.5, slowlimit=0.05)
expected["mama_0p5_0p05"] = clean(mama)
expected["fama_0p5_0p05"] = clean(fama)
expected["sarext_default"] = clean(
talib.SAREXT(
h,
low,
startvalue=0.0,
offsetonreverse=0.0,
accelerationinitlong=0.02,
accelerationlong=0.02,
accelerationmaxlong=0.2,
accelerationinitshort=0.02,
accelerationshort=0.02,
accelerationmaxshort=0.2,
)
)
macd, macdsignal, macdhist = talib.MACDEXT(
c, fastperiod=12, fastmatype=0, slowperiod=26, slowmatype=0, signalperiod=9, signalmatype=0
)
expected["macdext_12_26_9"] = clean(macd)
expected["macdext_signal_12_26_9"] = clean(macdsignal)
expected["macdext_hist_12_26_9"] = clean(macdhist)
expected["beta_5"] = clean(talib.BETA(c, c2, timeperiod=5))
expected["correl_30"] = clean(talib.CORREL(c, c2, timeperiod=30))
periods = np.full(N, 14.0)
periods[N // 2 :] = 30.0
expected["mavp_14_30"] = clean(talib.MAVP(c, periods, minperiod=2, maxperiod=30, matype=0))
cdl_funcs = sorted(talib.get_function_groups()["Pattern Recognition"])
for name in cdl_funcs:
fn = getattr(talib, name)
expected[name.lower()] = clean(fn(o, h, low, c))
expected["ema_14"] = clean(talib.EMA(c, timeperiod=14))
expected["dema_14"] = clean(talib.DEMA(c, timeperiod=14))
expected["tema_14"] = clean(talib.TEMA(c, timeperiod=14))
expected["t3_5_0p7"] = clean(talib.T3(c, timeperiod=5, vfactor=0.7))
expected["kama_30"] = clean(talib.KAMA(c, timeperiod=30))
expected["trix_15"] = clean(talib.TRIX(c, timeperiod=15))
expected["rsi_14"] = clean(talib.RSI(c, timeperiod=14))
expected["cmo_14"] = clean(talib.CMO(c, timeperiod=14))
expected["apo_12_26"] = clean(talib.APO(c, fastperiod=12, slowperiod=26, matype=1))
expected["ppo_12_26"] = clean(talib.PPO(c, fastperiod=12, slowperiod=26, matype=1))
expected["ultosc_7_14_28"] = clean(talib.ULTOSC(h, low, c, timeperiod1=7, timeperiod2=14, timeperiod3=28))
expected["bop"] = clean(talib.BOP(o, h, low, c))
macd_m, macd_s, macd_h = talib.MACD(c, fastperiod=12, slowperiod=26, signalperiod=9)
expected["macd_12_26_9"] = clean(macd_m)
expected["macd_signal_12_26_9"] = clean(macd_s)
expected["macd_hist_12_26_9"] = clean(macd_h)
srsi_k, srsi_d = talib.STOCHRSI(c, timeperiod=14, fastk_period=5, fastd_period=3, fastd_matype=0)
expected["stochrsi_k_14_5_3"] = clean(srsi_k)
expected["stochrsi_d_14_5_3"] = clean(srsi_d)
expected["adx_14"] = clean(talib.ADX(h, low, c, timeperiod=14))
expected["adxr_14"] = clean(talib.ADXR(h, low, c, timeperiod=14))
expected["plus_di_14"] = clean(talib.PLUS_DI(h, low, c, timeperiod=14))
expected["minus_di_14"] = clean(talib.MINUS_DI(h, low, c, timeperiod=14))
expected["dx_14"] = clean(talib.DX(h, low, c, timeperiod=14))
expected["plus_dm_1"] = clean(talib.PLUS_DM(h, low, timeperiod=1))
expected["minus_dm_1"] = clean(talib.MINUS_DM(h, low, timeperiod=1))
bb_u, bb_m, bb_l = talib.BBANDS(c, timeperiod=20, nbdevup=2.0, nbdevdn=2.0, matype=0)
expected["bb_upper_20_2"] = clean(bb_u)
expected["bb_middle_20_2"] = clean(bb_m)
expected["bb_lower_20_2"] = clean(bb_l)
expected["linearreg_14"] = clean(talib.LINEARREG(c, timeperiod=14))
expected["linearreg_slope_14"] = clean(talib.LINEARREG_SLOPE(c, timeperiod=14))
expected["linearreg_angle_14"] = clean(talib.LINEARREG_ANGLE(c, timeperiod=14))
expected["linearreg_intercept_14"] = clean(talib.LINEARREG_INTERCEPT(c, timeperiod=14))
expected["tsf_14"] = clean(talib.TSF(c, timeperiod=14))
aroon_down, aroon_up = talib.AROON(h, low, timeperiod=14)
expected["aroon_up_14"] = clean(aroon_up)
expected["aroon_down_14"] = clean(aroon_down)
expected["aroon_osc_14"] = clean(talib.AROONOSC(h, low, timeperiod=14))
expected["ad"] = clean(talib.AD(h, low, c, v))
expected["adosc_3_10"] = clean(talib.ADOSC(h, low, c, v, fastperiod=3, slowperiod=10))
expected["sar_0p02_0p2"] = clean(talib.SAR(h, low, acceleration=0.02, maximum=0.2))
expected["sum_10"] = clean(talib.SUM(c, timeperiod=10))
expected["avgprice"] = clean(talib.AVGPRICE(o, h, low, c))
expected["medprice"] = clean(talib.MEDPRICE(h, low))
expected["typprice"] = clean(talib.TYPPRICE(h, low, c))
expected["wclprice"] = clean(talib.WCLPRICE(h, low, c))
fixture = {
"meta": {
"seed": SEED,
"n": N,
"talib_version": talib.__version__,
"description": "Deterministic TA-Lib parity reference for ta-indicators.",
},
"input": {key: clean(value) for key, value in s.items()},
"expected": expected,
}
OUT_PATH.parent.mkdir(parents=True, exist_ok=True)
OUT_PATH.write_text(json.dumps(fixture, indent=1))
print(f"wrote {OUT_PATH} ({OUT_PATH.stat().st_size / 1024:.0f} KiB)")
print(f"functions: {len(expected)} (incl. {len(cdl_funcs)} CDL patterns)")
if __name__ == "__main__":
main()