chore(reset): v2.0.0 — storico certificato Deribit mainnet, ripartenza pulita
Reset del progetto su fondamenta verificate dopo la scoperta che l'intera libreria "validata OOS" era artefatto di feed contaminato (print fantasma del feed Cerbero TESTNET + storico Binance/USDT). - Storico ricostruito da Deribit MAINNET (ccxt pubblico, tokenless) e CERTIFICATO (certify_feed.py): BTC/ETH puliti su TUTTA la storia (mediana 2-6 bps vs Coinbase USD), integrita' OHLC + coerenza resample (maxΔ 0.00) + cross-venue OK. Alt esclusi (illiquidi/divergenti: LTC/DOGE 50-82% barre flat; XRP/BNB non certificabili). - Verdetto sul feed pulito: FADE / PAIRS / XS01 / TSM01 morti (ogni portafoglio Sharpe -2.3..-3.0, DD ~40%); solo SH01 e frammenti HONEST con segnale residuo, da ri-validare in isolamento. - Cleanup "restart pulito": strategie, stack live (src/live, src/portfolio, runner/executor, yml, docker), ~100 script ricerca/gate, waste/games/ portfolios, dati non certificati + cache e 60+ diari -> archiviati in Old/ (preservati, non cancellati). Diario consolidato in un unico documento. - Skeleton ricerca tenuto: Strategy ABC + indicatori + src/fractal + src/backtest/engine + load_data; tool dati certificati (rebuild_history, certify_feed, audit_feed, multi_source_check). - Universo dati ATTIVO: solo BTC/ETH (5m/15m/1h); guardrail fisico (load_data su alt -> FileNotFoundError). Esecuzione DISABILITATA, conto flat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user