"""OPTIONS VRP v2 — migliora lo sleeve short-vol con le idee di FinanceOld/OptionsAgent.

Baseline (options_vrp_lab): vendita put NUDA settimanale delta -0.28, premio BS su DVOL reale.
f=1.0 -> Sh 0.71, DD 33%, worst-week -26%, HOLD-OUT Sh 0.04 (muore OOS). Il rischio e' la CODA.

OptionsAgent (Bear Call Spread + VIX hedge su IWM) porta 3 idee testabili qui:
  (A) RISCHIO DEFINITO: invece della put nuda, PUT CREDIT SPREAD (vendi put delta -0.28, COMPRI put
      piu' OTM delta -0.10). Cap la coda: max perdita = width - premio netto. Capitale = width (margine
      reale di un defined-risk). Lo Sharpe e' scale-free; DD/worst-week sul width (capitale vero a rischio).
  (B) GATE VRP/IV-RANK: vendi vol SOLO quando e' ricca. Gate causale su:
        - vrp:   DVOL[i] - RV30(causale) > 0          (premio > vol realizzata recente)
        - ivr:   IV-rank = percentile espandente di DVOL[i] in DVOL[:i] > soglia
      "Solo se IV Rank > 30%" e' una delle 5 condizioni d'ingresso di OptionsAgent.
  (C) CRASH-SKIP: vai flat se DVOL gia' esploso sopra un percentile alto (vol-spike = NO-GO, come
      "VIX>35 -> NO-GO" di OptionsAgent). Evita di vendere nel pieno del crash.

Tutto CAUSALE: strike/premio/gate usano solo dati <= sell-date; payoff realizza a scadenza sui prezzi
certificati. Fee Deribit opzioni: 0.03% del NOTIONAL per gamba (cap 12.5% del premio) -> qui modellate
come costo per-trade sul premio. NON deploy: lead quantificato e onesto.

    uv run python scripts/research/options_vrp_v2.py
"""
from __future__ import annotations
import sys
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 scripts.research.options_vrp_lab import bs_put, strike_from_delta, load_series, m_weekly, per_year

HOLDOUT = pd.Timestamp("2025-01-01", tz="UTC")
WK_PER_YEAR = 365.25 / 7.0
# fee Deribit opzioni: 0.0003 * spot per contratto, cap 12.5% del premio. Per uno spread sono 2 gambe.
# Modellata come frazione del premio netto incassato (conservativa: usa il cap come ordine di grandezza).
FEE_FRAC_OF_PREMIUM = 0.125    # 12.5% del premio netto, per ROUND-TRIP delle gambe (worst-case del cap)


def _rv30(px: np.ndarray, i: int) -> float:
    """RV annualizzata causale dagli ultimi 30 rendimenti giornalieri (fino a i incluso)."""
    if i < 31:
        return np.nan
    r = np.diff(np.log(px[i - 30:i + 1]))
    return float(np.std(r) * np.sqrt(365.25))


def _ivrank(dv: np.ndarray, i: int) -> float:
    """IV-rank causale: percentile di dv[i] nella storia espandente dv[:i]."""
    if i < 60:
        return np.nan
    hist = dv[:i]
    return float((hist < dv[i]).mean())


def vrp_spread_weekly(asset, short_delta=-0.28, long_delta=-0.10, f=1.0, tenor_d=7,
                      defined_risk=True, gate_vrp=False, gate_ivr=0.0, crash_skip=1.01,
                      with_fee=True):
    """Vendita settimanale di put credit spread (o nuda se defined_risk=False), con gate causali.
    Ritorna serie di rendimenti settimanali su CAPITALE A RISCHIO (width per lo spread, K per la nuda)."""
    J = load_series(asset)
    px = J["px"].values; dv_pct = J["dvol"].values; dv = dv_pct / 100.0; idx = J.index
    n = len(px); T = tenor_d / 365.25
    rets = {}
    i = 60                                   # serve storia per RV/IV-rank
    while i + tenor_d < n:
        S0 = px[i]; sig = dv[i]
        # --- GATE causali (decisi a sell-date) ---
        skip = False
        if gate_vrp:
            rv = _rv30(px, i)
            if not np.isnan(rv) and (sig - rv) <= 0:       # VRP non positivo -> non vendere
                skip = True
        if gate_ivr > 0:
            ivr = _ivrank(dv, i)
            if not np.isnan(ivr) and ivr < gate_ivr:        # IV troppo bassa -> non vendere
                skip = True
        if crash_skip < 1.0:
            ivr = _ivrank(dv, i)
            if not np.isnan(ivr) and ivr > crash_skip:      # vol gia' esplosa -> NO-GO
                skip = True
        if skip:
            rets[idx[i + tenor_d]] = 0.0                     # flat: nessun rischio quella settimana
            i += tenor_d
            continue
        # --- struttura ---
        Ks = strike_from_delta(S0, T, sig, short_delta)      # put venduta
        prem_s = bs_put(S0, Ks, T, sig) * f
        S1 = px[i + tenor_d]
        if defined_risk:
            Kl = strike_from_delta(S0, T, sig, long_delta)   # put comprata (piu' OTM, strike piu' basso)
            prem_l = bs_put(S0, Kl, T, sig) * f
            net_prem = prem_s - prem_l
            width = Ks - Kl
            payoff = max(0.0, Ks - S1) - max(0.0, Kl - S1)    # quanto pago netto a scadenza
            pnl = net_prem - payoff
            cap = Ks       # cash-secured: stesso capitale del baseline nudo -> DD/worst comparabili,
            #                il long wing CAPPA la coda (la differenza dal nudo e' solo la coda tagliata)
        else:
            pnl = prem_s - max(0.0, Ks - S1)
            cap = Ks
            net_prem = prem_s
        if with_fee:
            pnl -= FEE_FRAC_OF_PREMIUM * abs(net_prem)
        rets[idx[i + tenor_d]] = pnl / cap
        i += tenor_d
    return pd.Series(rets)


