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,89 @@
|
||||
# 2026-06-01 — "Win" che perdono: metrica netto-fee + filtro TP edge-minimo
|
||||
|
||||
> Partito da un'osservazione dell'utente sui trade live PORT06: **ci sono close con
|
||||
> `win=True` ma `pnl` negativo**. Due problemi distinti, entrambi risolti.
|
||||
|
||||
## Problema 1 — la metrica `win` mentiva (lordo invece di netto)
|
||||
|
||||
In `strategy_worker.py::_close_position`:
|
||||
|
||||
```python
|
||||
trade_return = price_change * direction # LORDO, prima delle fee
|
||||
net = trade_return * leverage - fee_rt * leverage
|
||||
pnl = capital * position_size * net # corretto (netto)
|
||||
is_win = trade_return > 0 # BUG: usa il LORDO
|
||||
```
|
||||
|
||||
`is_win` scattava appena il prezzo si muoveva di un soffio a favore, **prima delle
|
||||
fee**. Capitale e PnL erano giusti (netti); solo la metrica `win`/`accuracy` era
|
||||
gonfiata.
|
||||
|
||||
**Quantificazione (51 close live):** 39 win dichiarate (76,5%) → **13 falsi win**
|
||||
(`win=True` ma `pnl≤0`) → accuratezza **netta reale 52,9%**. PnL realizzato +€0,77
|
||||
(resta positivo: lo trascinano i pairs).
|
||||
|
||||
**Fix:** `is_win = net > 0`. + `tests/portfolio/test_win_net_of_fees.py` (mossa
|
||||
sotto-fee = non win; oltre-fee = win; perdita = non win).
|
||||
|
||||
**Riconciliazione contatori persistiti:** i `total_wins` su disco erano gonfiati dal
|
||||
vecchio conteggio lordo. Ricalcolati come `net_return>0` dai `trades.jsonl`:
|
||||
**MR01_BTC 7→1, DIP01_BTC 7→1** (gli unici toccati; tutti gli altri già coerenti).
|
||||
Capitale invariato.
|
||||
|
||||
## Problema 2 — i 13 falsi win erano tutti MR01_BTC / DIP01_BTC in take_profit
|
||||
|
||||
Causa: in `MR01_bollinger_fade` e `DIP01_dip_buy` il **TP è la media** (`tp = ma[i]`)
|
||||
e l'entry è a `close[i]` appena fuori banda. Nel regime BTC **piatto** (inchiodato
|
||||
~73.700 per ore, vol bassissima) la media è a pochi dollari dall'entry → il TP cade
|
||||
**dentro** il costo round-trip (0,10%): colpire il TP = perdita netta garantita.
|
||||
|
||||
**Meccanismo del fix (importante).** "Spostare il TP più in là" NON garantisce di non
|
||||
perdere: il prezzo rientra solo fino alla media, non oltre → si finirebbe su SL/time-
|
||||
limit, perdendo di più. La mossa provabilmente non-perdente è un **filtro di edge
|
||||
minimo**: se `|tp − entry|/entry ≤ min_tp_frac` non si apre la trade. Break-even
|
||||
esatto = `fee_rt` (= 0,10%, indipendente dalla leva, perché
|
||||
`ret = mossa·lev − fee_rt·lev > 0 ⇔ mossa > fee_rt`).
|
||||
|
||||
**Implementazione:** parametro `min_tp_frac` (default 0.0 = off) in **tutte le 4 fade**
|
||||
(MR01 banda, MR02 midpoint canale, MR07 ATR-scaled) e DIP01; salta i segnali sotto
|
||||
soglia. Cablato negli sleeve live a **0.0015 (1,5× fee)** in `_defs.py` (`MIN_TP_FRAC`).
|
||||
|
||||
**Validazione backtest (BTC+ETH 1h, config sleeve, min_tp_frac ∈ {0,.001,.0015,.002}):**
|
||||
neutro su tutte e 4 le fade.
|
||||
- MR01: 0 trade rimossi (BTC +8028€, ETH +10395€) — metriche identiche.
|
||||
- DIP01 BTC: −1 trade a 0.002, **migliora** (+7492→+7522€, DD 26,3→25,9%).
|
||||
- MR02 BTC: −1 trade a 0.0015 (pnl invariato +12198€), ETH 0 rimossi.
|
||||
- MR07 BTC/ETH: 0 rimossi (TP ATR-scaled sempre ben oltre le fee nello storico).
|
||||
|
||||
Conclusione: i micro-scalp sotto-fee **non esistono nel campione storico** — sono un
|
||||
artefatto del regime attuale. Il filtro è **puro upside**: neutro sul backtest validato,
|
||||
protettivo dal vivo. (Le 12 trade live incriminate, tutte MR01/DIP01 BTC, avevano gap
|
||||
~0,026%, ben sotto 0,15% → tutte bloccate.)
|
||||
|
||||
+ `tests/portfolio/test_min_tp_frac.py` (monotonia + ogni superstite ha gap > soglia
|
||||
+ default-off invariato).
|
||||
|
||||
## Nota deploy — il codice è COTTO nell'immagine, non montato
|
||||
|
||||
Scoperta durante il deploy: `docker-compose.yml` monta solo `./data` e
|
||||
`./portfolios.yml`; il sorgente (`src/`, `scripts/`) è `COPY` nel Dockerfile. Quindi
|
||||
**`docker compose restart` NON ricarica le modifiche al codice** — serve
|
||||
`docker compose up -d --build`. Conseguenza retroattiva: anche il fix SH01
|
||||
horizon-exit di stamattina è andato live solo con questo rebuild. Da ricordare per ogni
|
||||
futura modifica ai worker. Il volume `./data` persiste → i 14 worker fanno RESUME
|
||||
puliti dopo il rebuild (capitale e posizioni intatti).
|
||||
|
||||
## Stato finale
|
||||
|
||||
- `is_win = net > 0` live; contatori riconciliati (MR01/DIP01 BTC 1/9).
|
||||
- Filtro `min_tp_frac=0.0015` live su tutti i fade + DIP01 (attivo solo MR01/DIP01).
|
||||
- Fix SH01 horizon-exit ora **effettivamente** live (rebuild).
|
||||
- Suite: 49 passed. Container ricostruito, healthy, 14 sleeve in RESUME.
|
||||
|
||||
## Lezione
|
||||
|
||||
1. Una metrica di "win" deve essere **netto fee**, altrimenti l'accuracy è teatro.
|
||||
2. Quando il TP è dentro il costo di transazione, la trade è persa in partenza: meglio
|
||||
**non prenderla** che ritoccare il TP.
|
||||
3. Per i worker live in Docker: **rebuild**, non restart. Il restart ricarica solo lo
|
||||
stato dal volume, non il codice.
|
||||
Reference in New Issue
Block a user