diff --git a/core/engine.py b/core/engine.py index 3e854d1..903340f 100644 --- a/core/engine.py +++ b/core/engine.py @@ -2616,8 +2616,26 @@ class TradingEngine: self._cb_limit_pct = max(0.0, float(st["cb_limit_pct"] or 0)) if "margin_pct" in st: from core.config import set_margin_buffer as _setp, get_margin_buffer + _ini_pct = round(get_margin_buffer() * 100.0, 1) # Stand AUS DER INI _setp(float(st["margin_pct"] or 0) or 95.0) self._margin_pct = round(get_margin_buffer() * 100.0, 1) + # ⚠⚠ LAUT sagen, wenn `runtime_state` die ini ueberstimmt. + # ANLASS (2026-08-20): die ini stand auf 40 %, `runtime_state` + # auf 95 % — und weil das UI-Feld „Einsatz %" am 19.08. + # ENTFERNT wurde, gab es keinen Weg mehr, den persistierten + # Wert zu aendern. Er ueberstimmte die ini dauerhaft und + # still. Folge: ein manueller Trade nahm 95 % der freien + # Margin, und der Breakout-Trader konnte mit seinem Rest- + # Prozentsatz kein Mindestlot mehr bilden — er hat einen + # ganzen Tag lang KEINEN Trade eroeffnet. + # Dieselbe Falle wie `auto_signal` am 31.07. Der Unterschied: + # dort gab es einen Schalter, hier nicht mehr. + if abs(self._margin_pct - _ini_pct) > 0.05: + log.warning( + f"⚠ Einsatz-%: runtime_state ({self._margin_pct:.0f} %) " + f"ueberstimmt die ini ({_ini_pct:.0f} %). Es gibt KEIN " + f"UI-Feld mehr dafuer — zum Angleichen den Schluessel " + f"`margin_pct` aus runtime_state.json entfernen.") if "cone_skalen" in st: # ⚠ JSON liefert String-Schluessel → auf int zurueckholen, sonst # findet `cone.build` sie nicht (es sucht nach `bars` als int). @@ -4962,6 +4980,14 @@ class TradingEngine: "trail": bool(getattr(self.trail_brk, "enabled", False)), } if ps.get("ticket") else None)(self.trader_brk.snapshot()), # Einsatz je autonomem Pfad; 0 = der globale `margin_pct` gilt. + # ⚠⚠ WARUM liegt keine Pending-Order? Der Grund stand bisher NUR im + # Log — und dort ging er zwischen 269 Zeilen unter. Am 20.08. hat + # der Breakout-Trader deshalb einen ganzen Tag lang nichts + # eroeffnet, ohne dass es an der Oberflaeche sichtbar war. + # Kostet nichts: der Text entsteht ohnehin beim Platzierungs- + # Versuch, hier wird er nur veroeffentlicht. + "squeeze_block": (sorted(getattr(self, "_squeeze_pending_err", {}).values())[:1] + or [None])[0], "margin_brk": getattr(self, "_margin_brk", 0.0), "margin_m15": getattr(self, "_margin_m15", 0.0), # Live-Handelskosten relativ zur Vola (2026-07-24): Spread/ATR(M5). diff --git a/web/app.js b/web/app.js index b930cc7..f15c8a9 100644 --- a/web/app.js +++ b/web/app.js @@ -176,8 +176,20 @@ function renderBrk(d) { const bp = d.position_brk, bpEl = $streng("brk-pos"); if (bpEl) { if (!bp) { - bpEl.textContent = "keine — Slot frei"; - bpEl.className = ""; + // ⚠⚠ "Slot frei" allein ist irrefuehrend, wenn der Trader gar nicht + // ORDERN KANN. Am 20.08. hat er einen ganzen Tag lang keinen Trade + // eroeffnet ("Einsatz 10 % von 58 EUR freier Margin = 5,85 EUR, + // Mindestlot braucht 7,46") — der Grund stand NUR im Log, zwischen + // 269 gleichlautenden Zeilen. Ein leerer Slot und ein blockierter + // Slot sehen sonst identisch aus. + const grund = d.squeeze_block; + bpEl.textContent = grund ? ("⚠ keine — " + grund) : "keine — Slot frei"; + bpEl.className = grund ? "bad" : ""; + bpEl.title = grund + ? "Der Breakout-Trader wollte ordern und konnte nicht. Reine Anzeige — " + + "die Einstellung dahinter (Einsatz je Pfad / Einsatz %) ist eine " + + "bewusste Entscheidung und wird nicht automatisch geaendert." + : ""; } else { // Richtung aus SL/TP ableiten: place_stop setzt IMMER einen SL. const e = Number(bp.entry) || 0; @@ -2374,7 +2386,7 @@ async function closeSlot(slot, label, pnl) { // und der Browser zeigte Altes. Statt das jedes Mal von Hand zu diagnostizieren, // sagt die Seite es jetzt selbst. // ⚠ Bewusst SICHTBAR statt nur Konsole: die Konsole sieht am Handy niemand. -const _JS_V = "201"; +const _JS_V = "202"; { const htmlV = document.body && document.body.dataset ? document.body.dataset.htmlV : null; diff --git a/web/index.html b/web/index.html index 3388d9c..24eea01 100644 --- a/web/index.html +++ b/web/index.html @@ -12,9 +12,9 @@ ⚠ LOKAL eingebunden, kein CDN: die PWA laeuft ueber WireGuard und muss offline funktionieren. --> - + - +