"""verify_survivors — adversarial 'Verify' phase for the xsec sweep (2026-06-20). The Find phase flagged 42/257 cross-sectional configs as earns_slot=True on the certified Hyperliquid panel. ALL the slot-earners share two tells: (a) strongly NEGATIVE corr to TP01 (-0.2..-0.4), (b) PnL concentrated in 2025. Hypothesis under test (the only thing that matters before promoting any of them to a sleeve): "These are not N independent edges. They are ONE regime bet — short the high-beta alt junk during the 2024-26 alt-bear — wearing many masks (low-vol, low-beta, low-corr, reversal, trend-gated-mom). The drop-one-month jackknife is robust only WITHIN that single regime." Three skeptics, deterministic (no agents): S1 (distinctness/redundancy): mutual correlation matrix of the strongest survivor per family. If they're all mutually >0.6 correlated -> one bet, not many. S2 (short-beta tell): correlation of each survivor to two reference factors built on the SAME panel: SHORTBETA = book ranking by -roll_beta; SHORTMKT = -equal-weight alt-market return. A genuinely market-neutral factor should NOT load heavily on "short the market". S3 (single-regime): per-calendar-year Sharpe. If the edge is ~entirely 2025, the 2.5y panel has ONE up-for-the-factor regime and the hold-out (2025-26) cannot prove robustness. Run: uv run python scripts/research/xsec/verify_survivors.py """ import sys from pathlib import Path import numpy as np import pandas as pd sys.path.insert(0, str(Path(__file__).resolve().parent)) import xslib as xs # noqa: E402 import altlib as al # noqa: E402 (via xslib sys.path) def roll_corr_to_market(ret, win): """Rolling corr of each asset's return to the equal-weight market (causal).""" mkt = pd.Series(xs.market_ret(ret)) out = np.full_like(ret, np.nan) for a in range(ret.shape[1]): out[:, a] = pd.Series(ret[:, a]).rolling(win, min_periods=max(5, win // 2)).corr(mkt).values return out def book(universe, score_fn, H=10, k=5, long_short=True): p = xs.load_panel(universe) return xs.xs_backtest(p, score_fn(p), H=H, k=k, long_short=long_short) # ── strongest representative survivor per family (from the Find-phase output) ── SURV = { "XV02_lowidiovol": lambda: book("majors", lambda P: -xs.roll_std(xs.residual_return(P.ret, 60), 30)), "XV01_lowvol": lambda: book("majors", lambda P: -xs.roll_std(P.ret, 30)), "XV03_lowbeta": lambda: book("all", lambda P: -xs.roll_beta(P.ret, 60)), "XS06b_lowcorr": lambda: book("all", lambda P: -roll_corr_to_market(P.ret, 60)), "XU02_lowvol_maj": lambda: book("majors", lambda P: -xs.roll_std(P.ret, 30), k=5), "XM09_trendgmom": lambda: book("all", lambda P: _trend_gated_mom(P, 60)), "XL02_voltrendmom": lambda: book("majors", lambda P: xs.xs_zscore(xs.past_return(P.close, 60)) + xs.xs_zscore(xs.volume_z(P.vol, 30))), "XR02_revgated": lambda: book("majors", lambda P: _vol_gated_rev(P, 3), H=3), } def _trend_gated_mom(P, L): """XS momentum, but zeroed on days the equal-weight market trailing-sum is non-positive.""" s = xs.past_return(P.close, L) mkt = xs.market_ret(P.ret) up = pd.Series(mkt).rolling(L, min_periods=L // 2).sum().values > 0 out = s.copy() out[~up, :] = np.nan # flat (no ranking) when market not trending up return out def _vol_gated_rev(P, L): """Short-term reversal, active only when market realized vol is in its high regime.""" rev = -xs.past_return(P.close, L) mvol = pd.Series(xs.market_ret(P.ret)).rolling(20, min_periods=10).std() thr = mvol.expanding(min_periods=60).quantile(0.70).values hi = (mvol.values > thr) out = rev.copy() out[~hi, :] = np.nan return out # reference factors (the suspected single underlying bet) REF = { "SHORTBETA": lambda: book("all", lambda P: -xs.roll_beta(P.ret, 60)), # explicit short-high-beta "SHORTMKT": None, # -equal-weight alt market } def main(): print("=" * 96) print(" ADVERSARIAL VERIFY — are the xsec survivors one regime bet (short alt-beta) or N edges?") print("=" * 96) series = {n: al._to_daily(f()) for n, f in SURV.items()} # SHORTMKT reference = negative equal-weight alt-market daily return (vol-targeted like a book) p_all = xs.load_panel("all") mkt = pd.Series(-xs.market_ret(p_all.ret), index=p_all.index) series["SHORTBETA"] = al._to_daily(book("all", lambda P: -xs.roll_beta(P.ret, 60))) series["SHORTMKT"] = al._to_daily(mkt) df = pd.DataFrame(series).dropna(how="all") names = list(SURV.keys()) # ── S1: mutual correlation matrix ───────────────────────────────────────── print("\n[S1] Mutual correlation matrix of survivors (>0.6 = same bet):") C = df[names].corr() hdr = " " + " ".join(f"{n[:8]:>8s}" for n in names) print(hdr) for n in names: row = " ".join(f"{C.loc[n, m]:>8.2f}" for m in names) print(f" {n:<16s} {row}") iu = [(a, b) for i, a in enumerate(names) for b in names[i + 1:]] pair_corrs = [C.loc[a, b] for a, b in iu] print(f" --> mean off-diagonal corr = {np.mean(pair_corrs):+.2f} " f"(share |r|>0.6: {np.mean([abs(x) > 0.6 for x in pair_corrs]) * 100:.0f}%)") # ── S2: load on short-beta / short-market ───────────────────────────────── print("\n[S2] Correlation of each survivor to the suspected single bet:") print(f" {'survivor':<18s} {'corr_SHORTBETA':>15s} {'corr_SHORTMKT':>15s}") for n in names: cb = df[n].corr(df["SHORTBETA"]) cm = df[n].corr(df["SHORTMKT"]) print(f" {n:<18s} {cb:>15.2f} {cm:>15.2f}") # ── S3: per-year Sharpe (single-regime test) ────────────────────────────── print("\n[S3] Per-calendar-year Sharpe (is the edge ~entirely 2025?):") print(f" {'survivor':<18s} {'2024':>8s} {'2025':>8s} {'2026':>8s}") for n in names: s = df[n].dropna() cells = [] for y in (2024, 2025, 2026): sy = s[s.index.year == y] cells.append(f"{al._sh(sy):>8.2f}" if len(sy) > 20 else f"{'--':>8s}") print(f" {n:<18s} " + " ".join(cells)) print("\n" + "=" * 96) print(" VERDICT logic: high mutual corr + high SHORTBETA/SHORTMKT load + 2025-only Sharpe") print(" => one short-alt-beta regime bet on a single-regime 2.5y panel. LEAD/forward-monitor,") print(" NOT a sleeve (cannot prove it survives an alt-bull regime flip).") print("=" * 96) if __name__ == "__main__": main()