"""XM07 — Sharpe-rank momentum cross-sectional strategy.

Score = roll_mean(ret, L) / roll_std(ret, L)   (realized Sharpe ratio over L days)
Rank assets cross-sectionally each H days, long top-k / short bottom-k.
Grid: L in {30, 60, 90}, then vary universe/H/k around the best L.
<=5 study_xs calls total.
"""
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/xsec")
import xslib as xs
import numpy as np


def sharpe_score(P, L):
    """Causal realized Sharpe = roll_mean(ret, L) / roll_std(ret, L).
    Uses daily returns (P.ret). Higher = stronger risk-adjusted momentum -> long.
    """
    mu = xs.roll_mean(P.ret, L)
    sigma = xs.roll_std(P.ret, L)
    # avoid division by near-zero vol; set to NaN if sigma too small
    score = mu / np.where(sigma > 1e-8, sigma, np.nan)
    return score  # (n_days x n_assets), higher = long


# ---- Grid (5 calls) --------------------------------------------------------
# Step 1: sweep L on "majors" universe with fixed H=10, k=5, long_short=True
print("=" * 60)
print("XM07 Sharpe-rank momentum — grid search")
print("=" * 60)

results = {}

# Call 1: L=30, majors
r1 = xs.study_xs("XM07_L30_majors", lambda P: sharpe_score(P, 30),
                 universe="majors", H=10, k=5, long_short=True)
print(xs.fmt(r1))
results["L30_majors"] = r1

# Call 2: L=60, majors
r2 = xs.study_xs("XM07_L60_majors", lambda P: sharpe_score(P, 60),
                 universe="majors", H=10, k=5, long_short=True)
print(xs.fmt(r2))
results["L60_majors"] = r2

# Call 3: L=90, majors
r3 = xs.study_xs("XM07_L90_majors", lambda P: sharpe_score(P, 90),
                 universe="majors", H=10, k=5, long_short=True)
print(xs.fmt(r3))
results["L90_majors"] = r3

# Pick best L by hold-out Sharpe among the 3
best_L_key = max(["L30_majors", "L60_majors", "L90_majors"],
                 key=lambda k: results[k]["holdout"]["sharpe"])
best_L = int(best_L_key.split("_")[0][1:])  # extract integer
print(f"\nBest L = {best_L} (by hold-out Sharpe)")

# Call 4: best L on "all" universe (49 alts) to test breadth
r4 = xs.study_xs(f"XM07_L{best_L}_all", lambda P: sharpe_score(P, best_L),
                 universe="all", H=10, k=5, long_short=True)
print(xs.fmt(r4))
results[f"L{best_L}_all"] = r4

# Call 5: best L on majors, try H=20 (less frequent rebalance, lower fee drag)
r5 = xs.study_xs(f"XM07_L{best_L}_H20", lambda P: sharpe_score(P, best_L),
                 universe="majors", H=20, k=5, long_short=True)
print(xs.fmt(r5))
results[f"L{best_L}_H20"] = r5

# ---- Pick overall best config -----------------------------------------------
print("\n" + "=" * 60)
print("SUMMARY — picking best config")
print("=" * 60)

def score_config(r):
    """Prefer: earns_slot, then hold-out, then full Sharpe, then distinctness."""
    earns = int(r.get("earns_slot", False))
    ho = r["holdout"]["sharpe"]
    full = r["full"]["sharpe"]
    dist = 1.0 - abs(r.get("corr_xs01", 1.0))  # higher = more distinct
    return (earns, ho, full, dist)

best_key = max(results.keys(), key=lambda k: score_config(results[k]))
best = results[best_key]

print(f"Best config: {best_key}")
print(xs.fmt(best))
print("JSON:", xs.as_json(best))
