"""XM05 — Momentum Acceleration MECHANISM: Score = past_return(close, L_short) - past_return(close, L_long) i.e. is momentum ACCELERATING? The idea: assets that are outperforming recently vs. their longer-run momentum are gaining momentum -> rank them high. Assets that were strong long-term but are slowing down -> rank low. L_short=20, L_long=60 (canonical config). Grid: vary universe (all/majors), H (5/10), and L_short param to find the best config within <=5 backtests. Distinctness target: if score is correlated to raw momentum (XS01), it's just XS01. If acceleration captures something different (regime change, reversal of leaders), it could be distinct and add to portfolio. """ import sys sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/xsec") import xslib as xs import numpy as np print("XM05 — Momentum Acceleration (L_short - L_long)") print("=" * 60) def mom_accel(close, L_short, L_long): """Score = short-term return minus long-term return (causal). Higher = accelerating.""" r_short = xs.past_return(close, L_short) r_long = xs.past_return(close, L_long) return r_short - r_long # --- 5 targeted backtests --- # 1) Canonical config: all universe, L_short=20, L_long=60, H=10, LS rep1 = xs.study_xs( "XM05_ALL_20_60", lambda P: mom_accel(P.close, 20, 60), universe="all", H=10, k=5, long_short=True ) print(xs.fmt(rep1)) print("JSON:", xs.as_json(rep1)) print() # 2) Majors universe (19 XS01 assets), same canonical L_short=20, L_long=60 rep2 = xs.study_xs( "XM05_MAJ_20_60", lambda P: mom_accel(P.close, 20, 60), universe="majors", H=10, k=5, long_short=True ) print(xs.fmt(rep2)) print("JSON:", xs.as_json(rep2)) print() # 3) All universe, shorter window: L_short=10, L_long=30 (faster acceleration signal) rep3 = xs.study_xs( "XM05_ALL_10_30", lambda P: mom_accel(P.close, 10, 30), universe="all", H=10, k=5, long_short=True ) print(xs.fmt(rep3)) print("JSON:", xs.as_json(rep3)) print() # 4) All universe, L_short=20, L_long=60, longer holding period H=20 rep4 = xs.study_xs( "XM05_ALL_20_60_H20", lambda P: mom_accel(P.close, 20, 60), universe="all", H=20, k=5, long_short=True ) print(xs.fmt(rep4)) print("JSON:", xs.as_json(rep4)) print() # 5) All universe, longer windows: L_short=30, L_long=90 (medium-term acceleration) rep5 = xs.study_xs( "XM05_ALL_30_90", lambda P: mom_accel(P.close, 30, 90), universe="all", H=10, k=5, long_short=True ) print(xs.fmt(rep5)) print("JSON:", xs.as_json(rep5)) print() # Pick best: prefer earns_slot, then hold-out sharpe, then distinctness from XS01 all_reps = [rep1, rep2, rep3, rep4, rep5] def score_rep(r): earns = int(r["earns_slot"]) hold_sh = r["holdout"].get("sharpe", -9) full_sh = r["full"]["sharpe"] corr_xs01 = r.get("corr_xs01") or 1.0 distinctness = 1 - abs(corr_xs01) # higher = more distinct 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))