Files
PythagorasGoal/CLAUDE.md
T
Adriano Dal Pastro c21e1dc635 docs: timing-sweep pairs/honest (NO deploy) + stato mainnet + vincolo feed v2
- CLAUDE: feed ETH ancora congelato 36h+; vincolo Cerbero v2 (serve SOLO
  5m/15m/1h, 30m/10m -> 400, legacy 404; _SUBHOURLY "30m" speculativo mai
  testato); esito timing-sweep (5m non conviene: regime recente peggiore +
  flat ETH 29%; gate full+OOS necessario ma non sufficiente); stato mainnet
  (token in .env.mainnet verificato is_mainnet=True, conto VUOTO = blocco)
- spec mainnet-microtest: blocco STATO 2026-06-14, .env.mainnet separato +
  servizio dedicato, checklist aggiornata (token done, funding = blocco)
- nuovo diario 2026-06-14-timing-sweep-pairs-honest.md + harness riusabile
  scripts/analysis/timing_sweep_pairs_honest.py
- .gitignore: .env.mainnet (token mainnet mai in git)

Nessuna modifica a codice/config live: PORT06 invariato (19 sleeve).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 20:19:56 +00:00

628 lines
61 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): dati + ordini
(place_order market/reduce_only, get_trade_history, get_positions)
execution.py → ExecutionClient: esecuzione REALE su Deribit (shadow). notional→
amount (lineari USDC + inverse), open/close_amount reduce-only,
verifica sul trade (order_id), fee reali dai trades[]
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),
exit close-confirm SL via param sl_confirm_atr (EXIT-16)
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);
exit_lab.py + exit_policies/ (harness ricerca exit: cache segnali, train/OOS);
options_fetcher.py + options_chain.py (storico opzioni REALE da cerbero-bite);
option_overlay_lab.py (overlay opzioni prezzato; mr02eth_port06_gate.py = gate swap-sleeve);
pairs15m_*.py (gate/flatcheck/smoke ETH/BTC 15m -> sleeve PR_ETHBTC_15M)
scripts/games/ → gioco "Blind Traders": 100 agenti ciechi su BTC/ETH anonimi (engine.py,
arena.py epoche+cull, agent_brief.py digest, run_game.py). Origine del BLEND 15m.
Varianti: options_* (strutture in opzioni, BS+skew+DVOL; opt_calibrate da
cerbero-bite), session_* (pattern orari), grid_* (griglie da STRATEGIA_GRIGLIA.md;
gate scripts/analysis/grid_game_gate.py). arena: GAME_NO_LIVE=1 vieta le
strategie gia' deployate (pairs, fade zscore/breakout/momentum)
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/options/ → storico catena opzioni per-strike (bid/ask/IV/greche/OI) importato da cerbero-bite (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/live_exec_smoke.py # smoke ESECUZIONE reale (ordine→verifica→fee, testnet)
uv run python scripts/analysis/live_shadow_smoke.py # smoke catena shadow nel worker (open/close reali)
uv run python scripts/analysis/regime_fetcher.py # fetch DVOL+funding (Deribit mainnet) -> data/regime/
uv run python scripts/analysis/exit_lab.py # (ri)costruisci cache segnali exit-lab + parity check
uv run python -m src.live.dashboard --port 8787 # dashboard web (servizio compose 'dashboard', porta 8787)
uv run python scripts/analysis/ledger_vs_backtest.py --since 2026-06-13 # report fuga di esecuzione (reale vs sim)
./scripts/deploy.sh [patch|minor|major] # DEPLOY: bump versione + commit + rebuild Docker
uv run pytest # test
```
> **Dashboard web (`src/live/dashboard.py`, servizio compose `dashboard`, porta 8787).**
> Stdlib http.server, legge `data/`: equity (restyling), PnL totale e per-strategia,
> **grafico equity per famiglia**, trade attivi in tempo reale (entry/mark/PnL **REALI** —
> il feed sim-decisione testnet è dislocato, vedi sotto) + chiusi, modal per-strategia
> (curva reale vs sim + scheda `strategie_attive.html`), **area PAPER distinta** (equity
> propria dei 4 multi-asset), strategie **ritirate** marcate (staleness >30min) + versione
> sistema/strategia. Nessuna auth → solo rete interna. `docker compose up -d --build dashboard`.
> **Cron host (monitoraggio, già schedulati):** `hourly_report.py` (orario), `drift_monitor.py`
> (07:15), `reconcile_account.py --telegram` (:40), **`ledger_vs_backtest.py --since 2026-06-13
> --telegram` (08:30)** = il GATE per scalare il capitale (per gli sleeve eseguiti sim==backtest
> per costruzione → reale-vs-sim = fuga di esecuzione: slippage+fee+netting; verdetto 🟢/🟡/🔴).
> **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).
**SWAP filtri fade: hurst→trend (2026-06-07).** Il gate PORT06 sul path live
(`scripts/analysis/trendmax_port06_impact.py`, parità 1.00000 col canonico) ha mostrato che
**post-EXIT-16 il loss-guard Hurst è ridondante-dannoso**: EXIT-16 ha eliminato i wick-stop che
hurst evitava → gli ingressi saltati (66% delle barre!) sono in maggioranza tornati vincenti.
Su PORT06: LIVE hurst-only FULL Sh 7.23 / OOS 9.35-DD 1.68 vs **TREND-ONLY 7.89 / 9.91-DD 1.20**
(domina su tutte le metriche; hurst+trend insieme over-filtra: 7.11, metà dei trade; plateau
trend_max 2.5/3.0/3.5 robusto). TREND-ONLY è la config che la ricerca EXIT-16 aveva davvero
promosso (entries trend-filtrate, no hurst) e che il live non aveva mai eseguito. **Live ora:
`trend_max=3.0, ema_long=200` nelle 6 fade di `_defs.py`, `hurst_max` rimosso** (la maschera resta
in `fade_base`). Monitor: `hourly_report` traccia lo stop-rate per epoca PRE→HURST→TREND.
Lezione: ri-gateare ogni filtro quando cambia l'exit engine. Diario
`docs/diary/2026-06-07-trendmax-gate.md`. Il paragrafo sotto resta come record storico:
**Loss-guard Hurst (storico: live dal 2026-06-02 al 2026-06-07, poi sostituito dal filtro trend).** 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%).
**FIX EXIT-16 live — confirm su barra COMPLETATA (2026-06-05).** Il worker valutava il confirm-SL
sul prezzo della candela IN FORMAZIONE ad ogni poll → reintroduceva la wick-sensitivity che EXIT-16
elimina (audit: 2 stop su 3 del crash ETH erano wick-stop che il backtest non avrebbe preso in quel
momento). Ora `tick` valuta il confirm SOLO sul close dell'ultima barra completata (detection: la
riga -1 del df è la candela in corso finché `now < ts[-1]+bar_ms`), buf dall'ATR della stessa barra;
fill al prezzo corrente (≈ stress lag_close_exit, OK in exit-lab); TP intrabar invariato. La concausa
feed-gap NON è mitigabile lato exit (fill reali ≈ sim) e l'entry-guard post-flat è BOCCIATA (skippare
i segnali dopo barre flat PEGGIORA tutti gli sleeve ETH: la candela-gap è l'overshoot che la fade
fada). Aggiunto alert Telegram `STALE_FEED` (≥2 barre 1h flat → notifica + gap % al risveglio, solo
osservabilità). Diario `docs/diary/2026-06-05-confirm-sl-forming-bar.md`.
**EXIT-16 close-confirm SL (ATTIVO LIVE, 2026-06-04; esteso a DIP01 il 2026-06-07).** Le fade E
DIP01 accettano `sl_confirm_atr` (live: 0.5 in `_defs.py`): lo SL **intrabar è disattivato** e lo stop scatta solo se il **CLOSE** della barra
sfonda `sl ∓ 0.5·ATR14`, con uscita al close (TP intrabar al livello e max_bars invariati; in modalità
confirm il TP ha priorità nel bar). Scoperta della ricerca exit-lab (34 agenti, 23 famiglie esplorate +
10 verifiche avversariali + test PORT06): **gli stop intrabar da wick sono falsi negativi** — l'overshoot
che buca lo stop e rientra è esattamente il movimento che la fade fada. Verificato: indipendente dal
loss-guard Hurst, plateau buffer 0.4-1.0, regge fee 2x/lag/slippage, coda ≈ base nei crash veri (FTX:
+2.4% vs 39% del no-SL puro). **PORT06: FULL Sharpe 6.47→7.84 DD 4.10→2.60, OOS Sharpe 8.82→10.06 DD
1.30→1.15.** La famiglia "cavalca il prezzo" (trailing/ride/partial-runner, 15 varianti) è invece tutta
SCARTATA: oltre il TP=media non c'è coda (4ª conferma). Collaterali: l'engine intrabar filla gli stop
"al livello" anche su gap-through (54% dei casi per stop tight) → bias PRO stop-stretti nelle ricerche
future; mai deployare strategie con `sl=0` (il fallback 2% del worker non si applicherebbe). Harness
riusabile `scripts/analysis/exit_lab.py` + policy in `exit_policies/`. Implementazione: `fade_base.backtest`
+ `StrategyWorker.tick` (param `sl_confirm_atr`, None = comportamento storico; il backtest canonico
`build_everything` resta NON filtrato → il live farà meglio del backtest, come per il loss-guard).
Diario `docs/diary/2026-06-04-exit-lab.md`.
**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`.
**Storico opzioni REALE da cerbero-bite (2026-06-09, il muro ARGO è caduto per il periodo recente).**
Il container **cerbero-bite** (accanto, `/opt/docker/cerbero-bite`) accumula in continuo lo **storico
per-strike** della catena opzioni Deribit (BTC+ETH) nella tabella `option_chain_snapshots` del suo
SQLite (`bite-data:/app/data/state.sqlite`, root-only): bid/ask/mid/**IV per-strike**/greche/OI/volume,
**dal 2026-05-01**, cadenza ~12 min (~110k righe/asset). È esattamente il dato che ARGO/W18-21
credevano non-gratuito. `scripts/analysis/options_fetcher.py` lo importa via `docker exec`
`data/options/{eth,btc}_chain.parquet`; `scripts/analysis/options_chain.py` (`OptionChain`) espone
loader + `skew_curve()` / `premium_levels()` (aggregati ROBUSTI) + `quote()` causale best-effort.
**Caveat granularità:** cerbero-bite snapshotta una **fetta rotante** (~1 scadenza per ciclo) → ottimo
per skew/premi **aggregati**, limitato per il pricing **per-trade** preciso (usare la finestra di
staleness). **Pannello regime `market_snapshots`** (anch'esso importato → `data/options/market_snapshots.parquet`,
loader `options_chain.load_market(asset)`; merge causale su prezzo via `options_chain.attach_market(df, asset)`):
feature REALI pre-calcolate — **spot, dvol, realized_vol_30d, iv_minus_rv (VRP), funding perp/cross,
`dealer_net_gamma` (net-GEX dealer!), `gamma_flip_level`, oi_delta_pct_4h, liquidation_long/short_risk**.
È il segnale ARGO/GEX che il progetto dichiarò non-backtestabile: ora accumula, reale e allineato (lo
`spot` elimina il proxy ATM). **Copertura reale:** spot/dvol dal 2026-03-26 (sparse/daily fino a fine
aprile), **net-GEX denso orario solo da ~2026-05-01** → ~5-6 settimane, **un singolo regime calmo**:
analisi esplorativa OK ma NIENTE edge validabile ora (i prior GEX/VRP non si replicano su 9 sett.;
`liquidation_risk` costante 'low'). Valore FORWARD: rivalutare quando il pannello attraversa
gamma-flip/liquidazioni/crash con held-out. **NB look-ahead:** usare `attach_market` (merge_asof
causale, NaN prima della copertura), MAI `astype(int64)` su un timestamp datetime (darebbe ns →
match all'ultimo snapshot = leak). **Numeri reali misurati (ETH):** skew put 10% OTM = **×1.1** (liquido, spread ~7%, NON ×1.6
come sembrava da un singolo snapshot a 1g, illiquido); premio reale put 10% OTM ≈ **1.0%/mese** (0.65%/sett),
catastrofe 15%+ OTM ≈ 0.33%/mese. **Vincolo strutturale:** gli strike **10% OTM a 24h NON esistono**
(Deribit lista solo near-ATM sui tenor corti) → un overlay **per-trade a 24h è infattibile**; l'unica
struttura eseguibile è una **put settimanale/mensile standing** (catastrofe-cap di sleeve), da gateare
coi premi reali. Vedi `docs/diary/2026-06-08-mr02eth-replace-search.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/XSEC).
- **Default `portfolios.yml`:** PORT06 (master+shape), `weighting=cap pairs 0.33 + shape 0.0588`, **leva 3x** (era 2x fino al 2026-06-12, vedi sotto), ribilancio 1D. Backtest PORT06 canonico (dati al 2026-05-28, pre-cap-shape): FULL Sharpe 6.47 DD 4.10% / OOS Sharpe 8.82 DD 1.30%; **con EXIT-16 close-confirm: FULL 7.84 / 2.60%, OOS 10.06 / 1.15%** (i vecchi 6.07/8.19 erano pre-loss-guard/pre-refresh dati). Col cap SHAPE (2026-06-05): FULL 6.43 / 3.96%, OOS 8.58 / 1.36% — assicurazione sulla coda SH01, vedi sotto. Col BLEND ETH/BTC 15m (2026-06-09, v1.1.16): FULL 7.20 / 3.68%, OOS 9.66 / 1.31% — 18 sleeve. Con XS01 (2026-06-09): OOS Sharpe 10.07, FULL DD 3.46 — 19 sleeve (pool live real-only 15; i 4 book multi-asset TR01/ROT02/TSM01/XS01 girano in statistica). **Con SWAP fade 1h→15m (config live attuale, v1.1.30, 2026-06-12, vedi sotto): FULL Sharpe 8.13 DD 2.47% / OOS Sharpe 10.86 DD 2.09%** (regression-lock `test_backtest_parity_cap`).
- **LEVA 3x (v1.1.29, 2026-06-12, scelta utente).** Era 2x ("sobrio") dal primo deploy live. Frontiera ACCEL50 (`scripts/analysis/accel50_research.py`): a Sharpe OOS ~10 il collo di bottiglia è la TAGLIA, non il rischio — lev 2→3 porta gli anni-a-€50/g da 3.3 a 1.9 con FULL DD 3.5→5.2% (modello lineare). PAIRS `position_size_family` 0.20→0.13 per CONSERVARE l'esposizione validata ~0.40 (la leva accelera le famiglie con stop, non i pairs no-stop). Diario `docs/diary/2026-06-12-accel50.md`.
- **SWAP fade 1h→15m (v1.1.30, 2026-06-12, scelta utente "swap secco").** Le 6 fade (MR01/02/07 × BTC/ETH) girano a **15m** invece di 1h: stessi `sid` (pesi/alloc/ledger intatti), `tf="15m"` in `_defs.FADE` + `combine_portfolio.FADE_TF` (le due facce backtest/live sulla stessa def). Gate `scripts/analysis/fade15m_port06_gate.py`: parametri 1h NON ri-tunati (anti-overfit), corr daily 15m↔1h media **0.26** (diversificazione vera, non lo stesso edge più veloce), edge ETH regge il flat-entry-skip (non stale-print). SWAP promosso: FULL 7.34→8.13 / OOS 10.07→10.86 (OOS DD 1.48→2.09 accettato). DIP01 resta 1h (non era nel gate). `max_bars=24` ora = 6h; EXIT-16 confirm sulla barra 15m completata. Caveat: MR02_BTC 15m è il più fee-sensitive (fee2x OOS Sh 0.60) → monitorare le fee reali. **TF sweep** (`fade_tf_sweep.py`): 1m/2m chiusi (fee-death + flat 13-26%), 5m no-swap (MR02_BTC muore a fee2x), 10m in watchlist (ADD bocciato: OOS 10.86→10.76). Diari `docs/diary/2026-06-12-fade15m-gate.md` / `-fade-tf-sweep.md`.
- **XS01 — Cross-Sectional Reversion (ATTIVO LIVE 2026-06-09; dispersion-gate v1.1.20, 2026-06-10).**
Famiglia nuova XSEC, distinta da pairs (pairwise) e fade (single-asset): ogni `hold=12` ore
classifica 8 crypto (UNIVERSE8) per rendimento a `lb=48` ore e va long i perdenti relativi /
short i vincenti (peso ∝ (ret media cross-section)), market-neutral gross 1, fee 0.20%
RT/book (turnover 2). Scorrelato ~0 da pairs e fade. FULL Sharpe ~3.3, plateau lb 12-72 ×
hold 6-24; cost-sensitive (muore a ~0.35% RT/book). Gate PORT06: OOS Sharpe 9.66→10.07,
FULL DD 3.68→3.46. **Dispersion-gate** (`disp_min=0.0313` = p50 TRAIN): entra solo se la
std cross-section del momentum ≥ soglia — diagnostica monotona TRAIN+OOS, plateau p30-p70,
standalone Sharpe 2.50→3.46 (regge fee 2x), PORT06 OOS 10.07→10.37 a DD pari. Come
trend/hurst sulle fade, il gate agisce SOLO sul path live (backtest canonico non filtrato
→ il live farà meglio del backtest). 8 gambe → PAPER (niente esecuzione reale a €2k).
Worker `CrossSectionalWorker` validato (`validate_xsec_worker`: replay == backtest esatto).
**PHASE-TRANCHING (2026-06-11, `tranches=3`)**: la fase del roll non-sovrapposto è arbitraria
e da sola muove Sharpe daily FULL 1.52-2.33 / DD 13.8-33% (timing-luck) → live gira con 3
sub-book sfasati di hold/3 su capitale comune (PnL/K), ensemble di fase SENZA parametri
fittati. Gate `xs01_tranche_gate.py`: plateau K=2 E K=3 promossi (PORT06 OOS Sh 10.07→10.15,
DD 1.48→1.38, FULL pari); validatore esteso (K=1 == xsec_sim esatto; K=3 == unione fasi
0/4/8 esatto). Solo path live (backtest canonico single-phase). Strategia
`scripts/strategies/XS01_cross_sectional.py`, gate `scripts/analysis/xsec_port06_gate.py` /
`xs01_dispersion_gate.py`. NB scartata nella stessa tornata: FC01 funding-carry (nessun
edge su 6.5 anni). Diari `docs/diary/2026-06-09/10-*`.
- **BLEND ETH/BTC 15m flat-skip (ATTIVO LIVE v1.1.16, 2026-06-09).** Sleeve `PR_ETHBTC_15M` accanto al 1h: pairs ETH/BTC a **15m** (n=66, z_in=1.674, z_exit=1.0, max_bars=35) a **mezza size** (`params.position_size=0.10`). **Origine: gioco "Blind Traders"** (100 agenti ciechi su BTC/ETH anonimizzati → riscoprono la mean-reversion, vincitore = spread ETH/BTC 15m; `scripts/games/`). Gate PORT06 sul serio: **non duplicato (corr 1h↔15m = 0.37)**, robusto (16/16 celle Sharpe>1), e l'edge **NON è artefatto delle candele flat ETH 15m** (16% storico; filtrandole resta l'83% dello Sharpe). Engine `pairs_research.pairs_sim_flat` con uscita **LIVE-REALIZABLE** (`flat_skip`: niente entry/exit su barre O=H=L=C, esce alla 1a barra pulita), **regression-lock** a `pairs_sim` con flat_skip=False. **Worker validato** (`validate_worker_pairs` a 15m: replay == backtest, 8452 vs 8453 trade; 1h byte-exact). Runner: fetch sub-orario diretto da Cerbero + `pos_for_spec` override **per-sleeve** (params.position_size > famiglia > globale). Mezza size perché a peso pieno il 15m pesava il 25.8% del rischio PORT06 (→ 11.5%, bilanciato col 1h) — blend-tilt prudente sul **caveat slippage** (a slippage realistico il vantaggio di Sharpe regge, quello di DD si assottiglia: il vero banco di prova è il ledger reale shadow). Diari `docs/diary/2026-06-09-pairs15m-*.md`, gate `scripts/analysis/pairs15m_port06_gate.py` / `pairs15m_gate_final.py` / `pairs15m_flatcheck.py`, smoke `pairs15m_live_smoke.py`.
- **SH01 SENZA STOP-LOSS — by design, CONFERMATO da ricerca (2026-06-05).** Dopo il crash ETH (15.6% su un trade SH01 live), ricerca multi-agente con harness dedicato `scripts/analysis/sh01_exit_lab.py` (cache segnali walk-forward, engine con **fill gap-aware** `worse(livello, open)`, parity esatta con explore_lab, protocollo train≤2023-11-01/OOS): **11 famiglie di stop testate (ATR intrabar/close-confirm, %, chandelier, breakeven, giveback, loser-timestop, disaster-cap close+intrabar, swing, vol-regime), 0 sopravvissute** al gate (ETH migliorato senza degradare BTC, train E oos, plateau). Pattern: ogni stop stretto abbastanza da toccare la coda ETH rompe BTC; ogni stop largo non arriva alla coda; nei crash il fill è al gap, non al livello (lo stop "protettivo" PEGGIORA la coda OOS). Mitigazione adottata: **cap famiglia SHAPE a 0.0588 in PORT06** (≈ dimezzata; costo OOS Sharpe 0.24, FULL DD 0.14pp) — la prossima coda impatta il conto per metà. NON impostare mai `sl`/`sl_confirm_atr` su SH_BTC/SH_ETH. Direzione futura: liquidity-gate sull'entry (skip dopo feed flat). Diario `docs/diary/2026-06-05-sh01-sl-research.md`.
- **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 +32% vs +42% — residuo di convenzione capitale-unico vs media-equity; il bug `mean(rets)` sui soli asset in posizione, che dava 44%, è FIXATO il 2026-06-11: sovrappesava N/k a paniere parziale).
- **SH01 bootstrap full-history (punto-10, 2026-06-07).** La ri-validazione col train-window del
regime live (`sh01_trainwindow_validate.py`) ha mostrato che SH01 a train 365g **NON è robusto**
(BTC fee-2x FULL 42%, ETH Sharpe 0.02, trade-rate 22-26% vs 10% validato: LogReg over-confident,
soglia inerte — la diagnosi sweep era esatta). L'edge è MONOTONO nella memoria: solo l'expanding
full-history passa il gate. Fix live: il runner passa agli sleeve `ml` la storia FULL
(`_with_history`: parquet locale + feed, gap-guard con WARN) e `ml_wf_entries(last_block_only=True)`
fitta SOLO l'ultimo blocco del walk-forward → segnali **identici per costruzione** al WF completo
(parity test esatto), 0.6 s/tick su 73k barre. Manutenzione: tenere fresco il parquet
(`download_all()`). MAI ri-tunare la soglia nel regime corto (instabile/incoerente fra asset).
Diario `docs/diary/2026-06-07-sh01-trainwindow.md`.
- **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.
- **ESECUZIONE REALE — pairs 2 gambe + SH01 (2026-06-08).** Estesa oltre i fade: i **5 pairs PR01**
eseguono reale a 2 gambe (`PairsExecutionClient`: open/close long A/short B, leg-risk unwind, MAI
close_position; `pairs_enabled: true` acceso a conto flat, v1.1.12); **SH01** (BTC/ETH) esegue
single-leg con exit a orizzonte H=12 — niente TP/SL, `_place_real_tp` no-op e `_real_close` chiude
tutto market reduce-only, disaster-bracket on-book come unica protezione di coda (v1.1.13). Motivo:
SH01 è il diversificatore più decorrelato (senza i 5 sleeve PAPER il DD del portafoglio sale
3.96→5.35%). Copertura reale ora 15 sleeve su 19 (fade+DIP+**6 pairs incl. ETH/BTC 15m**+SH01);
restano simulati TR01/ROT02/TSM01/XS01 (book multi-asset, bloccati dal capitale: rumore
arrotondamento 20-30% a €2k, serve ~€20k). Diari `docs/diary/2026-06-08-pairs-executor.md`.
- **ESECUZIONE REALE — shadow (v1.0.3, 2026-06-03).** I **6 fade** (MR01/MR02/MR07 × BTC/ETH) eseguono ordini **REALI su Deribit testnet** accanto al fill simulato (*shadow*: sim + reale in parallelo, il sim resta la verità che guida le decisioni). `src/live/execution.py` `ExecutionClient`: `open` (market) + `close_amount` (market **reduce-only della SOLA quota del worker** — i 3 fade BTC condividono lo strumento e le posizioni si nettano per conto, quindi NON si usa `close_position` che flatterebbe le quote altrui); **verifica l'esecuzione sul TRADE** (order_id in `get_trade_history`, non sulla size netta aggregata); **fee REALI lette dai `trades[]`**. Strumenti = **lineari USDC** (`BTC_USDC`/`ETH_USDC-PERPETUAL`, amount nel base-coin, step 0.0001/0.001): scelti perché il **payoff lineare == matematica del backtest** (l'inverse `*-PERPETUAL` introdurrebbe una base 1/prezzo) e fee/PnL sono in USDC. Lo `StrategyWorker` tiene un **ledger reale parallelo** (`real_capital`, persistito) e logga `REAL_OPEN`/`REAL_CLOSE` col confronto **slippage** (prezzo sim vs eseguito) e **fee** (assunta 0.10% vs reale). Config: `portfolios.yml``overrides.execution {enabled, sleeves:[MR01,MR02,MR07,DIP01], instruments:{BTC:BTC_USDC-PERPETUAL, ETH:ETH_USDC-PERPETUAL}}` (**DIP01 aggiunto il 2026-06-04**: stesso wiring single-leg, TP resting incluso); pairs/rotation/tsmom/shape restano **simulati** (pairs richiede un executor a 2 gambe con gestione leg-risk; shape non ha TP). **Fee reali misurate = 0.05%/lato = 0.10% RT** (== assunto del backtest, su ETH; BTC inverse era ~0.094%). **Alert Telegram:** `REAL_EXEC_LIVE` (primo ordine reale verificato per worker) + `REAL_OPEN_FAIL`. Smoke (testnet, €0): `scripts/analysis/live_exec_smoke.py` (layer: ordine→verifica→fee) e `live_shadow_smoke.py` (catena worker open/close). **Capitale live portato a 2000** (notional fade ~$35) per ridurre il rumore di arrotondamento su BTC (step lineare ~$6.7). NB: ledger reale ≠ ledger sim — i worker già in posizione sim a un restart non hanno quota reale corrispondente; lo shadow reale parte pulito dalla prossima apertura.
- **Disaster-bracket on-book + alert outage (v1.1.4, 2026-06-07).** A ogni `REAL_OPEN` dei fade
eseguiti il worker piazza uno **STOP_MARKET reduce-only a ~30%** dall'ingresso (trigger sul mark,
`ExecutionClient.place_disaster_sl`, cancellato in `_real_close`): assicurazione per gli outage
(poll-loop fermo = exit non valutati), in operatività normale non scatta mai → 0 costo Sharpe.
Config `overrides.execution.disaster_sl_pct` (0.30; 0=off). NB: il `set_stop_loss` di cerbero-mcp
è un `private/edit` (solo ordini APERTI) → inutilizzabile su market fillati; la cancel di un
trigger order risponde `untriggered` (= successo, verificato testnet). Alert Telegram `FEED_OUTAGE`
dopo 5 poll falliti consecutivi (con elenco posizioni reali aperte) + notifica di ripresa. I fix
di parità della stessa tornata (v1.1.3): TR01 fee×leva + forming-bar TR01/Pairs + WARN
`PANEL_SHORT` su TSM01/ROT02; `hourly_report` ora mostra i multi-asset (sezione MULTI-ASSET).
Diario `docs/diary/2026-06-07-sweep-fixes.md`.
- **VERITÀ CONTABILE su netting di conto (v1.1.24, 2026-06-11, da audit live).** Il modello
"quote per-worker con reduce-only" si rompe quando worker dello stesso strumento hanno
direzioni OPPOSTE (pairs long ETH vs fade short ETH): Deribit cappa/respinge i reduce-only
in silenzio. L'audit ha trovato: close fillato 0.078 ma bookato 0.105 (`Fill.amount` era il
RICHIESTO), 3 gambe pairs mai eseguite col PnL sim sommato al ledger reale (gamba orfana sul
conto, ETH/SOL di fatto short nudo), e il conto short 0.027 ETH più dei libri (riallineato a
mano). Fix: (1) `Fill.filled_amount` (fonte `order.filled_amount`) usato da TUTTI i ledger;
(2) `REAL_CLOSE_PARTIAL` (log+alert) quando il close filla meno del residuo; (3) pairs:
PnL bookato SOLO per le gambe verificate, gamba respinta → `orphan_legs` persistito +
alert `PAIR_LEG_ORPHAN`, `applied` solo con ENTRAMBE le gambe (altrimenti sim_fallback
dichiarato); (4) `REAL_DIVERGENCE` anche su jsonl (prima solo Telegram); (5) runner:
tick isolato per-worker (un'eccezione non salta gli altri; alert `WORKER_ERROR_STREAK` a 5).
**RISOLTO in v1.1.25 col NETTING delle chiusure**: `close_amount` tenta il reduce-only
(sicurezza storica: un bug di stato filla 0) e riesegue il RESIDUO cappato/respinto in
**market puro** — che muove il conto esattamente del delta del libro, cioè netta contro le
quote opposte → niente più gambe orfane/close cappati per costruzione (copre anche i pairs
via `close_pair`). Il chiamante riceve UN Fill combinato (prezzo medio pesato, fee sommate,
`notes` con 'netting'); evento `NET_CLOSE` (log+Telegram) a ogni fallback. La sicurezza
persa sul residuo è coperta dal **reconciler orario** (`reconcile_account.py`, cron host
:40, alert `ACCOUNT_DRIFT`): conto vs Σ libri+orfani, tolleranza 1.5×step, anti-race.
`orphan_legs`/`REAL_CLOSE_PARTIAL` restano come ultima difesa (se fallisce anche il market
puro). Test `tests/portfolio/test_netting_close.py`.
- **TP_PHANTOM — il tocco TP va confermato dal book reale (v1.1.23, 2026-06-11).** Il feed
testnet stampa wick anomali che (a) generano segnali fade su ETH e (b) "toccano" il TP
intrabar della stessa barra: il sim bookava +4% fantasma a bars_held=0 e `_real_close`
chiudeva A MERCATO una posizione il cui resting TP non aveva mai fillato (fee/spread a
giro, 14 giri l'11-06, report Telegram 26/0 vs reale 11/15 — fix conteggio in v1.1.22).
Gate in `StrategyWorker._tp_phantom` (zero parametri, verita' d'esecuzione, NON un filtro
di strategia): tocco sim + **resting LIMIT a zero fill** + prezzo corrente che non ha
raggiunto il livello → exit SOPPRESSA (il limit sul book reale e' l'oracolo: se il prezzo
avesse scambiato li', avrebbe fillato); SL close-confirm e max_bars restano attivi.
Fill (anche parziale) o prezzo oltre il livello o worker non eseguito → comportamento
storico. Fail-open su errori di rete. Log `TP_PHANTOM` dedup per barra + alert Telegram.
Test `tests/portfolio/test_tp_phantom.py`.
- **TP reale = LIMIT reduce-only AL LIVELLO (2026-06-04).** Misurati +235 bps di slippage medio sulle uscite take-profit market-on-poll (sim esce al livello intrabar, il reale chiudeva al poll post-rimbalzo: sim +11.85 vs reale +0.62 USD sui primi 7 close). Fix: a ogni `REAL_OPEN` il worker piazza un **limit reduce-only al TP** (`ExecutionClient.place_tp_limit`, prezzo quantizzato al tick, SOLA quota del worker) → `REAL_TP_RESTING`; a ogni chiusura sim `_real_close` **cancella il resting → riconcilia i fill (anche parziali) via `get_trade_history` per order_id → market reduce-only solo del residuo** → ledger su prezzo combinato. `real_tp_order_id` persistito in `status.json` (resume-safe). Lo **SL resta market-on-poll** (deliberato: i trigger Deribit generano un nuovo order_id al trigger → fill non verificabile per order_id; e sul SL il rimbalzo lavora a favore). Fill da resting = fee **maker ~0%**. Smoke: `live_shadow_smoke.py` (2 scenari, testnet). Diario `docs/diary/2026-06-04-shadow-divergence.md`.
- **position_size per-famiglia (2026-06-07).** `portfolios.yml` accetta `position_size_family`
(chiave = `weighting.family_of`); plumbing `runner.pos_for_spec`. **PAIRS a 0.20** (esposizione
0.40 ≈ il validato 0.45): la famiglia è senza stop e col globale 0.5×lev2 girava a ~2.2x il
validato (ETH/BTC DD grezzo 78% a quella taglia). PORT06 OOS DD 3.40→1.26% al costo di OOS
Sharpe 9.05→8.43 — assicurazione come il cap SHAPE. Gate `pairspos_port06_impact.py`,
diario `docs/diary/2026-06-07-pairspos-gate.md`. NB (2026-06-11): il **disaster-cap sullo
z** (exit se |z|≥z_stop, griglia pre-registrata 3.0-5.0 su tutte le coppie) è **BOCCIATO**
— coda e Sharpe OOS peggiorano ovunque (lo stop realizza la perdita al massimo overshoot e
l'engine rientra nello spread divergente: churn di fee), **5ª conferma** che gli stop su
mean-reversion sono falsi negativi. Record `scripts/analysis/pairs_zstop_research.py`,
diario `docs/diary/2026-06-11-stability-sweep.md`.
- **REAL-TRUTH ledger (2026-06-10, scelta utente).** Con `overrides.execution.real_truth: true`
(ATTIVO) il `capital` dei worker eseguiti si aggiorna col **PnL dei FILL REALI** (fee reali
incluse) invece del PnL sim: `_real_close`/`_real_close_pair` ritornano `(real_pnl, applied)` e
`_close_position`/`_close` applicano il reale al ledger; il sim resta SOLO diagnostica nel log
CLOSE (`pnl_source`/`sim_pnl`/`real_pnl`). Fallback al sim dichiarato (`pnl_source=sim_fallback`)
solo se il trade reale non è mai esistito/fillato (REAL_OPEN_FAIL/leg-fail). Equity → pesi →
allocazioni → notional derivano così dai soldi veri sul conto (il notional reale era GIÀ la
formula sim `capital·ps·lev`; il gap storico sim/reale era contabile: ledger separati + spike
print delle candele testnet che il sim bookava e il reale no). Le DECISIONI (entry/exit) restano
guidate dal feed; i multi-asset (TR01/ROT02/TSM01/XS01) restano sim per costruzione. Test:
`tests/portfolio/test_real_truth.py`. Diario `docs/diary/2026-06-10-real-truth-ledger.md`.
- **RIBILANCIO CONSERVA L'EQUITY (fix v1.1.31, 2026-06-13).** Il ribilancio giornaliero
gonfiava l'equity quando un worker era in posizione: i flat si dividevano l'INTERO `total`
(che includeva il capitale degli in-position) e gli in-position lo trattenevano in più →
doppio conteggio di Σ(capitalalloc) sugli in-pos. Caso reale: MR02_BTC 15m seedato a 181.19
(eredità INIT_LINEAGE) e in posizione al ribilancio delle 00:01 → **+4.77 di equity dal nulla**
(scoperto perché l'equity saliva senza un trade dietro). Fix: `ledger.allocate(weights, reserved={sid:cap})`
— gli in-position TRATTENGONO il loro capitale (deployato, non spostabile), i flat si dividono
`total Σreserved` per peso RINORMALIZZATO → `Σalloc == total` sempre, equity conservata per
costruzione. `runner.rebalance_allocations` passa i `reserved` dai worker `in_position`. Parità
runner intatta (5.8e-08). Test `test_ledger.py`/`test_runner_rebalance.py`. Diario
`docs/diary/2026-06-13-rebalance-conservation.md`.
- **INIT_LINEAGE — eredità capitale al cambio timeframe (2026-06-12).** Un worker al primo avvio
(niente status.json) eredita `capital`/`real_capital` dal worker più recente di STESSA
strategia+asset su altro tf (glob `{strategy}__{asset}__*`), MAI la posizione. Nato dallo swap
fade 1h→15m: i worker nuovi partivano dall'allocazione del pool scartando il PnL del gemello
(16.8 di equity, riallineata a mano col seed la prima volta). `StrategyWorker._inherit_lineage_capital`,
evento `INIT_LINEAGE`. Test `tests/portfolio/test_capital_lineage.py`.
- **STALE_REAL_POSITION — guard anti-orfano nel reconciler (v1.1.31, 2026-06-13).** Lo swap
1h→15m aveva ritirato MR02_BTC 1h dal config mentre teneva uno short REALE → posizione nuda non
gestita per 13h, e il reconciler NON la vedeva (lo status fermo del worker morto contava ancora
come "libro" → conto == libri). `reconcile_account.compute_stale_real_positions(max_age_min=15)`:
un worker con `real_in_position` e status.json fermo da >15 min = non gestito → alert
`STALE_REAL_POSITION`. Discriminante = STALENESS (un worker vivo riscrive ogni poll): cattura
ritirati-da-swap, crashati, rimossi dal config. Orfano chiuso a mano (testnet, +0.85). Resta da
fare la PREVENZIONE lato runner (flattare/consegnare la posizione del ritirato al boot). Diario
`docs/diary/2026-06-13-orphan-swap-guard.md`.
- **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.
- **FEED TESTNET INAFFIDABILE — `ETH-PERPETUAL` inverse CONGELATO (2026-06-13).** Il feed di
DECISIONE che il runner usa per BTC/ETH (inverse perp, `INSTRUMENT_MAP`) può congelarsi sul
testnet: il 13-06 `ETH-PERPETUAL` era fermo a **1661.95 per 12h+** (1 solo valore) mentre il
reale (`ETH_USDC-PERPETUAL`, lineare, dove si ESEGUE) si muoveva (gap 1.3%); BTC era vivo.
Effetto: tutte le decisioni ETH (SH01_ETH, 3 fade ETH 15m, gambe ETH dei pairs) girano su un
prezzo morto → **spiega lo 0-trade delle fade ETH 15m** (feed piatto = niente rottura banda).
È un GUASTO testnet, non di strategia: su **mainnet** l'arbitraggio tiene inverse ≈ lineare ≈
realtà. Fix testnet possibile (reroute decisione ETH a USDC lineare in `INSTRUMENT_MAP`, con
caveat bootstrap SH01) NON applicato — il fix vero è andare a mainnet. Nota: la dashboard mostra
entry/mark/PnL **REALI** (non il sim-feed dislocato); il sim resta solo nel modal diagnostico.
**Update 2026-06-14: ANCORA congelato a 1661.95 (36h+).** Confermato anche un vincolo strutturale
del feed: **Cerbero v2 (`get_historical_v2`) serve SOLO 5m/15m/1h** — 30m/10m danno HTTP 400 in
ogni formato (l'endpoint legacy `get_historical` è 404, rimosso). La voce `"30m"` in
`runner._SUBHOURLY` era **speculativa, mai testata live** → qualsiasi TF sub-orario oltre 5m/15m
va DERIVATO resamplando nel runner (come 4h/1d dal 1h), non fetchato diretto.
- **TIMING SWEEP pairs & honest (2026-06-14, NESSUN deploy).** Domanda utente: pairs/honest
beneficiano di TF più veloci come le fade (swap 1h→15m)? Esito: **no, niente deployato.** Vincolo
dati (alt 1h-only) → sweepabili solo **pairs ETH/BTC** e **DIP01 (BTC)**; TR01/ROT02 fuori scope
(multi-asset alt + lente). Il gate full+OOS migliorava PORT06 (+10m/30m/5m su tutte le metriche),
MA due muri: (a) **30m/10m feed-blocked** (v2 serve solo 5/15m/1h, vedi sopra); (b) **5m** è
nativo ma nel regime ATTUALE è il PEGGIORE (10.5% 30g, DD 19.5%/180g vs 3% del 1h, Sharpe sotto
15m/1h) + flat-share ETH 29% (slippage reale) → guadagno backtest modesto (FULL Sh +0.39) non vale
il rischio su soldi reali. **Lezione: il gate PORT06 full+OOS è necessario ma NON sufficiente**
incrociare con (1) la fattibilità del FEED live e (2) il regime RECENTE (un edge full-history può
essere un relitto di volatilità passata: "più veloce = più Sharpe storico" si rovescia in "più
veloce = più DD nel regime calmo attuale"). Artefatti: `scripts/analysis/timing_sweep_pairs_honest.py`,
diario `docs/diary/2026-06-14-timing-sweep-pairs-honest.md`. (Config live invariata: 19 sleeve.)
- **MICRO-TEST MAINNET = il gate per scalare il capitale (piano pronto, 2026-06-13).** Testnet
valida solo la MECCANICA (feed/fill farlocchi); l'edge sopravvive ai fill veri? si risponde solo
con poco denaro reale su mainnet. **Switch già eseguibile via `.env`**: `CerberoClient` legge il
token da env (`CERBERO_TOKEN`, default testnet invariato; `is_mainnet()` helper) → puntare a
mainnet = solo `.env`, niente codice. Piano completo (fasi smoke→fade-only €1000 2-4 sett→verdetto
ledger-vs-backtest→espansione; sizing: fade €1000 = arrotondamento 2.6% BTC, pairs esclusi al 30%):
`docs/specs/mainnet-microtest-plan.md`. **Stato 2026-06-14:** token mainnet (`MAINNET_TOKEN` di
cerbero-mcp, che ha già le chiavi Deribit LIVE) **wired e VERIFICATO**`is_mainnet()=True`, auth
OK su `get_account_summary` read-only. Tenuto in **`.env.mainnet`** dedicato (chmod 600, gitignored),
FUORI dal `.env` condiviso → il runner testnet NON flippa a mainnet a un riavvio accidentale (il
micro-test girerà come **servizio separato** con dir dati pulita + portfolios fade-only, per non
mescolare i ledger). NB architettura token: il `MAINNET_TOKEN` è la chiave d'accesso *all'MCP* +
selettore d'ambiente (non una chiave Deribit) → il nostro client DEVE presentarlo per essere
instradato al live. **UNICO BLOCCO: conto Deribit mainnet VUOTO (€0 USDC/BTC/ETH) → serve deposito
~€1000 USDC** prima di Fase 0 smoke. Minimo assoluto €500 (rumore BTC ~5%), raccomandato €1000 (2.6%).