"""WEEKEND CRYPTO -> LUNEDI' AZIONARIO — validazione avversariale dell'anticipazione.

L'analisi lead-lag ha trovato UNA anticipazione pulita: il movimento crypto del weekend (Sab+Dom,
azionario chiuso) anticipa il lunedi' azionario (gap corr ~0.24, OOS piu' forte). Prima di crederci,
due test scettici:
  (A) INCREMENTALE: aggiunge info OLTRE il rendimento del VENERDI'? (o e' solo momentum equity?)
      regressione  Mon ~ weekend_crypto + friday_equity ; il coeff del crypto resta significativo?
  (B) TRADABILE: segnale eseguibile = osservo weekend crypto (noto Dom 24:00 UTC), entro al Monday
      OPEN, esco al Monday CLOSE. Net di costi (4 bps RT ETF). Sharpe/hit/OOS vs sempre-long lunedi'.

DATI: cache su disco (BTC Deribit 1h->1d; ETF eq_* con OPEN). Nessun IB online.
"""
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"))
from src.data.downloader import load_data
import eqlib

OOS = pd.Timestamp("2022-01-01", tz="UTC")
COST_RT = 0.0004   # 4 bps round-trip ETF (entry open + exit close)


def _sh_weekly(r):
    r = np.asarray(pd.Series(r).dropna(), float)
    return float(np.mean(r) / np.std(r) * np.sqrt(52)) if len(r) > 2 and np.std(r) > 0 else 0.0


def build(asset_etf="QQQ"):
    btc = load_data("BTC", "1h").set_index("datetime")["close"].astype(float).resample("1D").last()
    cal = pd.date_range(btc.index[0], btc.index[-1], freq="D", tz="UTC")
    bc = btc.reindex(cal).ffill()
    oc = eqlib.load_eq(asset_etf)["open"].astype(float)
    cc = eqlib.load_eq(asset_etf)["close"].astype(float)
    rows = []
    for mon in cc.index:
        if mon.weekday() != 0:
            continue
        fri = mon - pd.Timedelta(days=3)
        thu = mon - pd.Timedelta(days=4)
        if fri not in cc.index or fri not in bc.index or mon not in bc.index:
            continue
        wk = bc.loc[mon] / bc.loc[fri] - 1.0                        # weekend crypto (Ven 00:00 -> Lun 00:00)
        fri_eq = (cc.loc[fri] / cc.loc[thu] - 1.0) if thu in cc.index else np.nan  # rendimento venerdi'
        gap = oc.loc[mon] / cc.loc[fri] - 1.0
        intr = cc.loc[mon] / oc.loc[mon] - 1.0                      # tradabile: open->close lunedi'
        rows.append((mon, wk, fri_eq, gap, intr))
    return pd.DataFrame(rows, columns=["mon", "wk", "fri_eq", "gap", "intr"]).dropna().set_index("mon")


def main():
    print("=" * 92)
    print("  WEEKEND CRYPTO -> LUNEDI' AZIONARIO — validazione avversariale")
    print("=" * 92)
    for etf in ("QQQ", "SPY", "IWM"):
        D = build(etf)
        print(f"\n  ===== {etf}  (n={len(D)} lunedi', {D.index[0].date()}..{D.index[-1].date()}) =====")

        # (A) INCREMENTALE vs venerdi' — regressione OLS standardizzata, t-stat su weekend_crypto
        for tgt in ("gap", "intr"):
            y = (D[tgt] - D[tgt].mean()) / D[tgt].std()
            x1 = (D["wk"] - D["wk"].mean()) / D["wk"].std()
            x2 = (D["fri_eq"] - D["fri_eq"].mean()) / D["fri_eq"].std()
            X = np.column_stack([np.ones(len(D)), x1.values, x2.values])
            beta, *_ = np.linalg.lstsq(X, y.values, rcond=None)
            resid = y.values - X @ beta
            se = np.sqrt(np.sum(resid**2) / (len(D) - 3) * np.diag(np.linalg.inv(X.T @ X)))
            t_wk = beta[1] / se[1]; t_fri = beta[2] / se[2]
            partial = float(pd.Series(resid).corr(x1))  # ~ contributo crypto al netto del resto
            print(f"    [{tgt:4}] beta_weekendCrypto {beta[1]:+.3f} (t={t_wk:+.1f})  | "
                  f"beta_fridayEq {beta[2]:+.3f} (t={t_fri:+.1f})  -> crypto {'INCREMENTALE' if abs(t_wk)>2 else 'non signif.'}")

        # (B) TRADABILE: long lunedi' intraday se weekend crypto > 0, short se < 0 (net costi)
        sig = np.sign(D["wk"].values)
        gross = sig * D["intr"].values
        net = gross - COST_RT
        D2 = D.assign(net=net)
        full = D2["net"]; oos = D2[D2.index >= OOS]["net"]; ins = D2[D2.index < OOS]["net"]
        bh = D["intr"]  # baseline: sempre-long lunedi' intraday
        hit = float((np.sign(gross) > 0).mean()) if False else float((sig == np.sign(D["intr"].values)).mean())
        print(f"    TRADE (long/short Mon intraday su segno weekend-crypto, net {COST_RT*1e4:.0f}bps):")
        print(f"      hit-rate segno {hit*100:.0f}% | Sharpe(sett.) FULL {_sh_weekly(full):.2f}  IS {_sh_weekly(ins):.2f}  OOS22+ {_sh_weekly(oos):.2f}")
        print(f"      ritorno medio/lun {full.mean()*1e4:+.1f}bps (net) | baseline sempre-long {bh.mean()*1e4:+.1f}bps | "
              f"ann.~{full.mean()*52*100:+.1f}%")
        # long-flat (piu' realistico: long se crypto su, altrimenti cash)
        lf = np.where(D["wk"].values > 0, D["intr"].values, 0.0) - np.where(D["wk"].values > 0, COST_RT, 0.0)
        lfs = pd.Series(lf, index=D.index)
        print(f"      variante LONG-FLAT (long se crypto su, else cash): Sharpe FULL {_sh_weekly(lfs):.2f}  "
              f"OOS {_sh_weekly(lfs[lfs.index>=OOS]):.2f}  ann.~{lfs.mean()*52*100:+.1f}%")

    print("\n  NB: ~52 lunedi'/anno -> Sharpe settimanale; OOS = 2022+. Multiple-testing: 3 ETF x 2 target.")


if __name__ == "__main__":
    main()
