1c0b5f1869
Branch dedicato. Disciplina v2.0.0: prima il dato certificato, poi la strategia. IB paper (gnzsnz/ib-gateway) da' storia daily ADJUSTED_LAST (div+split) profonda. - ib_equities_probe.py: sonda fattibilita' dati (profondita', adjusted, subscription). - fetch_ib_equities.py: FETCH+CERTIFY universo -> data/raw/eq_<sym>_1d.parquet (ms epoch, namespace dedicato). RIPARTIBILE (salta i parquet gia' scritti) -> niente refetch da IB. Certifica: integrita', gap lunghi, sanita' ritorni, sanita' adjustment. - eqlib.py: harness ricerca equity. Legge la CACHE su disco (lru_cache) MAI da IB; universi (11 settori SPDR + 9 classici 1998+ + broad), panel allineato, riusa lo scorer indurito altlib. UNIVERSO CERTIFICATO (17, data/raw/eq_* gitignored = cache locale): 9 settori classici dal 1998-12-22 (27.5y) + XLRE(2015)/XLC(2018) + SPY(1996,30y)/QQQ/IWM/ GLD(2004)/HYG(2007)/TLT(2016). Tutti integri (monotoni, no dup, no spike>50%, gap-lunghi 0). Start comune: 9 classici 1998, 11 settori 2018. Prossimo passo: prima ricerca = momentum cross-sectional settoriale, gauntlet onesto. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
96 lines
4.0 KiB
Python
96 lines
4.0 KiB
Python
"""IB EQUITIES/ETF DATA PROBE — certifica cosa il paper IB dà per la ricerca su azioni/ETF.
|
|
|
|
Gemello equity di certify_feed.py: PRIMA il dato (cosa c'è, quanto indietro, aggiustato per
|
|
dividendi/split?, cosa costa), POI la strategia. Disciplina v2.0.0.
|
|
|
|
Universo candidato per la prima ricerca equity (cross-sectional momentum / trend, l'edge "noioso e
|
|
robusto" più plausibile in un mercato efficiente):
|
|
* 11 SPDR settoriali (XLK..XLC) — universo canonico del momentum cross-section settoriale;
|
|
* ETF broad / macro (SPY QQQ IWM TLT GLD HYG) — per trend e risk-on/off;
|
|
* 2 azioni (AAPL MSFT) per tarare profondità/qualità.
|
|
|
|
Per ogni simbolo: profondità storica daily con whatToShow=ADJUSTED_LAST (split+dividendi, OBBLIGATORIO
|
|
per un backtest equity onesto) e TRADES (raw), + flag se scatta errore di subscription market-data.
|
|
|
|
uv run --with ib_async python scripts/research/ib_equities_probe.py
|
|
"""
|
|
import argparse, sys
|
|
|
|
SECTORS = ["XLK", "XLF", "XLE", "XLV", "XLI", "XLP", "XLY", "XLU", "XLB", "XLRE", "XLC"]
|
|
BROAD = ["SPY", "QQQ", "IWM", "TLT", "GLD", "HYG"]
|
|
STOCKS = ["AAPL", "MSFT"]
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--host", default="127.0.0.1")
|
|
ap.add_argument("--port", type=int, default=4002)
|
|
ap.add_argument("--client-id", type=int, default=88)
|
|
ap.add_argument("--years", default="20 Y", help="durata storica da richiedere")
|
|
args = ap.parse_args()
|
|
|
|
try:
|
|
from ib_async import IB, Stock
|
|
except Exception:
|
|
print("ib_async non importabile. Esegui con: uv run --with ib_async python ...")
|
|
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)[:140]}")
|
|
sys.exit(1)
|
|
|
|
print("=" * 96)
|
|
print(f" IB EQUITIES/ETF PROBE — {args.host}:{args.port} | acct {ib.managedAccounts()} | depth req {args.years}")
|
|
print("=" * 96)
|
|
|
|
universe = [("SECTOR", s) for s in SECTORS] + [("BROAD", s) for s in BROAD] + [("STOCK", s) for s in STOCKS]
|
|
print(f" {'sym':6} {'tipo':7} {'ADJUSTED_LAST':>26} {'TRADES':>22} note")
|
|
rows = []
|
|
for cat, sym in universe:
|
|
con = Stock(sym, "SMART", "USD")
|
|
try:
|
|
cds = ib.reqContractDetails(con)
|
|
if not cds:
|
|
print(f" {sym:6} {cat:7} {'-- no contract --':>26}")
|
|
continue
|
|
except Exception as e:
|
|
print(f" {sym:6} {cat:7} ERR resolve {repr(e)[:40]}")
|
|
continue
|
|
|
|
def hist(what):
|
|
try:
|
|
b = ib.reqHistoricalData(con, endDateTime="", durationStr=args.years,
|
|
barSizeSetting="1 day", whatToShow=what,
|
|
useRTH=True, formatDate=1, timeout=45)
|
|
if not b:
|
|
return "0 barre", None
|
|
return f"{len(b)}b {b[0].date}..{b[-1].date}", b
|
|
except Exception as e:
|
|
return f"ERR {repr(e)[:30]}", None
|
|
|
|
adj_s, adj_b = hist("ADJUSTED_LAST")
|
|
trd_s, _ = hist("TRADES")
|
|
note = ""
|
|
if "ERR" in adj_s or "0 barre" in adj_s:
|
|
note = "subscription? prova delayed"
|
|
print(f" {sym:6} {cat:7} {adj_s:>26} {trd_s:>22} {note}")
|
|
if adj_b:
|
|
rows.append((sym, len(adj_b), str(adj_b[0].date), str(adj_b[-1].date)))
|
|
|
|
print("-" * 96)
|
|
if rows:
|
|
depth = min(r[1] for r in rows); start = max(r[2] for r in rows)
|
|
print(f" CERTIFICABILI (ADJUSTED_LAST): {len(rows)}/{len(universe)} | profondità comune ~{depth}b | start comune {start}")
|
|
print(f" -> per un backtest cross-sectional servono date allineate: lo start comune e' il limite.")
|
|
else:
|
|
print(" NESSUN simbolo ha reso storia ADJUSTED — probabile mancanza market-data subscription.")
|
|
print(" Ripiego: whatToShow='TRADES' (raw, non adj) o dati 'delayed' / fonte esterna certificabile.")
|
|
ib.disconnect()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|