"""CRYPTO x MERCATI IB — correlazioni e ANTICIPAZIONI (lead-lag).

Obiettivo: la crypto (24/7) anticipa i mercati IB (azioni/bond/oro/credito), o viceversa?
Disciplina onesta: i tranelli di timing daily sono enormi (crypto chiude 00:00 UTC, US equity 21:00
UTC -> il lag-0 e' contaminato), quindi (1) allineo i rendimenti sullo STESSO intervallo
(compounding crypto sul grid giorni-di-borsa), (2) guardo i lag >=1 giorno, (3) test del segno con
hit-rate e split in-sample/OOS, (4) flag multiple-testing.

Ipotesi piu' pulita = EFFETTO WEEKEND: la crypto si muove Sab+Dom (azionario chiuso) -> quel
movimento e' informazione PRIOR al lunedi'. Predice il gap/intraday del lunedi' azionario?
Uso gli OPEN dei parquet eq_ (Monday open noto alle 13:30 UTC, weekend crypto noto alle 00:00 UTC).

DATI: cache su disco (BTC/ETH Deribit 1h->1d UTC; ETF IB eq_*). 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

ETFS = ["SPY", "QQQ", "IWM", "TLT", "GLD", "HYG"]


def crypto_daily_close(asset="BTC") -> pd.Series:
    df = load_data(asset, "1h").set_index("datetime")["close"].astype(float)
    return df.resample("1D").last().dropna()   # close ~00:00 UTC del giorno dopo


def _ret(s):
    return s.pct_change()


def _corr_lags(x: pd.Series, y: pd.Series, lags=range(-5, 6)):
    """corr(x_{t-k}, y_t): k>0 => x ANTICIPA y di k giorni. Allineati sullo stesso grid."""
    J = pd.concat({"x": x, "y": y}, axis=1, join="inner").dropna()
    out = {}
    for k in lags:
        out[k] = round(float(J["x"].shift(k).corr(J["y"])), 3)
    return out


def main():
    print("=" * 98)
    print("  CRYPTO x MERCATI IB — correlazioni & anticipazioni (lead-lag)")
    print("=" * 98)
    btc = crypto_daily_close("BTC"); eth = crypto_daily_close("ETH")
    btc_r = _ret(btc); eth_r = _ret(eth)
    # equity close + grid giorni-di-borsa
    eq_close = {s: eqlib.load_eq(s)["close"].astype(float) for s in ETFS}
    eq_open = {s: eqlib.load_eq(s)["open"].astype(float) for s in ETFS}
    grid = eq_close["SPY"].index
    grid = grid[grid >= btc.index[0]]
    # crypto compoundato sul grid giorni-di-borsa (stesso intervallo dell'equity ret)
    def to_grid(s):
        cum = (1 + _ret(s)).cumprod()
        return (cum.reindex(cum.index.union(grid)).ffill().reindex(grid)).pct_change()
    btc_g = to_grid(btc); eth_g = to_grid(eth)

    print(f"  overlap dal {grid[0].date()} ({len(grid)} giorni di borsa)\n")

    print("  --- (1) CORRELAZIONE CONTEMPORANEA (stesso intervallo; lag0 contaminato da timing) ---")
    print(f"    {'ETF':5} {'corr BTC':>9} {'corr ETH':>9}")
    for s in ETFS:
        er = _ret(eq_close[s]).reindex(grid)
        cb = round(float(pd.concat([btc_g, er], axis=1).dropna().corr().iloc[0, 1]), 3)
        ce = round(float(pd.concat([eth_g, er], axis=1).dropna().corr().iloc[0, 1]), 3)
        print(f"    {s:5} {cb:>9} {ce:>9}")

    print("\n  --- (2) LEAD-LAG BTC vs ETF: corr(BTC_{t-k}, ETF_t), k>0 = BTC ANTICIPA ---")
    print(f"    {'ETF':5} " + " ".join(f"k{ k:+d}" for k in range(-3, 4)) + "   picco")
    for s in ETFS:
        er = _ret(eq_close[s]).reindex(grid)
        cl = _corr_lags(btc_g, er, range(-3, 4))
        peak = max(cl, key=lambda k: abs(cl[k]))
        row = " ".join(f"{cl[k]:+.2f}" for k in range(-3, 4))
        tag = f"k={peak:+d} ({'BTC->ETF' if peak>0 else 'ETF->BTC' if peak<0 else 'contemp'})"
        print(f"    {s:5} {row}   {tag}")

    print("\n  --- (3) EFFETTO WEEKEND: crypto Sab+Dom -> lunedi' azionario (anticipazione pulita) ---")
    # weekend crypto = close(Dom 00:00 lun) / close(Ven) - 1 ; calcolato su crypto daily (calendario)
    cal = pd.date_range(btc.index[0], btc.index[-1], freq="D", tz="UTC")
    bc = btc.reindex(cal).ffill()
    for s in ["SPY", "QQQ", "IWM", "HYG"]:
        oc = eq_open[s]; cc = eq_close[s]
        rows = []
        for mon in grid:
            if mon.weekday() != 0:      # solo lunedi'
                continue
            fri = mon - pd.Timedelta(days=3)
            if fri not in cc.index:      # venerdi' non di borsa (festa) -> salta
                continue
            wk = float(bc.get(mon, np.nan) / bc.get(fri + pd.Timedelta(days=0), np.nan) - 1) if fri in bc.index else np.nan
            # weekend crypto: da venerdi 00:00(close ven) a lunedi 00:00 -> usa bc[fri]..bc[mon]
            wk = float(bc.loc[mon] / bc.loc[fri] - 1) if (mon in bc.index and fri in bc.index) else np.nan
            gap = float(oc.loc[mon] / cc.loc[fri] - 1) if (mon in oc.index and fri in cc.index) else np.nan
            intr = float(cc.loc[mon] / oc.loc[mon] - 1) if mon in oc.index else np.nan
            rows.append((mon, wk, gap, intr))
        D = pd.DataFrame(rows, columns=["mon", "wk", "gap", "intr"]).dropna().set_index("mon")
        if len(D) < 50:
            print(f"    {s}: pochi dati ({len(D)})"); continue
        def stat(col):
            c = float(D["wk"].corr(D[col]))
            hit = float((np.sign(D["wk"]) == np.sign(D[col])).mean())
            return c, hit
        cg, hg = stat("gap"); ci, hi = stat("intr")
        # OOS: split 2022
        Dh = D[D.index >= pd.Timestamp("2022-01-01", tz="UTC")]
        cg_o = float(Dh["wk"].corr(Dh["gap"])); ci_o = float(Dh["wk"].corr(Dh["intr"]))
        print(f"    {s}: n={len(D)} | weekend-crypto -> Mon GAP corr {cg:+.2f} hit {hg*100:.0f}% (OOS22+ {cg_o:+.2f}) "
              f"| Mon INTRADAY corr {ci:+.2f} hit {hi*100:.0f}% (OOS {ci_o:+.2f})")

    print("\n  NB: lag-0/contemporanea contaminata dal timing (crypto chiude 00:00, equity 21:00 UTC).")
    print("      Il GAP del lunedi' e' il test pulito (weekend crypto = info prior all'apertura).")


if __name__ == "__main__":
    main()
