fix(book): gate fail-safe posizione + diagnostica equity — niente errori silenziosi nel path live
Terza+quarta chiusura del pattern "eccezione ingoiata -> stato safe silenzioso"
in src/live, dopo skh_error (31369b3). Emerse durante la verifica del gate SKH01.
Opzione A (gate fail-safe posizione):
- shadow._positions: se la read della posizione reale Deribit lancia, assume flat
MA ora ritorna un pos_error esplicito (prima solo una note mai propagata).
- Propagato shadow_report -> book_report -> book_execute: se ONLINE ma posizione
IGNOTA, l'esecutore NON opera a cieco (return + alert), come gia' per 'online'.
Opzione B (diagnostica equity, no halt):
- shadow._equity/shadow_report: se ONLINE ma equity reale non leggibile, il book
ripiega su paper_cap (~$2000) invece del conto reale (~$598) -> sovradimensiona
~33%, ma l'hard-cap $300/asset limita il downside. Nuovo flag eq_fallback ->
book_execute stampa warning + alert Telegram MA prosegue (niente gate: la scelta
e' solo diagnostica, l'hard-cap gia' protegge).
Test: +8 (4 gate A, 4 diagnostica B). Suite 156/156. Path live pulito
(skh_error/pos_error/eq_fallback = None). Diario 2026-07-01-book-live-error-surfacing.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+2
-1
@@ -136,6 +136,7 @@ def book_report(offline: bool = False, equity_override: float | None = None,
|
||||
last_data=sh["last_data"], online=sh["online"],
|
||||
real_equity=sh["real_equity"], equity=equity, eq_basis=sh["eq_basis"],
|
||||
cap_per_asset=cap, weights=dict(TP01=W_TP01, SKH01=W_SKH),
|
||||
assets=assets, orders=orders, skh_error=skh_error,
|
||||
assets=assets, orders=orders, skh_error=skh_error, pos_error=sh.get("pos_error"),
|
||||
eq_fallback=sh.get("eq_fallback"),
|
||||
flat=all(abs(x["net_target"]) < FLAT_USD for x in assets),
|
||||
)
|
||||
|
||||
+17
-5
@@ -72,14 +72,18 @@ def _marks(client, dfs):
|
||||
|
||||
def _positions(client):
|
||||
if client is None:
|
||||
return {a: 0.0 for a in ASSETS}, "offline -> assunto flat"
|
||||
pos, note = {}, "mainnet"
|
||||
return {a: 0.0 for a in ASSETS}, "offline -> assunto flat", None
|
||||
pos, note, failed = {}, "mainnet", []
|
||||
for a in ASSETS:
|
||||
try:
|
||||
pos[a] = client.position_usd(INSTRUMENT[a])
|
||||
except Exception as e:
|
||||
pos[a], note = 0.0, f"read fallito ({type(e).__name__}) -> assunto flat"
|
||||
return pos, note
|
||||
failed.append(f"{a} ({type(e).__name__}: {e})")
|
||||
# pos_error != None SOLO se ONLINE ma la read ha lanciato: la posizione e' IGNOTA (assunta flat) ->
|
||||
# l'esecutore NON deve operare a cieco. L'offline (client=None) e' gestito a parte dal gate 'online'.
|
||||
err = ("posizione non leggibile, assunta FLAT: " + ", ".join(failed)) if failed else None
|
||||
return pos, note, err
|
||||
|
||||
|
||||
def _equity(client, marks):
|
||||
@@ -101,6 +105,8 @@ def _equity(client, marks):
|
||||
pass
|
||||
if any_ok and tot > 1:
|
||||
return tot, "mainnet coin-margined"
|
||||
# real_eq=None qui -> shadow_report ripiega su paper_cap e alza eq_fallback (diagnostica B):
|
||||
# segnalato a book_execute (alert), non blocca (l'hard-cap $300/asset limita il downside).
|
||||
return None, "conto flat / non finanziato"
|
||||
|
||||
|
||||
@@ -113,7 +119,7 @@ def shadow_report(offline: bool = False, equity_override: float | None = None) -
|
||||
|
||||
client = None if offline else _safe_client()
|
||||
marks, marks_src = _marks(client, dfs)
|
||||
positions, pos_src = _positions(client)
|
||||
positions, pos_src, pos_error = _positions(client)
|
||||
real_eq, eq_src = _equity(client, marks)
|
||||
|
||||
paper = json.loads(PAPER_STATE.read_text()) if PAPER_STATE.exists() else None
|
||||
@@ -124,6 +130,11 @@ def shadow_report(offline: bool = False, equity_override: float | None = None) -
|
||||
equity = equity_override if equity_override is not None else (real_eq if real_eq else paper_cap)
|
||||
eq_basis = ("override" if equity_override is not None
|
||||
else eq_src if real_eq else "paper capital (ipotetico: conto non finanziato)")
|
||||
# Opzione B (diagnostica): ONLINE ma equity reale non leggibile -> sizing su paper_cap invece del
|
||||
# conto reale. NON blocca (l'hard-cap $300/asset limita il downside), ma va SEGNALATO.
|
||||
eq_fallback = (f"equity reale non leggibile ({eq_src}) -> sizing su paper_cap ${paper_cap:,.0f} "
|
||||
f"invece del conto reale") if (equity_override is None and client is not None
|
||||
and not real_eq) else None
|
||||
|
||||
assets, orders = [], []
|
||||
for a in ASSETS:
|
||||
@@ -172,7 +183,8 @@ def shadow_report(offline: bool = False, equity_override: float | None = None) -
|
||||
last_data=str(pd.Timestamp(last_ts, unit="ms", tz="UTC").date()),
|
||||
online=(client is not None and marks_src.get("BTC") == "mainnet"),
|
||||
real_equity=real_eq, equity=equity, eq_basis=eq_basis,
|
||||
pos_src=pos_src, assets=assets, orders=orders, live_trades=live_trades,
|
||||
pos_src=pos_src, pos_error=pos_error, eq_fallback=eq_fallback,
|
||||
assets=assets, orders=orders, live_trades=live_trades,
|
||||
disaster_sls=disaster_sls,
|
||||
flat=all(abs(targets[a]) < 1e-9 for a in ASSETS),
|
||||
paper_aligned=(paper_ts == last_ts),
|
||||
|
||||
Reference in New Issue
Block a user