Telegram: ALLE Trade-Opens und -Closes + Reichweiten-Waechter fuer den 2. Slot

(1) User-Vorgabe: "Alle Trade Closes und Opens sollen per Telegram gemeldet
werden".
 - close_notify_min_eur 50 -> 0, also jeder Close ohne Schwelle
   (Backup oil_widget_config.ini.bak-2026-08-19-tgopen)
 - neuer _check_open_notify: meldet jede Eroeffnung, ueber BEIDE Slots. Der
   BRK-Slot ist ausdruecklich dabei - ein autonomer Entry soll nicht unbemerkt
   bleiben, genau dafuer wurde der zweite Slot gebaut; die Meldung nennt den
   Slot mit.
 - _TG_ERLAUBT um "Trade eroeffnet" erweitert (das Gate ist EINE Filterstelle,
   nicht 17 Einzeleingriffe).
 ⚠ 1x je Ticket ueber _open_notify_gemeldet - ohne den Merker feuerte es im
   1-s-Takt weiter, solange die Position offen ist.
 ⚠ In der Startup-Schonfrist wird nur GEMERKT, nicht gemeldet: nach einem
   Neustart erkennt der Trader eine BESTEHENDE Position wieder, und das ist
   keine Eroeffnung.

(2) Reichweiten-Waechter (_check_slot_reichweite). Gemessener Befund: von den
Schutz-Checks ist NUR _check_circuit_breaker slot-faehig. _check_auto_close
(Notfall-Stop, haengt an 4 Ticket-Variablen), _check_sr_close, _check_adverse15
und _check_flip_close kennen nur Slot 1. Heute folgenlos, weil alle vier AUS
sind - aber wird einer eingeschaltet, waehrend eine BRK-Position laeuft, gilt er
stillschweigend nur fuer Slot 1, und im Dashboard saehe alles normal aus.
Dieselbe Klasse wie Deployment-Drift Fall 8: nicht die Strategie driftet,
sondern ihre REICHWEITE. Der Waechter macht das Loch LAUT (Log-Warnung 1x je
Ticket + Snapshot-Feld `slot_luecke`), bis die vier slot-faehig sind.

⚠ Die volle Parametrisierung der vier Checks steht AUS. _check_auto_close allein
haengt an vier Ticket-Variablen und ist der Notfall-Stop - das gehoert in eine
eigene Sitzung mit Tests, nicht ans Ende einer langen.

⚠ EIGENER FEHLER auf dem Weg: der erste Bauversuch lief per Heredoc und riss das
Telegram-Format-Literal an einem \n auseinander (SyntaxError). Das ist exakt die
im Projekt dokumentierte \n-Falle - "mehrzeilige Strings mit \n NIE per Heredoc
schreiben, den Edit-Weg nehmen". Ich habe die eigene Regel verletzt; engine.py
wurde per git checkout zurueckgesetzt und sauber ueber Edit neu gebaut.

