"""CROSS-SECTIONAL su universo Hyperliquid certificato (19 alt, 1d, 2024-2026).

Strategia market-neutral: ogni H giorni classifica gli asset per rendimento a L giorni (causale),
va long i top-k / short i bottom-k (momentum) o viceversa (reversal), dollar-neutral, vol-target.
Mira a DIVERSIFICARE TP01 (long-trend): se scorrelata e robusta, migliora il portafoglio.
Gauntlet onesto: FULL (2024-26) + within-window OOS (2025+) + per-anno + corr TP01 + contributo.

Caveat: storia corta (~2.5 anni). Risultati suggestivi, non robusti come BTC/ETH 6 anni.

    uv run python scripts/portfolio/xsec_research.py
"""
from __future__ import annotations
import sys, glob
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))
import numpy as np, pandas as pd
from src.portfolio.portfolio import to_daily, metrics, HOLDOUT, Sleeve, StrategyPortfolio
from src.portfolio.sleeves import tp01_sleeve

RAW = PROJECT_ROOT / "data" / "raw"
FEE = 0.001


def load_universe():
    cols = {}
    for f in sorted(glob.glob(str(RAW / "hl_*_1d.parquet"))):
        s = Path(f).stem.replace("hl_", "").replace("_1d", "").upper()
        d = pd.read_parquet(f)
        cols[s] = pd.Series(d["close"].values.astype(float), index=pd.to_datetime(d["timestamp"], unit="ms", utc=True))
    C = pd.concat(cols, axis=1, join="inner").sort_index().dropna()
    return C


def xs_book(C, L, H, k, mode="mom", target_vol=0.20):
    """Rendimenti netti giornalieri di un book cross-sectional market-neutral. Causale."""
    assets = list(C.columns); A = len(assets)
    px = C.values; n = len(px)
    dret = np.vstack([np.zeros(A), px[1:] / px[:-1] - 1.0])
    W = np.zeros((n, A))            # peso per asset per giorno (deciso a close[i], tenuto in i+1)
    w = np.zeros(A)
    for i in range(n):
        if i >= L and i % H == 0:
            lb = px[i] / px[i - L] - 1.0
            order = np.argsort(lb)
            w = np.zeros(A)
            lo, hi = order[:k], order[-k:]            # peggiori / migliori
            if mode == "mom":
                w[hi] = 0.5 / k; w[lo] = -0.5 / k     # long forti / short deboli
            else:
                w[lo] = 0.5 / k; w[hi] = -0.5 / k     # reversal
        W[i] = w
    # rendimento book: peso[i-1] guadagna dret[i]; fee su turnover
    gross = np.zeros(n); gross[1:] = np.sum(W[:-1] * dret[1:], axis=1)   # W[i-1] guadagna dret[i]
    turn = np.zeros(n); turn[0] = np.abs(W[0]).sum()
    turn[1:] = np.abs(np.diff(W, axis=0)).sum(axis=1)                    # turnover per (ri)settare W[i]
    net = gross - turn * (FEE / 2.0)
    s = pd.Series(net, index=C.index)
    # vol-target (causale): scala per target/vol_realizzata(30) shiftata
    rv = s.rolling(30, min_periods=15).std().shift(1) * np.sqrt(365.25)
    scale = np.clip(np.nan_to_num(target_vol / rv.replace(0, np.nan).values, nan=0.0), 0, 3.0)
    return pd.Series(s.values * scale, index=C.index)


def yr_breadth(daily):
    pre = daily
    yr = [float((1 + g).prod() - 1) for _, g in pre.groupby(pre.index.year)]
    consec = mx = 0
    for v in yr: consec = consec + 1 if v < 0 else 0; mx = max(mx, consec)
    return yr, (sum(v > 0 for v in yr) / len(yr) if yr else 0), mx


