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>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
# 2026-06-04 — Shadow execution: divergenza sim/reale misurata + TODO limit reduce-only
|
||||
|
||||
## Deploy v1.0.6
|
||||
|
||||
Fix float deployata (`74670da` + release): `_quantize_step` con `Decimal` in
|
||||
`src/live/execution.py` — `72*0.001 = 0.07200000000000001` causava `Invalid params`
|
||||
su Deribit (1 `REAL_OPEN_FAIL` su MR07 il 2026-06-03). Resume pulito al restart.
|
||||
|
||||
## Misura della divergenza sim vs reale (primi 7 close shadow)
|
||||
|
||||
**Bilancio: PnL sim +11.85 USD vs reale +0.62 USD → delta −11.22 USD.**
|
||||
Le fee NON sono il problema ($0.63 totali = 0.05%/lato, esattamente l'assunto del
|
||||
backtest). Il problema è tutto nel **timing delle uscite**.
|
||||
|
||||
- **Aperture allineate**: slippage da −8 a +27 bps, mediana ~±7 bps. OK.
|
||||
- **Chiusure take-profit: +235 bps di slippage medio** (MR07 ETH short: +257/+267/+283/+333 bps).
|
||||
Causa strutturale, non liquidità: il sim esce *al livello TP* intrabar (come il
|
||||
backtest), il reale chiude **a mercato solo al poll del tick**, quando il prezzo è
|
||||
già rientrato (TP toccato a ~1790, fill reale a ~1840). Sim +3 USD a trade, reale ~0.
|
||||
- **Stop-loss: stessa meccanica ma a favore** (MR02 SL: −0.68 reale vs −2.87 sim).
|
||||
Le fade però hanno più TP che SL → netto negativo.
|
||||
- **2 `REAL_CLOSE` non verificate** (order_id null, fallback su stima sim). Venue flat
|
||||
(`get_positions()=[]`), nessuna posizione orfana — ma sorvegliare i prossimi close.
|
||||
|
||||
**Implicazione onesta: l'edge di MR07 non sopravvive all'esecuzione market-on-poll.**
|
||||
Il backtest assume il fill al livello TP; il reale incassa il prezzo post-rimbalzo.
|
||||
|
||||
## TODO (richiesto, da implementare)
|
||||
|
||||
Nello shadow-execution (`src/live/execution.py` + `StrategyWorker`): all'apertura
|
||||
della posizione reale piazzare su Deribit un **LIMIT reduce-only appoggiato al
|
||||
livello TP** (e idealmente stop-market per lo SL), invece di chiudere a mercato al
|
||||
poll — così il fill reale replica l'assunzione intrabar del backtest.
|
||||
|
||||
Dettagli da considerare:
|
||||
- cancellare/aggiornare l'ordine resting se il sim esce per `max_bars` o al restart (resume);
|
||||
- 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