"""PIANO STORICO DERIBIT — quanta storia copre davvero il venue dove ESEGUIAMO? Obiettivo: scegliere la fonte migliore per ricostruire lo storico di backtest, dato che si esegue su Deribit. Principio (gia' misurato in multi_source_check): l'ancora giusta e' il VENUE DI ESECUZIONE, non Binance/USDT. Qui rispondo con i numeri a: 1. COPERTURA: da quando esiste OHLCV su Deribit MAINNET (ccxt pubblico, no token) per gli strumenti che tradiamo — inverse (BTC/ETH-PERPETUAL) e lineari USDC. 2. TIMEFRAME nativi disponibili su Deribit. 3. FEDELTA' inverse-vs-lineare (stesso indice? -> posso usare l'inverse, storia lunga, come price-series e i lineari recenti sono ridondanti per il PREZZO). 4. GAP pre-Deribit: quanto indietro vanno le strategie e cosa manca -> da gap-fillare con Coinbase USD (spot, NON USDT). Tutto via ccxt pubblico Deribit (= api.deribit.com mainnet, reale). Non modifica nulla. uv run python scripts/analysis/deribit_history_plan.py """ from __future__ import annotations import sys from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(PROJECT_ROOT)) import numpy as np import pandas as pd import ccxt DERIBIT = ccxt.deribit({"enableRateLimit": True}) COINBASE = ccxt.coinbase({"enableRateLimit": True}) def earliest(symbol: str, tf: str = "1d") -> tuple[str | None, int, str | None]: """Trova la prima candela disponibile (probe since 2016) + n candele totali stimate.""" since = int(pd.Timestamp("2016-01-01", tz="UTC").timestamp() * 1000) try: rows = DERIBIT.fetch_ohlcv(symbol, tf, since=since, limit=5000) except Exception as e: return None, 0, f"{type(e).__name__}: {str(e)[:60]}" if not rows: return None, 0, "no-data" first = pd.to_datetime(int(rows[0][0]), unit="ms", utc=True) last = pd.to_datetime(int(rows[-1][0]), unit="ms", utc=True) return f"{first.date()} -> {last.date()}", len(rows), None def list_perps() -> dict: """Risolve i simboli ccxt reali dei perp Deribit per BTC/ETH (inverse + lineari).""" DERIBIT.load_markets() found = {} for sym, m in DERIBIT.markets.items(): if not m.get("swap"): continue base = m.get("base") if base not in ("BTC", "ETH"): continue settle = m.get("settle") kind = "inverse" if m.get("inverse") else "linear" found[f"{base}-{kind}({settle})"] = sym return found def fetch_series(ex, symbol, tf, start, end, limit=1000): start_ms = int(pd.Timestamp(start, tz="UTC").timestamp() * 1000) end_ms = int(pd.Timestamp(end, tz="UTC").timestamp() * 1000) tf_ms = ex.parse_timeframe(tf) * 1000 out, since = {}, start_ms while since <= end_ms: try: rows = ex.fetch_ohlcv(symbol, tf, since=since, limit=limit) except Exception: break if not rows: break for r in rows: if start_ms <= int(r[0]) <= end_ms and r[4]: out[int(r[0])] = float(r[4]) nxt = int(rows[-1][0]) + tf_ms if nxt <= since: break since = nxt if len(rows) < limit and since > end_ms: break return pd.Series(out) def dev_bps(a: pd.Series, b: pd.Series) -> tuple[int, float, float, float]: df = pd.concat([a.rename("a"), b.rename("b")], axis=1, join="inner") if len(df) == 0: return 0, 0, 0, 0 d = (df["a"] - df["b"]).abs() / df["b"] * 1e4 return len(df), float(d.median()), float(d.quantile(.95)), float(d.max()) def main(): print("=" * 84) print(" PIANO STORICO DERIBIT MAINNET (ccxt pubblico, reale)") print("=" * 84) print("\n[1] Simboli perp Deribit BTC/ETH risolti:") perps = list_perps() for k, v in perps.items(): print(f" {k:<22s} -> {v}") print("\n[2] COPERTURA storica (1d, probe da 2016):") print(f" {'strumento':<22s}{'range disponibile':<28s}{'giorni':>8s}") cov = {} for k, sym in perps.items(): rng, n, err = earliest(sym, "1d") cov[k] = (sym, rng, n) print(f" {k:<22s}{(rng or err or '-'):<28s}{n:>8d}") print("\n[3] TIMEFRAME nativi Deribit (test su BTC inverse, oggi):") bsym = next((s for k, s in perps.items() if k.startswith("BTC-inverse")), "BTC/USD:BTC") tfs = [] for tf in ("1m", "3m", "5m", "10m", "15m", "30m", "1h", "2h", "3h", "4h", "6h", "12h", "1d"): try: r = DERIBIT.fetch_ohlcv(bsym, tf, limit=3) tfs.append(tf if r else f"{tf}:vuoto") except Exception: tfs.append(f"{tf}:NO") print(f" ok: {[t for t in tfs if ':' not in t]}") print(f" ko: {[t for t in tfs if ':' in t]}") print("\n[4] FEDELTA' inverse-vs-lineare USDC (close 1h, ultimi ~40g):") for base in ("BTC", "ETH"): inv = next((s for k, s in perps.items() if k.startswith(f"{base}-inverse")), None) lin = next((s for k, s in perps.items() if k.startswith(f"{base}-linear")), None) if not inv or not lin: print(f" {base}: manca inverse o lineare"); continue a = fetch_series(DERIBIT, inv, "1h", "2026-04-15", "2026-05-27") b = fetch_series(DERIBIT, lin, "1h", "2026-04-15", "2026-05-27") n, med, p95, mx = dev_bps(a, b) print(f" {base}: barre={n} inverse-vs-lineare med {med:.1f} bps p95 {p95:.1f} max {mx:.1f}") print("\n[5] GAP pre-Deribit: Deribit inverse vs Coinbase USD su finestra PROFONDA (2020-06, 1d):") for base in ("BTC", "ETH"): inv = next((s for k, s in perps.items() if k.startswith(f"{base}-inverse")), None) a = fetch_series(DERIBIT, inv, "1d", "2020-06-01", "2020-09-01") b = fetch_series(COINBASE, f"{base}/USD", "1d", "2020-06-01", "2020-09-01", limit=300) n, med, p95, mx = dev_bps(a, b) cov_first = cov.get(f"{base}-inverse(BTC)" if base == "BTC" else f"{base}-inverse(ETH)", (None, "?", 0))[1] print(f" {base}: Deribit-vs-Coinbase barre={n} med {med:.1f} bps p95 {p95:.1f} max {mx:.1f}") if __name__ == "__main__": main()