Files
PythagorasGoal/scripts/analysis/multi_source_check.py
Adriano Dal Pastro 14522262e6 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>
2026-06-19 15:20:59 +00:00

129 lines
4.9 KiB
Python

"""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()