Un wick transitorio fa calcolare un tp dal lato sbagliato dell'entry (donchian: segnale su barra wickata, entry al prezzo recuperato oltre il proprio tp) -> l'exit intrabar scatta a bars_held=0 in perdita (16-06: 8 giri MR02_BTC 15m, sim -17.9 / reale -2.3). TP_PHANTOM non lo prende (niente resting oracle, prezzo oltre il livello). Gate zero-parametri in StrategyWorker._open_position, solo path live. Test + diario + CLAUDE. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
64 KiB
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 inuv.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
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 composedashboard, porta 8787). Stdlib http.server, leggedata/: 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 + schedastrategie_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 restartNON ricarica il codice: servedocker compose up -d --build(o./scripts/deploy.sh, che bumpa la versione, committa e rebuilda). Il volumedata/persiste → i worker fanno RESUME dello stato. La versione (fileVERSION, semver, +1 ad ogni deploy viadeploy.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:
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 consign(close[i]-close[i-1])(la candela di breakouti) ma entrano aclose[i-1]— cioè comprano prima della candela che usano per scegliere la direzione. Dal vivo il worker scopre il breakout solo aclose[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). Vediscripts/analysis/oos_validation.pyeintrabar_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 funzionekeltner_faderesta instrategy_research_v2.pycome 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(paramsl_confirm_atr, None = comportamento storico; il backtest canonicobuild_everythingresta NON filtrato → il live farà meglio del backtest, come per il loss-guard). Diariodocs/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) e5050fra le due famiglie (minimo DD: 5.0% full / 4.5% OOS). È la configurazione consigliata. ComePORT01, sono meta-portafogli (scriptrun()di report), nonStrategycongenerate_signals, quindi non nelstrategy_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 UNIVERSALEn=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, sezionepairs:instrategies.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-PERPETUALdanno 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):
- Ingresso eseguibile: direzione e prezzo decisi con dati fino a
close[i], maiclose[i-1]con direzione dai. - Backtest NETTO dopo fee realistiche Deribit (0.10% RT taker, non 0.20%) + leva.
- Validazione out-of-sample (held-out) + robustezza su griglia parametri + sweep fee.
- Crea script in
scripts/strategies/, aggiungi aMODULE_MAP(strategy_loader.py) e astrategies.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 disleeves.py→ parità esatta conreport_families) e live (PortfolioRunner: capitale pool condiviso, sizing per peso, ribilancio giornaliero, ledger aggregato indata/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 inweighting.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-locktest_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). PAIRSposition_size_family0.20→0.13 per CONSERVARE l'esposizione validata ~0.40 (la leva accelera le famiglie con stop, non i pairs no-stop). Diariodocs/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). Gatescripts/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=24ora = 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). Diaridocs/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=12ore classifica 8 crypto (UNIVERSE8) per rendimento alb=48ore 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). WorkerCrossSectionalWorkervalidato (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. Gatexs01_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). Strategiascripts/strategies/XS01_cross_sectional.py, gatescripts/analysis/xsec_port06_gate.py/xs01_dispersion_gate.py. NB scartata nella stessa tornata: FC01 funding-carry (nessun edge su 6.5 anni). Diaridocs/diary/2026-06-09/10-*. - BLEND ETH/BTC 15m flat-skip (ATTIVO LIVE v1.1.16, 2026-06-09). Sleeve
PR_ETHBTC_15Maccanto 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). Enginepairs_research.pairs_sim_flatcon uscita LIVE-REALIZABLE (flat_skip: niente entry/exit su barre O=H=L=C, esce alla 1a barra pulita), regression-lock apairs_simcon flat_skip=False. Worker validato (validate_worker_pairsa 15m: replay == backtest, 8452 vs 8453 trade; 1h byte-exact). Runner: fetch sub-orario diretto da Cerbero +pos_for_specoverride 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). Diaridocs/diary/2026-06-09-pairs15m-*.md, gatescripts/analysis/pairs15m_port06_gate.py/pairs15m_gate_final.py/pairs15m_flatcheck.py, smokepairs15m_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-awareworse(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 maisl/sl_confirm_atrsu SH_BTC/SH_ETH. Direzione futura: liquidity-gate sull'entry (skip dopo feed flat). Diariodocs/diary/2026-06-05-sh01-sl-research.md. - Data layer Cerbero v2:
get_historical_v2unificato +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 dedicatiBasketTrendWorker(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 bugmean(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 sleevemlla storia FULL (_with_history: parquet locale + feed, gap-guard con WARN) eml_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). Diariodocs/diary/2026-06-07-sh01-trainwindow.md. - FIX SH01 wiring (2026-06-01). SH01 gira come
StrategyWorkerNORMALE (NON il vecchioMLWorkerWrapperdimulti_runner, che usava ilSignalEnginesqueeze SCARTATO: apriva senza metadata ed usciva ahold_bars=3, ignorando del tutto SH01_shape_ml).SH01_shape_ml.generate_signalsfa il walk-forward (retraining) internamente ad ogni tick ed emettemetadata.max_bars=12→ exit a orizzonte viaStrategyWorker.tick. Serve ≥4000 barre 1h (_ML_LOOKBACK_DAYS=365). Vedidocs/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); filtromin_tp_frac(salta micro-scalp col TP entro le fee); loss-guardhurst_max=0.55sulle fade (vedi sopra). - Exit intrabar (fase 3, risolto): lo
StrategyWorkerora 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: trueacceso a conto flat, v1.1.12); SH01 (BTC/ETH) esegue single-leg con exit a orizzonte H=12 — niente TP/SL,_place_real_tpno-op e_real_closechiude 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). Diaridocs/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.pyExecutionClient: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 usaclose_positionche flatterebbe le quote altrui); verifica l'esecuzione sul TRADE (order_id inget_trade_history, non sulla size netta aggregata); fee REALI lette daitrades[]. 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*-PERPETUALintrodurrebbe una base 1/prezzo) e fee/PnL sono in USDC. LoStrategyWorkertiene un ledger reale parallelo (real_capital, persistito) e loggaREAL_OPEN/REAL_CLOSEcol 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) elive_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_OPENdei 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. Configoverrides.execution.disaster_sl_pct(0.30; 0=off). NB: ilset_stop_lossdi cerbero-mcp è unprivate/edit(solo ordini APERTI) → inutilizzabile su market fillati; la cancel di un trigger order rispondeuntriggered(= successo, verificato testnet). Alert TelegramFEED_OUTAGEdopo 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 + WARNPANEL_SHORTsu TSM01/ROT02;hourly_reportora mostra i multi-asset (sezione MULTI-ASSET). Diariodocs/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.amountera 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(fonteorder.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_legspersistito + alertPAIR_LEG_ORPHAN,appliedsolo con ENTRAMBE le gambe (altrimenti sim_fallback dichiarato); (4)REAL_DIVERGENCEanche su jsonl (prima solo Telegram); (5) runner: tick isolato per-worker (un'eccezione non salta gli altri; alertWORKER_ERROR_STREAKa 5). RISOLTO in v1.1.25 col NETTING delle chiusure:close_amounttenta 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 viaclose_pair). Il chiamante riceve UN Fill combinato (prezzo medio pesato, fee sommate,notescon 'netting'); eventoNET_CLOSE(log+Telegram) a ogni fallback. La sicurezza persa sul residuo è coperta dal reconciler orario (reconcile_account.py, cron host :40, alertACCOUNT_DRIFT): conto vs Σ libri+orfani, tolleranza 1.5×step, anti-race.orphan_legs/REAL_CLOSE_PARTIALrestano come ultima difesa (se fallisce anche il market puro). Testtests/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_closechiudeva 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 inStrategyWorker._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. LogTP_PHANTOMdedup per barra + alert Telegram. Testtests/portfolio/test_tp_phantom.py. - INVERTED_TP_SKIP — l'entry con TP dal lato sbagliato va soppressa (v1.1.33, 2026-06-16).
Un wick transitorio sul feed puo' far calcolare alla strategia un
tpdal lato SBAGLIATO dell'entry (es. donchian: segnale su barra wickata in basso → tp=centro canale, entry al prezzo RECUPERATO sopra il proprio tp). L'exit intrabarbar_high>=tp(long)/bar_low<=tp(short) scatta abars_held=0in PERDITA, con churn di fee e TP reduce-only respinti (16-06: 8 giri MR02_BTC 15m, sim −17.9 / reale −2.3 grazie al real-truth; TP_PHANTOM NON lo prende — niente resting oracle, il prezzo HA superato il livello). Gate inStrategyWorker._open_position(zero parametri, verita' d'esecuzione): setp<=entry(long) /tp>=entry(short) → entry soppressa (niente ordine reale/disaster-SL), logINVERTED_TP_SKIPdedup per-barra + alert Telegram. Solo path live (il backtest entra al close del bar del segnale → mai invertito, resta non filtrato → live meglio del backtest). Cerotto testnet (il fix vero e' mainnet). Testtests/portfolio/test_inverted_tp.py, diariodocs/diary/2026-06-16-inverted-tp-guard.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_OPENil 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_closecancella il resting → riconcilia i fill (anche parziali) viaget_trade_historyper order_id → market reduce-only solo del residuo → ledger su prezzo combinato.real_tp_order_idpersistito instatus.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). Diariodocs/diary/2026-06-04-shadow-divergence.md. - position_size per-famiglia (2026-06-07).
portfolios.ymlaccettaposition_size_family(chiave =weighting.family_of); plumbingrunner.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. Gatepairspos_port06_impact.py, diariodocs/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. Recordscripts/analysis/pairs_zstop_research.py, diariodocs/diary/2026-06-11-stability-sweep.md. - REAL-TRUTH ledger (2026-06-10, scelta utente). Con
overrides.execution.real_truth: true(ATTIVO) ilcapitaldei worker eseguiti si aggiorna col PnL dei FILL REALI (fee reali incluse) invece del PnL sim:_real_close/_real_close_pairritornano(real_pnl, applied)e_close_position/_closeapplicano 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 simcapital·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. Diariodocs/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 Σ(capital−alloc) 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 dividonototal − Σreservedper peso RINORMALIZZATO →Σalloc == totalsempre, equity conservata per costruzione.runner.rebalance_allocationspassa ireserveddai workerin_position. Parità runner intatta (5.8e-08). Testtest_ledger.py/test_runner_rebalance.py. Diariodocs/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_capitaldal 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, eventoINIT_LINEAGE. Testtests/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 conreal_in_positione status.json fermo da >15 min = non gestito → alertSTALE_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). Diariodocs/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]includeclose[i]che è un candle nel futuro — usarereturns[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 chereturns[k]usaclose[k+1]. I feature devono fermarsi areturns[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_datecome data nuda è inclusivo dell'intera giornata fino all'ultima candela chiusa (es.end=oggiarriva 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-PERPETUALinverse 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-06ETH-PERPETUALera 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 inINSTRUMENT_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 legacyget_historicalè 404, rimosso). La voce"30m"inrunner._SUBHOURLYera speculativa, mai testata live → qualsiasi TF sub-orario oltre 5m/15m va DERIVATO resamplando nel runner (come 4h/1d dal 1h), non fetchato diretto. - GATE FEED CONGELATO — sleeve ETH-leg sospesi (v1.1.32, 2026-06-15, scelta utente "congela
gli sleeve ETH-leg finché il feed non si sblocca"). Audit "stato ordini": il feed ETH congelato
stava generando perdite REALI (SH01_ETH −2.83 reali vs −0.09 sim su un close, poi riapertura
della stessa trappola; 4 pairs con gamba ETH entrati su z-score spuri −3/−5/+5.6 = artefatto del
log-ratio con ETH pinnato). Fix:
runner._frozen_assets+_feed_gated_sids— quando il feed di decisione 1h di un asset è CONGELATO, gli sleeve CONCENTRATI (single/ml/pairs) che ne dipendono saltano il tick (entry E exit) finché non si sblocca (come un outage; disaster-SL on-book = protezione di coda). Auto-guarente: si rilascia alla prima barra COMPLETA non-flat (NON è l'entry-guard post-flat bocciata: quella salta la candela-gap di ripresa, questo no). Detector guasto-vs-illiquido: conta la run di close INVARIATE (prezzo mai cambiato), NON le barre flat — sul feed reale ETH/BNB/DOGE hanno run 40-64 con 1-4 valori distinti/48h (MORTI) mentre SOL/LTC/ADA sono flat ma VIVI (run ≤12, 5-31 valori/48h: si muovono ogni ~10 barre). Sogliafeed_freeze_gate_bars=24(1 giorno di prezzo immobile, configurabile, 0=off): gatea le 9 gambe ETH esatte (3 fade ETH 15m + SH_ETH + 5 pairs con gamba ETH) lasciando attivi BTC-only, PR_BTCLTC (BTC vivo + LTC solo illiquido) e i multi-asset PAPER. Le posizioni ETH già aperte NON vengono flattate (freeze=pausa: un close forzato passerebbe per lo stesso feed morto). Alert TelegramFEED_FROZEN_GATEGATED/RIPRESO. Testtests/portfolio/test_freeze_gate.py, diariodocs/diary/2026-06-15-frozen-feed-gate.md. Resta un cerotto testnet: il fix vero è mainnet. - 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, diariodocs/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:CerberoClientlegge 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_TOKENdi cerbero-mcp, che ha già le chiavi Deribit LIVE) wired e VERIFICATO —is_mainnet()=True, auth OK suget_account_summaryread-only. Tenuto in.env.mainnetdedicato (chmod 600, gitignored), FUORI dal.envcondiviso → 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: ilMAINNET_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%).