"""IB DATA PROBE — enumera cosa un conto paper Interactive Brokers espone (dati storici). NON e' una strategia: e' lo scan di FATTIBILITA' DATI, gemello di certify_feed.py per il mondo IB. Disciplina v2.0.0: prima il dato (cosa c'e', quanto indietro, che qualita', cosa costa), poi la strategia. "Solo dati, decido dopo". PREREQUISITO: una sessione IB Gateway/TWS PAPER loggata e raggiungibile (default 127.0.0.1:4002). Tipico: avvii IB Gateway (Paper) sul tuo PC con API abilitata su 4002, poi reverse-tunnel SSH verso questo server: ssh -R 4002:localhost:4002 utente@server ESECUZIONE (senza sporcare le dipendenze del progetto): uv run --with ib_async python scripts/research/ib_probe.py uv run --with ib_async python scripts/research/ib_probe.py --port 7497 # TWS paper Cosa fa, in ordine, e si ferma con diagnosi chiara al primo errore: (1) connette e stampa server version + account paper; (2) risolve un set di contratti rilevanti per QUESTO progetto: - CME crypto: BTC (5 BTC), MBT (micro 0.1 BTC), ETH, MET (micro); -> per il BASIS/carry - spot crypto Paxos (se abilitato): BTC, ETH; - 2 azioni/ETF di riferimento (SPY, AAPL) per tarare durate/qualita'; (3) per ogni contratto risolto: chiede un piccolo storico (durata breve, 1 day bars) e riporta n barre, range, e se scatta un errore di SUBSCRIPTION mancante (codice 354/10089/10090...); (4) sintesi: cosa e' scaricabile GRATIS su paper vs cosa richiede market-data a pagamento. """ import argparse, sys def main(): ap = argparse.ArgumentParser() ap.add_argument("--host", default="127.0.0.1") ap.add_argument("--port", type=int, default=7497, help="7497=TWS paper (default), 4002=GW paper, 4001/7496=live") ap.add_argument("--client-id", type=int, default=77) args = ap.parse_args() try: from ib_async import IB, Future, Stock, Crypto, util except Exception: print("ib_async non importabile. Esegui con: uv run --with ib_async python scripts/research/ib_probe.py") sys.exit(2) ib = IB() try: ib.connect(args.host, args.port, clientId=args.client_id, timeout=15) except Exception as e: print(f"[CONNESSIONE FALLITA] {args.host}:{args.port} -> {repr(e)[:160]}") print(" Verifica: IB Gateway/TWS Paper acceso, API abilitata, porta giusta, tunnel attivo.") sys.exit(1) print("=" * 90) print(f" IB DATA PROBE — connesso {args.host}:{args.port} | serverVersion={ib.client.serverVersion()}") try: accts = ib.managedAccounts() print(f" account: {accts} (paper se inizia per 'D' tipicamente)") except Exception as e: print(f" account: ? ({repr(e)[:60]})") print("=" * 90) # (2) contratti rilevanti candidates = [] # CME crypto futures: lasciamo che IB scelga il front-month (no expiry -> reqContractDetails) candidates += [("CME BTC fut", Future("BTC", exchange="CME")), ("CME MBT micro", Future("MBT", exchange="CME")), ("CME ETH fut", Future("ETH", exchange="CME")), ("CME MET micro", Future("MET", exchange="CME"))] # spot crypto Paxos (puo' non essere abilitato su paper) candidates += [("Paxos BTC", Crypto("BTC", exchange="PAXOS", currency="USD")), ("Paxos ETH", Crypto("ETH", exchange="PAXOS", currency="USD"))] # riferimenti equity candidates += [("SPY ETF", Stock("SPY", "SMART", "USD")), ("AAPL", Stock("AAPL", "SMART", "USD"))] resolved = [] print("\n (A) RISOLUZIONE CONTRATTI") for label, c in candidates: try: cds = ib.reqContractDetails(c) if not cds: print(f" {label:16} -> NESSUN match") continue # per i futures prendi la scadenza piu' vicina disponibile cd = sorted(cds, key=lambda d: getattr(d.contract, "lastTradeDateOrContractMonth", "") or "")[0] con = cd.contract extra = f" exp={con.lastTradeDateOrContractMonth}" if getattr(con, "lastTradeDateOrContractMonth", "") else "" print(f" {label:16} -> OK {con.localSymbol or con.symbol} {con.exchange}{extra} (n match={len(cds)})") resolved.append((label, con)) except Exception as e: print(f" {label:16} -> ERR {repr(e)[:70]}") # (3) prova storico breve print("\n (B) STORICO DI PROVA (durata 10 D, barre 1 day)") for label, con in resolved: try: bars = ib.reqHistoricalData(con, endDateTime="", durationStr="10 D", barSizeSetting="1 day", whatToShow="TRADES", useRTH=False, formatDate=1, timeout=30) if not bars: print(f" {label:16} -> 0 barre (forse serve subscription o whatToShow diverso)") else: print(f" {label:16} -> {len(bars)} barre {bars[0].date} .. {bars[-1].date} close={bars[-1].close}") except Exception as e: print(f" {label:16} -> ERR {repr(e)[:90]}") print("\n (C) NOTE") print(" - errori 354/10089/10090/10168 = market-data subscription mancante (paper la eredita dal live).") print(" - per il BASIS/carry servono i MULTIPLI futures (front+next) -> poi reqContractDetails senza filtro expiry.") ib.disconnect() if __name__ == "__main__": main()