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>
This commit is contained in:
Adriano Dal Pastro
2026-06-16 14:16:13 +00:00
parent ef797f1ff0
commit b576ee66ac
4 changed files with 187 additions and 4 deletions
+13
View File
@@ -502,6 +502,19 @@ queste fade, ma va confermato col paper trader live prima di rischiare capitale
Fill (anche parziale) o prezzo oltre il livello o worker non eseguito → comportamento
storico. Fail-open su errori di rete. Log `TP_PHANTOM` dedup per barra + alert Telegram.
Test `tests/portfolio/test_tp_phantom.py`.
- **INVERTED_TP_SKIP — l'entry con TP dal lato sbagliato va soppressa (v1.1.33, 2026-06-16).**
Un wick transitorio sul feed puo' far calcolare alla strategia un `tp` dal lato SBAGLIATO
dell'entry (es. donchian: segnale su barra wickata in basso → tp=centro canale, entry al
prezzo RECUPERATO sopra il proprio tp). L'exit intrabar `bar_high>=tp` (long)/`bar_low<=tp`
(short) scatta a `bars_held=0` in PERDITA, con churn di fee e TP reduce-only respinti
(16-06: 8 giri MR02_BTC 15m, sim 17.9 / reale 2.3 grazie al real-truth; TP_PHANTOM NON
lo prende — niente resting oracle, il prezzo HA superato il livello). Gate in
`StrategyWorker._open_position` (zero parametri, verita' d'esecuzione): se `tp<=entry`
(long) / `tp>=entry` (short) → **entry soppressa** (niente ordine reale/disaster-SL), log
`INVERTED_TP_SKIP` dedup per-barra + alert Telegram. Solo path live (il backtest entra al
close del bar del segnale → mai invertito, resta non filtrato → live meglio del backtest).
Cerotto testnet (il fix vero e' mainnet). Test `tests/portfolio/test_inverted_tp.py`,
diario `docs/diary/2026-06-16-inverted-tp-guard.md`.
- **TP reale = LIMIT reduce-only AL LIVELLO (2026-06-04).** Misurati +235 bps di slippage medio sulle uscite take-profit market-on-poll (sim esce al livello intrabar, il reale chiudeva al poll post-rimbalzo: sim +11.85 vs reale +0.62 USD sui primi 7 close). Fix: a ogni `REAL_OPEN` il worker piazza un **limit reduce-only al TP** (`ExecutionClient.place_tp_limit`, prezzo quantizzato al tick, SOLA quota del worker) → `REAL_TP_RESTING`; a ogni chiusura sim `_real_close` **cancella il resting → riconcilia i fill (anche parziali) via `get_trade_history` per order_id → market reduce-only solo del residuo** → ledger su prezzo combinato. `real_tp_order_id` persistito in `status.json` (resume-safe). Lo **SL resta market-on-poll** (deliberato: i trigger Deribit generano un nuovo order_id al trigger → fill non verificabile per order_id; e sul SL il rimbalzo lavora a favore). Fill da resting = fee **maker ~0%**. Smoke: `live_shadow_smoke.py` (2 scenari, testnet). Diario `docs/diary/2026-06-04-shadow-divergence.md`.
- **position_size per-famiglia (2026-06-07).** `portfolios.yml` accetta `position_size_family`
(chiave = `weighting.family_of`); plumbing `runner.pos_for_spec`. **PAIRS a 0.20** (esposizione