"""XU01 — Momentum Universe Sweep MECHANISM: Best momentum z-blend (blend of past_return z-scores at L=30 and L=90), run on different universe sizes: majors (19), top20, top30, all (49). Goal: map where cross-sectional momentum alpha lives — does expanding to top20/top30/all help or hurt vs the tight 19-major universe of XS01? Grid (<=5 backtests): 1. majors (19) — baseline reference, should approach XS01 2. top20 — add one more liquid alt 3. top30 — mid-tier liquidity 4. all (49) — known to dilute (confirm) 5. top30, long-only (best mid-tier config variant) Signal: xs_zscore(past_return(close,30)) + xs_zscore(past_return(close,90)) — same blend as XS01. H=10, k=5, long_short=True (except run 5 long-only). """ import sys sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/xsec") import xslib as xs import numpy as np print("XU01 — Momentum Universe Sweep") print("=" * 60) def blend_score(P): """Z-blend of 30d and 90d momentum — same signal as XS01 but on any universe.""" z30 = xs.xs_zscore(xs.past_return(P.close, 30)) z90 = xs.xs_zscore(xs.past_return(P.close, 90)) return np.nanmean(np.stack([z30, z90], axis=0), axis=0) # 1) Majors (19) — baseline rep1 = xs.study_xs( "XU01_MAJORS", blend_score, universe="majors", H=10, k=5, long_short=True ) print(xs.fmt(rep1)) print("JSON:", xs.as_json(rep1)) print() # 2) Top-20 by $-volume rep2 = xs.study_xs( "XU01_TOP20", blend_score, universe=20, H=10, k=5, long_short=True ) print(xs.fmt(rep2)) print("JSON:", xs.as_json(rep2)) print() # 3) Top-30 by $-volume rep3 = xs.study_xs( "XU01_TOP30", blend_score, universe=30, H=10, k=5, long_short=True ) print(xs.fmt(rep3)) print("JSON:", xs.as_json(rep3)) print() # 4) All (49) — expected dilution rep4 = xs.study_xs( "XU01_ALL", blend_score, universe="all", H=10, k=5, long_short=True ) print(xs.fmt(rep4)) print("JSON:", xs.as_json(rep4)) print() # 5) Top-30, long-only — does dropping the short leg help with mid-tier names? rep5 = xs.study_xs( "XU01_TOP30_LO", blend_score, universe=30, H=10, k=5, long_short=False ) print(xs.fmt(rep5)) print("JSON:", xs.as_json(rep5)) print() # Pick best: prefer earns_slot, then hold-out sharpe, then full sharpe, then distinctness all_reps = [rep1, rep2, rep3, rep4, rep5] def score_rep(r): earns = int(r.get("earns_slot", False)) hold_sh = (r.get("holdout") or {}).get("sharpe", -9) full_sh = (r.get("full") or {}).get("sharpe", -9) corr_xs01 = r.get("corr_xs01") or 1.0 distinctness = 1 - abs(corr_xs01) return (earns, hold_sh, full_sh, distinctness) best = max(all_reps, key=score_rep) print("=" * 60) print(f"BEST CONFIG: {best['name']}") print(xs.fmt(best)) print("JSON:", xs.as_json(best))