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,87 @@
|
||||
"""Smoke test REALE dei pairs: fetch live da Cerbero + un tick vero per coppia.
|
||||
|
||||
A differenza di validate_worker_pairs.py (replay su parquet storici), questo verifica
|
||||
la PIPELINE LIVE end-to-end: chiama Cerbero per entrambe le gambe, controlla che lo
|
||||
strumento esista e sia fresco, fa un tick reale del PairsWorker e riporta lo stato.
|
||||
|
||||
Serve a scoprire i problemi che il backtest nasconde (es. un perp alt non disponibile
|
||||
sull'endpoint Deribit). NON apre ordini reali: e' solo paper/lettura.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from src.live.cerbero_client import CerberoClient
|
||||
from src.live.multi_runner import INSTRUMENT_MAP
|
||||
from src.live.pairs_worker import PairsWorker
|
||||
from scripts.strategies.PR01_pairs_reversion import PAIRS
|
||||
|
||||
|
||||
def fetch(cli, asset, start, end):
|
||||
inst = INSTRUMENT_MAP.get(asset, f"{asset}-PERPETUAL")
|
||||
try:
|
||||
cs = cli.get_historical(inst, start.strftime("%Y-%m-%d"), end.strftime("%Y-%m-%d"), "60")
|
||||
if not cs:
|
||||
return inst, None, "VUOTO (strumento assente sull'endpoint)"
|
||||
df = pd.DataFrame(cs)
|
||||
df["timestamp"] = df["timestamp"].astype("int64")
|
||||
df = df.sort_values("timestamp").reset_index(drop=True)
|
||||
last = pd.to_datetime(df["timestamp"].iloc[-1], unit="ms", utc=True)
|
||||
age = (datetime.now(timezone.utc) - last).total_seconds() / 3600
|
||||
return inst, df, f"{len(df)} barre, ultima {last:%Y-%m-%d %H:%M} ({age:.1f}h fa)"
|
||||
except Exception as e:
|
||||
return inst, None, f"ERRORE {type(e).__name__}: {str(e)[:60]}"
|
||||
|
||||
|
||||
def main():
|
||||
cli = CerberoClient()
|
||||
end = datetime.now(timezone.utc)
|
||||
start = end - timedelta(days=60)
|
||||
assets = sorted({a for a, _, _ in PAIRS} | {b for _, b, _ in PAIRS})
|
||||
|
||||
print("=" * 80)
|
||||
print(" SMOKE TEST LIVE PAIRS — fetch reale Cerbero + tick (no ordini reali)")
|
||||
print("=" * 80)
|
||||
data = {}
|
||||
for a in assets:
|
||||
inst, df, msg = fetch(cli, a, start, end)
|
||||
data[a] = df
|
||||
print(f" {a:<4s} [{inst:<16s}] {msg}")
|
||||
|
||||
print("\n tick reale per coppia:")
|
||||
tmp = Path(tempfile.mkdtemp())
|
||||
try:
|
||||
for a, b, p in PAIRS:
|
||||
if data.get(a) is None or data.get(b) is None:
|
||||
print(f" {a}/{b:<4s}: SKIP (manca feed live di una gamba) -> non tradabile live ora")
|
||||
continue
|
||||
w = PairsWorker(a, b, "1h", params=p, fee_rt=0.001, data_dir=tmp)
|
||||
w._log = lambda *x, **k: None
|
||||
w._notify = lambda *x, **k: None
|
||||
m = data[a][["timestamp", "close"]].merge(
|
||||
data[b][["timestamp", "close"]], on="timestamp", how="inner")
|
||||
if len(m) < p["n"] + 2:
|
||||
print(f" {a}/{b:<4s}: merge {len(m)} barre < n+2 ({p['n']+2}) -> dati insufficienti")
|
||||
continue
|
||||
z, _ = w._zscore(m["close_x"].values, m["close_y"].values)
|
||||
w.tick(data[a], data[b])
|
||||
state = ("IN POS " + ("LONG " + a if w.direction == 1 else "SHORT " + a)
|
||||
if w.in_position else "FLAT")
|
||||
print(f" {a}/{b:<4s}: OK merge {len(m)} barre, z_ora={z[-1]:+.2f} -> {state}")
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
print("\n Solo le coppie con entrambe le gambe fresche su Cerbero sono tradabili live.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user