From ebf10da00e3862936fb57bfaf792ba5509d26cb5 Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Thu, 20 Aug 2026 22:43:19 +0200 Subject: [PATCH] G/V eingefroren: Trend-Loop haing am mt5_lock + bind() legte keine DB-Zeile an MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User: "G/V aktualisiert nicht mehr." ⚠⚠ BEFUND 1 - ES WAR EIN HAENGER, KEIN ANZEIGEFEHLER. Der Snapshot war frisch (ts lief weiter), aber `position.pnl` und `cur_price` standen still, waehrend der Broker-Profit zwischen 1,23 und 1,62 pendelte. Die Loop-Spuren im Log: _trend_loop letzte Zeile 22:30:05 (= Serverstart) _pos_loop letzte Zeile 22:31:20 _price_loop lief weiter, scheiterte aber: "Time-Stop-Close fehlgeschlagen: MT5 belegt" - alle 15 s, ueber vier Minuten. Jemand hielt den mt5_lock dauerhaft. Folge: KEIN Trailing-Nachzug, KEIN Time-Stop, KEIN Circuit-Breaker, KEIN Notfall-Stop. Die Position lief nur noch am Broker-SL. URSACHE (nicht bewiesen, aber die Gleichzeitigkeit ist eindeutig): heute kamen VIER zusaetzliche PatternDetector dazu, und der Trend-Loop rief alle FUENF in EINER Schleife auf. Jeder macht `copy_rates_from_pos(240)` unter `mt5_lock(timeout=2)`. Die gestaffelten Drosseln (20/25/35/60 s) greifen dabei NICHT: beim Start sind alle faellig, also liegen fuenf Fetches unmittelbar hintereinander unter demselben Lock. Der Trend-Loop blieb genau dort stehen. Behoben: REIHUM statt Pulk - je Durchlauf genau EIN Detektor. Der Trend-Loop laeuft im Sekundentakt, jede Zeitebene kommt weiterhin oft genug dran. ⚠ Ohne Thread-Dump (py-spy nicht installiert) ist die Ursache nicht bewiesen. Die Lock-Last je Durchlauf zu fuenfteln ist aber unabhaengig davon richtig. Nach dem Neustart 3 Minuten beobachtet: 0x "MT5 belegt", _pos_loop lebendig, G/V folgt dem Kurs wieder. ⚠⚠ BEFUND 2, direkt danach aufgefallen: "DB-Nachtrag T=50454589 nach 60 Versuchen aufgegeben". `TradeManager.bind()` setzt das Ticket DIREKT und umgeht damit den Adoptions-Zweig in `_refresh_locked`, der sonst `history.log_trade_open` ruft (ein `nur_ticket`-Manager steigt dort ohne Ticket sofort aus). Fuer eine gebundene Position gab es also GAR KEINE DB-Zeile, und `tag_bot_trade` fand nichts zum Aktualisieren. Das ist Deployment-Drift Fall 8 in neuer Form: nicht die Strategie driftet, sondern ihre BEOBACHTBARKEIT - der Trade fehlte in B4-Monitor, squeeze_b5 und squeeze_entry_gap. Genau der Pfad, der seit dem Pending-Umbau 7 von 8 BRK-Trades traegt. Behoben in `bind()`, gekapselt (ein DB-Fehler darf das Binden nicht verhindern). Die laufende Position nachgetragen (DB-Backup oil_widget_history.db.bak-2026-08-20-bind). ✅ NEBENBEI DER LIVE-BELEG FUER DEN TRAILING-FIX VON HEUTE MORGEN: 22:39:34 🧷 BRK-Slot uebernimmt Pending-Fill T=50454589 22:39:34 Trailing des BRK-Slots aktiviert (T=50454589) Vor dem Fix waere diese Position nur mit dem Broker-SL gelaufen - die gemessen durchgefallene SL-only-Variante. Snapshot bestaetigt `trail: true`. Co-Authored-By: Claude Opus 5 --- core/engine.py | 26 ++++++++++++++++++++++---- core/trader.py | 24 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/core/engine.py b/core/engine.py index d9f1593..aa34bf3 100644 --- a/core/engine.py +++ b/core/engine.py @@ -864,10 +864,28 @@ class TradingEngine: try: self.structure.refresh_market(self.data.symbol) except Exception as se2: log.warning(f"structure.refresh_market: {se2}") try: - # ⚠ `self.patterns` steckt als "M30" mit in der Karte — - # NICHT doppelt aufrufen (eigene Drossel je Instanz). - for _pd in self.patterns_tf.values(): - _pd.refresh_market(self.data.symbol) + # ⚠⚠ REIHUM, NICHT alle fuenf hintereinander (Fix 2026-08-20, + # nach einem realen Haenger). Jede Instanz hat zwar ihre + # eigene Drossel (20/25/35/60 s) — die greift aber NICHT, + # wenn alle im selben Aufruf drankommen: beim Start sind + # alle faellig, und dann liegen fuenf + # `copy_rates_from_pos(240)` unmittelbar hintereinander + # unter `mt5_lock`. + # Real am 20.08. um 22:30: der Trend-Loop blieb SOFORT + # nach dem Start stehen, kurz darauf auch `_pos_loop`; + # der Time-Stop meldete 15 s lang "MT5 belegt" und die + # Position lief nur noch am Broker-SL. G/V und `cur_price` + # waren eingefroren, waehrend der Kurs weiterlief. + # ⚠ Ehrlich: ohne Thread-Dump ist die Ursache nicht + # BEWIESEN — belegt ist die Gleichzeitigkeit. Die + # Lock-Last je Durchlauf zu fuenfteln ist aber in jedem + # Fall richtig und kostet nichts: jede Zeitebene kommt + # weiterhin oft genug dran (Trend-Loop laeuft im + # Sekundentakt, die Drosseln bleiben unveraendert). + _pk = list(self.patterns_tf) + _pi = getattr(self, "_pat_rr", 0) % len(_pk) + self._pat_rr = _pi + 1 + self.patterns_tf[_pk[_pi]].refresh_market(self.data.symbol) except Exception as pe2: log.warning(f"patterns.refresh_market: {pe2}") try: self.candle_anat.refresh_market(self.data.symbol) except Exception as ka: log.warning(f"candles.refresh_market: {ka}") diff --git a/core/trader.py b/core/trader.py index be6b73c..f03f8fa 100644 --- a/core/trader.py +++ b/core/trader.py @@ -571,6 +571,30 @@ class TradeManager: self.sl = float(getattr(pk, "sl", 0.0) or 0.0) self.tp = float(getattr(pk, "tp", 0.0) or 0.0) self.open_time = float(getattr(pk, "time", 0) or 0) - _boff + # ⚠⚠ DB-ZEILE ANLEGEN (Fix 2026-08-20). `bind()` setzt das Ticket + # DIREKT — der Adoptions-Zweig in `_refresh_locked`, der sonst + # `history.log_trade_open` ruft, wird dabei uebersprungen (ein + # `nur_ticket`-Manager steigt dort ohne Ticket sofort aus). + # Folge: fuer eine gebundene Position gab es GAR KEINE DB-Zeile, + # und `tag_bot_trade` fand nichts zum Aktualisieren. Real am + # 20.08.: "⚠ DB-Nachtrag T=50454589 nach 60 Versuchen aufgegeben". + # Das ist Deployment-Drift Fall 8 in neuer Form — nicht die + # Strategie driftet, sondern ihre BEOBACHTBARKEIT: der Trade + # fehlte in B4-Monitor, `squeeze_b5` und `squeeze_entry_gap`. + # ⚠ `log_trade_open` ist idempotent genug (INSERT je Ticket); ein + # Fehlschlag darf das Binden NICHT verhindern, deshalb gekapselt. + if self.history: + try: + self.history.log_trade_open( + ticket=int(pk.ticket), + symbol=getattr(pk, "symbol", sym), + direction="BUY" if pk.type == mt5.ORDER_TYPE_BUY else "SELL", + lots=float(pk.volume), + entry_price=float(pk.price_open), + ) + except Exception as e: + log_trade.warning(f"bind: DB-Zeile fuer T={pk.ticket} " + f"nicht angelegt: {e}") return True def refresh(self, sym):