"""XR04 — Volume-shock reversal.

IDEA: Long recent losers that ALSO had a volume spike.
  score = -past_return(L) * (volume_z > 1)

The intuition: large volume + price drop signals capitulation/panic selling.
The oversold name with elevated volume is more likely to bounce vs a name
that drifted down quietly. L=3 is the suggested lookback.

Grid (<=5 calls):
  1. majors H=5 k=3 LS=True  L=3  (baseline config)
  2. majors H=5 k=5 LS=True  L=3  (more positions)
  3. all    H=5 k=5 LS=True  L=3  (wider universe)
  4. majors H=3 k=3 LS=True  L=5  (slightly longer reversal)
  5. majors H=5 k=3 LS=True  L=3 with volume_z threshold = 0.5 (lower bar)
"""
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/xsec")
import xslib as xs
import numpy as np


def vol_shock_reversal_score(P, L=3, vz_thresh=1.0):
    """score = -past_return(L) when volume_z > vz_thresh, else 0.
    Higher score = more reversal candidate with volume spike.
    Causally computed: all data at row i uses data <=i."""
    ret_L = xs.past_return(P.close, L)        # (n, A)
    vz = xs.volume_z(P.vol, 20)              # rolling 20d volume z-score
    # Only count the reversal signal when volume is elevated
    mask = vz > vz_thresh                     # bool (n, A)
    score = np.where(mask, -ret_L, 0.0)
    # Where no data (NaN), set to NaN so harness skips
    score = np.where(np.isfinite(ret_L) & np.isfinite(vz), score, np.nan)
    return score


results = []

# 1. Baseline: majors, H=5, k=3, L=3, vz_thresh=1.0
r1 = xs.study_xs(
    "XR04-majors-H5k3-L3",
    lambda P: vol_shock_reversal_score(P, L=3, vz_thresh=1.0),
    universe="majors", H=5, k=3, long_short=True
)
print(xs.fmt(r1))
print("JSON:", xs.as_json(r1))
results.append(r1)

# 2. More positions: majors, H=5, k=5, L=3
r2 = xs.study_xs(
    "XR04-majors-H5k5-L3",
    lambda P: vol_shock_reversal_score(P, L=3, vz_thresh=1.0),
    universe="majors", H=5, k=5, long_short=True
)
print(xs.fmt(r2))
print("JSON:", xs.as_json(r2))
results.append(r2)

# 3. Wider universe: all, H=5, k=5, L=3
r3 = xs.study_xs(
    "XR04-all-H5k5-L3",
    lambda P: vol_shock_reversal_score(P, L=3, vz_thresh=1.0),
    universe="all", H=5, k=5, long_short=True
)
print(xs.fmt(r3))
print("JSON:", xs.as_json(r3))
results.append(r3)

# 4. Slightly longer reversal window: majors, H=3, k=3, L=5
r4 = xs.study_xs(
    "XR04-majors-H3k3-L5",
    lambda P: vol_shock_reversal_score(P, L=5, vz_thresh=1.0),
    universe="majors", H=3, k=3, long_short=True
)
print(xs.fmt(r4))
print("JSON:", xs.as_json(r4))
results.append(r4)

# 5. Lower volume threshold: majors, H=5, k=3, L=3, vz_thresh=0.5
r5 = xs.study_xs(
    "XR04-majors-H5k3-L3-vz05",
    lambda P: vol_shock_reversal_score(P, L=3, vz_thresh=0.5),
    universe="majors", H=5, k=3, long_short=True
)
print(xs.fmt(r5))
print("JSON:", xs.as_json(r5))
results.append(r5)

# Pick best by: earns_slot first, then hold-out sharpe, then distinctness
def rank_key(r):
    earns = int(r.get("earns_slot", False))
    h_sh = r["holdout"].get("sharpe", -99)
    f_sh = r["full"].get("sharpe", -99)
    corr_xs01 = r.get("corr_xs01") or 1.0
    distinct = 1 if corr_xs01 < 0.6 else 0
    return (earns, h_sh, f_sh, distinct)

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