From 9f34180bc1384739e405d744d4fa3917f9ae42f0 Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Tue, 28 Jul 2026 10:07:27 +0200 Subject: [PATCH] Auto-Squeeze: Wiedereinstiegs-Sperre nach jedem Close + Margin 95% MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Nach Notfall/SL/Time-Stop/manuellem Close eröffnet der Auto-Squeeze ~3 min nicht in gleicher Richtung wieder (_SQUEEZE_REENTRY_COOLDOWN_S=180). Verhindert die Open->Instant-Close-Kaskade bei zu engem Stop (real 27.07. -3-EUR-Stop killte Entry #1 in 16s, Squeeze noch aktiv -> sofort Re-Entry #2). - _check_auto_close erfasst Flat-Uebergang (_pos_close_ts/_pos_close_dir via _open_pos_dir); Squeeze-Entry-Pfad prueft ihn nach dem S/R-Cooldown. - margin_buffer_pct 80 -> 95 (User-Vorgabe) - CLAUDE.md aktualisiert Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 22 ++++++++++++++++++---- core/engine.py | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0b0d504..ede3490 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -397,10 +397,11 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche: M5/M15 gegen-Bouncen — sonst Patt: Long geblockt, Short fehlt). **H1 bleibt nur Kontext** (Filter/Konfluenz). Start-TF wird ins Band geklemmt. `agent` = LLM wählt; `M1..H1` = fest. Der **Agent steuert die TF nicht mehr**. -- **Positionsgröße = margin-basiert 80 % (User-Vorgabe 2026-07-20, `risk_pct=0`, - `margin_buffer_pct=80`):** `calc_lots` setzt die Lots auf ~80 % der freien Margin. - Verlauf 90→95→75→(1,5 % risiko, wenige Stunden am 2026-07-20)→**80 % margin** — - der User hat die Margin-Wahl damit 3× getroffen (bewusst; nicht ungefragt umstellen). +- **Positionsgröße = margin-basiert 95 % (User-Vorgabe 2026-07-28, `risk_pct=0`, + `margin_buffer_pct=95`):** `calc_lots` setzt die Lots auf ~95 % der freien Margin. + Verlauf 90→95→75→(1,5 % risiko, wenige Stunden am 2026-07-20)→80 %→**95 % margin + (2026-07-28)** — der User hat die Margin-Wahl damit mehrfach bewusst getroffen (nicht + ungefragt umstellen). ⚠ 95 % ist noch aggressiver als die 80 %-Referenz unten. ⚠ Aggressiv: der −95,48-€-Nacht-Trade 2026-07-20 (News-ATR → SL ~1,0 $ breit × 1,03 Margin-Lots ≈ 11 % Konto) ist der Referenzfall; mit `risk_pct=1.5` wären es ~0,15 Lots / ~−14 € gewesen (`backtest_sizing.py`: Margin-Sizing bis 36 % @@ -801,6 +802,19 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche: NICHT gesetzt → nach Ablauf feuert der Ausbruch, falls noch aktiv. **Der Reverse (Gegenrichtung) bleibt unberührt** — nur der Gleichrichtungs-Wiedereinstieg wird verzögert. + **Allgemeine Wiedereinstiegs-Sperre nach JEDEM Close (2026-07-28, Koordinations-Fix, + kein Backtest):** nach dem Schließen eines Trades durch **Notfall-Stop / Broker-SL / + Time-Stop / manuell** eröffnet der Auto-Squeeze **~3 min (`_SQUEEZE_REENTRY_COOLDOWN_S= + 180`) nicht in GLEICHER Richtung** wieder. Grund: real am **2026-07-27 15:39** killte + ein zu enger manueller −3-€-Notfall-Stop (Sub-Spread bei Margin-Sizing) den Auto- + Squeeze-Entry #1 nach **16 s**; der Squeeze war noch `active` → sofortiger Re-Entry #2 + (Open→Instant-Close-Kaskade, doppelter Spread). `_check_auto_close` erfasst den Flat- + Übergang (`_pos_close_ts`/`_pos_close_dir`, Richtung via `_open_pos_dir` solange offen), + der Squeeze-Entry-Pfad prüft ihn NACH dem S/R-Cooldown; `_squeeze_last_sig` wird NICHT + gesetzt → nach Ablauf feuert der Ausbruch, falls noch aktiv. Gegenrichtung/Reverse + unberührt. Deckt alle Close-Arten außer dem S/R-Close ab (der hat seinen eigenen + Marker). ⚠ Behebt das *Symptom*; der eigentliche Auslöser war der Sub-Spread-Stop + (`emergency_min_eur=3`-Guard fängt <3 € ab, −3 € genau auf der Grenze war zulässig). - **TF-Wechsel nicht mehr persistiert:** `engine._apply_wave_tf` merkt den Heuristik-TF nur im Speicher (kein `save_config` mehr) — sonst würde die Secrets-`ini` bei jedem Switch neu geschrieben (Kommentar-Verlust/Churn). Beim diff --git a/core/engine.py b/core/engine.py index 50ed2f1..b22dd2f 100644 --- a/core/engine.py +++ b/core/engine.py @@ -70,6 +70,11 @@ _SQUEEZE_NIGHT = (0, 1, 2, 3, 4, 5, 6, 7) # Auto-Squeeze-Nacht-Sperre (Kostenf _SQUEEZE_SR_COOLDOWN_S = 180.0 # nach S/R-Close kein Auto-Squeeze in GLEICHER Richtung # (sonst Close-„Level hält" vs. Entry-„Level bricht"- # Roundtrip in 1 s; 2026-07-17) +_SQUEEZE_REENTRY_COOLDOWN_S = 180.0 # nach JEDEM Close eines Trades (Notfall/SL/Time-Stop/ + # manuell) kein Gleichrichtungs-Squeeze-Wiedereinstieg — + # gegen die Open→Instant-Close-Kaskade bei zu engem Stop + # (real 2026-07-27 15:39: −3-€-Stop killte Entry #1 in 16 s, + # Squeeze noch aktiv → sofortiger Re-Entry; 2026-07-28) _STARTUP_GRACE_S = 60.0 # nach Bot-Start schließt der Bot 60 s lang KEINEN # laufenden Trade server-seitig (S/R-Close/Notfall/ # Time-Stop/Reverse) — die Daten (Preis/ATR/P(break)/ @@ -219,6 +224,10 @@ class TradingEngine: self._squeeze_entry_count = 0 self._sr_close_ts = 0.0 # Zeit + Richtung des letzten S/R-Closes self._sr_close_dir = None # → Cooldown gegen Gleichrichtungs-Roundtrip + self._open_pos_dir = None # Richtung der aktuell offenen Position (LONG/SHORT) + self._pos_close_ts = 0.0 # Zeit + Richtung des letzten Positions-Close (JEDE + self._pos_close_dir = None # Art) → Squeeze-Wiedereinstiegs-Sperre (gegen die + # Open→Instant-Close-Kaskade bei zu engem Notfall-Stop) self._sr_close_min_gain_armed_ticket = None # Ticket, für das die %-Schwelle schon # aus echter Margin berechnet wurde (Retry-Schutz, # s. `_check_auto_close`) @@ -682,10 +691,19 @@ class TradingEngine: ps = self.trader.snapshot() ticket = ps.get("ticket") if not ticket: + # Flat-Übergang: hatten wir gerade noch eine Position, merke Zeit+Richtung + # → Squeeze-Wiedereinstiegs-Sperre (gegen Open→Instant-Close-Kaskade). Gilt + # für JEDE Close-Art (Notfall/SL/Time-Stop/manuell); der S/R-Close hat schon + # seinen eigenen Marker, dieser deckt den Rest ab. + if self._last_pos_ticket is not None and self._open_pos_dir: + self._pos_close_ts = time.time() + self._pos_close_dir = self._open_pos_dir + self._open_pos_dir = None self._last_pos_ticket = None self._emergency_loss = None self._takeprofit = None return + self._open_pos_dir = "LONG" if ps.get("order_type") == 0 else "SHORT" if ticket != self._last_pos_ticket: # neue/wiedererkannte Position self._last_pos_ticket = ticket if ticket == self._emergency_state_ticket: @@ -921,6 +939,13 @@ class TradingEngine: if (d == self._sr_close_dir and time.time() - self._sr_close_ts < _SQUEEZE_SR_COOLDOWN_S): return + # Wiedereinstiegs-Sperre nach JEDEM Close (Notfall/SL/Time-Stop/manuell): + # nicht sofort in GLEICHER Richtung neu eröffnen — verhindert die + # Open→Instant-Close-Kaskade bei zu engem Stop (2026-07-27). `_squeeze_last_sig` + # NICHT setzen → nach Ablauf feuert der Ausbruch, falls noch aktiv. + if (d == self._pos_close_dir + and time.time() - self._pos_close_ts < _SQUEEZE_REENTRY_COOLDOWN_S): + return self._squeeze_last_sig = sig if isinstance(self._last_rec, dict): self._last_rec["setup"] = f"SQUEEZE_{d}"