Files
PythagorasGoal/Old/docs/diary/2026-06-16-inverted-tp-guard.md
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

3.8 KiB
Raw Permalink Blame History

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.