21d3ba609d
Trovate e promosse 3 strategie con edge netto distinto da MR01, stessa metodologia (ingresso close[i], netto fee 0.10% RT + leva 3x, OOS ultimo 30%, robustezza su griglia + sweep fee 0.00-0.20%): - MR02 Donchian Fade: fade rottura canale H/L, TP al centro. BTC +172% OOS. - MR03 Keltner Fade: canale ATR su EMA (indipendente da Bollinger). BTC +112%. - MR07 Return Reversal: fade movimento di barra estremo (z dei rendimenti). BTC +105%. Tutte positive netto OOS su entrambi gli asset e su tutto lo sweep fee, anche 0.20% RT pessimista (validate anche via oos_validation live-path). Scartate MR04 (= MR01 riparametrizzato), MR05 (ADX non robusto), MR06 (RSI2 ETH neg). Base condivisa fade_base.FadeStrategy (backtest intrabar TP/SL/max_bars). Aggiunte a strategy_loader e strategies.yml (BTC+ETH 1h). Ricerca in strategy_research_v2.py. Diario e CLAUDE.md aggiornati. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
148 lines
8.3 KiB
Markdown
148 lines
8.3 KiB
Markdown
# 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.00–0.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.
|