feat(live): disaster-bracket on-book sui fade reali + alert FEED_OUTAGE + osservabilita' multi-asset

Punti 5-6 dell'improvement-sweep 2026-06-06 (protezione capitale + osservabilita'):

Punto 5 — disaster bracket:
- ExecutionClient.place_disaster_sl: STOP_MARKET reduce-only a ~-30% dall'ingresso
  (trigger mark price), piazzato a ogni REAL_OPEN (MR01/MR02/MR07/DIP01) e
  cancellato in _real_close. Assicurazione outage: il poll-loop in except lascia
  le posizioni reali senza valutazione exit (ETH gap max storico 33%/1h). In
  operativita' normale non scatta mai -> 0 costo Sharpe. real_dsl_order_id
  persistito (resume-safe). Config overrides.execution.disaster_sl_pct (0.30).
- NB: set_stop_loss di cerbero-mcp e' un private/edit Deribit (solo ordini APERTI)
  -> non usabile su market fillati; il bracket e' un trigger order autonomo via
  place_order(type=stop_market). Cancel di un trigger order risponde 'untriggered'
  (= successo, verificato testnet: re-cancel -> order_not_found).
- Runner: alert Telegram FEED_OUTAGE dopo 5 poll falliti consecutivi (elenco
  posizioni reali aperte) + notifica RIPRESO con durata.

Punto 6 — osservabilita':
- in_position nei _save() di TR01/ROT02/TSM01; hourly_report: sezione MULTI-ASSET
  (book | ultimo flip | freschezza status) — prima i 3 worker erano invisibili
  (collect() filtra su event/in_position che non emettevano); esclusi dalla
  tabella IN CORSO (assume entry/bars single-leg).
- live_shadow_smoke esteso: scenari C/D SHORT (TP-resting BUY mai esercitato
  prima) + disaster bracket in tutti gli scenari.

Verifiche: 72/72 test; smoke testnet 4 scenari verdi (DSL piazzato/cancellato due
lati, zero ordini orfani sul book, conto flat); multi_asset_section renderizza sui
dati live. Diario docs/diary/2026-06-07-sweep-fixes.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-06-07 09:36:11 +00:00
parent bd5f1a6ea0
commit 62e272255b
12 changed files with 288 additions and 7 deletions
+73
View File
@@ -0,0 +1,73 @@
# 2026-06-07 — Implementazione fix improvement-sweep (P0 + punti 2-6, settimana 1)
Implementati e deployati i fix "settimana 1" del piano dello sweep 2026-06-06
(tutti fix di parità/protezione/osservabilità, nessun cambio di strategia → nessun
gate OOS richiesto). Versioni: v1.1.3 (punti 1-4), v1.1.4 (punti 5-6).
## P0 — cap SHAPE 0.0588 ripristinato (v1.1.3)
`portfolios.yml` ridichiara il dict caps COMPLETO `{PAIRS: 0.33, SHAPE: 0.0588}`
(l'override sostituisce interamente i caps di `_defs.py` via setattr in
`base.py:load_active_portfolio` — footgun ora commentato nel yml). Verificato live
post-restart: SH_BTC+SH_ETH = 0.0588 totale (prima 0.1176 = 2x intended), non-cappati
risaliti a 0.0647, sum = 1.0 — parità col backtest canonico (FULL 6.43 / OOS 8.58).
## Punti 2-4 — parità worker multi-asset (v1.1.3)
- **TR01 fee × leva**: `POS · LEV · fee_rt/2` per flip come il reference
`honest_improve2._tr_basket_daily:150` (prima sotto-caricata 2x).
- **TR01 forming-bar**: crossover EMA E booking del return solo su barre 4h COMPLETE
(riga -1 = candela in corso finché non è trascorsa la sua durata, pattern EXIT-16).
Evidenza live che non può più ripetersi: flip SOL 0→1→0 in 59min stessa finestra 4h.
- **PairsWorker forming-bar**: entry ED exit sul close di barra COMPLETA, come il
backtest `pairs_research` (close settled).
- **TSM01/ROT02 silent-return**: WARN log + Telegram `PANEL_SHORT` quando il panel
inner-join è sotto il lookback (helper condiviso `_warn_panel_short` in
rotation_worker), gated su "era già operativo", una notifica per episodio.
Verifiche: 72 test (7 nuovi), `validate_worker_pairs` ESATTO, `validate_honest_workers`
invariato. **Nota onesta**: TR01 replay worker 44% vs reference +42% è IDENTICO
pre/post fix → divergenza di convenzione pre-esistente (capitale-unico con
`mean(rets)` solo sugli asset in posizione vs media-equity 1/N del reference).
Da rivisitare a parte: "stesso ordine di grandezza" oggi non regge più.
## Punto 5 — disaster-bracket on-book + alert outage (v1.1.4)
Durante un outage (poll-loop in except) le posizioni REALI restavano senza stop sul
book e senza valutazione exit. Ora:
- **`ExecutionClient.place_disaster_sl`**: STOP_MARKET reduce-only a ~30%
dall'ingresso (trigger sul mark), piazzato a ogni REAL_OPEN sui fade eseguiti
(MR01/MR02/MR07/DIP01) e cancellato in `_real_close` (prima del TP-reconcile).
In operatività normale non scatta mai → 0 costo Sharpe; nei crash il fill è al
gap (cappa la coda, non la elimina). Config: `overrides.execution.disaster_sl_pct`
(default 0.30, 0 = off). `real_dsl_order_id` persistito (resume-safe).
- **SCOPERTA**: il `set_stop_loss` di cerbero-mcp è un `private/edit` Deribit (solo
ordini APERTI) → inutilizzabile su market già fillati; il bracket va piazzato come
trigger order autonomo via `place_order(type="stop_market")` (già supportato).
- **Semantica cancel trigger order** (verificata su testnet): la cancel risponde con
lo stato AL MOMENTO della cancel (`untriggered` = successo; il re-cancel dà
`order_not_found`); `error` = non più in book (probabile trigger scattato → il
market a valle filla 0 e REAL_CLOSE esce verified=False).
- **Alert `FEED_OUTAGE`**: dopo 5 poll falliti consecutivi (~5 min) Telegram con
l'elenco delle posizioni reali aperte; notifica di ripresa con durata.
Smoke testnet (`live_shadow_smoke.py`, esteso): 4 scenari long+short — il path
**short-side TP-resting** (BUY limit reduce-only) non era MAI stato esercitato.
Tutti verdi: resting/cross-immediato due lati, DSL piazzato/cancellato (verificato
zero ordini orfani sul book), conto flat a fine smoke.
## Punto 6 — osservabilità multi-asset (v1.1.4)
- `in_position` aggiunto ai `_save()` di TR01/ROT02/TSM01.
- `hourly_report`: nuova sezione **MULTI-ASSET** (book corrente | ultimo flip |
freschezza status) — prima i 3 worker erano invisibili nel report (collect()
filtra su event/in_position che non emettevano): impossibile distinguere
"flat/risk-off by-design" da "wiring rotto". I multi-asset sono esclusi dalla
tabella IN CORSO (assume entry/bars single-leg).
## Resta in roadmap (settimana 2, OGNUNO dietro gate PORT06/OOS)
trend_max=3.0 sulle 6 fade → pos pairs 0.15-0.25 per-famiglia → DIP01 EXIT-16
(validazione gap-aware) → SH01 ri-validazione train-window ~8760 barre.
Follow-up tecnico: divergenza convenzione TR01 worker vs reference (sopra).