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:
+27
-1
@@ -118,6 +118,11 @@ class ExecutionClient:
|
||||
client: CerberoClient = field(default_factory=CerberoClient)
|
||||
verify_polls: int = 4 # tentativi di riverifica
|
||||
verify_sleep: float = 0.6 # attesa fra i poll (s)
|
||||
# Disaster-bracket on-book (2026-06-07): distanza % dello STOP_MARKET reduce-only
|
||||
# piazzato a ogni REAL_OPEN come assicurazione per gli outage del feed/runner
|
||||
# (poll-loop fermo = posizione reale senza valutazione exit). None = disattivo.
|
||||
# Configurato dal runner da overrides.execution.disaster_sl_pct.
|
||||
disaster_sl_pct: float | None = None
|
||||
# NB leva: su Deribit la leva per-strumento NON e' impostabile (private/set_leverage
|
||||
# risponde 400 Bad Request — verificato 2026-06-03 nei log Cerbero; il set_leverage
|
||||
# di Cerbero fallisce sempre, soppresso). Il campo "leverage: 50" in get_positions
|
||||
@@ -207,9 +212,12 @@ class ExecutionClient:
|
||||
fee_coin * fill_price if (fee_coin and fill_price) else 0.0)
|
||||
|
||||
# VERIFICA: market = ordine filled E fill riscontrato (trades o history);
|
||||
# limit = accettato in book ('open') o gia' eseguito ('filled')
|
||||
# limit = accettato in book ('open') o gia' eseguito ('filled');
|
||||
# stop_market = trigger accettato ('untriggered' finche' il mark non tocca)
|
||||
if order_type == "market":
|
||||
verified = (state == "filled") and (bool(trades) or th is not None)
|
||||
elif order_type == "stop_market":
|
||||
verified = state in ("untriggered", "open", "filled")
|
||||
else:
|
||||
verified = state in ("open", "filled")
|
||||
return Fill(instrument, side, requested_notional, amount, fill_price,
|
||||
@@ -253,6 +261,24 @@ class ExecutionClient:
|
||||
return self._submit(instrument, opp, amount, 0.0, reduce_only=True,
|
||||
label=label, order_type="limit", price=px)
|
||||
|
||||
def place_disaster_sl(self, instrument: str, entry_side: str, amount: float,
|
||||
stop_price: float, label: str | None = None) -> Fill:
|
||||
"""STOP_MARKET reduce-only LONTANO (disaster bracket ~-30%): assicurazione
|
||||
on-book per gli outage — in operativita' normale non scatta mai (lo SL
|
||||
della strategia esce molto prima, market-on-poll) -> 0 costo Sharpe.
|
||||
Trigger sul MARK price; copre la SOLA quota del worker. Nei crash il fill
|
||||
e' al gap, non al livello: cappa la coda, non la elimina (exit-lab).
|
||||
NB: il set_stop_loss di cerbero_client e' un private/edit Deribit (solo
|
||||
ordini APERTI) -> inutilizzabile su una posizione gia' fillata; il
|
||||
bracket si piazza come ordine trigger autonomo."""
|
||||
opp = "sell" if entry_side == "buy" else "buy"
|
||||
px = quantize_price(instrument, stop_price)
|
||||
if amount <= 0 or px <= 0:
|
||||
return Fill(instrument, opp, 0.0, amount, None, 0.0, 0.0,
|
||||
None, None, False, notes="amount/stop non validi")
|
||||
return self._submit(instrument, opp, amount, 0.0, reduce_only=True,
|
||||
label=label, order_type="stop_market", price=px)
|
||||
|
||||
def cancel_order(self, order_id: str) -> dict:
|
||||
"""Cancella un ordine resting. {'state': 'cancelled'} su successo;
|
||||
'error' se l'ordine non e' piu' open (es. gia' fillato) — NON fatale:
|
||||
|
||||
Reference in New Issue
Block a user