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,128 @@
|
||||
"""CROSS-CHECK MULTI-FONTE — "chi ci dice che Binance e' corretto?"
|
||||
|
||||
Risponde col numero, non con la fede: scarica BTC/ETH 1h da 4 venue INDIPENDENTI
|
||||
(Binance USDT, Coinbase USD, Kraken USD, Deribit perp mainnet REALE via ccxt),
|
||||
li allinea per timestamp e misura di quanto divergono dal CONSENSO (mediana cross-venue).
|
||||
|
||||
Due finestre:
|
||||
A) regime CALMO recente -> baseline di concordanza
|
||||
B) STRESS depeg USDT (mag 2022) -> dove l'assunzione "Binance=verita'" si crepa
|
||||
(BTC/USDT != BTC/USD quando USDT perde il peg; noi eseguiamo USDC)
|
||||
|
||||
NON modifica nulla. Solo lettura + report. Deribit via ccxt = API pubblica MAINNET (reale).
|
||||
|
||||
uv run python scripts/analysis/multi_source_check.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
|
||||
|
||||
TF = "1h"
|
||||
TF_MS = 60 * 60 * 1000
|
||||
|
||||
# (label, ccxt_id, {asset: symbol}, per_request_limit)
|
||||
SOURCES = [
|
||||
("binance(USDT)", "binance", {"BTC": "BTC/USDT", "ETH": "ETH/USDT"}, 1000),
|
||||
("okx(USDT)", "okx", {"BTC": "BTC/USDT", "ETH": "ETH/USDT"}, 100),
|
||||
("coinbase(USD)", "coinbase", {"BTC": "BTC/USD", "ETH": "ETH/USD"}, 300),
|
||||
("kraken(USD)", "kraken", {"BTC": "BTC/USD", "ETH": "ETH/USD"}, 720),
|
||||
("deribit(perp)", "deribit", {"BTC": "BTC/USD:BTC", "ETH": "ETH/USD:ETH"}, 1000),
|
||||
]
|
||||
|
||||
WINDOWS = [
|
||||
("CALMO recente", "2026-04-15", "2026-05-27"),
|
||||
("STRESS depeg USDT","2022-05-08", "2022-05-16"),
|
||||
]
|
||||
|
||||
|
||||
def _ex(eid):
|
||||
return getattr(ccxt, eid)({"enableRateLimit": True})
|
||||
|
||||
|
||||
def fetch(eid, symbol, start_ms, end_ms, limit):
|
||||
"""OHLCV 1h paginato -> dict ts_ms -> close. Tollerante agli errori per-venue."""
|
||||
try:
|
||||
ex = _ex(eid)
|
||||
ex.load_markets()
|
||||
except Exception as e:
|
||||
return None, f"load_markets {type(e).__name__}: {str(e)[:60]}"
|
||||
if symbol not in ex.markets:
|
||||
# prova l'id grezzo (es. BTC-PERPETUAL) come fallback
|
||||
alt = symbol.split("/")[0] + "-PERPETUAL"
|
||||
symbol = alt if alt in ex.markets else symbol
|
||||
out, since, last_err = {}, start_ms, None
|
||||
while since <= end_ms:
|
||||
try:
|
||||
rows = ex.fetch_ohlcv(symbol, TF, since=since, limit=limit)
|
||||
except Exception as e:
|
||||
last_err = f"{type(e).__name__}: {str(e)[:60]}"
|
||||
break
|
||||
if not rows:
|
||||
break
|
||||
for r in rows:
|
||||
t = int(r[0])
|
||||
if start_ms <= t <= end_ms and r[4]:
|
||||
out[t] = 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
|
||||
if not out:
|
||||
return None, last_err or "no-data (storia non servita dalla venue)"
|
||||
return out, None
|
||||
|
||||
|
||||
def analyze(asset, wname, start, end):
|
||||
start_ms = int(pd.Timestamp(start, tz="UTC").timestamp() * 1000)
|
||||
end_ms = int(pd.Timestamp(end, tz="UTC").timestamp() * 1000)
|
||||
series, notes = {}, {}
|
||||
for label, eid, syms, lim in SOURCES:
|
||||
d, err = fetch(eid, syms[asset], start_ms, end_ms, lim)
|
||||
if d:
|
||||
series[label] = pd.Series(d, name=label)
|
||||
else:
|
||||
notes[label] = err
|
||||
if len(series) < 2:
|
||||
print(f" {asset} [{wname}] insufficienti venue ({list(series)}) — {notes}")
|
||||
return
|
||||
df = pd.concat(series.values(), axis=1, join="inner").sort_index()
|
||||
if len(df) == 0:
|
||||
print(f" {asset} [{wname}] nessun timestamp comune fra {list(series)}")
|
||||
return
|
||||
cons = df.median(axis=1) # consenso = mediana cross-venue
|
||||
print(f"\n {asset} [{wname}] barre comuni={len(df)} venue={len(df.columns)}")
|
||||
if notes:
|
||||
for k, v in notes.items():
|
||||
print(f" (assente: {k} — {v})")
|
||||
print(f" {'venue':<15s}{'med.bps':>9s}{'p95.bps':>9s}{'max.bps':>9s}"
|
||||
f"{'>10bps':>8s}{'>50bps':>8s}")
|
||||
for col in df.columns:
|
||||
dev = (df[col] - cons).abs() / cons * 1e4 # bps di scostamento dal consenso
|
||||
print(f" {col:<15s}{dev.median():>9.1f}{dev.quantile(.95):>9.1f}"
|
||||
f"{dev.max():>9.1f}{(dev>10).mean()*100:>7.1f}%{(dev>50).mean()*100:>7.1f}%")
|
||||
# spread max-min fra venue, barra per barra (quanto e' "largo" il disaccordo)
|
||||
spread = (df.max(axis=1) - df.min(axis=1)) / cons * 1e4
|
||||
print(f" -> spread cross-venue (max-min): med {spread.median():.1f} bps, "
|
||||
f"p95 {spread.quantile(.95):.1f} bps, max {spread.max():.1f} bps")
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 78)
|
||||
print(" CROSS-CHECK MULTI-FONTE — scostamento dal CONSENSO (mediana cross-venue), in bps")
|
||||
print(" 1 bps = 0.01%. 'Binance corretto' == vicino al consenso di venue indipendenti?")
|
||||
print("=" * 78)
|
||||
for wname, start, end in WINDOWS:
|
||||
print(f"\n--- FINESTRA: {wname} ({start} -> {end}) ---")
|
||||
for asset in ("BTC", "ETH"):
|
||||
analyze(asset, wname, start, end)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user