research(funding-carry): FC01 cross-sectional su HL -> fragile, NON regge + infra IB paper
Onda "nuova ricerca mirata". Unico meccanismo non coperto dalle 2 ondate: carry da funding (cashflow perp, delta-neutral). Scan dati: price-clock gia' FAIL (intraday), Deribit ccxt 0 righe, Cerbero solo candele -> fonte = API pubblica Hyperliquid. - fetch_hl_funding.py: 19 major, funding orario reale dal 2023-05, certificato (0 gap, cov 98-100%, ann +1.0% APT .. +21.6% NEAR). backoff anti-429. - funding_carry_hl.py: book dollar-neutral short-alto-funding/long-basso, causale come XS01, vol-target 20%, fee 0.05%/lato. Giudizio: marginal_vs_tp01 indurito + overlap XS01. VERDETTO: il premio esiste (carry >> anti) ma il book NON regge il gauntlet. FULL -0.12, HOLD -0.50, DILUTES vs TP01, in-sample edge <0.5, no multicut. Jackknife universo: FULL oscilla [-0.39,+0.30] togliendo UN asset -> FRAGILE/overfit. (preview a 17 asset era +0.62 ADDS: fortuna, mancavano NEAR/AAVE). corr XS01 -0.19 (ortogonale, non re-skin). Meccanismo: carry-vs-momentum, gli alto-funding pompano. -> NON entra in portafoglio, fetcher NON in cron. Diario completo. Infra IB (thread parallelo): gateway paper gnzsnz/ib-gateway (127.0.0.1:4002, READ_ONLY) in docker-compose + ib_probe.py. Esito dati basis CME micro: backtest NON fattibile (ContFuture back-adjusted, scaduti=1 barra). IB ok per esecuzione/forward, non ricerca. .env.ibgw gitignored (credenziali paper), template in .env.ibgw.example. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user