chore(reset): v2.0.0 — storico certificato Deribit mainnet, ripartenza pulita
Reset del progetto su fondamenta verificate dopo la scoperta che l'intera libreria "validata OOS" era artefatto di feed contaminato (print fantasma del feed Cerbero TESTNET + storico Binance/USDT). - Storico ricostruito da Deribit MAINNET (ccxt pubblico, tokenless) e CERTIFICATO (certify_feed.py): BTC/ETH puliti su TUTTA la storia (mediana 2-6 bps vs Coinbase USD), integrita' OHLC + coerenza resample (maxΔ 0.00) + cross-venue OK. Alt esclusi (illiquidi/divergenti: LTC/DOGE 50-82% barre flat; XRP/BNB non certificabili). - Verdetto sul feed pulito: FADE / PAIRS / XS01 / TSM01 morti (ogni portafoglio Sharpe -2.3..-3.0, DD ~40%); solo SH01 e frammenti HONEST con segnale residuo, da ri-validare in isolamento. - Cleanup "restart pulito": strategie, stack live (src/live, src/portfolio, runner/executor, yml, docker), ~100 script ricerca/gate, waste/games/ portfolios, dati non certificati + cache e 60+ diari -> archiviati in Old/ (preservati, non cancellati). Diario consolidato in un unico documento. - Skeleton ricerca tenuto: Strategy ABC + indicatori + src/fractal + src/backtest/engine + load_data; tool dati certificati (rebuild_history, certify_feed, audit_feed, multi_source_check). - Universo dati ATTIVO: solo BTC/ETH (5m/15m/1h); guardrail fisico (load_data su alt -> FileNotFoundError). Esecuzione DISABILITATA, conto flat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# 2026-06-01 — Bugfix: SH01 usciva a 3 barre invece di H=12 (exit a orizzonte)
|
||||
|
||||
> Diagnosi partita da un check sulla debolezza apparente di **SH01_BTC** nel paper
|
||||
> trading live PORT06 (accuratezza 33,3% su 3 trade). Non era sfortuna statistica:
|
||||
> era un bug di exit nello `StrategyWorker`.
|
||||
|
||||
## Sintomo
|
||||
|
||||
Nel live PORT06 (Docker `pythagoras-portfolio`), SH01_BTC mostrava 3 trade tutti
|
||||
`long`, **tutti chiusi con `reason: "hold_limit"` a `bars_held: 3`**, con `tp: null
|
||||
sl: null`:
|
||||
|
||||
| # | entry | exit | bars | net % | esito |
|
||||
|---|-------|------|------|------:|:---:|
|
||||
| 1 | 73529.5 | 73433.0 | 3 | −0,46% | ❌ |
|
||||
| 2 | 73759.5 | 73839.5 | 3 | +0,02% | ✅ |
|
||||
| 3 | 73811.5 | 73766.0 | 3 | −0,32% | ❌ |
|
||||
|
||||
`oos_signal_precision` nei log di TRAIN scendeva 55,6% → 50,0% → 43,3%.
|
||||
|
||||
## Causa
|
||||
|
||||
SH01 (`scripts/strategies/SH01_shape_ml.py`, config **W24 H12 th0.58**) è una
|
||||
strategia **horizon-only**: predice il segno del rendimento a **H=12 barre** ed esce a
|
||||
H barre. I suoi Signal portano `metadata={"max_bars": H}` (=12) e **nessun TP/SL**.
|
||||
|
||||
Nello `StrategyWorker.tick()` la logica di uscita era:
|
||||
|
||||
```python
|
||||
if self.tp and self.sl: # SH01: False (tp=sl=0)
|
||||
... usa self.max_bars ... # -> max_bars=12 consultato SOLO qui
|
||||
elif self.bars_held >= self.hold_bars: # fallback legacy hold_bars=3
|
||||
self._close_position(..., "hold_limit") # SH01 finiva QUI
|
||||
```
|
||||
|
||||
`self.max_bars` (=12, settato correttamente in `_open_position`) era onorato **solo
|
||||
dentro il ramo `tp and sl`**. Senza TP/SL, SH01 cadeva sul fallback `hold_bars=3` e
|
||||
chiudeva a 3 barre. L'edge di SH01 — per CLAUDE.md è nell'**asimmetria sull'orizzonte
|
||||
H, non nella frequenza** (win-rate ~50%) — non aveva tempo di realizzarsi: tagliato a
|
||||
3/12, degenera in rumore.
|
||||
|
||||
Solo SH01 (BTC+ETH) era colpito: tutte le fade (MR01/MR02/MR07, DIP01) portano
|
||||
tp+sl+max_bars e usano il ramo intrabar corretto.
|
||||
|
||||
## Fix
|
||||
|
||||
`src/live/strategy_worker.py`: aggiunto un ramo per l'exit a orizzonte puro, prima del
|
||||
fallback `hold_bars`:
|
||||
|
||||
```python
|
||||
elif self.max_bars:
|
||||
# Exit puro a orizzonte (strategie senza TP/SL, es. SH01 shape-ML H=12):
|
||||
# onora max_bars dalla metadata del Signal, non il fallback hold_bars=3.
|
||||
if self.bars_held >= self.max_bars:
|
||||
self._close_position(current_price, "time_limit")
|
||||
```
|
||||
|
||||
Le fade restano invariate (entrano nel ramo `tp and sl`).
|
||||
|
||||
## Verifica
|
||||
|
||||
- Nuovo test `tests/portfolio/test_horizon_exit.py` (2 casi): con `max_bars=12` resta
|
||||
in posizione a 3 barre; esce a 12 con `reason: "time_limit"` e `bars_held: 12`.
|
||||
- Suite completa: **43 passed**.
|
||||
- Container riavviato: **tutti i 17 sleeve RESUME puliti**, inclusa una posizione
|
||||
SH01_ETH short aperta che ora seguirà l'exit a 12 barre.
|
||||
|
||||
## Atteso d'ora in poi
|
||||
|
||||
I trade SH01 nei log mostreranno `reason: "time_limit"` con `bars_held: 12` invece di
|
||||
`hold_limit / 3`. Il 33% di accuratezza era un artefatto dell'exit prematuro; ora la
|
||||
strategia gira sull'orizzonte su cui è validata (BTC OOS Sharpe 2,72, expanding).
|
||||
Resta comunque un **diversificatore** del MASTER, non un motore di ritorno standalone.
|
||||
|
||||
## Lezione
|
||||
|
||||
Il backtest di SH01 (`fade_base`/engine onesto) esce a H barre via `max_bars`; il
|
||||
worker live deve replicarlo. Quando una strategia non porta TP/SL ma solo un
|
||||
orizzonte, il fallback `hold_bars` del worker la **falsa silenziosamente**. Verificare
|
||||
sempre che la convenzione di exit del worker live coincida con quella del backtest
|
||||
validato — non solo l'ingresso.
|
||||
Reference in New Issue
Block a user