"""XR03 — Residual Short-Term Reversal Score = -(sum of residual_return over last L days) Idiosyncratic reversal: removes market beta before computing the short-term reversal signal. L in {3, 5}; beta window fixed at 60d. Grid (<= 5 study_xs calls): 1. L=3, majors, H=5, k=5, long_short=True 2. L=5, majors, H=5, k=5, long_short=True 3. L=3, all, H=5, k=5, long_short=True 4. L=5, all, H=5, k=5, long_short=True 5. Best-L from above, all, H=10, k=5, long_short=True """ import sys sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/xsec") import xslib as xs import numpy as np BETA_WIN = 60 # rolling beta window for residual computation def score_xr03(P, L): """Causal residual reversal score. residual[i] = ret[i] - beta_rolling[i] * market_ret[i] score[i] = -sum(residual[i-L+1 .. i]) HIGHER score = more negative recent idio returns = long (reversal) """ res = xs.residual_return(P.ret, BETA_WIN) # (n_days, n_assets) # rolling sum of last L residuals (causal: sum of rows [i-L+1..i]) res_sum = xs.roll_mean(res, L) * L # roll_mean * L = roll_sum # reversal: negative of cumulative idio return score = -res_sum return score # --- Grid --- results = [] # 1. L=3, majors r1 = xs.study_xs("XR03_L3_maj", lambda P: score_xr03(P, 3), universe="majors", H=5, k=5, long_short=True) results.append(r1) print("=== XR03 L3 majors H5 ===") print(xs.fmt(r1)) # 2. L=5, majors r2 = xs.study_xs("XR03_L5_maj", lambda P: score_xr03(P, 5), universe="majors", H=5, k=5, long_short=True) results.append(r2) print("=== XR03 L5 majors H5 ===") print(xs.fmt(r2)) # 3. L=3, all r3 = xs.study_xs("XR03_L3_all", lambda P: score_xr03(P, 3), universe="all", H=5, k=5, long_short=True) results.append(r3) print("=== XR03 L3 all H5 ===") print(xs.fmt(r3)) # 4. L=5, all r4 = xs.study_xs("XR03_L5_all", lambda P: score_xr03(P, 5), universe="all", H=5, k=5, long_short=True) results.append(r4) print("=== XR03 L5 all H5 ===") print(xs.fmt(r4)) # 5. Best-L (by hold-out sharpe) with H=10 # pick best L from runs 1-4 best_so_far = max(results, key=lambda r: r["holdout"]["sharpe"]) best_L = 3 if "L3" in best_so_far["name"] else 5 best_univ = "all" if "all" in best_so_far["name"] else "majors" r5 = xs.study_xs(f"XR03_L{best_L}_{best_univ}_H10", lambda P: score_xr03(P, best_L), universe=best_univ, H=10, k=5, long_short=True) results.append(r5) print(f"=== XR03 L{best_L} {best_univ} H10 ===") print(xs.fmt(r5)) # --- Pick best overall by marginal robustness then hold-out --- def score_key(r): earns = r.get("earns_slot", False) oos = r.get("marginal", {}).get("robust_oos", False) verdict = r.get("marginal", {}).get("verdict", "") adds = verdict == "ADDS" hold = r["holdout"]["sharpe"] full = r["full"]["sharpe"] return (int(earns), int(adds), int(oos), hold, full) best = max(results, key=score_key) print("\n========== BEST CONFIG ==========") print(xs.fmt(best)) print("JSON:", xs.as_json(best))