"""ANALISI OPTIONS per BTC/ETH — onesta sui dati REALI disponibili (cerbero-bite mainnet).

Dati: Old/data/options (chain per-strike + dvol + market_snapshots). Finestra ~2026-05-01→06-11
(~6 settimane, REGIME UNICO calmo). NON si può validare OOS un edge su opzioni qui; si possono
MISURARE i livelli reali (VRP, premi put, skew, liquidità) e ragionare sull'USO delle opzioni
per il book BTC/ETH certificato. cerbero-bite è ancora vivo -> la fonte continua ad accumulare.

    uv run python scripts/analysis/options_analysis.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
import pandas as pd

OPT = PROJECT_ROOT / "Old" / "data" / "options"


def load(name):
    return pd.read_parquet(OPT / name)


def market_snapshots_analysis():
    print("=" * 90)
    print("  (1) MARKET SNAPSHOTS — VRP, DVOL, funding, dealer-gamma (livelli reali)")
    print("=" * 90)
    ms = load("market_snapshots.parquet")
    t = pd.to_datetime(ms["timestamp"], utc=True, errors="coerce")
    print(f"  copertura: {t.min()} -> {t.max()}  ({len(ms)} righe)")
    for a in ("BTC", "ETH"):
        d = ms[ms["asset"] == a].dropna(subset=["iv_minus_rv"])
        if len(d) == 0:
            print(f"  {a}: nessun dato"); continue
        vrp = d["iv_minus_rv"].astype(float)
        dvol = d["dvol"].astype(float)
        rv = d["realized_vol_30d"].astype(float)
        fund = d["funding_perp_annualized"].astype(float) if "funding_perp_annualized" in d else pd.Series([np.nan])
        gam = d["dealer_net_gamma"].astype(float) if "dealer_net_gamma" in d else pd.Series([np.nan])
        print(f"\n  {a}  (n={len(d)})")
        print(f"    VRP (IV-RV):   media {vrp.mean():+.1f}  mediana {vrp.median():+.1f}  "
              f">0 nel {100*(vrp>0).mean():.0f}% del tempo   [IV-RV in punti di vol annua]")
        print(f"    DVOL:          media {dvol.mean():.1f}  range [{dvol.min():.1f}, {dvol.max():.1f}]")
        print(f"    Realized30d:   media {rv.mean():.1f}")
        print(f"    Funding perp:  media {fund.mean():+.1f}% annuo")
        if gam.notna().any():
            print(f"    Dealer net-γ:  >0 nel {100*(gam>0).mean():.0f}% del tempo (>0 = dealer long gamma = mean-rev)")


def chain_analysis(asset):
    print("\n" + "=" * 90)
    print(f"  (2) CHAIN {asset} — premi put protettivi, skew, liquidità (livelli reali)")
    print("=" * 90)
    ch = load(f"{asset.lower()}_chain.parquet")
    for col in ("strike", "bid", "ask", "mid", "iv", "delta", "gamma"):
        if col in ch:
            ch[col] = pd.to_numeric(ch[col], errors="coerce")
    ch["option_type"] = ch["option_type"].astype(str)
    dv = load("dvol_history.parquet")
    dv = dv[dv["asset"] == asset][["timestamp", "spot"]].copy()
    dv["spot"] = pd.to_numeric(dv["spot"], errors="coerce")
    # timestamp -> datetime UTC nativo (sono datetime64[tz], NON ms int: to_numeric li romperebbe)
    ch["t"] = pd.to_datetime(ch["timestamp"], utc=True, errors="coerce")
    dv["t"] = pd.to_datetime(dv["timestamp"], utc=True, errors="coerce")
    ch = ch.dropna(subset=["t"]).sort_values("t").reset_index(drop=True)
    dv = dv.dropna(subset=["t", "spot"]).sort_values("t").reset_index(drop=True)
    # spot causale per timestamp della chain (merge_asof nearest, tolleranza 1h)
    ch = pd.merge_asof(ch, dv[["t", "spot"]], on="t", direction="nearest",
                       tolerance=pd.Timedelta("1h"))
    ch = ch.dropna(subset=["spot", "mid", "strike"])
    # days-to-expiry
    exp = pd.to_datetime(ch["expiry"], utc=True, errors="coerce")
    ch["dte"] = (exp - ch["t"]).dt.total_seconds() / 86_400.0
    ch = ch[(ch["dte"] > 0.5) & (ch["dte"] < 90)]
    ch["money"] = ch["strike"] / ch["spot"]
    ch["prem_pct"] = ch["mid"] * 100          # mid è in COIN (frazione del sottostante) -> %-del-notional
    # NB: iv è GIÀ in percento (35.94 = 35.94%, coerente col DVOL ~40) -> non riscalare
    ch["spread_pct"] = (ch["ask"] - ch["bid"]) / ch["mid"].replace(0, np.nan) * 100

    puts = ch[ch["option_type"].str.lower().str.startswith("p")]
    calls = ch[ch["option_type"].str.lower().str.startswith("c")]

    def band(df, mlo, mhi, dlo, dhi):
        s = df[(df["money"] >= mlo) & (df["money"] <= mhi) & (df["dte"] >= dlo) & (df["dte"] <= dhi)]
        return s

    print("  PUT protettive — premio reale (mid/spot) e liquidità per tenor/moneyness:")
    print(f"    {'tenor':<10s}{'moneyness':<14s}{'premio%':>9s}{'/mese%':>9s}{'spread%':>9s}{'n':>7s}{'strike?':>9s}")
    for dlo, dhi, tn in [(5, 12, "settim."), (18, 45, "mensile")]:
        for mlo, mhi, ml in [(0.97, 1.03, "ATM"), (0.88, 0.93, "~10% OTM"), (0.83, 0.88, "~15% OTM")]:
            s = band(puts, mlo, mhi, dlo, dhi)
            if len(s) == 0:
                print(f"    {tn:<10s}{ml:<14s}{'—':>9s}{'—':>9s}{'—':>9s}{0:>7d}{'NO':>9s}")
                continue
            prem = s["prem_pct"].median()
            permonth = prem * 30.0 / s["dte"].median()
            print(f"    {tn:<10s}{ml:<14s}{prem:>8.2f}%{permonth:>8.2f}%{s['spread_pct'].median():>8.1f}%"
                  f"{len(s):>7d}{'SI':>9s}")

    # skew: IV put 10% OTM vs IV call 10% OTM (stesso tenor mensile)
    pv = band(puts, 0.88, 0.93, 12, 50)["iv"].median()
    cv = band(calls, 1.07, 1.12, 12, 50)["iv"].median()
    atmv = band(ch, 0.98, 1.02, 12, 50)["iv"].median()
    if pd.notna(pv) and pd.notna(cv):
        print(f"  SKEW: IV put 10%OTM {pv:.0f}% vs call 10%OTM {cv:.0f}% vs ATM {atmv:.0f}%"
              f"  -> skew put {pv-cv:+.0f} pt vol (>0 = put care = paura del crash prezzata)")


def main():
    market_snapshots_analysis()
    for a in ("BTC", "ETH"):
        chain_analysis(a)
    print("\n" + "=" * 90)
    print("  NB: finestra ~6 settimane, REGIME UNICO calmo -> livelli REALI misurabili, ma NESSUN")
    print("  edge su opzioni è validabile OOS qui. Vedi commento finale.")
    print("=" * 90)


if __name__ == "__main__":
    main()
