Files
PythagorasGoal/Old/docs/diary/2026-06-04-shadow-divergence.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

5.3 KiB
Raw Permalink Blame History

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.py72*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.