Files
PythagorasGoal/docs/diary/2026-06-16-inverted-tp-guard.md
T
Adriano Dal Pastro b576ee66ac feat(worker): guard TP-invertito — sopprime l'entry quando il TP e' gia' sfondato (wick-print)
Un wick transitorio fa calcolare un tp dal lato sbagliato dell'entry (donchian: segnale
su barra wickata, entry al prezzo recuperato oltre il proprio tp) -> l'exit intrabar
scatta a bars_held=0 in perdita (16-06: 8 giri MR02_BTC 15m, sim -17.9 / reale -2.3).
TP_PHANTOM non lo prende (niente resting oracle, prezzo oltre il livello). Gate
zero-parametri in StrategyWorker._open_position, solo path live. Test + diario + CLAUDE.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 14:16:13 +00:00

3.8 KiB
Raw 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.