c997bce00e
attach_market(df, asset): merge_asof causale del pannello market_snapshots (spot/VRP/funding/net-GEX/gamma-flip/liquidation) sul prezzo, pronto per regime_lab. Fix look-ahead: merge su datetime tz-aware (ns-aligned), MAI astype(int64) su datetime (darebbe ns -> match all'ultimo snapshot = leak). Copertura reale documentata: net-GEX denso solo da ~2026-05-01 (~5-6 sett, singolo regime) -> infrastruttura pronta, edge validabile solo forward. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
464 lines
43 KiB
Markdown
464 lines
43 KiB
Markdown
# 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 6–8 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)
|
||
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
|
||
./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).
|
||
|
||
**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).
|
||
- **Default `portfolios.yml`:** PORT06 (master+shape), `weighting=cap pairs 0.33 + shape 0.0588`, leva 2x, 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 (config live attuale): 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.
|
||
- **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 stesso ordine — differenza di convenzione capitale-unico vs media-equity).
|
||
- **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 ~81% (fade+DIP+pairs+SH01); restano simulati TR01/ROT02/TSM01
|
||
(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`.
|
||
- **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`.
|
||
- **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.
|