# -*- coding: utf-8 -*- """P3.1e REGIME DIAGNOSTIC — SniperGold_ML. Diagnostic ONLY. Answers the fifth research question: "Apakah relationship feature -> label berubah berdasarkan market state?" Regime proxies use EXISTING semantics only (no new regime model): * ATR percentile : rolling percentile rank of ATR over trailing 500 bars * Realized vol : std of 20-bar log returns (trailing) * Trend proxy : |f16 mom20_atr| magnitude (already a contract feature) * Range proxy : f17 range_atr (already a contract feature) Analysis: * P(Y=1) / P(Y=-1) per regime tercile (low/med/high) * feature x regime: univariate AUC of key features within each tercile -> does the feature->label relationship change across regimes? * regime x SMC state: P(Y=1) for selected combos No MS-GARCH / HMM / regime gate is built. """ import os import sys import json import numpy as np HERE = os.path.dirname(os.path.abspath(__file__)) if HERE not in sys.path: sys.path.insert(0, HERE) import p3_common as P3 import train_model as TM KEY_FEATS = [0, 5, 7, 9, 16, 18] def terciles(x): q33, q66 = np.quantile(x, [1 / 3, 2 / 3]) lo = x <= q33 hi = x >= q66 md = ~lo & ~hi return lo, md, hi def main(): t, o, h, l, c, v, htf = P3.load_data() F = P3.load_F() A = P3.atr_series(h, l, c) n = len(c) lab = P3.make_label(c, A) labeled = lab != 0 midx = np.where(labeled)[0] y = lab[midx] ypos = (y == 1).astype(int) # ---- regime proxies ---- atr_pct = np.zeros(n) for i in range(500, n): atr_pct[i] = (A[i - 500:i] <= A[i]).mean() atr_pct[:500] = 0.5 lr = np.zeros(n) lr[1:] = np.log(c[1:] / np.maximum(c[:-1], 1e-12)) rv = np.zeros(n) for i in range(20, n): rv[i] = lr[i - 19:i + 1].std(ddof=1) rv[:20] = np.nan rv = np.nan_to_num(rv, nan=np.nanmedian(rv[20:])) proxies = { "ATR_percentile": atr_pct, "RealizedVol20": rv, "TrendAbs_mom20(f16)": np.abs(F[:, 16]), "RangeATR(f17)": F[:, 17], } report = {"provenance": P3.provenance(), "dataset_hash": P3.dataset_hash(F, lab, A, t), "regimes": {}} print("=== REGIME DIAGNOSTIC (tercile low/med/high) ===") for pname, px in proxies.items(): lo, md, hi = terciles(px) r = {} print(f"\n[{pname}]") for rname, m in (("low", lo), ("med", md), ("high", hi)): ml = m[midx] if ml.sum() < 100: continue p1 = float(ypos[ml].mean()) pm1 = float((y[ml] == -1).mean()) r[rname] = {"n": int(ml.sum()), "P1": p1, "Pm1": pm1, "lift_P1": float(p1 / ypos.mean())} print(f" {rname:>4}: n={int(ml.sum()):>6} P(+1)={p1:.4f} " f"P(-1)={pm1:.4f} lift={p1 / ypos.mean():.3f}") for j in KEY_FEATS: xs = F[midx, j][ml] if len(np.unique(xs)) < 3: continue a = TM.auc(ypos[ml].astype(int), xs) r[rname]["auc_" + P3.FEAT_NAMES[j]] = float(a) print(" univariate AUC by regime (long label):") for fname in [P3.FEAT_NAMES[j] for j in KEY_FEATS]: vals = [] for rname in ("low", "med", "high"): vv = r.get(rname, {}).get("auc_" + fname) vals.append(vv if vv is not None else float("nan")) print(" %-16s low=%.4f med=%.4f high=%.4f" % (fname, vals[0], vals[1], vals[2])) report["regimes"][pname] = r # ---- regime x SMC state combos ---- print("\n=== REGIME x SMC STATE: P(Y=1) selected combos ===") atr_lo, atr_md, atr_hi = terciles(atr_pct) f9 = F[:, 9] f7 = F[:, 7] f18 = F[:, 18] combos = { "BUY_cand & ATR_low": (f9 == 1) & (f7 > 0) & atr_lo, "BUY_cand & ATR_high": (f9 == 1) & (f7 > 0) & atr_hi, "SELL_cand & ATR_low": (f9 == 1) & (f7 < 0) & atr_lo, "SELL_cand & ATR_high": (f9 == 1) & (f7 < 0) & atr_hi, "conf>=60 & ATR_low": (f18 >= 60) & atr_lo, "conf>=60 & ATR_high": (f18 >= 60) & atr_hi, "MTF_bull & ATR_low": (F[:, 0] > 0) & (F[:, 1] > 0) & (F[:, 2] > 0) & atr_lo, "MTF_bull & ATR_high": (F[:, 0] > 0) & (F[:, 1] > 0) & (F[:, 2] > 0) & atr_hi, } comb = {} for cname, mask in combos.items(): ml = mask[midx] if ml.sum() < 100: comb[cname] = {"n": int(ml.sum()), "note": "too few"} print(f" {cname:<24} n={int(ml.sum())} (too few)") continue p1 = float(ypos[ml].mean()) pm1 = float((y[ml] == -1).mean()) comb[cname] = {"n": int(ml.sum()), "P1": p1, "Pm1": pm1, "lift_P1": float(p1 / ypos.mean())} print(f" {cname:<24} n={int(ml.sum()):>6} P(+1)={p1:.4f} " f"P(-1)={pm1:.4f} lift={p1 / ypos.mean():.3f}") report["regime_x_smc"] = comb P3.save_json("regime_diagnostic.json", report) print("\nRegime diagnostic selesai. Output: ml/p3/output/regime_diagnostic.json") if __name__ == "__main__": main()