"""DERIBIT-monitor -> IB-trade: il segnale crypto overnight AGGIUNGE al long-overnight indice?

Idea utente: guardare crypto live su Deribit (24/7) e tradare l'indice su IB. Il GAP di apertura =
movimento OVERNIGHT dei futures (MES/MNQ/M2K, tradati di notte) -> catturabile, net ~2bps.

DOMANDA DECISIVA (test onesto): l'azionario ha gia' un OVERNIGHT PREMIUM noto (il drift positivo
notturno). Quindi "long indice overnight" rende di suo. Il segnale crypto MIGLIORA quel baseline,
o e' solo overnight-premium + beta? Confronto:
  A) ALWAYS-LONG overnight (incondizionato) = cattura il premio notturno puro
  B) LONG se crypto-overnight>0, else FLAT = usa il crypto come filtro
  C) LONG/SHORT sul segno del crypto = il segnale pieno
La metrica chiave e' B,C VS A (l'uplift del crypto sul puro premio notturno), non A in assoluto.

Dati: cache su disco (crypto 1h Deribit; ETF eq_* = proxy del future indice). net 2bps RT (futures).
"""
import sys
from pathlib import Path
import numpy as np, pandas as pd

ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT)); sys.path.insert(0, str(ROOT / "scripts" / "research"))
import eqlib
from crypto_lead_harness import crypto_hourly, at, OPEN_H, CLOSE_H

OOS = pd.Timestamp("2022-01-01", tz="UTC")
COST = 0.0002   # ~2bps RT micro-future (commissione + spread)


def _sh(r):
    r = np.asarray(r, float); r = r[np.isfinite(r)]
    return float(np.mean(r) / np.std(r) * np.sqrt(252)) if len(r) > 5 and np.std(r) > 0 else 0.0


def build(target="SPY", lead="BTC"):
    bc = crypto_hourly(lead)
    oc = eqlib.load_eq(target)["open"].astype(float); cc = eqlib.load_eq(target)["close"].astype(float)
    idx = cc.index; rows = []
    for j in range(1, len(idx)):
        D = idx[j]; P = idx[j-1]
        d_open = D.normalize() + pd.Timedelta(hours=OPEN_H)
        p_close = P.normalize() + pd.Timedelta(hours=CLOSE_H)
        c1 = at(bc, d_open); c0 = at(bc, p_close)
        if not (np.isfinite(c1) and np.isfinite(c0) and c0 > 0):
            continue
        crypto = c1 / c0 - 1.0
        overnight = oc[D] / cc[P] - 1.0       # gap = ritorno overnight del future indice (catturabile)
        rows.append((D, crypto, overnight))
    return pd.DataFrame(rows, columns=["d", "crypto", "on"]).set_index("d")


def main():
    print("=" * 92)
    print("  CRYPTO overnight -> LONG INDICE OVERNIGHT (Deribit-monitor / IB-trade): aggiunge al premio?")
    print("=" * 92)
    print(f"  net {COST*1e4:.0f}bps RT (micro-future). overnight = open[D]/close[P]-1 (= move notturno future).\n")
    for tgt in ("SPY", "QQQ", "IWM"):
        for lead in ("BTC", "ETH"):
            D = build(tgt, lead)
            up = D[D["crypto"] > 0]["on"]; dn = D[D["crypto"] <= 0]["on"]
            # strategie
            A = D["on"].values - COST                                   # always-long
            B = np.where(D["crypto"] > 0, D["on"], 0.0) - np.where(D["crypto"] > 0, COST, 0.0)  # long se crypto su
            C = np.sign(D["crypto"]) * D["on"] - COST                   # long/short
            shA, shB, shC = _sh(A), _sh(B), _sh(C)
            # OOS
            m = D.index >= OOS
            print(f"  {tgt} <- {lead}: n={len(D)} | notti crypto-SU {len(up)} ret medio {up.mean()*1e4:+.1f}bps "
                  f"| crypto-GIU {len(dn)} ret medio {dn.mean()*1e4:+.1f}bps  (spread {(up.mean()-dn.mean())*1e4:+.1f}bps)")
            print(f"        Sharpe: A always-long {shA:.2f} | B long-if-cryptoUp {shB:.2f} | C long/short {shC:.2f}  "
                  f"-> UPLIFT crypto B-A {shB-shA:+.2f}, C-A {shC-shA:+.2f}")
            print(f"        OOS22+: A {_sh(A[m]):.2f} | B {_sh(B[m]):.2f} | C {_sh(C[m]):.2f} | "
                  f"ann: A {np.nanmean(A)*252*100:+.1f}% B {np.nanmean(B)*252*100:+.1f}% C {np.nanmean(C)*252*100:+.1f}%")
        print()

    # focus SPY-BTC: per-anno A vs C, e sketch deploy
    D = build("SPY", "BTC")
    A = D["on"].values - COST; C = np.sign(D["crypto"]) * D["on"] - COST
    print("  --- SPY<-BTC per-anno: Sharpe A(always-long) vs C(crypto long/short) ---")
    for y in sorted(set(D.index.year)):
        mm = D.index.year == y
        if mm.sum() >= 40:
            print(f"    {y}: A {_sh(A[mm]):+.2f}  C {_sh(C[mm]):+.2f}  (n={mm.sum()})")
    print("\n  NB: se C-A ~ 0, il crypto NON aggiunge al premio overnight (e' solo il premio + beta).")
    print("      Se C-A >> 0 e A gia' alto, il crypto e' un filtro REALE sul rischio notturno.")


if __name__ == "__main__":
    main()
