feat(live): TP reale = LIMIT reduce-only al livello (fix +235bps slippage exit)
All'apertura reale il worker piazza un limit reduce-only al TP della strategia (quota del SOLO worker, prezzo quantizzato al tick); alla chiusura sim cancella il resting, riconcilia i fill (anche parziali) via get_trade_history per order_id e chiude a market solo il residuo. real_tp_order_id persistito (resume-safe). SL resta market-on-poll (deliberato: trigger Deribit = nuovo order_id al trigger, fill non verificabile; e il rimbalzo sul SL lavora a favore). Smoke testnet 2 scenari OK (resting+cancel / fill immediato). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -37,3 +37,56 @@ Dettagli da considerare:
|
||||
- i 3 fade BTC condividono lo strumento (posizioni nettate per conto): il limit
|
||||
reduce-only deve coprire la SOLA quota del worker, come già fa `close_amount`;
|
||||
- verificare il fill del resting order via `get_trade_history` (order_id), come per i market.
|
||||
|
||||
## IMPLEMENTATO (sera): LIMIT reduce-only al TP
|
||||
|
||||
**Flusso nuovo.** A ogni `REAL_OPEN` verificato il worker piazza subito un
|
||||
**limit reduce-only al livello TP** (lato opposto, stesso `amount` della SOLA
|
||||
quota del worker — strumento condiviso fra i 3 fade per coin) → evento
|
||||
`REAL_TP_RESTING` (o `REAL_TP_FAIL` → si resta sul vecchio market-on-poll).
|
||||
Alla chiusura sim, qualunque sia la ragione, `_real_close` ora:
|
||||
1. **cancella** il resting (innocuo se già fillato — e dopo il cancel nessun
|
||||
fill può più arrivare, niente race);
|
||||
2. **riconcilia i fill** (anche parziali) dal `get_trade_history` per order_id
|
||||
(stessa fonte autorevole dei market), con retry se il cancel risulta
|
||||
"già fillato" ma la history è in ritardo;
|
||||
3. chiude a **market reduce-only solo la quota residua** (exit non-TP:
|
||||
stop_loss/time_limit, o TP non raggiunto sullo strumento d'esecuzione);
|
||||
4. ledger reale su **prezzo d'uscita combinato** (media pesata fill-TP +
|
||||
market) e fee sommate. `REAL_CLOSE` logga `tp_order_id`,
|
||||
`tp_filled_amount`, `market_amount`.
|
||||
|
||||
**Dettagli del TODO coperti:**
|
||||
- `max_bars`/`stop_loss` → il cancel è il passo 1 di ogni chiusura;
|
||||
- restart → `real_tp_order_id` è **persistito** in `status.json`: al resume il
|
||||
resting resta in book e la prima chiusura lo riconcilia normalmente;
|
||||
- quota del worker → il limit usa lo stesso `amount` dell'apertura (mai
|
||||
`close_position`); il residuo è ri-quantizzato allo step (Decimal) dentro
|
||||
`close_amount` (niente artefatti float tipo `0.072-0.024`);
|
||||
- prezzi limit quantizzati al **tick** dello strumento (`quantize_price`,
|
||||
BTC_USDC 0.5 / ETH_USDC 0.05 — Deribit rifiuta prezzi off-tick).
|
||||
|
||||
**SL: niente stop-market, decisione deliberata.** (a) Su Deribit un trigger
|
||||
order, quando scatta, genera l'ordine eseguito con un **nuovo order_id** → il
|
||||
fill non sarebbe verificabile via `get_trade_history` per order_id (il
|
||||
requisito di verifica); (b) la misura di stamattina mostra che sul SL il
|
||||
market-on-poll lavora **a favore** (il rimbalzo riduce la perdita). Lo SL resta
|
||||
quindi market-on-poll.
|
||||
|
||||
**Bonus fee.** Quando il TP filla da resting il fill è **maker** (~0% su
|
||||
Deribit) invece di taker 0.05% — il reale farà meglio dell'assunto 0.10% RT
|
||||
sulle uscite TP.
|
||||
|
||||
**Verifica.** `live_shadow_smoke.py` esteso ai due percorsi e passato su
|
||||
testnet: (A) resting in book → exit `time_limit` → cancel + market fallback,
|
||||
conto flat; (B) TP già crossato → fill immediato del limit, chiusura
|
||||
riconciliata dalla history **senza ordine market** (`market_amount=0`),
|
||||
prezzo persino migliore del TP (+48 bps, taker sul cross). 54 test pytest OK.
|
||||
|
||||
**Caveat residuo onesto.** Se il processo muore DOPO il fill del resting e
|
||||
PRIMA della chiusura sim, il worker al resume riconcilia al prossimo exit sim
|
||||
(corretto); ma nell'intervallo la quota reale è già chiusa mentre il sim la
|
||||
crede aperta — finestra breve e solo contabile. Inoltre il TP del sim è sul
|
||||
feed (`BTC-PERPETUAL` inverse) mentre il limit vive sul lineare USDC: la base
|
||||
fra i due può far fillare/non fillare il limit a cavallo del livello — il
|
||||
fallback market copre il caso.
|
||||
|
||||
Reference in New Issue
Block a user