Dritter Slot-Durchgang: TRAIL-Knopf und Wiedereinstiegs-Sperre waren blind
User-Auftrag (dritte Runde). Diesmal die Bereiche ausserhalb der
`self.trader.`-Zugriffe geprueft. ZWEI neue Funde, beide behoben:
⑤ toggle_trail() schaltete NUR `self.trail`. Hielt allein der BRK-Slot eine
Position, liess sich dessen Trailing per Dashboard weder ein- noch
ausschalten. ⚠ Das ist keine Kosmetik: ohne `trail_brk` laeuft ein BRK-Trade
in der gemessen DURCHGEFALLENEN SL-only-Variante (backtest_brk_slonly.py,
alle zehn KI enthalten die Null, Trefferquote faellt 40 -> 21 %).
Neu: haelt NUR BRK etwas, wird dessen Trailing geschaltet; sonst (auch bei
zwei offenen Positionen) das von Slot 1 - die Position, die der Nutzer selbst
geoeffnet hat.
⑥ Die Wiedereinstiegs-Sperre sah den BRK-Slot NICHT. `_check_auto_close` setzt
`_pos_close_ts`/`_pos_close_dir` aus `self.trader.snapshot()`; schloss eine
BRK-Position, blieb die Sperre unarmiert und der Squeeze durfte sofort in
dieselbe Richtung neu eroeffnen. Genau die Open->Instant-Close-Kaskade, gegen
die _SQUEEZE_REENTRY_COOLDOWN_S am 27.07. gebaut wurde.
Neu `_check_brk_close_cooldown` im _pos_loop. ⚠ Bewusst DIESELBEN Felder wie
Slot 1: die Sperre ist eine Aussage ueber den MARKT ("in diese Richtung
gerade nicht"), nicht ueber einen Slot.
GEPRUEFT UND SAUBER in diesem Durchgang:
- core/trailing.py hat KEINE Klassen-Variablen und keine veraenderlichen
Modul-Globalen -> die zwei TrailingManager-Instanzen sind vollstaendig
unabhaengig. Das war die groesste offene Sorge.
- _check_close_notify arbeitet ueber history.trade_by_ticket, also DB-basiert
und slot-unabhaengig.
⚠ WEITER OFFEN, bewusst nicht angefasst: `set_sltp()` wirkt nur auf Slot 1 (die
SL/TP-Felder koennen eine BRK-Position nicht anpassen) - das ist eine
UI-Entscheidung, welches Feld zu welchem Slot gehoert, und braucht eine Vorgabe
statt eines stillen Fixes. Ebenso `_check_auto_m15`, das `self.trader.open_long`
VOR dem `_open`-Aufruf bindet und damit `_slot()` umgeht (funktioniert derzeit
nur, weil beide dasselbe liefern).
91 Tests gruen, Pipeline inkl. der neuen Stufe E gruen, Deploy verifiziert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
2b498fc20e
commit
5e476025d7
+50
-2
@@ -868,6 +868,7 @@ class TradingEngine:
|
|||||||
# BRK-Slot mitfuehren (strikt ticket-gebunden, adoptiert nichts)
|
# BRK-Slot mitfuehren (strikt ticket-gebunden, adoptiert nichts)
|
||||||
self.trader_brk.refresh(self.data.symbol)
|
self.trader_brk.refresh(self.data.symbol)
|
||||||
self._rebind_brk() # nach Neustart zurueckbinden
|
self._rebind_brk() # nach Neustart zurueckbinden
|
||||||
|
self._check_brk_close_cooldown() # Sperre auch bei BRK-Close
|
||||||
self._check_circuit_breaker() # Tagesverlust-Stopp (Überleben zuerst)
|
self._check_circuit_breaker() # Tagesverlust-Stopp (Überleben zuerst)
|
||||||
self._check_auto_close()
|
self._check_auto_close()
|
||||||
# ⚠ NACH `_check_auto_close`: erst dort wird der Flat-Übergang
|
# ⚠ NACH `_check_auto_close`: erst dort wird der Flat-Übergang
|
||||||
@@ -994,6 +995,34 @@ class TradingEngine:
|
|||||||
log.warning(f"⚠ DB-Nachtrag T={tk} nach {n} Versuchen aufgegeben "
|
log.warning(f"⚠ DB-Nachtrag T={tk} nach {n} Versuchen aufgegeben "
|
||||||
f"— der Trade bleibt setup=NULL und fehlt in B4/B5.")
|
f"— der Trade bleibt setup=NULL und fehlt in B4/B5.")
|
||||||
|
|
||||||
|
def _check_brk_close_cooldown(self):
|
||||||
|
"""Wiedereinstiegs-Sperre auch für BRK-Closes armieren (Fix 2026-08-20).
|
||||||
|
|
||||||
|
⚠⚠ `_check_auto_close` setzt `_pos_close_ts`/`_pos_close_dir` aus
|
||||||
|
`self.trader.snapshot()` — es sieht den BRK-Slot also NICHT. Schloss eine
|
||||||
|
BRK-Position, blieb die Sperre unarmiert, und der Squeeze durfte sofort
|
||||||
|
in dieselbe Richtung neu eröffnen. Genau die Open→Instant-Close-Kaskade,
|
||||||
|
gegen die `_SQUEEZE_REENTRY_COOLDOWN_S` am 27.07. gebaut wurde — damals
|
||||||
|
killte ein zu enger Notfall-Stop den Entry nach 16 s, der Squeeze war
|
||||||
|
noch `active`, und es ging sofort wieder los (doppelter Spread).
|
||||||
|
⚠ Dieselben Felder wie Slot 1, bewusst: die Sperre ist eine Aussage über
|
||||||
|
den MARKT („in diese Richtung gerade nicht"), nicht über einen Slot.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
tk = getattr(self.trader_brk, "ticket", None)
|
||||||
|
vorher = getattr(self, "_brk_letzte", None)
|
||||||
|
if tk:
|
||||||
|
self._brk_letzte = (tk, (self.trader_brk.snapshot() or {}).get("order_type"))
|
||||||
|
return
|
||||||
|
if vorher:
|
||||||
|
self._brk_letzte = None
|
||||||
|
self._pos_close_ts = time.time()
|
||||||
|
self._pos_close_dir = "LONG" if vorher[1] == 0 else "SHORT"
|
||||||
|
log.info("BRK-Slot geschlossen (T=%s) — Wiedereinstiegs-Sperre "
|
||||||
|
"%s armiert.", vorher[0], self._pos_close_dir)
|
||||||
|
except Exception as e:
|
||||||
|
log.debug(f"_check_brk_close_cooldown: {e}")
|
||||||
|
|
||||||
def _rebind_brk(self):
|
def _rebind_brk(self):
|
||||||
"""BRK-Position nach einem Neustart zurückbinden (Fix 2026-08-20).
|
"""BRK-Position nach einem Neustart zurückbinden (Fix 2026-08-20).
|
||||||
|
|
||||||
@@ -1198,6 +1227,12 @@ class TradingEngine:
|
|||||||
self._emergency_loss = None
|
self._emergency_loss = None
|
||||||
self._takeprofit = None
|
self._takeprofit = None
|
||||||
return
|
return
|
||||||
|
# ⚠⚠ Der BRK-Slot wird hier NICHT erfasst: `ps` kommt aus
|
||||||
|
# `self.trader.snapshot()`. Schliesst eine BRK-Position, armiert die
|
||||||
|
# Wiedereinstiegs-Sperre also NICHT — und der Squeeze duerfte sofort
|
||||||
|
# in dieselbe Richtung neu eroeffnen. Genau die Open->Instant-Close-
|
||||||
|
# Kaskade, gegen die die Sperre am 27.07. gebaut wurde.
|
||||||
|
# `_check_brk_close_cooldown` (im `_pos_loop`) schliesst das.
|
||||||
self._open_pos_dir = "LONG" if ps.get("order_type") == 0 else "SHORT"
|
self._open_pos_dir = "LONG" if ps.get("order_type") == 0 else "SHORT"
|
||||||
if ticket != self._last_pos_ticket: # neue/wiedererkannte Position
|
if ticket != self._last_pos_ticket: # neue/wiedererkannte Position
|
||||||
self._last_pos_ticket = ticket
|
self._last_pos_ticket = ticket
|
||||||
@@ -2950,10 +2985,23 @@ class TradingEngine:
|
|||||||
return "; ".join(fehler) if fehler else None
|
return "; ".join(fehler) if fehler else None
|
||||||
|
|
||||||
def toggle_trail(self) -> bool:
|
def toggle_trail(self) -> bool:
|
||||||
|
"""TRAIL-Knopf — trifft den Slot, der tatsaechlich eine Position haelt.
|
||||||
|
|
||||||
|
⚠⚠ Bis 2026-08-20 schaltete er NUR `self.trail`. Hielt allein der
|
||||||
|
BRK-Slot eine Position, liess sich dessen Trailing per Dashboard weder
|
||||||
|
ein- noch ausschalten — und ohne `trail_brk` laeuft ein BRK-Trade in der
|
||||||
|
gemessen DURCHGEFALLENEN SL-only-Variante (`backtest_brk_slonly.py`:
|
||||||
|
alle zehn KI enthalten die Null, Trefferquote faellt 40 -> 21 %).
|
||||||
|
⚠ Haelt NUR der BRK-Slot etwas, wird dessen Trailing geschaltet; sonst
|
||||||
|
(auch bei zwei offenen Positionen) das von Slot 1 — das ist die
|
||||||
|
Position, die der Nutzer selbst geoeffnet hat.
|
||||||
|
"""
|
||||||
sym = self.data.symbol
|
sym = self.data.symbol
|
||||||
if sym:
|
if not sym:
|
||||||
return self.trail.toggle(sym)
|
|
||||||
return self.trail.enabled
|
return self.trail.enabled
|
||||||
|
nur_brk = (bool(getattr(self.trader_brk, "ticket", None))
|
||||||
|
and not (self.trader.snapshot() or {}).get("ticket"))
|
||||||
|
return (self.trail_brk if nur_brk else self.trail).toggle(sym)
|
||||||
|
|
||||||
def _sr_levels(self, market: dict) -> dict:
|
def _sr_levels(self, market: dict) -> dict:
|
||||||
"""Nächste Widerstandslinie ÜBER Preis (Short) und Unterstützung UNTER
|
"""Nächste Widerstandslinie ÜBER Preis (Short) und Unterstützung UNTER
|
||||||
|
|||||||
Reference in New Issue
Block a user