Files
PythagorasGoal/Old/docs/diary/2026-06-16-inverted-tp-guard.md
T
Adriano Dal Pastro 14522262e6 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>
2026-06-19 15:20:59 +00:00

67 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-06-16 — Guard TP-invertito (churn MR02_BTC 15m)
## Sintomo
Burst di **8 trade in 15 minuti** su `MR02_donchian_fade__BTC__15m` (09:0009:15 UTC),
tutti `long`, tutti chiusi `take_profit` a `bars_held=0-1` **in perdita**, accuracy
crollata 60%→27%. TP **fisso** a 66189.25, **sotto** ogni entry (6670066834):
```
09:00 OPEN long 66780.5 tp=66189.25 sl=64218.21 ← tp E sl ENTRAMBI sotto l'entry
09:02 CLOSE take_profit exit=66189.25 held=0 sim=-2.63 real=-0.12
... ×8 ... +7× REAL_TP_FAIL: invalid_reduce_only_order
```
I trade MR02 sani prima del cluster hanno il TP dal lato giusto (long → tp>entry:
65033>63414, 64148>63775, 66079>65717). Il cluster delle 09:00 è l'unico **invertito**.
## Causa radice — segnale da wick transitorio (NON feed congelato, NON spike d'ingresso)
BTC alle 09:00 era **davvero** ~66780 (MR01 short@66919, MR07 short@66780.5 nello stesso
istante, slippage reale 0.72.5 bps → feed sano). Il difetto è a monte:
1. Una barra con **wick verso il basso** (~64218 = il livello SL) fa scattare il donchian
"rottura canale basso" → segnale **LONG**, con `tp`=centro canale (66189) e `sl`=64218
calcolati **dal contesto wickato**.
2. Il wick rientra; il worker entra `long` al prezzo **recuperato** 66780 — cioè **sopra
il proprio TP=66189**.
3. L'exit intrabar (`bar_high >= tp`) è banalmente vera all'istante → chiude "take_profit"
a 66189 = **perdita garantita a held=0**. Si ripete 8× perché il livello donchian
wickato resta nel lookback.
Differenza chiave da TP_PHANTOM: lì il wick è sull'**exit** (tocco fantasma del TP);
qui il wick produce un **tp dal lato sbagliato dell'entry** → la posizione non doveva
proprio aprirsi.
## Perché i guard esistenti non l'hanno preso
- **TP_PHANTOM** richiede un limit reduce-only resting come oracolo. Qui il limit è
**respinto** (`invalid_reduce_only_order`: una sell reduce-only a 66189 sotto il mercato
è invalida) → niente `real_tp_order_id` → fail-open → l'exit passa. E il prezzo corrente
(66780) ha **realmente** superato il livello → non è un tocco fantasma.
- **Freeze-gate (v1.1.32)**: BTC non è congelato (prezzi che si muovono) → non scatta.
## Danno reale: contenuto (il real-truth ledger ha funzionato)
- Σ **reale** del cluster ≈ **−€2.29** (solo slippage+fee: il fill reale è al prezzo vero
~66700-66730, non al TP fantasma).
- Σ **sim** sarebbe stato ≈ **−€17.9** → il real-truth ledger ha evitato che ~**€15.6** di
perdita fantasma colpisse l'equity.
- Costi collaterali: 8 round-trip di fee, 7 TP reduce-only respinti, 8 disaster-SL da
piazzare/cancellare, sim accuracy/report inquinati.
- Auto-esaurito alle 09:15 (sleeve poi flat).
## Fix — guard "TP invertito" (v1.1.33, zero parametri)
`StrategyWorker._open_position`: **prima** di aprire, se il TP è già sfondato all'ingresso
(`long & tp<=entry` oppure `short & tp>=entry`) il segnale è malformato (artefatto wick)
**NON si apre** (niente ordine reale, niente disaster-SL). Verità d'esecuzione, non un
filtro di strategia (stessa filosofia di TP_PHANTOM). Log `INVERTED_TP_SKIP` (dedup
per-barra) + alert Telegram una-tantum per processo. Agisce **solo sul path live**: il
backtest canonico entra al close del bar del segnale (entry == base di tp/sl → mai
invertito) e resta non filtrato → il live farà meglio del backtest, come loss-guard /
trend / TP_PHANTOM.
Test: `tests/portfolio/test_inverted_tp.py` (long/short invertiti skippati, controlli
positivi long/short validi aprono, no-tp apre, dedup per-barra). Suite portfolio 146/146.
## Lezione
Cerotto testnet (il feed wicka): il fix vero è **mainnet**, dove l'arbitraggio elimina i
wick-print. Ma il guard è corretto in assoluto — un segnale con TP dal lato sbagliato
dell'entry non va mai eseguito, su qualsiasi venue.