14522262e6
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>
129 lines
4.9 KiB
Python
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()
|