Deploy ueber tools/deploy.py --feld position_brk, alle 5 Schritte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-19 23:23:01 +02:00
co-authored by Claude Opus 5
parent 0fe51dea48
commit f02fd43b39
+84 -1
View File
@@ -183,7 +183,9 @@ def _p_break(mom6: float, mom3: float, wt: float, dist: float) -> float:
# EINE AUSNAHME mit Bypass: "<ok_word> FEHLGESCHLAGEN" beim Notfall-/ # EINE AUSNAHME mit Bypass: "<ok_word> FEHLGESCHLAGEN" beim Notfall-/
# Gewinn-Close. Dort ist die Position NICHT zu und es braucht eine # Gewinn-Close. Dort ist die Position NICHT zu und es braucht eine
# Handeingabe -- Stille genau dort waere die gefaehrlichste Variante. # Handeingabe -- Stille genau dort waere die gefaehrlichste Variante.
_TG_ERLAUBT = ("Trade geschlossen",) # ⚠ User-Vorgabe 2026-08-19: ALLE Trade-Closes UND -Opens melden.
# `close_notify_min_eur` steht dafür auf 0 (= jeder Close, keine Schwelle).
_TG_ERLAUBT = ("Trade geschlossen", "Trade eroeffnet")
def send_telegram(text, *a, **kw): def send_telegram(text, *a, **kw):
@@ -862,6 +864,8 @@ class TradingEngine:
# ⚠ NACH `_check_auto_close`: erst dort wird der Flat-Übergang # ⚠ NACH `_check_auto_close`: erst dort wird der Flat-Übergang
# erkannt und die Benachrichtigung vorgemerkt. # erkannt und die Benachrichtigung vorgemerkt.
self._check_close_notify() # Telegram bei jedem Close ab Schwelle self._check_close_notify() # Telegram bei jedem Close ab Schwelle
self._check_open_notify() # Telegram bei jeder Eröffnung
self._check_slot_reichweite() # meldet ungedeckte 2. Position
self._check_adverse15() # 15-Min-Regel (nur Bot-Trades) self._check_adverse15() # 15-Min-Regel (nur Bot-Trades)
self._check_flip_close() # Empfehlung dreht → schließen self._check_flip_close() # Empfehlung dreht → schließen
self._check_sr_close() self._check_sr_close()
@@ -945,6 +949,85 @@ class TradingEngine:
return False return False
return self._circuit_tripped_date == datetime.now(_BERLIN).date() return self._circuit_tripped_date == datetime.now(_BERLIN).date()
def _check_open_notify(self):
"""Telegram bei JEDER Eröffnung — beide Slots (User-Vorgabe 2026-08-19).
Bewusst über BEIDE Trader: ein autonomer BRK-Entry soll nicht
unbemerkt bleiben genau der Fall, für den der zweite Slot gebaut
wurde. Die Meldung nennt deshalb den Slot mit.
1× je Ticket (`_open_notify_gemeldet`); ohne den Merker feuerte sie im
1-s-Takt weiter, solange die Position offen ist.
In der Startup-Schonfrist wird nur GEMERKT, nicht gemeldet: nach einem
Neustart erkennt der Trader eine BESTEHENDE Position wieder, und das ist
keine Eröffnung.
"""
if not hasattr(self, "_open_notify_gemeldet"):
self._open_notify_gemeldet = set()
for tr, quelle in ((self.trader, "Slot 1"), (self.trader_brk, "BRK")):
try:
ps = tr.snapshot() or {}
tk = ps.get("ticket")
if not tk or tk in self._open_notify_gemeldet:
continue
self._open_notify_gemeldet.add(tk)
if self._in_startup_grace():
continue
lg = ps.get("order_type") == 0
zeilen = [
f"📈 <b>Trade eroeffnet</b> ({quelle})",
f"{self.data.symbol or '?'} "
f"{'▲ LONG' if lg else '▼ SHORT'} "
f"{ps.get('lots')} Lots @ {ps.get('entry_price')}",
f"SL {ps.get('sl') or ''} · TP {ps.get('tp') or ''}",
]
send_telegram("\n".join(zeilen))
except Exception as e:
log.debug(f"_check_open_notify: {e}")
if len(self._open_notify_gemeldet) > 200:
self._open_notify_gemeldet = set(list(self._open_notify_gemeldet)[-50:])
# ⚠⚠ REICHWEITEN-WÄCHTER (2026-08-19). Vier Schutz-Checks kennen bislang NUR
# den ersten Slot: `_check_auto_close` (Notfall-Stop, hängt an 4
# Ticket-Variablen), `_check_sr_close`, `_check_adverse15`,
# `_check_flip_close`. Solange sie AUS sind, ist das folgenlos — der
# BRK-Slot hat Broker-SL, `trail_brk` und den Circuit-Breaker.
# ⚠ Wird einer davon wieder eingeschaltet, WÄHREND eine BRK-Position
# läuft, gilt er stillschweigend nur für Slot 1: die zweite Position liefe
# ohne ihn, und im Dashboard sähe alles normal aus. Dieselbe Klasse wie
# Deployment-Drift Fall 8 — nicht die Strategie driftet, sondern ihre
# REICHWEITE. Bis die vier slot-fähig sind, macht dieser Wächter das Loch
# LAUT statt still (1× je Ticket, kein Dauerlärm).
def _check_slot_reichweite(self):
try:
tk = self.trader_brk.ticket
if not tk:
self._reichweite_gemeldet = None
return None
offen = []
if ((getattr(self, "_emergency_margin_pct", 0) or 0) > 0
or (getattr(self, "_emergency_pct", 0) or 0) > 0
or (getattr(self, "_emergency_remember", 0) or 0) > 0):
offen.append("Notfall-Stop")
if getattr(self, "_auto_sr_close", False):
offen.append("S/R-Close")
if (getattr(self, "_adverse15_atr", 0) or 0) > 0:
offen.append("15-Min-Regel")
if getattr(self, "_flip_close", False):
offen.append("Flip-Close")
if not offen:
self._reichweite_gemeldet = None
return None
if getattr(self, "_reichweite_gemeldet", None) != tk:
self._reichweite_gemeldet = tk
log.warning("⚠ REICHWEITE: %s aktiv, gilt aber NUR für Slot 1 — "
"die BRK-Position T=%s läuft ohne (Broker-SL, "
"Trailing und Circuit-Breaker greifen weiter).",
", ".join(offen), tk)
return offen
except Exception as e:
log.debug(f"_check_slot_reichweite: {e}")
return None
def _check_circuit_breaker(self): def _check_circuit_breaker(self):
"""Tagesverlust-Stopp (User-Vorgabe 2026-07-27): erreicht der Tages-P&L """Tagesverlust-Stopp (User-Vorgabe 2026-07-27): erreicht der Tages-P&L
(realisiert heute + offene Position) `daily_loss_limit_pct`% der Balance, (realisiert heute + offene Position) `daily_loss_limit_pct`% der Balance,