From a652223a0505daf4f77f5d01eea90be8998bb5d6 Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Thu, 20 Aug 2026 08:37:11 +0200 Subject: [PATCH] Slot-Pruefung: close() traf nur Slot 1, squeeze_reverse den falschen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User: "ueberpruefe die Trade Logiken genau auf weitere Fehler oder Widersprueche, besonders bezogen auf die 2 Slots". Systematisch alle `self.trader.`-Zugriffe in engine.py durchgegangen (24 Stellen, nach Funktion gruppiert). VIER Widersprueche, alle aus dem Umbau vom 19.08.: ① BEHOBEN - `engine.close()` schloss NUR Slot 1. Hielt der BRK-Slot eine Position und Slot 1 war leer, tat der CLOSE-Knopf im Dashboard NICHTS. Das ist die gefaehrlichste Variante der Luecke: nicht eine Zahl stimmt nicht, sondern eine SCHUTZHANDLUNG greift ins Leere. Jetzt ueber beide Slots, Slot 1 zuerst (wer CLOSE drueckt, meint meist die selbst geoeffnete Position), Fehler gesammelt gemeldet. ③ BEHOBEN - `squeeze_reverse` rief `self.trader.close()`, traf also die MANUELLE Position statt der BRK-Position, die der Reverse eigentlich drehen soll. Dormant (auto_squeeze_reverse=false), aber falsch verdrahtet. ② GEMELDET, NICHT geaendert - `set_sltp()` wirkt nur auf Slot 1. Die SL/TP-Felder der Trade-Leiste koennen eine BRK-Position nicht anpassen. Das ist eine UI-Frage (welches Feld gehoert zu welchem Slot?) und braucht eine Entscheidung, keinen stillen Fix. ④ GEMELDET - `_check_auto_m15` bindet `self.trader.open_long` VOR dem Aufruf von `_open`, umgeht also `_slot(source)`. Funktioniert derzeit nur, weil `_slot("auto_m15")` ohnehin `self.trader` liefert. Fragil: bekaeme M15 je einen eigenen Slot, waere es still falsch. GEPRUEFT UND KORREKT: `_manage_squeeze_pending` (pending_orders/cancel_pending/ place_stop sind magic-basierte BROKER-Abfragen, kein Slot-Zustand), `_broker_offset_s` (Zeitzone), `snapshot()` (position / position_brk getrennt), Circuit-Breaker (summiert und schliesst beide). ⚠ Beim Bau eine Einrueckung zerschossen - Stufe A der Pipeline hat es sofort gefangen (IndentationError + ty invalid-syntax + pytest). Genau dafuer ist sie da. 91 Tests gruen, Deploy verifiziert. Co-Authored-By: Claude Opus 5 --- core/engine.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/core/engine.py b/core/engine.py index a86e319..714c74f 100644 --- a/core/engine.py +++ b/core/engine.py @@ -1732,7 +1732,10 @@ class TradingEngine: pnl = ps.get("pnl") log.info(f"🔄 AUTO-SQUEEZE-REVERSE: {pos_dir}-Position gegen {d}-Ausbruch " f"@ {lvl} (P&L {pnl:+.2f} EUR) → sofort schließen, dann drehen") - err = self.trader.close(reason="squeeze_reverse") + # ⚠ Der Reverse gehoert zum BRK-Pfad und muss dessen Slot + # schliessen — vorher traf er Slot 1, also die MANUELLE Position. + # Dormant (`auto_squeeze_reverse=false`), aber falsch verdrahtet. + err = self.trader_brk.close(reason="squeeze_reverse") tg = self.cfg["telegram"] tg_on = (tg.get("enabled", "false").lower() == "true" and tg.get("bot_token")) if err: @@ -2856,7 +2859,28 @@ class TradingEngine: return None def close(self) -> str | None: - return self.trader.close(reason="manual") + """Schliesst die offene Position — BEIDE Slots (Fix 2026-08-20). + + ⚠⚠ Bis heute lief das nur auf `self.trader`. Hielt der BRK-Slot eine + Position und Slot 1 war leer, tat der CLOSE-Knopf im Dashboard NICHTS — + der Nutzer konnte einen autonom eroeffneten Trade nicht von Hand + schliessen. Das ist die gefaehrlichste Variante der Slot-Luecke: nicht + eine Zahl stimmt nicht, sondern eine SCHUTZHANDLUNG greift ins Leere. + ⚠ Reihenfolge bewusst Slot 1 zuerst: das ist die manuelle Position, und + wer CLOSE drueckt, meint in aller Regel die, die er selbst geoeffnet hat. + Sind beide offen, werden beide geschlossen und Fehler gesammelt gemeldet. + """ + fehler = [] + for tr, name in ((self.trader, "Slot 1"), (self.trader_brk, "BRK")): + try: + if not (tr.snapshot() or {}).get("ticket"): + continue + e = tr.close(reason="manual") + if e: + fehler.append(f"{name}: {e}") + except Exception as ex: + fehler.append(f"{name}: {ex}") + return "; ".join(fehler) if fehler else None def toggle_trail(self) -> bool: sym = self.data.symbol