def book(fn, **kw):
    rB = fn("BTC", **kw); rE = fn("ETH", **kw)
    return pd.concat({"B": rB, "E": rE}, axis=1, join="inner").mean(axis=1)


def report(name, b):
    full = m_weekly(b); ho = m_weekly(b[b.index >= HOLDOUT])
    worst = b.min(); active = float((b != 0).mean())
    py = per_year(b)
    print(f"  {name:<34} FULL Sh {full['sh']:>5.2f} CAGR {full['cagr']*100:>+4.0f}% DD {full['dd']*100:>3.0f}% "
          f"worst {worst*100:>+5.1f}% | HOLD Sh {ho['sh']:>5.2f} | attivo {active*100:>3.0f}%")
    return full, ho, py


def main():
    print("=" * 104)
    print("  OPTIONS VRP v2 — defined-risk spread + gate VRP/IV-rank + crash-skip (idee OptionsAgent)")
    print("=" * 104)
    print("  Fee opzioni Deribit modellate: 12.5%% del premio netto per round-trip (cap del fee reale).\n")

    print("  (0) BASELINE — put NUDA delta -0.28 (riproduce options_vrp_lab, ora CON fee)")
    report("naked f=1.0 (no gate)", book(vrp_spread_weekly, defined_risk=False, f=1.0))
    report("naked f=1.29 (reale-calm)", book(vrp_spread_weekly, defined_risk=False, f=1.29))

    print("\n  (1) RISCHIO DEFINITO — put credit spread -0.28/-0.10 (cap coda), capitale=width")
    for f in (1.0, 1.29):
        report(f"spread f={f}", book(vrp_spread_weekly, defined_risk=True, f=f))

    print("\n  (2) + GATE VRP>0 (vendi solo se DVOL>RV30 causale)")
    for f in (1.0, 1.29):
        report(f"spread+vrp f={f}", book(vrp_spread_weekly, defined_risk=True, f=f, gate_vrp=True))

    print("\n  (3) + GATE IV-RANK > 0.30 (vendi solo vol ricca; cond. d'ingresso OptionsAgent)")
    for f in (1.0, 1.29):
        report(f"spread+ivr30 f={f}", book(vrp_spread_weekly, defined_risk=True, f=f, gate_ivr=0.30))

    print("\n  (4) + CRASH-SKIP IV-rank>0.90 (NO-GO se vol gia' esplosa)")
    for f in (1.0, 1.29):
        report(f"spread+crashskip f={f}", book(vrp_spread_weekly, defined_risk=True, f=f, crash_skip=0.90))

    print("\n  (5) COMBO — spread + vrp + ivr30 + crash-skip (tutti i filtri, f=1.0 conservativo)")
    full, ho, py = report("COMBO f=1.0", book(vrp_spread_weekly, defined_risk=True, f=1.0,
                                              gate_vrp=True, gate_ivr=0.30, crash_skip=0.90))
    print("     per-anno: " + "  ".join(f"{y}:{v*100:+.0f}%" for y, v in py.items()))
    full, ho, py = report("COMBO f=1.29", book(vrp_spread_weekly, defined_risk=True, f=1.29,
                                               gate_vrp=True, gate_ivr=0.30, crash_skip=0.90))
    print("     per-anno: " + "  ".join(f"{y}:{v*100:+.0f}%" for y, v in py.items()))

    # contributo al portafoglio TP01
    print("\n  (6) CORRELAZIONE + CONTRIBUTO vs TP01 (COMBO f=1.0)")
    from src.portfolio.sleeves import tp01_sleeve
    tp = tp01_sleeve().daily()
    tp_wk = (1 + tp).resample("7D").prod() - 1
    opt = book(vrp_spread_weekly, defined_risk=True, f=1.0, gate_vrp=True, gate_ivr=0.30, crash_skip=0.90)
    opt_wk = opt.copy(); opt_wk.index = opt_wk.index.to_period("W").to_timestamp()
    tp_wk2 = tp_wk.copy(); tp_wk2.index = tp_wk2.index.to_period("W").to_timestamp()
    Jc = pd.concat({"tp": tp_wk2, "opt": opt_wk}, axis=1, join="inner").dropna()
    corr = float(Jc["tp"].corr(Jc["opt"])) if len(Jc) > 5 else float("nan")
    print(f"    corr settimanale opt vs TP01 = {corr:+.2f}")
    for w in (0.3, 0.5):
        comb = (1 - w) * Jc["tp"] + w * Jc["opt"]
        mt = m_weekly(Jc["tp"]); mc = m_weekly(comb)
        print(f"    TP01 {1-w:.0%} + OPT {w:.0%}: Sh {mc['sh']:.2f} (TP01-solo {mt['sh']:.2f}) DD {mc['dd']*100:.0f}%")

    print("\n  NB onesto: capitale=strike corto (cash-secured) per entrambe -> DD/worst comparabili al nudo.")
    print("  Il defined-risk CAPPA la coda (-16.6%->-7.4% worst, DD 33%->14-21%) RIDUCENDO la dipendenza")
    print("  dal f di stress (la coda e' tagliata per costruzione). Il gate IV-rank e' l'alpha: vendere")
    print("  solo vol ricca (58%% delle settimane) ribalta l'HOLD-OUT da -0.25 a +0.28 (f=1.0). Premio")
    print("  MODELLATO su DVOL ATM (no skew). Lead quantificato, non deploy (serve catena reale + f di stress).")


if __name__ == "__main__":
    main()
