cff0d08fca
Filtro opzionale trend_max/ema_long su tutte le fade (MR01/MR02/MR03/MR07): salta i segnali quando |close-EMA200|/ATR supera la soglia (non fadare un trend o crollo estremo). Con trend_max=3.0 (default in strategies.yml): accuratezza su e DD giu' su 7/8 sleeve, drastico su ETH (MR01 71->26%, MR02 42->25%, MR03 66->34%, MR07 46->21%); edge OOS confermato. MR03 BTC: filtro disattivo (unico sleeve dove peggiora entrambe). Scartate come non robuste: vol-target sizing e skip-alta-volatilita' (peggiorano sia Acc che DD). Aggiunto modello di portafoglio equipesato su sotto-conti indipendenti: DD aggregato ~14% full / ~10% OOS sul paniere di 8 sleeve, contro 20-70% del singolo -> vera leva anti-drawdown. Banco di prova: scripts/analysis/risk_improvements.py, risk_portfolio.py. Helper trend_distance() in fade_base. CLAUDE.md e diario aggiornati. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
194 lines
10 KiB
Markdown
194 lines
10 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.
|
||
|
||
---
|
||
|
||
### 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 2018–2021 (bear/covid/caos vol), su ETH DD
|
||
pieno 66–71%. 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** (`\|close−EMA200\|/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.
|