diff --git a/core/engine.py b/core/engine.py index 903340f..a0e568b 100644 --- a/core/engine.py +++ b/core/engine.py @@ -3033,6 +3033,49 @@ class TradingEngine: fehler.append(f"{name}: {ex}") return "; ".join(fehler) if fehler else None + def _trail_info(self, position: dict, market: dict) -> str | None: + """Ein Satz: was macht das Trailing gerade, und warum nicht mehr? + + ⚠ Rein zusammengesetzt aus dem, was der Snapshot ohnehin enthaelt — + kein zweiter Rechenweg (der Konsens-Pfeil-Fehler). Fail-safe: bei + fehlenden Werten None, die Zeile bleibt dann leer. + """ + try: + if not (position or {}).get("ticket"): + return None + t = self.trail.snapshot() or {} + if not t.get("enabled"): + return "Trailing AUS — nur der Broker-SL schützt." + atr = float(t.get("atr") or 0.0) + hw = t.get("high_water") + if not atr or hw is None: + return "Trailing an — wartet auf den ersten Tick." + mult = float(t.get("trail_mult") or 1.0) + lng = position.get("order_type") == 0 + ent = float(position.get("entry_price") or 0.0) + cur = float((market or {}).get("bid" if lng else "ask") or 0.0) + if not ent or not cur: + return None + gewinn = (cur - ent) if lng else (ent - cur) + r = gewinn / atr + ziel = float(hw) - mult * atr if lng else float(hw) + mult * atr + sl = float(position.get("sl") or 0.0) + if t.get("phase") == "Init": + # Was muesste der Kurs erreichen, damit der Trail ueberhaupt anspringt? + from core.exit_model import LIVE as _EX + start = _EX.trail_start + noetig = ent + start * atr if lng else ent - start * atr + return (f"Trailing wartet — springt ab {start:.1f}×ATR Gewinn an " + f"(jetzt {r:.2f}×, also ab Kurs {noetig:.3f}).") + # Trail laeuft: zieht er nach, oder haelt der (engere) Stop? + if sl and ((ziel <= sl) if lng else (ziel >= sl)): + return (f"Trailing läuft, zieht aber NICHT nach: sein Stop wäre " + f"{ziel:.3f}, der gesetzte {sl:.3f} ist enger. Es lockert " + f"einen Stop nie.") + return f"Trailing läuft — zieht den Stop auf {ziel:.3f} nach." + except Exception: + return None + def toggle_trail(self) -> bool: """TRAIL-Knopf — trifft den Slot, der tatsaechlich eine Position haelt. @@ -4915,6 +4958,18 @@ class TradingEngine: "wht_pct": self._wht_pct, "takeprofit": self._takeprofit, "takeprofit_remember": self._takeprofit_remember, + # ⚠⚠ WARUM tut das Trailing gerade nichts? (User-Frage 2026-08-20: + # "trailing scheint nicht zu funktionieren, wenn man es einmal + # manuell aktiviert hat und dann wieder einschaltet") + # Es TAT nichts, und beide Gruende waren richtig — nur unsichtbar: + # (a) Phase "Init": der Trail startet erst ab `trail_start`, und + # der steht seit dem 20.08. auf 1,3xATR (war 0,3). Wer die + # alte Einstellung gewohnt ist, erwartet ihn 10x frueher. + # (b) Er ZIEHT NIE ZURUECK. Nach einer Handeingabe liegt der SL + # oft enger als HW-mult*ATR — dann bleibt er zu Recht stehen. + # Die Trailing-Karte ist seit dem 22.07. ausgeblendet, es gab also + # KEINE Stelle, an der das sichtbar wurde. Reine Anzeige. + "trail_info": self._trail_info(position, market), "trail": self.trail.snapshot(), "wave_signal": wave_signal, "wave": wave_snap, diff --git a/core/trailing.py b/core/trailing.py index 58fd526..5d3ea95 100644 --- a/core/trailing.py +++ b/core/trailing.py @@ -63,7 +63,11 @@ _ATR_MIN = 0.06 # Untergrenze ATR. 0,12→0,06 gesenkt (gemessen, # jemand dort, ändern sich Live-Verhalten UND Messung gemeinsam. from core.exit_model import LIVE as _EXIT -_TRAIL_START_ATR = _EXIT.trail_start # 0,3 — Phase 1→2: ab hier HW-Trailing +_TRAIL_START_ATR = _EXIT.trail_start # 1,3 (war 0,3 bis 2026-08-20) — Phase 1→2: + # ab hier HW-Trailing. ⚠ Der Wert lebt in + # `exit_model.LIVE`, hier steht nur der + # Verweis — die Zahl im Kommentar war + # nach der Aenderung stale. _BREAKEVEN_ATR = _EXIT.be # 1,3 — Entry-Floor (Breakeven). 0,6 war zu # eng: 21 % der Trades wurden auf Breakeven gescratcht # (backtest_exit.py be). 1,3 = gemessenes Plateau-Optimum: diff --git a/web/app.js b/web/app.js index f15c8a9..21e0bc3 100644 --- a/web/app.js +++ b/web/app.js @@ -710,6 +710,17 @@ function renderKontext(snap) { // ── HL-Kontext (Funding / OI / Premium) — reine Anzeige ─────────────── // ⚠ Gemessen ohne Prognosewert (analyze_hl_funding.py): entüberlappt hält // kein Bucket die 2-Stichproben-Regel. Schreibt in die MELDUNGEN-Karte. + // Trailing-Zustand im Klartext (User-Frage 2026-08-20: "trailing scheint + // nicht zu funktionieren, wenn man es einmal manuell aktiviert hat und + // dann wieder einschaltet"). Der Text kommt FERTIG aus dem Backend + // (`trail_info`) — ihn hier nachzubauen waere die Divergenz-Falle, die im + // Projekt dreimal zugeschlagen hat. + const trN = $streng("trail-note"); + if (trN) { + const trTxt = snap && snap.trail_info; + trN.textContent = trTxt || ""; + trN.classList.toggle("hidden", !trTxt); + } const hlEl = $("hl-note"); const h = snap && snap.hl_ctx; if (h && h.premium != null) { @@ -2386,7 +2397,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 = "202"; +const _JS_V = "203"; { const htmlV = document.body && document.body.dataset ? document.body.dataset.htmlV : null; diff --git a/web/index.html b/web/index.html index 24eea01..a7210f1 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. --> - + -
+