From a9a6c487d2459ff172c4e8b635d9b5d089c990ce Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Wed, 19 Aug 2026 20:10:31 +0200 Subject: [PATCH] Circuit-Breaker und Tages-P&L summieren jetzt BEIDE Positions-Slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Letzte offene Luecke des Zwei-Slot-Umbaus (User: "ja"). WARUM ES ZWINGEND IST: nach dem Umbau kann BRK parallel zu einer manuellen oder M15-Position laufen. Haette der Tages-P&L weiter nur den ersten Slot gesehen, waere eine offene BRK-Position unsichtbar geblieben - der Breaker griffe also genau dann zu spaet, wenn zwei Positionen gleichzeitig laufen und das Risiko doppelt ist. Bei 40 % Einsatz je Position sind 80 % der Margin gebunden. GEAENDERT: - snapshot(): day_pl["open"] summiert beide Slots - _check_circuit_breaker(): dieselbe Summe als Ausloeser - und der Breaker schliesst jetzt BEIDE Positionen - eine Notbremse, die nur die Haelfte schliesst, ist keine. Fehlschlag am zweiten Slot wird geloggt, nicht verschluckt. 91 Tests gruen, Deploy ueber tools/deploy.py --feld position_brk. ⚠ NEBENBEFUND beim Verifizieren, NICHT von dieser Aenderung verursacht: der Circuit-Breaker steht auf 8 % (runtime_state cb_limit_pct = 8.0) und hat heute dreimal ausgeloest (19:17, 20:03, 20:07). Der Tages-P&L liegt bei -178,89 EUR gegen ein Limit von -55,89 EUR. Damit sind fuer heute KEINE autonomen Trades mehr moeglich - auch BRK nicht. Manuelle Orders bleiben frei (so gebaut). Co-Authored-By: Claude Opus 5 --- core/engine.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/core/engine.py b/core/engine.py index 5e3ef82..9498c64 100644 --- a/core/engine.py +++ b/core/engine.py @@ -964,7 +964,9 @@ class TradingEngine: if bal <= 0: return ps = self.trader.snapshot() - open_pnl = float(ps.get("pnl") or 0.0) + # ⚠ BEIDE Slots — s. Begruendung im Snapshot-Block. + ps_brk = self.trader_brk.snapshot() or {} + open_pnl = float(ps.get("pnl") or 0.0) + float(ps_brk.get("pnl") or 0.0) day_total = self._day_realized_cached() + open_pnl limit_eur = self._cb_limit_pct / 100.0 * bal if day_total > -limit_eur: @@ -978,6 +980,15 @@ class TradingEngine: tg_on = (tg.get("enabled", "false").lower() == "true" and tg.get("bot_token")) if ps.get("ticket"): err = self.trader.close(reason="circuit_breaker") + # ⚠ Der zweite Slot MUSS mit — eine Notbremse, die nur die Haelfte + # schliesst, ist keine. + if ps_brk.get("ticket"): + try: + e2 = self.trader_brk.close(reason="circuit_breaker") + if e2: + log.warning(f"Circuit-Breaker: BRK-Slot nicht geschlossen — {e2}") + except Exception as ce: + log.warning(f"Circuit-Breaker BRK-Close: {ce}") if err: log.error(f"Circuit-Breaker-Close fehlgeschlagen: {err}") if tg_on: @@ -4381,7 +4392,16 @@ class TradingEngine: .get("total_pnl") or 0.0) except Exception as e: log.warning(f"day stats: {e}"); day_realized = 0.0 + # ⚠⚠ BEIDE Slots summieren (Zwei-Slot-Umbau 2026-08-19). Wuerde hier + # nur der erste stehen, saehe der Tages-P&L eine offene BRK-Position + # nicht — und der Circuit-Breaker griffe zu spaet, also genau dann + # nicht, wenn zwei Positionen gleichzeitig laufen und das Risiko + # doppelt ist. open_pnl = float(position.get("pnl") or 0.0) + try: + open_pnl += float((self.trader_brk.snapshot() or {}).get("pnl") or 0.0) + except Exception: + pass day_pl = {"realized": round(day_realized, 2), "open": round(open_pnl, 2), "total": round(day_realized + open_pnl, 2)} # Circuit Breaker — Status für die UI (Tagesverlust-Stopp)