"""XV06 — Low Vol-of-Vol (stability of volatility)
Score = -roll_std(roll_std(ret, inner_win), outer_win)
Idea: assets whose volatility is most STABLE (predictable) are preferred long;
assets with high vol-of-vol (erratic/spiky volatility) are shorted.
Lower vol-of-vol = higher score = long bias.
Canonical: inner_win=10, outer_win=30.
"""
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/xsec")
import xslib as xs
import numpy as np


def score_xv06(P, inner=10, outer=30):
    """Score = -roll_std(roll_std(ret, inner), outer).
    Causal: each row uses only past data (rolling windows, no future leakage).
    Higher score = lower vol-of-vol = more stable volatility = preferred long.
    """
    # inner rolling std: daily vol estimate
    inner_vol = xs.roll_std(P.ret, inner)
    # outer rolling std of that vol: vol-of-vol
    vov = xs.roll_std(inner_vol, outer)
    # negate: lower vov = higher score = long
    return -vov


# --- Grid: 5 studies max ---
# 1) Canonical: inner=10, outer=30, all universe, H=10, k=5, L/S
rep1 = xs.study_xs(
    "XV06-i10-o30-H10-k5-LS",
    lambda P: score_xv06(P, inner=10, outer=30),
    universe="all", H=10, k=5, long_short=True
)
print(xs.fmt(rep1))
print("JSON:", xs.as_json(rep1))

# 2) Majors only (19 assets, better liquidity)
rep2 = xs.study_xs(
    "XV06-i10-o30-H10-k5-LS-majors",
    lambda P: score_xv06(P, inner=10, outer=30),
    universe="majors", H=10, k=5, long_short=True
)
print(xs.fmt(rep2))
print("JSON:", xs.as_json(rep2))

# 3) Wider outer window: inner=10, outer=60
rep3 = xs.study_xs(
    "XV06-i10-o60-H10-k5-LS",
    lambda P: score_xv06(P, inner=10, outer=60),
    universe="all", H=10, k=5, long_short=True
)
print(xs.fmt(rep3))
print("JSON:", xs.as_json(rep3))

# 4) Faster rebalance H=5
rep4 = xs.study_xs(
    "XV06-i10-o30-H5-k5-LS",
    lambda P: score_xv06(P, inner=10, outer=30),
    universe="all", H=5, k=5, long_short=True
)
print(xs.fmt(rep4))
print("JSON:", xs.as_json(rep4))

# 5) More concentrated k=3
rep5 = xs.study_xs(
    "XV06-i10-o30-H10-k3-LS",
    lambda P: score_xv06(P, inner=10, outer=30),
    universe="all", H=10, k=3, long_short=True
)
print(xs.fmt(rep5))
print("JSON:", xs.as_json(rep5))

# Pick best by: earns_slot first, then holdout Sharpe, then distinctness
reps = [rep1, rep2, rep3, rep4, rep5]
earners = [r for r in reps if r["earns_slot"]]
if earners:
    best = max(earners, key=lambda r: r["holdout"].get("sharpe", -999))
else:
    # fallback: positive full + hold-out + corr_xs01 < 0.6
    candidates = [r for r in reps if r["full"]["sharpe"] > 0 and r["holdout"].get("sharpe", 0) > 0
                  and (r.get("corr_xs01") or 1.0) < 0.6]
    if candidates:
        best = max(candidates, key=lambda r: r["holdout"].get("sharpe", -999))
    else:
        best = max(reps, key=lambda r: r["full"]["sharpe"])

print("\n=== BEST CONFIG ===")
print(xs.fmt(best))
print("JSON:", xs.as_json(best))
