Dashboard-Zeile fuer Slot 2 + Doku des Zwei-Slot-Umbaus

Die vom Breakout-Trader gehaltene Position ist jetzt sichtbar (#brk-pos in der
Breakout-Karte): Richtung, Lots, Einstieg, P&L - und vor allem der
Trailing-Zustand.

⚠ Der Trailing-Hinweis ist der wichtigste Teil der Zeile: steht er auf
"⚠ TRAILING AUS - nur Broker-SL!", laeuft der Trade in der gemessen
DURCHGEFALLENEN SL-only-Variante (backtest_brk_slonly.py: alle zehn
Konfidenzintervalle enthalten die Null, Trefferquote faellt 40 -> 21 %). Ohne
diese Anzeige waere das im Betrieb nicht bemerkbar - genau Deployment-Drift
Fall 8 (nicht die Strategie driftet, sondern ihre Beobachtbarkeit).

Richtung wird aus SL/TP abgeleitet statt ein weiteres Snapshot-Feld zu bauen -
place_stop setzt immer einen SL. Damit bleibt es eine reine Frontend-Aenderung
(v=185), kein Neustart noetig, die offene Position unberuehrt.

Verifiziert: v=185 wird ausgeliefert, div 85/85 ausgeglichen, keine doppelten
IDs, keine verwaisten JS-Referenzen, node --check gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-19 20:12:26 +02:00
co-authored by Claude Opus 5
parent a9a6c487d2
commit 5593f270cc
3 changed files with 85 additions and 3 deletions
+58 -1
View File
@@ -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"). („⚠ die Welle (steuert die Order) schweigt").
Live gegengerechnet, beide Zeilen erscheinen wie beabsichtigt. Live gegengerechnet, beide Zeilen erscheinen wie beabsichtigt.
⚠ Reine Anzeige — an Gewichten, Gates und Order-Logik ist nichts geändert. ⚠ 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 - **✅ 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 Schriftarten im Dashboard auf em einstellen, damit auf jedem Gerät oder je nach
Fenstergröße die Schriftgröße automatisch angepasst wird." 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 **Offen: der ticket-basierte Umbau** (Trailing je Position) — eigene Sitzung
mit **flachem Konto**. Ein halb fertiger Umbau lässt den Schutz-Stack an der mit **flachem Konto**. Ein halb fertiger Umbau lässt den Schutz-Stack an der
falschen der beiden Positionen hängen. 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`.
+24
View File
@@ -169,6 +169,30 @@ function beepRecommendation(sig, isNew) {
function renderBrk(d) { function renderBrk(d) {
const box = $streng("card-brk"); const box = $streng("card-brk");
if (!box) return; 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 -- // Einsatz je Pfad einspielen. ⚠ NICHT, solange das Feld den Fokus hat --
// sonst ueberschreibt der 1-s-Snapshot die laufende Eingabe (dieselbe Falle // sonst ueberschreibt der 1-s-Snapshot die laufende Eingabe (dieselbe Falle
// wie beim Mindestgewinn-Feld). // wie beim Mindestgewinn-Feld).
+3 -2
View File
@@ -12,7 +12,7 @@
⚠ LOKAL eingebunden, kein CDN: die PWA laeuft ueber WireGuard ⚠ LOKAL eingebunden, kein CDN: die PWA laeuft ueber WireGuard
und muss offline funktionieren. --> und muss offline funktionieren. -->
<link rel="stylesheet" href="pico.min.css?v=1"> <link rel="stylesheet" href="pico.min.css?v=1">
<link rel="stylesheet" href="style.css?v=184"> <link rel="stylesheet" href="style.css?v=185">
</head> </head>
<body> <body>
<!-- Modul-Menü (ganz oben) --> <!-- Modul-Menü (ganz oben) -->
@@ -171,6 +171,7 @@
<div class="kv"><span>Squeeze jetzt</span><b id="brk-squeeze"></b></div> <div class="kv"><span>Squeeze jetzt</span><b id="brk-squeeze"></b></div>
<div class="kv"><span>Liegende Orders</span><b id="brk-pending"></b></div> <div class="kv"><span>Liegende Orders</span><b id="brk-pending"></b></div>
<div class="kv" title="B5 = die VORAB fixierte Abbruchregel: nach 20 Trades ab 01.08. muss Verhaeltnis (OeGewinn/OeVerlust) >= 1,0 UND PF >= 1 sein."><span>B5-Regel</span><b id="brk-b5"></b></div> <div class="kv" title="B5 = die VORAB fixierte Abbruchregel: nach 20 Trades ab 01.08. muss Verhaeltnis (OeGewinn/OeVerlust) >= 1,0 UND PF >= 1 sein."><span>B5-Regel</span><b id="brk-b5"></b></div>
<div class="kv" title="Die vom Breakout-Autotrader gehaltene Position - SEIT dem Zwei-Slot-Umbau vom 19.08. ein EIGENER Slot, unabhaengig von manuellen und M15-Trades. WICHTIG: 'Trailing' muss AN sein. Steht es auf aus, laeuft der Trade nur mit dem Broker-SL - also in der gemessen DURCHGEFALLENEN SL-only-Variante (backtest_brk_slonly.py: alle zehn Konfidenzintervalle enthalten die Null)."><span>BRK-Position</span><b id="brk-pos"></b></div>
<div class="tb-row mg-row" title="Einsatz JE PFAD in Prozent der freien Margin. Leer oder 0 = der globale Wert gilt. WICHTIG: das ist KEINE gleichzeitige Reservierung - der Bot haelt EINE Position, es kann nie BRK und M15 nebeneinander geben. Das Feld steuert die GROESSE der jeweiligen Position. Manuelle Trades bleiben beim globalen Wert."> <div class="tb-row mg-row" title="Einsatz JE PFAD in Prozent der freien Margin. Leer oder 0 = der globale Wert gilt. WICHTIG: das ist KEINE gleichzeitige Reservierung - der Bot haelt EINE Position, es kann nie BRK und M15 nebeneinander geben. Das Feld steuert die GROESSE der jeweiligen Position. Manuelle Trades bleiben beim globalen Wert.">
<span class="tb-field"><label title="Einsatz % fuer autonome Breakout-Trades. Leer = globaler Wert.">Einsatz BRK %</label> <span class="tb-field"><label title="Einsatz % fuer autonome Breakout-Trades. Leer = globaler Wert.">Einsatz BRK %</label>
<input id="am-brk" type="number" inputmode="decimal" step="1" min="0" max="99" placeholder="global"></span> <input id="am-brk" type="number" inputmode="decimal" step="1" min="0" max="99" placeholder="global"></span>
@@ -388,6 +389,6 @@
</div> </div>
<div id="toast" class="toast hidden"></div> <div id="toast" class="toast hidden"></div>
<script src="app.js?v=184"></script> <script src="app.js?v=185"></script>
</body> </body>
</html> </html>