"""XL04 [LIQ] — Dollar-volume momentum.

Score = past_return of dollar-volume (close * volume) over W=30 days.
Idea: assets gaining LIQUIDITY / ATTENTION relative to peers will outperform.
This is the OPPOSITE of XL03 (which went long LOW dollar-volume names).

Mechanism:
  dvol[i] = close[i] * vol[i]          (daily dollar volume)
  score[i] = dvol[i] / dvol[i-W] - 1  (W-day return of dollar volume)
  -> long assets whose dollar volume is GROWING the fastest

Grid (<=5 runs):
  1. baseline: universe=all, H=10, k=5, long_short=True, W=30
  2. shorter window W=10 (faster attention signal)
  3. longer window W=60 (more stable)
  4. majors universe (19 XS01 assets — check distinctness from XS01)
  5. long-only version (long attention gainers, no shorting attention losers)
"""
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/xsec")
import xslib as xs
import numpy as np


# --- score factory -----------------------------------------------------------

def dvol_momentum_score(P, W=30):
    """Score = W-day past return of dollar volume (close * volume).
    CAUSAL: dvol_return[i] uses dvol[i] / dvol[i-W] - 1.
    Higher score = dollar volume growing faster = LONG.
    """
    dvol = P.close * P.vol           # (n, A) daily dollar volume
    score = np.full_like(dvol, np.nan)
    # past_return style: score[i] = dvol[i] / dvol[i-W] - 1
    # guard: if dvol[i-W] == 0 -> NaN
    denom = dvol[:-W]                # dvol[i-W]
    numer = dvol[W:]                 # dvol[i]
    with np.errstate(invalid="ignore", divide="ignore"):
        ratio = np.where(denom > 0, numer / denom - 1.0, np.nan)
    score[W:] = ratio
    return score


# --- grid -------------------------------------------------------------------

print("=" * 70)
print("XL04 [LIQ] Dollar-volume momentum — grid search")
print("=" * 70)

results = []

# Run 1: baseline (all, H=10, k=5, LS, W=30)
r1 = xs.study_xs("XL04-W30-all-LS",
                 lambda P: dvol_momentum_score(P, 30),
                 universe="all", H=10, k=5, long_short=True)
print(xs.fmt(r1))
print("JSON:", xs.as_json(r1))
results.append(r1)

# Run 2: shorter window W=10 (faster attention surge)
r2 = xs.study_xs("XL04-W10-all-LS",
                 lambda P: dvol_momentum_score(P, 10),
                 universe="all", H=10, k=5, long_short=True)
print(xs.fmt(r2))
print("JSON:", xs.as_json(r2))
results.append(r2)

# Run 3: longer window W=60 (sustained attention)
r3 = xs.study_xs("XL04-W60-all-LS",
                 lambda P: dvol_momentum_score(P, 60),
                 universe="all", H=10, k=5, long_short=True)
print(xs.fmt(r3))
print("JSON:", xs.as_json(r3))
results.append(r3)

# Run 4: majors universe only (19 XS01 assets)
r4 = xs.study_xs("XL04-W30-majors-LS",
                 lambda P: dvol_momentum_score(P, 30),
                 universe="majors", H=10, k=5, long_short=True)
print(xs.fmt(r4))
print("JSON:", xs.as_json(r4))
results.append(r4)

# Run 5: long-only (attention gainers only, no shorting losers)
r5 = xs.study_xs("XL04-W30-all-LO",
                 lambda P: dvol_momentum_score(P, 30),
                 universe="all", H=10, k=5, long_short=False)
print(xs.fmt(r5))
print("JSON:", xs.as_json(r5))
results.append(r5)

# --- pick best config -------------------------------------------------------
print("\n" + "=" * 70)
print("SUMMARY — best by: earns_slot first, then hold-out Sharpe, then distinctness from XS01")
print("=" * 70)


def rank_key(r):
    earns = int(r["earns_slot"])
    hold_sh = r["holdout"].get("sharpe", -9) or -9
    xs01_corr = abs(r.get("corr_xs01") or 1.0)
    full_sh = r["full"].get("sharpe", -9) or -9
    return (earns, hold_sh, full_sh, -xs01_corr)


best = max(results, key=rank_key)
print(f"\nBEST CONFIG: {best['name']}")
print(xs.fmt(best))
print("\nJSON (best):", xs.as_json(best))
