From e48ad80c0ac0f5d1ee705646c673d1cc405a3550 Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Wed, 19 Aug 2026 17:03:30 +0200 Subject: [PATCH] UI aufgeraeumt + Telegram auf Closes reduziert (v=177) Vier User-Vorgaben: 1. Einsatz-Felder entfernt. Backend bleibt vollstaendig -- Folge: der Prozentsatz ist ohne UI nur ueber ini + Neustart aenderbar. 2. BRK-Schalter entfernt, dafuer STOPP (Circuit-Breaker). Bewusster Tausch mit Konsequenz: der An/Aus-Schalter des AUTONOMEN Pfads ist aus der App weg (nur noch POST bzw. ini), sein Zustand bleibt in der Breakout-Karte sichtbar. Dafuer ist die Notbremse bedienbar, die am 17.08. real ausgeloest hat. set_circuit_breaker + POST /api/circuit + runtime_state (cb_limit_pct, cb_remember -- ohne das Gedaechtnis waere die Prozentzahl nach einmal Ausschalten fuer immer weg). 3. Telegram nur noch bei geschlossenen Trades: EINE Filterstelle vor _send_telegram_roh statt 17 Einzeleingriffe, _TG_ERLAUBT = ('Trade geschlossen',). Umkehren = Liste erweitern. Tages-/Wochenreport wird nur auf dem Telegram-Weg still, die E-Mail laeuft weiter. EINE Ausnahme mit Bypass: ' FEHLGESCHLAGEN' beim Notfall-Close -- dort ist die Position NICHT zu und es braucht eine Handeingabe. Verifiziert: /api/circuit vorher 405, nachher 200; Toggle aus->an->8 %/57,94 EUR persistiert; 85 Tests, node --check, check_nfalle gruen; 0 verwaiste JS-Referenzen; genau eine Instanz je Port. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 8302 ------------------------------ core/engine.py | 79 +- core/engine.py.bak-2026-08-19-tg | 4926 ++++++++++++++++++ server.py | 13 + server.py.bak-2026-08-19-cb | 474 ++ web/app.js | 112 +- web/index.html | 34 +- web/index.html.bak-2026-08-19-ui | 392 ++ 8 files changed, 5907 insertions(+), 8425 deletions(-) create mode 100644 core/engine.py.bak-2026-08-19-tg create mode 100644 server.py.bak-2026-08-19-cb create mode 100644 web/index.html.bak-2026-08-19-ui diff --git a/CLAUDE.md b/CLAUDE.md index eb0ed93..e69de29 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,8302 +0,0 @@ -# CLAUDE.md — Oil Trading Bot (MT5, WTI) - -Projektleitfaden für Claude Code. Antworten/UI-Texte auf **Deutsch**. - -> 🔄 **Pflege-Regel:** Diese Datei nach **jeder** inhaltlichen Änderung -> mitaktualisieren (geänderte Logik/Werte, neue Module/Endpoints, UI-/Alarm- -> Verhalten, Config-Schalter, Workflows, Asset-Version `v=N`). Kein separater -> Auftrag nötig — Teil jeder Aufgabe. -> -> 🧱 **5-Layer-Prozess bei jeder Änderung** (Daueregel, in dieser Reihenfolge): -> **L1** Kontext, Befunde **& Risiko/Auswirkung** (offene Befunde hier · „erst -> messen" bei Strategie · was kostet/ändert es — EUR-Risiko/Trade, reversibel?) · -> **L2** Code-Analyse & Design (Datenquelle, *Quelle der Wahrheit*, wo lebt die -> Logik) · **L3** UI-Design (Module klar in Text **und** Grafik, auf dem geklärten -> Datenmodell) · **L4** Implementieren, Verifizieren **& Rollout** (`py_compile` · -> JS-Klammern · Backtest/Test · Version-Bump `v=N` · Aktivierungsweg `restart_server.bat` -> · **Backup vor destruktiven Schritten**, v. a. DB) · **L5** Doku aktualisieren -> (`docs/` **und** CLAUDE.md, zuletzt). -> -> ⚖ **Layer an die Größe der Änderung skalieren** — nicht jede Aufgabe braucht alle -> 5. Strategie-Änderung = alle 5 (inkl. Backtest); reine Anzeige = L2–L5 light (kein -> Backtest); Doku-Korrektur = nur L5. Prozess soll helfen, nicht zur Bürokratie werden. -> -> 🔬 **Track B — Strategie-Lebenszyklus** (ZUSÄTZLICH zu L1–L5, für ALLES, was den -> **Edge** berührt: Signal, Risk/Sizing, Exit, Gate, Filter). Track A = L1–L5 -> (Code/UI/Infra) bleibt unverändert; Track B läuft *parallel* für Strategie-Ideen: -> **B1 Hypothese + Hebel-Check** — welcher Hebel? *Signal = klein* (6× belegt: Filter -> tragen kaum → **höhere Beweislast**, im Zweifel gar nicht bauen) vs. *Sizing/Exit = -> groß* (dort liegt das Geld). · **B2 2-Stichproben-Backtest** — IMMER ≥2 Zeiträume/ -> Regime, nie eine (Dead-Hour-Lektion: Std 13 war in einer Stichprobe „profitabel", -> in der zweiten Rauschen; `backtest_hourly_split.py`). · **B3 Kosten-Check** — Ø-Edge -> **netto > ~0,1×ATR** (real Spread+Komm.)? sonst raus. · **B4 Live-klein + Monitor** — -> ausrollen, dann **Live-WR/PF/Verhältnis** (Statistik-live-Kachel `stats_compact` + -> Flip-Zähler) **gegen die Backtest-Erwartung** halten; Kadenz **wöchentlich** (Termin, -> keine Stufe). · **B5 Behalten oder Verwerfen** — driftet es live weg (Regimewechsel) -> → **zurückbauen, kein Ego.** B4/B5 = die Verfall-Erkennung, die im reinen „Änderung -> machen"-Prozess fehlt (Trading ≠ Software: Edge kann verfallen). -> -> 🧾 **B0 — Handels-Checkliste (Mensch, vor jedem manuellen Trade):** (1) **nicht gegen -> das Signal** (gemessen WR 14 %); (2) **Notfall-Stop gesetzt?**; (3) **Gewinner läuft -> das Trailing** (nicht früh kappen — Ø-Verlust ≫ Ø-Gewinn ist die Kern-Leckage). Steuert -> die eigentliche Verlustquelle (diskretionäre Abweichungen), die kein Code-Layer erreicht. - -## Was das ist -Algorithmischer **WTI-Öl-Trading-Bot** auf MetaTrader 5. (Brent-Umstellung am -2026-07-09 auf User-Wunsch **noch am selben Tag zurückgenommen** — falls je wieder -Brent: Pepperstone-Symbol heißt `SpotBrent`, kein „UKOIL"; breakout_k=0,3 wurde -dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche: -- **`server.py` + `core/engine.py`** — headless FastAPI-Web-Backend für die - **Mobile-PWA** in `web/` (Zugang vom Handy via WireGuard, `192.168.188.100:8000`). - -> Das frühere Tkinter-Widget (`oil_trading_agent.py`, `ui/`, `single_instance.py`) -> wurde **entfernt** — nur noch der Server. ⚠ Immer nur **EINE** Server-Instanz -> gegen dasselbe MT5-Terminal laufen lassen (zwei TrailingManager = Konflikt). - -## Architektur (core/) -- `mt5data.py` Preis/Trend/Konto · `trader.py` Orders+SL/TP · `trailing.py` - Trailing/Teil-Exit · `wave_rec.py` **Empfehlung** · `elliott.py` EW/FVG · - `tu_rating.py` TradersUnion · `agent.py` KI-Copilot - (LLM) · `daily_levels.py` Tages-Prognose (Web) · `gaps.py` D1-Kurslücken · - `mailer.py` Graph-E-Mail · - `structure.py` Marktstruktur (M30: HH/HL/LH/LL, BOS, Regressionskanal — reine - Anzeige, kein Signal) · `candle_logger.py` M1-Candle-Log (Daten-Sammlung → `candles_m1`) · - `market_hours.py` Sessions · `history.py` SQLite · `config.py` Konstanten+ - `oil_widget_config.ini`-Loader · `mt5_utils.py` globaler `mt5_lock`. -- `engine.py` = headless Motor: instanziiert die core-Objekte, fährt Loops als - Threads, liefert `snapshot()` für `/api/snapshot` + WebSocket `/ws`. - `_verdict()` aggregiert die Module zur **Gesamtempfehlung** (`verdict` im - Snapshot) — Doku: `docs/gesamtempfehlung.md`. - -## Harte Fakten / Stolpersteine -- **MT5 ist Windows-only** (`MetaTrader5`-Wheel). Backend läuft auf Windows neben - dem Terminal; nur Reverse-Proxy/HTTPS könnte auf Linux/Docker. -- **Brokerzeit = UTC+3.** `deal.time`/`tick.time` immer via `_broker_offset_s()` - bzw. Live-Offset in UTC/Lokalzeit umrechnen. -- **`mt5_lock` (global, nicht reentrant):** alle MT5-Calls darüber, mit Timeout. - Nicht aus einem bereits-gehaltenen Lock erneut `close()` (Deadlock). ⚠ Auch - Thread-Pool-Jobs müssen den Lock nehmen: `M15Analyzer.analyze()` läuft im - Executor → eigener `mt5_lock`-Wrapper in `mt5data._fetch_trend_locked` (sonst - Race). `sr.detect()` läuft inline unter dem bereits gehaltenen Lock. Ebenso: - `mt5data.reconnect()` (shutdown/initialize) und `trader._log_open`-`delayed_log` - (positions_get im Thread) laufen jetzt unter `mt5_lock` (waren Races). -- ⚠⚠ **STATISTIK-PRÜFUNG 2026-08-05 (User-Auftrag): alle Aggregate KORREKT — aber - die NETTO-Rechnung überschätzt die Steuerlast um Faktor 4,7 und dreht das - Vorzeichen des Gesamtergebnisses.** - ✅ **Geprüft und exakt:** `/api/stats` gegen unabhängige SQL-Rechnung, ~50 Werte - über `today`/`week`/`all` — n_trades, wins/losses/breakeven, total_pnl, avg_win, - avg_loss, gross_win/loss, winrate, profit_factor, dazu die inneren Konsistenzen - (wins+losses+be = n · gross_win−gross_loss = total_pnl · Σ closed_by = n · - Σ hour_stats = wins/losses) und 8 Setup-Zeilen. **Alles stimmt bitgenau.** - Ebenso `stats_compact` (Dashboard-Kachel) und `day_pl` im Header. - ⚠⚠ **DER DEFEKT liegt in `server._add_net` / `_add_net_setup`:** - `net_pnl = total_pnl − gross_win × 26,375 %`. Das unterstellt, die Quellensteuer - auf JEDEN Gewinn-Trade sei endgültig verloren. **Sie wird aber größtenteils - erstattet** — genau das steht zwei Absätze weiter unten („erstattet via tägl. - *Tax settlement*"), fließt in die Rechnung aber nicht ein. - **Empirisch aus den echten Broker-Buchungen** (`history_deals_get`, 08.05.–05.08., - derselbe Zeitraum wie die Statistik): - | | | - |---|---| - | WHT einbehalten (685 Buchungen) | **−2.236,10 €** | - | erstattet (63 Tax-Buchungen) | **+1.789,65 €** | - | **tatsächlich verblieben** | **−446,45 €** | - | Modell der Statistik | −2.097,10 € | - | **angezeigtes Netto** | **−1.104,67 €** | - | **reales Netto** | **+545,98 €** | - **Abweichung +1.650,65 € — die Anzeige macht aus einem profitablen Konto ein - verlustreiches.** Real verbleiben nur **21 %** der modellierten Last. - ✅ **Richtige Lösung: die TATSÄCHLICHEN Buchungen verwenden statt zu schätzen.** - Die WHT-/Tax-Deals sind über `mt5.history_deals_get` + Kommentar-Filter - (`WHT #…` / `Tax settlement`) exakt abrufbar; die Schätzung kann ganz entfallen. - ⚠ Zu cachen (MT5-Abfrage gehört nicht in den 1-s-Snapshot) und unter `mt5_lock`. - ⚠ **Der Header-G/V ist ein anderer Fall** (`app.js`: `raw × (1 − wht/100)` für die - OFFENE Position): dort ist der Abzug im Moment des Schließens sachlich richtig — - die Erstattung kommt erst später. Das ist eine Cashflow-Sicht, also vertretbar, - aber ebenfalls pessimistisch. - **Nicht gebaut** — Umfang war die Prüfung. -- **Steuer:** Broker behält **WHT 26,375 %** (DE-Abgeltungst.+Soli) je Gewinn ein - (`WHT #…`-Buchungen), erstattet via tägl. „Tax settlement". Stats sind **brutto**; - `[trading] wht_pct` zeigt eine Netto-Schätzung. -- **Python:** `C:\Users\ah\AppData\Local\Programs\Python\Python312\python.exe` - (hat fastapi/uvicorn/MetaTrader5). System32 fehlte mal in der PATH → Skripte - nutzen absolute Pfade. -- ⚠⚠ **LLM-PROVIDER-WECHSEL 2026-08-05: `[agent] provider=zai` (war `deepseek`).** - DeepSeek-Guthaben ist leer — `HTTP 402 "Insufficient Balance"`. **z.ai/GLM - (`glm-4.5-flash`) ist kostenlos**, antwortet in ~2,3 s (DeepSeek ~6 s) und wird im - Projekt ohnehin für `daily_levels` genutzt; live verifiziert („[zai] NEUTRAL (30%)"). - Kimi (`kimi-k2.6`) antwortet ebenfalls (7,6 s), ist aber kostenpflichtig. - ⚠⚠ **Der eigentliche Befund: die Fallback-Kette hatte NICHT gegriffen.** `_active_ - provider` prüfte in `avail` nur das **Key-FORMAT** (`startswith("sk-")`), nicht ob der - Key noch funktioniert. Ein Provider ohne Guthaben galt damit als „verfügbar" und - **blockierte die gesamte Kette** (`deepseek→kimi→zai→local`) — der Copilot lief - **~18 h still ins Leere** (29 Fehlversuche ab 04.08. 14:52), ohne dass irgendetwas - außer einer WARNING-Zeile passierte. - ✅ **BEHOBEN am selben Tag (`agent._mark_dead`/`_provider_chain`/`_chain_raw`):** ein - hart gescheiterter Provider wird zeitweise übersprungen und `analyze()` nimmt den - nächsten aus der Kette. **Hart = 401/402/403/404** (Key/Guthaben/Modell) → **30 min**; - **nicht erreichbar** (z. B. Ollama aus) → 5 min. **Bewusst NICHT hart: Timeout, 429, - 5xx** — das sind vorübergehende Zustände; wer darauf wechselt, verliert bei jeder - Lastspitze den bevorzugten Anbieter und zahlt pro Zyklus die volle Timeout-Summe - ALLER Anbieter (90–120 s je Stück). Sichtbar im Snapshot als - **`agent.provider_dead {provider: Restminuten}`** — der Ausfall stand vorher nur im - Log, genau deshalb blieb er 18 h unbemerkt. `_dead` läuft absichtlich **ohne** - `self._lock` (`snapshot()` hält ihn bereits, nicht reentrant → Deadlock; dict-Zugriffe - sind unter dem GIL atomar). Mit **9 Szenarien getestet** (402→Fallback · zweiter Zyklus - überspringt direkt · zwei tote Anbieter · Timeout/429 lösen KEINEN Wechsel aus · - ConnectionError 5 min · alle tot → sauberer Fehler · Erholung nach Ablauf · Snapshot). - Zurück auf DeepSeek nach Aufladen: `provider = deepseek`. -- **LLM-Provider (Historie):** **DeepSeek (`[deepseek]`) war der aktive - Copilot-Provider** (`[agent] provider=deepseek`, 2026-07-20 bis 2026-08-05). `api.deepseek.com`, - Modell **`deepseek-v4-flash`** (`/models` listet flash + pro; alte Namen - `deepseek-chat/-reasoner` gibt es NICHT mehr). ⚠ v4-flash ist ein **Reasoning-Modell** - (content nach `reasoning_content` → `max_tokens=4000` in `_call_deepseek`, sonst leer; - temperature 0.3 ok, schnell ~6 s). **⚠ KEINE Web-Suche** → NICHT für `daily_levels` - nutzbar. Fallback-Kette kimi→zai→local greift, wenn der Key ausfällt. - **Kimi/Moonshot (`[kimi]`, `kimi-k2.6`)** bleibt als Provider verfügbar. Key nur auf dem - **internationalen Endpoint `api.moonshot.ai`** gültig (`.cn` = „Invalid - Authentication"). Modell **`kimi-k2.6`** (`/v1/models` listet auch k2.7-code/k3; - k3 = thinking-only). ⚠ **Zwei Fallstricke:** (1) `temperature` MUSS **1** sein - (andere Werte → HTTP 400); (2) k2.6 ist ein **Reasoning-Modell** → schreibt - VARIABLE ~1000–1200 `reasoning_tokens` (in `message.reasoning_content`) VOR dem - `content` → `max_tokens` großzügig (**4000**, `_call_kimi`), sonst frisst das - Reasoning das Budget und `content` bleibt leer (2000 war teils leer). Moonshot - cached den System-Prompt (`cached_tokens`) → günstiger bei Wiederholung. Kimi ist - CN-Modell → CJK-Drift möglich, vom `_has_cjk`-Retry abgedeckt. - Frühere Option **z.ai/GLM** (`[zai]`, `glm-4.5-flash`, kostenlos) bleibt - reaktivierbar; ⚠ GLM ist Thinking-Modell → IMMER `"thinking":{"type":"disabled"}`. - ⚠ **`_active_provider`-Fix 2026-07-20:** zai/kimi waren vorher NICHT in der - `avail`/`orders`-Map → `provider=zai` fiel still auf `local` (qwen) zurück (die - `[local]`-Logzeilen!). Jetzt beide routbar. OpenAI/Gemini-Keys = kein Guthaben (429). - - **KI-Copilot (`agent.py`):** `[agent] provider=deepseek`. CN-/lokale Modelle - (DeepSeek, Kimi, GLM, qwen) driften ins Chinesische → `_has_cjk`-Erkennung + 1× - Retry deckt `local`, `zai`, `kimi` UND `deepseek` ab. (alle bleiben als Provider- - Optionen wählbar.) - - **`daily_levels.py` bleibt auf z.ai (2026-07-20, gemessen):** Die Tagesprognose - BRAUCHT Live-Web-Suche. DeepSeek hat keine; Kimi-Web-Suche (`$web_search`-builtin) - läuft nur über einen Tool-Loop, ist >2 min langsam und lief im Test in `finish=length` - (Web-Ergebnisse+Reasoning sprengen selbst 4000 Tokens → leerer Content). z.ai macht - es in EINEM sauberen `web_search`-Call → kein Wechsel (Kontext-Feature, kein Edge). - ⚠ **S/R-Kontext-Fix 2026-07-15:** `_tool_zones` füttert dem LLM jetzt die **echten - M5-Pivots** (`wave.pb_levels`, geclustert, nächste 3 je Seite) statt der stale - `[zones]`-Config — plus Prompt-Leitplanke „nutze NUR die übergebenen Level, erfinde - keine runden Marken". Reiner Kontext-Fix (keine Strategie): macht die Kommentar-Level - aktuell/konsistent zum Chart, macht die LLM-Einschätzung aber NICHT validierter - (Copilot bleibt unbelegte Verdict-Stimme, Gewicht nur bei Bias). - - **Tages-Prognose (`daily_levels.py`):** z.ai + Web-Suche, 1×/Tag 07:30 Berlin. - -## Workflows / Befehle -- **Änderungen aktivieren:** `restart_server.bat` (lädt core neu). ⚠ Der Batch startet - das Oil-Backend im **VORDERGRUND** (Zeile 40) — also **nicht** mit Output-Umleitung - aus PowerShell aufrufen (`& .\restart_server.bat *>&1 | …`): der Aufruf blockiert bis - zum Timeout und der abgeschnittene Tail zeigt ein irreführendes „===== Server beendet - =====", obwohl der Server läuft. - ⚠⚠ **NACHTRAG 2026-08-02: der Batch hat ZWEIMAL still NICHT neu gestartet** (auch als - `Start-Process cmd /c restart_server.bat -WindowStyle Minimized`). Der alte Prozess lief - weiter — und weil statische Dateien bei jedem Request frisch von der Platte gelesen - werden, **belegt eine korrekt ausgelieferte `app.js?v=N` GAR NICHTS** über den geladenen - Python-Code. Zuverlässig ist nur: Port-8000-Prozess killen und direkt starten - (`Start-Process python -ArgumentList '-X','utf8','server.py' -WorkingDirectory …`), - danach **an einem NEUEN Snapshot-Feld prüfen**, dass der Python-Code wirklich neu ist - (hier: `hl_live.basis_stale` fehlte → alter Code entlarvt). Zum Schluss verifizieren, - dass **genau eine** Instanz je Port läuft. Auto-Start: - Aufgabe `OilTradingServer` (Login, versteckt via VBS). - ⚠ **Startet seit 2026-07-30 BEIDE Server** (User-Vorgabe): erst das - **Hyperliquid-Dashboard** (Port 8001, versteckt via dessen `start_hidden.vbs`, - wartet max. 10 s aufs Lauschen), dann das Oil-Backend (8000) im Vordergrund. - **Grund + behobener Bug:** die Kill-Zeile trifft mit `*server.py*` BEIDE Prozesse — - vorher wurde der HL-Server bei jedem MT5-Neustart still mit-erschlagen und nie wieder - gestartet. Das riss die **Lücken in die Lead-Lag-/Order-Flow-Datensammlung** (72 h - Spanne, aber nur 2292 Samples) UND ließ die Liquiditätslinien im Chart verschwinden. - Fail-safe: fehlt das HL-Projekt (`..\HyperLiquid-WTI Trader`), startet nur 8000 mit - Hinweis. Auto-Start des HL-Servers separat: Aufgabe `HyperliquidDashboard` (Login). -- **⚠ MQL5-Indikator: das RICHTIGE Terminal treffen (Fallstrick, 2026-07-31).** - Unter `%APPDATA%\MetaQuotes\Terminal\` liegen **mehrere** Instanz-Ordner. Wer das - Ziel über „hat `MQL5\Indicators`" + `Select-Object -First 1` bestimmt, erwischt - leicht den falschen — real passiert: v1.30 und v1.31 landeten in - `0BA5CADA…`, während der Bot in `D0E8209F…` schreibt und der Chart dort läuft. - Folge: der Kegel und die Konsolidierungs-Beschriftung waren einen halben Tag lang - „gebaut und kompiliert", aber unsichtbar — der Chart lief weiter auf v1.29. - **Richtig:** das Ziel über die **CSV** bestimmen, die der Bot tatsächlich schreibt: - ```powershell - $term = Get-ChildItem "$env:APPDATA\MetaQuotes\Terminal" -Directory | - Where-Object { Test-Path "$($_.FullName)\MQL5\Files\sr_levels.csv" } | - Sort-Object { (Get-Item "$($_.FullName)\MQL5\Files\sr_levels.csv").LastWriteTime } -Descending | - Select-Object -First 1 - ``` - Gegenprobe nach jedem Kompilieren: `#property version` im Terminal-`.mq5` **und** - der Zeitstempel der `.ex5` müssen zum erwarteten Stand passen. -- **✅⚠ AUFFANG-ELEMENT statt Disziplin (Fix 2026-08-07, v=150) — die Regel - direkt darunter wird jetzt vom CODE durchgesetzt.** `$()` gibt bei fehlendem - Element nicht mehr `null` zurück, sondern ein **detachiertes ``**: - ```js - const _FEHLT = document.createElement("span"); - const $ = (id) => document.getElementById(id) || _FEHLT; // + 1× console.warn je ID - const $streng = (id) => document.getElementById(id); // gibt null - ``` - ⚠⚠ **Anlass ist eine Messung, kein Gefühl: die Regel „neue Elemente per - `const el = $(…); if (el) {…}`" war bei 0 von 95 direkten Zugriffen befolgt.** - 95 Stellen, an denen ein fehlendes Element den **GESAMTEN** Render abbricht; - 0 davon abgesichert. Eine Regel mit 0 % Befolgung ist keine Regel — deshalb - löst es der Code an EINER Stelle statt an 95. - ⚠ **`?.` war NICHT die Lösung** (mein erster Vorschlag, und er war falsch): - **80 der 95** Zugriffe sind ZUWEISUNGEN, und `$("x")?.y = v` ist ein - **Syntaxfehler** — Optional Chaining ist links vom `=` verboten. - ⚠⚠ **`$streng` ist der wichtige Teil.** Alle Eingabefelder sind - `type="number"`, und **leer heißt dort ausdrücklich „Stop entfernen"** - (`sl: null`). Mit dem Auffang-Element wäre `.value` `undefined` → `parseFloat` - → NaN → `JSON.stringify` macht daraus wieder **`null`** — eine fehlende - Eingabemaske hätte den **Broker-SL einer laufenden Position stillschweigend - gelöscht.** Ein lauter Absturz ist dort das bessere Verhalten. Betroffen war - genau **eine** Funktion (`sendSltp`); `setSrMin`/`setEmg`/`setManualMargin`/ - `setMarginPct` fangen NaN bereits ab (geprüft, nicht angenommen). - ⚠ Der Fehler wird **nicht verschluckt**, sondern **1× je ID** in die Konsole - gemeldet — ein stiller Fallback wäre derselbe Fehler wie der `catch {}` in - `pollSnapshot`, der Render-Fehler lautlos gefressen hat (behoben 01.08.). - ✅ **Beide Richtungen verifiziert** an einem Minimal-DOM, das eine alte - `index.html` simuliert: mit der alten `$`-Definition bricht der Render beim - **zweiten** Feld ab, mit der neuen läuft er durch und nur die fehlenden Felder - bleiben leer; der Order-Pfad bricht in beiden Fällen sauber ab. - ⚠ Die 40 vorhandenen `if (!el) return;`-Wächter feuern jetzt nicht mehr. Sie - bleiben stehen (kein Nutzen im Entfernen, und sie greifen weiter, wenn jemand - `$streng` benutzt) — beim Lesen aber mitdenken. -- **✅ G/V-LAG: 1-s-FALLBACK + EHRLICHER VERBINDUNGSPUNKT (2026-08-07, v=151).** - User: „G/V wird extrem lagged — 1 Sekunde". - ⚠⚠ **ZUERST GEMESSEN, DANN GEDREHT — und das Backend war NICHT das Problem:** - | Stufe | gemessen | - |---|---| - | `/api/snapshot` (75 Abrufe / 20 s) | **21 verschiedene `ts`** = ~1/s | - | **WebSocket-Push, direkt am Socket** | **Median 1,04 s** (min 1,00 · max 1,06) | - | G/V-Änderungen | Ø 1,44 s — das ist der **Markt**, keine Verzögerung | - **Die ganze Server-Kette liefert bereits 1 s.** Der Lag entsteht erst am Handy: - bricht der WebSocket **still** weg (WireGuard-Aussetzer, App-Wechsel), trägt nur - noch der REST-Poll — und der stand auf **4 s**. - ✅ **`POLL_MS` 4000 → 1000, aber MIT Sperre:** solange der WS liefert - (`lastWsMs`, Fenster `WS_FRISCH_MS`=2200 ms), überspringt der Poll den Abruf. - **Kein zusätzlicher Verkehr im Normalfall** (~14 KB/Abruf über WireGuard). - ⚠ **Denkfehler, den erst die Simulation zeigte:** der erste Entwurf las die - WS-Frische aus `lastRecvMs` (jede Quelle) — damit **drosselte sich der Poll - selbst** und kam bei totem WS nur auf **2,9 s** statt 1 s. Jetzt zwei getrennte - Uhren: `lastWsMs` (nur `sock.onmessage`) entscheidet über das Überspringen, - `lastRecvMs` (jede Quelle) über die Stale-Anzeige. - **Simuliert:** WS gesund → **0** Zusatz-Abrufe, max. Alter **0,9 s** · WS tot → - 1/s, max. Alter **0,9 s** · WS träge (5 s) → max. 2,9 s. Vorher bei totem WS: - 4,0 s. - ✅ **Der Verbindungspunkt log** — behoben. Er wurde **nur** bei `sock.onclose` - auf „off" gesetzt; genau der Fall, der den Lag erzeugt, feuert aber **kein** - `onclose`: der Socket hängt still, der Punkt blieb **grün**. Neu `.dot.stale` - (bernstein, pulsierend) ab **3,5 s** ohne frischen Snapshot — gemessen wird, - was ANKOMMT, nicht was der Socket über sich behauptet. Tooltip nennt das Alter. - ⚠ Kein Konflikt mit `render()`, das `className` komplett überschreibt: läuft - `render()`, ist gerade ein Snapshot angekommen und `stale` gehört ohnehin weg. - `.dot.stale` steht in `style.css` **nach** `.on`/`.off` — gleiche Spezifität, - die Reihenfolge entscheidet. -- **⚠ NEUE HTML-Elemente IMMER null-sicher ansprechen (Fix 2026-08-01).** - `index.html` und `app.js` werden vom Browser **unabhängig** gecacht. Trifft neue JS - auf eine alte, gecachte HTML, wirft ein direkter Zugriff (`$("neu").textContent = …`) - eine TypeError — und weil das mitten in `render()` passiert, bricht der **GESAMTE - Render** ab: das Dashboard friert ein und zeigt Altwerte, obwohl Server und Snapshot - korrekt sind. Real passiert beim HL-Kurs-Einbau (User: „ich sehe die Änderungen - nicht"); Server lieferte nachweislich v=129 samt `#hdr-hl` aus, `Cache-Control: - no-store` war gesetzt, kein Service-Worker — die Ursache lag allein in dieser - Kombination. **Regel:** neue Elemente per `const el = $("id"); if (el) { … }` - ansprechen, dann läuft der Rest weiter und nur das neue Feld fehlt, bis die HTML - nachgeladen ist. (Das `?v=N`-Bump bustet nur `app.js`/`style.css` — NICHT die - `index.html` selbst.) -- **Web-Assets:** Browser cacht hart → in `web/index.html` `?v=N` **hochzählen** - bei jeder JS/CSS-Änderung; Server sendet `Cache-Control: no-store`. Hängt der - Handy-Cache: Inkognito oder Seite mit frischem `?xN` öffnen. -- **Empfehlungs-Edge messen:** `python backtest_signal.py [TF] [N] [K]` - (ereignisbasiert, nutzt die echte `_build`-Logik). Ø-Edge>0 = trägt. -- **Ausführungs-Kalibrierung:** `python analyze_execution.py` — (A) echtes - Spread-Profil je Berlin-Stunde aus dem Bar-`spread`-Feld, (B) Slippage bei - SL-Closes, (C) MAE/MFE der Live-Trades. **Kern-Befund 2026-07-13:** Spread ist - absolut ~konstant (0,0225), aber relativ **Ø 0,265×ATR** — die alte - Kostenannahme 0,1×ATR war v. a. in Niedrig-Vola-Phasen ~2,5× zu optimistisch - (erklärt Live-PF < Backtest-Erwartung). Stunden 15–17 am günstigsten (~0,13), - **Nacht 0–7 = Kostenfalle (0,32–0,50×ATR)**. Slippage-Tail real: die −35/−54- - Desaster hatten 0,75 Slippage ÜBER den Initial-SL hinaus (harter SL deckelt in - schnellen Märkten nicht). MAE/MFE live: Exits validiert (nur 7 % der Verlierer - waren je ≥1,3×ATR im Plus → Breakeven-Schwelle passt; Gewinner-MAE Median 0,28). - ⚠ Künftige B3-Checks mit **stunden-echten Kosten** rechnen, nicht pauschal 0,1. - (WR/PF/Verhältnis/Worst je KW, Ausrichtungs-Split mit/gegen/ohne) gegen die - Backtest-Erwartung (WR~68 % · PF~1,3 · Verh~0,55) und flaggt Leckagen (Gegen-Signal- - Anteil, WR-Verfall, PF<1, Einzelverlust). **Wöchentlich** laufen lassen (B4/B5). -- **Vor „fertig":** immer `python -m py_compile `; JS via **`node --check`** - (Stufe B in `tools/check_nfalle.py`, läuft im pre-commit-Hook mit). - ⚠⚠ **KORREKTUR 2026-08-07: „kein node im Env" stimmte nicht mehr** — Node - **v24.18.0** ist installiert. Die Zeile hat die Frage „können wir Node nutzen?" - jahrelang falsch beantwortet. - **Gemessen, warum das zählt:** die alte Klammern-Balance findet **1 von 5** - realistischen Fehlern — `const x = ;`, doppeltes `let`/`const` und `return` - ausserhalb einer Funktion laufen glatt durch; `node --check` findet **5 von 5** - in **86 ms**. Ein Syntaxfehler in `app.js` bricht den **GESAMTEN** Render - (Dashboard friert auf Altwerten ein) — bei **95 direkt dereferenzierten** - `$("id")`-Zugriffen ist das kein theoretisches Risiko. - ⚠ **Geprüft wird als MODUL (`.mjs`), nicht als CommonJS:** als CJS hält Node ein - top-level `return` für legal, der Browser lädt `app.js` aber als klassisches - Script, wo es ein Syntaxfehler ist. Verifiziert, dass das echte `app.js` den - strengeren Modus **ohne Anpassung** besteht. - ⚠ **Kein npm, keine `package.json`, kein `node_modules`** — `node --check` ist - die Laufzeit selbst. Damit entsteht NICHT die Divergenz „Prüfumgebung ≠ - Laufumgebung" (die beim `.ps1`-BOM und beim `pre-commit`-Framework das Problem - war): Node und Chrome benutzen **denselben Parser (V8)**. - ✅ **Drei Richtungen verifiziert:** sauber → grün · mit eingebautem Fehler → - **Exit 1**, Commit gestoppt · **ohne Node im PATH** → Rückfall auf die alte - Prüfung, Exit 0 (eine frische Maschine darf am Hook nicht hängenbleiben). -- **✅ BOOT-LÜCKE GESCHLOSSEN: Auto-Logon AKTIV (2026-08-07).** - `OilTradingServer` und `HyperliquidDashboard` hängen an einem **Logon**-Trigger - — nach einem Update-Reboot läuft nichts bis zur Anmeldung (real 20.07.: - Reboot 01:30, Server bis 07:42 tot, −95,48 €). ⚠ Ein Task „ohne angemeldete - Sitzung" hilft **nicht**: der Bot braucht das laufende **MT5-Terminal**, und das - ist eine GUI-Anwendung — es braucht eine echte Desktop-Sitzung. Auto-Logon ist - hier also tatsächlich das richtige Mittel, nicht nur das bequeme. - **Vorbereitet:** `tools/autologon_einrichten.ps1` + Sysinternals `Autologon` - (Microsoft-signiert, Signatur geprüft; `tools/autologon/` ist **gitignored** wie - `tools/piper/`). - ⚠⚠ **Bewusst NICHT der Registry-Weg.** Der verbreitete Weg legt das Passwort als - `DefaultPassword` im **Klartext** unter `HKLM\…\Winlogon` ab — lesbar für jeden - lokalen Admin und in jedem Backup-Image. Sysinternals `Autologon` speichert es - als **verschlüsseltes LSA-Secret**. Das Skript prüft danach nach, dass - `DefaultPassword` NICHT im Klartext steht. - ⚠⚠ **Das eigentliche Restrisiko ist nicht das Passwort, sondern die offene - Sitzung:** nach dem Reboot steht ein angemeldeter Desktop mit MT5 und - Live-Broker-Verbindung da. Deshalb legt das Skript zusätzlich die Aufgabe - `OilAutoLogonLock` an — 30 s nach der Anmeldung `LockWorkStation`. Die Sitzung - **läuft** (MT5 und beide Server arbeiten), der Bildschirm ist gesperrt. - ⚠ **Muss ELEVATED ausgeführt werden** — `HKLM` ist aus einer normalen Sitzung - nicht schreibbar (getestet: „Requested registry access is not allowed"). Das - Passwort steht **nicht** im Skript und wird nirgends gespeichert; es wird im - Autologon-Dialog eingegeben. **Zurück:** `Autologon /accepteula` → Disable, und - `schtasks /Delete /TN OilAutoLogonLock /F`. - ⚠⚠ **ZWEI FEHLER IM ERSTEN VERSUCH (07.08., beide behoben) — beide von der - Sorte, die Erfolg meldet, wo keiner war:** - **(1)** `$ErrorActionPreference = 'Stop'` PLUS `schtasks /Delete` auf eine noch - nicht existierende Aufgabe → native Programme schreiben nach stderr, PowerShell - macht daraus unter `Stop` einen **Abbruch**. Das Skript endete, BEVOR `/Create` - lief; die Sperr-Aufgabe existierte nie. Jetzt wird vorher per `/Query` geprüft. - **(2)** Die Erfolgsmeldung **log**: ein fehlendes `DefaultPassword` wurde als - „LSA-Secret ✅" ausgegeben — dabei war schlicht **gar nichts** gesetzt (der - Autologon-Dialog war ohne *Enable* geschlossen worden). Jetzt ist - **`AutoAdminLogon = 1` die Bedingung**, und erst danach wird über die - Speicherart geurteilt; ohne aktives Auto-Logon bricht das Skript mit Code 1 ab - und überspringt Schritt 2. - ⚠⚠ **Der GUI-Weg führte zweimal zu nichts.** Diagnostiziert: der Dialog - **öffnet sich** (Fenstertitel „Autologon - Sysinternals", Prozess verifiziert) - — er muss aber mit **ENABLE** bestätigt werden; nur schließen tut nichts. - Deshalb gibt es jetzt **`-Direkt`**: Autologon 3.10 nimmt Benutzer/Domäne/ - Passwort als Argumente, das Skript fragt es per `Read-Host -AsSecureString` ab - (unsichtbar, nicht in der History) und ruft es ohne Dialog auf. - **➜ `powershell -ExecutionPolicy Bypass -File toolsutologon_einrichten.ps1 -Direkt`** - ⚠ Abwägung: bei `-Direkt` steht das Passwort für den Moment des Aufrufs in der - Kommandozeile des Kindprozesses (für Admins kurz sichtbar). Es landet **nicht** - in einer Datei, nicht in der Shell-History — und anschließend als - verschlüsseltes LSA-Secret, nicht im Klartext in der Registry. - ⚠ **Und ein Design-Fehler: die Sperre hätte bei JEDER Anmeldung gefeuert.** Ein - `ONLOGON`-Trigger unterscheidet nicht. Deshalb prüft `tools/autologon_lock.ps1` - jetzt die **Systemlaufzeit** — gesperrt wird nur, wenn der Rechner **< 3 min** - läuft (= Auto-Logon nach Boot). Nach manueller Anmeldung passiert nichts. - ⚠⚠ **ENDSTAND — der SICHERE Weg hat NICHT funktioniert.** Sysinternals - `Autologon` beendete sich mit **ExitCode 0**, schrieb aber **nichts** (weder - über den Dialog noch über die Argument-Form). Ausgeschlossen wurden: der - Windows-11-Blocker `DevicePasswordLessBuildVersion` (nicht gesetzt), fehlende - Elevation (Admin-Prüfung bestand), ein hängender Prozess. **Ursache ungeklärt.** - Aktiv ist deshalb der **Registry-Weg** — verifiziert: - | | | - |---|---| - | `AutoAdminLogon` | **1** | - | `DefaultUserName` / `DefaultDomainName` | `ah` / `HOMEBASE` | - | `DefaultPassword` | ⚠ **KLARTEXT** (Länge 4) | - | Aufgabe `OilAutoLogonLock` | **Bereit**, läuft als `ah`, ruft `autologon_lock.ps1` | - ⚠ Das Passwort ist damit für **jeden lokalen Administrator und jedes - Backup-Image** lesbar. Entfernen (deaktiviert zugleich den Auto-Logon): - `Remove-ItemProperty -Path 'HKLM:\…\Winlogon' -Name DefaultPassword`. - ⚠ **Offen:** die Wirkung ist erst beim **nächsten Reboot** belegt — bis dahin - ist nur die Konfiguration verifiziert, nicht das Verhalten. - ⚠⚠ **DRITTER Fall derselben Fehlerklasse in diesem einen Skript** (Erfolg - melden, wo keiner ist): die Speicherart wurde aus einer **stale** Variable - gelesen, die VOR dem Registry-Schreiben gefüllt war — die Ausgabe widersprach - sich direkt („steht jetzt im KLARTEXT" … zwei Zeilen später „nicht im Klartext - ✅"). Behoben: der Zustand wird nach jedem Schreibvorgang **frisch gelesen**. - Die drei Fälle zusammen sind die Lehre: **eine Erfolgsmeldung muss den Zustand - NACH der Aktion prüfen, nicht den davor.** -- **⚠⚠ .ps1 MIT NICHT-ASCII BRAUCHT EIN BOM — sonst bricht `powershell.exe` 5.1 - (2026-08-07, real passiert).** Das Autologon-Skript ließ sich nicht ausführen: - „Schließende ) fehlt", „Zeichenfolge hat kein Abschlusszeichen". Es war **kein** - Syntaxfehler: **Windows PowerShell 5.1 liest `.ps1` als CP1252, wenn kein BOM da - ist.** Aus dem Gedankenstrich `—` werden drei Zeichen (`â€"`), das enthaltene - `"` schließt den String vorzeitig, und es kaskadiert bis zum Dateiende. - ⚠⚠ **Warum es beim Schreiben nicht auffiel — und das ist der eigentliche Punkt:** - das hiesige PowerShell-Werkzeug ist **pwsh 7**, und das liest UTF-8 **ohne** BOM - klaglos. Die Datei war also in meiner Umgebung fehlerfrei und beim Nutzer kaputt. - Dieselbe Divergenz-Klasse wie der Deployment-Drift, nur eine Ebene tiefer: **die - Prüfumgebung war nicht die Laufumgebung.** - ✅ Behoben (UTF-8 **mit** BOM) und mit dem **echten 5.1-Parser** gegengeprüft - (`Parser::ParseFile`, 0 Fehler). Dabei ein **zweiter** Fall gefunden: - `scripts/send_daily_report.ps1` (Umlaute, kein BOM). - ✅ **Dauerhaft abgesichert:** `tools/check_nfalle.py` prüft es als Stufe **C2**. - ⚠ `.bat` ist NICHT betroffen (`cmd.exe` verzeiht das in `echo`/Kommentaren — - `restart_server.bat` enthält zwar `─`/`⚠`, bricht aber nicht). -- **✅ STUFE 5 DER PIPELINE IST JETZT EIN SKRIPT (`tools/deploy.py`, 2026-08-07).** - Ersetzt das von Hand zusammengesetzte „killen, starten, warten, nachsehen" und - macht die dokumentierte Schwachstelle prüfbar. - ``` - python tools/deploy.py --feld # Neustart + Beweis - python tools/deploy.py --nur-pruefen # nichts anfassen, nur Zustand - python tools/deploy.py --hl # HL-Dashboard (8001) mitnehmen - ``` - Fünf Schritte, **Rückgabecode 0 nur wenn alle durchlaufen** (verkettbar): - **(1)** Prozess **gezielt am Port** beenden — ⚠ NICHT per `*server.py*`-Muster - wie `restart_server.bat`, das trifft auch das HL-Dashboard auf 8001 (real: es - wurde bei jedem MT5-Neustart still mit-erschlagen und riss die Lücken in die - Datensammlung). **(2)** starten und warten, bis `/api/snapshot` **wirklich** - antwortet. **(3)** **genau EINE** Instanz je Port. **(4)** `--feld` prüfen. - **(5)** Log **ab der Startposition** auf ERROR/Traceback. - ⚠⚠ **Schritt 4 ist der Kern.** `restart_server.bat` hat zweimal still nicht neu - gestartet, und weil statische Dateien je Request frisch von der Platte gelesen - werden, **belegt eine korrekt ausgelieferte `app.js?v=N` GAR NICHTS** über den - geladenen Python-Code. Ein neues Snapshot-Feld ist der einzige belastbare - Beweis — genau daran fiel der Fehler 2026-08-02 auf (`hl_live.basis_stale`). - Ohne `--feld` sagt das Skript das ausdrücklich dazu. - ✅ **Beide Richtungen geprüft**: mit `--feld rec_track` → „Python-Code ist neu", - Exit 0; mit einem erfundenen Feld → „FEHLT — der alte Code läuft weiter!", - **Exit 1**. Eine Prüfung, die nicht scheitern kann, wäre wertlos. -- **⚠⚠ DER ENGPASS DES PROJEKTS WAR NIE DIE MATHEMATIK — er war eine - quadratische Schleife (Profil 2026-08-07, ~9 min → 8,9 s).** Anlass war die - Frage nach numpy. Statt zu vermuten wurde profiliert, und das Ergebnis war ein - anderes als erwartet: von 10,07 s Gesamtlaufzeit steckten **8,17 s in der - EIGENZEIT der Backtest-Schleife**, nur 0,44 s in `_build`. Ursache: - ```python - [C[j] for j in PH if i - _PIV_LOOK <= j <= i - _PIV_K] # je BAR, ganze Liste - ``` - Das ist **O(Bars × Pivots)** — bei 80k Bars hunderte Millionen Vergleiche, und - genau daher kamen die 9–20-Minuten-Läufe. PH/PL sind **sortiert**, also genügen - zwei Binärsuchen (`bisect_left`/`bisect_right`) für **dieselbe Menge** in - O(log n). - **Gemessen: `backtest_signal_touchfill.py` über 80k Bars von ~9 min auf 8,9 s** - (~60×), bei 8k Bars 10,07 s → 1,43 s. - ⚠⚠ **Die Migrations-Regel verlangt Bit-Gleichheit — und ein Vorher/Nachher- - Vergleich zweier LÄUFE taugt dafür NICHT**: `copy_rates_from_pos` ankert am - neuesten Bar, zwischenzeitlich eintreffende Live-Bars verschieben das Fenster - (real ein Trade Unterschied, s. 06.08.). Die Gleichheit wird deshalb **direkt - auf der Datenstruktur** bewiesen — `tests/test_pivotfenster.py` (4 Tests: - 4.000 Bars × 400 Pivots, Randfälle, leere Liste, beidseitig inklusive Grenzen). - ✅ Angewandt auf **7 Skripte** (`auto_signal_v3`, `breakout_gated`, - `dist_gated`, `htf_vola`, `metalabel`, `sl_width`, `trail_be`) — mechanisch - identische Transformation, alle kompilieren, Stichprobe gegengelaufen. - ⚠ **Lehre:** die naheliegende Antwort („numpy") war die falsche. Ein Profil - kostet zwei Minuten und hätte diese Bremse jederzeit gezeigt — sie lag seit - Monaten in jedem gegateten Backtest. -- **⚠⚠ PIPELINE-STUFE 1 IST JETZT AUTOMATISCH — `pre-commit`-Hook (2026-08-07).** - Nach einer Web-Recherche zum 2026er-Python-Standard umgesetzt, aber **gefiltert**: - von uv/Ruff/pyproject/CI ist hier nur das sinnvoll, was zu einer Einzelmaschine - mit GUI-gebundenem MT5 und Live-Geld passt. - **(1) Abhängigkeiten festgehalten** — `requirements.txt` (Laufzeit) und - `requirements-dev.txt` (Werkzeuge/Messung), **Versionen gepinnt**. ⚠ Bis dahin - gab es **gar keine** Liste; nach einem Maschinenausfall wäre die Rekonstruktion - Raten gewesen. Ermittelt per AST über `core/`, `server.py`, `tools/` — nicht - geraten. - **(2) `tools/pre_commit.py`** fährt vor jedem Commit `check_nfalle` + `pytest` - (~5 s). Einrichten: `python tools/pre_commit.py --install`. Notausgang - `git commit --no-verify` ist Absicht — eine Prüfung, die einen dringenden Fix - blockiert, wird sonst deinstalliert. - ⚠ **Bewusst OHNE das `pre-commit`-Framework**: dessen Nutzen ist Werkzeug- - Versionierung über mehrere Maschinen. Hier gibt es eine. Es würde stattdessen - eine ZWEITE ruff-Version in einer isolierten Umgebung installieren — genau die - Divergenz „Prüfumgebung ≠ Laufumgebung", die heute schon einmal zugeschlagen hat. - **(3) Ruff statt pyflakes** (`ruff check --select F821`) — gleiche Befunde, - ganzer Scan in 2,1 s. ⚠ **Kein `ruff format`**: das würde 161 Dateien - umformatieren, darunter alle Backtests → Migrations-Regel verletzt. - ⚠⚠ **VIERTER Fall der Fehlerklasse „Erfolg melden, wo nichts geprüft wurde" — - und diesmal fiel er nur durch einen Test auf.** `--select F821,E999` lässt ruff - 0.16 **komplett abbrechen** („Rule E999 was removed", Exit 2); die Meldung geht - nach stderr, und weil ich den **Rückgabecode nicht prüfte**, meldete die Stufe - „keine undefinierten Namen" — obwohl sie **gar nichts** geprüft hatte. Ein - absichtlich eingebauter `F821`-Fehler kam **glatt durch den Hook**. Behoben: - nur `F821`, und **Exit 2 = Werkzeugfehler** löst jetzt den pyflakes-Rückfall aus - statt als „sauber" zu gelten. - ✅ **Beide Richtungen verifiziert** (wie beim Deploy-Skript): mit eingebautem - Fehler → Hook **stoppt** den Commit; ohne → grün. Der Test-Commit wurde - zurückgenommen (war nur lokal). - ⚠ **Nicht umgesetzt und warum:** *Gitea Actions* — der Runner liefe auf - derselben Maschine wie der Live-Bot und konkurrierte um CPU, bei 5 s Prüfzeit - ohne Mehrwert gegenüber dem Hook. *Container/Blue-Green* — unmöglich, MT5 - braucht eine Desktop-Sitzung, und es gibt EINE Broker-Verbindung. - *mypy/ty* — kaum Annotationen vorhanden, grosser Aufwand für wenig Ertrag. - ⚠ **Struktureller Fund dabei:** `core/engine.py:3527` importiert - **`backtest_breakout_squeeze`** (ebenso `weekly_review.py`). Ein Backtest ist - damit Teil des **Live**-Abhängigkeitsgraphen — „Backtests sind nur Doku" stimmt - nicht, ein Fehler dort kann den laufenden Bot treffen. -- **⚠⚠ TEST-SUITE (`pytest`, `tests/`, seit 2026-08-07) — 17 Tests, ~1 s.** - Im Projekt sind über Monate **dutzende Szenario-Tests** entstanden („mit 10 - Szenarien getestet", „mit 18 synthetischen Szenarien"), alle als - Wegwerf-Skripte. **Keiner davon lief je ein zweites Mal.** Bei einem System, das - man vor jeder Änderung neu starten und live verifizieren muss, ist das der - teuerste Teil der Schleife. - Aufruf: **`python -m pytest`** (Konfiguration in `pytest.ini`). - Erfasst sind zunächst die beiden jüngsten Pfade: **`_check_pending_fill`** - (6 Fälle, u. a. „fremde Position NICHT taggen" und „derselbe Fill zählt nur - einmal") und die **Selbst-Kalibrierung** (7 Fälle: Episoden-Dedup, WARTEN - re-armt, Auswertung in beide Richtungen inkl. Broker-Offset, fehlende Bar bleibt - offen, Aggregation). - ⚠ **Drei harte Regeln in `tests/conftest.py`**, damit die Suite jederzeit - gefahrlos läuft: **keine Live-DB** (jeder Test bekommt eine frische Temp-Datei), - **kein MT5, kein laufender Server** (alles Broker-Nahe gestubbt), **kein Netz**. - ⚠ Das Schema wird über die **echten** Konstruktoren angelegt — `HistoryLogger` - UND `CandleLogger` (`candles_m1` gehört nicht zum History-Schema). Ein von Hand - nachgebautes Test-Schema würde irgendwann vom Produktivstand abweichen. - ⚠ **Warum die Temp-DB und nicht eine Kopie der Live-DB:** am 06.08. haben ECHTE - `candles_m1`-Zeilen in einem vermeintlich leeren Fenster einen Test verfälscht — - zweimal hintereinander sah es nach einem Code-Fehler aus, es waren Testfehler. - ✅ **Mutations-geprüft** (eine Suite, die immer grün ist, ist wertlos): eine - absichtlich eingebaute „fremde Position wird doch getaggt"-Regression wird - gefangen. ⚠ Nebenbefund dabei: der Fill-Dedup ist **doppelt** gesichert - (`_pending_tagged` UND der `pop` aus `_pending_tickets`) — nur eines von beiden - zu entfernen fällt nicht auf, beides zusammen bricht sofort zwei Tests. Das ist - gewollte Redundanz, kein ungetesteter Zweig. -- **⚠⚠ KONFIDENZINTERVALLE statt Bauchgefühl (`backtest_cost_gate.ci95`, seit - 2026-08-07).** Das Projekt entschied bis dahin über **Punktschätzungen** plus die - 2-Stichproben-Regel; ob ein ØR von +0,12 bei n=30 überhaupt von null zu - unterscheiden war, musste jedes Mal im Kopf abgeschätzt werden. `kennzahlen()` - liefert jetzt zusätzlich **`ci`** = 95-%-Bootstrap-Intervall des Mittelwerts - (Perzentil, 2000 Resamples, `random_state` fest → reproduzierbar; ab n≥20, sonst - `None`). ⚠ **Rein additiv** — alle 11 Aufrufer lesen weiter nur `n/wr/oer/sr/pf`, - keine dokumentierte Zahl verschiebt sich. - ✅ **Sofortiger Ertrag:** der erste Lauf hat das Ergebnis vom 06.08. korrigiert - (s. „Verworfene Setups auf der Ausführungs-Achse" — aus „3 von 7 tragen" wird - „1 gesichert, 2 unklar"). - ⚠⚠ **GRENZE, die man kennen muss:** der Bootstrap unterstellt **unabhängige** - Ziehungen. Für die sequentielle Sim mit EINEM Slot stimmt das näherungsweise - (jeder Trade beginnt nach dem Exit des vorigen). Für **überlappende** - Beobachtungen ist er **zu optimistisch** — stündliche Werte mit 24-h-Horizont - teilen 23 von 24 Stunden, das effektive n ist ~n/24. Genau daran ist - `analyze_hl_funding.py` am 06.08. aufgelaufen (dort wird **entüberlappt** statt - gebootstrappt). Für solche Fälle bräuchte es einen **Block-Bootstrap** — - `ci95` ist dafür NICHT gedacht. -- **⚠⚠ DIE `\n`-FALLE — und der Scan dagegen (`tools/check_nfalle.py`, 2026-08-07).** - Wird Code über ein Shell-**Heredoc** geschrieben, macht die Shell aus einem - gemeinten `\n` einen **echten Zeilenumbruch mitten im String-Literal**. Real an - EINEM Tag **dreimal** passiert: Telegram-Text in `engine.py`, Reminder-Eintrag in - `measurement_reminder.py`, und beim Dokumentieren dieser Regel selbst (dort - zusätzlich an den deutschen Anführungszeichen). - **➜ Regel: mehrzeilige Strings mit `\n` oder Text mit „ " NIE per Heredoc - schreiben — den Edit-Weg nehmen.** - Der Scan prüft in BEIDE Richtungen: **(A)** kompiliert alle `.py` · **(B)** sucht - in JS unabgeschlossene String-Literale · **(C)** den UMGEKEHRTEN Fall, literales - `\n` als sichtbarer Text in HTML-`title=` und Markdown · **(D)** die Signatur der - Fälle (Zeile endet offen, nächste besteht nur aus einem Quote). - ⚠ **Fehlalarm-Falle im Scanner selbst, behoben:** Quotes zu ZÄHLEN meldet `'"'` - als offenes Literal (gültiges JS mit einem Anführungszeichen darin — real in - `app.js`). Der Scan verfolgt jetzt zeichenweise den AKTIVEN Quote-Typ. - Erster vollständiger Lauf: **161 Python-Dateien, `app.js`, alle HTML/MD sauber.** -- **⚠⚠ LINTER: `pyflakes` (seit 2026-08-07 installiert, Stufe A2 in - `tools/check_nfalle.py`) — und er hat beim ERSTEN Lauf zwei echte Fehler - gefunden.** `py_compile` prüft **nur die Syntax**; ein Tippfehler in einem - **Namen** ist syntaktisch einwandfrei. Dieses Projekt ist dafür besonders - anfällig, weil es sehr viele breite `except Exception`-Blöcke hat (bewusst, als - Fail-safe) — ein `NameError` darin ist **unsichtbar**: keine Meldung, kein - Logeintrag, nur ein Zweig, der nichts tut. Genau das war am 04.08. schon einmal - fast passiert (`wave_snap`), gefunden damals nur durch Lesen. - **Die beiden Funde in `core/trader.py`** — beide nutzten `log.`, den Namen gibt - es dort NICHT (nur `log_trade`/`log_hist`): - · **`_broker_offset_s()`** (seit dem Fix vom 01.08. drin): der `NameError` lief in - das `except Exception: pass` **und nahm die darunter stehende Zuweisung - `self._boff = int(off)` mit** → ein echter **Broker-Zeitzonen-Wechsel wäre nie - übernommen worden**, dauerhaft und lautlos. Ausgerechnet der Zweig, der als - Deployment-Drift **Fall 4** gebaut wurde (falscher Offset → `open_time` in der - Zukunft, **Time-Stop-Alter negativ**). - · **`modify_sltp()`** (seit dem **Initial-Commit**): steht **nicht** in einem - `try` → der Fehler lief bis in `/api/sltp` durch. Folge: der Broker hatte SL/TP - **bereits geändert**, der User bekam trotzdem eine Fehlermeldung, und - `set_sltp` kam nie bis `trail.deactivate()` — **das Trailing blieb an** und hätte - die Handeingabe beim nächsten Tick zurückgezogen, also genau das, was die - User-Vorgabe verhindern soll. Aufgefangen hat das nur die separate - Extern-Erkennung in `trailing._do_modify`, einen Tick später. - ✅ Beide behoben (`log_trade`), dazu die Annotation `'HistoryLogger | None'` über - `if TYPE_CHECKING` sauber importiert. **Projektweit jetzt 0 undefinierte Namen.** - ⚠ **Schweregrad-Trennung ist Absicht:** gemeldet werden nur `undefined name` / - `syntax error`; die ~105 kosmetischen Hinweise (ungenutzte Importe, f-Strings - ohne Platzhalter) werden unterdrückt. Auch `redefinition of unused` steht bewusst - NICHT in der Liste — es trifft das legitime Muster `hook = None` + bedingtes - `def hook` (geprüft in zwei Backtests, beide korrekt). **Eine Prüfung mit - Dauer-Treffern wird ignoriert, und mit ihr die eine echte Meldung.** - ⚠ Backtest-Dateien wurden **nicht** angefasst — eine Änderung dort könnte ihre - dokumentierten Zahlen verschieben. -- **Konsole:** UTF-8 erzwingen (`PYTHONIOENCODING=utf-8`, `-X utf8`). -- **Uvicorn-Access-Log AUS** (`access_log=False` in `uvicorn.run`, 2026-07-22): seit - dem 4-s-REST-Polling + WS-Push flutete `GET /api/snapshot … 200 OK` die Konsole. - Betrifft NUR Uvicorns eigenes Zugriffs-Log, nicht die `oil.*`-Logger (eigene - Hierarchie, `core/logger.py`, unverändert in Konsole + Datei). - -## Empfehlungs- & Trade-Logik (Stand aktuell) -- **Richtung = EMA12 vs EMA50** (`wave_rec.py`), NICHT mehr ZigZag-Momentum - (das hatte ~0 Edge). Totband (Chop→WARTEN), Anti-Überdehnung - (`_STRETCH_MAX=3,5×ATR`), +S/R-Konfidenz, +Session-Konfidenz. (TU **entfernt** — - s. u.) -- **✅ ABER SIE TAUGT ALS VETO — und DAS hält der 2-Stichproben-Regel stand - (2026-08-06, Ausrichtungs-Split über die GANZE Historie, 1.216 geschlossene - Trades).** Bisher stand dazu nur `alignment_stats(40)` = die letzten 40 Trades - im Order-Dialog. Über alles, je Lot normiert (Positionsgrößen schwanken stark, - s. die Lehre vom 04.08.): - | | n | WR | ΣP&L | **je Lot** | H1 / H2 je Lot | - |---|---|---|---|---|---| - | **MIT** Signal | 354 | 56,5 % | +555,66 € | **+3,22** | +0,37 / +5,62 | - | **GEGEN** Signal | 87 | **40,2 %** | −184,08 € | **−6,60** | **−7,01 / −5,68** | - | OHNE Signal (WARTEN) | 775 | 56,6 % | +186,17 € | +0,36 | −2,68 / +0,76 | - ⚠⚠ **Die schärfste Zeile ist der Vergleich MIT gegen OHNE: 56,5 % zu 56,6 % — - identisch.** Der Empfehlung zu FOLGEN bringt gegenüber „ohne Empfehlung handeln" - gemessen **nichts**. Nur sie zu VERLETZEN kostet, und das **robust in beiden - Hälften** (−7,01 / −5,68 je Lot; die einzige Richtungsaussage über die - Gesamtempfehlung, die den 2-Stichproben-Test besteht). - ✅ **Damit ist die Rolle präzise: die Gesamtempfehlung ist ein VETO, keine - Prognose.** Sie grenzt ein, was man nicht tun sollte; die Auswahl innerhalb des - Erlaubten leistet der Mensch (dessen Vorteil separat belegt ist). - ⚠ Grenzen: `gegen` hat nur n=87 (73/14), und die Zuordnung ist konfundiert — - der Mensch wählt ja aus, wann er gegen das Signal geht. Die Richtung des Effekts - ist über beide Hälften konsistent und groß (~10 € je Lot Unterschied), die - Punktschätzung ist es nicht. -- **⚠⚠ DIE RICHTUNGS-AUSSAGE DER LIVE-EMPFEHLUNG IST EIN MÜNZWURF — an den LIVE - ausgegebenen Zeilen gemessen (`analyze_signal_live.py`, 2026-08-06, User-Frage - „die Empfehlungen heute waren fast immer gegenläufig, sollten wir das Konzept - überdenken?").** Kein Backtest: ausgewertet wurden die **tatsächlich - ausgegebenen** `recommendations`-Zeilen (also nach ALLEN Gates) gegen den echten - Kursverlauf. 20.365 Zeilen → **1.071 Episoden** (08.05.–06.08.; die Rohzeilen - zählen dieselbe Marktlage dutzendfach, deshalb Episoden), gegen die **Drift** - der jeweiligen Hälfte gelesen. - | Horizont | H1 Überschuss / Treffer | H2 Überschuss / Treffer | - |---|---|---| - | 30 min | +0,079×ATR / 49,7 % | +0,003×ATR / 51,0 % | - | 60 min | +0,085×ATR / 50,7 % | +0,034×ATR / 51,1 % | - | 120 min | +0,002×ATR / 50,5 % | **−0,259×ATR** / 48,3 % | - **Die Trefferquote liegt über alle sechs Felder zwischen 48,3 und 51,1 %.** Der - Überschuss über die Drift ist im besten Fall +0,085×ATR — das liegt **unter der - B3-Kostenhürde (0,1×ATR)** und weit unter den realen M5-Kosten (0,265×ATR). Als - **Richtungsprognose bezahlt das Signal nicht einmal seinen eigenen Spread.** - ✅ **Der Tag, um den es ging, war kein Ausreißer, sondern typisch:** 11 gerichtete - Episoden, auf 30 min **8 von 10 gegenläufig**. Die fünf Vormittags-SHORTs liefen - gegen einen Tag, der von 75,5 auf 78 stieg (11:54 SHORT: −5,09×ATR auf 120 min). - Das ist die nachlaufende EMA im Trendtag — sie shortet die Rücksetzer. - ⚠⚠ **DAS HEISST NICHT, DASS DAS SYSTEM WERTLOS IST — es verdient nur nicht mit - der Richtung.** Dasselbe Signal ist mit **ruhender Order am Bestätigungs-Level** - in BEIDEN Hälften klar positiv (`backtest_auto_signal_v3.py`: ØR +0,35/+0,32, - PF 2,2), am Markt dagegen negativ (−0,09/−0,11). Der Ertrag kommt aus - **Einstiegspreis und Exit-Asymmetrie**, nicht aus der Trefferquote — genau wie - beim Squeeze (43–46 % Treffer, trotzdem positiv, weil die Gewinner laufen). - ✅ **Konsequenz für die Lesart:** die Headline ist **kein Kursurteil**, sondern - ein **Auslöser, an einem Level eine Order zu platzieren**. Der Konfidenz-Ring - darf erst recht nicht als P(Erfolg) gelesen werden (unabhängig belegt: - `analyze_verdict_calibration.py`, in H2 sogar invertiert). - ⚠ **Was daraus NICHT folgt:** die Suche nach einem besseren Richtungssignal. - Die ist mit **23 verworfenen Eingriffen** über jede orthogonale Indikatorklasse - hinweg beantwortet (Momentum · Inter-Market · Volume Profile · SMC/Liquidity · - Chartmuster · Fibonacci · Kerzen · Marktstruktur · Meta-Labeling). -- **Volatilitäts-Squeeze-Breakout = ADDITIVER SETUP, UMGESETZT (User-Idee - „Breakouterkennung" 2026-07-14, gemessen `backtest_breakout_squeeze.py`):** - struktureller Ausbruch aus einer **Kompression** (Box = Spanne der letzten `_SQ_N`=10 - abgeschlossenen M5-Bars ≤ `_SQ_MULT`=2,5×ATR) um `_SQ_K`=0,1×ATR — NICHT der - EMA-Trend-Breakout. **2-Stichproben-positiv** (ØR **+0,227/+0,141** netto Echtkosten, - PF 1,35/1,20, 173/413 Trades; schlägt den Ausbruch aus *beliebiger* Box −0,045/+0,161 - in BEIDEN Hälften) & **robust über Box 10–20 / k 0,1–0,2** (alle 8 Kombis positiv, - kein Parameter-Zufall). Andere Signalklasse als die Welle → komplementär. **Umsetzung:** - `wave._squeeze_one(highs,lows,closes,atr)` (M5). ⚡ **Erkennungs-Latenz ~5 s (Variante A, - 2026-07-21):** der Squeeze läuft auf einem EIGENEN, schnellen 60-Bar-M5-Fetch - (`_sq_due`/`_SQ_REFRESH_S=5.0`, entkoppelt von der 15-s-TF-Ampel-Drossel) → Ausbruch wird - ~15 s→5 s schneller erkannt, dann ~1 s Handeln (`_pos_loop`). Gleiche validierte M5-Regel, - reine Infra (kein Backtest nötig). **Variante B (Squeeze auf M1) = OFFEN/UNBELEGT** — erst - backtesten, wenn der M1-Logger (`candles_m1`, seit 2026-07-21) ab ~2027-01 genug History hat. -- **⚠⚠⚠ VERWORFENE SETUPS AUF DER AUSFÜHRUNGS-ACHSE NEU GEMESSEN — und das - Ergebnis stellt den LIVE-Squeeze mit in Frage (`backtest_verworfene_v2.py`, - 2026-08-06, User: „backteste alle verworfenen Signale").** - ⚠ Bewusst **kein** erneuter Komplettlauf — der lief am 04.08. (14 Klassen, alle - blieben verworfen). Neu ist genau **eine** Achse, der Einstiegspreis, und sie - wurde mit der heutigen Korrektur angewandt: **Fill bei BERÜHRUNG**, nicht bei - Schluss-Bruch. Geprüft wurde nur, wo die Achse überhaupt greifen kann — - **stehendes Level + Ausbruchsrichtung** (Momentum scheitert, weil sein Level - wandert; Reversions-Einstiege werden von einer ruhenden Order sogar schlechter, - `backtest_candle_fade2.py`). - | Setup | A) Markt | B) Close-Bruch | **C) Berührung (real)** | n | - |---|---|---|---|---| - | *KONTROLLE Squeeze* | −0,180/−0,017 | +0,560/+0,755 | **+0,133/+0,293** | 1094 | - | ORB EU 15/30 · US 15/30 | negativ | +0,45…+0,73 | **alle 4 ❌** | ~200 | - | PDH / PDL Bruch | −0,280/+0,044 | +0,906/+1,368 | **+0,246/+0,464** | 147 | - | Asian Range Bruch | +0,021/−0,116 | +0,928/+0,718 | **+0,142/+0,134** | 218 | - | Equal H/L Bruch | −0,279/+0,228 | +0,574/+0,905 | **+0,218/+0,170** | 168 | - ✅ Die Kontrolle reproduziert `backtest_squeeze_touchfill.py` (+0,133/+0,293 - gegen +0,124/+0,292) — Pipeline in Ordnung. ✅ **ORB bleibt verworfen** in allen - vier Varianten (bestätigt 2026-07-17). ✅ Die drei Überlebenden sind - **nachbar-robust** (PDH/PDL über die Gültigkeitsdauer, Asian Range über das - Fenster, Equal H/L über Toleranz und Gültigkeit — 11 Zellen, keine kippt). - ⚠⚠ **NACHTRAG 2026-08-07 — mit 95-%-Konfidenzintervallen relativiert sich das - „3 von 7" deutlich.** Seit `backtest_cost_gate.ci95` (Bootstrap) hängt an jeder - ØR-Zahl ihr Intervall. Auf der entscheidenden Spalte C: - | Setup | H1 95-%-KI | H2 95-%-KI | - |---|---|---| - | **KONTROLLE Squeeze** | **[+0,037 … +0,229]** | **[+0,225 … +0,366]** | - | PDH / PDL | [−0,032 … +0,544] ⚠ enthält 0 | [+0,155 … +0,764] | - | Equal H/L | [+0,001 … +0,425] (haarscharf) | [−0,035 … +0,356] ⚠ enthält 0 | - | **Asian Range** | [−0,067 … +0,357] ⚠ | [−0,060 … +0,332] ⚠ | - **Nur der Squeeze hat in BEIDEN Hälften ein Intervall klar über null.** Bei - Asian Range enthalten **beide** die Null — als „Überlebender" war es zu stark - formuliert. - ⚠ **Zwei verschiedene Fragen, beide nötig:** die 2-Stichproben-Regel prüft, ob - das **Vorzeichen über Regime hinweg stabil** ist; das KI prüft, ob eine einzelne - Schätzung **überhaupt von Rauschen zu unterscheiden** ist. Hier war Ersteres - erfüllt und Letzteres nicht. Und: zwei Hälften, die beide in dieselbe Richtung - zeigen, sind zusammen mehr Evidenz als jede für sich — „KI enthält 0" heißt - also **nicht widerlegt**, sondern **nicht belegt**. - ✅ An der Schlussfolgerung ändert das nichts (es wurde ohnehin nichts gebaut), - aber die Formulierung „3 von 7 tragen" ist auf **„1 gesichert, 2 unklar"** zu - korrigieren. - ⚠⚠ **Das sind KEINE drei neuen Signale.** PDH/PDL, Asian-Range-Extrem, Equal - Highs/Lows und die Squeeze-Box sind **dasselbe**: ein waagerechtes Level aus - früheren Kursextremen, gebrochen, mit ruhender Stop-Order betreten und mit dem - kanonischen Trailing verlassen. Deckt sich mit der Multi-Instrument-Kontrolle - („beliebige Box" ist überall positiv, die Kompression trägt +0,009…+0,155). - **Der Edge liegt in der Kombination Ausführung + Exit, nicht in der Regel, die - das Level auswählt.** - ⚠⚠⚠ **UND JETZT DER TEIL, DER AUCH DAS LAUFENDE SYSTEM BETRIFFT — mit der - gemessenen Exit-Slippage (`analyze_slippage_holdtime.py`) bleibt fast nichts - übrig:** - | Setup | 0 $ | **0,0124 $** (Tagfenster) | 0,0370 $ (alle Std) | - |---|---|---|---| - | **Squeeze (LIVE!)** | +0,133/+0,293 | **−0,002/+0,221** | −0,270/+0,080 | - | PDH / PDL | +0,246/+0,464 | **+0,099/+0,386** | −0,192/+0,231 | - | Equal H/L | +0,218/+0,170 | +0,077/+0,092 | −0,202/−0,062 | - | Asian Range | +0,142/+0,134 | −0,011/+0,065 | −0,316/−0,071 | - **Die Edges dieser ganzen Familie liegen in derselben Größenordnung wie die - Exit-Slippage.** Beim günstigsten gemessenen Wert hält nur **PDH/PDL** beide - Hälften klar; Equal H/L ist dünn, Asian Range fällt — und der **live laufende - Auto-Squeeze steht in H1 bei −0,002, also auf null**. - ⚠ Die Slippage ist dabei eine **Untergrenze** (s. Herleitung dort) — der wahre - Abzug ist größer, nicht kleiner. - ✅ **Konsequenz: KEIN neues Setup bauen.** Drei weitere Module derselben Klasse - addieren Frequenz zu einem Edge, der von den Ausstiegskosten aufgezehrt wird. - Der nächste Hebel ist **die Ausführungsqualität am EXIT** (dort ist der Posten, - der alles frisst), nicht ein weiteres Einstiegs-Signal. - ⚠ **Nicht mitgemessen, ehrlich benannt:** (a) **Volume-Profile-LVN** — stehendes - Level, aber das Profil müsste je Bar neu gebinnt werden (Laufzeit); (b) - **Chartmuster-/Doppeltop-Nackenlinie** — stehendes Level und Ausbruchsrichtung, - qualifiziert sich also, braucht aber den Muster-Detektor kausal getrieben. - **Beide bleiben offen.** Alle übrigen Ablehnungen (Inter-Market, Chop-Gate, - Meta-Labeling, RSI/MACD/ADX, HTF-Winkel, H1-Stärke, Konfluenz-Strafe) haben gar - kein Level — die Achse kann dort nichts ändern. -- **⚠⚠ DER SQUEEZE TRÄGT AUCH AUF ANDEREN INSTRUMENTEN — 7 von 9 (gemessen - `backtest_squeeze_multi.py`, 2026-08-06).** Anlass: `analyze_signal_live.py` hat - gezeigt, dass die Richtung ein Münzwurf ist; das Grundproblem ist also nicht die - Qualität des Squeeze, sondern seine **Frequenz** (2–3×/Woche auf einem - Instrument). Auf dieses Problem hat das Projekt bisher zweimal reagiert — mehr - Signale auf demselben Instrument (23 Fehlschläge) oder feinere Zeitebene (Kosten - erledigen es). Die dritte Achse war unberührt: **dasselbe Signal, mehr Märkte.** - | Symbol | Spr/ATR | H1 ØR / PF | H2 ØR / PF | | - |---|---|---|---|---| - | **GER40** | 0,086 | +0,668 / 4,23 | +0,595 / 3,82 | ✅ | - | **SpotBrent** | 0,184 | +0,503 / 2,90 | +0,653 / 3,85 | ✅ | - | **XAUUSD** | 0,028 | +0,559 / 3,74 | +0,556 / 3,95 | ✅ | - | **NAS100** | 0,072 | +0,552 / 3,32 | +0,481 / 2,94 | ✅ | - | SpotCrude *(Kontrolle)* | 0,181 | +0,460 / 2,67 | +0,605 / 3,51 | ✅ | - | XAGUSD | 0,261 | +0,395 / 2,19 | +0,379 / 2,30 | ✅ | - | Copper | 0,327 | +0,300 / 1,83 | +0,375 / 2,11 | ✅ | - | NatGas | 0,651 | +0,229 / 1,52 | +0,240 / 1,56 | ✅ | - | **Gasoline** | **0,972** | −0,610 / 0,35 | −0,148 / 0,76 | ❌ | - | **USDX** | **1,972** | −1,269 / 0,13 | −1,317 / 0,14 | ❌ | - ✅ **Die Kontrolle reproduziert** (SpotCrude +0,460/+0,605 gegen bekannt - +0,456/+0,611) — die Pipeline ist in Ordnung. ✅ **Alle 7 sind nachbar-robust** - (Box 10/15 × k 0,1/0,2, 28 Zellen, keine kippt) und halten Einstiegs-Slippage bis - +0,20×ATR. - ✅✅ **Die beiden Durchfaller sind der beste Beleg, dass es kein Zufall ist:** es - sind exakt die zwei mit **Spread ≥ ATR** — dieselbe Kosten-Arithmetik, die auch - M1 erledigt hat. Der Test scheitert dort, wo er scheitern MUSS. - ⚠ **ATR-Floor relativ gesetzt** (0,542 × Median-ATR statt der WTI-Dollar-Zahl - 0,06) — ein absoluter Floor wäre auf Gold wirkungslos und auf NatGas ein - Selektions-Artefakt (Lehre aus `analyze_tageszeit.py`). - ⚠⚠ **DREI Vorbehalte, die vor einem Bau geklärt gehören:** - (1) **„7 von 9" sind NICHT 7 unabhängige Tests.** Crude/Brent korrelieren ~0,9, - Gold/Silber und NAS100/GER40 ebenso — effektiv sind es eher 3–4 unabhängige - Fälle, und alle im **selben Makro-Zeitraum**. Mehrere Slots gleichzeitig wären - dann dasselbe Risiko doppelt, keine Diversifikation. - (2) **Die Kompression trägt weniger als der Ausbruch selbst.** Die Kontrolle - „beliebige Box" ist überall ebenfalls positiv (+0,144…+0,533); der Beitrag der - Kompression liegt bei **+0,009…+0,155** und ist bei XAGUSD (+0,009/+0,040) und - NAS100 (+0,034/+0,013) kaum vorhanden. Gemessen wird hier also zu einem großen - Teil **„Ausbruch + unser Exit-Stack"**, nicht der Squeeze im engeren Sinn. - (3) **Swap/Kommission je Symbol, Handelszeiten und Mindest-Lots sind NICHT - modelliert** — nur der Bar-Spread. - ⚠ Und der Fill-Befund direkt darunter gilt hier genauso: die Zahlen stehen in - Variante (a), real ist (b) — also rund **0,3 R niedriger** anzusetzen. -- **⚠⚠⚠ FILL-ANNAHME: „am Level einsteigen" hat ZWEI Bedeutungen — und die - teurere ist die richtige (gemessen `backtest_squeeze_touchfill.py` + - `backtest_momentum_v2.py`, 2026-08-06).** Aufgefallen beim Momentum-Nachtest. - | | | - |---|---| - | **(a) Projekt-Standard** | einsteigen, WO das Signal ausgelöst hat — also dort, wo der **Bar-CLOSE** das Level gebrochen hat, zum Level-Preis | - | **(b) Realität** | einsteigen, WANN die liegende Order gefüllt hätte — bei der **ersten Berührung**, egal wo der Bar schließt | - **Live gilt (b).** (a) konditioniert auf den Bar-Schluss, den eine ruhende Order - nicht kennen kann — das ist **Look-ahead**, kein Modellierungsdetail. - **Gemessen auf dem Squeeze (80k M5, 2 Halbjahre, kanonischer Exit, Echtkosten):** - | Fill-Annahme | H1 ØR / PF / n | H2 ØR / PF / n | - |---|---|---| - | (a) Close bricht Level | +0,461 / 2,68 / 801 | +0,605 / 3,51 / 1280 | - | **(b) Berührung (LIVE)** | **+0,124 / 1,24 / 1092** | **+0,292 / 1,64 / 1807** | - **Aufschlag +0,337 (H1) / +0,312 (H2) R je Trade** — und **291 bzw. 527 - zusätzliche Fills**, im Schnitt Verlierer (Dochte, die wieder in die Box - zurückfallen; passt zu „54 % aller Box-Ausbrüche kehren binnen 3 Bars zurück", - `backtest_consolidation.py`). - ✅ **Der Squeeze hält trotzdem** — beidhälftig positiv, PF > 1. Und die - Umstellung auf ruhende Orders bleibt richtig: (b) +0,124/+0,292 gegen - **Market −0,158/−0,006**. Bemerkenswert: (b) trifft fast genau das ORIGINAL - validierte Band **+0,14…+0,23** aus `backtest_breakout_squeeze.py` — der - B4-Maßstab war also die ganze Zeit der realistischere. - ⚠⚠ **KONSEQUENZ: die heute mehrfach zitierten +0,456/+0,611 sind als - Live-Erwartung ~0,32 R zu hoch.** Sie bleiben als Vergleich zwischen Market und - Level gültig (dafür waren sie gerechnet), taugen aber nicht als Ertragsprognose. - ✅✅ **ERLEDIGT — der SIG-Pending-Pfad FÄLLT DURCH (`backtest_signal_touchfill.py`, - 2026-08-06, 80k M5, voller Live-Gate-Stack, kanonischer Exit, Echtkosten).** - | Konf | A) Markt am Bar-Close | B) Level, Fill bei Close-Bruch | **C) Level, Fill bei BERÜHRUNG** | - |---|---|---|---| - | ≥55 | −0,015 / −0,172 | +0,381 / +0,315 | **−0,392 / −0,338** ❌ | - | ≥65 | −0,024 / −0,085 | +0,367 / +0,385 | **−0,478 / −0,471** ❌ | - | ≥75 | −0,148 / −0,310 | +0,288 / +0,136 | **−0,184 / −0,202** ❌ | - **C ist in ALLEN SECHS Feldern negativ** (PF 0,38–0,71). Der Look-ahead-Aufschlag - beträgt **0,77–0,85 R** — grösser als beim Momentum (0,45) und beim Squeeze (0,32). - ⚠⚠ **C ist sogar schlechter als der MARKT-Einstieg** (−0,392 gegen −0,015). Der - Mechanismus ist im Trade-Zähler sichtbar: C hat **mehr** Fills als B (441 gegen - 328 · 659 gegen 455) — die zusätzlichen sind genau die Berührungen, die - zurückfallen, und die sind die Verlierer. Eine Order an einem **wandernden** - Level fängt systematisch die Fehlausbrüche ein. - ✅ **Damit ist die Vorhersage bestätigt:** entscheidend ist nicht „Level statt - Markt", sondern ob das Level **STILLSTEHT**. Squeeze-Box → hält (+0,124/+0,292); - `_pend` und Momentum → wandern → fallen durch. - ⚠ **Die im Bau-Eintrag vom 05.08. zitierten +0,39/+0,36 sind damit als - Live-Erwartung ungültig** — sie stehen in Variante B. Der Eintrag bleibt als - Historie stehen, gilt aber nur noch für den Vergleich Markt↔Level. - ✅✅ **ABGESCHALTET (User-Entscheidung 2026-08-06): `signal_pending_entry = false`.** - Backup `oil_widget_config.ini.bak-2026-08-06-sigpending`. ⚠ Der Schlüssel lebt - **nicht** in `runtime_state.json` (kein UI-Toggle) — die ini greift direkt. - Live verifiziert: `signal_pending=False`, `squeeze_pending=True`. - Neu im **Config-Wächter** (validiert = `false`), damit ein Zurückschalten - auffällt: es hiesse, SIG über den gemessen schlechtesten Einstiegsweg zu handeln - — schlechter als Market. - ⚠ **`squeeze_pending_entry` bleibt begründet** — dort steht die Box still, und - der Touchfill-Test ist bestanden. - ⚠ Zur Migrations-Prüfung: `lauf()` hat einen `fill`-Parameter bekommen - (Default `"close"`). Der Vorher/Nachher-Vergleich war **nicht bitgenau** — - Abweichung: EIN Trade in H2 (454→455). Ursache ist nicht der Patch (mit - `fill="close"` sind die ausgeführten Anweisungen identisch), sondern - **zwischenzeitlich eingetroffene Live-Bars**: `copy_rates_from_pos(0, 80000)` - ankert am neuesten Bar, das Fenster verschiebt sich zwischen zwei Läufen. - H1 war in beiden Läufen identisch. - -- **⚠⚠ MOMENTUM-CONTINUATION MIT LEVEL-ORDER NEU GEMESSEN = BLEIBT VERWORFEN - (`backtest_momentum_v2.py`, 2026-08-06, User-Wunsch).** Kein bloßes Wiederholen: - der Lauf von 2026-07-17 stieg am **Bar-Close** ein, und genau diese Annahme hat - am 05.08. Squeeze und Auto-Signal von negativ auf positiv gedreht. Momentum hat - ein sauber definiertes Level (`mom_N` kreuzt X×ATR ⇔ Preis erreicht - **C[i−N] ± X×ATR**), die Frage war also offen. - | Variante | H1 | H2 | - |---|---|---| - | A) Bar-Close (der alte Lauf) | −0,10…−0,19 | −0,08…+0,00 | - | B) am Level, Fill bei **Kreuzung** | +0,35…+0,47 | +0,47…+0,56 | - | **C) am Level, Fill bei BERÜHRUNG (echt)** | **−0,04…−0,09** | **−0,02…+0,07** | - **Alle neun N/X-Kombinationen** sehen in B großartig aus und sind in C - beidhälftig ohne Edge. **Der Aufschlag von ~0,45 R ist reines Look-ahead.** - ⚠⚠ **Der strukturelle Grund — und er ist die eigentliche Lehre:** das - Momentum-Level **wandert jede Bar** (`C[i−N]` rutscht mit). Es ist damit kein Ort, - an dem eine Order liegen bleiben könnte; sie müsste im 5-Minuten-Takt umgesetzt - werden und fängt dabei jede Zuckung ein. Die Squeeze-Box **steht** — deshalb hält - sie (+0,124/+0,292) und Momentum nicht. **Ein Level ist nur dann ein - Ausführungs-Vorteil, wenn es STILLSTEHT.** - ⚠ **Zwei eigene Fehler auf dem Weg, beide dokumentiert:** (1) der erste Entwurf - prüfte nicht, ob das Level als Stop-Order überhaupt auf der **richtigen Seite des - Marktes** liegt — dadurch „füllte" die Sim zu längst passierten Preisen und - lieferte **ØR +2,2**, was schlicht unmöglich ist und den Fehler verriet. - (2) Variante B war überhaupt erst nötig, weil ich den Unterschied - Kreuzung/Berührung zunächst nicht gesehen hatte. **Merksatz: eine Zahl, die zu - gut ist, ist ein Befund über den Code, nicht über den Markt.** - ✅ Der Befund bestätigt zugleich die heutige Regel aus dem Kerzen-Fade — „ruhende - Order hilft bei Momentum-Entries" — in **präzisierter** Form: sie hilft bei - **Ausbrüchen an stehenden Strukturen**, nicht bei Momentum allgemein. -- **⚠⚠ SCALPING-MODUL AUF M1 = ABGERATEN, bevor überhaupt gebaut wird (gemessen - `analyze_scalping_costs.py`, 2026-08-06, User-Frage „wie sinnvoll wäre ein - zusätzliches Modul Scalping auf 1M-Basis, über das Dashboard steuerbar?").** - Es ist zuerst eine **Kosten-**, keine Signalfrage: der Spread ist bei WTI absolut - ~konstant und skaliert NICHT mit der Zeitebene, die Bewegung schon. - | TF | Ø ATR | Ø Spread | **Median Sp/ATR** | Ø Sp/ATR | - |---|---|---|---|---| - | **M1** | 0,0870 $ | 0,0254 $ | **0,319** | **0,416** | - | M5 | 0,1677 $ | 0,0226 $ | 0,193 | 0,256 | - | M15 | 0,2269 $ | 0,0250 $ | 0,133 | 0,171 | - Der Spread ist auf allen drei Ebenen praktisch derselbe Betrag — **eine mittlere - M1-Kerze ist nur 3,4× so groß wie der Spread.** Nötige Trefferquote bei 1:1 - (Ziel/Stop je 1×ATR_M1): **65,9 %** statt 50 %; bei 0,5:0,5 sogar **81,9 %**. - Zum Vergleich: nichts in diesem Projekt hat je über ~46 % geliefert. - ⚠ **Nachts ist es arithmetisch unmöglich:** um **01:00 beträgt der Spread - 1,051×ATR_M1** — er ist größer als die durchschnittliche Bar-Spanne (00:00 0,801 · - 06:00 0,750). Handelbar wäre allenfalls **10–17 Uhr** (0,210–0,265), also dort, wo - M1 ungefähr die heutigen M5-Kosten erreicht. - ⚠⚠ **DREI Gründe, die schwerer wiegen als die Kosten selbst:** - **(1) Ein Backtest nach Projektstandard ist NICHT möglich.** Die M1-Historie des - Brokers ist hart bei **80.000 Bars = 80 Tage** gedeckelt (`copy_rates_from_pos` - liefert darüber `None`, `copy_rates_range` ebenso). Zwei Stichproben à 40 Tage - liegen im **selben Regime** — genau die Konstellation, an der ORB und die - Dead-Hour-Stunde 13 gescheitert sind. Der eigene `candles_m1`-Logger steht bei - 19 Tagen und erreicht die 180 erst ~2027-01. - **(2) Das Auflösungs-Artefakt trifft M1 am härtesten.** `analyze_trail_resolution.py` - (05.08.) hat gezeigt: eine Bar-Simulation **überschätzt enge Stops systematisch**, - Kipp-Punkt bei ~0,3×ATR. Ein Scalp lebt in 2–3 Bars — eine M1-Bar-Sim hat dort - praktisch keine Auflösung mehr. Ein positives Ergebnis wäre nicht überprüfbar; es - bräuchte Tick-Daten. - **(3) Der Slippage-Schwanz ist ABSOLUT und frisst deshalb den kleinen Stop.** - Gemessen (`analyze_execution.py`): Median 0,017 $ (unkritisch), **Ø 0,234 $, Max - 4,192 $**. Ein 2×ATR_M1-Stop ist **0,174 $** breit — der MITTLERE Slippage-Wert - übersteigt ihn bereits. Der reale 0,751-$-Fall vom 30.06. entspricht **8,6×** einem - M1-Stop gegen 2,2× einem M5-Stop; er löscht rund **11** gewonnene Scalps statt 4. - ✅ **Was FÜR die Idee spricht (fairerweise):** der Bau wäre billig — Pending-Stop- - Orders, `_auto_guard`, `_pending_ziel` mit Vorrang, Dashboard-Toggle (Muster - 🚀 BRK / 🎯 SIG), `runtime_state.json`, B4-Monitor und die Setup-Kennzeichnung - (seit 06.08.) existieren alle. Und **höhere Frequenz = schnellere Validierung**: - der Squeeze braucht 20 Trades und hat nach 5 Tagen 8. - ✅ **Empfehlung: KEIN eigenständiges Scalping-Modul, aber eine engere Variante ist - messbar** — der **Squeeze auf M1** (= die schon offene Variante B), beschränkt auf - 10–17 Uhr, mit ruhender Stop-Order. Das ist kein neues Signal, sondern eine - validierte Regel auf einer feineren Ebene; die 80 Tage reichen für ein - **Ausschluss**-Urteil (nicht für eine Bestätigung). -- **⚠⚠ SQUEEZE AUF M1 (Variante B) GEMESSEN — NICHT ausgeschlossen, aber in JEDER - Variante schlechter je Trade als M5; die Entscheidung hängt an EINER ungemessenen - Zahl (`backtest_squeeze_m1.py`, 2026-08-06).** 80.000 M1-Bars (18.05.–06.08.), - echte `_squeeze_one`-Regel, Einstieg als ruhende Stop-Order am Level, kanonischer - Exit, Echtkosten, sequentiell EIN Slot, **M5-Kontrolle auf DEMSELBEN Fenster** - (weil beide Hälften im selben Regime liegen — der interne Vergleich ist der - belastbare Teil, nicht die Absolutwerte). - | Variante | H1 ØR / ΣR | H2 ØR / ΣR | n (H1) | - |---|---|---|---| - | **M5-Kontrolle** (live laufende Ebene) | **+0,605 / +163** | **+0,491 / +132** | 270 | - | A) M1, Stop aus ATR_M1, alle Stunden | +0,405 / **+473** | +0,385 / **+476** | 1168 | - | **C) A + nur 10–17 Uhr** (beste M1) | **+0,474 / +201** | **+0,387 / +189** | 424 | - | B) M1-Einstieg, Stop aus ATR_M5 | +0,207 / +176 | +0,137 / +118 | 848 | - | D) B + nur 10–17 Uhr | +0,231 / +73 | +0,130 / +44 | 316 | - | A ohne ATR-Floor (Selektions-Kontrolle) | +0,385 / +457 | +0,342 / +442 | 1186 | - **Nichts fällt durch:** alle Varianten beidhälftig positiv, alle 5 Nachbar-Kombis - (Box 10/15/20 × k 0,1/0,2) ebenfalls, Einstiegs-Slippage hält bis 0,20×ATR - (+0,254/+0,130). Auch die Selektions-Kontrolle ohne ATR-Floor bestätigt — der - massstabsgerecht skalierte Floor (0,024 statt 0,06) erzeugt den Befund nicht. - Kein Broker-Hindernis: `trade_stops_level = 0`, ein 2×ATR_M1-Stop (0,159 $) ist - platzierbar. - ⚠⚠ **DER ENTSCHEIDENDE BEFUND IST DIE EXIT-SLIPPAGE — und sie trifft M1 doppelt - so hart, genau wie vorhergesagt.** Slippage ist eine **absolute** Größe; sie frisst - von einem kleinen M1-Stop einen viel größeren Anteil. In DOLLAR angesetzt, auf - beide Ebenen gleich: - | Exit-Slippage | M1 (Variante C) | M5-Kontrolle | - |---|---|---| - | 0,000 $ | +0,474 / +0,387 | +0,605 / +0,491 | - | **0,017 $** (gemessener **Median**) | +0,309 / +0,196 | +0,511 / +0,384 | - | **0,050 $** | **−0,012 / −0,175 ❌** | +0,330 / +0,176 | - | 0,100 $ | −0,499 / −0,737 ❌ | −0,139 (H2) ❌ | - **M1 kippt bei ~0,04 $, M5 erst bei ~0,09 $** — Faktor ~2,2, praktisch das - ATR-Verhältnis (0,198/0,079 = 2,5). Gemessen (`analyze_execution.py`): - **Median 0,017 $** (beide halten) · **Ø 0,234 $** (beide fallen, M1 weit - deutlicher) · Max 4,192 $. - ✅✅ **DIE FEHLENDE ZAHL IST NACHGEMESSEN (`analyze_slippage_holdtime.py`, - 2026-08-06, 317 SL-Closes).** Die bisher zitierten „Median 0,017 · Ø 0,234" - waren die Statistik **nur über die Ausreißer** — `analyze_execution.py` zählt - ausschließlich Fälle, die schlechter als der Initial-SL endeten (`if gap > 0`). - Sauber ist die Untergrenze **E[Slippage] ≥ P(gap>0) × Ø(gap | gap>0)**; sie ist - unkontaminiert, weil das **Trailing den gap nur negativer machen kann** (es zieht - den SL in Gewinnrichtung) — ein positiver gap ist damit zwingend Slippage. - | Haltedauer | alle Stunden | **nur 10–17 Uhr** | - |---|---|---| - | 0–15 min | **0,0470 $** | **0,0058 $** | - | 15–30 min | 0,0164 $ | 0,0238 $ | - | 60–120 min | 0,0502 $ | 0,0449 $ | - | >120 min | **0,1691 $** | 0,0464 $ | - | **<30 min gewichtet** | **0,0370 $** | **0,0124 $** | - ⚠⚠ **Der Schwanz sitzt AUSSERHALB von 10–17** — bei kurzen Trades ist die - Slippage dort **8× kleiner** (0,0058 gegen 0,0470). Damit ist die - Stunden-Beschränkung nicht bloß eine Kosten-Optimierung: sie entfernt auch den - Slippage-Schwanz. Beides hat dieselbe Ursache (Buchtiefe), das ist ein - Mechanismus und kein Data-Mining. - **ENDVERGLEICH — jede Variante mit dem gemessenen Wert IHRES Fensters:** - | Variante | H1 ØR / ΣR | H2 ØR / ΣR | n (H1) | - |---|---|---|---| - | M1 A, alle Stunden · 0,0370 $ | **−0,107 / −125 ❌** | **−0,161 / −199 ❌** | 1168 | - | **M1 C, 10–17 Uhr · 0,0124 $** | +0,354 / **+150** | +0,247 / **+121** | 424 | - | M5, alle Stunden · 0,0370 $ | **+0,401** / +108 | **+0,261** / +71 | 270 | - | M5, 10–17 Uhr · 0,0124 $ | **+0,751** / +68 | **+0,444** / +45 | 90 | - ✅ **M1 über alle Stunden fällt jetzt HART durch** (PF 0,83/0,76) — die - Nachtstunden fressen es auf, exakt wie die Kosten-Arithmetik vorhergesagt hat. - Der ΣR-Vorsprung von Variante A war reines Artefakt der Annahme „keine - Exit-Slippage". - ⚠ **Auf demselben Fenster (10–17) bleibt es ein echter Zielkonflikt:** M5 liefert - je Trade **rund das Doppelte** (+0,751/+0,444 gegen +0,354/+0,247), M1 in der - Summe **rund das Doppelte** (+150/+121 gegen +68/+45) — weil es 4,7× so oft - handelt. Beide konkurrieren um denselben EINEN Positions-Slot. - ✅ **Urteil: NICHT umstellen.** Drei Gründe: (a) der entscheidende Parameter ist - nur als **Untergrenze** bekannt, und M1 C hat davon bis zur Kippgrenze nur - **~3× Luft** (0,0124 → ~0,04), M5 dagegen ~7×; (b) die Stichprobe hinter „M5 ist - je Trade doppelt so gut" ist mit n=90/101 dünn, die hinter der Slippage-Schätzung - im 10–17-Fenster mit 17 Ereignissen ebenso; (c) 80 Tage = EIN Regime. - ⚠ **Die Bedingung, unter der es kippen würde, ist benennbar** (für später): bleibt - die gemessene Exit-Slippage für <30-min-Trades im Fenster 10–17 bei größerer - Stichprobe unter ~0,02 $, dann ist der **Hybrid** (M1 während 10–17, M5 sonst) - rechnerisch ~1,9× der reinen M5-Variante (ΣR +190/+147 gegen +108/+71) und einen - kleinen Live-Test wert. Vorher nicht. - ⚠ **Zwei Methodik-Lehren aus dem Lauf selbst:** - (1) **Der Härtetest lief zuerst auf der SCHWÄCHSTEN Variante.** `beste = "D" if …` - wählte nach Reihenfolge statt nach Ergebnis — D (+0,231/+0,130) statt C - (+0,474/+0,387). Ein Robustheitstest auf der falschen Variante beweist nichts. - (2) **Die vorab fixierte Regel „nicht schlechter als die M5-Kontrolle" war wieder - unterspezifiziert** — M1 verliert in ØR und gewinnt in ΣR um das Dreifache. - Dieselbe Falle wie am 05.08. beim Kosten-Gate. Die Regel wurde **nicht - nachträglich gelockert**; stattdessen zeigt `zeig()` jetzt beide Spalten, damit - die Auswahl nicht stillschweigend passiert. - ⚠ **80 Tage = EIN Regime** (Broker-Deckel). Ein ✅ heißt hier „nicht - ausgeschlossen", **nicht** „bestätigt". - Snapshot **`wave.squeeze {state, dir, level, box_atr}`**: `active` = Ausbruch (echtes - Signal LONG/SHORT) · `armed` = komprimiert, Ausbruch steht bevor · None = keine - Kompression (transient — der Ausbruch weitet die Box, Signal klärt sich selbst). - **Verdict-Modul „Squeeze"** (`_verdict`, Gewicht 2,0, nur bei `active`; unter der - Welle=3,0). **UI Welle-Kachel** (`#wave-squeeze`): grün/rot bei `active` - („🚀 Squeeze-Breakout ▲ LONG @ …"), blau bei `armed` („🎯 Kompression … Ausbruch - steht bevor"), grau sonst. ⚠ **Nur der ENTRY** — der User-Teil „Gegen-Trade schließen - + Stop-&-Reverse" ist NICHT gebaut (nicht validiert, verdoppelt Kosten, Chop-Risiko). - Läuft normal über SL/Trailing/Notfall-Stop. Caveats: modeste Stichprobe (Box>12 = - 12–22 Trades = Rauschen), Frequenz ~2–3/Woche → **B4-Wochenmonitor** (Live-ØR/PF - gegen +0,14…+0,23), Runner-abhängig (43 % Treffer). Verwerfen, wenn Live wegdriftet (B5). -- **Momentum-Continuation als Auto-Setup = VERWORFEN (gemessen `backtest_momentum.py`, - 2026-07-17):** Kandidat für Autonomie (Einstieg in Richtung eines frischen - Momentum-Schubs, mom_N kreuzt ±X×ATR; seq. Sim, Live-Exit, Echtkosten). **Fällt - durch den 2-Stichproben-Test:** fast alle N/X in H1 negativ (−0,03…−0,22), in H2 - positiv (+0,02…+0,07) → regime-abhängig. Einzige „beide positiv"-Kombi (N=24/X=2,0) - ist H1 **+0,002 / PF 1,00** = Breakeven-Rauschen, nicht der Squeeze-Maßstab - (+0,14…+0,23). Grund: Momentum-Continuation IST Trendfolge — erbt die Regime- - Anfälligkeit des Wave-Signals, kein distinkter Edge wie die Kompression→Ausbruch- - Klasse. **Autonomie-Fazit: der Squeeze bleibt der EINZIGE validierte Auto-Entry.** - (P(break)-Level-Bounce als Entry ist separat schon verworfen, s. „Range-Einstieg".) -- **Opening Range Breakout (ORB) als Auto-Setup = VERWORFEN (gemessen - `backtest_orb.py`, 2026-07-17, Recherche-Kandidat):** Box = erste 15/30 min nach - EU-Open (10:00 Brk) bzw. US-Open (16:30 Brk), Ausbruch ±0,1×ATR, one-shot, Live- - Exit, Echtkosten. **3 von 4 Varianten kippen zwischen den Hälften** (US-Open H1 - −0,19…−0,27!); einzig EU-15min ist beidseitig positiv, aber nur ØR +0,069/+0,027 - (PF 1,11/1,04) = Rauschen unter dem Kosten-Puffer, und die Nachbar-Parameter - kippen (keine Robustheit — beim Squeeze waren ALLE 8 Kombis positiv). ⚠ Lehrstück: - erster Lauf zeigte EU-Open ØR +1,3/PF 10 — war ein **Selektions-Artefakt** (ATR- - Skip statt Floor ließ in der Niedrig-Vola-H1 nur 9 Morgen durch); mit Floor - verschwand der „Edge". Zeit-Anker ersetzt keinen Struktur-Anker. -- **ATR-Breakout-Bestätigung** (`wave._confirm_breakout`, live `[trading] - breakout_k=0.3` — Split-validiert, s. u.; 0=aus): ein - Richtungssignal wird **erst durchgelassen, wenn der Kurs k×ATR in - Signalrichtung** gelaufen ist („X dynamisch"); läuft er vorher k×ATR DAGEGEN/ - Timeout → neu verankern, solange **WARTEN** (Grund „warte auf Breakout …", - `snap.breakout`). **Stateful** (`_pend`), reset bei TF-Wechsel. Gemessen - (`backtest_breakout.py`): hebt Ø-Edge/Trade **~2×** (+0,185→+0,36 bei k=1,0), - Treffer 43→47 %, ~halb so viele Trades → **netto** klar besser (halbe Spread-/ - Komm.-Kosten; lohnt ab Kosten >0,037×ATR, real ~0,1×ATR). = die „Zwei-Konten- - Hedge"-Idee des Users, auf EINEM Konto ohne doppelte Kosten. **k=0,3 seit - 2026-07-08 (Split-validiert, 80k Bars in 2 Halbjahren):** k=0,3 ist in BEIDEN - Hälften das Beste — H1: ØR +0,119/netto +339 (0,5: +0,055/**−831**; 1,0: - +0,046/−805 — beide netto negativ!), H2: +0,362/+4239 (0,5: +3308; 1,0: +3838). - Grund: 0,3 filtert Fehlstarts, behält aber den besseren Einstiegspreis (höheres k - = späterer Einstieg, Anfang des Moves verschenkt). ⚠ Ein-Stichproben-Lauf hatte - „1,0 besser" gezeigt — Split-Prüfung drehte das (B2!). Nebeneffekt: kürzere - „warte auf Breakout"-Phasen. -- **Entry-Raum-Gate (AKTIV, `[trading] entry_room_atr=0.6` — kurz 1,0, am selben - Tag zurück auf 0,6, s. u.; gemessen `backtest_entryroom.py` 2026-07-16):** - Richtungssignal → WARTEN, wenn das - **Gegenlevel** (M5-Pivot in Trade-Richtung, trainingsgleich zum S/R-Auto-Close) - näher als X×ATR_M5 liegt. Befund (seq. Sim MIT S/R-Close-Exit, Echtkosten, - 2 Halbjahre): **monoton in BEIDEN Hälften** — Raum <0,3: ØR −0,119/−0,067 - (PF 0,42/0,66!) · 0,3–0,6: −0,100/−0,059 (⚠ 79–82 % WR und trotzdem PF<1 = - Klein-Close-Falle) · 0,6–1,0: −0,123/−0,060 · ≥1,0 bzw. „kein Gegenlevel" positiv. - Kosten-Arithmetik, kein Signal-Timing (deshalb robust, anders als die 9 verworfenen - Filter): Ertrag am Level gedeckelt (63 % Containment) − 0,265×ATR Kosten < 0. - Auslöser: Live-Trades mit Sofort-/Klein-Close (0-min-Trade +0,08 direkt am Level; - 4 der 5 sr_closes desselben Tages waren dagegen RETTER — Exit NICHT anfassen). - Umsetzung: `wave._room_gate` (in `refresh_market` VOR `_confirm_breakout`; - `set_entry_room`, Grund „kein Raum: Widerstand X nur 0,4×ATR entfernt …"). - Backtests via `_build` bleiben gate-frei (kein pb_levels-State). **0,6→1,0→0,6 - (2026-07-24):** kurz auf 1,0 erhöht (gemessen in BEIDEN Hälften besser, H1 +415/ - H2 +266 R; Live-Kurzcheck: S/R-Closes seit 16.07. +925 € bei nur 28 % Klein-Closes) - — aber der User empfand im gemischten Regime (M15/M30 ab, M5/H1 auf) **zu viel - WARTEN** und stellte noch am selben Tag auf 0,6 zurück. ⚠ Lehr-Fall dabei - festgehalten: „kein LONG trotz steigendem Kurs" lag NICHT am Raum-Gate, sondern am - **M30-Gegen-Trend-Filter** (M30 ab → LONG unterdrückt, gewollt); das Raum-Gate - mutete nur den daraus resultierenden SHORT (Support 0,09×ATR entfernt). 0,6 vs 1,0 - ist reiner Frequenz-gegen-Edge-Kompromiss (gemessen 1,0 netto besser). ⚠ **P(break)-Freigabe des Gates = VERWORFEN (gemessen - `backtest_entryroom_pbreak.py`, 2026-07-24):** Idee war, Raum<1,0-Signale - durchzulassen, wenn P(break)@Entry hoch (Level bricht eh → Ertrag nicht gedeckelt). - Das Modell TRENNT zwar auch am Entry (Freigabe-Gruppen klar besser als der Rest), - aber JEDE Schwelle kippt zwischen den Hälften (p≥0,5: −0,016/+0,083 · p≥0,6: - +0,089/−0,006 · p≥0,7: +0,076/−0,029) → nicht robust, reines Distanz-Gate bleibt. - **13. verworfener Signal-Eingriff.** (Nebenbefund: geblockte Gruppe in beiden - Hälften klar negativ, −436/−251 ΣR — bestätigt die 1,0-Erhöhung.) -- **Mindest-Konfidenz-Gate** (`_MIN_CONF=55`): Signal < 55 % → WARTEN. Gemessen - (`backtest_conf.py`): Band **40–54 % = negativer Edge** (−0,025), ≥55 % = +0,058; - Gate hebt Ø-Edge ~+50 % und den Gesamtertrag (entfernt die schwachen Setups, bei - denen zu viele Strafen gestapelt sind — typisch der Lagging-Short am Boden). -- **⚠⚠ DER 43-%-MASSSTAB WAR UNGÜLTIG — der richtige liegt bei ~80 % WARTEN - (gemessen `backtest_dist_gated.py`, 2026-08-05).** `backtest_dist.py` misst - **`_build` ALLEIN**, ohne **Breakout-Bestätigung** und ohne **Entry-Raum-Gate**. - Genau die beiden sind live die größten Blocker. Über 80k M5-Bars: - | | gerichtet | WARTEN | - |---|---|---| - | nur `_build` (die alte Referenz) | **77,9 %** | 22,1 % | - | **voller Live-Stack** | **19,5 %** | **80,5 %** | - | LIVE (seit 01.08., ohne `stale`) | 7,2 % | 92,8 % | - Aufschlüsselung im vollen Stack: entry_room **41,4 %** · breakout_pending 17,0 % · - stretch 13,8 % · deadband 7,4 % · min_conf 0,9 %. - ✅ **Damit ist der „93 % statt 43 %"-Alarm entschärft**, der den TF-Churn-Fix - (2026-07-31) ausgelöst hat: verglichen wurden zwei verschiedene Systeme. Der reale - Abstand zum gültigen Maßstab ist **12,3 Pp**, nicht 50. - ⚠ **Ein Rest bleibt** (19,5 → 7,2 %) und ist NICHT wegerklärt. Kandidaten, die eine - Bar-Simulation prinzipiell nicht abbildet: der **TF-Wechsel** (live wandert die - Basis-TF M5…M30, die Sim ist fix M5), Cooldowns, EIA-/News-Fenster, und die - **Abtastung** — live wird pro Sekunde gemessen, ein `breakout_pending`-Zustand - hält über eine ganze Bar an und ist deshalb live überrepräsentiert (26,5 % gegen - 17,0 %). Der auffälligste Einzelposten ist **min_conf: live 16,3 %, Sim 0,9 %** — - das ist der nächste konkrete Ansatzpunkt, wenn jemand dem Rest nachgehen will. - ⚠⚠ **NACHGEPRÜFT AM 05.08. (User: „heute nur sehr wenige Empfehlungen")** — - 1.427 Zeilen geloggt (Soll ~1.440, die Erfassung selbst ist also intakt), - **5,0 % gerichtet**. Auffällig ist nicht die Menge, sondern die **Verteilung - über den Tag**: alle 71 gerichteten Empfehlungen fielen **vor 10:00**, danach - 16 Stunden lang genau eine. - | Block | 05.08. live | Backtest (voller Stack) | - |---|---|---| - | entry_room | 36,5 % | 41,4 % | - | breakout_pending | 19,4 % | 17,0 % | - | **min_conf** | **16,1 %** | **0,9 %** | - | htf_counter | 7,8 % | — (aus M5 aggregiert) | - | stretch | 6,7 % | 13,8 % | - | eia / stale | 4,1 / 4,1 % | — (nicht modelliert) | - | **gerichtet** | **5,0 %** | **19,5 %** | - ✅ **entry_room ist NICHT der Unterschied** — es ist live sogar seltener als im - Backtest. Die Lücke von 14,5 Pp wird von den **live-only-Gates** getragen, allen - voran **min_conf (+15,2 Pp)**, dazu htf_counter, EIA und `stale`. Zusammen sind - das 31 Punkte, also mehr als genug, um die Differenz zu erklären. - ⚠ Tageszeitlich dominierte nachmittags/abends **entry_room** (21:00: 59 von 60 - Minuten), ab 22:00 **min_conf** (100 %). Strukturelle Mitursache beim Raum-Gate: - es nutzt die **M5**-Pivots, deren Median-Abstand mit **0,61×ATR** praktisch auf - der Gate-Schwelle von 0,60 liegt — es blockt damit rund die Hälfte der Zeit - schon per Konstruktion (s. `backtest_level_tf.py`). - ✅ **TELEMETRIE-LÜCKE GEFUNDEN UND GESCHLOSSEN:** `min_conf`-Zeilen schrieben - `conf_pct = 0` — die verworfene Konfidenz stand NUR als Fließtext in den Gründen - und war nicht auswertbar. Damit liess sich beim zweitgrössten Blocker nicht - sagen, ob die Signale **knapp** (50–54) oder **weit** (20–30) unter der Schwelle - lagen. `wave_rec._build` gibt den Wert jetzt mit (reine Telemetrie, das Signal - bleibt WARTEN). Erste Live-Zeile: **conf 52** — drei Punkte zu wenig. - ⚠ **Was das noch NICHT beantwortet:** ob 55 die richtige Schwelle ist. - `backtest_conf.py` hat das Band 40–54 % als negativ gemessen (−0,025 gegen - +0,058), eine Senkung ist also nicht angezeigt. Die neue Zahl sagt nur, WIE VIEL - Signalmenge unmittelbar hinter der Schwelle steht — nach ein paar Tagen - auswertbar. - ⚠ **Die im TF-Churn-Fix dokumentierte Erfolgskontrolle („WARTEN-Anteil Richtung - 43 % bewegen") ist damit hinfällig** und sollte nicht weiterverfolgt werden. -- **Signalhäufigkeit** (`backtest_dist.py`): ~57 % der Zeit LONG/SHORT, ~43 % WARTEN. - WARTEN-Treiber: M30-Gegen-Trend (~27 % der Bars, gewollt, Edge×2) + Überdehnung - + Totband. `_STRETCH_MAX` 2,5→**3,5** angehoben (`backtest_stretch.py`: +26 % - Signale, Gesamtertrag steigt; ab 3,5×ATR kippt der Edge negativ). -- **⚠ M30-FILTER NACH VOLA-REGIME GEPRÜFT = KEINE ÄNDERUNG (gemessen - `backtest_htf_vola.py`, 2026-08-04).** Anlass: Am 04.08. fiel WTI −4,12 $ (davon - −4,16 $ in EINER Stunde), der Bot stand zu 93 % auf WARTEN, `htf_counter` blockte - **160-mal** — scheinbar genau die Richtung, in die der Markt lief. Hypothese: die - nachlaufende M30-EMA sperrt in schnellen Umkehrungen das Richtige. - **Die Messung invertiert die Hypothese.** Geblockte Signale, kanonischer Exit, - Echtkosten, nach ATR-Terzil × 2 Halbjahre: - | ATR-Terzil | geblockt H1 | geblockt H2 | durchgelassen H1 | durchgelassen H2 | - |---|---|---|---|---| - | niedrig | −0,086 | +0,107 | −0,136 | −0,134 | - | mittel | −0,172 | +0,067 | −0,198 | −0,069 | - | **hoch** | **−0,625** | **−0,329** | −0,061 | **+0,014** | - Im HOHEN Terzil sind die geblockten Signale **mit Abstand die schlechtesten des - gesamten Datensatzes** (PF 0,15 / 0,42) — der Filter ist dort am wertvollsten, nicht - am schädlichsten. Die durchgelassenen Hoch-Vola-Signale in H2 sind das **einzige - positive Feld** der ganzen Tabelle. In den unteren Terzilen kippen die geblockten - Signale zwischen den Hälften = Rauschen. **Den Filter in schnellen Bewegungen zu - lockern wäre die schlechtestmögliche Änderung gewesen. 22. verworfener Eingriff.** - ⚠⚠ **Auflösung des scheinbaren Widerspruchs — und eine Lehre über Populationen:** - Die 160 geblockten Signale und die 17 manuellen Gewinn-Trades desselben Tages sind - **verschiedene Grundgesamtheiten**. Die manuellen Trades liefen ALLE auf - `rec=WARTEN`, stammen also gar nicht aus der Signal-Logik — der Filter hat den User - nie gebremst, er handelte unabhängig. „Das Gate blockte X" und „der Mensch verdiente - Y" gehören nur dann zusammen, wenn Y aus X stammt. Die Ausgangsvermutung hat genau - das verwechselt. -- **Higher-TF-Gegen-Trend-Filter:** Signal wird verworfen (→WARTEN), wenn der - übergeordnete **M30**-Trend (EMA12/50, Totband `_HTF_DEADBAND=0,15×ATR`) klar - dagegen steht — kein Short im M30-Aufwärtstrend & umgekehrt. Nur für Basis-TF - < M30 aktiv (`_htf_sign`, holt M30-Bars im selben `mt5_lock`). Per Backtest - belegt: Ø-Edge ×2 (+0,012→+0,027), Treffer 52→54 %. Mess-Tool: - `python backtest_htf_filter.py M5 M30 4000 0.15`. -- **Einstiegsqualität (nur Konfidenz/Score, NICHT Richtung):** nach gemessenem - Edge (`backtest_pullback.py`) gestaffelt — **tiefer Pullback** (Kurs durch die - EMA zurück, `near<0`) = bestes CRV (+15, „⭐ tiefer Pullback"); laue Zone direkt - an der EMA (0–0,3×ATR) = schwächster Edge (−3); Trend-Momentum (1–2×ATR) +8. - **Kein** Spitzen-Gate: ein hartes „nur an Wellenspitze"-Filter senkte den - Gesamtertrag (−80 % Trades), darum verworfen. Konfidenz blockt NICHT das Ordern - (nur WARTEN/Gegen-Signal tut das). -- **Multi-TF-Konfluenz (nur Konfidenz/Anzeige):** M30 **und** H1 bestätigen die - Richtung → +12, „⭐⭐ Konfluenz M30+H1"; H1 dagegen → −8. Per Backtest belegt: - Ø-Edge ×3,5 (+0,016 nur-M30 → +0,056 M30+H1). `_htf_sign` holt zusätzlich - H1-Bars. Mess-Tool: `python backtest_improve.py 8000`. -- **„Morgens fällt der Kurs, nachmittags steigt er" = WIDERLEGT (gemessen - `analyze_tageszeit.py`, 2026-08-04, User-Beobachtung):** ROHE Kursdrift je - Berliner Stunde (nicht Signal-Edge wie `backtest_hourly_split.py`), 80k M5-Bars - (17.06.25–04.08.26), DST-korrekt via `zoneinfo`, 2 Hälften, Überschuss gegen den - jeweiligen Tagesschnitt (sonst färbt das Regime alle Stunden ein: H1 fiel, H2 stieg). - **ALLE VIER Tagesblöcke kippen zwischen den Hälften** — es gibt überhaupt kein - Tageszeit-Muster (Zehntel-Cent je Bar): - | Block | H1 | H2 | | - |---|---|---|---| - | Vormittag 08–12 | +0,06 | −0,43 | kippt | - | Nachmittag 14–18 | −1,26 | +1,96 | kippt | - | Abend 19–23 | −0,39 | +1,67 | kippt | - | Nacht 00–07 | +0,42 | −1,30 | kippt | - Robust auf: 2/5/20/21/23 Uhr · robust ab: 14/18/19 Uhr — verstreute Einzelstunden - aus 24 Tests, kein Vormittag/Nachmittag-Muster. - ⚠ **Größenordnung macht es ohnehin unhandelbar:** die stärkste belastbare Stunde - (20:00, +1,9/+3,3) sammelt über eine volle Stunde ~0,02–0,04 $ — der Spread allein - ist **0,0225 $**. Selbst wenn die Drift echt wäre, ist sie kleiner als die Kosten. - ⚠ Std 23 sticht mit +33,0 in H2 hervor, hat aber nur n=648 (22 Uhr: 1062, sonst - ~1750) — das ist der Marktschluss-Rand, kein Befund. - ⚠⚠ **ZWEI eigene Messfehler auf dem Weg dorthin — beide dokumentiert, weil sie - Fehlerklassen sind, keine Ausrutscher:** - **(1) Selbst gebauter ATR-Filter (04.08.).** Der erste Lauf meldete „Vormittag - robust negativ" — Artefakt von `atr < 0,06` übersprungen + ATR-Normierung. In den - ruhigen Morgenstunden der älteren Periode liegt der ATR unter dem Floor → von - 1.740 Bars je Stunde blieben **138–391** übrig, und zwar die ungewöhnlich - volatilen Morgen. Gleiche Fehlerklasse wie das ORB-Selektions-Artefakt 2026-07-17. - **Regel: ein Filter, der Bars nach einer Eigenschaft verwirft, die mit der - untersuchten Achse korreliert, ERZEUGT den Befund.** - **(2) Zeitzone um 3 h verschoben (05.08., beim Squeeze-Nachgang aufgefallen).** - `datetime.fromtimestamp(rates["time"], UTC+3)` behandelt den Bar-Zeitstempel als - echten UTC-Epoch — MT5 liefert dort aber bereits die **Broker-Wallclock**. Ergebnis: - jede Stunde war **3 Stunden zu spät** einsortiert; der zweite Lauf meldete deshalb - „Nachmittag robust negativ", was in Wahrheit der Abend war. **Richtig:** - `fromtimestamp(ts, UTC).replace(tzinfo=UTC+3).astimezone(Berlin)` (Helfer `_berlin` - im Skript). ⚠ Der Rest des Projekts war NICHT betroffen (geprüft) — `core/gaps.py` - & Co. nutzen die richtige Konvention; der Fehler steckte nur in diesem neuen Skript. - **Plausibilitätstest, der ihn entlarvt hätte: die dünnen Stunden (n≈650/1060) müssen - am Marktschluss liegen (22/23 Uhr), nicht mitten in der Nacht.** -- **Tageszeit-Gate = AUS geschaltet (User-Vorgabe 2026-07-22, `[trading] dead_hours=` - leer — bewusst GEGEN die Messung).** Jetzt **config-steuerbar** (`wave.set_dead_hours`, - Komma-Liste von Stunden; leer=aus). Gemessener Default im Code = `0,1,2,3,4,5,6,7,12,16` - (die ini überschreibt auf leer). ⚠ 12/16 waren zuletzt am 2026-07-21 - (`backtest_hourly_split.py`) noch beidhälftig negativ — der User schaltet das Gate - trotzdem ab. Reaktivieren = ini-Stunden eintragen. Der **EIA-Blackout (Mi 15:30–16:30) - bleibt separat aktiv** (eigenes Event-Gate, nicht Teil des `dead_hours`). Historie/ - Messung (Gate war 11–14 → 12+16 → aus → zurück → jetzt wieder aus): - Nach der **Echtkosten-Messung** (`backtest_realcosts.py`, Kosten = Bar-Spread/ATR - statt pauschal 0,1×ATR — Ø real 0,265!): **Nacht 0–7 = Kostenfalle** (konstanter - Spread ÷ niedriger Nacht-ATR = 0,32–0,50×ATR, in beiden Hälften negativ; reine - Arithmetik) + **12/16 Uhr 3× unabhängig negativ**. Politik „0–7+12" verbessert - BEIDE Hälften (H1 −4038→−1870, H2 +100→+1191). **Robust positiv nur 21–22 Uhr.** - ⚠ Die „15 robusten Negativ-Stunden" NICHT blocken — in-sample-zirkulär (Overfit). - breakout_k=0,3 auch unter Echtkosten bestätigt (H1 einziges positives k). Historie - (Gate war 11–14, dann 12+16, dann auf User-Wunsch aus, jetzt gezielt zurück): - - **Von 11–14 auf 12+16 umgestellt (gemessen+verifiziert, `backtest_hourly.py` + - `backtest_hourly_split.py`, 80k M5 in 2 Halbjahren):** Netto-R je Stunde zeigt - **12 Uhr (−0,11/−0,12) und 16 Uhr (−0,17/−0,10) in BEIDEN Hälften negativ**; - **Std 13 kippt das Vorzeichen** (−0,048/+0,039 = Rauschen), 11/14 ~breakeven. Das - alte Pauschal-Gate 11–14 blockte harmlose Stunden und ließ die real schlechte 16 - laufen. **Gate 12+16 schlägt 11–14 im Gesamt-Netto-R in beiden Zeiträumen** - (H1 −1357→−1253, H2 +1562→+1799) und handelt dabei mehr Trades. Intraday-Edge - generell dünn; **Löwenanteil in US-Session 21–22 Uhr** (+0,25/+0,49). - - **Reversal-Ausnahme vom Gate = VERWORFEN (gemessen, `backtest_deadhour.py`):** - Reversal in der Dead-Hour nur **+0,062×ATR brutto** (vs +0,190 außerhalb) → nach - Kosten (~0,1×ATR) negativ. Trade-①-Gewinner war Varianz. Gate gilt auch für Reversal. -- **EIA-Blackout (kausales Event-Gate, AKTIV):** **Mittwoch 15:30–16:30 Berlin** - (Vorlauf der EIA-Lagerdaten 16:30 = 10:30 ET, Offset ganzjährig 6 h) → hartes - WARTEN („EIA-Blackout …"). Gemessen (`backtest_events.py`, 2 Halbjahre): dieses - Fenster in BEIDEN Hälften netto negativ (−0,147/−0,088 vs rest) — deckt sich mit - dem alten „16 Uhr"-Befund. eia_post/api_pre/api_post NICHT robust → nicht geblockt. - Nur im Live-Pfad (`hour≠None`), Backtests bleiben gate-frei. Best-Practice - „Event-Blackout" statt Pauschal-Gate. -- **Wirtschaftskalender-Blackout (`core/econ_calendar.py`, 2026-07-30, `[trading] - news_blackout=true`, ±15 min):** Erweiterung der EIA-Idee auf ALLE High-Impact- - Termine (FOMC/Zinsentscheid/NFP/CPI/GDP/PCE + alle Oil-/OPEC-Titel). Quelle - **Forex Factory JSON** (`nfs.faireconomy.media`, kostenlos, kein Key, mit - `impact`-Feld); geholt werden **beide Wochen** (`thisweek`+`nextweek` — „thisweek" - endet samstags, der Markt öffnet aber Sonntag 23:00 → sonst blinder Fleck), 1×/h - **im Daemon-Thread** (ein hängender HTTP-Call darf den Trend-Loop nicht blockieren), - dedupliziert über (ts, title). Filter: `impact=High` + `country=USD`, ODER Titel - enthält crude oil/opec/oil inventories/petroleum (der EIA-Report läuft bei FF teils - als Low/Medium). ⚠ **NICHT gemessen** — Events sind zu selten für eine - 2-Stichproben-Aussage, das ist die Handbuch-Regel („keine Trades 15 Min. vor - High-Impact-News"), kein belegter Edge. Deshalb bewusst asymmetrisch verdrahtet: - **sperrt nur die AUTONOMEN Entries** (`_check_auto_squeeze` + `_check_auto_signal`, - jeweils OHNE Dedup-Marker → nach dem Fenster feuert das Setup, falls noch aktiv) und - erscheint in der Entry-Checkliste nur als **„warn"**, nicht „fail" (soll den Verdict - nicht allein auf `stop` kippen); **manuelle Orders bleiben frei.** Der gemessene - EIA-Blackout bleibt unabhängig davon aktiv — er greift auch, wenn der Kalender kein - Netz hat (Fail-safe: ohne Daten → kein Blackout, Log 1×, dann still). Snapshot - `calendar {enabled, n, blackout{title,impact,ccy,minutes}, next, age_min}`, - Frontend `#cal-note` (Meldungen-Karte, bernstein: laufendes Fenster bzw. Vorwarnung - ≤60 min). Abschalten `news_blackout=false`; Fenster via `news_blackout_before_min`/ - `_after_min`. Mit 4 synthetischen Szenarien getestet (5 min vor/nach Event, 50 min - davor = kein Blackout, disabled) + Live-Fetch verifiziert (EIA lag korrekt auf - Mi 16:30 Berlin). -- **⚠⚠ WINKEL-BESCHRIFTUNG WAR ÜBERALL INVERTIERT — Text korrigiert 2026-08-04, ZAHLEN - unverändert.** Beim Spiegeltest (User: „prüfe die Logik im umgekehrten Fall, fallender - Kurs") kam heraus: `calc_trend_angle` liefert **0°=AUFWÄRTS … 180°=ABWÄRTS**, also ist - `ad = angle−90` **negativ bei STEIGENDEM** Kurs. Damit sagten die Texte das Gegenteil - dessen, was passierte — ein LONG in einem steigenden Markt bekam „⚠ Winkel gegen EMA" - (−10), ein LONG in einem fallenden „Winkel bestätigt" (+5). - ⚠ **Und `backtest_angle.py` benutzt DIESELBE verdrehte Beschriftung** - (`a5_dis = a5 < 90-dead` für LONG heißt „dagegen", ist aber STEIGEND). Der - dokumentierte Befund „Winkel dafür/neutral +0,055 vs dagegen +0,025" bedeutet richtig - gelesen: **LONG bei FALLENDEM Kurzfrist-Winkel (= Pullback) trägt doppelt so gut wie - bei bereits steigendem.** Das ist der im Projekt mehrfach belegte **Pullback-Effekt** - (vgl. „⭐ tiefer Pullback" und den RSI-Befund: *bestätigende* Oszillatoren sind - schlechter). - ✅ **Konsequenz: die Gewichtung war von Anfang an NUMERISCH RICHTIG** — nur hieß der - Bonus „bestätigt", obwohl er den Gegen-Winkel belohnt. **Nicht umdrehen.** Geändert - wurden ausschließlich die Reason-Texte („⭐ Pullback (Kurzfrist-Winkel gegen den - Trend)" / „kein Pullback — Kurs läuft dem Signal schon davon") und die Kommentare. - Verifiziert: Konfidenzwerte vorher/nachher identisch (60/70/75 je Richtung). - ⚠ **Lehre:** Der erste Verdacht lautete „systematisch invertierte Logik" — er war - falsch. Die Zahlen stimmten, die Sprache log. Bei einer verdrehten Konvention muss man - prüfen, ob die MESSUNG dieselbe Verdrehung teilt, bevor man den Code „repariert". - (Beim Reversal war es anders: dort hat die eigenständige Messung das Setup unabhängig - von der Beschriftung als negativ ausgewiesen.) - ✅ **Spiegeltest bestanden:** Mit `reversal_enabled=false` ist das Verhalten über die - ganze Überdehnungs-Spanne **exakt symmetrisch** (steigend/LONG ↔ fallend/SHORT): - gleiche Signale, gleiche Konfidenz, gleicher Anti-Überdehnungs-Schnitt bei 3,5. -- **Winkel-vs-EMA-Warnung (nur Konfidenz):** Regressions-Winkel der Basis-TF gegen - die EMA-Richtung → −10 „⚠ Winkel gegen EMA (mögliche Wende)" (nachlaufende EMA!); - dafür → +5. `backtest_angle.py`: dafür/neutral +0,055 vs dagegen +0,025; als Gate - aber Gesamtertrag-negativ → bewusst nur Konfidenz/Warnung (Hilfe fürs manuelle - Überspringen der Lagging-Shorts). -- **Höher-TF-Winkel-Filter (M30+H1-Winkel) = VERWORFEN (gemessen, - `backtest_htf_angle.py`):** Signal verwerfen, wenn M30/H1-Regressionswinkel gegen - die Richtung steht → in JEDER Schwelle/Variante **schlechter**. Die entfernten - Signale (Short bei aufwärts-Winkel) haben **höheren** Edge (+0,04–0,05) als die - behaltenen (+0,02) — es ist der profitable Pullback-Setup. Filter kostet ~⅔ des - Gesamtertrags (+287→~+70). Gegen-Winkel-Shorts NICHT filtern; einzelne große - Verluste (z. B. −37 an einem H1-Auf-Tag) sind Varianz, Schutz über Notfall-Stop/ - Sizing, nicht über Signal-Filter. - - **Auch als Konfidenz-STRAFE verworfen (`backtest_confluence_angle.py`):** statt - Hard-Filter den Konfluenz-Bonus streichen/Konf senken, wenn M30/H1-Winkel - dagegen → borderline-Signale fallen unters 55-Gate. Ergebnis: in JEDER Variante - **schlechter** (Δ −2500…−3467 R); die entfernten Gegen-Winkel-Signale haben - ØR **+0,26…+0,32** (die BESTEN), die behaltenen +0,12. **3× gemessen** (M5-Winkel, - Höher-TF-Winkel-Filter, Konfluenz-Strafe): „nicht gegen den Winkel shorten" - macht den Bot IMMER schlechter. **Nicht mehr am Signal drehen** — Edge ist klein - & varianzlastig; der Hebel ist **Sizing (risiko-basiert) + viele Trades**, nicht - der Filter. - - **H1-STÄRKE-Filter (nicht Winkel!) = VERWORFEN (gemessen `backtest_short_h1filter.py`, - 2 Halbjahre, 2026-07-15):** Motiviert durch Live-Befund „SHORT-Empfehlungen 31 % WR - in Aufwärts-Regime" (LONG 62 %, SHORT 31 %, letzte 40 MIT-Trades). Idee: Signal - verwerfen, wenn es gegen einen **starken H1-Trend** (|H1 EMA12−EMA50| > thr×ATR_H1) - läuft. **Fällt durch:** die weggefilterten Signale flippen das Vorzeichen zwischen - den Hälften — thr 0,5: ØR **H1 −0,198 / H2 +0,001**; thr 1,0: **−0,069 / +0,013**; - thr 1,5: **+0,029 / −0,063**. In KEINER Schwelle robust negativ in BEIDEN → Filter - hätte in H2 (positive/breakeven drops) **Geld gekostet**. **7× belegt: die 31 %-SHORT- - Schwäche ist REGIME (aktueller Uptrend), kein Signalfehler** — in H2 tragen dieselben - Gegen-H1-Shorts. Kein Filter; der Hebel bleibt Sizing + Notfall-Stop + Diskretion - (User-WARTEN-Übersteuerungen liefen 68 % WR / +259 €). - - **ER-Chop-Gate = VERWORFEN (gemessen, `backtest_chopgate.py`, 2 Halbjahre):** - Signal→WARTEN wenn Efficiency Ratio der letzten N Bars < X. Chop-Signale sind NICHT - robust schlechter — Effekt kippt: H1 Trend leicht besser (Ø-R +0,070 vs +0,053), - H2 **umgekehrt** (Chop +0,138 vs +0,129). Gate wirft zudem ~70 % (profitables) - Volumen weg (ΣR +2299→+836). Das Totband filtert Extrem-Chop schon (ER-0,02-Tag = - 71 % WARTEN); Rest-Signale = Varianz. **5× belegt: Signal-Filter tragen nicht.** -- **Vola-Abzug (nur Konfidenz):** hohes ATR (oberes Terzil, `_ATR_HIGH≈0,27`) = - schwächster/negativer Edge → −8 „hohe Vola". Schwelle regime-abhängig → kein Gate. -- **Reversal-Lockout/-Persistenz = VERWORFEN (gemessen, `backtest_revlock.py`):** - Gegen-Signale **kurz nach** einem Reversal sind sogar **besser** als normale - (+0,072 vs +0,018 bei N≤6) → Reversal-Signale NICHT sperren/halten. Einzelne - Verluste (Bottom-Short nach REV) sind Varianz, kein Muster. -- **Rec-Logging:** `engine._run_analysis` → `history.log_recommendation` (Throttle - intern) füllt die `recommendations`-Tabelle (Signal/Score/Conf/Winkel/Setup) für - Tages-Reviews (`analyze_today.py` rekonstruiert sonst aus M5-Bars). -- **Reversal-/BOUNCE-Einstieg (antizyklisch, markiertes ZWEITsignal):** **überdehnt** - (|stretch|≥`_REVERSAL_STRETCH`=**3,0**, **entkoppelt** von Anti-Überdehnung - `_STRETCH_MAX`=3,5) **UND** Regressions-Winkel hat gedreht → Einstieg in - **Winkel-Richtung, gegen die EMA** (überverkauft + Winkel auf → LONG; überkauft + - Winkel ab → SHORT). Setup `WAVE_REV_*`, Grund „🔄 Reversal"; hebt Anti-Überdehnung - **und** M30-Filter bewusst auf. **Schwelle 3,5→3,0 gesenkt** (User wollte mehr - Bounces; `backtest_bounce.py`): Ø-R +0,185, PF 1,35, Treffer 70 %, Worst −2×ATR - (SL-gedeckelt), ~1,5× mehr Bounces als 3,5. - ⚠⚠ **WINKEL-BEDINGUNG LÄUFT GEGEN IHRE EIGENE DOKUMENTATION (gefunden 2026-08-04, - User-Frage „warum SHORT im starken M5-Aufwärtstrend?").** `calc_trend_angle` liefert - laut Docstring **0° = starker AUFWÄRTStrend, 90° = flach, 180° = ABWÄRTStrend** — - also invertiert. Damit heißt `ad = angle−90`: **ad < 0 = STEIGEND**, ad > 0 = fallend. - Der Code triggert aber `stretch >= +3.0 and ad <= -2` → SHORT, kommentiert als - „überkauft + Winkel ab". Tatsächlich feuert er bei überkauft **UND WEITER STEIGENDEM** - Winkel — also GEGEN den Winkel, während CLAUDE.md „Einstieg IN Winkel-Richtung" - beschreibt. Dasselbe Vorzeichen steckt im Konfidenz-Block (Zeile ~790): ein SHORT - bekommt „**Winkel bestätigt**" (+5), während der Kurs steigt. Mit synthetischen - Reihen direkt nachgestellt: überkauft + steigend → `WAVE_REV_SHORT`, Grund „Winkel - gedreht" + „Winkel bestätigt". **Erklärt zwei dokumentierte Befunde:** die ~50 % - Richtungstreffer der Bounce-Anzeige (`backtest_bounce_reliability.py`) und „blutet in - starken Trends". - ⚠⚠ **GEMESSEN (`backtest_reversal_angle.py`, 80k M5, kanonischer Exit, Echtkosten, - 2 Halbjahre) — ALLE drei Varianten fallen durch:** - | Variante | H1 ØR | H2 ØR | PF | n | - |---|---|---|---|---| - | **IST** (Live) überkauft + Winkel steigt → SHORT | −0,122 | −0,031 | 0,79/0,94 | 1100/1615 | - | **KORRIGIERT** (Doku-Absicht) Winkel fällt → SHORT | **−0,173** | **−0,061** | 0,68/0,89 | 106/163 | - | **KONTROLLE** ohne Winkelbedingung | **−0,099** | **−0,023** | 0,82/0,96 | 1153/1693 | - Drei Schlüsse: (a) **Die Vorzeichenkorrektur ist SCHLECHTER** — gut, dass sie nicht - blind eingebaut wurde; sie feuert zudem kaum (106 statt 1100 Trades, weil bei 3×ATR - Überdehnung die 14-Bar-Regression fast immer noch steigt). (b) **Die Kontrolle ohne - Winkel ist die BESTE der drei** — die Winkelbedingung entfernt Trades, die im Schnitt - besser waren als die behaltenen; sie trägt mit KEINEM Vorzeichen etwas bei. - (c) **Der Trigger ist in jeder Variante beidhälftig negativ.** - ⚠ Gemessen wurde der ROHE Trigger (ohne Konfidenz-/Raum-Gate); der Legacy-Recheck der - gegateten Variante kam auf ~Breakeven (PF 0,93/1,06) — dieselbe Richtung. **Keine - Messung stützt die dokumentierten „+0,185 / PF 1,35" mehr.** - ✅ **ABGESCHALTET (User-Entscheidung 2026-08-04, `[trading] reversal_enabled=false`).** - Ausschlag gab nicht nur die negative Zahl, sondern dass das Setup **zwei gemessen - POSITIVE Schutzmechanismen aushebelt** (Anti-Überdehnung UND den M30-Gegen-Trend- - Filter, Edge ×2) — ein Setup ohne eigenen Edge, das validierte Filter umgeht, ist der - ungünstigste Fall. - ⚠ **Wichtig für Backtests: der Modul-Default in `wave_rec` bleibt `True`.** Sämtliche - Backtests rufen dasselbe `_build`; ein False-Default hätte alle bestehenden Messungen - still verändert. Abgeschaltet wird ausschließlich im Live-Pfad - (`engine` → `wave.set_reversal_enabled(...)`, Config-Default dort `false`). - Verhaltensänderung nur im Band **|stretch| 3,0–3,5**: dort kam vorher ein - antizyklisches REV-Signal, jetzt das normale Trendsignal (und ab 3,5 greift wie - gehabt die Anti-Überdehnung). Mit 3 Szenarien getestet — überkauft+steigend: - `SHORT/WAVE_REV_SHORT` → **`LONG/WAVE_LONG`**; nicht-überdehnter Trend unverändert. - Live verifiziert: bei 3,18×ATR Überdehnung jetzt `WARTEN · setup=WAVE · - reversal=None` statt REV_SHORT; Log „Reversal-Setup AUS". - Überwacht via `measurement_reminder.py` (CONFIG_DEPS, 11 Werte). - **Zurückholen:** `reversal_enabled = true`. Die **Bounce-Anzeige** (`wave.bounce`, - Kachel „Überdehnung") bleibt unverändert — sie war ohnehin nur Warn-Kontext, kein - Trade-Trigger. - ⚠⚠ **DIESE ZAHLEN GELTEN NICHT MEHR** (Legacy-Recheck 2026-07-31, hier eingetragen - am 2026-08-02): `backtest_bounce.py` nutzt den **vereinfachten Exit OHNE Kosten** - (`LEGACY_SIMPLE`: kein Breakeven, kein Lock, kein Time-Stop). Mit dem **echten** - Exit + Echtkosten liegt das Setup bei ~**Breakeven** (PF 0,93/1,06) — es ist - **kein +0,185-Ertragsbringer**. Was HÄLT, ist die *relative* Aussage: Reversal - schlägt Trend in beiden Hälften (+0,140/+0,102). Deshalb bleibt es ein sinnvolles - **nachrangiges, markiertes Zweitsignal** (so ist es live auch eingebaut), aber - nicht mehr als das. Details im Abschnitt „LEGACY-RECHECK". - Weiterhin gültig: **schwächer als Trend** & **regime-anfällig** (trägt in - Seitwärts/Mean-Reversion, blutet in starken Trends). Nachrangiges, markiertes Setup. **Bounce-Status — Multi-TF** im Wave- - Snapshot (`wave.bounce = {state, dir, tf}`): scannt **M1/M5/M15/M30** (`_bounce_one`, - Aggregat in `refresh_market`, ~15 s-Throttle via TF-Ampel-Fetch), nimmt den - **stärksten** (aktiv>erwartet, dann größte Überdehnung). **`expected`** = überdehnt - (≥`_REVERSAL_STRETCH`), Winkel noch NICHT gedreht · **`active`** = + Winkel gedreht. - `dir` LONG (überverkauft) / SHORT (überkauft), `tf` = Quelle. Frontend `#wave-bounce` - als **„Überdehnung"-Warn-Gauge** (NICHT „Bounce/Signal"): grau „keine" · bernstein - „⚠ überdehnt … Reaktion nach oben ↑ / nach unten ↓ möglich" · kräftiger bei - `active` (+ TF). **Kein - grün/rot** (kein Trade-Trigger). **Reversal-Konfidenz-Boni = GEPRÜFT & BEHALTEN - (gemessen `backtest_revconf.py`, 2 Halbjahre, 2026-07-15):** Befund war, dass - „starker Trend" (+10) und „⭐ tiefer Pullback" (+15) beim Reversal semantisch - GEGEN das Signal stehen und Reversals so leichter durchs 55er-Gate kommen. - Korrektur (Boni beim REV streichen) **fällt durch**: die dadurch rausfallenden - Reversals kippen das Vorzeichen — H1 ØR **−0,323** (127 St., Fix ok), H2 ØR - **+0,109/PF 1,16** (1608 St., Fix hätte die BESSEREN gekappt, WR 45 %). Nicht - robust → Konfidenz-Berechnung bleibt. **8. verworfener Signal-Eingriff.** Die - hohe REV-Conf-Anzeige (~75–80 %) ist also kein Gate-Fehler; wer will, liest die - Reason-Liste („Reversal" + „hohe Vola") als Risiko-Kontext. **Gemessen (`backtest_bounce_reliability.py`): - Richtungs-Trefferquote der „aktiv"-Anzeige ~50 % (Münzwurf), Ø-Vorlauf ~0×ATR über - alle TF & H1-Regime → reiner Überdehnungs-Kontext, KEIN Signal.** (Die früheren - „70 %" aus `backtest_bounce.py` waren exit-getrieben, nicht Richtungs-Vorhersage.) -- **⚠⚠ TF-CHURN-FIX (2026-07-31, zwei Eingriffe) — Ursache für „93 % WARTEN statt 43 %":** - Auslöser war die User-Frage „warum hat der Bot die Treppe nicht erkannt und long - eröffnet?" zur Rally am 31.07. (82,5 → 84,4 in 2 h). Befund: in dem Fenster gab es - **99× WARTEN und 1× SHORT** — und dieser SHORT wurde am Tief autonom eröffnet - (AUTOSIG, −26,31 €, per `adverse15` geschlossen); den LONG machte der User um 11:56 - von Hand gegen die Empfehlung. **Der WARTEN-Anteil live ist strukturell zu hoch:** - | | WARTEN | - |---|---| - | Backtest-Erwartung (`backtest_dist.py`) | ~43 % | - | Live letzte 24 h | **93,2 %** | - | Live letzte 7 Tage | 88,0 % | - | Live gesamt (93.599 Zeilen) | 78,6 % | - **MECHANISMUS (im Log belegt):** `_choose_tf` setzt den Score einer TF **hart auf - 0,0**, sobald sie überdehnt ist (`raw = sep if (sep>=deadband and stretch<=_STRETCH_MAX) - else 0.0`) — genau das passiert M5 IM Trend. Real am 31.07.: M5 sprang zwischen - **0,00 und 1,93**, die TF wechselte **33× am Tag, Median-Abstand 5 min**. Die - vorhandene 1,2×-Hysterese ist dagegen wirkungslos (gegen 0,0 kommt jeder Wert durch). - Und `set_timeframe()` verwarf bei JEDEM Wechsel die laufende Breakout-Bestätigung - (`_pend = None`) und verankerte sie beim AKTUELLEN Kurs neu. Bei `breakout_k=0,3` und - ATR_M30≈0,53 braucht sie ~0,16 $, der Kurs lief ~0,08 $ je 5 min → **die Bestätigung - konnte rechnerisch nie fertig werden.** Dazu: landet die Heuristik im Trend auf M30, - braucht das EMA-Kreuz dort Stunden — eine 2-h-Bewegung wird nie zum Signal. - ⚠ **Gleicher Fehlertyp wie beim P(break)-Modell am selben Tag:** `backtest_breakout.py` - hat k=0,3 auf einer **FESTEN** Zeitebene validiert; live wandert sie — die Bedingung, - unter der die Messung gilt, existiert im Betrieb nicht. - **FIX 1 (`wave_rec.set_timeframe`): `_pend` wird NICHT mehr zurückgesetzt.** Der Anker - gehört zum Signal, nicht zur Zeitebene; dreht die Richtung, verankert - `_confirm_breakout` ohnehin neu (`p.get("dir") != d`). Das Level behält den ATR der - alten TF — bewusst, eine Neuberechnung wäre wieder ein Reset. - **FIX 2 (`engine._tf_loop`): Mindest-Verweildauer `[trading] tf_min_dwell_s=900`** - (0 = aus). Gibt einer TF Zeit, ihr Signal überhaupt fertig zu bestätigen. - **Verifiziert mit synthetischen Szenarien:** Fix 1 — Bestätigung überlebt den Wechsel - M5→M30 und feuert LONG, Richtungswechsel verankert korrekt neu; Fix 2 — an der ECHTEN - Score-Folge vom 31.07. nachgespielt: 4 Wechsel → 2. - ⚠ **Ehrlich zur Beweislage:** das ist **NICHT backtestbar** — Backtests laufen - grundsätzlich auf einer festen TF, das Churning existiert dort per Konstruktion nicht. - Begründung ist also nicht „gemessen besser", sondern „stellt die Bedingung her, unter - der die vorhandene Messung gilt". **Erfolgskontrolle = der WARTEN-Anteil**: er muss - sich Richtung der gemessenen ~43 % bewegen (Abfrage s. Tabelle oben). - ⚠ **Nicht behoben (bewusst):** der 0,0-Einbruch des Scores bei Überdehnung selbst — - das wäre eine Änderung der TF-Bewertung und damit messpflichtig. Fix 2 halbiert das - Churning, verhindert aber nicht den ersten Absturz auf M30. -- **Timeframe:** `[trading] tf_select=heuristic` → `engine._choose_tf` wählt jede - Minute die TF mit klarstem Trend aus dem **Band `[tf_min … tf_max]`** (leichter - Höher-TF-Bias). **Einstellung: `tf_min=M5`, `tf_max=M30`** (M1 raus: gemessen - ~0 Edge; M30 zugelassen, damit der Bot in einem M30-Trend nicht festhängt, wenn - 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 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 % - Quasi-Ruin, 1–2 % = 0 %). Risiko-Modus bleibt gebaut & abgesichert (Guards s. u.), - Umschalten = `risk_pct=1.5`. Mechanik: `trader._send_locked` - bestimmt **erst den SL**; bei `risk_pct>0` → risiko-basiert (`calc_lots_risk`, - Verlust ≈ risk_pct % Equity, Margin als Deckel) = der gemessene Fix gegen die - Verlust-Asymmetrie. **Umschalten via `[trading] risk_pct` (z. B. 1.5 = überlebbar).** - ⚠ **Risiko-Modus-Guards (Fix 2026-07-19):** `calc_lots_risk` rundet NICHT mehr auf - `volume_min` auf (unter Mindestlot → 0.0 statt still mehr Risiko), und - `_send_locked` fällt bei `risk_pct>0` NICHT mehr still auf Margin-Sizing zurück - (75 % wäre ein Vielfaches des gewollten Risikos) — der Trade wird stattdessen mit - Fehlermeldung abgelehnt. Bei `risk_pct=0` (aktuell) unverändert margin-basiert. - Harter Broker-SL immer mitgeschickt; adoptierte Fremd-Trades bekommen Schutz-SL. - - **Sizing-Simulation (`backtest_sizing.py`):** 90 % Margin = Wachstums-optimal, - ABER **36 % P(DD>80 %)** = Ruin-Risiko. Risiko 1–2 %/Trade = überlebbar (0 % - Quasi-Ruin). Exit-Asymmetrie gemessen `backtest_exit2.py` (Live-Exit Trail 1,5 - ist OK; Problem war Sizing + manuelles Gewinner-Kappen, NICHT die Exit-Logik). -- **SL:** Band **1,8–2,2×ATR** (Ziel 2,0; `INIT_SL_MIN/MAX_ATR`). **Breakeven ab - +1,3×ATR** (`_BREAKEVEN_ATR`; war 0,6 → 21 % Breakeven-Scratches, gemessen - `backtest_exit.py be`: 1,3 = Plateau-Optimum → Scratch 5 %, Ø-R +15 %, kein - Tail-Risiko). **Früh-Ausstieg = AUS** (`_ADVERSE_EXIT_ATR=0`). Trailing - Init/Trail/Lock. **Teil-Exit AUS** (`_PARTIAL_TP_FRAC=0`, User-Vorgabe) → am TP - wird der **komplette** Trade geschlossen (kein 50 %-Runner mehr). - - **Früh-Ausstieg NUR bei Trades OHNE Signal-Deckung = VERWORFEN (gemessen - `backtest_adverse_nosignal.py`, 2026-07-23, Auslöser −57,34-€-Trade: manueller - LONG gegen WARTEN, KEIN Squeeze/Signal-Flip beteiligt):** Hypothese war, dass ein - Adverse-Exit bei signal-ungedeckten Trades (Totband/WARTEN-Zone, B0 zeigt live - 39 % WR dort) helfen könnte, auch wenn er pauschal auf ALLE Trades schadet. **Fällt - durch — bei JEDER Schwelle (0,5/0,75/1,0/1,5×ATR), in BEIDEN Populationen (SIG/ - NOSIG), fast durchgehend in BEIDEN Hälften negativ** (einzige Ausnahme: SIG H1 - X=1,5 knapp +11 R, aber H2 dort −46). WR bricht von ~38–43 % auf 18–26 % ein — der - Früh-Ausstieg kappt zwar einige Trades vor dem vollen SL, schneidet aber weit öfter - Trades ab, die sich erholt hätten; NOSIG-H2 war ohne Eingriff sogar leicht profitabel - (ΣR +80, PF 1,15) und wird durch jede Adverse-Schwelle verschlechtert. Hypothese - „schwächere Population profitiert eher" widerlegt. Keine Änderung. - - **⚠⚠ TRAIL × BREAKEVEN GEMEINSAM GEPRÜFT = KEINE ÄNDERUNG — und ein - METHODEN-BEFUND, der einen alten Schluss entwertet (2026-08-05, - `backtest_trail_be.py` + `analyze_trail_resolution.py`).** Anlass: User-Frage zur - Kalibrierung „jetzt bei 3 Lot". - ⚠ **Zur Prämisse: die Positionsgröße ändert das Trail-Optimum NICHT.** R ist - ATR-normiert; Lots skalieren nur das EUR-Ergebnis. Gemessen wurde deshalb die - echte offene Frage: **`be=1,3` stammt aus `backtest_exit.py`, als der Trail noch - 1,5 war; `mult` wurde am 31.07. auf 1,0 gesenkt, OHNE das Breakeven neu zu - prüfen.** 2D-Sweep über beide Populationen (2.365 Squeeze- / 1.566 Wellen-Entries), - feste Entry-Listen, kanonischer Exit, Echtkosten, 2 Halbjahre. - **(1) Das Breakeven ist praktisch wirkungslos.** Bei mult ≤ 0,6 sind ALLE - be-Zeilen **identisch** — der enge Trail erreicht den Einstand ohnehin früher als - der Breakeven-Boden. Erst ab mult 0,8 wirkt er überhaupt, und dort ist be=0,6 - marginal besser (+0,019 bzw. +0,018 ØR). ⚠ Das liegt **unter einem Standardfehler** - (≈0,026 bei n≈1.500) → **kein Handlungsbedarf**, be bleibt 1,3. - **(2) Der Trail ist monoton besser, je enger — bis 0,05×ATR.** Und genau das ist - das Problem: ein Trail von 0,05×ATR (≈3 Cent) wäre vom Spread allein ausgelöst. - Ein Optimum, das bis an die physikalische Unmöglichkeit monoton bleibt, misst - nicht den Markt, sondern die Methode. - ⚠⚠ **AUFLÖSUNGS-ARTEFAKT NACHGEWIESEN.** `simulate()` prüft den Stop gegen - Bar-Hoch/-Tief. Ein Trail, der ENGER ist als die typische Intrabar-Spanne, ist - nicht auflösbar — das Modell nimmt den Bar-Hochpunkt als Gewinn mit und steigt - erst im NÄCHSTEN Bar aus. Test mit **in DOLLAR fixiertem** Trail auf M5 gegen M1 - (597 gemeinsame Einstiege): - | Trail | M5 ØR | M1 ØR | Differenz | - |---|---|---|---| - | 0,10×ATR | +0,093 | +0,066 | **−0,027** | - | 0,20×ATR | +0,049 | +0,039 | −0,010 | - | 0,30×ATR | +0,013 | +0,016 | +0,003 | - | 1,00×ATR | −0,046 | −0,010 | **+0,036** | - | 1,50×ATR | −0,038 | +0,028 | **+0,066** | - **Das Vorzeichen kippt bei ~0,3×ATR:** die gröbere Auflösung **überschätzt enge - Trails und unterschätzt weite**. Eine Tick-Simulation würde denselben Weg noch - weiter gehen. ⚠ **Damit ist der Befund vom 31.07. („enger ist monoton besser", - `backtest_trailmult.py`) teilweise entwertet** — er ruht auf derselben - M5-Auflösung. Die damalige Entscheidung, NICHT auf 0,5 zu gehen, war trotzdem - richtig, nur aus dem falschen Grund (genannt wurde „Randwert + Slippage"; - der eigentliche Grund ist die Auflösungsgrenze). - ⚠ Nebenbefund gegen meine eigene Slippage-Vermutung: die Trade-ZAHL ist über alle - Multiplikatoren fast konstant (2.108 bei 0,3 gegen 2.083 bei 1,0), nur die - Haltedauer sinkt (12 → 18 min) und der Worst-Case ist überall identisch (−2,44, - der SL deckelt). Die Slippage-Exposition steigt also NICHT dramatisch — das - Auflösungs-Argument trägt, das Slippage-Argument kaum. - ✅ **Ergebnis: Trail 1,0 / Breakeven 1,3 bleiben.** 1,0×ATR entspricht rund einer - M5-Bar-Spanne (die ATR IST die mittlere Bar-Spanne) und ist damit ungefähr die - feinste Einstellung, die diese Simulation ehrlich auflösen kann. Wer enger will, - braucht Tick-Daten — nicht mehr Bars. -- **Trailing-Optimierung geprüft = KEINE Änderung (gemessen, `backtest_trailing.py`, - 2 Halbjahre, echte Phasen-Sim):** Die Live-Config (`_TRAIL_START_ATR=0,3` · - mult 1,5 M5 · Breakeven 1,3) hat die **beste Verlustseite** (bestes H1, Worst nur - −1,5×ATR). Trailing trägt klar: ohne Trailing ΣR −1313/Worst −2,0 vs. Live +1777/ - Worst −1,5 (~+3000 R **und** kleinerer Einzelverlust). **Breiter ziehen** (mult - 2,0–2,5) oder **Trail später starten** (0,3→1,0) macht H1 schlechter/Worst größer; - der frühe Trail-Start ist **netto schützend** (holt „läuft-schief"-Trades billiger - raus). Einzige mithaltende Variante (start0,6) kippte zwischen den Läufen = - Rauschen. → Ausschütteln ist Varianz, nicht Logikfehler. - - **Time-Stop (AKTIV, gemessen `backtest_timestop.py`):** Position wird geschlossen, - wenn sie nach **120 min noch in Phase „Init"** hängt (HW-Profit nie ≥0,3×ATR = - Whipsaw-Opfer; `trailing._TIMESTOP_MIN=120`, 0=aus, `closed_by=timestop`). - Verbessert BEIDE Hälften (H1 +98→+162, H2 +2191→+2298 R), Worst unverändert; - kürzere Fenster (30/60 min) kippten zwischen den Hälften = Rauschen. Dank - Phasen-Ratsche ist „Init nach 2 h" exakt „nie Trail-Start erreicht". - - **ATR-Floor 0,12→0,06 gesenkt (gemessen, `backtest_atrfloor.py`, 2 Halbjahre):** - Die reale Vola fiel unter den alten Floor (H1: **81 % der Signale ATR<0,12** → - Floor band dauernd, Exit-Distanzen künstlich ~1,3–1,6× breit, dazu ~35k - Log-Warnungen je 5-MB-Rotation). Floor 0,06 in **beiden** Hälften besser - (H1 −153→−27 Pts, H2 +512→+554); ganz ohne Floor nur marginal besser → 0,06 als - Schutz behalten. Floor-Warnung jetzt **gedrosselt** (max. 1×/5 min). - - **Befund B — umgesetzt (Exit-Simulation `backtest_exit.py`, Doku - `docs/exit-simulation.md`):** altes Band 1,2–1,5 war zu eng (MAE ~1,7×ATR, - 31–37 % Frühstopps). 2,0×ATR hebt Ø-R +32 %, PF 1,33→1,39, Frühstopps →25 %, - Worst-Case auf 2,0×ATR gedeckelt. Breiter (3–4×ATR) wäre im Schnitt noch - besser, aber Tail-/Drawdown-Risiko → verworfen. ⚠ Da Sizing margin-basiert - (90 %, nicht risiko-basiert), schlägt die SL-Breite voll auf den EUR-Verlust - durch. Qualität-abhängiger SL NICHT belegt (MAE über alle Buckets gleich). - - **S/R-Close (am Level schließen) = pauschal 2× VERWORFEN, als GEMESSENES - Segment aber AKTIV:** Pauschal (`backtest_srclose.py`, Echtkosten, 79k Signale) - verdoppelt Close@Level die WR (35→67 %), verschlechtert aber PF/ΣR in BEIDEN - Hälften (0,78→0,67 · 0,88→0,76) = Gewinner-Kappen. **ABER** (`backtest_srbreak.py`, - 47k Touches, User-Idee „P(Durchbruch)>60 % → laufen lassen"): Basisrate - P(break)=36–37 %; das **Anlauf-Momentum trennt robust in beiden Hälften** — - Kurs KRIECHT ans Level (letzte 6 Bars <0,3×ATR) → P(break) nur 26 %, **Close - schlägt Laufenlassen Ø +0,12 R**; mit Schwung (≥0,3) → laufen lassen (41–43 %). - Volumen/Touch-Zahl trennen NICHT robust. - - **Kalibriertes P(Durchbruch)-Modell (2026-07-13, `analyze_srwaves.py` + - `backtest_srclose_prob.py`):** Containment-Befund — **~68 % der Wellen erreichen - das gegenüberliegende Level, davon brechen nur ~37 % durch → ~63 % prallen ab** - (robust beide Hälften). P(break) ist **vorhersagbar & kalibriert**: Logit auf 4 - Merkmalen (Anlauf-Momentum 6/3 Bars ·d, mit-Trend, Level-Distanz), auf H1 - trainiert, auf H2 **AUC 0,71 + Kalibrierung trifft** (pred 17→21 %, 49→50 %, - 69→76 %). Dominant = 3-Bar-Beschleunigung (`_PB_W` mom3 +0,78). **R-Ertrag - (Echtkosten):** Regel „close wenn P(break) < X" schlägt reines Trailing in BEIDEN - Hälften massiv (+6000/+7600 R); **User-Schwelle 0,60 im Optimum-Plateau (0,50–0,60 - ~gleich), validiert.** ⚠ Impliziert Strategiewechsel: ~90 % der Level-Touches - werden geschlossen (Trendfolger→Level-Scalper) — trägt, weil echte Kosten (0,265× - ATR) + 63 % Containment das Laufenlassen teuer machen. - - **S/R-Close SIGNAL-gegated („bei mit-Trend halten statt schließen") = VERWORFEN - (gemessen `backtest_srclose_signal.py`, 2026-07-22, User-Idee):** Vorschlag war, - den S/R-Close zu unterdrücken, solange die Empfehlung noch in Trade-Richtung zeigt - (halten bis WARTEN). **Fällt klar durch — in BEIDEN Hälften ~−5300…−5700 R - schlechter** als der reine P(break)-Close. Grund/Datenbeleg: ~85 % ALLER Level- - Touches passieren, WÄHREND die (nachlaufende) EMA noch „in Richtung" zeigt - (23,8k/20,1k von 27,8k/26,2k Touches) — genau dort HÄLT das Level aber. Diese - Touches geschlossen = ΣR +11703/+13606, geHALTEN = nur +5978/+8347. Die - Trend-Empfehlung lagt → sie „bestätigt" ausgerechnet am Abpraller. Das kalibrierte - P(break)-Modell (hat „mit-Trend" als Merkmal) macht das schon richtig; es mit dem - groben EMA-Signal zu übersteuern kostet ~⅔ des S/R-Close-Edges. **12. verworfener - Signal-Eingriff.** - - **⚠⚠ AUTO-FLIP-CLOSE = TROTZ NEGATIVER MESSUNG GEBAUT (User-Vorgabe 2026-07-31 - „schließe den Trade automatisch beim Close-Signal"), `[trading] auto_flip_close=true`, - `auto_flip_close_min_r=0.5`.** Dreht die Empfehlung GEGEN die offene Position und - steht sie ≥0,5×ATR im Plus → `trader.close(reason="flip_close")`. - **NEU GEMESSEN mit dem ECHTEN Exit (`backtest_flipclose2.py`, 80k M5, 2 Halbjahre, - Echtkosten, Exit aus `core/exit_model.py`) — bestätigt das alte Urteil, deutlicher:** - | Variante | Δ H1 | Δ H2 | - |---|---|---| - | Flip ab 0,0×ATR | −126 | −221 | - | Flip ab 0,3×ATR | −77 | −216 | - | **Flip ab 0,5×ATR** | **−92** | **−160** ← mildeste, deshalb Default | - | Flip auch im Minus | −205 | −364 | - Basis ohne Flip: H1 ΣR −537 · H2 −140. **JEDE Variante ist in BEIDEN Hälften - schlechter.** Mechanik: Trefferquote steigt 39 → 45 %, Ertrag fällt = - **Gewinner-Kappen** (die nachlaufende EMA dreht oft mitten im Pullback). - ⚠ **Warum überhaupt neu gemessen:** das alte `backtest_flipclose.py` nutzte ein - **VIERTES Exit-Modell** (Trail fest 1,5 · KEINE Lock-Phase · KEIN Time-Stop · - KEIN Initial-TP · `_MAXH` 288 · Phasen über den Bar-Close statt über das - High-Water) — auf dieser Grundlage wäre das Urteil für den Live-Betrieb nicht - bindend gewesen. Für die Neumessung bekam `exit_model.simulate()` einen - **`stop_when`-Hook** (+ `ret_bar`), damit die Phasen-Mechanik NICHT wieder kopiert - werden musste; Rückwärtskompatibilität verifiziert (ohne Hook bitgenau identisch). - **NACHRANG gegenüber dem S/R-Close (gemessen `backtest_exit_combo.py`, - 2026-07-31, `auto_flip_close_subordinate=true`):** Beide Exits konkurrieren um - dieselben Trades — wer zuerst feuert, gewinnt. Die S/R-Freigabe lief OHNE - Flip-Close, die Baseline existierte also nach dem Einbau nicht mehr. Gemessen auf - **IDENTISCHEN Entries** (5 Varianten, 2 Halbjahre, Echtkosten, Exit-Kern): - | Variante | Δ H1 | Δ H2 | S/R-Closes H1/H2 | - |---|---|---|---| - | nur S/R-Close | +59 | −14 | 535 / 284 | - | nur Flip-Close | −1 | −43 | — | - | BEIDE | +56 | −50 | **507 / 252** | - | **BEIDE, Flip nachrangig** | **+62** | **−49** | **516 / 260** | - **Kollision belegt:** der Flip nahm dem S/R-Close 28 bzw. 32 Trades weg (5–11 %) — - genau die, für die dieser validiert ist. Mit Nachrang ist es in BEIDEN Hälften - besser als ohne. Umsetzung: liegt ein S/R-ZIEL ≤0,6×ATR entfernt (dieselbe - Schwelle wie der Live-Hinweis), hält der Flip sich zurück. Mit 4 Szenarien - getestet (in/außer Reichweite · kein Level · Nachrang aus). - ⚠ **Ehrlich dazu:** auch MIT Nachrang kostet der Flip in H2 rund 35 R gegenüber - „nur S/R-Close" (−49 vs −14). Der Nachrang begrenzt den Schaden, er dreht ihn - nicht um. Abschalten des Features bleibt `auto_flip_close=false`. - ⚠ **Methodik-Fehler beim Bau, korrigiert:** der erste Lauf ließ die sequentielle - Sim nach dem Exit bei `xb+1` weiterlaufen — dadurch hatte JEDE Variante eine - ANDERE Trade-Folge (sobald eine früher aussteigt, verschiebt sich alles - Nachfolgende) und die Zahlen waren nicht vergleichbar. Jetzt feste, geteilte - Entry-Liste wie in `backtest_pbreak_rvalue.py`. - Umsetzung `engine._check_flip_close` im `_pos_loop`: nur im PLUS, 1× je Ticket, - Startup-Schonfrist beachtet, Log 🔄 + Telegram, Snapshot `flip_close`/ - `flip_close_min_r`/`flip_close_count`. Mit **10 synthetischen Szenarien** getestet - (beide Richtungen · aus · unter Schwelle · gleichgerichtet · WARTEN · im Minus · - flat · Schonfrist · Ticket-Dedup). Der Alarm (Blinken/Ton/Telegram) bleibt - unabhängig davon. **B4/B5-Pflicht: Live-Ertrag gegen die Erwartung halten — - driftet es weg, `auto_flip_close=false`.** - - **Auto-Flip-Close = VERWORFEN (gemessen `backtest_flipclose.py`, 2 Halbjahre, - sequentielle 1-Positions-Sim, 2026-07-16):** User-Taktik-Kern „Position im Plus - automatisch schließen, wenn die Empfehlung dreht" (heute nur Alarm). KEINE Variante - (minR 0/0,3/0,5×ATR, auch-im-Minus) hält in beiden Hälften ØR & ΣR ≥ Basis: - H1 ΣR −278→−280…−417, H2 **+174→+119…+144** (kostet 17–32 % des Ertrags). - Mechanik: WR steigt 43→50 %, Ø-Gewinn schrumpft = **Gewinner-Kappen** — die - nachlaufende EMA dreht oft mitten im Pullback, der Trade wäre weitergelaufen. - Flip bleibt **Alarm + menschliche Entscheidung** (User-Übersteuerungen 68 % WR). - Der Rest der User-Taktik (Ziel=nächstes S/R, weiterlaufen wenn stark) IST der - P(break)-Auto-Close; der „3 %-Margin-Anker" = EUR-Schwelle, Klasse „verliert". - - **⚠ NACHGEMESSEN 2026-08-04 mit dem NACHTRAINIERTEN Modell — Ablehnung BESTÄTIGT, - deutlicher als zuvor** (User-Frage „es werden immer noch Mini-Trades mit geringen - Gewinnen geschlossen, wie verhindern wir das?"). Δ ΣR gegen minR=0: - | minR | H1 | H2 | - |---|---|---| - | 0,1×ATR | −6 | +10 | - | 0,2×ATR | **−720** | −42 | - | 0,3×ATR | −1136 | −157 | - | 0,5×ATR | −1998 | −683 | - | 0,8×ATR | −2887 | −1577 | - **Monoton schlechter in BEIDEN Hälften.** - ⚠⚠ **Und die Prämisse der Frage stimmt so nicht.** Auswertung der 295 Trades seit - 20.07.: die 28 Gewinne ≤2 € bringen zusammen **+21,11 €** = **0,8 %** der - Brutto-Gewinne (2.770 €) — sie sind kein Ertragsproblem. Entscheidend ist, WOHER - sie kommen: - | Schließgrund | Gewinner | Median | davon ≤2 € | - |---|---|---|---| - | `sr_close` (Bot) | 81 | **+14,65** | 10 (+7,65) | - | `manual` (User) | 64 | **+6,37** | **18** (+13,46) | - **Zwei Drittel der Mini-Gewinne sind manuelle Closes**, und der manuelle - Median-Gewinn ist **weniger als die Hälfte** des Bot-Medians. Die Klein-Closes des - BOTS sind gemessen richtig; die eigentliche Asymmetrie liegt im diskretionären - Früh-Kappen — genau die dokumentierte Kern-Leckage (B0-Checkliste: „Gewinner läuft - das Trailing"). Ein Code-Gate am Bot-Exit adressiert das falsche Ende. - ⚠⚠ **KONTRAFAKTISCH NACHGERECHNET (`analyze_manual_close.py`, 2026-08-04) — DIE - „FRÜH-KAPPEN"-HYPOTHESE IST WIDERLEGT.** Für jeden real geschlossenen Trade wurde - ab dem echten Einstieg der kanonische Exit simuliert (EUR-Faktor 87,6 €/$/Lot, - robust aus 251 Trades geschätzt — `trader.pnl` enthält Swap, eine direkte - Rückrechnung wäre verfälscht): - | Schließgrund | n | IST | Bot-Exit | Δ | - |---|---|---|---|---| - | `manual` Gewinn | 143 | **+1428** | +962 | **−467** | - | `sr_close` Gewinn | 106 | +1591 | +1060 | −530 | - | `emergency` Verlust | 98 | −1750 | −1235 | +515 | - | **`sl` Verlust** | **69** | **−1926** | **−697** | **+1229** | - **Das diskretionäre Früh-Kappen kostet NICHTS — es bringt +467 € MEHR** als der - kanonische Exit gebracht hätte; in **62 %** der Fälle war der manuelle Close - besser. Dasselbe gilt für den S/R-Auto-Close (−530). Beide realisieren Gewinne - besser, als ein reines Trailing es täte. - ✅ **Die Leckage liegt auf der VERLUSTSEITE:** die 69 per Broker-SL geschlossenen - Trades hätten mit dem kanonischen Exit **1.229 € weniger** verloren (−697 statt - −1926), die 98 Notfall-Closes 515 € weniger. Über ALLE Trades summiert wäre der - kanonische Exit rund **+700 €** besser gewesen — praktisch vollständig aus der - Verlustseite. - ⚠ **Confounder, der das Ergebnis begrenzt:** `simulate()` setzt den Initial-SL auf - **2,0×ATR_M5**, live kommt er aus **ATR_M15** (`_calc_sl_tp`) und ist damit oft - deutlich weiter — unter Margin-Sizing schlägt das voll auf den EUR-Verlust durch. - Der +1.229-Wert misst also zu einem erheblichen Teil **SL-Breite**, nicht - Exit-Logik. `backtest_sl_tf_mismatch.py` hat die Umstellung auf M5-ATR bereits - geprüft und wegen H2 verworfen — dieser Befund gibt Anlass, das mit dem - kanonischen Exit ZU WIEDERHOLEN. - ⚠ Zweite Grenze: der Vergleich gilt **pro Trade**, nicht als Kontostand- - Hochrechnung — ein länger laufender Trade hätte den Positions-Slot belegt und - Folge-Trades verhindert. - ✅ **NACHGEMESSEN (`backtest_sl_width.py`, 2026-08-04): der Confounder war die - Erklärung — aber das Geld ist NICHT einfach zu holen.** Gegatete Live-Population - (1.224 Entries, Reversal aus, k=0,3), kanonischer Exit, variiert wird - AUSSCHLIESSLICH die Breite des Initial-SL: - **ATR_M15 / ATR_M5 = 1,80 im Median** → der Live-SL (2,0×ATR_M15) ist rund - **3,6×ATR_M5** breit, nicht 2,0. Genau daraus entstanden die 1.229 €. - | SL-Basis | H1 ØR / ΣR | H2 ØR / ΣR | Worst H1/H2 | - |---|---|---|---| - | 2,0×ATR_M5 | −0,092 / −44 | −0,024 / −18 | −2,33 / −2,29 | - | **2,0×ATR_M15 (LIVE)** | −0,145 / −69 | **+0,064 / +48** | **−4,77 / −5,64** | - | 1,5×ATR_M5 | −0,081 / −38 | −0,038 / −28 | **−1,83 / −1,85** | - | 2,5×ATR_M5 | −0,121 / −58 | −0,013 / −10 | −2,83 / −2,79 | - | 3,0×ATR_M5 | −0,130 / −62 | +0,018 / +13 | −3,33 / −3,29 | - **KEINE engere Variante schlägt den Live-SL in BEIDEN Hälften** — H2 bevorzugt den - weiten Stop klar (+48 gegen −18). Die vorab fixierte Regel („nur wenn beidhälftig - mehr ΣR UND Worst-Case nicht schlechter") ist damit **nicht erfüllt: keine - Änderung.** Das **reproduziert `backtest_sl_tf_mismatch.py`** (2026-07-23, dort auf - Squeeze-Entries mit eigenem Exit-Nachbau) jetzt auf der gegateten Population mit - dem kanonischen Exit — der alte Schluss war korrekt. - ⚠⚠ **Der echte Unterschied ist das TAIL-RISIKO, und das ist eine SIZING-Frage:** - Worst-Case **−5,64×ATR** (live) gegen **−1,85×ATR** (bei 1,5×ATR_M5) — Faktor 3. - Bei ATR 0,30 sind das ~1,7 $ Bewegung; unter 95 %-Margin-Sizing auf ~800 € Konto - ist ein einzelner solcher Trade rund **19 % des Kontos**. Der weite Stop kauft die - H2-Performance mit genau diesem Tail. Damit landet die Frage dort, wo CLAUDE.md sie - ohnehin verortet: **„Der Hebel ist Sizing, nicht der Exit."** Wer den Tail begrenzen - will, senkt `margin_buffer_pct` — nicht die SL-Breite (die kostet gemessen H2). - - **Mindestgewinn für den S/R-Close = VERWORFEN (gemessen `backtest_srclose_prob.py`, - Mindestgewinn-Sektion):** „nur closen wenn ≥minR im Plus" verliert **monoton in - BEIDEN Hälften** (minR 0,2: −444/−213 · 0,5: −1425/−854 R vs minR=0). Die vielen - kleinen Gewinne SIND der Edge — übersprungene Klein-Closes prallen am (haltenden!) - Level ab und enden schlechter. Kleine EUR-Beträge = Positionsgröße, kein Exit-Thema. - 2 Halbjahre, Echtkosten):** LONG an Support / SHORT an Resistance, gegated mit - P(Level hält), Ziel = Gegenlevel — **verliert in BEIDEN Hälften** (FIX H1 −3393/ - H2 −2242, +Trailing/-nur-Trailing ebenso negativ; WR 46–53 %, PF<1). **Asymmetrie: - P(break) taugt zum EXIT** (Trade schon mit-Trend im Plus am Gegenlevel → kassieren), - **nicht zum ENTRY** (antizyklisch am Extrem = Münzwurf-Richtung, 6× belegt). Einstieg - bleibt mit-Trend (Pullback). **Trailing bleibt** — nicht mehr Standard-Exit (das ist - der S/R-Close), aber Manager für die ~10 % Durchbruch-Runner (P≥Schwelle) + Fallback - für Trades ohne erreichbares Gegenlevel. - - **Umsetzung:** `engine._p_break(mom6,mom3,wt,dist)` (eingebettete `_PB_MU/SD/W`); - `wave_rec.snapshot` liefert **`pb_feats` {mom6, mom3, ema_diff, atr} — FEST aus M5** - (aus dem ~15-s-Turn-Fetch gecacht, ATR-Floor 0,12 wie im Training). ⚠ **Bugfix - 2026-07-13:** vorher kamen die Features aus der Wave-TF (Heuristik bis M30 → mom6 - war 3-h- statt 30-min-Momentum, ATR 2–3× → P falsch skaliert) und das Level aus dem - Misch-Set `_sr_levels` (Zonen/Gaps/Tageslevel). **2. Bugfix gleicher Tag:** die - M15-Cluster-Level (`market.sr`, SR_MIN_TOUCHES) sind in frischen Trends oft LEER → - Hint verschwand komplett. Jetzt **trainingsgleiche Level**: `wave.pb_levels` = rohe - **M5-Pivots (k=3, ~300 Bars)** aus dem Turn-Fetch (M5 dafür 320 Bars tief), Highs - in `_m5_hl` gecacht. Am Rally-Hoch gibt es für LONGs korrekt KEIN Gegenlevel → - kein Hint (nichts zu closen). ⚠ **3. Bugfix 2026-07-15:** das Close-Level kommt - jetzt aus **`_draw_levels`** (dieselbe geclusterte+Hysterese-Linie wie Chart & - Dashboard) statt aus den rohen `pb_levels` — sonst feuerte die Meldung an einem - anderen Level als die gezeichnete Linie (User: „passt nicht zusammen"). P(break)- - Features (M5) unverändert, nur `level`/`dist` verschieben sich um Cents. - **`_sr_close_hint`** (Anzeige ≠ Auslösung, Fix - 2026-07-14 — das 0,15×ATR_M5-Fenster ≈ 0,03 $ war als reine Anzeige unsichtbar - klein): Hinweis erscheint ab **≤0,6×ATR Annäherung** (blaue „nähert sich"- - Vorwarnung mit Live-P(break) + `gap_atr`); **Auslösung `close=true` nur AM Level** - (≤0,15×ATR = gemessener Touch) und `p_break < [trading] sr_close_pbreak` - (Code-Default 0,60, Band 0,30–0,90; **ini seit 2026-07-24 = 0,55** auf User-Wunsch — - liegt im validierten Optimum-Plateau 0,50–0,60, Hysterese-Zone damit 50/60). - Snapshot `sr_close_hint {level, p_break, - at_level, gap_atr, close, confirm}`. Hinweis existiert NUR mit offener Position. - **`confirm` = Bruch-BESTÄTIGUNGS-Preis** (Fix 2026-07-22, User-Frage nach einem - Trade, der am Level geschlossen wurde, obwohl der Kurs „durchgebrochen" war): - `level ± 0,5×ATR` in Trade-Richtung — **dieselbe Definition wie beim Modell- - TRAINING** (`_BRK_ATR=0,5`/`_BRK_W=12` in `backtest_srclose_prob.py`/ - `analyze_srwaves.py`). Ein Docht, der die nackte Levelzahl nur um wenige Cent - überschreitet, zählt NICHT als Durchbruch — reales Fallbeispiel 2026-07-22: - Close bei Level 85,943 (P(break) 31 %), Kurs wischte auf 85,994 (< Bestätigung - 86,017 = Level+0,5×ATR) und fiel zurück → nach Modell-eigener Definition eine - ABLEHNUNG, keine Bestätigung; die Vorhersage war korrekt. `confirm` jetzt in der - Log-Zeile („— Bestätigung erst ab X") UND im Frontend-Hinweistext (`#pos-srhint`, - alle 3 Zweige: Vorwarnung/Close/Laufen) sichtbar — macht den Unterschied - „Kurs > Levelzahl" vs. „strukturell bestätigter Bruch" auf einen Blick klar. - **Live-Prognose-Tracking (2026-07-23, User-Wunsch „mitloggen wie oft richtig/ - falsch"):** jeder frische Level-Touch (`at_level` False→True) loggt EINMAL die - aktuelle Vorhersage (`bounce` wenn P(break)0`) wenn `close=True`, 1×/ - Ticket, wirkt bei gesperrtem Handy. **Optionaler Mindestgewinn** (`[trading] - sr_close_min_gain` Default 0=aus · UI `#srmin-input` · `POST /api/srclose_min` · - `set_sr_close_min_gain` · Snapshot `sr_close_min_gain`): unter der EUR-Schwelle - wird am Level NICHT geschlossen. ⚠ Gemessen ist OHNE besser (Verworfen-Eintrag) — - Opt-in auf User-Wunsch, UI-Status warnt. **Automatisch auf X % des Einsatzes - (`[trading] sr_close_min_gain_pct`) — Stand 2026-07-29 wieder AUS (`=0`, User-Wunsch - „gewinn close und notfall stop wieder auf leer beim Eröffnen")** (Verlauf: 1,0→3,0 - am 07-23 · 0 am 07-24 früh „nichts beim Öffnen setzen" · 3,0 am 07-24 abends zurück · - **0 am 07-29** — gilt NUR für den Gewinn-Close, der Auto-Notfall-Stop bleibt AUS). - Bei `pct>0` wird bei JEDER echt neuen Position - (nicht beim Wiedererkennen nach Neustart) `sr_close_min_gain` frisch auf - `pct% × Margin dieser Position` gesetzt (`_check_auto_close`, retry-sicher via - `_sr_close_min_gain_armed_ticket` — Margin aus `trader.refresh()` im selben - `_pos_loop`-Tick, jeden Tick erneut versucht bis Margin>0; persistiert via - `_save_runtime_state`, Log „Gewinn-Close armiert … +X (N% Margin Y)"). Bei `pct=0` - wird beim Öffnen NICHTS gesetzt — dann bleibt der manuell im UI gesetzte/gemerkte - `sr_close_min_gain` maßgeblich. ⚠ **Umstellen auf 0 braucht Neustart:** `_sr_close_ - min_gain_pct` lebt im Speicher; solange der alte Wert (3,0) läuft, re-armt JEDER neue - Trade erneut — die ini-Änderung greift erst nach `restart_server.bat`. Beim 0-Stellen - zusätzlich den persistierten Alt-Wert in `runtime_state.json` nullen (`sr_close_min_gain`) - UND `emergency_state.json` `loss` (Erb-Falle, sonst restauriert der Neustart den Alt-Wert). - **Gate verifiziert (2026-07-23, User-Nachfrage „darf nicht schließen bevor der - Wert erreicht wurde"):** `_check_sr_close` prüft `pnl < sr_close_min_gain → return` - **bevor** `_sr_close_hint`/`trader.close` überhaupt aufgerufen werden — isoliert - mit 6 synthetischen Szenarien getestet (unter/exakt-auf/über Schwelle, P(break) - hoch trotz Profit, Schwelle aus, Verlust-Trade) → alle korrekt. **Dabei echten - Bug gefunden & gefixt:** die 1%-Berechnung lief nur EINMALIG beim Ticket-Wechsel - (`if ticket != self._last_pos_ticket`); war `order_calc_margin` genau in diesem - einen Tick 0/nicht verfügbar (Broker-Race direkt nach Order-Fill), blieb - `sr_close_min_gain` für die GESAMTE Laufzeit der Position auf dem STALEN Wert der - vorherigen Position hängen (der Ticket-Wechsel-Zweig feuert nur 1×, keine zweite - Chance) — im ungünstigen Fall ein zu NIEDRIGER Alt-Wert, der den Trade dann exakt - zu früh schließt. **Fix:** neuer State `_sr_close_min_gain_armed_ticket` - (getrennt vom Notfall-Stop/TP-Restore-Zweig) — die %-Berechnung läuft jetzt JEDEN - `_pos_loop`-Tick erneut, bis sie für das aktuelle Ticket einmal erfolgreich war - (Margin>0), statt nur im Ticket-Wechsel-Moment. Wiederhergestellte Positionen - nach Neustart markieren sich sofort als „armiert" (kein Überschreiben des - persistierten Werts) — mit Test verifiziert. Log `🎯 S/R-AUTO-CLOSE` + Telegram + Toast - (`sr_close_count`), `closed_by=sr_close`. UI Position-Kachel: bernstein „Durchbruch - nur X% → Bot schließt" bzw. grün „Durchbruch X% wahrscheinlich → läuft weiter". - Abschalten `auto_sr_close=false` / `set_sr_autoclose(False)`; Schwelle via - `sr_close_pbreak`. Kein pauschaler S/R-Close (2× verworfen) — nur mit P(break)-Gate. - - **S/R-verankertes TRAILING = GEMESSEN, NICHT gebaut (`backtest_trailing_sr.py`, - 2026-07-31, User-Idee „Trailing TP und SL auf die S/R-Linien anpassen"):** Die Idee - zerfällt in drei Teile — Initial-SL an S/R (schon verworfen, s. u.), fester TP am - Gegenlevel (schon 2× verworfen, `backtest_srclose.py`) und **Trailing-SL ans Level - nachziehen** = der einzig offene Teil (bisher wurden nur ATR-Abstände variiert). - Aufbau identisch zu `backtest_trailing.py` (echte Phasen-Mechanik, Breakeven 1,3), - Level = **M30-Pivots k=3, kausal** (Pivot erst 3 M30-Bars später bekannt), Deckel - **[0,5…2,0]×ATR** gegen den −11,4×ATR-Fall; 80k Bars, ~19,7k Signale je Hälfte. - **Ergebnis: schadet nicht, trägt aber zu wenig.** Basis (ATR-Trail 1,5) H1 −0,020 / - H2 +0,064; **3 von 5 Varianten schlagen sie in BEIDEN Hälften** (Puffer 0,10: - +0,009/+0,004 · Puffer 0,50: +0,003/+0,014 · **Hybrid „engerer von beiden": - +0,006/+0,006**, WR 37→43 % bzw. 40→43 %). **Trotzdem nicht gebaut**, zwei Gründe: - (a) **Effektgröße 0,006–0,014 R/Trade** = eine Größenordnung unter der B3-Hürde - (~0,1×ATR netto); (b) **nicht parameter-robust** — Puffer 0,10 besteht, **0,25 fällt - durch** (H1 −0,023), 0,50 besteht wieder; ein Parameter, dessen Nachbar das Vorzeichen - kippt, ist Rauschen (dasselbe Kriterium, an dem ORB und die SMC-Sweeps starben — - beim Squeeze waren ALLE 8 Kombis positiv). ✅ Positiv-Befund: **der Worst-Case bleibt - in ALLEN Varianten −2,00×ATR** — der Deckel funktioniert, S/R-Verankerung fügt kein - Tail-Risiko hinzu (das war die Sorge aus `backtest_sl_method.py`). ⚠ **Die - KONTROLLVARIANTE „TP am Gegenlevel" reproduziert den alten Befund exakt** (WR 40→54 %, - ΣR H2 +1261→+740 = Gewinner-Kappen) — damit ist die Simulation gegen - `backtest_srclose.py` validiert und der TP-Teil endgültig erledigt. - **NACHTEST auf NUR Squeeze-Entries (`--squeeze`, 2026-07-31, Echtkosten): ERLEDIGT — - dort ist der S/R-Trail nicht nur zu klein, sondern klar SCHLECHTER.** Basis - H1 +0,102/H2 +0,108; **keine einzige Variante besteht** (S/R 0,10: +0,096/+0,093 · - 0,25: +0,083/+0,098 · 0,50: +0,052/+0,092 · Hybrid: +0,093/+0,122 = kippt · - Hybrid weit: +0,091/+0,084). Anders als im Wave-Lauf ist das **kein Rauschen**: der - reine S/R-Trail wird **monoton schlechter, je breiter der Puffer** (in BEIDEN - Hälften) — sauberer monotoner Verlauf, nur gegen die Idee. **Mechanismus:** der - Squeeze lebt von wenigen Läufern (43 % Treffer, Ertrag im Tail); ein an das - nächstgelegene Level gebundener Stop holt genau dort raus, wo der Lauf beginnt. Bei - Wave-Signalen fällt das kaum auf (selten große Runner) — daher das umgekehrte - Vorzeichen zwischen beiden Läufen. ⚠ **Stärkster Gewinner-Kappen-Beleg des - Projekts:** die Kontrollvariante (fester TP am Gegenlevel) dreht den Squeeze von - **+0,102/+0,108 auf −0,066/−0,004** = Edge vollständig zerstört. ⚠ **Abgrenzung - gegen den laufenden S/R-Auto-Close:** der ist **P(break)-gegated** (schließt nur, - wenn das Level wahrscheinlich HÄLT) und war auf Squeeze-Trades separat gemessen ein - **Wash** (`backtest_squeeze_srclose.py`, +0,190→+0,224 / +0,394→+0,377). **Das Gate - ist der Unterschied zwischen „Wash" und „Edge zerstört"** — ein ungegateter Level-TP - ist etwas grundlegend anderes. Damit ist die User-Idee in allen DREI Teilen - durchgemessen; der Live-Exit-Stack bleibt unverändert. - - **⚠⚠ SL-BASIS M5 vs. M15 auf der NEUEN Level-Entry-Population (gemessen - `backtest_sl_basis.py`, 2026-08-05, User-Frage „wäre M5-ATR vertretbar?") — - DIFFERENZIERT, und dabei eine Mess-Betriebs-Lücke gefunden.** - ⚠ **Zuerst der strukturelle Fund:** `exit_model.simulate()` nimmt **EINEN** `atr` - für SL, TP, Trail und Breakeven — und **alle Backtests übergeben ATR_M5**. Live - kommt der SL dagegen aus **ATR_M15** (`mt5_utils.atr_value` mit `SL_TF`), im - Median **1,80×** so groß. Praktisch jede Projekt-Messung modelliert damit einen - SL von 2,0×ATR_M5, während live **~4,0×ATR_M5** gilt. Einzige bisherige Ausnahme: - `backtest_sl_width.py` (04.08.), das genau das explizit verglichen hat. - **Ergebnis auf Level-Entries (80k, 2 Halbjahre, nur die SL-Basis variiert):** - | Population | Variante | H1 ØR / Worst | H2 ØR / Worst | - |---|---|---|---| - | **SQUEEZE** | LIVE (≈4,1×ATR_M5) | +0,455 / **−5,87** | +0,614 / **−8,83** | - | | 2,5×ATR_M5 | +0,459 / −2,82 | +0,620 / −2,94 | - | | 2,0×ATR_M5 | +0,452 / −2,32 | +0,610 / −2,44 | - | **WELLE** | LIVE (≈3,9×ATR_M5) | **+0,371** / −4,97 | **+0,356** / −6,00 | - | | 2,5×ATR_M5 | +0,362 / −2,79 | +0,343 / −2,72 | - | | 3,0×ATR_M5 | +0,358 / −3,29 | +0,352 / −3,22 | - ✅ **Ertragsseitig ist es ein WASH auf der Squeeze-Population** — alle Varianten - liegen innerhalb eines Standardfehlers (≈0,035 bei n≈800), und die einzige Zelle, - die den Live-Stand in ØR beidhälftig schlägt (2,5), hat **schlechtere Nachbarn** - → nach der Nachbar-Regel Rauschen. **Auf der Wellen-Population ist der weite - Live-SL dagegen in BEIDEN Hälften der beste** — jede Verengung kostet dort. - ⚠⚠ **Der EINZIGE robuste Unterschied ist das TAIL-RISIKO — und der ist groß und - monoton:** Worst-Case **−8,83×ATR** (live, Squeeze H2) gegen **−2,94** bei - 2,5×ATR_M5, also **Faktor 3**. Bei 3,5 Lots und ATR_M5 0,33 sind das ~**−890 €** - gegen ~−300 € im einzelnen schlechtesten Trade. - ✅ **Antwort: „vertretbar" ja — aber es ist keine Ertrags-, sondern eine - Risiko-Entscheidung.** Für den Squeeze kostet die Verengung gemessen nichts und - drittelt den Tail; für Wellen-/Signal-Trades kostet sie Ertrag in beiden Hälften. - Eine saubere Umsetzung wäre **quellen-abhängig** (`_calc_sl_tp` nutzt M5-ATR nur - bei `source="auto_squeeze"`) — strukturell begründet (Breakout- gegen - Pullback-Einstieg haben verschiedene Nachlauf-Dynamik), nicht data-mined. - ✅✅ **GEBAUT & LIVE (2026-08-05, User-Entscheidung „für Squeeze-Trades: ja").** - `trader._calc_sl_tp(sym, otype, entry, atr_tf=None)` bekommt die ATR-Zeitebene als - Parameter (Default `SL_TF` = M15); `_send`/`_send_locked`/`open_long`/`open_short` - und `place_stop` reichen sie durch. Gesetzt wird sie an genau zwei Stellen: - `engine._open` (`source == "auto_squeeze"` → M5) und `_manage_squeeze_pending` - (`quelle == "squeeze"` → M5). **Alles andere — manuelle Trades, Auto-Signal — - bleibt unverändert bei M15.** - ⚠ **Die Multiplikatoren sind NICHT angefasst** (1,8–2,2×ATR): nur die Zeitebene - wechselt, aus der der ATR kommt. Der Log macht es sichtbar („… ATR aus M5"). - ⚠⚠ **DER TP IST BEWUSST NICHT BETROFFEN** (User-Vorgabe: „nur für SL, der TP soll - mit dem Runner mitwachsen"). Geprüft statt angenommen: der von `_calc_sl_tp` - zurückgegebene TP wird von **KEINEM** Aufrufer gesendet — `_send_locked` und - `place_stop` setzen ausschliesslich `req["sl"]`. Das Ziel verwaltet - `core/trailing.py` dynamisch (`_TP_INIT_ATR`=3,5 × ATR der **Wellen**-Zeitebene) - und zieht es mit dem Lauf nach. Ein Warnkommentar im Code hält fest, warum der TP - dort nicht mit in die Order darf: er wäre sonst an die SL-Breite gekoppelt und - würde genau die Runner kappen, von denen der Squeeze lebt (43 % Treffer, Ertrag - im Tail). - Mit 4 Szenarien getestet (M5 kappt auf 2,2×ATR_M5=0,66 · M15 greift auf den - Mindest-Deckel 1,8×ATR=0,99 · M5 enger als M15 · SHORT spiegelbildlich). - ⚠ Beim Testen eine eigene Fehlannahme korrigiert: ohne Pivot greift der - 1,2-%-Fallback, der beim M15 vom **Mindest**-Deckel aufgeweitet wird — die - 2,2×-Obergrenze wird dort gar nicht erreicht. - Zurück: die beiden `mt5.TIMEFRAME_M5`-Zuweisungen in `engine.py` auf `None`. - ⚠ Das reproduziert NICHT `backtest_sl_tf_mismatch.py` (23.07., dort war M5 in H2 - schlechter): dort wurde am Bar-Close eingestiegen, hier am Level. Der - Einstiegspreis hat den Befund gedreht. -- **S/R-verankerter SL = KEIN Edge (gemessen, `backtest_sl_method.py`):** - Pivot+Band vs. „S/R ∓ ATR/0,10-Puffer" vs. fest 2,0×ATR — **gedeckelt alle - identisch** (Ø-R 0,196–0,201, PF 1,38–1,39, Worst −2,2×ATR): der **Band-Cap - [1,8–2,2]×ATR dominiert**, die Verankerung ist Rauschen. Nur **ungedeckelt** - sieht S/R besser aus (Ø-R 0,249) — aber Worst-Case **−11,4×ATR** (Kontokiller - bei 90 %-Sizing) → verworfen. Aktuelle SL-Logik bleibt. - - **SL-ATR-Timeframe bei Squeeze-Trades (M5-Signal, aber SL_TF fix M15) = GEPRÜFT, - KEINE Änderung (gemessen `backtest_sl_tf_mismatch.py`, 2026-07-23, Auslöser - −89,36-€-Nacht-Trade):** `_calc_sl_tp` sizt den Initial-SL IMMER aus M15-ATR, - unabhängig vom Signal-TF — bei Squeeze (M5-Signal) kann das stark divergieren - (real: M5-ATR fiel 0,29→0,10 — genau die Kompression, die den Squeeze auslöste — - während M15-ATR noch bei ~0,36 lag → SL 0,787 statt ~0,22 bei M5-Sizing). Der - validierte Squeeze-Backtest (`backtest_breakout_squeeze.py`) nutzt durchgehend - M5-ATR für den SL — Live weicht hier vom Backtest ab. **2-Stichproben-Test der - Umstellung auf M5-ATR-SL bei Squeeze-Trades: GEMISCHT** — H1 leicht besser - (ØR +0,167 vs +0,064, ΣR +35 vs +13), H2 (größere, aktuellere Stichprobe) klar - SCHLECHTER (WR 51 % vs 59 %, ΣR +281 vs +323) → fällt nach der beidhälftig- - robust-Regel durch. Der breitere M15-SL kassiert gelegentlich einen vollen - Verlust wie diese Nacht, verhindert aber mehr vorzeitige Ausstoppungen bei - normalen Rücksetzern — netto vorteilhaft. **Keine Änderung.** -- **Inter-Market (Brent-Bestätigung / DXY-Gegenwind) = VERWORFEN (gemessen - `backtest_intermarket.py`, 2026-07-19, letzte ungetestete Indikator-Klasse):** - 1366 Trades, seq. Sim, Live-Exit, Echtkosten, 2 Halbjahre; Buckets nach Zustand - am Entry (Symbole: `SpotBrent`, `USDX`). **Beide Hypothesen kippen zwischen den - Hälften:** (A) Brent-Trend dagegen H1 −0,002 vs H2 −0,187 (H1 sogar BESSER als - „dafür"!), Brent-Mom-1h dagegen H1 **+0,311**/H2 −0,233 = Flip; (B) DXY-Gegenwind - H1 −0,13…−0,19 (schlechter, wie vermutet) aber H2 **+0,13…+0,17 = BESSER als - Rückenwind** — komplett invertiert, klassische Regime-Abhängigkeit (wie H1-Stärke- - Filter/Momentum). Einzig Brent-Mom-3h ist richtungs-konsistent, aber winzig - (Δ~0,06–0,14) und die Schwester-Varianten kippen → nach der vorab festgelegten - Verdict-Regel (beide Hälften UND Varianten-robust) **kein Einbau**. Damit ist auch - die letzte orthogonale Indikator-Klasse durch: **am Signal ist nichts mehr zu - holen — Hebel bleiben Sizing/Exit/Verhalten** (nicht gegen das Signal handeln: - live gemessen 39 % WR / −166 € vs. mit 55 % / +65 €). - `backtest_confluence.py` — MACD/ADX-Richtung praktisch neutral, ADX-Stärke leicht - negativ, ein MACD+ADX-Gate senkt den Edge (−78 % Trades). RSI *bestätigend* ist - kontraproduktiv (RSI *gegen* Richtung = doppelter Edge = der Pullback-Effekt, den - der „⭐ tiefer Pullback"-Bonus schon nutzt). Standard-Oszillatoren also nicht - draufsatteln. Mehrquellen-Redundanz läuft bereits über die Gesamtempfehlung. -- **TradersUnion (TU) aus der Empfehlung ENTFERNT (User-Vorgabe 2026-07-06):** TU ist - Standard-Indikator-Konsens (MA/Oszillator-Ratings einer Website) — **dieselbe Klasse - wie MACD/ADX/RSI = kein Edge**, plus **Latenz** (5m „Strong Buy" während 4h/1d „Strong - Sell") und **nicht backtestbar** (Live-Scrape, keine History). Floss vorher als - Konfidenz-Faktor (`_tu_check` ±6/−8 je 5m/15m) UND als Verdict-Stimme ein — **beides - neutralisiert** (`wave_rec._build` zieht `_tu_check` nicht mehr; `_verdict` ohne - TU-Vote). ⚠ Nebenbefund: der Konfidenz-Pfad hatte **keinen** `_STALE_S`-Check (nur - `trade_signal` hatte ihn) — mit der Entfernung erledigt. TU bleibt **reine Anzeige** - (Snapshot `tu`); `_tu_check`/`_TU_*` als Code erhalten (reaktivierbar). Fetch war - auf 5 min gedrosselt (`_TU_S=300`); **Loop seit 2026-07-19 ganz stillgelegt** - (`_tu_loop` nicht mehr in `start()` — TU ist aus Empfehlung/Verdict/Copilot raus - UND die Kachel ausgeblendet, der Scrape lief für niemanden; reaktivieren = Loop - wieder in die Liste). Elliott loggt nur noch **bei Änderung** (vorher - ~6k identische Zeilen/Logrotation). -- **Order ohne Empfehlung = bestätigbarer HINWEIS, kein Block:** Bei WARTEN/ - Gegen-Signal zeigt das Frontend einen **Bestätigungs-Dialog** („… trotzdem - eröffnen?") — bestätigen = Trade läuft, Abbrechen = kein Trade. **Kein** hartes - Blockieren (überschreibbar). `engine._open` blockt nicht (loggt nur). - ⚠ **Log-Quellen-Attribution (Fix 2026-07-29):** `_open(fn, want, source)` bekommt - jetzt eine `source` (Default `"manuell"`); der Auto-Squeeze ruft `open_long/short - (source="auto_squeeze")`. Vorher schrieb `_open` bei JEDEM Auftrag gegen die - Empfehlung „— vom User bestätigt" — auch bei autonomen Squeeze-Entries (der User - fragte, ob ein Trade auto/manuell war; die Zeile log fälschlich „User"). Jetzt: - autonome Orders → „autonom vom Auto-Squeeze eröffnet", manuelle → wie bisher. Die - `🚀 AUTO-SQUEEZE-ENTRY`-Zeile bleibt die eindeutige Quelle der Wahrheit. -- **Entry-Checkliste im Order-Dialog (`engine._entry_checklist`, User-Vorgabe - 2026-07-30 „blende einen Hinweis ein wenn die Checkliste gegen meine Eröffnung ist", - v=120):** Vor JEDER manuellen Eröffnung prüft der Server 7 Punkte mit LIVE-Daten und - zeigt die verletzten im Bestätigungs-Dialog. **Blockt nicht**, macht aber sichtbar, - was dagegen spricht — und zwar **auch dann, wenn ein Signal vorliegt** (der alte - Dialog kam nur bei WARTEN/Gegen-Signal). Punkte: (1) **Signal-Deckung** (die - gemessene Kern-Leckage), (2) **HTF-Trend M30+H1** („nur mit dem HTF-Trend, niemals - dagegen"), (3) **Entry-Raum** zum Gegenlevel (<0,6×ATR = gemessen negativ), - (4) **News-Konflikt** (|Sentiment|≥0,5 gegen die Richtung), (5) **Kosten** - (Spread/ATR, ab 0,32 = Kostenfalle), (6) **Mental-Check: Verlust-Serie heute** - („nach 2 Losses Session beenden" — zeigt Anzahl, Tages-P&L und %-Anteil am Konto), - (7) **Nacht-Stunde** 0–7 Uhr. `verdict`: `stop` (≥2 „fail") · `warn` · `ok`. - Snapshot `checklist {long, short}` — nutzt nur bereits berechnete Snapshot-Teile; - die DB-Abfrage für die Verlust-Serie ist ~120 s gecacht (`_loss_streak_today`). - ⚠ Die Gewichtung ist **NICHT backtest-optimiert**, sondern aus den im Projekt - gemessenen Leckagen + der User-Checkliste abgeleitet (Entscheidungshilfe, kein - validiertes Signal). Real beim Einbau (30.07. abends): beide Richtungen `stop` — - „keine Empfehlung" · „HTF unklar/dagegen" · „11 Verluste heute (−261 € = 80 % des - Kontos)". -- **Liquiditäts-Sweeps (SMC: Equal Highs/Lows, Swing Liquidity) = VERWORFEN - (gemessen `backtest_liquidity_sweep.py`, 2026-07-30, User-Vorgabe mit Chart- - Beispielen + WTI-Praxisregeln):** Getestet, weil der **Sweep** (Fehlausbruch: Level - kurz durchstochen, Close zurück in der Zone) eine ANDERE Mechanik ist als der schon - verworfene Nackenlinien-Bruch (`backtest_doubletop.py`). Achsen: 3 Muster × Level-TF - (M5/M30/H1, die „Liquidität auf höherem TF"-Regel) × 2 Exits × 2 Halbjahre, plus - Session-Filter (NY 14–16 / Asia 2–4 Uhr Berlin). **Kernbefunde:** (a) Der - **SMC-eigene Exit** („Stop über dem Sweep-Docht, Ziel = Gegen-Liquidität") ist in - **ALLEN 9 Kombinationen negativ** — die Lehre steht und fällt mit ihm, er trägt - nicht; mit dem generischen Live-Trailing sieht dasselbe Setup besser aus (derselbe - Exit-Artefakt wie bei `backtest_patterns.py`). (b) Equal Highs/Lows fallen fast - überall durch (Vorzeichen kippt zwischen den Hälften). (c) Einziger Treffer: - „Swing-Liquidity, M30-Level, Live-Exit" (+0,241/+0,358) — aber die **Nachbar-TFs - kippen** (M5 −0,152/+0,247 · H1 −0,111/+0,692) = **nicht parameter-robust**, exakt - das ORB-Muster von 2026-07-17. (d) Session-Filter: zu wenige Trades (n=3–31) für - eine Aussage. **Kein Einbau in die Gesamtempfehlung. 16. verworfener Signal- - Eingriff.** ⚠ Die DXY-Korrelations-Regel wurde bewusst NICHT erneut getestet — die - Inter-Market-Klasse ist 2026-07-19 gemessen durchgefallen. -- **Fibonacci-Retracements = VERWORFEN (gemessen `backtest_fibonacci.py`, 2026-08-04, - User-Frage zu einem USOIL-Kommentar „ein Fibonacci-Retracement sollte den Kurs auf - 79 $ drücken"):** Fib-Level waren im Projekt bis dahin **nie gemessen** — weder als - S/R noch als Einstiegszone. Aufbau wie beim P(break)-Training: Schwünge aus - **M15**-Pivots (die genannte Zeitebene), Berührung ≤0,15×ATR, Ausgang ±0,5×ATR in - 12 M5-Bars, 80k Bars, 2 Halbjahre — mit ZWEI Kontrollgruppen. - | Haltequote | H1 | H2 | - |---|---|---| - | **Fibonacci** | 54,3 % | 54,5 % | - | (a) Zufalls-Level gleicher Distanz | **55,7 %** | 53,8 % | - | (b) M30-Pivots (Live-Quelle) | **56,6 %** | **55,4 %** | - **Handelbarkeit (Bounce am Level, kanonischer Exit, Echtkosten):** H1 ØR −0,168 / - PF 0,71 · H2 ØR −0,082 / PF 0,85 — beidhälftig negativ. - ⚠⚠ **Der aufschlussreichste Wert ist die Zufalls-Kontrolle: ~55 % Haltequote.** - Ein BELIEBIGES Level hält in diesem Markt in gut der Hälfte der Fälle — das ist die - Basisrate kurzfristiger Mean-Reversion, keine Eigenschaft von Fibonacci. Die Frage - war nie „hält das Level?", sondern „hält es BESSER als eine willkürliche Linie?" — - und Fib tut das nicht. **Damit ist auch das Selbsterfüllungs-Argument entkräftet** - („so viele schauen darauf, also wirkt es"): dann müsste Fib über der Zufallslinie - liegen; es liegt darunter bzw. gleichauf. - ✅ **Bestätigt umgekehrt die Struktur-These:** M30-Pivots — Stellen, an denen real - gehandelt wurde — halten in BEIDEN Hälften besser als beide gerechneten Linien. - Dieselbe Trennlinie wie beim Regressionskanal (`backtest_structure_tf.py`, 24/24 - negativ) und beim P(break)-Modell: **„Linie im Chart" ist nicht „Struktur im Markt".** - **21. verworfener Signal-Eingriff.** ⚠ Grenzen: eine Schwung-Definition (M15-Pivots - k=3, ≥2×ATR); die Pivot-Kontrolle ist dünner (n=173/224). Der Vergleich ist aber - design-intern, die relative Aussage damit robuster als die Absolutwerte. -- **Meta-Labeling (López de Prado) auf dem Wellensignal = VERWORFEN (gemessen - `backtest_metalabel.py`, 2026-08-05, aus der Web-Recherche „Best Practices"):** - Kein 23. Filter, sondern ein **Zweitmodell**, das für ein bereits erzeugtes Signal - P(Gewinn) schätzt — dieselbe Machinerie wie das erfolgreiche P(break)-Modell - (12 kausale Merkmale: Konfidenz · Überdehnung · HTF/H1-Übereinstimmung · Kosten · - Vola-Regime · mom3/mom6 · Efficiency Ratio · Nacht · Entry-Raum · Winkel; - logistische Regression, Fit auf einer Hälfte, Test auf der anderen, **in BEIDEN - Richtungen**). Population = der volle Live-Gate-Stack (929 bzw. 1316 Signale). - **Fällt klar durch:** - | | Fit H1 → Test H2 | Fit H2 → Test H1 | - |---|---|---| - | **AUC oos** | **0,508** | **0,479** | - | Kalibrierung | 49,3 % vorhergesagt vs. **38,6 %** real | 37,2 vs. 38,0 % | - | Top-Bucket (p≥0,55) | **74,1 % vorhergesagt, 39,1 % real** | (existiert nicht) | - Münzwurf in beiden Richtungen (P(break) erreicht 0,65), und **8 von 12 - Merkmalsgewichten kippen das Vorzeichen** zwischen den Fits — das Modell lernt - nichts Stabiles. Die im H1→H2-Lauf scheinbar „besseren" Schwellen (p≥0,45…0,55) - sind reine **Handelsvermeidung** (ΣR steigt nur, weil n fällt; ØR bleibt negativ) - und reproduzieren in der Gegenrichtung nicht. **23. verworfener Signal-Eingriff.** - ⚠ Deckt sich exakt mit dem Caveat der Quelle (Hudson & Thames): *„if the algorithm - is bad then meta-labeling would likely only reduce the downside."* Das Wellensignal - ist in H1 gemessen negativ — ein Zweitmodell rettet ein schwaches Primärsignal nicht. - ✅ **Was das positiv zeigt:** die Trennung „am Signal ist nichts zu holen, der Hebel - liegt in **Ausführung** und Verhalten" hält auch gegen die aktuelle Lehrmeinung. - Am selben Tag hat der Ausführungs-Hebel (Stop-Order statt Market) **+0,61 ØR** - gebracht — mehr als alle 23 Signal-Ideen zusammen. -- **„Erst messen, dann umbauen":** Strategie-Änderungen per Backtest belegen, nicht - raten. Gegen-Trend verliert; nicht bei WARTEN handeln. **Stichproben ~28 Tage** — - ein Trend-Regime; Befunde später auf mehr/andere History gegenprüfen. -- **Wave-Auto-Trader (Dry-Run) KOMPLETT ENTFERNT (2026-07-23, Code-Review-Fund):** war - seit Langem Verlierer (WR 29 % / PF 0,64) vs. manuelle Trades (WR 56–76 %); UI-Toggle - bereits vorher entfernt (Dashboard-Button steuert seitdem Auto-Squeeze), lief aber - weiter dormant bei jedem Tick mit (toter Ballast). `core/auto_trader.py` gelöscht - (Sicherung: `.removed_backup/`), `engine.self.auto` + alle 4 Aufrufstellen - (`_price_loop`, `_run_analysis`, `toggle_auto`, Snapshot-Keys `auto`/`min_conf`) - sowie die tote `auto`-Referenz in `TradingAgent.__init__` entfernt, `/api/auto` - gelöscht. `min_conf` (Frontend-Warnschwelle „Order ohne Signal") lebt jetzt als - eigene Konstante `_MIN_CONF_DISPLAY=70` in `engine.py`. Die `intended_trades`-DB- - Tabelle + `history.intended_*`-Methoden bleiben (historische Daten, kein Schreiber - mehr aktiv). Manuelles Handeln bleibt der validierte Edge auf dem Wave-Signal. - - **RE-TEST 2026-07-30 (User-Wunsch „Trades bei Empfehlung automatisch eröffnen") = - ERNEUT VERWORFEN, gemessen `backtest_auto_signal.py`:** Auto-Entry auf dem Wave- - Signal mit den HEUTIGEN Gates, echte `_build`-Logik (kein Nachbau), M30-/H1-Trend - aus M5 aggregiert und in `_build` gefüttert, sequentielle 1-Positions-Sim, Live-Exit - (SL 2,0/Trail 1,5/BE 1,3/Time-Stop 120 min), Echtkosten (Bar-Spread/ATR), 80k M5-Bars - in 2 Halbjahren. **ALLE 8 Varianten fallen durch** (Konf-Schwelle 55/65/70/75 × - Nacht-Sperre an/aus): **H1 durchgehend NEGATIV** (ØR −0,076…−0,182, PF 0,78–0,90), - **H2 durchgehend positiv** (+0,112…+0,167) → klassisches Regime-Kippen, n=718–1167 - je Hälfte (keine Stichproben-Frage). ⚠ **Kontroll-Beleg, dass es am SIGNAL liegt und - nicht an der Sim:** der Squeeze läuft durch DIESELBE Exit-/Kosten-Maschinerie und ist - in H1 **+0,187/PF 1,29** — die Simulation funktioniert also, nur das Wave-Signal trägt - in H1 nicht. Deckt sich exakt mit dem Live-Dry-Run (WR 29 %/PF 0,64). Nebenbefunde: - (a) höhere Konfidenz hilft NICHT monoton (75 schlechter als 65) — bestätigt - `analyze_verdict_calibration.py` (conf_pct ist NICHT kalibriert, in H2 invertiert), - ein „nur bei hoher Konfidenz"-Auto-Entry ist damit auch erledigt; (b) die Nacht-Sperre - verbessert konsistent (H1 −0,172→−0,123), reicht aber nicht; (c) ⚠ **Methodik-Lehre:** - ein Vorlauf mit nur 6k Bars zeigte „Konf≥65+Nacht TRÄGT" — der 80k-Lauf drehte das - komplett (n war 60–99/Hälfte = Rauschen). **Kleine Stichprobe hätte hier zum Fehl- - Einbau geführt.** Warum manuelle „MIT Signal"-Trades trotzdem profitabel sind - (+118 €/WR 56 %, `analyze_entries`-Auswertung 2026-07-30): **menschliche AUSWAHL** - unter den Signalen (Kontext/Tageszeit/Marktlage), nicht „alle nehmen" — die - Grundgesamtheit ALLER Signale ist der Verlierer. **14. verworfener Signal-Eingriff.** -- **Auto-Signal-Entry = TROTZDEM GEBAUT auf ausdrücklichen User-Wunsch (2026-07-30, - „probieren wir es trotzdem mal"), `[trading] auto_signal`, Default FALSE.** ⚠ **Der - Backtest direkt darüber sagt NEIN** (H1 durchgehend negativ) — der User hat das nach - vollständiger Information (inkl. Live-Dry-Run-Historie WR 29 %/PF 0,64) bewusst - entschieden. Gebaut mit den **gemessen BESTEN** Parametern, nicht mit Default-Raten: - `auto_signal_min_conf=75` (mit Nacht-Sperre H1 −0,053 = mildester Verlust, H2 +0,141 - = bestes Netto; ⚠ NICHT weil „mehr Konfidenz besser" — conf_pct ist unkalibriert, 75 - ist empirisch) und `auto_signal_skip_night=true` (verbessert JEDE Variante). - **Mechanik** (`engine._check_auto_signal` im `_pos_loop`, 1 s, NACH den Close-Checks): - eröffnet nur **FLAT**, nur bei `wave.signal()` LONG/SHORT mit `conf_pct ≥ min_conf`, - **1× je Signal-Episode** (Dedup `_signal_last_sig`, re-armt bei WARTEN/Flip — nicht - über den Preis wie beim Squeeze, weil das Signal keine Level-Marke hat); erbt die - Squeeze-Guards: Circuit-Breaker, Nacht-Sperre, S/R-Close-Cooldown, allgemeine - Wiedereinstiegs-Sperre. **Kein Nachkauf** bei gleichgerichteter Position, **kein - Drehen** einer Gegen-Position (unbelegt, wie Squeeze-Reverse). Log `🎯 AUTO-SIGNAL- - ENTRY` + Telegram, Setup-Tag `AUTOSIG_` (macht die Trades in der DB separierbar - → B4-Monitor). Snapshot `auto_signal`/`auto_signal_min_conf`/`signal_entry_count`, - Toggle `POST /api/autosignal` = Dashboard-Button **🎯 SIG** (bernstein statt blau, - weil gemessen nicht tragfähig; zeigt die Schwelle an), neustart-fest via - `runtime_state.json`. - ⚠⚠ **ABGESCHALTET am 2026-08-01 (`auto_signal=false`, via Toggle → `runtime_state.json`).** - Zwei Gründe, KEINER davon die Live-Bilanz (n=4 = +21,79 € sagt nichts): (1) gemessen in - H1 durchgehend negativ, gebaut gegen die Messung; (2) **beide Auto-Pfade konkurrieren um - den EINEN Positions-Slot** — ein AUTOSIG-Trade blockiert einen Squeeze-Einstieg und - umgekehrt. Solange beide laufen, ist die B5-Abbruchprüfung des Auto-Squeeze (s. u.) - nicht sauber messbar. ⚠ Er lief bis dahin **unbemerkt**, obwohl die ini `false` sagte: - ein Dashboard-Toggle hatte `true` in `runtime_state.json` persistiert, und das - überschreibt die ini beim Start. Der Config-Wächter prüft das jetzt mit. - Reaktivieren = Button 🎯 SIG; dann gilt wieder: ≥20 AUTOSIG-Trades, Live-ØR/PF gegen - die Erwartung halten (H2-Regime +0,14 vs H1-Regime −0,05). - ⚠⚠ **NEU GEMESSEN 2026-08-04 nach dem Reversal-Abschalten - (`backtest_auto_signal_norev.py`, 80k M5, 2 Halbjahre, Nacht-Sperre an, keine - 15-Min-Regel):** Frage des Users — trägt der Auto-Signal-Entry mit den heutigen - Änderungen? **Antwort: nein, aber deutlich näher dran.** - | conf ≥ 75 (LIVE-Schwelle) | ØR | PF | n | - |---|---|---|---| - | H1 | **−0,047** | 0,94 | 678 | - | H2 | **+0,120** | 1,17 | 710 | - H1 bleibt negativ → **2-Stichproben-Regel weiterhin NICHT erfüllt**, und kein - Konfidenz-Schwellwert rettet das (55: −0,103 · 65: −0,111 · 75: −0,047). - ✅ **Das Reversal-Abschalten wirkt aber konsistent** — Δ ØR durch die Abschaltung: - conf 55 **+0,015/+0,099** · conf 65 +0,001/+0,047 · **conf 75 +0,099/+0,087**. - An der Live-Schwelle verbessern sich BEIDE Hälften um ~+0,09; H1 geht von −0,145 - auf −0,047, PF von 0,82 auf 0,94. Erste Bestätigung der Abschaltung auf dem - GESAMTEN Signal (bisher nur der isolierte Trigger). - ⚠⚠ **Dabei gefunden — die Messung vom 30.07. war strukturell unvollständig:** - `backtest_auto_signal.py` ruft `_build` **ohne `angle=`** auf → Default 90,0 → - `ad = 0`. Damit konnte dort **weder der Reversal-Zweig feuern noch der - Winkel-Konfidenzterm (±15 Punkte) greifen** — obwohl die Docstring „echte - `_build`-Logik inkl. Reversal-Zweig" behauptet. Wirkung: der Winkelterm allein hebt - H1 bei conf 75 von −0,123 auf −0,047. **Deployment-Drift Fall 5** (s. Tabelle). - Die Originaldatei bleibt unverändert (ihre Zahlen sind dokumentiert); die live-treue - Variante steht als `backtest_auto_signal_norev.py` daneben und sollte künftig die - Referenz sein. -- **⚠⚠ AUTO-SIGNAL NEU GEMESSEN 2026-08-05 (`backtest_auto_signal_v3.py`) — ZUM - ERSTEN MAL BESTEHT ER, ABER NUR MIT EINEM ANDEREN EINSTIEGSPREIS.** - Anlass: User-Wunsch nach einer ausführlichen Neumessung „mit den neuen Änderungen". - Geändert seit dem 04.08. waren Nacht-Fenster 0–7→3–6 und — der eigentliche Punkt — - der **Ausführungs-Befund vom selben Tag**: `_confirm_breakout` hat ein LEVEL - (`_pend["level"]` = Anker ± k×ATR), an dem eine ruhende Stop-Order liegen könnte. - ALLE bisherigen SIG-Messungen sind am Bar-Close eingestiegen. - Voller Live-Gate-Stack (echte `_build`/`_confirm_breakout`/`_room_gate`), - sequentiell EIN Slot, Live-Dedup (1× je Episode), kanonischer Exit, Echtkosten, - 80k M5, 2 Halbjahre: - | Konf | **A) Market (Bar-Close)** H1 / H2 | **B) am Bestätigungs-Level** H1 / H2 | - |---|---|---| - | ≥55 | −0,098 / −0,093 | **+0,349 / +0,331** | - | ≥65 | −0,092 / −0,112 | **+0,357 / +0,317** | - | **≥75 (live)** | −0,050 / −0,189 | **+0,380 / +0,272** | - | ≥85 | −0,118 / −0,234 | **+0,351 / +0,279** | - **A ist in ALLEN acht Feldern negativ** (bestätigt die drei Vormessungen). - **B besteht in ALLEN acht** — Trefferquote 39,6 → 53,2 %, PF 0,83 → 2,20, und - **alle vier Nachbar-Schwellen halten mit** (Parameter-Robustheit, an der ORB und - die SMC-Sweeps gescheitert sind). - ⚠ **HÄRTETEST Fill-Slippage** (Konf≥65) — eine Stop-Order füllt zum Markt, der - Idealpreis ist die Obergrenze: +0,05 → **+0,315/+0,249** · +0,10 → +0,273/+0,197 · - **+0,20 → +0,148/+0,090** (noch beidseitig positiv) · **+0,31 → +0,026/−0,031**. - ✅ **Die letzte Zeile ist die interne Kontrolle:** bei Slippage = dem gemessenen - Median-Abstand (0,311×ATR) konvergiert B gegen A — das Modell prüft sich selbst. - Der Kipp-Punkt liegt bei ~0,25–0,30×ATR Fill-Slippage. - **Abstand Bar-Close ↔ Bestätigungs-Level: Median +0,311×ATR** (Ø +0,422, Max - +5,29) — praktisch derselbe Wert wie beim Squeeze live (+0,275). Gleicher - Mechanismus, gleiche Größenordnung. - ⚠ **Nacht-Fenster ist hier nahezu irrelevant:** 3–6 (+0,380/+0,272) · 0–7 - (+0,371/+0,226) · keine Sperre (+0,359/**+0,302**) — alle drei bestehen, ohne - Sperre ist H2 sogar am besten. Deckt sich mit `backtest_cost_gate.py`. - ⚠⚠ **GRENZEN — bewusst NICHTS umgeschaltet:** - (1) **Wo LIVE zwischen A und B liegt, ist für SIG NICHT verifizierbar** — beim - Squeeze gab es 34 echte Entries zum Nachrechnen, hier sind es 4. Die Empfehlung - macht das zwar gegenstandslos (eine ruhende Order IST B per Konstruktion), aber - behauptet ist es damit nicht. - (2) **Variante A ist STRENGER als live:** für die Vergleichbarkeit steigen beide - Varianten nur im Bar der frischen Bestätigung ein (`frisch`); live darf - `_check_auto_signal` auch später feuern. A ist ohnehin negativ, die Folgerung - bleibt — aber A ist nicht exakt das Live-Verhalten. - (3) **Operativ deutlich schwieriger als beim Squeeze:** die Squeeze-Box steht über - viele Bars still, `_pend` dagegen **verankert sich neu**, sobald der Kurs k×ATR - dagegen läuft, nach 12 Bars Timeout ODER bei Richtungswechsel. Eine ruhende Order - müsste ständig nachgezogen werden (Broker-Last, mehr Ablehnungen am - Mindestabstand) — und **wenn sie nicht platzierbar ist, landet man wieder bei A**, - also im gemessen negativen Fall. Wie oft das passiert, ist ungemessen. - (4) `_pend` wird aktuell **nicht im Snapshot veröffentlicht** — das wäre die erste - Bauarbeit. - ✅✅ **PENDING-PFAD FÜR SIG GEBAUT & LIVE (2026-08-05, User-Vorgabe), - `[trading] signal_pending_entry=true`.** SIG läuft damit auf Variante B statt A. - ⚠⚠ **Der heikelste Punkt war NICHT die Order, sondern die Kollision der beiden - Pfade.** `trader.pending_orders()` liefert ALLE magic-gleichen Orders — zwei - getrennte Manager (Squeeze/Signal) hätten sich **gegenseitig die Orders - storniert**, sporadisch und im Log kaum erkennbar. Deshalb **EIN gemeinsamer - Ziel-Zustand**: `engine._pending_ziel()` liefert `{order_type: preis}` + Quelle, - `_manage_squeeze_pending` gleicht nur noch ab. **VORRANG: Squeeze > Signal** — der - Squeeze ist unabhängig validiert und selten (~2–3×/Woche), das Signal feuert - dauernd; bei EINEM Positions-Slot ist die Reihenfolge damit eindeutig. - ⚠ **Zwei additive Ergänzungen in `wave_rec._confirm_breakout` waren nötig:** - (1) Das Bestätigungs-Level wird jetzt **auch nach der Bestätigung** veröffentlicht - (`snap["breakout"]["level"]`, `confirmed=True`) — die Nachjagd-Bremse im - Market-Fallback braucht es genau dann. `pending` bleibt False, das Frontend rendert - nur bei `pending` und ist unberührt. (2) **`conf`** = die Konfidenz VOR dem - Stummschalten: der Rückgabewert setzt `conf_pct` auf 0 (das Signal gilt ja noch - nicht), die ruhende Order müsste sonst **blind** platziert werden. Damit wird die - Mindest-Konfidenz jeden Tick geprüft und die Order storniert, sobald sie fällt — - strenger als der Live-Check von vorher (der prüfte nur im Moment des Entries). - ⚠ **Nachjagd-Bremse auch für SIG** (`squeeze_max_chase_atr`, 0,20): greift die - Pending nicht, wird nur noch bis 0,20×ATR über dem Bestätigungs-Level nachgekauft — - der Härtetest zeigt dort +0,148/+0,090, ab ~0,25–0,30 kippt es. - ⚠ **Der wandernde Anker ist die operative Hauptlast:** `_pend` verankert sich bei - jeder k×ATR-Gegenbewegung, nach Timeout und bei Richtungswechsel neu — die Order - wird dann nachgezogen (Toleranz 0,02×ATR gegen Order-Churn). **Mit 12 Szenarien - getestet** (Vorrang · reiner Signal-Fall · SHORT · Konfidenz zu niedrig · Wechsel - Signal→Squeeze mit Storno · wanderndes Level · Toleranz · Gate · Fill · Feature aus - · Bestätigung eingetreten · Schonfrist). Live verifiziert am Snapshot: - `signal_pending=true`, `breakout` liefert `level`/`conf`/`confirmed`; - `pending_levels` leer, weil eine Position offen war = korrektes Verhalten. - Snapshot-Felder umbenannt: `squeeze_pending_levels` → **`pending_levels`** + - **`pending_quelle`**. Zurück: `signal_pending_entry=false`. - ⚠ **B4-Pflicht:** dieselbe Erfolgskontrolle wie beim Squeeze — der Abstand - Einstieg↔Level muss gegen 0 gehen. AUTOSIG-Trades sind über `setup=AUTOSIG_*` - separierbar. - ⚠⚠ **ZWEITER Methodik-Fehler, am selben Tag gefunden und die Messung WIEDERHOLT: - `set_entry_room(0.6)` fehlte.** `WaveRecommender.__init__` setzt - `_entry_room_atr = 0.0`, und `_room_gate` gibt dann `rec` **unverändert** zurück — - der Aufruf sieht im Code aus wie ein Gate, ist aber ein No-op. Betroffen waren - DREI am 05.08. neu gebaute Skripte (`backtest_auto_signal_v3`, `_metalabel`, - `_dist_gated`); die Referenz `backtest_breakout_gated.py` (04.08.) macht es richtig. - Aufgefallen ist es erst, weil `entry_room` in der Verteilung mit **0 %** auftauchte, - live aber 35 % blockt. **Neu gerechnet ändert sich die Population stark (n 967→295 - bzw. 1327→524), die Schlussfolgerung aber NICHT:** Market bleibt in 7 von 8 Feldern - negativ, am Level bestehen weiterhin **alle vier** Konfidenz-Schwellen - (ØR +0,392/+0,356 · PF 2,37/2,39), und der Slippage-Härtetest hält bis 0,20×ATR - (+0,177/+0,118). **Die heutige Bau-Entscheidung ist damit gedeckt.** - ⚠ Lehre: ein aufgerufenes Gate ist nicht dasselbe wie ein aktives Gate. Beim - Kopieren einer Backtest-Struktur gehört die **Konfiguration** mitkopiert — sonst - misst man einen Stack, den es nicht gibt. - ⚠ **Methodik-Fehler beim Bau, gefunden und behoben:** der erste Entwurf liess - `pend["level"]` nach der Bestätigung über weitere Bars stehen — bei verzögertem - Einstieg (Slot belegt, Konfidenz zu niedrig) wäre er zu einem Preis eingestiegen, - den der Markt längst verlassen hatte. Kein Look-ahead, aber **unerreichbar**, und - sichtbar an einem unplausiblen Bruch zwischen Konf 65 und 75 (+0,33 → −0,12). - Behoben über das `frisch`-Flag. **Lehre: ein Preis, der einmal galt, ist keine - Einstiegsmöglichkeit — nur der Moment der Auslösung zählt.** -- **⚠⚠ 15-MINUTEN-REGEL GANZ ABGESCHALTET (`adverse_15min_atr = 0`, 2026-07-31, - User-Entscheidung nach der Messung).** Auf die User-Frage „vielleicht die Auto-Close - auf 30 min setzen?" wurde die **ZEIT-Achse** mitgemessen - (`backtest_adverse15_squeeze.py`, 15/30/45/60 min × 0,5/0,8×ATR, BEIDE Populationen, - kanonischer Exit, Echtkosten, 2 Halbjahre): - | Zeitpunkt | Schwelle | Squeeze Δ H1/H2 | Wave Δ H1/H2 | - |---|---|---|---| - | 15 min | 0,5 | −18 / −34 ← war live | −25 / −41 | - | 30 min | 0,5 | −20 / −35 | −22 / −13 | - | 45 min | 0,8 | −12 / −18 | −3 / −7 | - | 60 min | 0,8 | −8 / −10 | −3 / −3 | - **ALLE 16 Kombinationen sind in BEIDEN Hälften negativ** — die Regel wird nur - *monoton weniger schädlich*, je später und lockerer sie prüft. Die „beste" Variante - ist praktisch die, die nie feuert. **Es gibt keine Einstellung, bei der sie hilft** - → 30 min hätte den Schaden halbiert, nicht beendet. Deshalb ganz aus. - ⚠ **Damit ist auch die frühere Einschätzung „auf dem Auto-Signal-Pfad ~neutral" - korrigiert**: sie stützte sich auf `backtest_auto_signal.py` (eigenes Exit-Modell, - +0,001 vs +0,026); mit dem kanonischen Exit sind es −25/−41. Sechster Fall desselben - Musters an einem Tag (gemessen unter X, betrieben unter Y). - Der Schutz-Stack bleibt vollständig: **Broker-SL 2×ATR + Trailing + Time-Stop 120 min**. - Die Squeeze-Ausnahme (unten) bleibt im Code — falls die Regel je reaktiviert wird, - gilt sie weiterhin nicht für den Squeeze. -- **⚠ 15-MINUTEN-REGEL: SQUEEZE-TRADES AUSGENOMMEN (gemessen - `backtest_adverse15_squeeze.py`, 2026-07-31).** Die Schwelle 0,5×ATR wurde in - `backtest_auto_signal.py` auf **WAVE-SIGNAL-Entries** kalibriert und danach auf ALLE - Bot-Trades angewandt — wieder ein Kalibrierungs-Bruch (gemessen unter X, betrieben - unter Y). Auf SQUEEZE-Entries schadet die Regel bei **JEDER** Schwelle in **BEIDEN** - Hälften: Δ gegen „ohne Regel" 0,3 → −25/−38 · **0,5 → −18/−34** · 0,8 → −12/−39 · - 1,2 → −5/−18. **Monoton** — also ist die Regel selbst das Problem, nicht ihre - Einstellung. Grund: der Squeeze ist runner-abhängig (43 % Treffer, Ertrag im Tail), - ein Schnitt nach 15 min trifft genau den Tail. Ohne Regel H1 −54 / H2 **+220**, mit - 0,5 H1 −72 / H2 +186. - **Anlass (real, 2026-07-31):** drei Bot-Trades wurden per `adverse15` geschlossen — - der **Auto-Signal**-Trade um 11:44 (−26,31 €) und die beiden **Squeeze**-Trades um - 20:15/20:36 (−13,77 / −28,09 €). - ⚠⚠ **DIE URSPRÜNGLICHE ANEKDOTE DAZU WAR FALSCH — korrigiert 2026-08-01.** Hier stand, - der 11:44-Trade sei „gerettet" worden (Kurs lief 32 € weiter dagegen) und die beiden - Squeeze-Trades seien „zu früh gekappt" worden (+19,28 / +17,60 € möglich). Beide Zahlen - waren **maximale Auslenkung** (bester bzw. schlechtester danach berührter Kurs) — also - das, was der Markt hergab, NICHT das, was der Exit gefangen hätte. Mit dem kanonischen - Exit nachgerechnet (`core/exit_model.py`, M5 aus `candles_m1`): - | Trade | IST (mit Regel) | ohne Regel, Trail 1,5 | - |---|---|---| - | 11:29 AUTOSIG | −26,31 | **−21,43** (also NICHT gerettet) | - | 20:00 SQUEEZE | −13,77 | **−23,27** (also NICHT zu früh) | - | 20:21 SQUEEZE | −28,09 | −25,02 | - Über alle 7 Bot-Trades des Tages hätte das Abschalten der Regel **−16,42 € gekostet**; - die Regel hat an DIESEM Tag also geholfen. ⚠ **Die Entscheidung, sie abzuschalten, - bleibt trotzdem richtig** — sie ruht auf `backtest_adverse15_squeeze.py` (alle 16 - Kombinationen in BEIDEN Hälften negativ), nicht auf einem Tag mit n=3. Lehre: eine - Einzeltag-Anekdote taugt nicht als Beleg, auch nicht als *bestätigende* — und - „maximale Auslenkung" ist keine erzielbare Zahl. Der Gewinn des Tages kam woanders her: - die Umstellung **Trail 1,5 → 1,0** brachte auf denselben 7 Trades **+24,86 €**. - **Umsetzung:** `_bot_open_source` merkt sich beim Öffnen, WOMIT der Bot eröffnet hat - (`auto_squeeze` | `auto_signal`); `_check_adverse15` steigt bei `auto_squeeze` aus. - Mit 7 Szenarien getestet (Signal/Squeeze/manuell/altes Ticket · zu früh · unter - Schwelle · im Plus). **Für den Auto-Signal-Pfad bleibt sie aktiv** — dort ist sie - gemessen ~neutral (ØR +0,001 mit vs +0,026 ohne), also eine **Versicherung mit - kleiner Prämie**: hilft im schlechten Regime, kostet im guten. `=0` schaltet sie ganz aus. -- **15-Minuten-Regel (`[trading] adverse_15min_atr=0.5`, AKTIV, User-Wunsch 2026-07-30 - „wenn der Trade nach 15 Minuten gegen die Richtung läuft schließe ihn"):** steht ein - **vom BOT eröffneter** Trade 15 min nach Entry ≥ X×ATR_M5 gegen die Richtung → - schließen (`engine._check_adverse15`, `closed_by=adverse15`, Log ⏱ + Telegram). - ⚠ **Schwelle GEMESSEN kalibriert, NICHT die ursprüngliche Vorgabe** (`backtest_auto_ - signal.py`, 15-Min-Achse): „sobald im Minus" (0,0) ist die **SCHLECHTESTE** Variante - (WR bricht 39→31 %, Netto-ØR −0,067 vs +0,026 ohne Regel) — sie kappt Trades, die - sich erholt hätten. **0,5×ATR ist die beste Regel-Variante** (H1 −0,111→−0,079). - Netto über beide Hälften bringt die Regel ~nichts (+0,001 vs +0,026 **ohne**) → sie - ist ein **REGIME-Schutz** (hilft im schlechten Regime, kostet im guten), bewusst als - Sicherheit akzeptiert. **2. unabhängige Bestätigung** von `backtest_adverse_nosignal.py` - (dort fiel der Adverse-Exit bei JEDER Schwelle durch). Greift **NICHT bei manuellen - Trades** (`_bot_open_ticket`-Marker — dort bleibt die Diskretion beim User, gemessen - sein Vorteil) und nicht in der Startup-Schonfrist; **1× je Ticket** geprüft - (`_adverse15_done_ticket`) genau beim 15-min-Zeitpunkt. Snapshot `adverse15_atr`/ - `adverse15_count`. 0 = aus. Mit **18 synthetischen Szenarien getestet** (9 Entry: - Konf-Gate, Dedup, WARTEN-Re-Arm, Nachkauf, Gegen-Position, Cooldown, Toggle, - Nacht-Sperre · 9 Adverse: über/unter Schwelle, zu früh, im Plus, SHORT-Richtung, - manueller Trade, aus, 1×-je-Ticket, Startup-Grace) — alle korrekt. -- **⚠⚠ ZUVERLÄSSIGKEITS-PRÜFUNG 2026-08-11 (`backtest_squeeze_woche.py`, User: - „funktioniert der Breakout-Auto-Trader zuverlässig? simuliere die letzte Woche").** - ⚠⚠ **BEFUND 0 beantwortet die Frage vorweg: DER TRADER IST AUS.** - `runtime_state.json` enthält `"auto_squeeze": false` und **überschreibt die ini** - (`= true`) beim Start — dieselbe Falle wie am 31.07. bei `auto_signal`, nur mit - umgekehrtem Vorzeichen. Letzter Squeeze-Trade **06.08. 13:52**, - `squeeze_entry_count = 0`. ✅ Der Config-Wächter meldet es korrekt („ist 'false' - — validiert gegen 'true'"); es wurde nur nicht gehandelt. Zurück: 🚀 BRK. - ✅ **BEFUND 1 — die MECHANIK arbeitet korrekt.** Die Simulation nutzt die echte - `_squeeze_one` und reproduziert **10 von 11 Live-Trades binnen 10 min und auf - wenige Cent genau** (live 06.08 02:11 SELL 75,127 ↔ Sim 02:10 SHORT 75,132). - Die Erkennung ist nicht das Problem. - **BEFUND 2 — die Woche** (ruhende Stop-Order, Fill bei BERÜHRUNG, Nacht 3–6 - gesperrt, EIN Slot, kanonischer Exit, Echtkosten): - | Fill-Slippage | n | Treffer | ØR | PF | ΣR | - |---|---|---|---|---|---| - | **0,00×ATR** | 47 | 57 % | **+0,196** | 1,37 | +9,20 | - | 0,10×ATR | 47 | 57 % | +0,100 | 1,17 | +4,69 | - | **0,20×ATR** | 46 | 50 % | **−0,013** | 0,98 | −0,58 | - ≈ **+610 €** bei 2,5 Lots, ØR im validierten Band (+0,14…+0,23). ⚠⚠ **Drei - Vorbehalte:** **(1)** die Sim **überhandelt um das 2,4-fache** (26 statt 11 - Einstiege in der An-Phase) — ihr fehlen Live-Dedup (dir, level), 3-min- - Wiedereinstiegssperre und S/R-Cooldown; ΣR ist nach oben verzerrt. **(2)** der - Edge **stirbt bei 0,20×ATR Fill-Slippage**, und live lag der Abstand - Einstieg↔Level vor dem Pending-Umbau bei Median **+0,275×ATR** — alles hängt - daran, ob die ruhenden Orders am Level füllen (`squeeze_entry_gap`: **8/12**). - **(3)** eine Woche ist Rauschen — die Phase, in der der Trader **AUS** war, ist - sogar die bessere (ØR +0,287 gegen +0,122). - ⚠⚠ **BEFUND 3 — der GEPAARTE Vergleich widerspricht dem B4-Befund vom 01.08.** - Auf **denselben 10 Einstiegen**: **LIVE −109,52 €** gegen **kanonischer Exit - −179,27 €** — der Live-Exit war **70 € BESSER**. Am 01.08. hiess es „die Lücke - entsteht im EXIT"; hier ist es umgekehrt. Im Detail rettete der **S/R-Close** - drei Trades (05.08 08:33 **+12,56 gegen −113,64** · 06.08 13:51/52 je - **+0,26/+2,88 gegen −86,38**), während der kanonische Exit bei 06.08 09:04 - (**−46,82 gegen −175,21**) und 02:11 (**+23,83 gegen −43,29**) klar besser war. - **Gemischt, n=10, von wenigen Trades dominiert — kein Urteil, aber ein - Warnschild gegen den umgekehrten Schluss.** - **BEFUND 4 — B5: 11 von 20 Trades (55 %).** Beide Bedingungen sind bereits weit - verletzt (je Lot ØGewinn 5,20 · ØVerlust 31,12 → **Verhältnis 0,17**, **PF - 0,45**) bei **73 % Trefferquote** — die dokumentierte Squeeze-Signatur. ⚠ Formal - **nicht fällig**; die Latte nachträglich zu senken wäre genau der Fehler, gegen - den sie vorab fixiert wurde. - ⚠ **OFFEN, nicht verifiziert:** am 06.08. **13:51 und 13:52** stehen ZWEI - Live-BUYs zum identischen Preis 76,315 auf EINEN Ausbruch (beide Pending-Fills, - beide binnen einer Minute per `sr_close` geschlossen) — sieht nach - **Doppel-Einstieg auf ein Signal** aus. Ungeprüft, ob Defekt im OCO-Ersatz oder - Teilfüllung. -- **⚠⚠ AUTO-SQUEEZE B4-BEFUND 2026-08-01: der EINSTIEG ist intakt, der EXIT hat den - Edge gefressen — deshalb NICHT zurückgebaut, aber mit vorab fixierter Abbruchregel.** - Anlass war die User-Frage zu den negativen Auto-Trades vom 31.07. Der B4-Monitor - trennt beides sauber: - | | Trefferquote | ØR | PF | - |---|---|---|---| - | **Mechanik** (Regel neu gerechnet, Backtest-Exit = isolierter Einstieg) | 46 % | **+0,102** | 1,15 | - | **Live** (n=31, 17.–31.07., Gesamtsystem mit echten Exits) | 45 % | **−0,242** | 0,49 | - Die **Trefferquote stimmt praktisch überein (46 vs 45 %)** — das Setup findet also - weiterhin, was es finden soll. Die Lücke von **0,34 R/Trade** entsteht komplett im - Exit: Ø-Gewinn **+11,02 €** gegen Ø-Verlust **−19,83 €** (Verhältnis 0,56, ΣEUR - **−162,99**). Der Squeeze ist runner-abhängig (43 % Treffer tragen nur, wenn die - Gewinner deutlich größer sind) — genau die Läufer wurden gekappt. - **Alle drei Ursachen sind am 31.07. behoben worden**, der Live-Zeitraum liegt also - fast vollständig VOR den Reparaturen: (a) das **P(break)-Modell** schloss 91 % aller - Level-Berührungen = faktisch pauschaler S/R-Close, und der ist 2× als Gewinner-Kappen - gemessen → nachtrainiert; (b) die **15-Min-Regel** schnitt Squeeze-Trades, auf dieser - Population in beiden Hälften schädlich → aus; (c) **Trailing 1,5–3,0 je nach TF** - statt der gemessenen 1,5 (Deployment-Drift Fall 3) → einheitlich 1,0. - ⚠ **Ehrlich:** die Mechanik liegt mit ØR +0,102 **unter** dem validierten Band - +0,14…+0,23 — der Einstieg ist also auch selbst schwächer geworden (Regime). Das ist - kein Freibrief, sondern der Grund für eine harte Latte statt unbegrenztem Zuwarten. - ✅ **B5-ABBRUCHREGEL, VORAB FESTGELEGT (User-Zustimmung 2026-08-01):** nach **≥20 - Squeeze-Trades ab dem 01.08.** — **Verhältnis (ØGewinn/ØVerlust) < 1,0 ODER PF < 1 - → `auto_squeeze=false`.** Bewusst VOR der Beobachtung fixiert, damit die Latte - hinterher nicht verschoben wird; überwacht als Messung `squeeze_b5` im - `measurement_reminder.py`. Nacht-Anteil der Altdaten: 12 der 31 Trades / −78,92 € — - die stammen aus der Zeit vor dem Nacht-Guard (seit 24.07. wieder an). -- **⚠⚠ DEPLOYMENT-DRIFT FALL 7 — DER SQUEEZE WIRD ZU EINEM PREIS GEHANDELT, DEN DIE - VALIDIERUNG NIE UNTERSTELLT HAT (gemessen `backtest_squeeze_entry.py`, 2026-08-05).** - Anlass: User-Frage zu einem Auto-Trade, der „am höchsten Punkt einer aufsteigenden - Kerze" eröffnet wurde (05.08. 08:33:20 — Level 75,757, Einstieg **75,822**, also - über dem Hoch 75,808 der laufenden M5-Kerze; die schloss dann bei 75,693). - **`backtest_breakout_squeeze.py` steigt AM LEVEL ein** (`sim(lvl, …)`). Live schickt - `_check_auto_squeeze` eine **MARKET**-Order, sobald `state=='active'` — der Kurs ist - dann schon ein Stück weiter. **Aus 34 echten Live-Entries (Logs 16.07.–05.08., - Level aus der Log-Zeile × Einstiegspreis aus der DB): Median +0,275×ATR über dem - Level**, Mittel +0,686 · 75 % +0,744 · Max +4,339; nur **18 %** liegen innerhalb - 0,10×ATR, **53 % über 0,30×ATR**. - **Was dieser Preisunterschied kostet** (80k M5, 2 Halbjahre, sequentiell EIN Slot, - kanonischer Exit, Echtkosten): - | Variante | H1 ØR / ΣR | H2 ØR / ΣR | WR | - |---|---|---|---| - | **A Einstieg AM LEVEL** (Backtest-Annahme) | **+0,456 / +369,8** | **+0,611 / +777,8** | 56/62 % | - | **B Einstieg am Bar-Close** (≈ live) | **−0,158 / −125,9** | **−0,006 / −7,6** | 37/42 % | - **Allein der Einstiegspreis dreht das Setup — −0,61 ØR in BEIDEN Hälften, WR bricht - von 56 auf 37 %.** Modell B ist dabei **nicht pessimistisch**: sein Median-Abstand - (0,38×ATR) liegt nah am LIVE gemessenen (0,28×ATR); der Live-Betrieb liegt also - ~80 % des Weges von A nach B, also grob bei **breakeven bis leicht negativ** — - nicht bei den dokumentierten +0,14…+0,23. - ⚠⚠ **Das ordnet den B4-Befund vom 01.08. NEU ein.** Dort stand: „Mechanik +0,102 vs. - Live −0,242 → **die Lücke entsteht im EXIT**". Die Mechanik-Zahl unterstellt aber - denselben Level-Einstieg. Der Einstiegspreis erklärt den Großteil der 0,34-R-Lücke - — **es war überwiegend der ENTRY, nicht der Exit.** - ⚠ **BEIDE naheliegenden Reparaturen fallen durch** (Regel vorab fixiert: ΣR und ØR - in beiden Hälften > B, Nachbarn ebenso): - · **Abstands-Bremse** („überspringen, wenn der Kurs schon > X×ATR über dem Level - steht", X = 0,10…0,40) — **alle 5 Schwellen durch.** Kontraintuitiv, aber konsistent: - die *nahen* Ausbrüche sind pro Trade die SCHLECHTEREN (0,10×ATR → ØR −0,254), weil ein - gerade eben überschrittenes Level oft ein Fehlausbruch ist (54 % der Box-Ausbrüche - kehren binnen 3 Bars zurück, `backtest_consolidation.py`). Die Bremse senkt den - Gesamtverlust nur, indem sie weniger handelt. - · **LIMIT-Order am Level** (Retest, 3/6/12 Bars Gültigkeit, ±0,1×ATR Puffer) — - **alle 5 Varianten SCHLECHTER als Market** (ØR −0,14…−0,27 gegen −0,006…−0,158); - 151–430 Ausbrüche werden dabei verpasst. Deckt sich mit dem Retest-Befund aus - `backtest_consolidation.py`. - ✅ **Schlussfolgerung: Variante A war nie erreichbar** — sie unterstellt einen Fill zu - einem Preis, den der Markt bereits verlassen hat. Die Frage ist damit nicht „wie - steigt man klüger ein", sondern **ob der Auto-Squeeze überhaupt bleibt**. Das ist die - bereits fixierte **B5-Abbruchregel** (≥20 Squeeze-Trades ab 01.08.; Verhältnis < 1,0 - ODER PF < 1 → `auto_squeeze=false`) — dieser Befund stärkt sie erheblich. - **Nichts abgeschaltet** — Autonomie-Schalter sind User-Entscheidungen. - ⚠ Grenzen: die Live-Stichprobe ist n=34 mit genäherter ATR-Zuordnung; A und B sind - zwei Modell-Enden, der Live-Betrieb liegt dazwischen (näher an B). - ✅✅ **LÖSUNG GEFUNDEN — RUHENDE STOP-ORDER STATT MARKET (Variante E, gemessen - 2026-08-05; Anstoß aus der Web-Recherche: „don't chase breakouts with market orders; - place orders just beyond the breakout level").** Eine PENDING `BUY_STOP`/`SELL_STOP` - liegt bereits am Ausbruchs-Level, WÄHREND der Squeeze `armed` ist (das Level ist da - schon bekannt = Box-Grenze), und wird vom Ausbruch selbst ausgelöst — anders als die - Limit-Order (D) wartet sie NICHT auf einen Rücklauf, verpasst also nichts. - | Fill-Annahme | H1 ØR / PF / ΣR | H2 ØR / PF / ΣR | - |---|---|---| - | Level exakt (= A) | +0,456 / 2,65 / +370 | +0,611 / 3,56 / +778 | - | **Level +0,05×ATR** | **+0,397 / 2,28 / +322** | **+0,553 / 3,06 / +697** | - | Level +0,10×ATR | +0,355 / 2,07 / +288 | +0,509 / 2,76 / +639 | - | **Level +0,20×ATR (pessimistisch)** | **+0,244 / 1,62 / +196** | **+0,426 / 2,25 / +531** | - | Market/Live (= B) | −0,158 / 0,74 / −126 | −0,006 / 0,99 / −8 | - **Selbst mit 0,20×ATR unterstellter Fill-Slippage — dem ~9-fachen des Median-Spreads - — bleibt es in BEIDEN Hälften klar positiv.** - ⚠⚠ **Der Schlüssel ist nicht der Median, sondern der SCHWANZ.** Live ist der - Median-Abstand +0,275×ATR, der **Mittelwert aber +0,686** (Max +4,339). Eine - Stop-Order **deckelt den Einstiegspreis konstruktiv**; die Market-Order auf dem - 5-s-Poll tut das nicht. Die Extremfälle (+3,5 / +4,3×ATR) stammen erkennbar aus - **blockiert-dann-freigegebenen** Ausbrüchen: feuert der Squeeze, während eine - Position offen ist (oder Nacht-/News-Fenster), wird KEIN Dedup-Marker gesetzt — der - Entry kommt später zum dann gültigen Marktpreis, u. U. weit hinter dem Level. - ✅✅ **GEBAUT & LIVE (2026-08-05, User-Vorgabe „setze alle Vorschläge um"), - `[trading] squeeze_pending_entry=true`.** `engine._manage_squeeze_pending` (im - `_pos_loop`, VOR dem Market-Pfad) hält **beide Box-Grenzen** mit ruhenden - `BUY_STOP`/`SELL_STOP` bestückt, solange der Squeeze `armed` ist: - `trader.place_stop/cancel_pending/pending_orders` (Größe und SL aus **denselben** - Funktionen wie die Market-Order, nur bezogen auf den Trigger-Preis — sonst wäre der - SL-Abstand ein anderer als validiert). **OCO-Ersatz:** füllt eine Seite, existiert - eine Position → die Bedingung „soll liegen" wird falsch und die Gegenseite wird im - selben Tick storniert (MT5 kennt kein natives OCO). - ⚠ **Der kritische Punkt bei einer RUHENDEN Order: die Gates.** Nacht-Fenster, - News-Blackout, Circuit-Breaker und der An/Aus-Schalter waren bisher Laufzeit-Checks - im Market-Pfad; eine liegende Order würde sie schlicht ignorieren. Deshalb neu - **`engine._squeeze_guard()` als EINE Quelle für beide Wege** — jeden Tick neu - bewertet, und sobald ein Gate zuschlägt, werden die Orders **storniert**. Zwei - Kopien derselben Gates wären genau die Divergenz, die hier siebenmal als - Deployment-Drift dokumentiert ist. - ⚠ **Market bleibt nur noch FALLBACK — mit Nachjagd-Bremse** (`squeeze_max_chase_atr` - = **0,20**): greift keine Pending (Broker-Mindestabstand, Squeeze sprang direkt auf - `active`, Order-Fehler), wird nur noch eingestiegen, solange der Kurs **höchstens - 0,20×ATR** über dem Level steht — genau der Bereich, der gemessen in beiden Hälften - positiv ist (+0,244/+0,426). Darüber: Ausbruch auslassen, Log „⏭ Auto-Squeeze - übersprungen". `_squeeze_last_sig` wird dabei NICHT gesetzt → läuft der Kurs zum - Level zurück, darf der Entry noch kommen. - ⚠ Weitere Fallstricke, alle behandelt: **Broker-Mindestabstand** - (`trade_stops_level`) wird VOR dem Senden geprüft und der Fehler nur bei ÄNDERUNG - geloggt (sonst 1 Zeile/Sekunde, wenn der Kurs an der Box-Grenze klebt) · Order auf - der falschen Marktseite wird abgelehnt · **Toleranz 0,02×ATR**, damit nicht jede - ATR-Zuckung die Orders löscht und neu setzt · Startup-Schonfrist gilt auch hier. - **Mit 10 Szenarien getestet** (setzen · Idempotenz beim zweiten Tick · Fill → - Gegenseite weg · Squeeze klärt sich · Gate schlägt zu · Schonfrist · Feature aus · - Box verschiebt sich · Broker lehnt ab · Nachjagd-Bremse in beide Richtungen). - Live verifiziert am Snapshot: `squeeze_pending=true`, `squeeze_max_chase_atr=0.2`, - `squeeze_pending_levels` (leer, weil eine Position offen war = korrektes Verhalten). - **Zurück zum alten Verhalten: `squeeze_pending_entry=false`.** - ⚠ **B4-Pflicht + Werkzeug:** der Live-Abstand Einstieg↔Level muss jetzt gegen ~0 - gehen (vorher Median +0,275×ATR). Dafür gibt es jetzt - **`analyze_squeeze_entry_gap.py`** (führt die zwei nötigen Quellen zusammen: das - Level steht NUR im Log, der Fill NUR in `trades`) — mit Vorher/Nachher-Split am - Umbau-Stichtag. ⚠ **Die Erfolgsmeldung ist paradox:** eine per Pending gefüllte - Order erzeugt **keine** `AUTO-SQUEEZE-ENTRY`-Logzeile (die entsteht nur im - Market-Fallback). Squeeze-Trades **ohne** Log-Treffer sind also der Erfolg, nicht - ein Fehler — das Skript weist sie deshalb getrennt aus. Überwacht als Messung - `squeeze_entry_gap` im `measurement_reminder.py` (≥12 Squeeze-Trades ab dem - Umbau; Stichtag exakt 05.08. 08:55, nicht Mitternacht — der 08:33-Trade lief noch - über Market). Bleibt der Abstand bei ~0,3×ATR, füllt die Pending nicht → Broker- - Mindestabstand und die Logzeile „Squeeze-Pending nicht platzierbar" prüfen. - ⚠ **Zahlen-Korrektur (05.08.):** zuerst standen hier Median +0,306 / Ø +0,741 — - das war eine Scratchpad-Auswertung mit FESTEM Stundenversatz. `analyze_squeeze_ - entry_gap.py` rechnet DST-korrekt über `zoneinfo` und liefert **+0,275 / +0,686** - (n=34 unverändert). Gültig sind die zweiten Werte; die Schlussfolgerung ändert - sich nicht. - - **⚠⚠ EIN PENDING-FILL LIEF AN DER GESAMTEN BUCHFÜHRUNG VORBEI (Fix 2026-08-06, - User-Auftrag „überprüfe die heutigen autotrades").** Der Befund kam nur zustande, - weil die Frage überhaupt gestellt wurde: die DB meldete für den 06.08. **null - Bot-Trades**, das Log dagegen vier gefüllte `SQZ-STOP`-Orders. Beides stimmte. - **Mechanismus:** eine ruhende Stop-Order füllt **im Broker**, nicht über - `engine._open`. Die Position wird erst später von `trader._refresh_locked` per - magic-match **adoptiert** — und dieser Pfad ruft `history.log_trade_open()` nur mit - Ticket/Symbol/Richtung/Lots/Preis auf. Es fehlten damit **alle** Felder, die der - Market-Pfad setzt: `setup`, `sl_at_entry` und die **16 `ctx_*`-Spalten** der - Telemetrie-Stufe 1. Vier reale Trades waren so von manuellen **nicht - unterscheidbar**. - ⚠ **Die Tragweite ist größer als „ein NULL-Feld":** genau die Messpipeline, die den - Pending-Umbau vom 05.08. kontrollieren soll, war **blind für ihn** — B4-Monitor, - `squeeze_b5` (Abbruchregel!) und `squeeze_entry_gap` zählten null Trades, und - `sl_at_entry` fehlte ausgerechnet bei den Trades, an denen die M5-SL-Umstellung zu - prüfen war. **Deployment-Drift Fall 8**, und die erste Variante, bei der nicht die - Strategie driftet, sondern **ihre Beobachtbarkeit**. Ein Umbau, der den Bestellweg - ändert, ändert auch, welcher Code die Bücher führt — das gehört beim Umbau geprüft. - **Fix:** `history.tag_bot_trade(ticket, setup)` (UPDATE nur wenn `setup IS NULL`, - stiller Fail-open — `core/history.py` hat bewusst keinen Logger) + - `engine._pending_tickets` (Ticket → Quelle, beim Platzieren gesetzt, beim - Stornieren/Taggen entfernt) + `_check_pending_fill()` im `_pos_loop` VOR dem - Pending-Manager. Die Zuordnung ist eindeutig, weil bei einem Pending-Fill die - **Positions-Nummer identisch mit der Order-Nummer** ist (an allen vier - Broker-Datensätzen des 06.08. verifiziert) — kein Zeitfenster-Raten. Der Fix setzt - zusätzlich `_bot_open_ticket`/`_bot_open_source` und die Entry-Zähler, damit - 15-Min-Regel, B4-Monitor und Snapshot wieder dieselbe Wahrheit sehen. - Mit **6 Szenarien getestet**, darunter die beiden gefährlichen: fremde/manuelle - Position wird NICHT getaggt, und derselbe Fill zählt bei wiederholten Ticks nur - **einmal**. Die vier Trades des 06.08. wurden nachgetragen (DB-Backup - `oil_widget_history.db.bak-2026-08-06`). - ⚠ **Bewusst NICHT nachgetragen: `ctx_*` und `sl_at_entry`.** Die stehen für den - Zustand **im Moment des Fills** und lassen sich nachträglich nur rekonstruieren, - nicht messen — eine rekonstruierte Zahl in einer Telemetrie-Spalte wäre später von - einer gemessenen nicht mehr unterscheidbar. Ab jetzt sind sie für Pending-Fills - weiterhin leer; wer sie braucht, muss den Kontext beim **Platzieren** der Order - einfrieren (offen, nicht gebaut). - - **⚠ OFFEN: Pending-Churn im SIGNAL-Pfad (gefunden 06.08., nicht behoben).** Am - 06.08. wurde dieselbe SELL_STOP viermal in 76 s gesetzt und storniert — **bei - identischem Level 74,981**, es wandert also nichts. Die Ursache steht in - `recommendations.block_reason`: der Grund **pendelt zwischen `entry_room` und - `breakout_pending`**. Sobald das Raum-Gate den Kurs kurz stummschaltet, liefert - `_pending_ziel()` `{}` und der Manager storniert; eine Sekunde später ist das Gate - wieder offen und die Order wird neu gelegt. - ⚠ **Das ist wieder Deployment-Drift, nicht nur Log-Lärm:** `backtest_auto_signal_v3.py` - hat den Pending-Vorteil (ØR +0,35/+0,32 gegen −0,09/−0,11 am Markt) unter der - Annahme gemessen, dass die Order **durchgehend am Level liegt**. Flackert sie im - 5-s-Takt, geht genau der Ausbruch verloren, der in eine Storno-Lücke fällt — und - das ist der Fall, für den sie da ist. - **Lösungsrichtung (ungebaut):** harte von weichen Storno-Gründen trennen. HART - (sofort stornieren) = Position offen · Startup-Schonfrist · Guard (Nacht/Circuit/ - News) · Richtungswechsel · Level wandert über die Toleranz · Feature aus. WEICH - (Order liegen lassen) = kurzzeitiges Stummschalten durch `entry_room`/`min_conf`/ - `breakout_pending`. Argument dafür: die Order füllt nur, wenn der Kurs das Level - **bricht** — dann hat er sich vom Gegenlevel wegbewegt, die Raum-Bedingung ist im - Fill-Moment also besser als im Flacker-Moment. Argument dagegen: das ist eine - Aussage über den Einstiegs-Filter und damit **nicht rein infrastrukturell** — - vor dem Bau messen (Variante „Order bleibt liegen" gegen „Order flackert"). -- **Auto-Squeeze-Entry = LIVE AUTONOM (User-Vorgabe 2026-07-16, `[trading] - auto_squeeze=true`, Default FALSE):** der Bot eröffnet **selbständig eine echte - Order**, sobald der **gemessen-validierte** Squeeze-Ausbruch feuert (`wave.squeeze. - state=='active'`) UND **flat** ist — NICHT auf dem Wave-Signal (das ist der - Dry-Run-Verlierer), sondern nur auf dem 2-Stichproben-positiven Squeeze. - `engine._check_auto_squeeze` im `_pos_loop` (1 s): **1× je Ausbruch** (Dedup über - (dir, level); re-armt, wenn der Squeeze klärt), öffnet via `open_long/short` (→ - Trailing an, Emergency-Stop armiert 2 % beim nächsten Tick), Log `🚀 AUTO-SQUEEZE- - ENTRY` + Telegram + Fehlschlag-Push. Snapshot `auto_squeeze`/`squeeze_entry_count`; - Toggle `POST /api/autosqueeze` (`set_auto_squeeze`) = Dashboard-Button **🚀 BRK** - (blau=AN, ersetzt den alten Dry-Run-AUTO-Button). **Dead-Hour-Guard = AN - (User-Vorgabe 2026-07-24, `[trading] auto_squeeze_skip_night=true`) — **Fenster - seit 2026-08-05 auf `3,4,5,6` VERKÜRZT** (`[trading] auto_squeeze_night_hours`, - User-Vorgabe „verkürze auf die wirklich toten Stunden"; war fest 0–7):** der Bot - eröffnet in diesen Stunden **KEINE** Squeeze-Trades (`_squeeze_night_hours` → - `_check_auto_squeeze` return; kein Entry UND kein Reverse; leere Liste = aus). - ⚠ **`_SQUEEZE_NIGHT=(0..7)` bleibt bestehen, ist aber NICHT mehr das Gate** — es - ist jetzt nur noch die STATISTISCHE Nacht-Definition für die Entry-Checkliste - (dort gilt die Kostenfalle weiterhin, s. u.) und den Tag/Nacht-Split im - B4-Monitor (damit der Vergleich mit den Altdaten stabil bleibt). Der - Auto-Signal-Pfad nutzt dasselbe neue Fenster (ist ohnehin aus). - ⚠⚠ **Die Verkürzung ist NICHT ertrags-begründet, sondern preis-begründet** — - gemessen ist **keine einzige Nachtstunde in beiden Halbjahren negativ**, jede - geblockte Stunde kostet Ertrag (Details beim Fallbeispiel unten). 3–6 ist - schlicht das **günstigste Fenster mit noch ≥4 h Schutz**: Preis ΣR - **−12,5/−62,9 R** gegen **−42,4/−242,6** bei 0–7 = **~27 %** der bisherigen - Kosten. Freigegeben sind **0, 1, 2 und 7 Uhr**. ⚠ Std 0 ist dabei die **beste - Stunde des ganzen Tages** für den Squeeze (ØR +1,48/+1,51) — sie zu blocken war - der teuerste Einzelposten. Live verifiziert am neuen Snapshot-Feld - `squeeze_night_hours` + Startup-Log „Auto-Squeeze-Nachtsperre: 3:00, 4:00, 5:00, - 6:00". Zurück auf den alten Stand: `auto_squeeze_night_hours = 0,1,2,3,4,5,6,7`. - Doppelt begründet: (1) **gemessen** unrentabel (`backtest_realcosts.py`, Spread÷Nacht- - ATR 0,32–0,50×ATR, beide Halbjahre negativ = Kostenfalle) und (2) **kein Trade - soll unbeaufsichtigt nachts feuern, während der User schläft** — Auslöser: „müsste - automatisch schließen wenn ich nicht da bin, z. B. nachts". Der Guard LÖST das, - indem er den Trade gar nicht erst öffnet; tagsüber (User eher erreichbar) läuft der - bestehende Auto-Close-Stack (Broker-SL 2×ATR broker-seitig + Trailing + Time-Stop + - S/R-Close). ⚠ Historie: bis 2026-07-17 war der Guard AUS (User wollte rund um die - Uhr), am 2026-07-24 auf User-Wunsch **zurück auf AN**. `=false` schaltet ihn wieder - aus (dann auch nachts, unbeaufsichtigt). Einen engen Schutz-%-Stop NUR für Squeeze - wurde bewusst NICHT gebaut (enger %-Stop unter Margin-Sizing wird vom Squeeze- - Rauschen ausgelöst, −48,94-€-Tag — der Squeeze braucht Raum). - ⚠⚠ **FALLBEISPIEL 05.08.2026 — der Guard hat 5 Ausbrüche geblockt, darunter den - Einstieg direkt vor einem 1,8-$-Rutsch (User-Frage „warum ist der Squeeze nicht - eingestiegen?").** Nachgerechnet mit der ECHTEN `_squeeze_one` auf den M5-Bars - (nicht aus dem Log geschlossen — der Squeeze-ZUSTAND wird nirgends geloggt, nur der - Entry): zwischen 02:00 und 07:30 Berlin feuerte 5× `active`, **alle in 0–7 Uhr**. - Der Bot war ab 01:15 flat, die Nachtsperre war also der ALLEINIGE Blocker (kein - Cooldown, keine offene Position). Kursverlauf: Hoch **76,373** um 03:30 → Tief - **74,548** um 04:40 = **−1,825 $ in 70 min**; der Ausbruch **04:00 SHORT @ 76,11** - lag praktisch auf dem Hoch. - **Mit dem kanonischen Exit** (`exit_model.LIVE`, Echtkosten aus dem Bar-Spread — - bewusst NICHT „maximale Auslenkung", s. die Korrektur bei der 15-Minuten-Regel): - 02:00 SHORT **+3,28 R** · 03:00 LONG −0,24 · 04:00 SHORT **+1,36 R** · 06:15 LONG - −0,60 · 07:30 SHORT −0,24 → **Σ +3,56 R ≈ +95 € bei 2,66 Lots**. - ⚠ **Trotzdem kein Argument gegen den Guard:** n=5 an EINEM Tag, und **92 % davon - stecken in zwei Trades** — ohne den 02:00er bleiben +0,29 R ≈ +12 €. Ein Einzeltag - taugt nicht als Beleg, auch nicht als widerlegender (dieselbe Lehre wie am 01.08.). - Die Guard-Begründung ruht auf 2 Halbjahren; dazu kommt der nicht-ökonomische Grund - (keine unbeaufsichtigten Nacht-Trades), der von Zahlen gar nicht berührt wird. - ✅ **Ein messbarer Ansatz ergibt sich aber:** die Nacht-Begründung ist eine - **Kosten**-Aussage (Spread÷ATR 0,32–0,50). In dieser Nacht lagen die realen Kosten - bei **0,11–0,22×ATR**, also klar UNTER der Falle — weil der ATR hoch war. Ein - **kosten-basiertes statt stunden-basiertes Gate** („blocke, wenn Spread/ATR > X") - würde genau solche Nächte durchlassen und ruhige weiter sperren. - ⚠⚠ **GEMESSEN am selben Tag (`backtest_cost_gate.py`, 80k M5, 2 Halbjahre, - kanonischer Exit, Echtkosten, ECHTE `_squeeze_one`) — und das Ergebnis fällt - anders aus als die Frage lautete.** - **(a) Kein Kosten-Gate ersetzt das Stunden-Gate** nach der vorab fixierten Regel - (ΣR **und** ØR in beiden Hälften ≥ Stunden-Gate, plus Nachbar-Schwellen): alle 7 - Schwellen 0,15–0,50 fallen durch. - **(b) Der eigentliche Befund: das STUNDEN-Gate selbst kostet Geld — in BEIDEN - Hälften.** Sequentiell mit EINEM Positions-Slot (die konto-relevante Rechnung; - ein geblockter Nacht-Trade gibt den Slot frei): - | Variante | H1 ΣR | H2 ΣR | - |---|---|---| - | **ohne Gate** | **+369,5** | **+778,1** | - | Stunden-Gate 0–7 (live) | +327,1 | +535,5 | - | Kosten-Gate 0,30 | +356,3 | +760,1 | - | Kosten-Gate 0,35 | +369,3 | +781,9 | - → das Live-Gate kostet **−42 R (H1) und −243 R (H2)**. Die von ihm GEBLOCKTEN - Ausbrüche sind in beiden Hälften **positiv** (ØR +0,496/+0,589) — in H1 sogar - besser als die behaltenen (+0,382). Es wirft gute Trades weg. - ⚠⚠ **MECHANISMUS BELEGT — die Nachtsperre ist auf der FALSCHEN Population - begründet (Deployment-Drift, Fall 6).** Die Nacht-Kostenfalle ist echt und - reproduziert (`backtest_realcosts.py`): über ALLE Nacht-Bars Median **0,333** / - Ø **0,387** ×ATR gegen Tag 0,163/0,185. **Aber der Squeeze feuert dort gar nicht** — - er verlangt einen Ausbruch, also anziehenden ATR. Seine Nacht-Ausbrüche kosten nur - **Median 0,183 / Ø 0,191** (Tag 0,156/0,162), liegen also praktisch auf Tag-Niveau - und weit unter der Falle. **Der Squeeze selektiert sich aus der Kostenfalle - heraus.** Gemessen wurde die Falle am Wellensignal, angewandt wird das Gate auf den - Squeeze — dieselbe Fehlerklasse wie bei der 15-Minuten-Regel (auf Wave-Entries - kalibriert, auf alle Bot-Trades angewandt, auf Squeeze gemessen schädlich). - ⚠ Nebenbefund: **ein Kosten-Gate TRENNT tatsächlich** in die richtige Richtung - (bei 0,30 hat die geblockte Gruppe H1 WR 44,9 % / ØR +0,175 gegen +0,485 behalten) - — nur ist auch sie noch positiv, deshalb kostet jedes Blocken ΣR. Bester ΣR-Wert - ist 0,35, und der blockt praktisch nichts mehr (1 bzw. 10 Trades) = faktisch „kein - Gate". - ⚠ **Methodik-Lehre zur eigenen Regel:** „ΣR UND ØR ≥ Referenz" ist als - Entscheidungsregel schlecht spezifiziert, wenn die Varianten unterschiedlich viele - Trades handeln — ØR und ΣR laufen dann mechanisch gegeneinander (0,30 schlägt das - Stunden-Gate im ΣR um +29/+225, verfehlt das ØR in H2 um 0,004). Die Regel wurde - **nicht nachträglich gelockert**; stattdessen ist der Vergleich „Gate gegen KEIN - Gate" separat ausgewiesen — er stand ohnehin in derselben Tabelle. - ⚠⚠ **DREI Gründe, warum daraus NICHT automatisch „Nachtsperre aus" folgt:** - (1) **Slippage ist nicht modelliert** — nachts ist das Buch dünner, der gemessene - Tail liegt bei 0,75×ATR über den Stop hinaus (`analyze_execution.py`). Das trifft - Nacht-Trades selektiv und ist die größte Bedrohung des Befunds. (2) **Live sagt das - Gegenteil**, wenn auch dünn: von den 31 Squeeze-Trades des B4-Fensters waren 12 - Nacht-Trades mit **−78,92 €** — allerdings alle VOR den Exit-Reparaturen vom 31.07. - (P(break) nachtrainiert, 15-Min-Regel aus, Trail 1,5→1,0). (3) Der - **Beaufsichtigungs-Grund** („kein Trade soll feuern, während ich schlafe", - User-Vorgabe 2026-07-24) ist eine Präferenz und wird von keiner Zahl berührt. - ✅ **Konsequenz (User-Vorgabe 2026-08-05 „verkürze auf die wirklich toten - Stunden"): Fenster von 0–7 auf 3–6 verkürzt** — Details oben beim Guard. - ⚠⚠ **Die Prämisse der Vorgabe hält der Messung nicht stand: es gibt KEINE toten - Stunden.** Je Stunde einzeln, sequentiell, beide Halbjahre — **alle 24 Stunden - sind in beiden Hälften positiv**, keine einzige ist beidhälftig negativ. Die - ruhigsten Nachtstunden (n über beide Hälften): 5 (41 Ausbrüche) · 4 (56) · 3 (61) - · 6 (62); die aktivsten 7 (75) · 0/2 (72) · 1 (71). ⚠ **Std 0 ist die stärkste - Stunde des gesamten Tages** (ØR +1,483/+1,510, ΣR +22,2/+86,1) — ausgerechnet sie - lag im alten Gate. - Damit ist die Auswahl **keine Ertrags-Optimierung, sondern eine Preis-Minimierung - für eine Präferenz** — und deshalb auch nicht overfitting-gefährdet: es wird keine - prädiktive Behauptung aufgestellt. Preisliste (ΣR gegen „kein Gate"): - | Fenster | Std | Preis H1 | Preis H2 | - |---|---|---|---| - | 4–5 | 2 | −3,9 | −18,1 | - | 4–6 | 3 | −6,8 | −39,9 | - | **3–6 (gewählt)** | **4** | **−12,5** | **−62,9** | - | 2–5 | 4 | −12,8 | −77,2 | - | 0–7 (alt) | 8 | −42,4 | −242,6 | - Die 4-Stunden-Untergrenze ist **gesetzt, nicht gemessen** — darunter wäre der - Schutzzweck hinfällig. Wer weniger Schutz will, nimmt 4–5 (fast gratis); wer den - alten Stand will, trägt `0,1,2,3,4,5,6,7` ein. - ⚠ **Was die Verkürzung NICHT ausräumt:** die drei Vorbehalte oben gelten - unverändert — vor allem die **nicht modellierte Slippage** (dünnes Nacht-Buch, - Tail 0,75×ATR über den Stop). Die freigegebenen Stunden 0/1/2/7 liegen mitten in - der dünnsten Zeit. **B4-Pflicht: die Squeeze-Trades aus 0/1/2/7 Uhr getrennt - mitzählen** — bleiben sie hinter der Erwartung (ØR ~+0,5 R), war die Slippage - der Grund und das Fenster gehört zurück auf 0–7. - **Stop-&-Reverse = AUS (Default, `[trading] auto_squeeze_reverse=false`, - `_squeeze_reverse`; User-Vorgabe 2026-07-17):** War kurz scharf, dann abgeschaltet — - nur der **flat-Entry ist gemessen-validiert**, der Reverse ist unbelegt, verdoppelt - Kosten und dreht am **Ausbruch-Extrem** (real 2026-07-17 18:55: WAVE_REV_SHORT gegen - einen News-Rally-LONG-Ausbruch bei −29,64 € geschlossen, dann LONG am Erschöpfungs-Top - 82,32 — Elliott „impulse_up/5 exhausted" im selben Moment). Bei `false` bleibt ein - Gegen-Trade **unangetastet** (Drehen = manuelle Entscheidung); `_squeeze_last_sig` - wird dann NICHT gesetzt → schließt der Gegen-Trade (SL/manuell), kann der flat-Entry - auf denselben Ausbruch noch feuern. Bei `true` (historisch, reaktivierbar): läuft ein - Trade GEGEN den Ausbruch (auch manueller/Fremd-Trade!) → sofort `close(reason= - "squeeze_reverse")` (Log 🔄 + Telegram, Dedup `_squeeze_reverse_sig`), nächster flat-Tick - dreht. ⚠ NICHT gemessen (der Squeeze-Backtest nahm flat-Entry an) — deshalb Default aus. - **Exit-only-Variante („Gegen-Position beim Ausbruch schließen, OHNE Re-Entry") = - VERWORFEN (gemessen `backtest_squeeze_exit.py`, 2026-07-22, User-Idee „Breakout - schneller schließen"):** die sicherere Halb-Variante (nur schließen, nicht drehen) - fällt ebenfalls durch — in BEIDEN Hälften schlechter als der Live-Exit (SL 2×ATR+Trailing): - ΔΣR H1 −15 (immer)/−5 (nur im Minus), H2 −27/−29. Grund: der Gegen-Ausbruch ist oft ein - Fehlausbruch/Whipsaw → die Position prallt zurück, der frühe Close kappt Trades, die - sich erholt hätten. Der 07:38-Fall (LONG lief zum SL während SHORT-Ausbruch) war Varianz. - **Fazit: Squeeze taugt als ENTRY (validiert), NICHT als Gegen-EXIT.** Der einzige - Speed-Hebel bleibt die schnellere Erkennung (Variante A, ~5 s). - ⚠⚠ **DRITTE MESSUNG 2026-08-05 (`backtest_squeeze_exit3.py`) — jetzt mit dem - KANONISCHEN Exit, und der Grund für das Nein ist ein anderer als gedacht.** - Anlass: Trade T=49423144 (manueller SELL 3,49 @ 75,012) lief in 14 min 1,135 $ - ins Minus und endete am Initial-SL bei **−343 €**; User-Wunsch: „der Breakout - hätte den Trade früher schließen sollen", nachgeschärft zu „bei einem Squeeze den - SL sofort eng setzen". - ⚠ **Was an dem Tag wirklich passierte:** beim Einstieg war der Squeeze **active - SHORT** — er stand FÜR den Trade. Die Empfehlung war WARTEN (`block=entry_room`), - der Einstieg manuell. Der Gegen-Ausbruch (armed LONG @ 75,729) wurde erst ~3–5 min - später aktiv; ein Close dort hätte ≈**−219 €** statt −343 € bedeutet (~124 €). - **Gemessen (beide Varianten, kanonischer Exit, Echtkosten, 2 Halbjahre, zwei - Populationen — 2.365 Squeeze- und 1.566 Wellen-Entries):** - | Variante | Auslösungen H1/H2 | - |---|---| - | sofort schließen (Squeeze-Entries) | **1 / 1** von 809 bzw. 1.274 | - | **enger SL 0,1–0,5×ATR** (Squeeze-Entries) | **0 / 0** | - | sofort schließen (Wellen-Entries) | 8 / 8 von 386 bzw. 835 | - | enger SL (Wellen-Entries) | 2–3 / 2–3 | - ✅✅ **DAS IST DER EIGENTLICHE BEFUND: der Mechanismus feuert praktisch NIE.** - Nicht weil die Idee schlecht wäre, sondern weil sie **konstruktionsbedingt zu spät - kommt**: ein Gegen-Ausbruch verlangt eine NEUE Kompression (Box über 10 M5-Bars = - 50 min), der Ausbruch selbst weitet die Box aber gerade erst. Die mittlere - Haltedauer liegt bei **12–18 min**. Der Gegen-Ausbruch trifft also in rund **0,1 % - (Squeeze) bzw. 1 % (Welle)** der Trades ein — der Fall des Users war einer davon. - Die Kennzahl-Unterschiede in der Tabelle sind entsprechend Rauschen aus einer - Handvoll Fällen; keine Variante besteht auf beiden Populationen. - ⚠ **Auch die abgeschwächte Form („enger SL statt Close") ändert nichts** — sie - adressiert zwar genau den dokumentierten Einwand (Gegen-Ausbrüche sind oft - Fehlausbrüche), kann ihn aber nicht ausspielen, wenn sie 0-mal auslöst. - ⚠ Und sie wäre von dieser Simulation ohnehin **überschätzt**: ein enger Stop wird - von einer Bar-Simulation systematisch zu gut gerechnet (s. Auflösungs-Artefakt, - `analyze_trail_resolution.py`, 05.08.). - **Damit zum DRITTEN Mal verworfen — und diesmal mit einer Begründung, die nicht - vom Exit-Modell abhängt.** - **Re-Test unter Vola-Regimen = ERNEUT VERWORFEN (gemessen - `backtest_squeeze_exit_vol.py`, 2026-07-24, User-Frage „Markt gerade sehr volatil — - wirklich nicht profitabel?"):** Verschärfter Test mit echten SQUEEZE-Entries (statt - EMA-Crosses), 4 Varianten (immer/nur-im-Minus/nur-Verlust>0,5×ATR), ATR-Terzil-Split - je Hälfte + Extra-Fenster letzte ~30 Tage. Ergebnis: Effektgröße überall ~0 - (±0,002–0,005 R/Trade bei ~1150 Trades/Hälfte), **Vorzeichen kippt zwischen den - Regimen** (MODL: H1-Hochvola −1,6 / H2-Hochvola +3,0 / letzte 30 Tage −0,3) → nach - der vorab festgelegten Regel (beide Hälften UND Hoch-Vola-Terzil ≥ BASE) fällt JEDE - Variante durch. **Kernbefund: Worst-Case ist in ALLEN Varianten identisch** - (−2,33/−2,47 = SL-Deckel) — der Gegen-Squeeze-Exit reduziert das Tail-Risiko um - exakt 0, weil bei echten schnellen Brüchen immer der SL zuerst greift; er kappt nur - langsame Kriecher, von denen sich >50 % erholt hätten. (Methodik-Hinweis: absolute - ØR pessimistischer als im validierten Squeeze-Backtest — Entry am Bar-Close statt - am Level, keine Flat-Bedingung; für den Varianten-VERGLEICH neutral, da alle Exits - denselben Entries folgen.) 3. Messung derselben Idee (Reverse · Exit-only · - Vola-Regime-Re-Test) — gleiche Antwort. - **S/R-Close kappt Squeeze-Runner? = GEPRÜFT, KEINE Änderung (gemessen - `backtest_squeeze_srclose.py`, 2026-07-24, User-Beobachtung „Squeeze wird direkt am - nächsten S/R geschlossen"):** Der Log bestätigt, dass Auto-Squeeze-Trades regelmäßig - vom P(break)-S/R-Auto-Close am nächsten Gegen-Level geschlossen werden (der Squeeze - wurde mit reinem Trailing validiert, ist runner-abhängig — Verdacht: Runner gekappt). - Direkter Vergleich NUR auf Squeeze-Entries (Box≤2,5×ATR, k=0,1): **Exit A = nur - Trailing (validiert)** vs. **Exit B = +S/R-Close (aktuell live)** = **Wash, kein - robuster Schaden** — H1 ØR +0,190→**+0,224** (ΣR +40→+47, B besser), H2 ØR - +0,394→+0,377 (ΣR +290→+278, B marginal schlechter) = Δ ±0,02 R/Trade (Rauschen). - **Warum unkritisch:** der S/R-Close ist P(break)-gegated (schließt nur wenn Level - wahrscheinlich HÄLT, P<60 %) → echte, starke Ausbrüche haben hohe P(break) und - laufen weiter; **PF/Runner-Tail bleiben erhalten** (H2 PF 1,58=1,58), nur die WR - steigt (50→55 %, Abpraller früher als Klein-Gewinn kassiert). Squeeze-Trades vom - S/R-Close auszunehmen ist also NICHT belegt. (Reaktivierbar per Toggle, falls der - User trotzdem die Runner nie kappen will — kostet gemessen ~nichts, ist aber - Präferenz, kein Edge.) - **S/R-Close-Cooldown (2026-07-17, Koordinations-Fix, kein Backtest):** nach einem - S/R-Auto-Close eröffnet der Auto-Squeeze **~3 min (`_SQUEEZE_SR_COOLDOWN_S=180`) - nicht in GLEICHER Richtung** wieder. Grund: der S/R-Close ging von „Level hält" - (P(break)0`: erreicht der **Tages-P&L - (realisiert heute + offene Position)** −pct % der Balance, schließt der Bot die - Position (`closed_by=circuit_breaker`) und sperrt **neue Auto-Trades bis zum - nächsten Tag** (`_check_auto_squeeze` prüft `_circuit_halted()`). `_check_circuit_ - breaker` im `_pos_loop` (1 s, unter Startup-Grace pausiert), realisierter Tages-P&L - ~30 s gecacht (`_day_realized_cached`), Berlin-Datum als Trip-Marker (`_circuit_ - tripped_date`) → automatischer Reset am neuen Tag. Log 🛑 + Telegram (eigener, - nicht vom Close-Gate betroffen). Snapshot `circuit_breaker {enabled, limit_pct, - limit_eur, day_total, tripped}`, Frontend `#circuit-banner` (rot bei ausgelöst, - bernstein ab 60 % des Limits, v=115). ⚠ **80 %-Margin-Sizing bleibt** (User- - Entscheidung, 3.+ Mal bestätigt; `risk_pct=0`) → der 8 %-Tagesstopp schützt v. a. - gegen die **Verlust-Serie/-Kaskade**, NICHT vollständig gegen den Einzeltrade - (dessen 2×ATR-SL unter 80 % Margin allein schon >8 % verlieren kann). 0 = aus. - Mit 5 synthetischen Szenarien getestet (nicht-zu-früh · Trip+Close · kein - Doppel-Close · aus · Auto-Squeeze-Block). ⚠ Der frühere „NICHT einbauen"-Grund - gilt nicht mehr — Ziel hat sich zu **autonomem Handel** verschoben, und ein - unbeaufsichtigter Agent braucht die Notbremse im Code. -- **Notfall-Stop = AUS beim Öffnen (Stand 2026-07-24, `auto_emergency_margin_pct=0`, - `auto_emergency_pct=0`, `auto_emergency_loss=0`).** User-Vorgabe 2026-07-24: „gewinn - close und notfall close … nicht setzen beim Eröffnen" → alle drei Auto-Arm-Modi 0, - beim Öffnen einer Position wird KEIN Notfall-Stop gesetzt. Nur ein **manuell** im - UI-Feld eingegebener Wert armiert (bleibt bis zum nächsten Positionswechsel). - **⚠ Vertipper-Schutz `[trading] emergency_min_eur=3` (Fix 2026-07-27):** ein manuell - gesetzter Notfall-Stop UNTER diesem Minimum wird als „aus" behandelt (nicht - scharfgestellt) + Log-Warnung. Grund: ein Sub-Spread-Wert schließt den Trade - **sofort** UND vererbt sich per `_emergency_remember` auf ALLE Folge-Trades — real - am 2026-07-27 setzte der User versehentlich −1 € → jeder neue Trade wurde in - Sekunden per Notfall-Close gekillt (Tickets 48504360/48504814 u. a., „sofort - geschlossen"). Guard in `set_emergency` (greift NUR auf die manuelle Eingabe; - die %-Auto-Arm-Pfade sind eh 0). 0 = kein Floor. - Kurz-Historie des margin-%-Modus: 2026-07-17 komplett raus (2 % ≈ 0,76×ATR zerschoss - die Squeeze-Trades, −48,94-€-Tag) · 2026-07-20 wenige Stunden als **3-%-GAP-NETZ - (Balance-Basis)** mit `risk_pct=1.5` · **2026-07-23 kurz margin-basiert `=3.0`** - (dritter Modus, ausgelöst durch zwei Notfall-Closes am selben Abend −11,20 €/−20,47 €, - wobei die Schwelle beim ersten Trade noch auf einem händischen UI-Restwert von −5 € - hing) · **2026-07-24 auf User-Wunsch wieder ganz AUS.** Der margin-%-Modus bleibt im - Code (retry-sicheres Armieren via `_emergency_margin_armed_ticket`, hat Vorrang vor - Balance-%/Fixwert wenn >0), reaktivierbar per ini. ⚠ Kopplungs-Regel (falls - reaktiviert): unter Margin-Sizing kann ein enger %-Stop schneller greifen als der - 2×ATR-SL. Schutz-Stack aktuell: **Broker-SL 2×ATR + Trailing + Time-Stop** (der - Auto-Notfall-Stop ist inaktiv). - **UI-Feld** (`#emg-input`, User-Vorgabe 2026-07-22, v=105) in der Trade-Leiste - (rot, neben Gewinn-Close), Enter blurrt nur (kein Doppel-Send/ungewolltes 0, - gleicher Fix wie Mindestgewinn 2026-07-22) — ein am Handy gesetzter Wert - überschreibt den Auto-Arm für den laufenden Trade und bleibt bis zum nächsten - echten Positionswechsel maßgeblich (mit Test verifiziert). ⚠ Server-seitig: wirkt - nur bei laufendem Server (Update-Reboot-Lücke → „Auch beachten"). Der Server - schließt die Position - **sofort**, sobald `P&L ≤ −Wert` (`engine._check_emergency` im `_pos_loop`, 1 s; - wirkt auch bei gesperrtem Handy). **Auto-Arm — drei Modi (Priorität: margin-% > - Balance-% > Fixwert):** - (a) **NEU — % der Einsatz-Margin dieser Position (aktiv, User-Vorgabe 2026-07-23):** - `[trading] auto_emergency_margin_pct=3.0` → Schwelle = **pct% × Margin** (dieselbe - Margin wie beim Gewinn-Close, aus `trader.refresh()`). **Retry-sicher** (Fix - 2026-07-23, gleiches Muster wie beim Gewinn-Close): läuft NICHT nur einmalig beim - Ticket-Wechsel, sondern jeden `_pos_loop`-Tick erneut, bis `order_calc_margin` - einmal >0 liefert (`_emergency_margin_armed_ticket`-Retry-Schutz) — verhindert, - dass bei einem Broker-Race im ersten Tick nach dem Öffnen dauerhaft ein stalen - Wert der vorherigen Position hängen bleibt. Mit 4 synthetischen Szenarien - verifiziert (sofort verfügbare Margin · verzögerte Margin/Retry · manuelles - Überschreiben bleibt erhalten · Neustart überschreibt NICHT). Snapshot - `emergency_margin_pct`. - (b) **% des Kontos (Fallback, wenn margin-%=0):** `[trading] auto_emergency_pct=2.0` - → bei jeder neuen Position wird die Schwelle auf **pct% × Balance** gesetzt - (`_emergency_pct`, skaliert mit dem Konto; z. B. 2 % von 831 € = −16,62 € ≈ - 0,4×ATR; 1× auf 1 % getestet, war zu eng → 2 %). UI-Feld zeigt im Flat-Zustand - den dynamischen Wert (Frontend rechnet `pct%×balance`, Snapshot `emergency_pct`). - (c) **Fixwert (Fallback, wenn beide %-Modi aus):** Schwelle = **zuletzt - genutzter Wert** (`_emergency_remember`, Startwert `[trading] auto_emergency_loss` - Default **10**, 0 = aus). - Setzt der User im UI einen neuen Wert, wird der gemerkt und ab dem nächsten Trade - übernommen. Aktiver Stop verfällt nach Auslösen UND bei Positions-Close, der - gemerkte Wert bleibt. **Neustart-fest (Fix 2026-07-17, `emergency_state.json`):** - Der aktive Notfall-Stop/TP wird mit dem **Ticket** persistiert (`_save/_load_emergency_ - state`, atomarer Write; bei Auto-Arm UND manuellem Set). Erkennt der Server dieselbe - offene Position nach einem `restart_server.bat` wieder, **stellt er den persistierten - Wert wieder her** (`_check_auto_close`: `ticket == _emergency_state_ticket` → restore, - Log „Auto-Close wiederhergestellt") statt auf die pct-Automatik zurückzuarmieren. Grund: - real am 2026-07-17 14:30 setzte ein Neustart bei offenem Trade die von Hand gesetzten - −25 € auf −19,42 € (2 %) zurück → Notfall-Close bei −19,64 statt bei −25. Eine echt - neue Position (anderes Ticket) armiert normal aus pct/Merkwert und persistiert neu. - ⚠ **Trailing-Zustand ebenfalls neustart-fest (Fix 2026-07-29):** `emergency_state.json` - persistiert jetzt zusätzlich `trail` (= `trail.enabled`) per-Ticket. Erkennt der Server - dieselbe Position wieder und war das Trailing vor dem Neustart AN, **reaktiviert er es - automatisch** (`_check_auto_close`-Restore-Zweig → `trail.toggle`, Log „Trailing - wiederhergestellt … nach Neustart"). Grund: real am 2026-07-29 lief eine Auto-Squeeze- - Position nach mehreren Server-Neustarts nur mit dem **Broker-SL** (Trailing/Breakeven/ - Time-Stop weg), weil `trail.enabled` nur im Speicher lebte und die Wiedererkennung - (magic-match) es NIE reaktivierte — der User musste es von Hand einschalten. War das - Trailing AUS (z. B. nach manuellem SL/TP via `/api/sltp` → `trail.deactivate`), bleibt - es aus (der persistierte Zustand entscheidet — kein Überschreiben eines manuellen TP). - Der Zustand wird bei **jeder Änderung** persistiert (Change-Detection im `_pos_loop`, - fängt auch die Selbst-Deaktivierung des Trailings bei manueller SL/TP-Erkennung). - **„aus"-Button schaltet auch den Auto-Arm ab** (setzt - `_emergency_remember=0` — vorher blieb der gemerkte Wert und re-armte beim nächsten - Trade, „aus" wirkte nur bis dahin; Bug gefixt 2026-07-07). Gilt genauso fürs Gewinn-Ziel. (Auto-Arm 1× auf manuell-only gestellt, nach Fehl-Trade - wieder reaktiviert — echter Schutz gegen falsche Empfehlungen; Signal-Filter dagegen - 4× gemessen wirkungslos/schädlich, s. `backtest_rev_htf.py`.) `engine.set_emergency()` · `POST /api/emergency {value}` · - `snapshot.emergency_loss`. Das ist KEIN automatischer Circuit-Breaker, sondern - ein manueller Hard-Stop (vom User gewünscht). **Benachrichtigung IMMER** (Ausnahme - vom „Close-Push nur im Plus"): bei Erfolg „🚨 Notfall-Close", bei **Fehlschlag** - „⚠ Notfall-Close FEHLGESCHLAGEN — manuell schließen!" (Schwelle bleibt dann aktiv, - nächster Tick versucht erneut). Eigener `send_telegram`, **nicht** vom - `pnl>0`-Gate (`history`) noch vom 5-Min-Cooldown betroffen. - -## ⚠⚠ TELEGRAM WAR NICHT KAPUTT — ES SENDETE NUR NICHTS MEHR (2026-08-11) - -User: „ich bekomme keine Telegram-Nachrichten mehr." -✅ **Kanal nachweislich intakt:** `getMe` → `@AH_Oiltrader_bot`, `getChat` → privat -„Axel", **Testnachricht zugestellt** (message_id 1764). Im gesamten Log **keine -einzige** Fehlermeldung; der Tagesreport kam täglich (08.–11.08. je 07:3x, alle -`ok`). Letzte Nicht-Report-Nachricht **07.08. 22:49**, davor am 06.08. 19:33–19:58 -im 5-Minuten-Takt = die Close-Alarm-Sperre. -⚠⚠ **DIE URSACHE: ALLE NEUN AUSLÖSER WAREN AUS** — Auto-Squeeze (über -`runtime_state.json`, unbemerkt), Auto-Signal, S/R-Auto-Close, Flip-Close, -15-Minuten-Regel, Circuit-Breaker und die **drei** Notfall-Stop-Modi. **Jede -Abschaltung war für sich begründet und gemessen; zusammen ergaben sie eine -Stille, die niemand beschlossen hatte.** Übrig blieb genau ein Live-Trigger, der -🔔 Close-Alarm — und der braucht eine **gerichtete** Empfehlung, die es nur in -**5 %** der Zeit gibt (letzte 7 Tage: 508 von 9.976 Zeilen). -⚠ Dazu kam: der Trade-Abschluss-Push war früher bewusst entfernt worden. Deshalb -kam auch der **−87-€-SL vom 10.08. wortlos**. - -✅ **GEBAUT: Close-Benachrichtigung `engine._check_close_notify`** -(`[telegram] close_notify_min_eur = 50`, 0 = aus; Backup -`oil_widget_config.ini.bak-2026-08-11-closenotify`). Feuert bei **jedem** -geschlossenen Trade ab der Schwelle — **hängt an KEINER Automatik**, also auch -bei manuellem Close und Broker-SL. Meldet Betrag, Richtung, Lots, Ein-/Ausstieg, -**Schließgrund** und Dauer. Snapshot-Feld `close_notify_min`. -⚠ **Schwelle aus den Daten gewählt, nicht geraten:** bei ~17 Trades/Tag liefert -50 € rund **2,4–6,1 Meldungen/Tag** und erfasst **54–80 %** des bewegten Geldes. -⚠ **Eine absolute Euro-Schwelle altert mit der Positionsgröße** — Median |P&L| -über 30 Tage 14,65 €, über die letzten 7 Tage 35,80 €. Bei wachsendem Konto -kommen mehr Meldungen. Bewusst so: ein Mensch denkt in Euro. -⚠⚠ **Der Unterschied zum entfernten Push ist die SCHWELLE** (der alte feuerte bei -jedem Close), und sie liegt in der **Engine**, nicht in der DB-Schicht. -⚠ **Vorgemerkt statt sofort gesendet:** der Flat-Zustand kommt aus -`trader.snapshot()` und ist dem DB-Schreiber gelegentlich einen Tick voraus — -`exit_time IS NULL` heisst „noch nicht fertig", NICHT „kein Trade". Ohne diese -Unterscheidung meldete es einen Close **ohne P&L**. Aufgabe nach ~30 s. -✅ **7 Tests** (`tests/test_close_notify.py`, Netz abgefangen) + **Mutationsprobe -in beide kritischen Zweige**: Schwelle entfernt → `test_unter_schwelle` fällt · -Retry-Fall als „kein Trade" behandelt → `test_db_noch_nicht_fertig` fällt. - -✅ **AUTO-SQUEEZE WIEDER AN** (User-Entscheidung 2026-08-11) — über den offiziellen -Toggle `POST /api/autosqueeze`, damit ini UND `runtime_state.json` übereinstimmen -(`{"ok":true,"enabled":true}`). Der Config-Wächter meldet ihn seitdem nicht mehr. -⚠ Damit läuft die **B5-Abbruchregel** weiter (11/20 Trades) — ohne laufenden -Trader wäre sie nie abgelaufen. Latte unverändert: Verhältnis < 1,0 ODER PF < 1. - -## Mobile-UI (web/) & Alarme -- Aufbau: **Tab-Navigation ganz oben** (sticky: Dashboard/**SIG Live**/**SIG Anzeige**/Logs/Statistik/News), - darunter Header. -- **Design-System (`style.css`, Pass 2026-07-15, KEIN Framework — Tailwind bewusst - verworfen wegen WireGuard/Offline + kein Node):** Tokens in `:root` — Flächen-Leiter - `--bg < --surface-2 < --card`, `--border`/`--line`, Text `--dim`/`--bright` - (+`--text-*`-Aliasse, waren undefiniert = Bug gefixt), Radien `--r-sm/-r/-r-lg`, - `--shadow(-sm)`, `--mono`. **Font:** UI = System-Sans + `tabular-nums`; Zahlen-Werte - (Kurs/PnL/Felder/Logs) = `--mono`. Karten mit Elevation-Schatten, Fokus-Ringe auf - Feldern, Transitions auf Tabs/Buttons. Neutrale Hex-Literale → Tokens (Konsistenz). -- **Trade-Leiste** (`#trade-bar`, über der Gesamtempfehlung, nur bei offener Position, - 2026-07-15): **Laufzeit-Uhr** (2026-07-30, User-Wunsch „Uhr … wie lange diese schon - läuft"): `trader.snapshot()` liefert `open_time` = Öffnungszeit als **echte Epoch** - (Broker-Offset korrigiert, aus `position.time`); Frontend zeigt sie als Feld - `#tb-dur` **und** kompakt im Kachel-Titel (`#tb-runtime`, „⏱ 1:23:45"). Der Ticker - läuft **eigenständig 1×/s** (`_tickTradeDuration`), zählt also flüssig zwischen den - Snapshots; ab **2 h** bernstein (Time-Stop-Nähe: 120 min in Phase „Init"). ⚠ Zwei - Fallen beim Bau: (1) `_broker_offset_s()` liest `self.symbol`, die beim **Adoptieren** - einer Position noch NICHT gesetzt ist → Offset 0 → Zeit lag 3 h in der Zukunft; die - Funktion nimmt jetzt `sym` als Parameter (`_broker_offset_s(pos_sym)`). (2) Selbst- - heilung: liegt `open_time` >60 s in der Zukunft, wird sie neu berechnet; beim Close - auf 0 zurückgesetzt (kein stale Wert für den Folge-Trade). Berechnung nur 1× je - Position (nicht je 1-s-Refresh). Weiter in der Zeile: Richtung (▲LONG/▼SHORT) · Lots · Einsatz · **3 Eingabefelder - `.tb-field` (Label über Feld, rot=`.stop`/grün=`.gain`): SL · TP · Gewinn-Close - (`#srmin-input`)** (Notfall-Close-Feld **2026-07-17 entfernt** — Notfall-Stop ganz aus). - SL/TP: Enter/Verlassen → - `POST /api/sltp` → setzt am Broker **und schaltet Trailing ab** (`trail.deactivate`); - Vorbefüllung aus `position.sl/tp`, Broker-Ablehnung → Toast. Gewinn-Close - (2026-07-15 aus der Position-Kachel hierher verschoben, User-Vorgabe — gleiches Design) - behält ID/Handler (`/api/srclose_min`). **Auch der - Auto-Close-Schalter** (`#srclose-btn`, jetzt `.tb-btn`-Box, AN/AUS grün/grau) sitzt in - der Zeile. Titel „aktueller Trade läuft"; Richtung/Lots/Einsatz = Anzeige-Boxen - (`.tb-val`), Rest editierbar — alle im selben `.tb-field`-Look. Position-Kachel hält - nur noch `cur-near`-Warnung + `pos-srhint`. (`.emg*`/`.sr-toggle`-CSS entfernt.) -- ⚠⚠ **CHARTS-TAB KOMPLETT ENTFERNT (User-Vorgabe 2026-08-05, v=142).** Weg sind: - Tab-Button, `#view-charts`, der gesamte `app.js`-Chart-Block (Kartenaufbau, - `renderChart`, `loadCharts`, 20-s-Refresh, Resize-Handler), die Chart-CSS-Regeln, - die **vendorte Bibliothek** `web/lightweight-charts.standalone.production.js` - (164 KB) samt ` + diff --git a/web/index.html.bak-2026-08-19-ui b/web/index.html.bak-2026-08-19-ui new file mode 100644 index 0000000..574bb88 --- /dev/null +++ b/web/index.html.bak-2026-08-19-ui @@ -0,0 +1,392 @@ + + + + + + + Oil · MT5 + + + + + + + + + +
+
+ +
+
+ + +
+ +
+
+
+
+
+
+
+ +
+
+
+
+ +
+ + + + + + + + +
+

M15 · Setup-Bereitschaft

+ +
+
+
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+

Meldungen

+
+
+
+ + + + + + + + +
+
+ + + + +
+

Marktkontext

+ +
+ +
+ +
+
+ + + + + + + + +
+

Statistik (live)

+
lade …
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +