Files
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

10 KiB
Raw Permalink Blame History

2026-05-28 — Giorno 3: Bug dati Cerbero, paper trader fermo, fix MT01 multi-timeframe

12:20 — Sintomo: paper trader live a zero trade

Cosa: check del container pythagoras-multi (multi-strategy paper trader, 6 strategie). Reale: container healthy da ore, ma 0 trade su tutte le strategie, tutte FLAT a €1000. Primo falso indizio: last_bar_ts: 0 in tutti gli status.json. Indagando il worker, quel campo si aggiorna solo a posizione aperta (contatore hold_bars), non ad ogni candela → non è la causa. Il loop era vivo (status.json riscritti ogni 60s). Lezione: non fidarsi del nome di un campo; verificare nel codice quando viene scritto. L'healthcheck del container controlla solo l'esistenza di status.json, non la freschezza → un loop bloccato risulterebbe comunque "healthy".

12:45 — Causa radice: bug lato Cerbero MCP get_historical

Cosa: probe dirette all'endpoint /mcp-deribit/tools/get_historical. Reale: due bug lato server:

  1. end_date data-nuda tronca a mezzanotte: end=oggi restituiva candele solo fino a oggi 00:00. Il df live finiva sempre alla barra di mezzanotte e non avanzava durante la giornata → nessun breakout fresco sull'ultima barra → nessun ingresso (condizione worker last_signal.idx >= last_idx - 1).
  2. Cap a ~5000 righe che ignora start_date: una richiesta di 365g a 15m restituiva ~52 giorni. Ecco perché ML01 si addestrava su soli 88 samples (overfit, train_acc 100%). Lezione: lo zero-trade non era nelle strategie ma nel feed dati. Sempre validare la freschezza/copertura dei dati prima di sospettare la logica.

13:30 — Fix lato Cerbero + verifica

Cosa: report passato al dev di cerbero-mcp; fix deployato (riavvio container) + doc aggiornata in cerbero-mcp/docs/API_REFERENCE.md. Reale dopo deploy (verificato con probe):

  • end=oggi (data nuda) → ultima candela = ora corrente (age ~3 min).
  • 365g a 15m → 35.099 candele, span 365.6g, nessun cap.
  • Supportati anche timestamp con orario (...T14:00:00, naive = UTC).

Nostro client (src/live/cerbero_client.py) invariato: passa già end=oggi, ora corretto. Lezione: "trust but verify" — la doc dichiarava i fix prima che fossero deployati; solo la probe diretta ha confermato cosa era davvero attivo sul server.

14:00 — Problema residuo: MT01 usava un trend 1h STANTIO

Cosa: check di tutte le strategie sul percorso di codice reale con dati freschi. Reale:

  • Tutte le 6 strategie girano senza crash; SQ01/SQ02 generano molti segnali.
  • MT01 leggeva il trend 1h dal parquet statico (load_data(asset,"1h")), non da Cerbero. Il parquet finiva a mezzanotte → per ogni barra 15m di oggi searchsorted cadeva oltre la fine e si agganciava sempre alla candela di mezzanotte (gap 14.8h). La conferma multi-timeframe — il cuore di MT01 — era di fatto congelata e il gap cresce ogni giorno.
  • In data/raw/ mancavano del tutto i parquet 15m (btc_15m, eth_15m) → backtest 15m rotti. Lezione: una strategia live che dipende da un file statico ha un punto cieco temporale; il dato live e quello di backtest devono provenire da fonti coerenti.

14:30 — Fix MT01: trend 1h live da Cerbero

Cosa: modifica al runner perché MT01 prenda l'1h live, non dal parquet.

  • MT01.generate_signals accetta un df_1h opzionale (fallback al parquet se assente).
  • StrategyWorker.tick(df, df_1h=None) lo inoltra ai signal.
  • multi_runner fa fetch 1h live (resolution 60) per gli asset MT01 ad ogni poll (htf_cache). Reale (verificato a codice montato, pre-rebuild): gap del trend 1h sull'ultima barra 0.75h (fresco) contro 14.8h col parquet statico. Segnali invariati sullo storico. Lezione: isolare la dipendenza dal file statico rende MT01 immune al drift tra un download_all() e l'altro.

