diff --git a/docs/brk-eigener-slot.md b/docs/brk-eigener-slot.md new file mode 100644 index 0000000..5e42469 --- /dev/null +++ b/docs/brk-eigener-slot.md @@ -0,0 +1,125 @@ +# BRK mit eigenem Positions-Slot — Entwurf + +**Auftrag (User, 2026-08-19):** *„der brk trade soll komplett unabhängig von m15 +oder manuellen trades laufen"*. + +**Status:** Entwurf, am echten Code verifiziert. **Nicht gebaut.** Umsetzung +gehört in eine eigene Sitzung mit **flachem Konto**. + +--- + +## Warum der naheliegende Weg falsch ist + +Die erste Idee wäre, die beiden Sperren zu entfernen: + +| Stelle | heutiges Verhalten | +|---|---| +| `trader.open_long/short` (Z. 320/333) | lehnt ab, wenn `self.ticket` gesetzt ist | +| `engine._manage_squeeze_pending` | storniert die Pendings, sobald **irgendeine** Position offen ist | + +Das wäre an einem Abend gemacht — und **falsch**. `trader` hält EIN Ticket +(`self.ticket`), und `trailing`, Notfall-Stop, S/R-Close und Time-Stop hängen +alle daran (29 Stellen in `engine.py`). Der zweite Trade hätte nur den +Broker-SL. + +⚠⚠ **Und das ist gemessen nicht tragfähig** (`backtest_brk_slonly.py`, +2026-08-19, 80k M5, 2 Halbjahre): **keine** der fünf Exit-Varianten erfüllt die +vorab fixierte Regel, alle zehn Konfidenzintervalle enthalten die Null. SL-only +sieht im ØR zwar nicht schlechter aus, aber die Trefferquote fällt **40 → 21 %** +und die KI-Breite **vervierfacht** sich — ein Varianz-Befund, kein Edge. + +--- + +## Der Entwurf: ein ZWEITER, ticket-gebundener Trader + +Der entscheidende Befund aus der Code-Sichtung: **`TrailingManager.__init__` +nimmt bereits einen Trader entgegen** (`core/trailing.py:146`, `self.trader = +trader`) und liest ausschließlich über `self.trader.snapshot()`. Es ist also +nicht an eine globale Position gebunden, sondern an *seinen* Trader. + +Damit entfällt der 29-Stellen-Umbau: + +``` +engine.trader ─ TradeManager (wie heute) ─ trail ─ manuell + M15 +engine.trader_brk ─ TradeManager (NEU) ─ trail_brk ─ nur BRK +``` + +✅ **Der validierte Pfad bleibt unangetastet.** Manuelle Trades und der +Schutz-Stack laufen unverändert über `self.trader` — kein Regressionsrisiko auf +der Population, die gemessen die bessere ist (+1,56 gegen −4,71 €/Lot). + +--- + +## ⚠⚠ Der kritische Punkt: Adoption abschalten + +`TradeManager.refresh()` (`core/trader.py:511`) macht heute: + +```python +on_sym = mt5.positions_get(symbol=sym) or [] +all_pos = on_sym if on_sym else (mt5.positions_get() or []) +``` + +Es **adoptiert** also jede Position auf dem Symbol. Mit zwei Managern würden +sich beide dieselbe Position greifen und gegeneinander arbeiten — Trailing +zweimal auf demselben Ticket, oder beide auf dem falschen. + +**➜ Der BRK-Manager muss STRIKT ticket-gebunden sein: `positions_get(ticket=…)`, +keine Adoption, kein Symbol-Fallback.** Das ist die eine Änderung, an der der +ganze Entwurf hängt. + +Umsetzung: `TradeManager(..., nur_ticket: bool = False)`. Bei `True`: +- `refresh()` fragt ausschließlich `positions_get(ticket=self.ticket)` +- ist `self.ticket is None`, ist der Manager flat — er sucht sich **nichts** +- `_refresh_locked` adoptiert nicht per magic-match + +--- + +## Schritte (in dieser Reihenfolge) + +1. **`TradeManager` um `nur_ticket` erweitern.** Default `False` → der + bestehende Manager verhält sich **bitgenau** wie heute. Test, der das + festnagelt (Muster `tests/test_room_info.py`: alte Fassung eingefroren, über + Zufallslagen gegengerechnet). +2. **`engine.trader_brk` + `engine.trail_brk` anlegen.** Noch ohne Nutzung. +3. **BRK-Pfad umhängen:** `_manage_squeeze_pending`, `_check_pending_fill`, + `_check_auto_squeeze` benutzen `trader_brk`. Die FLAT-Bedingung wird zu + `trader_brk.ticket is None` — BRK sieht die andere Position nicht mehr. +4. **Schutz-Stack für BRK zweimal aufrufen:** `trail_brk.tick()` im `_pos_loop`, + dazu `_check_sr_close` / `_check_auto_close` / `_check_adverse15` mit dem + jeweiligen Trader parametrisieren (sie lesen heute `self.trader` fest). +5. **Snapshot:** zweites Positionsobjekt (`position_brk`) + Dashboard-Zeile. + ⚠ Ohne Anzeige wäre eine offene Position unsichtbar — das ist die + Buchführungs-Lücke von Deployment-Drift **Fall 8** in neuer Form. +6. **Circuit-Breaker und Tages-P&L** müssen **beide** Positionen summieren, + sonst greift die Notbremse zu spät. +7. **Margin:** `calc_lots` rechnet auf `margin_free`. Läuft schon eine Position, + ist die frei verfügbare Margin kleiner — BRK würde automatisch kleiner + sizen. Das ist gewollt, aber **das Feld „Einsatz je Pfad" muss dann als + Anteil der GESAMT-Margin gelesen werden, nicht der freien.** Vor dem Bau + entscheiden. + +--- + +## Was vorher geklärt sein muss + +- ⚠ **Konto muss FLAT sein.** Ein halb umgebauter Schutz-Stack hängt an der + falschen Position. +- ⚠ **`RETAIL_HEDGING` ist bestätigt** (Broker erlaubt mehrere Positionen) — + geprüft 2026-08-19. +- ⚠ **Zwei Positionen = zweimal Risiko.** Bei 40 % Einsatz je Position sind + 80 % gebunden. Der Circuit-Breaker (aktuell **aus**) wäre dann kein Luxus + mehr — das gehört mitentschieden. +- ⚠ **Die Squeeze-Zahlen gelten weiter nur mit dem kanonischen Exit.** Nach dem + Umbau muss `trail_brk` nachweislich laufen (Log + Snapshot), sonst ist man + ungewollt in der gemessen durchgefallenen SL-only-Variante. + +--- + +## Erfolgskontrolle nach dem Bau + +- Ein BRK-Trade läuft, während eine manuelle Position offen ist — beide mit + eigenem SL und **beide** mit aktivem Trailing (im Log sichtbar). +- `squeeze_entry_count` steigt wieder (heute: 0 bei fünf stornierten Fenstern). +- `analyze_squeeze_entry_gap.py`: der Abstand Einstieg↔Level bleibt bei ~0. +- Die B5-Zählung läuft auf einer NEU vorab fixierten Latte weiter (die alte ist + am 2026-08-17 gefallen und rechnerisch unerreichbar).