"""ONESTA' DI TIMING: il 'Sharpe 5' del crypto->overnight equity e' look-ahead?

Test: separare la versione OVERLAP (segnale e ritorno coprono le stesse ore = look-ahead) dalla
versione TRADABILE (segnale finisce all'ENTRATA, catturo solo il moto SUCCESSIVO).
  segnale crypto = BTC [P 21:00 -> D 13:00 UTC]  (noto solo a D 13:00, poco prima dell'open 13:30)
  - OVERLAP   capture = gap   = open[D]/close[P]-1   [P 21:00 -> D 13:30]  <-- sovrappone il segnale
  - TRADABILE capture = intra = close[D]/open[D]-1   [D 13:30 -> D 21:00]  <-- DOPO l'entrata, pulito
Se OVERLAP >> TRADABILE, il numero alto e' artefatto: all'open il gap e' gia' avvenuto, non lo catturi.
Annualizzazione CORRETTA sqrt(252) (giornaliero), non sqrt(52).
"""
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


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]
        c1 = at(bc, D.normalize() + pd.Timedelta(hours=OPEN_H))      # crypto a D 13:00 (entrata)
        c0 = at(bc, P.normalize() + pd.Timedelta(hours=CLOSE_H))     # crypto a P 21:00
        if not (np.isfinite(c1) and np.isfinite(c0) and c0 > 0):
            continue
        crypto = c1 / c0 - 1.0
        gap = oc[D] / cc[P] - 1.0          # OVERLAP col segnale
        intra = cc[D] / oc[D] - 1.0        # DOPO l'entrata (tradabile)
        rows.append((D, crypto, gap, intra))
    return pd.DataFrame(rows, columns=["d", "crypto", "gap", "intra"]).set_index("d")


def main():
    print("=" * 90)
    print("  TIMING ONESTO: OVERLAP (look-ahead) vs TRADABILE — crypto overnight -> equity")
    print("=" * 90)
    print("  segnale noto a D13:00. OVERLAP=gap [copre il segnale]. TRADABILE=intraday [dopo entrata]. net 2bps, sqrt(252)\n")
    for tgt in ("SPY", "QQQ", "IWM"):
        D = build(tgt, "BTC")
        for name, col in (("OVERLAP gap (look-ahead)", "gap"), ("TRADABILE intraday", "intra")):
            C = np.sign(D["crypto"]) * D[col] - COST
            m = D.index >= OOS
            print(f"  {tgt} {name:26}: Sharpe long/short {_sh(C):.2f}  (OOS {_sh(C[m]):.2f})  ann {np.nanmean(C)*252*100:+.1f}%")
        print()
    print("  --- VERDETTO ---")
    D = build("QQQ", "BTC")
    cg = _sh(np.sign(D["crypto"]) * D["gap"] - COST)
    ci = _sh(np.sign(D["crypto"]) * D["intra"] - COST)
    print(f"  QQQ: gap(overlap) Sharpe {cg:.2f}  vs  intraday(tradabile) Sharpe {ci:.2f}")
    print(f"  -> il moto che il crypto 'predice' avviene NELLA finestra del segnale (overnight), non dopo.")
    print(f"     All'entrata (D13:00, pre-open) il gap e' gia' realizzato -> NON catturabile con l'ETF.")
    print(f"     Cio' che resta da catturare (intraday) ha Sharpe ~{ci:.1f}: l'edge tradabile e' quello.")
    print(f"  Per catturare PARTE dell'overnight servirebbe entrare a META' notte via FUTURES IB e")
    print(f"  testare se il crypto [P21:00->T] predice il future [T->open] (finestre NON sovrapposte):")
    print(f"  richiede dati intraday dei futures indice (ES/NQ), che NON abbiamo in cache -> data step.")


if __name__ == "__main__":
    main()