14:55 — Rigenerazione dati + rebuild

Cosa: download_asset per 15m+1h (saltati 1m/5m, lenti e inutilizzati), poi docker compose up -d --build (il codice src/ è baked nell'immagine). Reale: parquet rigenerati con storia completa 2018→2026 e freschi (15m fino alle 14:45, 1h fino alle 14:00). Container ripartito: 6 strategie attive, ML01 riaddestrato su 534 samples (anno pieno), MT01 senza errori, fetch 1h live OK.

15:00 — Regressione backtest sui dati rigenerati

Cosa: rilanciati i backtest per confermare che i numeri documentati si riproducano sui dati ricreati da zero (BTC/ETH 15m, hold=3, fee 0.2% RT, leva 3x, pos 15%). Reale: accuratezze e drawdown identici, solo +1/+3 trade dalle barre recenti in più.

Strategia Ottenuto Documentato Esito
SQ01 BTC 15m 76.7% / DD 6.7% / 4063t 76.7% / 6.7% / 4062
SQ01 ETH 15m 76.4% / 6.2% / 2951t 76.4% / 6.2% / 2948
SQ02 BTC 15m 79.7% / 6.5% / 1251t 79.7% / 6.5% / 1250
SQ02 ETH 15m 78.6% / 3.4% / 944t 78.6% / 3.4% / 942
MT01 BTC 15m (ema20+vol) 82.7% / 5.9% / 503t 82.7% / 5.9% / 503 ✓ esatto
MT01 ETH 15m (ema20+vol) 81.2% / 2.9% / 404t ok

Lezione: l'integrità dei dati rigenerati è confermata — la pipeline di download produce risultati riproducibili. La config live di MT01 (ema20+vol) coincide col best documentato.

Punti aperti

  1. Backtest e drift dati: MT01 live ora è immune (1h da Cerbero), ma i backtest girano sempre sui dati fino all'ultimo download_all(). Per dati di backtest sempre freschi serve uno scheduling del download (cron/job).
  2. Healthcheck: valutare un check su mtime di status.json (< 180s) per rilevare uno stallo del loop, non solo l'esistenza del file.

23:00 — 3 nuove strategie con edge OOS fee-aware (branch strategy_free)

Obiettivo: trovare almeno 3 nuove strategie (oltre MR01), edge netto validato out-of-sample e fee-aware, per il target €1.000 → ~€50/giorno.

Metodologia (invariata dalla lezione squeeze): ingresso eseguibile a close[i] (nessun look-ahead), backtest netto dopo fee Deribit 0.10% RT + leva 3x, OOS = ultimo 30% held-out, robustezza su griglia parametri + sweep fee 0.000.20% RT, exit TP/SL intrabar o time-limit, una posizione per volta, capitale composto.

Candidati (scripts/analysis/strategy_research_v2.py), tutti mean-reversion (l'edge è sempre il rientro, mai la continuazione):

Candidato Esito Motivo
MR02 Donchian Fade Robusto su tutta la griglia n × sl_atr e tutte le fee
MR03 Keltner Fade Robusto su tutta la griglia n × k; banda ATR, indipendente da Bollinger
MR07 Return Reversal Intero blocco tp_atr=2.0 positivo full+OOS; esposizione ~8%
MR04 Z-score Reversion Robusto ma è MR01 riparametrizzato (stessa banda std): edge non nuovo
MR05 Bollinger + filtro ADX Non robusto: negativo su gran parte della griglia BTC
MR06 RSI(2) Connors ETH 1h negativo; non robusto su entrambi gli asset

Risultati (netto 0.10% RT, leva 3x, OOS, 1h):

Codice Meccanismo BTC OOS ETH OOS DD (full)
MR02 estremi canale Donchian H/L +172% enorme 30% / 42%
MR03 canale ATR su EMA +112% +886% 37% / 66%
MR07 z dei rendimenti di barra +105% +195% 25% / 46%

Validazione live-path (oos_validation.py, legge strategies.yml, exit hold del worker): tutte e tre positive netto OOS su tutto lo sweep fee, anche al pessimistico 0.20% RT → edge robusto pure al meccanismo di exit.

Verifiche: equivalenza esatta backtest produzione vs research engine (MR02 BTC: 2039 trade, DD 29% identici); le 3 classi si caricano dal strategy_loader; aggiunte a strategies.yml (BTC+ETH 1h). Nessuna suite di test nel progetto.

Onestà sul target: con 4 fade indipendenti × 2 asset il PnL storico aggregato supera €50/giorno, ma sono backtest a leva 3x su 8 anni con annate eccezionali (ETH 2024). Plausibile ma da confermare col paper trader live prima del capitale reale. DD alto su ETH (MR03 ~66%, come MR01) → leva più bassa consigliata per quell'asset.

File: strategy_research_v2.py, src/strategies/fade_base.py, scripts/strategies/MR0{2,3,7}_*.py (nuovi); strategy_loader.py, strategies.yml, CLAUDE.md (aggiornati). Lezione confermata: ogni edge robusto trovato finora è mean-reversion; ogni variante trend/continuation o oscillatore senza filtro perde netto.


23:45 — Aumentare Acc e ridurre DD (filtro trend + portafoglio)

Obiettivo: alzare accuratezza e abbassare drawdown sulle 4 fade, senza distruggere l'edge né overfittare (ogni leva misurata FULL e OOS).

Diagnosi: perdite/DD concentrati 20182021 (bear/covid/caos vol), su ETH DD pieno 6671%. Banco di prova: scripts/analysis/risk_improvements.py e risk_portfolio.py.

Leve testate:

Leva Esito Motivo
Sizing vol-target (size ∝ 1/dist-SL) Over-size sui trade a stop stretto → DD su, ritorno giù
Skip alta volatilità (ATR% in coda alta) L'alta vol è positiva per le fade (più reversione): Acc e ritorno giù
Filtro trend (|closeEMA200|/ATR > soglia → salta) Non fada trend/crolli estremi: Acc↑ ovunque, DD↓ molto su ETH, OOS regge
Portafoglio equipesato (sotto-conti indipendenti) Curve poco correlate → DD aggregato 14% (full)/10% (OOS) vs 20-70% singolo

Filtro trend — sweep soglia (assoluta in ATR, regola unica per tutte = niente overfit): 3.0 ATR è l'equilibrio (2.0 taglia troppo ritorno). Effetto su config deployata (base → filtro):

Sleeve Acc DD
MR01 ETH 46→55 71→26
MR02 ETH 49→55 42→25
MR03 ETH 49→52 66→34
MR07 ETH 48→54 46→21
MR01 BTC 51→54 32→34*
MR02 BTC 48→52 29→23
MR07 BTC 49→53 25→18
MR03 BTC 47→47 37→37 (filtro OFF)

*MR01 BTC: DD full +2pt ma Acc +3.7 e DD OOS piatto (14.8→15.0). MR03 BTC: il filtro peggiora entrambe (unico sleeve) → lasciato disattivo nello yaml.

Implementazione: helper trend_distance() in fade_base.py; param opzionali trend_max/ema_long (default None = retro-compatibile) in tutte le strategie (MR01/02/03/07); strategies.yml con trend_max: 3.0, ema_long: 200 (eccetto MR03 BTC). Verificato: equivalenza produzione vs ricerca.

Lezione: il modo onesto di ridurre il DD non è strozzare il sizing (peggiora), ma (a) non opporsi a trend estremi e (b) diversificare su strategie scorrelate.