G/V eingefroren: Trend-Loop haing am mt5_lock + bind() legte keine DB-Zeile an

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 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-20 22:43:19 +02:00
co-authored by Claude Opus 5
parent 21f2dda983
commit ebf10da00e
2 changed files with 46 additions and 4 deletions
+22 -4
View File
@@ -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}")