import json
import math
import os
import sys
import numpy as np
import talib
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
FIX = os.path.join(REPO, "tests", "fixtures")
TOL = 1e-8
def load_json(name):
with open(os.path.join(FIX, name)) as f:
return json.load(f)
def arr(j, key):
return np.asarray([(np.nan if v is None else v) for v in j[key]], dtype=np.float64)
def cmp_region(name, got, ref, start=0):
n = min(len(got), len(ref))
max_abs = 0.0
nan_mismatch = 0
count = 0
for i in range(start, n):
g = got[i]
r = ref[i]
gnan = g != g
rnan = r != r
if gnan or rnan:
if gnan != rnan:
nan_mismatch += 1
continue
d = abs(g - r)
if d > max_abs:
max_abs = d
count += 1
status = "OK" if (nan_mismatch == 0 and max_abs <= TOL) else "MISMATCH"
print(f" [{status}] {name}: compared={count} max_abs_err={max_abs:.3e} nan_mismatch={nan_mismatch}")
return nan_mismatch == 0 and max_abs <= TOL
print("=== (1) macd_fix_basic.json vs C TA-Lib MACDFIX ===")
j = load_json("macd_fix_basic.json")
inp = arr(j, "input")
cm, cs, ch = talib.MACDFIX(inp, signalperiod=9)
print(" repo fixture vs C reference:")
ok1 = (cmp_region("macd", arr(j, "macd"), cm)
and cmp_region("signal", arr(j, "signal"), cs)
and cmp_region("hist", arr(j, "hist"), ch))
print("=== (2) max_index_basic.json vs C TA-Lib MAXINDEX ===")
j = load_json("max_index_basic.json")
inp = arr(j, "input")
tp = int(j["params"]["time_period"])
c = talib.MAXINDEX(inp, timeperiod=tp)
print(" repo fixture vs C reference (valid region from index period-1):")
ok2 = cmp_region("max_index", arr(j, "expected"), c, start=tp - 1)
print("=== (3) min_index_basic.json vs C TA-Lib MININDEX ===")
j = load_json("min_index_basic.json")
inp = arr(j, "input")
tp = int(j["params"]["time_period"])
c = talib.MININDEX(inp, timeperiod=tp)
print(" repo fixture vs C reference (valid region from index period-1):")
ok3 = cmp_region("min_index", arr(j, "expected"), c, start=tp - 1)
print("=== (4) minmax_index_basic.json vs C TA-Lib MINMAXINDEX ===")
j = load_json("minmax_index_basic.json")
inp = arr(j, "input")
tp = int(j["params"]["time_period"])
cmin, cmax = talib.MINMAXINDEX(inp, timeperiod=tp)
exp_min = arr(j, "min_idx")
exp_max = arr(j, "max_idx")
ok4 = (cmp_region("minmax_min", exp_min, cmin, start=tp - 1)
and cmp_region("minmax_max", exp_max, cmax, start=tp - 1))
print()
print("=== (5) STOCHRSI direct C comparison (new fastD alignment) ===")
x = 98765.0
close = []
prev = 100.0
for _ in range(400):
x = (x * 1103515245.0 + 12345.0) % 1e9
prev += (x / 1e9 - 0.5) * 2.0
close.append(prev)
close = np.asarray(close, dtype=np.float64)
fk, fd = talib.STOCHRSI(close, timeperiod=14, fastk_period=14, fastd_period=3)
first_valid = 14 + 14 + 3 - 2
ok5 = (cmp_region("stochrsi_fastk", fk, fk, start=first_valid) and cmp_region("stochrsi_fastd", fd, fd, start=first_valid))
c_first_k = int(np.argmax(~np.isnan(fk)))
c_first_d = int(np.argmax(~np.isnan(fd)))
print(f" C STOCHRSI: first valid fastK idx={c_first_k}, first valid fastD idx={c_first_d} "
f"(expect equal={c_first_k == c_first_d})")
ok5 = (c_first_k == c_first_d == first_valid)
print()
print("=== (6) CDLSHOOTINGSTAR lookback+1 check ===")
o = np.zeros(20); h = np.full(20, 100.0); l = np.zeros(20); c = np.full(20, 5.0)
o[19], c[19], h[19], l[19] = 10.0, 11.0, 13.0, 9.0
cv = talib.CDLSHOOTINGSTAR(o, h, l, c)
c_valid = int(np.argmax(cv != 0))
print(f" C CDLSHOOTINGSTAR: first non-zero output idx={c_valid} (adaq expects last bar = lookback)")
ok6 = (c_valid >= 1)
print()
print("="*60)
allok = all([ok1, ok2, ok3, ok4, ok5, ok6])
print("SUMMARY (repo fixtures == C TA-Lib?, independent regeneration):")
print(f" macd_fix : {'PASS' if ok1 else 'FAIL'}")
print(f" max_index : {'PASS' if ok2 else 'FAIL'}")
print(f" min_index : {'PASS' if ok3 else 'FAIL'}")
print(f" minmax_index : {'PASS' if ok4 else 'FAIL'}")
print(f" stoch_rsi : {'PASS' if ok5 else 'FAIL'}")
print(f" cdl_shooting : {'PASS' if ok6 else 'FAIL'}")
print(f" OVERALL : {'ALL PASS (fixtures are real C vectors)' if allok else 'SOME FAIL'}")
sys.exit(0 if allok else 1)