"""FETCH futures indice ORARI (ES/NQ/RTY) da IB -> data/raw/fut_<sym>_1h.parquet (UTC).

Per il test onesto dell'idea "monitor Deribit / trade IB": serve il path INTRADAY del future indice
(che si trada di notte) per misurare finestre overnight NON sovrapposte col segnale crypto.
ContFuture orario, in chunk da 1 anno (IB limita le durate intraday). Convertito in UTC.
Resumable (salta i parquet gia' scritti). Per RETURNS/lead-lag il back-adjust del ContFuture e' ok
(i ritorni infra-contratto sono preservati; i gap di roll ~4/anno sono trascurabili).

  uv run --with ib_async python scripts/research/fetch_ib_futures.py
"""
import sys, time
from pathlib import Path
import numpy as np, pandas as pd

ROOT = Path(__file__).resolve().parents[2]
RAW = ROOT / "data" / "raw"
# sym -> exchange (indici US + esteri + commodity + bond per la ricerca cross-mercato oltre SP500)
SYMS = {"ES": "CME", "NQ": "CME", "RTY": "CME",
        "GC": "COMEX", "CL": "NYMEX", "HG": "COMEX", "ZN": "CBOT",
        "ESTX50": "EUREX", "DAX": "EUREX", "NKD": "CME"}
N_CHUNKS = 5   # (non usato: ContFuture non accetta endDateTime -> chiamata singola)


def main():
    from ib_async import IB, ContFuture
    ib = IB()
    try:
        ib.connect("127.0.0.1", 4002, clientId=144, timeout=15)
    except Exception as e:
        print(f"[CONNESSIONE FALLITA] {repr(e)[:100]}"); sys.exit(1)
    print(f"  acct {ib.managedAccounts()} | fetch futures orari -> data/raw/fut_*")
    for sym, exc in SYMS.items():
        out = RAW / f"fut_{sym.lower()}_1h.parquet"
        if out.exists():
            print(f"  {sym}: gia' su disco -> skip"); continue
        cf = ContFuture(sym, exchange=exc)
        try:
            ib.qualifyContracts(cf)
        except Exception as e:
            print(f"  {sym}: qualify ERR {repr(e)[:60]}"); continue
        # ContFuture NON accetta endDateTime (Error 10339) -> chiamata singola, durata massima (~3y orari)
        try:
            b = ib.reqHistoricalData(cf, endDateTime="", durationStr="4 Y", barSizeSetting="1 hour",
                                     whatToShow="TRADES", useRTH=False, formatDate=1, timeout=150)
        except Exception as e:
            print(f"  {sym}: ERR {repr(e)[:60]}"); continue
        if not b:
            print(f"  {sym}: VUOTO"); continue
        D = pd.DataFrame([(pd.Timestamp(x.date), x.open, x.high, x.low, x.close, x.volume) for x in b],
                         columns=["ts", "open", "high", "low", "close", "volume"]).drop_duplicates("ts").sort_values("ts").reset_index(drop=True)
        # -> UTC ms (robusto alla risoluzione us/ns: naive-UTC -> datetime64[ms] -> int64)
        ts = pd.to_datetime(D["ts"], utc=True).dt.tz_convert("UTC").dt.tz_localize(None)
        D["timestamp"] = ts.values.astype("datetime64[ms]").astype("int64")
        D = D.drop(columns=["ts"])
        D.to_parquet(out)
        u = pd.to_datetime(D["timestamp"], unit="ms", utc=True)
        print(f"  {sym}: SCRITTO {len(D)} barre  {u.iloc[0]} .. {u.iloc[-1]}  -> {out.name}")
        time.sleep(1.5)
    ib.disconnect()
    print("  done.")


if __name__ == "__main__":
    main()
