diff --git a/CLAUDE.md b/CLAUDE.md index fd801ae..3cd042f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4328,7 +4328,7 @@ Trader wäre sie nie abgelaufen. Latte unverändert: Verhältnis < 1,0 ODER PF < („⚠ die Welle (steuert die Order) schweigt"). Live gegengerechnet, beide Zeilen erscheinen wie beabsichtigt. ⚠ Reine Anzeige — an Gewichten, Gates und Order-Logik ist nichts geändert. -- Asset-Version aktuell **v=183** (in `web/index.html` hochzählen, siehe Workflows). +- Asset-Version aktuell **v=185** (in `web/index.html` hochzählen, siehe Workflows). - **✅ SCHRIFT SKALIERT MIT DEM FENSTER (2026-08-13, v=167).** User: „die Größe der Schriftarten im Dashboard auf em einstellen, damit auf jedem Gerät oder je nach Fenstergröße die Schriftgröße automatisch angepasst wird." @@ -8756,3 +8756,60 @@ Exit-Modell gewesen. ➤ **Offen: der ticket-basierte Umbau** (Trailing je Position) — eigene Sitzung mit **flachem Konto**. Ein halb fertiger Umbau lässt den Schutz-Stack an der falschen der beiden Positionen hängen. + +## ✅✅ BRK HAT EINEN EIGENEN POSITIONS-SLOT (2026-08-19, v=185) + +User-Vorgabe: *„der brk trade soll komplett unabhängig von m15 oder manuellen +trades laufen“*. Entwurf: `docs/brk-eigener-slot.md`. + +**Anlass war eine Messung, keine Vermutung:** BRK setzte am 19.08. **fünfmal** +ruhende Orders und wurde jedes Mal binnen Sekunden storniert, weil eine Position +offen war (16 s · 1 s · 20 s · 81 s · 9 s = zusammen **~2 Minuten am Markt** an +einem Tag mit 24 Trades). **Drei der fünf** löste der M15-Trader aus — einmal in +**derselben Sekunde**. Der SELL_STOP von 19:02 lag bei 86,080, das Tief danach +bei **86,012**: die Order hätte gefüllt. + +``` +engine.trader ─ TradeManager() ─ trail ─ manuell + M15 +engine.trader_brk ─ TradeManager(nur_ticket=True) ─ trail_brk ─ nur BRK +``` +✅ **Der Umbau blieb klein**, weil `TrailingManager.__init__` bereits SEINEN +Trader nimmt und nur über dessen `snapshot()` liest — es hängt nicht an einer +globalen Position. Der validierte Pfad bleibt damit **unangetastet** (kein +Regressionsrisiko auf der gemessen besseren Population, +1,56 gegen −4,71 €/Lot). + +⚠⚠ **DIE ZWEI KRITISCHEN STELLEN:** +**(1) Adoption.** `TradeManager.refresh()` greift per Default jede Position auf +dem Symbol (gewollt: ein von Hand eröffneter Trade bekommt so den Schutz-Stack). +Mit zwei Managern würden sich **beide dieselbe** Position schnappen. Neu: +`nur_ticket=True` sucht sich nichts, und der adoptierende Manager bekommt über +**`fremde_tickets`** eine Sperrliste (BRK-Ticket + liegende Pendings). +**(2) Der Fill.** Eine ruhende Order füllt **im Broker**; der ticket-gebundene +Slot kann sie nicht finden. Neu **`TradeManager.bind(ticket, sym)`**, gerufen aus +`_check_pending_fill` — es sieht direkt am Broker nach, welches der liegenden +Tickets zu einer Position geworden ist. + +**Weiter:** `_check_auto_squeeze`/`_manage_squeeze_pending` lesen den EIGENEN +Slot · `trail_brk` wird in `_price_loop` mit Preisen versorgt · Snapshot +**`position_brk`** · Dashboard-Zeile **`#brk-pos`** in der Breakout-Karte · +**Circuit-Breaker und Tages-P&L summieren BEIDE Slots**, und der Breaker +schließt beide (eine Notbremse, die nur die Hälfte schließt, ist keine). +⚠ Die Dashboard-Zeile warnt ausdrücklich **„⚠ TRAILING AUS — nur Broker-SL!“**: +ohne `trail_brk` liefe BRK in der gemessen **durchgefallenen** SL-only-Variante +(`backtest_brk_slonly.py`: alle zehn KI enthalten die Null; WR fällt 40 → 21 %). + +⚠⚠ **REIHENFOLGE-FEHLER, real passiert:** die Defaults für `_margin_brk`/ +`_margin_m15` standen in Zeile 631, `_load_runtime_state()` läuft in Zeile 553 — +der geladene Wert wurde also sofort mit 0 überschrieben. Die Eingabe WIRKTE, war +aber nach jedem Neustart weg (User: „die Eingabe bleibt wirkungslos, in den +Feldern erscheint global“). **Regel: erst Default setzen, dann `runtime_state` +darüber laden.** +⚠ Beim Verifizieren dazu ein **eigener Fehlalarm**: mein Test setzte 55/30, nach +dem Neustart standen 85/10 — ich las das als „verloren“ und sogar als +Vertauschung. Die Logspur zeigte, dass der Browser des Users währenddessen zwei +weitere POSTs geschickt hatte. **Bei einer Live-Verifikation gegen ein System, +das der Nutzer gleichzeitig bedient, gehört die Logspur mitgelesen.** + +⚠ **NOCH NICHT BEOBACHTET:** der erste echte Parallelbetrieb. Beweis beim +nächsten Squeeze mit offener Position — Log `🧷 BRK-Slot uebernimmt +Pending-Fill` und `position_brk.trail == true`. diff --git a/web/app.js b/web/app.js index 0fdf014..9fc4f70 100644 --- a/web/app.js +++ b/web/app.js @@ -169,6 +169,30 @@ function beepRecommendation(sig, isNew) { function renderBrk(d) { const box = $streng("card-brk"); if (!box) return; + // --- Slot 2: die vom Breakout-Trader gehaltene Position ------------------- + // Ohne diese Zeile waere eine offene BRK-Position im Dashboard unsichtbar -- + // genau Deployment-Drift Fall 8 (dort driftete nicht die Strategie, sondern + // ihre Beobachtbarkeit) in neuer Form. + const bp = d.position_brk, bpEl = $streng("brk-pos"); + if (bpEl) { + if (!bp) { + bpEl.textContent = "keine — Slot frei"; + bpEl.className = ""; + } else { + // Richtung aus SL/TP ableiten: place_stop setzt IMMER einen SL. + const e = Number(bp.entry) || 0; + const lg = (bp.sl && e) ? (bp.sl < e) : (bp.tp ? bp.tp > e : null); + const pfeil = lg === null ? "" : (lg ? "▲ LONG" : "▼ SHORT"); + const pnl = Number(bp.pnl) || 0; + const t = [`${pfeil} ${bp.lots ?? "?"} @ ${e ? e.toFixed(3) : "?"}`, + `${pnl >= 0 ? "+" : ""}${pnl.toFixed(2)} €`]; + // ⚠ Der wichtigste Teil der Zeile: ohne Trailing laeuft der Trade in der + // gemessen durchgefallenen SL-only-Variante. + t.push(bp.trail ? "Trailing an" : "⚠ TRAILING AUS — nur Broker-SL!"); + bpEl.textContent = t.join(" · "); + bpEl.className = bp.trail ? (pnl >= 0 ? "pos" : "neg") : "neg"; + } + } // Einsatz je Pfad einspielen. ⚠ NICHT, solange das Feld den Fokus hat -- // sonst ueberschreibt der 1-s-Snapshot die laufende Eingabe (dieselbe Falle // wie beim Mindestgewinn-Feld). diff --git a/web/index.html b/web/index.html index bd08774..64c7154 100644 --- a/web/index.html +++ b/web/index.html @@ -12,7 +12,7 @@ ⚠ LOKAL eingebunden, kein CDN: die PWA laeuft ueber WireGuard und muss offline funktionieren. --> - +
@@ -171,6 +171,7 @@