"""Demo / validation of the MARGINAL-vs-TP01 scorer (2026-06-20). Shows the lesson of the 104-hypothesis sweep operationalized: strategies that scored an absolute PASS but are just TP01/TSMOM with an overlay collapse to REDUNDANT/NEUTRAL/ DILUTES under marginal scoring, while the one genuine diversifier (STA05 long-short) earns ADDS. Run: uv run python scripts/research/alt/marginal_demo.py """ import sys sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt") import numpy as np import pandas as pd import altlib as al from src.strategies.trend_portfolio import CANONICAL, TrendPortfolio def tsmom_dir(df): """Long-flat multi-horizon TSMOM direction in {0,1} (the bare TP01 trend signal).""" c = df["close"].values.astype(float) bpd = al.bars_per_day(df) d = np.zeros(len(c)) for h in (30 * bpd, 90 * bpd, 180 * bpd): s = np.full(len(c), np.nan) s[h:] = np.sign(c[h:] / c[:-h] - 1.0) d += np.nan_to_num(s) return np.clip(np.sign(d), 0, None) def tp01_target(df): return TrendPortfolio(**CANONICAL).target_series(df) FAST, SLOW = [5, 10, 20, 40], [40, 80, 120, 200] PAIRS = [(f, s) for f in FAST for s in SLOW if f < s] def sta05(df, long_only): c = df["close"].values.astype(float) v = np.zeros(len(c)) for f, s in PAIRS: v += np.sign(al.ema(c, f) - al.ema(c, s)) d = v / len(PAIRS) if long_only: d = np.clip(d, 0.0, 1.0) return al.vol_target(d, df, 0.20, 30, 2.0) def vol03(df, asset): """DVOL-gated TSMOM (active only when DVOL below its expanding median).""" d = tsmom_dir(df) dv = pd.Series(al.dvol(df, asset)) thr = dv.expanding(min_periods=30).quantile(0.5) gate = dv.isna() | thr.isna() | (dv < thr) d = np.where(gate.values, d, 0.0) return al.vol_target(d, df, 0.20, 30, 2.0) def cmb04(df): """Momentum + low-vol filter (TSMOM taken only when realized vol below expanding median).""" d = tsmom_dir(df) bpd = al.bars_per_day(df) rv = al.realized_vol(al.simple_returns(df["close"].values.astype(float)), 30 * bpd, bpd * 365.25) med = pd.Series(rv).expanding(min_periods=60).median().values d = np.where((rv < med) | np.isnan(med), d, 0.0) return al.vol_target(d, df, 0.20, 30, 2.0) CANDIDATES = [ ("TP01-itself (sanity)", tp01_target), ("STA05 long-short (the lead)", lambda df: sta05(df, False)), ("STA05 long-only", lambda df: sta05(df, True)), ("VOL03 DVOL-gated TSMOM (overlay)", vol03), ("CMB04 momentum+low-vol (overlay)", cmb04), ] print("=" * 78) print("MARGINAL SCORING vs TP01 baseline — absolute Sharpe is NOT enough for a slot") print("=" * 78) rows = [] for name, fn in CANDIDATES: rep = al.study_marginal(name, fn, tf="1d") print() print(al.fmt_marginal(rep)) rows.append((name, rep["abs_grade"], rep["marginal_verdict"], rep["earns_slot"])) print("\n" + "=" * 78) print(f"{'candidate':<36s} {'absolute':>9s} {'marginal':>10s} {'earns_slot':>11s}") for n, ag, mv, es in rows: print(f"{n:<36s} {ag:>9s} {mv:>10s} {str(es):>11s}") # sanity asserts: baseline reproduces, and an overlay-on-TSMOM does NOT earn a slot sanity = al.marginal_vs_tp01(al.candidate_daily(tp01_target)) assert sanity["corr_full"] > 0.95, f"TP01-vs-itself corr should be ~1, got {sanity['corr_full']}" assert abs(sanity["blends"]["w25"]["uplift_full"]) < 0.05, "TP01-vs-itself uplift should be ~0" print("\nSANITY OK: TP01-vs-itself corr", sanity["corr_full"], "uplift_full", sanity["blends"]["w25"]["uplift_full"], "->", sanity["marginal_verdict"])