def main():
    C = load_universe()
    print("=" * 96)
    print(f"  CROSS-SECTIONAL Hyperliquid — {len(C.columns)} asset, {len(C)} giorni [{C.index[0].date()} -> {C.index[-1].date()}]")
    print("=" * 96)
    tp = tp01_sleeve(1.0); tp_daily = tp.daily()
    base = StrategyPortfolio([tp01_sleeve(1.0)]).backtest()

    print(f"\n  {'config':<24}{'FULL Sh':>9}{'OOS25 Sh':>10}{'ret%':>8}{'DD%':>7}{'corrTP':>8}{'anni+':>7}")
    cands = []
    grid = [("mom",L,H,k) for L in (30,60,90) for H in (5,10,20) for k in (3,5)] \
         + [("rev",L,H,k) for L in (3,7,14) for H in (3,5) for k in (3,5)]
    for mode,L,H,k in grid:
        d = to_daily(xs_book(C,L,H,k,mode))
        f=metrics(d); oos=metrics(d[d.index>=HOLDOUT])
        J=pd.concat({"tp":tp_daily,"x":d},axis=1,join="inner").dropna(); corr=float(J["tp"].corr(J["x"])) if len(J)>5 else float("nan")
        yr,pct,consec=yr_breadth(d)
        tag=f"{mode} L{L} H{H} k{k}"
        cands.append((tag,mode,L,H,k,f,oos,corr,pct,consec,d))
        if f["sharpe"]>0.6 or oos["sharpe"]>0.8:
            print(f"  {tag:<24}{f['sharpe']:>9.2f}{oos['sharpe']:>10.2f}{f['ret']*100:>+8.0f}{f['maxdd']*100:>7.1f}{corr:>+8.2f}{pct*100:>6.0f}%")

    # migliore per OOS Sharpe (con corr bassa) come candidato diversificatore
    good=[c for c in cands if not np.isnan(c[7]) and abs(c[7])<0.4 and c[5]["sharpe"]>0.5 and c[6]["sharpe"]>0]
    good.sort(key=lambda c:-(c[6]["sharpe"]))
    print(f"\n  Candidati scorrelati(<0.4) e positivi (FULL>0.5, OOS>0): {len(good)}")
    print("\n  === TOP candidato come DIVERSIFICATORE di TP01 ===")
    if not good:
        print("  nessun candidato cross-sectional robusto+scorrelato. Universo corto.")
        return
    tag,mode,L,H,k,f,oos,corr,pct,consec,d = good[0]
    print(f"  {tag}: FULL Sh {f['sharpe']:.2f} ret {f['ret']*100:+.0f}% DD {f['maxdd']*100:.1f}% | OOS25 Sh {oos['sharpe']:.2f} | corr TP01 {corr:+.2f} | anni+ {pct*100:.0f}% rossi-consec {consec}")
    per=[(y,round(v,3)) for y,(v) in zip([yy for yy,_ in d.groupby(d.index.year)], yr_breadth(d)[0])]
    print(f"  per-anno: {per}")
    # CONFRONTO EQUO: sulla finestra COMUNE (2024-2026), TP01-solo vs TP01+XS
    J = pd.concat({"tp": tp_daily, "xs": d}, axis=1, join="inner").dropna()
    tpw, xsw = J["tp"], J["xs"]
    bw_f = metrics(tpw); bw_h = metrics(tpw[tpw.index >= HOLDOUT])
    print(f"\n  [finestra comune {J.index[0].date()}->{J.index[-1].date()}]")
    print(f"  TP01 SOLO (su finestra comune): FULL Sh {bw_f['sharpe']:.2f} DD {bw_f['maxdd']*100:.1f}% | HOLD Sh {bw_h['sharpe']:.2f}")
    for w in (0.2, 0.3, 0.5):
        comb = (1 - w) * tpw + w * xsw
        cf = metrics(comb); ch = metrics(comb[comb.index >= HOLDOUT])
        print(f"  +XS w{w:.0%}: FULL {cf['sharpe']:.2f} ({cf['sharpe']-bw_f['sharpe']:+.2f}) DD {cf['maxdd']*100:.1f}%"
              f" | HOLD {ch['sharpe']:.2f} ({ch['sharpe']-bw_h['sharpe']:+.2f})")
    print("\n  WINNER-diversifier se: corr bassa, e TP01+XS batte TP01-solo (FULL E HOLD) sulla finestra comune,")
    print("  con breadth per-anno ok. Altrimenti no (e attenzione: storia XS solo ~2.5 anni).")


if __name__=="__main__":
    main()
