"""XS01b — Double-sort Momentum × Low-Vol
Score = xs_zscore(past_return(close, 60)) + xs_zscore(-roll_std(ret, 30))
Combines cross-sectional momentum with low-vol preference (lower realized vol = higher score).
Grid: universe x H x k variations, <=5 total backtests.
"""
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/xsec")
import xslib as xs
import numpy as np

# --- score factory ---
def score_mom_lowvol(mom_L=60, vol_win=30):
    """Double-sort: momentum z + low-vol z. Both causal (data <= close[i])."""
    def _score(P):
        mom = xs.xs_zscore(xs.past_return(P.close, mom_L))
        # low vol = higher score -> negate std
        lowvol = xs.xs_zscore(-xs.roll_std(P.ret, vol_win))
        return mom + lowvol
    return _score


# Grid (<=5 calls total):
# 1. Baseline: majors H10 k5 LS  (19 assets, closest to XS01 universe)
# 2. All universe H10 k5 LS
# 3. All universe H5 k5 LS  (faster rebalance)
# 4. Majors H10 k5 LS with longer mom window (90d) to differ from XS01
# 5. All universe H10 k7 LS (wider book)

configs = [
    dict(name="XS01b-MAJ-H10-k5",    universe="majors", H=10, k=5,  long_short=True,  fn=score_mom_lowvol(60,30)),
    dict(name="XS01b-ALL-H10-k5",    universe="all",    H=10, k=5,  long_short=True,  fn=score_mom_lowvol(60,30)),
    dict(name="XS01b-ALL-H5-k5",     universe="all",    H=5,  k=5,  long_short=True,  fn=score_mom_lowvol(60,30)),
    dict(name="XS01b-MAJ-H10-MOM90", universe="majors", H=10, k=5,  long_short=True,  fn=score_mom_lowvol(90,30)),
    dict(name="XS01b-ALL-H10-k7",    universe="all",    H=10, k=7,  long_short=True,  fn=score_mom_lowvol(60,30)),
]

results = []
for cfg in configs:
    print(f"\nRunning {cfg['name']} ...")
    fn = cfg.pop("fn")
    rep = xs.study_xs(score_fn=fn, **cfg)
    results.append(rep)
    print(xs.fmt(rep))
    print()

# --- pick best: prefer earns_slot, then hold-out sharpe, then corr_xs01 < 0.6
def score_result(r):
    earns = 1 if r["earns_slot"] else 0
    hold_sh = r["holdout"].get("sharpe", -99)
    full_sh = r["full"]["sharpe"]
    distinct = 1 if (r["corr_xs01"] or 1.0) < 0.6 else 0
    return (earns, hold_sh, full_sh, distinct)

best = max(results, key=score_result)
print("\n" + "="*60)
print("BEST CONFIG:")
print(xs.fmt(best))
print("JSON:", xs.as_json(best))
