Files
PythagorasGoal/CLAUDE.md
T
Adriano Dal Pastro 87b420f459 docs: monitor loss-guard nel report orario + effetto misurato (stop-loss -67%)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 16:17:05 +00:00

344 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PythagorasGoal — Istruzioni per agenti
## Panoramica
Progetto di ricerca: riconoscimento pattern frattali per trading algoritmico su criptovalute (BTC, ETH). L'obiettivo è arrivare a €50/giorno di profitto partendo da €1.000, entro 68 mesi.
## Stack
- **Linguaggio:** Python 3.11+
- **Package manager:** uv (dipendenze in `pyproject.toml`, lock in `uv.lock`)
- **Dati:** Parquet in `data/raw/` (non committati, ~70 MB)
- **ML:** scikit-learn (GradientBoostingClassifier)
- **Analisi:** numpy, pandas, scipy
- **API dati:** Cerbero MCP su `cerbero-mcp.tielogic.xyz` (Deribit, Bybit, Hyperliquid), ccxt/Binance come fallback
- **Config:** pyyaml per `strategies.yml`
## Struttura
```
src/data/ → download e caricamento dati
downloader.py → download/caricamento parquet (gate: solo strumenti validati)
instruments.py → discovery + validazione strumenti per exchange, registry
src/fractal/ → indicatori frattali (patterns.py, indicators.py, similarity.py)
src/backtest/ → engine di backtesting (engine.py)
src/strategies/ → classe base Strategy ABC + indicatori condivisi
base.py → Strategy, Signal, BacktestResult, YearlyStats
indicators.py → keltner_ratio, detect_squeezes, ema, atr, rv, correlation
src/live/ → paper trading live multi-strategia
multi_runner.py → orchestratore: carica YAML (strategies + pairs), fetch candele, tick worker
strategy_worker.py → worker single-leg: capital, trade log, stato persistente.
Exit guidati da strategia (TP/SL/max_bars via Signal.metadata),
fallback hold_bars/stop -2%. Usa fee_rt della strategia.
pairs_worker.py → worker a 2 GAMBE per PR01 (market-neutral): long A / short B sullo
z-score del log-ratio, exit |z|<=z_exit o max_bars, fee su 2 gambe.
strategy_loader.py → import dinamico classi Strategy da scripts/strategies/
cerbero_client.py → client HTTP per Cerbero MCP (Deribit testnet)
signal_engine.py → squeeze + ML real-time (legacy ML01, ora in waste) + validazione OOS
telegram_notifier.py → notifiche Telegram per trade
src/portfolio/ → portafogli di prima classe (capitale-pool, backtest+live)
base.py → SleeveSpec, Portfolio (.backtest), load_active_portfolio
weighting.py → schemi pesi: equal/cap/inverse_vol/cluster_rp/manual
sleeves.py → builder unificato equity-per-sleeve (fonte unica, parità report)
ledger.py → PortfolioLedger: capitale/PnL/DD/persistenza+resume
runner.py → PortfolioRunner live (data Cerbero v2, sizing, ribilancio)
src/version.py → APP_VERSION (legge il file VERSION) — mostrato nei msg Telegram
src/strategies/fade_base.py → FadeStrategy + helper: atr, trend_distance, hurst_skip_mask (loss-guard)
scripts/strategies/ → strategie con edge validato OOS: FADE (MR01/MR02/MR07),
HONEST (DIP01/TR01/ROT02), PAIRS (PR01), TSMOM + portafogli (PORT01/02/03);
FR01_hurst_calm_fade.py = record ricerca (robusto ma NON deployato)
scripts/portfolios/ → definizioni PORT01-06 (_defs.py) + report run() + hourly_report.py (Telegram)
scripts/waste/ → strategie scartate (W01-W28 + famiglia squeeze SQ/MT/ML/AD/CM/PD)
scripts/analysis/ → ricerca/validazione OOS fee-aware (strategy_research, oos_validation, ...);
regime_fetcher.py + regime_lab.py (DVOL/funding/feature regime per la ricerca)
scripts/bump_version.py, scripts/deploy.sh → versionamento e deploy (bump+commit+rebuild)
VERSION → versione semver (cotta nell'immagine, +1 ad ogni deploy)
strategies.yml → config multi-strategy paper trader
docs/diary/ → diario di ricerca giornaliero
docs/specs/ → specifiche di design
data/raw/ → file .parquet OHLCV (gitignored) | data/regime/ → DVOL+funding+feature (gitignored)
data/instruments_registry.json → allowlist strumenti validati (gate del downloader)
```
## Comandi
```bash
uv sync # installa dipendenze
uv run python -m src.data.downloader # scarica dati storici (solo strumenti validati)
uv run python -m src.data.instruments # (ri)costruisci il registry strumenti validati
uv run python scripts/strategies/MR01_bollinger_fade.py # backtest una strategia (es. fade)
uv run python scripts/strategies/PR01_pairs_reversion.py # backtest pairs market-neutral
uv run python scripts/analysis/strategy_research.py # ricerca strategie fee-aware OOS
uv run python scripts/analysis/oos_validation.py # perche' la famiglia squeeze e' scartata
uv run python scripts/analysis/report_families.py # report per anno di tutte le famiglie
uv run python scripts/analysis/validate_worker_pairs.py # replay worker 2 gambe == backtest
uv run python -m src.live.multi_runner # paper trading live multi-strategia (strategie + pairs)
uv run python scripts/portfolios/PORT06_master_shape.py # report backtest portafoglio (default)
uv run python -m src.portfolio.runner # paper trading a PORTAFOGLIO (capitale pool)
uv run python scripts/analysis/smoke_portfolio.py # smoke live data layer Cerbero v2
uv run python scripts/analysis/regime_fetcher.py # fetch DVOL+funding (Deribit mainnet) -> data/regime/
./scripts/deploy.sh [patch|minor|major] # DEPLOY: bump versione + commit + rebuild Docker
uv run pytest # test
```
> **Deploy.** Il sorgente è **COPY nell'immagine Docker** (non montato) → `docker compose restart`
> NON ricarica il codice: serve **`docker compose up -d --build`** (o `./scripts/deploy.sh`, che bumpa
> la versione, committa e rebuilda). Il volume `data/` persiste → i worker fanno RESUME dello stato.
> La **versione** (file `VERSION`, semver, +1 ad ogni deploy via `deploy.sh`) compare nei messaggi
> Telegram (notifiche trade + report orario) → correli ogni msg al codice che l'ha generato.
## Dati storici
Scaricati e salvati localmente in Parquet. Per rigenerarli:
```python
from src.data.downloader import download_all, load_data
download_all() # scarica BTC + ETH su 5m/15m/1h dal 2018
df = load_data("ETH", "15m") # carica un asset/timeframe
```
Fonte primaria: Cerbero MCP (endpoint `/mcp-deribit/tools/get_historical`).
Token observer: nel file `secrets/observer.token` del progetto CerberoSuite.
### Strumenti & validazione (gate raccolta dati)
`src/data/instruments.py` scopre e **valida** gli strumenti per ogni exchange
implementato — **Deribit** e **Hyperliquid** (esclusi Alpaca/stocks e **Bybit**,
il cui feed testnet è farlocco). Per ogni perpetuo enumera via `get_instruments`
/`get_markets` e verifica sui **dati storici realmente raccoglibili**:
esistenza, congruenza OHLC, not-flat (scarta contratti morti), liquidità (volume
daily) e **congruenza prezzo cross-exchange** (scostamento dalla mediana del
base-coin ≤ 5% → scarta outlier come `SOL-PERPETUAL`=9.6 vs SOL reale ~82).
Output: `data/instruments_registry.json` (strumenti validi, timeframe, start-date).
**Gate:** `_download_cerbero_range` rifiuta gli strumenti non validati (override
`allow_unvalidated=True` solo per casi eccezionali). Rigenera con
`python -m src.data.instruments`.
> **NB testnet.** Il token Cerbero punta a testnet; la congruenza cross-exchange
> è il filtro che distingue i feed realistici (Deribit, Hyperliquid) da quelli
> farlocchi (Bybit). Simboli Deribit: BTC/ETH = `<COIN>-PERPETUAL` (inverse);
> alt = `<COIN>_USDC-PERPETUAL` (lineari USDC). Registry attuale: Deribit 18/106,
> Hyperliquid 66/74 validi (major liquidi: BTC dal 2018, alt dal 2022).
## Strategie attive
> **LEZIONE CRITICA (2026-05-28).** L'intera famiglia squeeze-breakout (SQ01-04,
> MT01, ML01, AD01, CM01, PD01) è stata **scartata in `scripts/waste/`**: le
> accuratezze storiche 76-82% erano un **artefatto di look-ahead**. Quei backtest
> decidono la direzione con `sign(close[i]-close[i-1])` (la candela di breakout `i`)
> ma entrano a `close[i-1]` — cioè comprano *prima* della candela che usano per
> scegliere la direzione. Dal vivo il worker scopre il breakout solo a `close[i]`
> ed entra lì: l'edge sparisce (win-rate ~47%, lancio di moneta). Sotto ingresso
> onesto e fee reali **tutte perdono, anche a fee zero**. Inoltre i breakout
> *rientrano* (mean-reversion > continuation). Vedi `scripts/analysis/oos_validation.py`
> e `intrabar_test.py`.
Tutte le strategie estendono `src.strategies.base.Strategy`
(`generate_signals() → backtest()`). Le strategie mean-reversion condividono
`src.strategies.fade_base.FadeStrategy` (backtest intrabar TP/SL/max_bars).
**Strategie con edge netto validato OOS fee-aware (tutte fade/mean-reversion):**
| Codice | Nome | Meccanismo | Edge OOS netto (1h, fee 0.10% RT) | DD | Note |
|--------|------|-----------|-----------------------------------|----|------|
| **MR01** | Bollinger Fade | banda std attorno a SMA | BTC +201% / ETH +1238% | 15-72% | Fada la banda, TP alla media, SL ad ATR |
| **MR02** | Donchian Fade | estremi canale H/L | BTC +172% / ETH enorme | 30-42% | Fada la rottura del canale, TP al centro |
| **MR07** | Return Reversal | z dei rendimenti di barra | BTC +105% / ETH +195% | 25-46% | Fada il movimento estremo, exit in ATR; esposizione ~8% |
> **MR03 Keltner Fade** spostata in `scripts/waste/`: era la fade più debole
> (BTC Sharpe 1.22, il filtro trend la peggiorava) e ridondante con MR01 (stessa
> idea di banda). Rimuoverla dal portafoglio ne ha *migliorato* le metriche.
> La funzione `keltner_fade` resta in `strategy_research_v2.py` come record.
**Lezione confermata:** l'edge è sempre *mean-reversion* (i breakout rientrano).
Il trend-following (Donchian trend, RSI cross) e gli oscillatori senza filtro
(RSI revert, ADX-filtered fade) perdono netti → restano scartati.
Ogni strategia è robusta su **tutta** la sua griglia parametri (entrambi gli asset
→ tutte positive OOS) e su **tutte** le fee 0.00-0.20% RT (margine ampio).
MR01 validato col worker reale: BTC +196% / ETH +251% OOS (nov 2023→mag 2026).
Ricerca completa: `scripts/analysis/strategy_research.py` (MR01) e
`scripts/analysis/strategy_research_v2.py` (MR02/MR03/MR07).
Validazione live-path: `scripts/analysis/oos_validation.py`.
**Filtro trend (riduzione DD + aumento Acc).** Tutte le fade accettano i parametri
opzionali `trend_max` / `ema_long`: saltano i segnali quando il prezzo è troppo
esteso rispetto al trend di fondo (`|close EMA(ema_long)| / ATR(14) > trend_max`),
cioè quando si starebbe fadando un trend/crollo estremo. Con `trend_max=3.0`,
`ema_long=200` (default in `strategies.yml`): accuratezza su tutti gli sleeve
e DD giù drasticamente su ETH (MR01 71%→26%, MR02 42%→25%, MR03 66%→34%,
MR07 46%→21%), edge OOS confermato (vedi `scripts/analysis/risk_management.py`).
Unica eccezione: MR03 BTC, dove il filtro peggiora entrambe → lasciato disattivo.
Leva non robusta scartate: vol-target sizing e skip-alta-volatilità (peggiorano).
**Loss-guard Hurst (ATTIVO LIVE, 2026-06-02).** Le fade accettano `hurst_max`: saltano i segnali in
regime PERSISTENTE/trending (rolling-Hurst ≥ soglia), dove si concentrano stop-loss e perdite
(diagnosi: stop-rate 43% per hurst>0.55 vs 21% anti-persistente; i peggiori 1% trade hanno hurst
medio 0.61). Helper `src.strategies.fade_base.hurst_skip_mask` (rolling-Hurst causale **dalle sole
close** → nessun feed esterno; step=6 per velocità live). **`hurst_max=0.55` attivo sulle 6 fade in
`_defs.py`**: il test decisivo a livello PORT06 lo conferma — **FULL DD 4.10%→2.39% (quasi dimezzato),
Sharpe 6.62→6.76, OOS Sharpe 8.89→9.15**. È l'UNICO meccanismo anti-perdite che supera il gate (ADX,
vol-expansion/vratio, efficiency-ratio, time-stop, vol-target FALLISCONO: tagliano i winner insieme ai
loser; i claim esterni ADX/ATR-ratio non si replicano su queste fade crypto). NB: il filtro agisce solo
sul path LIVE (`spec.params`); il backtest canonico (`build_everything`/regression-lock) NON è filtrato
→ il live farà MEGLIO del backtest sul DD. Ricerca: `scripts/analysis/fade_lossguard_workflow.js`,
diagnosi `fade_loss_by_regime.py`, diario `docs/diary/2026-06-02-fade-lossguard.md`.
**Effetto misurato (backtest):** stop-loss fade 67% in numero (1881→621), perdite totali 68%, coda
61%→−48% (lo stop-RATE per-trade scende poco, 42→38%: il guard lavora riducendo l'ESPOSIZIONE nel
regime tossico, non rendendo sicuro ogni trade). **Monitor live:** `hourly_report.py` traccia lo
stop-rate fade PRIMA/DOPO l'attivazione (14:34 UTC del 2026-06-02) e dà il verdetto su Telegram quando
il campione DOPO ≥30 (già confermato: stop-rate live PRIMA 42% == backtest 42.1%).
**Portafoglio.** Diversificare su sotto-conti indipendenti equipesati (le 4 strategie
× BTC/ETH, pos 0.15 ciascuno) abbatte il DD aggregato: ~14% full / ~10% OOS sul
paniere di 8 sleeve, contro il 20-70% del singolo. È la vera leva anti-drawdown.
**Combinare le due famiglie (fade + honest).** Le fade (reversione intraday 1h) e le
honest (DIP/TR/ROT trend+rotazione multi-crypto) sono **quasi scorrelate**
(correlazione cross-famiglia ~0.05). Combinarle in un unico portafoglio migliora il
rischio/rendimento rispetto a ciascuna famiglia da sola: equal-weight dei 9 sleeve
→ DD 5.2% full / 4.7% OOS e Sharpe 4.23 full / 4.33 OOS (vs honest-only 12.6% DD /
2.20 Sharpe e fade-only 8.2% DD / 4.09 Sharpe), CAGR ~47% mantenuta. Studio in
`scripts/analysis/combine_portfolio.py`.
**ROT02 — riduzione DD (top_k 2→3).** La rotazione dual-momentum honest concentrava
il book su 2 soli asset (DD 40%). Diversificare su 3 (`top_k=3`) dimezza quasi il DD
(40%→26%) e *alza* pure il ritorno full (+1095%→+1303%, ret/DD da 27 a 50); il
vol-target abbassa il DD ma sacrifica ritorno, quindi si tiene top_k=3 senza VT.
Applicato a `ROT02_dual_momentum.py` e a `_rot_daily_equity` (usata dai portafogli).
**Portafogli pronti (artefatti accorpati e migliorati).** Oltre a `PORT01` (solo
honest), due script in `scripts/strategies/`:
- `PORT02_fade_master.py` — le 3 fade × BTC/ETH accorpate (6 sleeve, filtro trend),
equal-weight daily: DD ~8.2% full / 5.9% OOS, Sharpe 3.95/4.09, CAGR ~46%.
- `PORT03_all_master.py` — portafoglio MASTER (fade + honest, 9 sleeve). Due varianti:
`equal` (massimo Sharpe: DD 5.2%/4.7% OOS, Sharpe 4.23/4.33) e `5050` fra le due
famiglie (minimo DD: 5.0% full / 4.5% OOS). È la configurazione consigliata.
Come `PORT01`, sono meta-portafogli (script `run()` di report), non `Strategy` con
`generate_signals`, quindi non nel `strategy_loader`.
**Esplorazione famiglie alternative (branch `strategy_explore`, 2026-05-29).** Esplorate
9 famiglie nuove con agenti paralleli su harness onesto condiviso
(`scripts/analysis/explore_lab.py`). 7 sono rumore (rifiutate: stagionalità oraria/mensile,
cross-sectional reversal, opening-range breakout, lead-lag BTC→alt, continuation intraday —
quest'ultima riconferma la dominanza mean-reversion). Due edge reali:
- **PR01 Pairs** (`scripts/strategies/PR01_pairs_reversion.py`): spread reversion
market-neutral sul log-ratio z-score, **config UNIVERSALE** `n=50 z_in=2.0 z_exit=0.75
max_bars=72` (anti-overfit, niente tuning per-coppia). **5 coppie robuste**: ETH/BTC
(Sharpe 4.36), LTC/ETH (3.08), ADA/ETH (2.69), BTC/LTC (2.36, robusta anche 4h), ETH/SOL
(1.96, la più debole). Pattern: sempre alt-liquido vs major. Plateau confermato
(heatmap 20/20 Sharpe>1) + walk-forward (ETH/BTC 11/12 finestre+). **BNB/ETH scartata**
(overfit). Corr col mercato ~0.02-0.08. Fee su **2 gambe**: worker live implementato
(`src/live/pairs_worker.py`, sezione `pairs:` in `strategies.yml`). LOGICA validata
(`validate_worker_pairs.py`: replay == backtest ESATTO). LIVE (`live_smoke_pairs.py`,
smoke reale Cerbero): **tutte e 5 le coppie con feed live fresco**. Naming Deribit:
BTC/ETH = `<COIN>-PERPETUAL` (inverse); alt = `<COIN>_USDC-PERPETUAL` (lineari USDC,
storia dal 2022). Trappola: `LTC-PERPETUAL`/`SOL-PERPETUAL` danno vuoto/dati errati →
usare sempre `_USDC-PERPETUAL`. Resta da verificare solo liquidità/fill in esecuzione.
Verifica edge: `pairs_research.py`.
- **TSM01** (`scripts/analysis/tsmom_research.py`): TSMOM multi-orizzonte 3/6/12m + risk-off,
**gross 0.30**, distinto da ROT02 (corr 0.62), DD 15-22%, mai un anno negativo. Robusto
(36/36 config OOS+) ma diversificatore, non motore di ritorno (rende meno di ROT02).
Aggiungere i **5 pairs** al MASTER (quasi scorrelati, ~0.02-0.09) è il free-lunch più
grande (`scripts/analysis/combine_v2.py`). **Numeri sobri onesti** (l'OOS singolo 2024-25
è regime calmo → ottimistico ~50%): worst-DD su 90g rolling **~6%** (non 2.3%), Sharpe
atteso **~5** (mediana semestrale), ogni anno positivo dal 2021, regge **leva 2x +
slippage doppio** (CAGR 36%, Sharpe 5.1). Config robusta raccomandata: **MASTER-esteso
equal-weight, leva 2x, cap pairs ~30-35%** (i pairs sono ~57% del rischio; worker live a
2 gambe implementato, validato e con feed live su tutte e 5 le coppie — resta da
verificare liquidità/fill in esecuzione reale). La confluenza multi-TF è stata SCARTATA (overfit).
**Pattern del segnale per FORMA (branch `shape_patterns`, 2026-05-29).** Esplorate 5 famiglie
di *shape forecasting* con agenti paralleli su harness onesto (`scripts/analysis/shape_lab.py`:
analog kNN causale, no-look-ahead verificato). **4/5 sono RUMORE** (riconfermano la dominanza
mean-reversion): analog kNN sulla forma grezza (solo BTC-overfit), encoding candele
UP/DOWN/DOJI+body/shadow (hit-rate ~50%), DTW+template geometrici (DTW *peggiora* l'euclidea;
template overfit), PIP/pivot/zig-zag (0/48 robuste). Vedi `scripts/analysis/shape_*_research.py`.
- **SH01 Shape-ML** (`scripts/strategies/SH01_shape_ml.py`): UNICO edge. Una LogisticRegression
legge 17 feature di forma (body/shadow, rendimenti, pendenza/curvatura, pos max/min, RSI,
estensione) e predice il segno del rendimento a H barre in **walk-forward** (scaler+modello
solo sul passato, no leakage). Config **W24 H12 th0.58**. A differenza dello squeeze
**regge fee 0.20% RT**. Win-rate ~50% → l'edge è nell'**asimmetria**, non nella frequenza.
Validazione (`scripts/analysis/shape_ml_validate.py`): BTC robusto OVUNQUE (expanding +219%/
OOS +42% Sharpe 2.72 8-9 anni; rolling 2y +166%/+96%; stress 2x+slippage OK), ETH/ADA
robusti solo expanding (secondari), LTC/SOL/XRP scartati. Griglia: **5/27 celle robuste su
cresta stretta W24/H8-12** → overfit moderato, scelta la config conservativa. **Valore vero:
diversificatore** (corr +0.08 col MASTER); aggiungerlo migliora l'OOS del MASTER (Sharpe
4.33→5.10, DD 4.7%→4.2%). NON motore standalone. **LIVE (2026-06-01): gira come StrategyWorker
reale** (vedi fix wiring sotto in SCOPE LIVE). Diario: `docs/diary/2026-05-29-shape.md`.
**ARGO / GEX opzioni (analisi 2026-06-01, SCARTATO).** Valutato ARGO (lettura del gamma-exposure dei
dealer Deribit) come filtro di regime. Esito **NO-GO**: il net-GEX si calcola live (Deribit mainnet
public API, OI reale ~368k contratti, DVOL/funding storici gratis) ma **lo storico per-strike dell'OI
non è gratuito → non backtestabile OOS** (stesso muro delle opzioni W18/19/21). Niente evidenza crypto,
segno fragile, mercato dominato dai perp. Diario `docs/diary/2026-06-01-argo-gex-feasibility.md`.
**Frattali del segnale × Regime ARGO (ricerca 100 agenti, 2026-06-02, RECORD).** Cercata una strategia
che combini un segnale frattale (Hurst/Higuchi/Williams/analog) con un gate di regime (DVOL/VRP/funding).
Infrastruttura riusabile: `scripts/analysis/regime_fetcher.py` (DVOL+funding da Deribit mainnet →
**`data/regime/`**, NON `data/raw/` che è solo OHLCV) e `regime_lab.py` (feature regime+frattali causali,
cache, harness netto-OOS). Esito: **15 strategie robuste e causali, ma NESSUNA batte/migliora il PORT06**
(diversificatori sovrapposti alle fade). **Finding: il prior ARGO "VRP>0=range=fade" è SMENTITO**
l'edge è su **VRP<0 + DVOL bassa**. Il vincitore `FR01_hurst_calm_fade.py` è robusto ma DILUISCE il
PORT06 (OOS Sharpe 8.89→8.72) → **non deployato** (in `scripts/strategies/` ma NON in MODULE_MAP/yml).
Il sottoprodotto utile è stato il **loss-guard Hurst** (vedi sopra), che invece MIGLIORA il PORT06.
Diario `docs/diary/2026-06-02-fractal-argo-search.md`.
**Metodologia obbligatoria per ogni nuova strategia** (per non ripetere l'errore squeeze):
1. Ingresso eseguibile: direzione e prezzo decisi con dati **fino a `close[i]`**, mai `close[i-1]` con direzione da `i`.
2. Backtest **NETTO** dopo fee realistiche Deribit (**0.10% RT** taker, non 0.20%) + leva.
3. Validazione **out-of-sample** (held-out) + robustezza su griglia parametri + sweep fee.
4. Crea script in `scripts/strategies/`, aggiungi a `MODULE_MAP` (`strategy_loader.py`) e a `strategies.yml`.
Strategie scartate storiche in `scripts/waste/` (W01-W28 + la famiglia squeeze).
**Verso €50/giorno.** Con 4 strategie indipendenti (MR01/MR02/MR03/MR07) × 2 asset
(BTC/ETH) su €1000 ciascuna, il PnL medio storico aggregato è ben oltre €50/giorno;
ma quei numeri sono backtest a leva 3x su 8 anni e includono anni eccezionali (es.
ETH 2024). Stima onesta: il target è *plausibile* su un portafoglio diversificato di
queste fade, ma va confermato col paper trader live prima di rischiare capitale reale.
## Portafogli
- Un `Portfolio` è un oggetto di prima classe (`src/portfolio/`) con definizione (sleeve + schema pesi) e due facce sulla **STESSA** definizione: `.backtest()` (riusa il builder unico di `sleeves.py` → parità esatta con `report_families`) e live (`PortfolioRunner`: capitale pool condiviso, sizing per peso, ribilancio giornaliero, ledger aggregato in `data/portfolios/{code}/`).
- **Schemi peso:** `equal` (default), `cap` (tetto per famiglia, es. pairs 33% — config raccomandata), `inverse_vol`, `cluster_rp` (equal fra cluster naturali poi inverse-vol intra-cluster), `manual`. Definiti in `weighting.py`; la chiave cap è la famiglia (PAIRS/FADE/HONEST/SHAPE/TSM).
- **Default `portfolios.yml`:** PORT06 (master+shape), `weighting=cap pairs 0.33`, leva 2x, ribilancio 1D. Backtest PORT06: FULL Sharpe 6.07 / OOS Sharpe 8.19, DD 4.9% full / 2.3% OOS.
- **Data layer Cerbero v2:** `get_historical_v2` unificato + `get_instruments` (naming robusto) + `get_ticker_batch`. Trading su Deribit.
- **SCOPE LIVE (fase 2 completata):** il runner esegue TUTTI gli sleeve di PORT06. Worker: single `StrategyWorker` (fade MR01/02/07, DIP01, **e SH01**), `PairsWorker` (PR01 2 gambe), e i multi-asset dedicati `BasketTrendWorker` (TR01 4h), `RotationWorker` (ROT02 1d), `TsmomWorker` (TSM01 1d). Il runner fetcha 1h da Cerbero v2 e **resampla a 4h/1d** (lookback dimensionato sui daily: TSM01 usa 252g). Validazione: runner pool/ribilancio/ledger == backtest (`validate_portfolio_runner.py`, identico); worker multi-asset == reference (`validate_honest_workers.py`: TSM01 esatto, ROT02 +1303% canonico, TR01 stesso ordine — differenza di convenzione capitale-unico vs media-equity).
- **FIX SH01 wiring (2026-06-01).** SH01 gira come **`StrategyWorker` NORMALE** (NON il vecchio `MLWorkerWrapper` di `multi_runner`, che usava il `SignalEngine` **squeeze SCARTATO**: apriva senza metadata ed usciva a `hold_bars=3`, ignorando del tutto SH01_shape_ml). `SH01_shape_ml.generate_signals` fa il walk-forward (retraining) internamente ad ogni tick ed emette `metadata.max_bars=12` → exit a orizzonte via `StrategyWorker.tick`. Serve ≥4000 barre 1h (`_ML_LOOKBACK_DAYS=365`). Vedi `docs/diary/2026-06-01-sh01-wiring-squeeze-bug.md`.
- **Altri fix StrategyWorker (2026-06-01).** Exit a orizzonte puro per strategie senza TP/SL (`elif self.max_bars`, SH01 esce a H=12 non hold_bars=3); `is_win = net > 0` (win NETTO fee, non lordo); filtro `min_tp_frac` (salta micro-scalp col TP entro le fee); loss-guard `hurst_max=0.55` sulle fade (vedi sopra).
- **Exit intrabar (fase 3, risolto):** lo `StrategyWorker` ora esce sui TP/SL toccati INTRABAR (high/low della barra, al livello, SL prioritario) come il backtest — non più solo sul close. Allinea fade/DIP01 live al backtest intrabar (`tests/portfolio/test_intrabar_exit.py`). Caveat residuo onesto: nel paper trading l'high/low usato è quello della barra in corso al poll; su un fill reale conterebbe il momento del tocco.
- **Limite noto:** al ribilancio le posizioni APERTE restano sul loro notional (non travasate); fedele al backtest daily-rebalanced entro il turnover infragiornaliero.
## Multi-Strategy Paper Trader
Orchestratore che esegue N strategie in parallelo su dati live Cerbero, ognuna con €1000 USDC virtuali indipendenti.
**Config:** `strategies.yml` — due sezioni: `strategies` (single-leg: fade/honest) e
`pairs` (a 2 gambe). Attive: 6 fade (MR01/MR02/MR07 × BTC/ETH) + 5 coppie PR01.
**Due worker:** `strategy_worker.py` (single-leg) e `pairs_worker.py` (2 gambe,
long A / short B sullo z-score del log-ratio, fee su 2 gambe).
**Persistenza:** `data/paper_trades/{worker_id}/` con `trades.jsonl` (append-only) + `status.json` (resume al restart).
**Hot-add:** aggiungi riga YAML → `docker compose restart` → storico intatto.
**Exit strategia:** se un `Signal` porta `tp`/`sl`/`max_bars` in `metadata` (come le fade), il worker esce su take-profit/stop-loss/time-limit; i pairs escono su |z|≤z_exit o max_bars.
**Naming Deribit (feed live):** major = `<COIN>-PERPETUAL` (inverse); alt = `<COIN>_USDC-PERPETUAL` (lineari USDC). Vedi INSTRUMENT_MAP in `multi_runner.py`.
**Notifiche:** Telegram per ogni trade (richiede `.env` con `TELEGRAM_BOT_TOKEN` e `TELEGRAM_CHAT_ID`).
## Convenzioni
- Strategie in `scripts/strategies/` con codice univoco (MR01, ...).
- Script scartati in `scripts/waste/` (W01-W28 + famiglia squeeze).
- Diario in `docs/diary/YYYY-MM-DD.md`. Aggiornare dopo ogni esperimento significativo.
- Nessun dato sensibile nei commit (token, chiavi API). Usare `.gitignore`.
- Verificare sempre assenza di data leakage prima di fidarsi dei risultati. In particolare: `returns[i-w : i]` include `close[i]` che è un candle nel futuro — usare `returns[i-w : i-1]`.
## Attenzione
- **Data leakage:** è stata trovata e corretta nello script 05. Ogni volta che si usano rendimenti logaritmici (`np.diff(np.log(close))`), ricordare che `returns[k]` usa `close[k+1]`. I feature devono fermarsi a `returns[i-2]` se il prezzo corrente è `close[i-1]`.
- **Fee:** Deribit perp reale = taker ~0.05%/lato (**0.10% round-trip**), maker ~0%. Usare 0.10% RT come baseline (lo 0.20% storico era pessimista 2x). Includere SEMPRE nel backtest: sono vincolo di prim'ordine, molte operazioni = morte per fee. Il worker usa `strategy.fee_rt` (MR01 = 0.001).
- **Leva:** testato con 3x. Aumentare a 5x migliora i rendimenti ma raddoppia il drawdown.
- **GBM:** GradientBoostingClassifier di scikit-learn. Ensemble di alberi decisionali sequenziali. Walk-forward per evitare leakage temporale.
- **Cerbero `get_historical` (fix 2026-05-28):** `end_date` come data nuda è inclusivo dell'intera giornata fino all'ultima candela chiusa (es. `end=oggi` arriva fino ad ora, non più a mezzanotte); accettati anche timestamp con orario (`...T14:00:00`, naive=UTC); nessun cap a ~5000 righe (paginazione interna). Il client passa già `end=oggi`, ora corretto. Prima del fix il paper trader restava a zero trade perché il feed era fermo a mezzanotte.
- **Dati ETH Deribit 15m:** 14-30%/anno di candele *flat* (O=H=L=C, volume 0, run fino a ~54h) per bassa liquidità del perpetuo. Verificato (2026-05-28): escluderle NON cambia i backtest (Δacc ≤0.5pp) → edge robusto. Resta un caveat operativo (slippage/fill in trading reale, irrilevante per paper). BTC pulito eccetto picco ~8% nel 2024.