book_execute: integrazione U1+U2 — un solo Telegram in solo protezione, stato unknown, set_open_cap diretto (#18, #21, #22, #24)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -155,9 +155,9 @@ def _run():
|
||||
|
||||
if r.get("skh_error"): # SKH feed fallito -> book.py ha forzato flat IN SILENZIO
|
||||
# (#22) Non e' piu' "SKH forzato flat!": book_execute non manda PIU' ordini basati su
|
||||
# quel segnale ignoto (modo SOLO PROTEZIONE, sotto) — i target restano informativi.
|
||||
# quel segnale ignoto (modo SOLO PROTEZIONE, sotto, che porta anche questo errore su
|
||||
# Telegram — qui solo il print, per non mandare due notify per lo stesso guasto).
|
||||
print(f" ⚠️ SKH FEED ERRORE (SKH non calcolato: nessun ordine, solo protezione): {r['skh_error']}")
|
||||
notify("⚠️ BOOK — SKH feed fallito (sleeve forzato flat)", {"error": r["skh_error"]})
|
||||
|
||||
# --- FRESCHEZZA del feed 5m usato per il segnale SKH (2026-07-26) --------------------------
|
||||
# `fresh_5m` ricade sul feed certificato IN SILENZIO se il fetch pubblico Deribit fallisce, e
|
||||
@@ -248,9 +248,9 @@ def _run():
|
||||
return
|
||||
|
||||
if r.get("eq_fallback"): # equity reale non leggibile -> niente sizing sul conto
|
||||
# (#21) qui solo il print: il notify e' nel blocco SOLO PROTEZIONE sotto, che porta anche
|
||||
# questo errore su Telegram — un solo avviso per lo stesso guasto, non due.
|
||||
print(f" ⚠️ EQUITY FALLBACK (nessun ordine, solo protezione): {r['eq_fallback']}")
|
||||
if do_execute: # (#21) segnalato: il modo SOLO PROTEZIONE e' sotto
|
||||
notify("⚠️ BOOK LIVE — equity fallback (sizing su paper_cap)", {"nota": r["eq_fallback"]})
|
||||
|
||||
# --- SOLO PROTEZIONE (#21 equity ignota, #22 SKH ignoto) ------------------------------------
|
||||
# Principio unico: quando il giro non sa QUANTO vale il conto o QUALE e' il segnale, NON manda
|
||||
@@ -259,6 +259,10 @@ def _run():
|
||||
# da posizione e mark, non da equity o segnale. Stesso principio che pos_error applica gia'
|
||||
# bloccando l'esecuzione, qui senza saltare la protezione.
|
||||
solo_protezione: str | None = r.get("eq_fallback") or r.get("skh_error")
|
||||
# Calcolato una volta sola: serve sia nell'azione per-asset del ciclo sotto sia nel
|
||||
# riepilogo finale (#21, #22) — un solo motivo, non due troncamenti che potrebbero divergere.
|
||||
motivo_breve = (solo_protezione if solo_protezione and len(solo_protezione) <= 60
|
||||
else (solo_protezione[:60] + "…" if solo_protezione else None))
|
||||
if solo_protezione and do_execute:
|
||||
titolo = ("⚠️ BOOK LIVE — equity non leggibile: nessun ordine, solo protezione"
|
||||
if r.get("eq_fallback") else
|
||||
@@ -290,6 +294,7 @@ def _run():
|
||||
# non deve togliere la rete di sicurezza all'altro.
|
||||
falliti: list[str] = []
|
||||
scoperti: list[str] = []
|
||||
non_verificati: list[str] = []
|
||||
for a in r["assets"]:
|
||||
asset, inst = a["asset"], a["instrument"]
|
||||
net, cur, mark = a["net_target"], a["position_usd"], a["mark"]
|
||||
@@ -299,7 +304,6 @@ def _run():
|
||||
if solo_protezione:
|
||||
# (#21, #22) nessun rebalance: l'azione stampata lo dice, non e' "a target" — e'
|
||||
# "non tocco perche' non so quanto vale il conto / qual e' il segnale".
|
||||
motivo_breve = solo_protezione if len(solo_protezione) <= 60 else solo_protezione[:60] + "…"
|
||||
act = f"HOLD (solo protezione: {motivo_breve})"
|
||||
elif order is None:
|
||||
act = "HOLD (a target)"
|
||||
@@ -314,11 +318,10 @@ def _run():
|
||||
|
||||
try:
|
||||
if do_execute and order is not None and not solo_protezione:
|
||||
# U1/U2 si uniscono dopo questo rilascio: `set_open_cap` e' di U1 (execution.py,
|
||||
# stesso file, non toccato qui). hasattr perche' oggi DeribitTrader non lo ha
|
||||
# ancora — l'hasattr si toglie al merge, quando entrambi vivono nello stesso ramo.
|
||||
if hasattr(trader, "set_open_cap"):
|
||||
trader.set_open_cap(inst, r["cap_per_asset"] * r.get("scala", 1.0), mark)
|
||||
# (#18) tetto di apertura derivato dal cap di config del libro. Se cap_per_asset*scala
|
||||
# o mark sono <=0 solleva ValueError: e' giusto, finisce nell'isolamento per asset
|
||||
# (except sotto), non deve fermare il giro intero.
|
||||
trader.set_open_cap(inst, r["cap_per_asset"] * r.get("scala", 1.0), mark)
|
||||
try:
|
||||
fills = trader.rebalance_signed(inst, net, mark, min_usd=min_order)
|
||||
except RebalanceParziale as rp:
|
||||
@@ -360,6 +363,15 @@ def _run():
|
||||
"amount": round(ds.get("amount") or 0, 4),
|
||||
"azione": "ripiazzare il bracket a mano, oppure chiudere la posizione",
|
||||
"notes": str(ds.get("notes"))[:300]})
|
||||
elif stato == "unknown":
|
||||
# (#24) posizione o open_orders illeggibili: NON e' "naked" (non si e' tolto
|
||||
# nulla, ensure_disaster_sl non cancella ne' piazza su dati a meta'). E' un
|
||||
# giro degradato lo stesso: non si sa se la protezione esistente e' ancora
|
||||
# coerente con la posizione attuale.
|
||||
non_verificati.append(asset)
|
||||
print(f" ⚠️ {asset}: protezione NON VERIFICATA — {ds.get('notes')}")
|
||||
notify("⚠️ BOOK — disaster-SL non verificabile (lettura posizione/ordini fallita)",
|
||||
{"asset": asset, "notes": str(ds.get("notes"))[:300]})
|
||||
except Exception as e: # noqa: BLE001 — isolamento per asset: l'altro deve continuare
|
||||
d = diagnose(errori_dal_report(r) + [f"{type(e).__name__}: {e}"])
|
||||
falliti.append(asset)
|
||||
@@ -379,9 +391,15 @@ def _run():
|
||||
print(f" 🚨 POSIZIONI SCOPERTE (nessun disaster-SL on-book): {', '.join(scoperti)}")
|
||||
if falliti:
|
||||
print(f" 🛑 asset falliti in questo giro: {', '.join(falliti)}")
|
||||
if non_verificati:
|
||||
print(f" ⚠️ disaster-SL NON VERIFICATO (lettura posizione/ordini fallita): {', '.join(non_verificati)}")
|
||||
if not do_execute:
|
||||
print(" => DRY-RUN: nessun ordine inviato." +
|
||||
("" if enabled else " Per armare: config/live.json execution_enabled=true + --execute."))
|
||||
elif solo_protezione:
|
||||
# (#21, #22) non e' "Nessuna azione: conto gia' al target" — il conto potrebbe NON esserci
|
||||
# per niente al target, semplicemente non lo si sa. Distinto dal ramo HOLD sotto.
|
||||
print(f" => SOLO PROTEZIONE: nessun ordine di ribilancio ({motivo_breve}), disaster-SL controllato.")
|
||||
elif all(x.startswith("HOLD") for x in actions):
|
||||
print(" => Nessuna azione: conto gia' al target netto del book.")
|
||||
else:
|
||||
@@ -391,7 +409,9 @@ def _run():
|
||||
# (#21, #22) SOLO PROTEZIONE e' degradato anche senza asset falliti/scoperti: il giro non ha
|
||||
# ribilanciato nulla perche' non sapeva quanto vale il conto o qual era il segnale. Solo se
|
||||
# il giro era ARMATO: in DRY-RUN non si e' comunque tentato nulla, e' solo un'anteprima.
|
||||
if scoperti or falliti or (solo_protezione and do_execute):
|
||||
# (#24) `non_verificati` e' degradato allo stesso modo: la protezione esistente non e' stata
|
||||
# controllata, indipendentemente da solo_protezione.
|
||||
if scoperti or falliti or non_verificati or (solo_protezione and do_execute):
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user