feat(live): reconcile resting + orphan single-leg + circuit-breaker venue-lock + FEED_BOOK_GAP
Codice della tornata v1.1.27/28 (gia' in produzione, mai committato): - reconcile_account: estensione ordini RESTING (FILLED_UNBOOKED/MISSING/STALE, caso MR02_BTC: TP fillato di notte scoperto ore dopo) + expected_resting in books - strategy_worker: orphan_legs su REAL_CLOSE_PARTIAL anche single-leg, persistito - execution: circuit-breaker su venue-lock admin (stop ordini dopo errori ripetuti) - runner/hourly_report: alert FEED_BOOK_GAP + timestamp closed trades - cerbero_client: get_open_orders (merge all + trigger_all) Test: 12 nuovi, suite completa 126 passed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -96,8 +96,14 @@ def lossguard_section() -> str:
|
||||
return "\n".join(L)
|
||||
|
||||
|
||||
# Epoca v1.1.26 (deploy 2026-06-11 ~21:40 UTC): gate TP_PHANTOM attivo. I close
|
||||
# precedenti includono il churn da TP fantasma dell'11-06 17:32-17:58 (~24 giri,
|
||||
# win-rate inquinato) -> le accuratezze "pulite" si leggono da qui in poi.
|
||||
EPOCH_V1126 = "2026-06-11T21:40:00"
|
||||
|
||||
|
||||
def collect():
|
||||
closed = [] # (sleeve, reason, net_return, pnl, win)
|
||||
closed = [] # (sleeve, reason, net_return, pnl, win, ts)
|
||||
open_pos = [] # dict per posizione aperta
|
||||
realized = 0.0
|
||||
for sp in sorted(glob.glob(str(PAPER / "*" / "status.json"))):
|
||||
@@ -119,7 +125,7 @@ def collect():
|
||||
# resta il sim diagnostico: sui TP fantasma da spike testnet diceva
|
||||
# 26/0 mentre il reale era 11/15). Fallback nr>0 per eventi storici.
|
||||
closed.append((_short(wid), ev.get("reason", "?"), nr, pnl,
|
||||
bool(ev.get("win", nr > 0))))
|
||||
bool(ev.get("win", nr > 0)), ev.get("ts", "")))
|
||||
if "positions" in st or "weights" in st:
|
||||
continue # multi-asset (TR01/ROT02/TSM01): sezione dedicata
|
||||
if st.get("in_position"):
|
||||
@@ -187,7 +193,7 @@ def build_report() -> str:
|
||||
|
||||
# breakdown per motivo
|
||||
by_reason = defaultdict(lambda: [0, 0, 0.0]) # reason -> [win, loss, pnl]
|
||||
for _, reason, _, pnl, win in closed:
|
||||
for _, reason, _, pnl, win, _ in closed:
|
||||
r = by_reason[reason]
|
||||
r[0 if win else 1] += 1
|
||||
r[2] += pnl
|
||||
@@ -206,8 +212,12 @@ def build_report() -> str:
|
||||
if eq is not None:
|
||||
L.append(f"Equity €{eq:.2f} | Cap €{cap:.2f} | maxDD {dd:.3f}%")
|
||||
|
||||
# 1) CHIUSI
|
||||
# 1) CHIUSI — totale storico + epoca corrente (post gate TP_PHANTOM): i
|
||||
# numeri pre-v1.1.26 includono il churn fantasma e non misurano la strategia
|
||||
cur = [c for c in closed if c[5] >= EPOCH_V1126]
|
||||
cpos = sum(1 for c in cur if c[4])
|
||||
L.append(f"\n✅ <b>CHIUSI</b>: {pos} positivi / {neg} negativi (netto fee)")
|
||||
L.append(f" epoca v1.1.26+ (TP_PHANTOM attivo): {cpos}/{len(cur) - cpos}")
|
||||
rows = [f"{'motivo':<12}{'✅':>3}{'❌':>4}{'PnL€':>9}"]
|
||||
for reason, (w, l, pnl) in sorted(by_reason.items(), key=lambda x: x[1][2]):
|
||||
rows.append(f"{reason:<12}{w:>3}{l:>4}{pnl:>+9.2f}")
|
||||
|
||||
Reference in New Issue
Block a user