"""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))
