diff --git a/CLAUDE.md b/CLAUDE.md index 9aa953a..97d7885 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -42,15 +42,21 @@ src/portfolio/ → portafogli di prima classe (capitale-pool, backtest+l sleeves.py → builder unificato equity-per-sleeve (fonte unica, parità report) ledger.py → PortfolioLedger: capitale/PnL/DD/persistenza+resume runner.py → PortfolioRunner live (data Cerbero v2, sizing, ribilancio) +src/version.py → APP_VERSION (legge il file VERSION) — mostrato nei msg Telegram +src/strategies/fade_base.py → FadeStrategy + helper: atr, trend_distance, hurst_skip_mask (loss-guard) scripts/strategies/ → strategie con edge validato OOS: FADE (MR01/MR02/MR07), - HONEST (DIP01/TR01/ROT02), PAIRS (PR01), TSMOM + portafogli (PORT01/02/03) -scripts/portfolios/ → definizioni PORT01-06 + report run() + 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, ...) +scripts/analysis/ → ricerca/validazione OOS fee-aware (strategy_research, oos_validation, ...); + regime_fetcher.py + regime_lab.py (DVOL/funding/feature regime per la ricerca) +scripts/bump_version.py, scripts/deploy.sh → versionamento e deploy (bump+commit+rebuild) +VERSION → versione semver (cotta nell'immagine, +1 ad ogni deploy) strategies.yml → config multi-strategy paper trader docs/diary/ → diario di ricerca giornaliero docs/specs/ → specifiche di design -data/raw/ → file .parquet OHLCV (gitignored) +data/raw/ → file .parquet OHLCV (gitignored) | data/regime/ → DVOL+funding+feature (gitignored) data/instruments_registry.json → allowlist strumenti validati (gate del downloader) ``` @@ -70,10 +76,17 @@ uv run python -m src.live.multi_runner # paper trading live multi 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 -docker compose up -d # deploy Docker +uv run python scripts/analysis/regime_fetcher.py # fetch DVOL+funding (Deribit mainnet) -> data/regime/ +./scripts/deploy.sh [patch|minor|major] # DEPLOY: bump versione + commit + rebuild Docker uv run pytest # test ``` +> **Deploy.** Il sorgente è **COPY nell'immagine Docker** (non montato) → `docker compose restart` +> NON ricarica il codice: serve **`docker compose up -d --build`** (o `./scripts/deploy.sh`, che bumpa +> la versione, committa e rebuilda). Il volume `data/` persiste → i worker fanno RESUME dello stato. +> La **versione** (file `VERSION`, semver, +1 ad ogni deploy via `deploy.sh`) compare nei messaggi +> Telegram (notifiche trade + report orario) → correli ogni msg al codice che l'ha generato. + ## Dati storici Scaricati e salvati localmente in Parquet. Per rigenerarli: @@ -158,6 +171,19 @@ MR07 46%→21%), edge OOS confermato (vedi `scripts/analysis/risk_management.py` Unica eccezione: MR03 BTC, dove il filtro peggiora entrambe → lasciato disattivo. Leva non robusta scartate: vol-target sizing e skip-alta-volatilità (peggiorano). +**Loss-guard Hurst (ATTIVO LIVE, 2026-06-02).** Le fade accettano `hurst_max`: saltano i segnali in +regime PERSISTENTE/trending (rolling-Hurst ≥ soglia), dove si concentrano stop-loss e perdite +(diagnosi: stop-rate 43% per hurst>0.55 vs 21% anti-persistente; i peggiori 1% trade hanno hurst +medio 0.61). Helper `src.strategies.fade_base.hurst_skip_mask` (rolling-Hurst causale **dalle sole +close** → nessun feed esterno; step=6 per velocità live). **`hurst_max=0.55` attivo sulle 6 fade in +`_defs.py`**: il test decisivo a livello PORT06 lo conferma — **FULL DD 4.10%→2.39% (quasi dimezzato), +Sharpe 6.62→6.76, OOS Sharpe 8.89→9.15**. È l'UNICO meccanismo anti-perdite che supera il gate (ADX, +vol-expansion/vratio, efficiency-ratio, time-stop, vol-target FALLISCONO: tagliano i winner insieme ai +loser; i claim esterni ADX/ATR-ratio non si replicano su queste fade crypto). NB: il filtro agisce solo +sul path LIVE (`spec.params`); il backtest canonico (`build_everything`/regression-lock) NON è filtrato +→ il live farà MEGLIO del backtest sul DD. Ricerca: `scripts/analysis/fade_lossguard_workflow.js`, +diagnosi `fade_loss_by_regime.py`, diario `docs/diary/2026-06-02-fade-lossguard.md`. + **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. @@ -234,9 +260,25 @@ template overfit), PIP/pivot/zig-zag (0/48 robuste). Vedi `scripts/analysis/shap 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: serve worker con retraining - periodico** (lo StrategyWorker è a regola fissa) → in `MODULE_MAP` ma non ancora in - `strategies.yml`. Diario: `docs/diary/2026-05-29-shape.md`. + 4.33→5.10, DD 4.7%→4.2%). NON motore standalone. **LIVE (2026-06-01): gira come StrategyWorker + reale** (vedi fix wiring sotto in SCOPE LIVE). Diario: `docs/diary/2026-05-29-shape.md`. + +**ARGO / GEX opzioni (analisi 2026-06-01, SCARTATO).** Valutato ARGO (lettura del gamma-exposure dei +dealer Deribit) come filtro di regime. Esito **NO-GO**: il net-GEX si calcola live (Deribit mainnet +public API, OI reale ~368k contratti, DVOL/funding storici gratis) ma **lo storico per-strike dell'OI +non è gratuito → non backtestabile OOS** (stesso muro delle opzioni W18/19/21). Niente evidenza crypto, +segno fragile, mercato dominato dai perp. Diario `docs/diary/2026-06-01-argo-gex-feasibility.md`. + +**Frattali del segnale × Regime ARGO (ricerca 100 agenti, 2026-06-02, RECORD).** Cercata una strategia +che combini un segnale frattale (Hurst/Higuchi/Williams/analog) con un gate di regime (DVOL/VRP/funding). +Infrastruttura riusabile: `scripts/analysis/regime_fetcher.py` (DVOL+funding da Deribit mainnet → +**`data/regime/`**, NON `data/raw/` che è solo OHLCV) e `regime_lab.py` (feature regime+frattali causali, +cache, harness netto-OOS). Esito: **15 strategie robuste e causali, ma NESSUNA batte/migliora il PORT06** +(diversificatori sovrapposti alle fade). **Finding: il prior ARGO "VRP>0=range=fade" è SMENTITO** — +l'edge è su **VRP<0 + DVOL bassa**. Il vincitore `FR01_hurst_calm_fade.py` è robusto ma DILUISCE il +PORT06 (OOS Sharpe 8.89→8.72) → **non deployato** (in `scripts/strategies/` ma NON in MODULE_MAP/yml). +Il sottoprodotto utile è stato il **loss-guard Hurst** (vedi sopra), che invece MIGLIORA il PORT06. +Diario `docs/diary/2026-06-02-fractal-argo-search.md`. **Metodologia obbligatoria per ogni nuova strategia** (per non ripetere l'errore squeeze): 1. Ingresso eseguibile: direzione e prezzo decisi con dati **fino a `close[i]`**, mai `close[i-1]` con direzione da `i`. @@ -258,7 +300,9 @@ queste fade, ma va confermato col paper trader live prima di rischiare capitale - **Schemi peso:** `equal` (default), `cap` (tetto per famiglia, es. pairs 33% — config raccomandata), `inverse_vol`, `cluster_rp` (equal fra cluster naturali poi inverse-vol intra-cluster), `manual`. Definiti in `weighting.py`; la chiave cap è la famiglia (PAIRS/FADE/HONEST/SHAPE/TSM). - **Default `portfolios.yml`:** PORT06 (master+shape), `weighting=cap pairs 0.33`, leva 2x, ribilancio 1D. Backtest PORT06: FULL Sharpe 6.07 / OOS Sharpe 8.19, DD 4.9% full / 2.3% OOS. - **Data layer Cerbero v2:** `get_historical_v2` unificato + `get_instruments` (naming robusto) + `get_ticker_batch`. Trading su Deribit. -- **SCOPE LIVE (fase 2 completata):** il runner esegue TUTTI gli sleeve di PORT06. Worker: single `StrategyWorker` (fade MR01/02/07, DIP01), `PairsWorker` (PR01 2 gambe), `MLWorkerWrapper` (SH01 retraining), 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). +- **SCOPE LIVE (fase 2 completata):** il runner esegue TUTTI gli sleeve di PORT06. Worker: single `StrategyWorker` (fade MR01/02/07, DIP01, **e SH01**), `PairsWorker` (PR01 2 gambe), e i multi-asset dedicati `BasketTrendWorker` (TR01 4h), `RotationWorker` (ROT02 1d), `TsmomWorker` (TSM01 1d). Il runner fetcha 1h da Cerbero v2 e **resampla a 4h/1d** (lookback dimensionato sui daily: TSM01 usa 252g). Validazione: runner pool/ribilancio/ledger == backtest (`validate_portfolio_runner.py`, identico); worker multi-asset == reference (`validate_honest_workers.py`: TSM01 esatto, ROT02 +1303% canonico, TR01 stesso ordine — differenza di convenzione capitale-unico vs media-equity). +- **FIX SH01 wiring (2026-06-01).** SH01 gira come **`StrategyWorker` NORMALE** (NON il vecchio `MLWorkerWrapper` di `multi_runner`, che usava il `SignalEngine` **squeeze SCARTATO**: apriva senza metadata ed usciva a `hold_bars=3`, ignorando del tutto SH01_shape_ml). `SH01_shape_ml.generate_signals` fa il walk-forward (retraining) internamente ad ogni tick ed emette `metadata.max_bars=12` → exit a orizzonte via `StrategyWorker.tick`. Serve ≥4000 barre 1h (`_ML_LOOKBACK_DAYS=365`). Vedi `docs/diary/2026-06-01-sh01-wiring-squeeze-bug.md`. +- **Altri fix StrategyWorker (2026-06-01).** Exit a orizzonte puro per strategie senza TP/SL (`elif self.max_bars`, SH01 esce a H=12 non hold_bars=3); `is_win = net > 0` (win NETTO fee, non lordo); filtro `min_tp_frac` (salta micro-scalp col TP entro le fee); loss-guard `hurst_max=0.55` sulle fade (vedi sopra). - **Exit intrabar (fase 3, risolto):** lo `StrategyWorker` ora esce sui TP/SL toccati INTRABAR (high/low della barra, al livello, SL prioritario) come il backtest — non più solo sul close. Allinea fade/DIP01 live al backtest intrabar (`tests/portfolio/test_intrabar_exit.py`). Caveat residuo onesto: nel paper trading l'high/low usato è quello della barra in corso al poll; su un fill reale conterebbe il momento del tocco. - **Limite noto:** al ribilancio le posizioni APERTE restano sul loro notional (non travasate); fedele al backtest daily-rebalanced entro il turnover infragiornaliero. diff --git a/README.md b/README.md index 566cc7a..275c9da 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,9 @@ PythagorasGoal/ │ └── analysis/ # Ricerca/validazione OOS fee-aware, gestione rischio, report ├── strategies.yml # Config multi-strategy paper trader ├── data/ -│ └── raw/ # Parquet OHLCV (gitignored, ~70 MB) +│ ├── raw/ # Parquet OHLCV (gitignored, ~70 MB) +│ └── regime/ # DVOL + funding (Deribit mainnet) + cache feature regime (gitignored) +├── VERSION # versione semver (cotta nell'immagine, mostrata nei msg Telegram) ├── docs/ │ ├── diary/ # Diario di ricerca giornaliero │ └── specs/ # Specifiche di design @@ -153,10 +155,12 @@ Le strategie single-asset estendono `src.strategies.base.Strategy` | **PR01** | `PR01_pairs_reversion.py` | PAIRS | Spread reversion market-neutral su 5 coppie | | **TSM01** | `tsmom_research.py` | TSMOM | Time-series momentum multi-orizzonte + risk-off | -Le fade applicano un **filtro trend** opzionale (`trend_max`/`ema_long`): saltano i -segnali quando il prezzo è troppo esteso rispetto alla EMA200 — alza l'accuratezza e -abbassa il drawdown. Portafogli pronti: `PORT01` (honest), `PORT02` (fade), `PORT03` -(master fade+honest). +Le fade applicano due filtri di regime opzionali: un **filtro trend** (`trend_max`/`ema_long`, +salta i segnali col prezzo troppo esteso rispetto alla EMA200) e un **loss-guard Hurst** +(`hurst_max=0.55`, salta i segnali in regime persistente/trending dove si concentrano gli stop-loss +— dimezza il drawdown del portafoglio, calcolato dalle sole close). Più un filtro `min_tp_frac` +che scarta i micro-scalp col take-profit entro il costo delle fee. Portafogli pronti: `PORT01` +(honest), `PORT02` (fade), `PORT03` (master fade+honest), **`PORT06`** (master esteso, default live). **Scartate** (in `scripts/waste/`): la famiglia squeeze (SQ01-04, ML01, MT01, PD01, CM01, AD01 — artefatto di look-ahead), MR03 Keltner (debole/ridondante con MR01) e @@ -270,9 +274,33 @@ Il modulo `weighting.py` mette a disposizione cinque schemi: `equal` (default), La configurazione raccomandata è **PORT06** (`scripts/portfolios/PORT06_master_shape.py`): portafoglio master esteso che include tutte e sei le famiglie (FADE, HONEST, PAIRS, TSMOM, SHAPE), con schema `cap` che limita i pairs al 33% del capitale per moderare la loro concentrazione di rischio. Risultati del backtest: Sharpe 6.07 (FULL) / 8.19 (OOS), drawdown massimo 4.9% (FULL) / 2.3% (OOS), leva 2×. -### Scope live (v1) +### Scope live -Il runner esegue le famiglie per cui esiste un worker dedicato: **fade** (MR01, MR02, MR07), **pairs** (PR01, cinque coppie) e **shape** (SH01, con retraining periodico via `MLWorkerWrapper`). Le famiglie **honest** (DIP01, TR01, ROT02) e **TSMOM** (TSM01) sono al momento escluse dall'esecuzione live — restano nel backtest — e il runner lo segnala nel log, rinormalizzando automaticamente i pesi sugli sleeve attivi. Il supporto ai worker honest e TSM01 è previsto nella fase 2. +Il runner esegue **tutti e 17 gli sleeve** di PORT06: **fade** (MR01, MR02, MR07 × BTC/ETH), +**honest** (DIP01, TR01-basket 4h, ROT02-rotation 1d), **pairs** (PR01, cinque coppie), +**TSMOM** (TSM01 1d) e **shape** (SH01 × BTC/ETH). Worker dedicati: `StrategyWorker` (single-leg, fade/ +dip/**shape**), `PairsWorker` (2 gambe), `BasketTrendWorker`, `RotationWorker`, `TsmomWorker`. Il runner +fetcha 1h da Cerbero v2 e resampla a 4h/1d; il pool di capitale, il ribilancio giornaliero e il ledger +sono validati == backtest. + +> **SH01 (2026-06-01):** gira come `StrategyWorker` normale (il walk-forward è interno a +> `generate_signals`). Il vecchio `MLWorkerWrapper` usava il `SignalEngine` **squeeze scartato** — +> rimosso. **Loss-guard Hurst (2026-06-02):** le fade saltano i segnali in regime persistente +> (rolling-Hurst ≥ 0.55), dove si concentrano gli stop-loss — dimezza il drawdown del portafoglio +> (FULL 4.1%→2.4%). Calcolato dalle sole close, attivo live (`hurst_max` nei params). + +### Versione & deploy + +Ogni deploy ha una **versione** (file `VERSION`, semver) che compare nei messaggi Telegram (notifiche +trade + report orario), così correli ogni messaggio al codice che l'ha generato. Il sorgente è **cotto +nell'immagine** → per aggiornare il live serve un **rebuild**, non un semplice restart: + +```bash +./scripts/deploy.sh # bump patch (1.0.0 → 1.0.1) + commit + rebuild + ricrea container +./scripts/deploy.sh minor # 1.0.x → 1.1.0 +``` + +Il volume `data/` persiste tra i deploy → i worker fanno RESUME dello stato (capitale, posizioni aperte). ### Avvio del paper trader a portafoglio