Auslöser: User-Frage warum der Bot die Rally am 31.07. (82,5 -> 84,4 in 2 h) nicht gehandelt hat. Befund: in dem Fenster 99x WARTEN und 1x SHORT - und dieser SHORT wurde am Tief autonom eroeffnet (AUTOSIG, -26,31 EUR, per adverse15 geschlossen). Den LONG machte der User um 11:56 von Hand. Der WARTEN-Anteil ist strukturell zu hoch: 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 ueberdehnt ist - genau das passiert M5 IM Trend. Real am 31.07.: M5 sprang zwischen 0,00 und 1,93, die TF wechselte 33x am Tag (Median-Abstand 5 min). Die 1,2x-Hysterese ist dagegen wirkungslos. Und set_timeframe() verwarf bei JEDEM Wechsel die laufende Breakout- Bestaetigung und verankerte sie beim aktuellen Kurs neu. Bei k=0,3 und ATR_M30~0,53 braucht sie ~0,16 $, der Kurs lief ~0,08 $ je 5 min -> die Bestaetigung konnte rechnerisch nie fertig werden. 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 zurueckgesetzt. Der Anker gehoert zum Signal, nicht zur Zeitebene; bei Richtungswechsel verankert _confirm_breakout ohnehin neu. FIX 2 (engine._tf_loop): Mindest-Verweildauer [trading] tf_min_dwell_s=900. Verifiziert mit synthetischen Szenarien: Fix 1 - Bestaetigung ueberlebt M5->M30 und feuert LONG, Richtungswechsel verankert korrekt neu; Fix 2 - an der ECHTEN Score-Folge vom 31.07. nachgespielt: 4 Wechsel -> 2. NICHT backtestbar (Backtests laufen auf fester TF, das Churning existiert dort nicht). Begruendung ist "stellt die Bedingung her, unter der die Messung gilt", nicht "gemessen besser". Erfolgskontrolle = WARTEN-Anteil muss sich Richtung ~43 % bewegen. Bewusst nicht behoben: der 0,0-Einbruch des Scores bei Ueberdehnung selbst - das waere eine Aenderung der TF-Bewertung und damit messpflichtig. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2070 lines
164 KiB
Markdown
2070 lines
164 KiB
Markdown
# 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).
|
||
- **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 (Stand aktuell):** **DeepSeek (`[deepseek]`) ist der aktive
|
||
Copilot-Provider** (`[agent] provider=deepseek`, 2026-07-20). `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). 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).
|
||
- **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 <datei>`; JS grob via Klammern-
|
||
Balance prüfen (kein node im Env).
|
||
- **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.)
|
||
- **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.
|
||
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).
|
||
- **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).
|
||
- **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`.
|
||
- **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-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 — profitabel, aber **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.
|
||
- **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 = 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".
|
||
- **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)<Schwelle, sonst `break`) in die neue
|
||
Tabelle **`pbreak_predictions`** (`history.log_pbreak_prediction`, Felder u. a.
|
||
`predicted`, `confirm_price`, `reject_price`, `atr`). Auswertung läuft separat,
|
||
self-throttled ~5 min im `_trend_loop` (`engine._evaluate_pbreak_predictions`):
|
||
prüft gegen `candles_m1`, ob der Kurs binnen 60 min zuerst `confirm_price`
|
||
(→ outcome `break`) oder `reject_price` (→ `bounce`) erreicht — **exakt dieselbe
|
||
Definition wie beim Modell-TRAINING** (`_BRK_ATR=0,5`/`_BRK_W=12` M5-Bars=60 min).
|
||
Kein Treffer binnen 60 min → Timeout, zählt wie im Training als `bounce`. `correct`
|
||
= `predicted==outcome`. Broker-Zeit-Offset (`trader._broker_offset_s()`) korrekt
|
||
berücksichtigt (`pbreak_predictions.ts` = lokale Epoch, `candles_m1.time` = rohe
|
||
Broker-Zeit — Ausnahme, s. „Auch beachten"). Abfrage: `history.pbreak_accuracy
|
||
(period)` bzw. `GET /api/pbreak_accuracy?period=today|week|all` (read-only, kein
|
||
Token) → `{n, accuracy, break_accuracy, bounce_accuracy, pending, …}`. Getestet
|
||
mit synthetischen Szenarien (break/bounce/Gegenteil-falsch/Timeout) — alle 4 exakt
|
||
wie erwartet. **Reine Messung, kein Signal-/Exit-Eingriff** — noch keine UI-Karte
|
||
(Datensammlung; B4-artige Auswertung später möglich, wenn genug Zeilen vorliegen).
|
||
**Gegen-/Stop-Seiten-Anzeige `stop_approach` (2026-07-24, User-Frage „Kurs nähert
|
||
sich Support, aber keine Durchbruchwahrscheinlichkeit angezeigt"):** Der reguläre
|
||
`sr_close_hint` beobachtet nur das **Ziel-Level** (LONG→Resistance, SHORT→Support)
|
||
und wirkt nur im Plus — nähert sich der Kurs dem **Gegen-/Stop-Level** (LONG→Support
|
||
darunter, SHORT→Resistance darüber, typ. Verlust-Trade), war die App bewusst still.
|
||
Neu: `engine._stop_approach_hint` liefert für GENAU dieses Level eine **reine
|
||
Anzeige** von P(Durchbruch) — Break-Richtung `-d` (gegen den Trade), Feature-/
|
||
60-s-Glättung (`_stop_pb_state`) identisch zu `_sr_close_hint`. **KEIN Auto-Close.**
|
||
Snapshot `stop_approach {level, side, p_break, at_level, gap_atr, confirm, reject}`,
|
||
Frontend `#pos-stophint` (grün „Level hält wahrscheinlich" bei P<50, rot „bricht
|
||
wahrscheinlich durch" bei ≥50; nur wenn KEIN Ziel-`sr_close_hint` läuft; v=109).
|
||
**Vorher gemessen (`backtest_pbreak_calibration.py`, 2026-07-24, Gate „erst messen,
|
||
dann bauen"):** Das kalibrierte P(break)-Modell (auf Ziel-Seite trainiert) hält
|
||
auch auf der **Gegen-/Stop-Seite** — AUC **0,68/0,72** out-of-sample in BEIDEN
|
||
Hälften (≈ Ziel-Seite 0,67/0,71), im niedrigen P-Bereich sauber kalibriert (der
|
||
praktisch wichtige „hält der Support?"-Fall). ⚠ Einzige Schwäche: im HOHEN Bereich
|
||
(P≥0,5) GEGEN den Trend unterschätzt es die Bruch-Rate ~10–13 Pp (predicted 58 % →
|
||
echt 71 %) — also **konservativ** (warnt eher zu wenig vor einem Bruch), kein
|
||
Kalibrierungs-Bruch. Deshalb reine Anzeige, kein Trade-Trigger.
|
||
**Anti-Flattern (2026-07-14, gemessen `analyze_pbreak_flicker.py`:** mom3 der
|
||
laufenden M5-Kerze ließ P 7–15 Pp/Bar springen, Max 77 Pp; 8 % der Annäherungen
|
||
hatten ≥2 Schwellen-Flips): (1) **P zeit-geglättet** (~60-s-EMA in `_pb_state`,
|
||
Reset bei Level-Wechsel/flat; angezeigtes `p_break` = geglättet) · (2)
|
||
**Hysterese ±5 Pp** um die Schwelle — close↔laufen wechselt erst bei klarem
|
||
Durchbruch (55/65 bei Default 0,60, liegt im Optimum-Plateau 0,50–0,60).
|
||
**Dashboard-Schalter** in der
|
||
Position-Kachel (`#srclose-btn`, grün=AN) → `POST /api/srclose` (Toggle,
|
||
`engine.set_sr_autoclose`); Zustand `snapshot.auto_sr_close`. **AUTO-Close (`_check_sr_close` im `_pos_loop`,
|
||
`auto_sr_close=true`):** schließt **nur im PLUS** (`pnl>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.
|
||
- **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.
|
||
- **„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_<dir>` (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`. **B5-Pflicht:** Live-ØR/PF gegen die Backtest-Erwartung halten
|
||
(H2-Regime +0,14 vs H1-Regime −0,05) — driftet es weg, **zurückbauen** (`=false`).
|
||
- **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.
|
||
- **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`):** der Bot
|
||
eröffnet **nachts 0–7 Uhr KEINE** Squeeze-Trades (`_SQUEEZE_NIGHT=(0..7)`,
|
||
`_squeeze_skip_night` → `_check_auto_squeeze` return; kein Entry UND kein Reverse).
|
||
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).
|
||
**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).
|
||
**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)<Schwelle) aus, ein sofortiger Gleichrichtungs-Squeeze („Level bricht") war
|
||
der widersprüchliche 1-s-Roundtrip (LONG-Close +15 → 1 s später neuer LONG, doppelter
|
||
Spread; real beobachtet 2026-07-17 14:17). `_check_sr_close` merkt Zeit+Richtung
|
||
(`_sr_close_ts`/`_sr_close_dir`), der Entry-Pfad prüft sie; `_squeeze_last_sig` wird
|
||
NICHT gesetzt → nach Ablauf feuert der Ausbruch, falls noch aktiv. **Der Reverse
|
||
(Gegenrichtung) bleibt unberührt** — nur der Gleichrichtungs-Wiedereinstieg wird
|
||
verzögert.
|
||
**Allgemeine Wiedereinstiegs-Sperre nach JEDEM Close (2026-07-28, Koordinations-Fix,
|
||
kein Backtest):** nach dem Schließen eines Trades durch **Notfall-Stop / Broker-SL /
|
||
Time-Stop / manuell** eröffnet der Auto-Squeeze **~3 min (`_SQUEEZE_REENTRY_COOLDOWN_S=
|
||
180`) nicht in GLEICHER Richtung** wieder. Grund: real am **2026-07-27 15:39** killte
|
||
ein zu enger manueller −3-€-Notfall-Stop (Sub-Spread bei Margin-Sizing) den Auto-
|
||
Squeeze-Entry #1 nach **16 s**; der Squeeze war noch `active` → sofortiger Re-Entry #2
|
||
(Open→Instant-Close-Kaskade, doppelter Spread). `_check_auto_close` erfasst den Flat-
|
||
Übergang (`_pos_close_ts`/`_pos_close_dir`, Richtung via `_open_pos_dir` solange offen),
|
||
der Squeeze-Entry-Pfad prüft ihn NACH dem S/R-Cooldown; `_squeeze_last_sig` wird NICHT
|
||
gesetzt → nach Ablauf feuert der Ausbruch, falls noch aktiv. Gegenrichtung/Reverse
|
||
unberührt. Deckt alle Close-Arten außer dem S/R-Close ab (der hat seinen eigenen
|
||
Marker). ⚠ Behebt das *Symptom*; der eigentliche Auslöser war der Sub-Spread-Stop
|
||
(`emergency_min_eur=3`-Guard fängt <3 € ab, −3 € genau auf der Grenze war zulässig).
|
||
- **TF-Wechsel nicht mehr persistiert:** `engine._apply_wave_tf` merkt den
|
||
Heuristik-TF nur im Speicher (kein `save_config` mehr) — sonst würde die
|
||
Secrets-`ini` bei jedem Switch neu geschrieben (Kommentar-Verlust/Churn). Beim
|
||
Neustart wählt die Heuristik den TF binnen 1 min neu.
|
||
- **Circuit Breaker (Tagesverlust-Stopp) = gebaut, aktuell AUS `[trading]
|
||
daily_loss_limit_pct=0` (User schaltete ihn am 2026-07-27 wenige Stunden nach dem
|
||
Einbau wieder ab). Kurz auf 8 aktiv (Überlebens-Basis, mit dem Autonomie-Ziel
|
||
reaktiviert nach jahrelanger Ablehnung), dann `=0`. Reaktivieren = ini auf z. B. 8.**
|
||
Mechanik bei `>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.
|
||
|
||
## Mobile-UI (web/) & Alarme
|
||
- Aufbau: **Tab-Navigation ganz oben** (sticky: Dashboard/Logs/Statistik/**Charts**/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** (`#view-charts`): **M1/M5/M15/M30/H1 untereinander**, je ein
|
||
Candlestick-Chart mit **EMA12 (blau) + EMA50 (amber)**-Overlay und Trend-Header
|
||
(▲/▼/→ nach EMA12-vs-EMA50 mit Totband). Framework **TradingView Lightweight
|
||
Charts v4.2.3 — lokal vendored** (`web/lightweight-charts.standalone.production.js`,
|
||
KEIN CDN wegen WireGuard/Offline). Daten via `GET /api/bars?tf=&n=` →
|
||
`engine.get_bars` (OHLC+EMA+Trend unter `mt5_lock`). `app.js`: `loadCharts`/
|
||
`renderChart`, Charts nur bei sichtbarem Tab geladen + Auto-Refresh 20 s
|
||
(schont den Lock). Broker-Zeit (UTC+3) → UTC via `broker_utc_offset`. **Gitternetz
|
||
dezent** (2026-07-27, User „dunkler/unauffälliger": `grid`-Farbe `#161b22`→`#10151c`,
|
||
näher am Hintergrund `#0d1117`). **Chartmuster-Overlay (nur M30)** — s. Chartmuster-
|
||
Kachel: die 2 stärksten M30-Muster als Trigger-/Ziel-Preislinien.
|
||
**Regressionskanal-Overlay (nur M30, 2026-07-20):** `get_bars` liefert bei M30
|
||
`channel {dir, mid[], upper[], lower[]}` (Arrays deckungsgleich mit den Bars,
|
||
`structure.channel_series` — Regression über 60 abgeschl. Bars, linear über die
|
||
volle Breite extrapoliert). Frontend zeichnet 3 LineSeries (oben/unten gestrichelt,
|
||
Mitte gepunktet, grau). Dieselbe Kanal-Definition wie die Marktstruktur-Kachel —
|
||
REINE Anzeige (Signalvariante gemessen verworfen, s. `backtest_structure.py`).
|
||
**S/R-Overlay & Dashboard-Kachel = EINE Quelle** (`_draw_levels`; die Position-Kachel
|
||
`sr-res`/`sr-sup` speist jetzt via `_dash_sr` daraus statt aus dem Misch-Set
|
||
`_sr_levels` — vorher wichen Chart und Kachel ab, User-Feedback 2026-07-15). `_sr_levels`
|
||
bleibt intern für Strategie/Konfidenz. **„Klebrige" Hysterese** in `_draw_levels`
|
||
(`_draw_last`, verschärft 2026-07-21): die gezeichnete Linie **bleibt stehen, bis der
|
||
Kurs sie WIRKLICH durchbricht** (bid > R+0,1×ATR bzw. bid < S−0,1×ATR) — ein neuer,
|
||
näherer M5-Pivot löst KEINEN Wechsel mehr aus (das war die Zitter-Quelle: auf M5
|
||
bilden sich laufend neue Pivots, die alte 0,25×ATR-Regel sprang darauf an). Ausnahme:
|
||
ist die alte Linie >1,5×ATR weit weg UND ein deutlich näheres Level existiert, wird
|
||
refresht (sonst bliebe sie irrelevant hängen). Gilt für PWA-Chart UND MQL5-CSV (eine
|
||
Quelle).
|
||
**Level-Auswahl nach Kursposition statt Entstehungstyp (Fix 2026-07-23, User-Frage
|
||
„Widerstand/Support-Retest von der falschen Seite"):** vorher kam Widerstand NUR aus
|
||
`ph` (Swing-Hochs) und Support NUR aus `pl` (Swing-Tiefs) — ein gebrochenes Level blieb
|
||
für IMMER im falschen Topf hängen. 2 von 4 Retest-Varianten hatten dadurch NIE eine
|
||
Durchbruchwahrscheinlichkeit: (1) Widerstand von unten ✓ funktionierte, (2) **Support-
|
||
Retest von unten** (Kurs unter einem gebrochenen Support, nähert sich von unten wieder)
|
||
✗ kein Hinweis, (3) Support von oben ✓ funktionierte, (4) **Widerstand-Retest von oben**
|
||
(Kurs über einem gebrochenen Widerstand, nähert sich von oben wieder) ✗ kein Hinweis.
|
||
Fix: `ph`/`pl` bleiben GETRENNT geclustert (unverändert granular — ein erster Versuch,
|
||
beide Rohpools VOR dem Clustern zusammenzulegen, erzeugte in dichten Zonen Ketten-Mega-
|
||
Cluster durch die verdoppelte Punktdichte, >1×ATR breit, verworfen), aber NACH dem
|
||
Clustern werden beide fertigen Linien-Listen zusammengeführt und die Wahl erfolgt rein
|
||
per `min/max` relativ zu `bid` — nicht mehr nach Topf-Zugehörigkeit. Verifiziert: Fall 1
|
||
bei Live-Daten (bid 90,951) und ein dichter Kontrollfall liefern exakt dieselben Level
|
||
wie vorher (keine Regression); Fälle 2+4 (synthetische Retest-Szenarien) finden jetzt
|
||
das nahe, vorher übersehene Level statt eines weit entfernten Ersatzlevels. Betrifft
|
||
`_draw_levels` direkt und damit ALLE Konsumenten (PWA-Chart, MQL5-CSV, Dashboard-
|
||
Kachel, `_sr_close_hint`/Auto-Close) — eine Quelle, ein Fix.
|
||
**⚠ LEVEL-TF M5→M30 umgestellt (2026-07-30, gemessen `backtest_level_tf.py`):**
|
||
Auslöser war die User-Beobachtung „die S/R-Linien liegen oft sehr nahe beieinander" —
|
||
quantifiziert stimmte das: **71 % der M5-Level lagen näher als 1×ATR** (Median-Abstand
|
||
nur 0,61×ATR). Gemessen über 80k M5-Bars in 2 Halbjahren (Trades bleiben M5, nur die
|
||
LEVEL-Quelle variiert; Live-Exit + Echtkosten). **M30 gewinnt in BEIDEN Hälften:**
|
||
Abstand **1,95×ATR** (nur 28 % eng) · AUC out-of-sample **0,721** (M15 0,725, M5 0,710)
|
||
· R-Ertrag der P(break)-Close-Regel **rund doppelt** (+12.424/+13.047 vs +6.698/+7.749
|
||
bei P<0,55). M15 lag dazwischen; M30 gewählt wegen des klar besten R-Ertrags und der
|
||
weitesten Level. **Das P(break)-Modell wurde dafür NACHTRAINIERT** (`_PB_MU/_SD/_W`
|
||
in `engine.py`): out-of-sample validiert (H1→H2), dann auf allen 80k Bars final
|
||
gefittet — 34.771 Touches, Basisrate 39,4 %, Kalibrierung trifft (pred/real 23/25 ·
|
||
38/36 · 58/59 · 86/87). Die **Merkmale bleiben M5** (mom6/mom3 auf M5-Bars), nur die
|
||
Level sind M30. Quelle live: `wave.pb_levels30` (M30-H/L aus dem 15-s-Turn-Fetch,
|
||
k=3, letzte 50 Bars = trainingsgleich zum 300-M5-Lookback), Fallback `pb_levels` (M5)
|
||
beim Kaltstart. ⚠ **NICHT mitverändert: das Entry-Raum-Gate** (`wave._room_gate`) nutzt
|
||
weiter die M5-`pb_levels` — `entry_room_atr=0.6` ist darauf kalibriert, eine Umstellung
|
||
dort wäre ungemessen. Alte M5-Modellwerte stehen als Kommentar über `_PB_MU` (Rückweg).
|
||
Real nach der Umstellung: R/S 84,617/83,722 = 2,9×ATR Abstand (vorher 0,28×ATR).
|
||
**S/R-Overlay:** `get_bars` liefert `levels {res, sup}` aus **`engine._draw_levels`**
|
||
= echte Kursstruktur (rohe M30-Pivots aus `wave.pb_levels30`, geclustert ~0,5×ATR,
|
||
**nur die NÄCHSTE je Seite = 2 Linien total, deckungsgleich mit Dashboard
|
||
sr-res/sr-sup** — mehr Linien tauchten beim Kurslauf laufend auf/ab, User-Feedback
|
||
2026-07-14; identisch zur Auto-Close-Logik). ⚠ Bugfix 2026-07-14: vorher
|
||
kamen die Linien aus `_sr_levels` (stale [zones]-Config + Tageslevel + Gaps) —
|
||
gezeichnet wurden Uralt-Level (81,24/82,64) statt aktueller Struktur. Farben
|
||
**blau** (User-Wunsch): Widerstand #1f6feb, Support #58a6ff. **Beschriftung**
|
||
(2026-07-14): PWA-Achsenlabel „Widerstand"/„Unterstützung" (+Nummer ab 2);
|
||
MQL5 zusätzlich sichtbares OBJ_TEXT-Label je Linie („Widerstand 78.65",
|
||
~8 Kerzen vor dem rechten Rand, in Linienfarbe).
|
||
⚠ **Kein MT5-Terminal-Zeichnen aus Python:** das `MetaTrader5`-Paket hat KEINE
|
||
`object_create`/`OBJ_HLINE`/`chart_*`-Funktionen (nur MQL5). S/R-Linien daher im
|
||
PWA-Chart. **Für das Desktop-Terminal: Datei-Bridge** — `engine._write_levels_file`
|
||
(im `_run_analysis`, ~5 s, `[trading] export_mql5_levels=true`) schreibt atomar
|
||
`<terminal>\MQL5\Files\sr_levels.csv` (`SYM;…` / `R;preis` / `S;preis` / **`CH;<U|M|L>;
|
||
t1;p1;t2;p2`** = Regressionskanal-Anker / **`LQ;<B|A>;preis;size;<U|D>;<1|0>`** = ECHTE
|
||
Liquiditäts-Wand + Pfeilrichtung + Dominanz (⚠ seit MQL5 v1.14 zeichnet der
|
||
Indikator davon NUR noch den PFEIL, rechtsbündig am aktuellen Rand — Linie und
|
||
Text-Label sind auf User-Wunsch raus, Details stehen im Tooltip) / **`LT;t1;p1;t2;p2;<U|D>`** =
|
||
Liquiditäts-Trendlinie, s. u.); Quelle **`_draw_levels`** (S/R) +
|
||
**`structure.channel_line`** (Kanal) + **`self.hl_walls`** (Liquidität),
|
||
Indikator-Farben blau DodgerBlue/DeepSkyBlue, Wände grün/orangerot.
|
||
⚠ **Chartmuster-Linien (`PT;…`) 2026-07-30 auf User-Wunsch ENTFERNT** („entferne die
|
||
chartmuster linien aus dem mt5" — Chart war zu voll); die Muster-Kachel + das
|
||
PWA-Chart-Overlay bleiben unverändert, nur der MT5-Export/Indikator-Handler ist raus.
|
||
**ECHTE Liquiditäts-Wände im MT5-Chart (User-Wunsch 2026-07-30 „zeichne mir die
|
||
liquiditätslinien in den mt5 ein"):** `core/hl_walls.py` holt vom **HL-Dashboard**
|
||
(`GET http://127.0.0.1:8001/api/walls`, `[trading] export_hl_walls=true`) das
|
||
**größte L2-Orderbuch-Level je Seite** — die einzigen Linien im Chart, die NICHT aus
|
||
Pivots geschätzt sind, sondern **tatsächlich ruhende Orders** zeigen (Pepperstone =
|
||
Market Maker, liefert kein Buch). ⚠ **Basis-Korrektur zwingend:** HL `xyz:CL` notiert
|
||
**~0,45 $ unter** dem CFD (anderer Kontrakt/Contango) — die Umrechnung passiert
|
||
serverseitig im HL-Projekt (Median über 60 Snapshots), hier kommt schon
|
||
`bid_mt5`/`ask_mt5` an; fehlt die Basis, wird NICHT gezeichnet. ⚠ **`127.0.0.1`, nicht
|
||
`localhost`** (IPv6-Auflösung → 2066 ms statt 19–46 ms, lief in den 2-s-Timeout) und
|
||
der HL-Endpoint liest **nur seine DB** (live-`fetch_l2_book` dauerte ~2 s = Timeout).
|
||
**Pfeil + Liquiditäts-Trendlinie (User-Wunsch 2026-07-30, Indikator v1.13):**
|
||
(a) Jede Wand bekommt einen **Pfeil in ihrer Wirkrichtung** (Wingdings 233/234) —
|
||
Lesart **„die Wand HÄLT"**: Nachfrage-Wand stützt von unten (▲), Angebots-Wand
|
||
deckelt (▼); die **größere** der beiden wird dicker gezeichnet + „<<" im Label
|
||
(`dominant` aus dem Endpoint). ⚠ **Die Pfeilrichtung ist INTERPRETATION, nicht
|
||
gemessen** — die Gegenthese („Liquidity Grab": der Kurs läuft ZUR Wand, um sie
|
||
abzuräumen) ist genauso verbreitet. Welche stimmt, beantwortet `analyze_bookflow.py`
|
||
(Frage 3) nach genug Daten; bis dahin ist der Pfeil eine Lese-Hilfe, kein Signal.
|
||
(b) **Pinke Liquiditäts-Trendlinie** (`OBJ_TREND`, nach rechts verlängert,
|
||
`InpShowLiqTrend`/`InpLiqTrendColor`): lineare Regression über den **volumen-
|
||
gewichteten Wand-Schwerpunkt** `(bid_px·bid_sz + ask_px·ask_sz)/(bid_sz+ask_sz)`
|
||
der letzten **60 min** (≥20 Punkte nötig). ⚠ Sie zeigt etwas ANDERES als eine
|
||
Kurs-Trendlinie: **wohin das Gewicht der RUHENDEN Orders wandert**, nicht wohin der
|
||
gehandelte Preis läuft — steigt sie, ziehen die Käufer ihre großen Gebote hoch.
|
||
Zeiten werden im Bot in **Broker-Zeit** umgerechnet (`trader._broker_offset_s()`),
|
||
weil MT5-Charts die brauchen; Preise sind bereits basis-korrigiert.
|
||
Fail-safe: HL-Server aus → keine Linien, kein Fehler (Log 1×, dann still), Cache 10 s,
|
||
Daten älter als 60 s werden verworfen. **REINE ANZEIGE** — ob HL-Buch-Signale auf den
|
||
CFD übertragbar sind, wird gerade gemessen (`analyze_bookflow.py` im HL-Projekt,
|
||
Datensammlung seit 2026-07-30, Urteil in ~2 Wochen). Der Indikator
|
||
**`mql5/SR_Levels.mq5`** (**v1.29** — `LQ`-Handler zeichnet seit v1.14 **nur noch
|
||
den PFEIL**, rechtsbündig am aktuellen Rand (in `RepositionLabels()` nachgeführt,
|
||
bleibt beim Scrollen rechts; Details im **Tooltip**) — Linie und Text-Label sind
|
||
auf User-Wunsch raus („Chart zu voll"). **v1.15 = CHART-THEME** (User „Farben im
|
||
MT5 wie im Bild"): `ApplyTheme()` setzt beim Start Hintergrund/Gitter/Kerzenfarben
|
||
auf die Dashboard-Palette — Bull **#3B82F6**, Bear **#EF4444**, BG **#0D1117**,
|
||
Gitter **#10151C** (dezent), Text #8B949E; Liquiditäts-Pfeile **Gold #F59E0B**
|
||
(= „Liquiditätszone" der Legende). ⚠ Das ändert das Chart **dauerhaft** (bleibt
|
||
auch nach Entfernen des Indikators) → `InpApplyTheme=false` schaltet es ab, alle
|
||
Farben sind einzeln als Inputs überschreibbar. Schalter: `InpShowWalls`/
|
||
`InpWallBidColor`/`InpWallAskColor`/`InpShowLiqTrend`/`InpLiqTrendColor`;
|
||
PT-Handler entfernt; in `MQL5\Indicators\`, **braucht
|
||
`OnCalculate` als No-op** sonst Compile-Error, F7-kompilieren, auf den Chart ziehen)
|
||
liest die S/R-Linien per Timer (OBJ_HLINE) UND den **M30-Regressionskanal als 3
|
||
diagonale Trendlinien** (OBJ_TREND, nach rechts verlängert, oben/unten gestrichelt/
|
||
Mitte gepunktet grau; `InpShowChannel`/`InpChanColor`, Default an) — Anker in
|
||
Broker-Zeit (= Server-Zeit im Terminal), zeichnet **nur bei Datei-Änderung neu**
|
||
(Inhalts-Cache `g_last` gegen Flackern). Der Kanal ist dieselbe M30-Definition wie
|
||
PWA-Overlay + Marktstruktur-Kachel; ⚠ nach dem Update den Indikator **neu F7-
|
||
kompilieren** (v1.10). Die dynamische Handels-Range (`DrawRange`, Hoch/Tief
|
||
der letzten `InpRangeBars` Kerzen) ist **per Default AUS** (`InpShowRange=false`,
|
||
User-Wunsch 2026-07-14) — bei Bedarf über die Indikator-Eingaben aktivierbar. (Die
|
||
statischen `[zones]`-Config werden NICHT mehr in die CSV geschrieben — waren stale
|
||
WTI-Level, füllten den Chart.)
|
||
**Architektur bewusst: Python = Gehirn (Analyse/Strategie/PWA/News/LLM/Reports),
|
||
MQL5 = nur dünne Zeichen-Schicht.** Kein Full-Rewrite in MQL5 (PWA/Backtests/
|
||
Integrationen wären dort nicht/kaum machbar). **Sym-Zeile:** Symbol links · rechts `hdr-clock`
|
||
(**Srv = Broker-Wallclock** aus `market.server_time` via `getUTC*`, **Lok** =
|
||
Browser) + **Session** (`#session`; zeigt 21–22 Uhr lokal „🕘 PRIME-TIME" —
|
||
gemessen die stärksten Stunden — und bei Headline-Burst „📰 NEWS-STURM"/„viele
|
||
News" aus `news.storm_state()` → Snapshot `news_storm` {level: normal/elevated/
|
||
storm, fresh, base_per_h}; Headline-Rate letzte Stunde vs Basisrate, Basis-Fenster
|
||
min. 6 h verankert. Beides reine Anzeige) + Verbindungspunkt. Darunter Balance ·
|
||
**Tag P/L** · Equity. **Kein** Bid/Ask/Spread (Kurs steht in der Position-Kachel,
|
||
färbt sich nahe S/R). **Tag P/L** (`day-pl`) = realisiert heute
|
||
(`stats_overview("today")`) + offene Position; grün/rot
|
||
(`day_pl{realized,open,total}` im Snapshot).
|
||
- **Gesamtempfehlung** (`#card-verdict`, ganz oben): grafische Aggregation aller
|
||
Module — Konfidenz-Ring (conic-gradient = `conf_pct`), Bias-Balken mit Nadel
|
||
(SHORT↔LONG) und Modul-Chips (Welle/M30/H1/KI/Elliott/**Squeeze**, je grün/rot/grau; **TU
|
||
entfernt**). ⚠ **Bias-Fix 2026-07-15:** stimmberechtigt nur Module MIT Aussage —
|
||
Squeeze (nur `active`), Elliott (nur mit Ziel), KI (nur mit Bias) haben sonst
|
||
**Gewicht 0** (vorher dämpfte das fast immer neutrale Squeeze-w=2,0 die Nadel
|
||
permanent ~×0,8); `agree/total` zählt nur noch die ANDEREN stimmberechtigten
|
||
Module (Welle zählte sich selbst mit).
|
||
⚠ **Bias-Fix ZWEITER TEIL 2026-07-30 (Review „Gesamtempfehlung komplett prüfen"):**
|
||
Die Regel „ohne Aussage = Gewicht 0" galt bis dahin für Squeeze/Elliott/KI, **NICHT
|
||
für die Welle selbst** — sie stand mit w=3,0 permanent im Nenner, auch bei WARTEN.
|
||
Das wog schwer: **~91 % der letzten 2000 Empfehlungen sind WARTEN** (Range-Regime),
|
||
die Nadel war also fast immer künstlich zur Mitte gezogen (realer Fall: bias
|
||
**+0,312 statt +0,625**, Faktor ~2). Jetzt `weight=0` bei WARTEN. **Zweiter Fix:**
|
||
`agree` nutzte als Referenz immer die Wellen-Richtung → bei WARTEN stand dort
|
||
zwangsläufig „0/N", obwohl sich die anderen Module einig waren (real „0/4" bei
|
||
2 einigen Modulen). Jetzt Referenz = Wellen-Richtung, ersatzweise die **Bias-
|
||
Richtung** (real: „2/3"). Beides REINE ANZEIGE — Order-Logik unberührt. Mit 6
|
||
synthetischen Szenarien verifiziert (alle einig · Rest dagegen · alles neutral ·
|
||
WARTEN+alle SHORT · Welle+Squeeze). ⚠ **Offen (dokumentiert, nicht geändert):**
|
||
(a) **Doppelzählung** — die Welle nutzt M30 als Gegen-Trend-Filter und H1 als
|
||
Konfluenz-Bonus; beide zählen zusätzlich als eigene Stimmen (je 1,5), dieselbe
|
||
Information also zweimal. (b) **Drei divergierende Level-Quellen** seit der
|
||
M30-Umstellung: `_draw_levels` (M30 → Chart/S/R-Close), `pb_levels` (M5 → Entry-
|
||
Raum-Gate), `_sr_levels` (Misch-Set → Wellen-Konfidenz); real wichen die Supports
|
||
um 0,65 $ ab. Bewusst so (jedes Gate ist auf seine Quelle kalibriert), aber eine
|
||
Stolperfalle. (c) `tu` wird noch als Parameter durchgereicht, seit 2026-07-06 ungenutzt.
|
||
Formel/Details: `docs/gesamtempfehlung.md`.
|
||
Headline = **validierte Welle** (steuert Order) + **Zeitebene** (`verdict.tf`,
|
||
z. B. „▲ LONG · M5" — TF, für die die Empfehlung gilt); Rest = nur Konsens-Anzeige.
|
||
Backend `engine._verdict` → `verdict` im Snapshot; Render `renderVerdict`
|
||
(`app.js`). Details: `docs/gesamtempfehlung.md`.
|
||
**⚠ Konfidenz ist NICHT kalibriert (gemessen `analyze_verdict_calibration.py`,
|
||
2026-07-24, „Gesamtempfehlung verbessern"-Paket):** conf_pct gegen Forward-Return
|
||
(2 h, ×ATR, 1604 gesampelte Signale aus `recommendations`, 2 Hälften) — H1 (Mai–Jun)
|
||
perfekt monoton (conf 85–100: WR 68 %/ØR +1,13; <55 klar negativ), **H2 (Jun–Jul)
|
||
komplett INVERTIERT** (conf 85–100: WR 40 %/ØR −0,90; mittlere Bins besser). Hohe
|
||
Konf = starker Trend + Konfluenz — im Chop-/Hochvola-Regime sind genau das die
|
||
Überdehnungen vor der Wende. **Konsequenzen:** conf-Anzeige NICHT als P(Erfolg)
|
||
aufwerten; **Konfidenz-Sizing-Idee VERWORFEN** (hätte in H2 die schlechtesten
|
||
Signale am größten gesized); Ring bleibt, was er ist (Score-Aggregat). Copilot-Bias
|
||
in derselben Messung nicht robust prädiktiv (n klein bzw. ØR ~0) — für die
|
||
datenbasierte Entscheidung (behalten/Gewicht/raus wie TU) loggt jetzt
|
||
**`verdict_votes`** (neue DB-Tabelle, `history.log_verdict_votes`, 1×/60 s aus
|
||
`_run_analysis`: headline/conf/bias/tf + Modul-Votes Welle/M30/H1/KI(+w)/
|
||
Elliott(+w)/Squeeze + news_score) — Auswertung nach ein paar Wochen analog
|
||
`analyze_verdict_calibration.py`.
|
||
**Live-Kosten-Chip (2026-07-24):** Snapshot `cost_ratio` = (ask−bid)/ATR_M5
|
||
(`pb_feats.atr`, Floor 0,06) → in der `vd-consensus`-Zeile „Kosten 0,14×ATR ✅
|
||
günstig / · normal / ⚠ teuer" (Schwellen 0,20/0,32 aus `backtest_realcosts.py`:
|
||
Ø 0,265, Nacht 0,32–0,50 = Falle, 15–17 Uhr ~0,13). Reine Info — die ehrliche
|
||
Alternative zum abgeschalteten Dead-Hours-Gate.
|
||
**Ausrichtungs-Split im Order-Dialog (2026-07-24, Verhaltens-Hebel/B0):** Der
|
||
„trotzdem eröffnen?"-Dialog zeigt jetzt die EIGENEN Zahlen aus der DB
|
||
(`history.alignment_stats(40)` → Snapshot `alignment`, Engine-Cache ~120 s):
|
||
„📊 Deine Trades OHNE Signal: 42 % Trefferquote · −423 € (n36) / Mit Signal: …"
|
||
(Gruppen <5 Trades werden ausgeblendet). Real bei Einbau: 36 der letzten 40
|
||
Trades liefen OHNE Signal-Deckung mit −423 € — exakt die dokumentierte
|
||
Kern-Leckage, jetzt im Moment der Entscheidung sichtbar.
|
||
**News-Konflikt-Chip (2026-07-24, Auslöser 23.07.: zwei Shorts gegen einen
|
||
+0,98-Sentiment-Burst):** Snapshot `news_sentiment` (Score −1..+1 aus
|
||
`news.sentiment`); Frontend `#news-conflict` (Meldungen-Karte, bernstein) warnt
|
||
bei |Score|≥0,5 gegen offene Position bzw. aktives Signal („📰 News-Flow stark
|
||
bullisch (+0,98) — steht gegen deine SHORT-Position"). Reiner Kontext —
|
||
Prädiktivität unbelegt (Kalibrier-Messung: H2 nur n=4/6); dafür wird
|
||
`recommendations.news_score` seit 2026-07-24 wieder BEFÜLLT (war auf None
|
||
degradiert) → in ein paar Monaten messbar.
|
||
- **Karten** (Dashboard, von oben): Gesamtempfehlung · **Meldungen** · Marktstruktur ·
|
||
KI-Copilot · Letzte Trades · Kurslücken · **Statistik (live) ganz unten**.
|
||
(Tagesziele-Kachel **entfernt** 2026-07-13. **Welle-Karte komplett entfernt
|
||
2026-07-15** — TF-Ampel wanderte in die Gesamtempfehlung, die Wellen-Meldungen
|
||
[Squeeze/Überdehnung/Setup/Lauf/Gründe] in die **„Meldungen"-Karte** (`#card-pos`,
|
||
ex-„Position"); das Wave-Signal ist eh die Verdict-Headline. **Trailing-Karte
|
||
ausgeblendet 2026-07-22** — `#card-trail.hidden` per CSS, User-Vorgabe; Backend/
|
||
Trailing-Logik + Snapshot-Felder `trail.*` unverändert aktiv, nur die Anzeige raus,
|
||
wieder einblendbar durch Entfernen der `hidden`-Klasse in `index.html`.)
|
||
- **Auto-Squeeze-Monitor (B4)** (`#sqm-box`, oben im **Statistik-Tab**, 2026-07-20):
|
||
zwei Blöcke — **Entry-Edge · Mechanik** (`engine.squeeze_monitor` rechnet die Squeeze-
|
||
Regel mit Live-Params auf ~30 Tagen M5 NEU, Backtest-Exit → **isoliert den Einstieg**,
|
||
ØR/PF gegen Erwartung **+0,14…+0,23 · PF>1**) und **Live-Trades · echt** (SQUEEZE_*-
|
||
Trades aus der DB, Gesamtsystem inkl. realer Exits: n/WR/Ø-Gew/-Verl/Verhältnis/ΣEUR/
|
||
Tag-Nacht; ØR-Näherung via SL-Distanz). Verdict-Badge (trägt/dünn/**driftet→B5**) aus
|
||
der Mechanik. **Kernaussage der Ansicht:** klaffen Mechanik (Edge lebt) und Live
|
||
(echtes Geld) auseinander → es liegt am **Exit/Verhalten, nicht am Signal** (real
|
||
2026-07-20: Mechanik ØR **+0,414**/PF 1,64, Live ØR −0,07/−2,41 € — der alte Notfall-
|
||
Stop + Nacht-Trades fraßen den Edge, jetzt behoben → sollte konvergieren). Endpoint
|
||
`/api/squeeze_monitor` (Cache 60 s), lädt beim Öffnen des Statistik-Tabs (`loadStats`).
|
||
- **Statistik-live-Kachel** (`#card-stats-live`): kompakt Woche + Gesamt —
|
||
Trades, WR, PF, Ø-Gewinn/-Verlust, **Verhältnis** (Øgew/Øverl, <1 = Verlust-
|
||
Schiefe), PnL. Quelle `engine._compact_stats` → `snapshot.stats_compact`. Zum
|
||
objektiven Verfolgen der Stichprobe (statt Bauchgefühl pro Einzeltrade).
|
||
**TradersUnion- und Elliott/FVG-
|
||
Einzelkacheln ausgeblendet.** Elliott/FVG fließt weiter in die Gesamtempfehlung
|
||
(`_verdict`); **TU ist ganz raus aus Empfehlung, Verdict UND KI-Copilot-Kontext**
|
||
(`agent._tool_market` ohne `tradersunion`/`tu_consensus` — war die letzte Hintertür,
|
||
da der Copilot eine Verdict-Stimme ist; nur noch Snapshot-Anzeige).
|
||
- **TF-Wende-Ampel** (`#wave-turns`, jetzt IN der Gesamtempfehlung): Chips
|
||
**M1/M5/M15/M30/H1** (H1 neu 2026-07-15 — im `turn_data`-Fetch, aber NICHT im
|
||
Bounce-Scan, der bleibt M1–M30), je grün(auf)/rot(ab) nach **EMA12/50-Stand**;
|
||
**frische Kreuzung (≤3 Bars)** blinkt + zeigt ⟳ + Richtungspfeil + Bars-seit-Wende.
|
||
Quelle: `wave.tf_turns`
|
||
(Backend `WaveRecommender._turn_state` mit **Totband** `_TREND_DEADBAND×ATR` →
|
||
kein Geblinke bei seitwärts laufenden EMAs). 4-TF-Fetch ist **gedrosselt (~15 s)**
|
||
via `_turn_due` (spart Lock-Zeit; letzter Stand in `_last_tf_turns` gehalten).
|
||
REINE Anzeige — ändert die Empfehlung NICHT.
|
||
- **Marktstruktur-Kachel** (`#card-structure`, 2026-07-20, User-Idee „Pro-Chart-
|
||
Setup-Erkennung"): zeigt die Price-Action-Struktur auf **M30** — Swing-Folge
|
||
**HH/HL/LH/LL** (Chips grün=bullisch/rot=bärisch), letzter **BOS** (Break of
|
||
Structure, Richtung + gebrochenes Level + Bars her) und den **Regressionskanal**
|
||
(Richtung + Position des Kurses im Kanal 0–100 % als Balken+Punkt). Quelle
|
||
`core/structure.py` (`MarketStructure`, Pivots k=3, Kanal = LS-Regression über 60
|
||
Bars ± max. Abweichung, ~30 s gedrosselt im Trend-Loop unter `mt5_lock`), Snapshot
|
||
`structure`. **REINE ANZEIGE — kein Trade-Trigger, kein Verdict-Gewicht, KEINE
|
||
Prognose/Pfeil.** **Signalvariante DIREKT gemessen & VERWORFEN (`backtest_structure.py`,
|
||
2026-07-20, 60k M30-Bars/5 J, 2 Halbjahre, dieselbe structure.py-Logik kausal):**
|
||
ALLE Varianten in BEIDEN Hälften negativ — (A) Kanal-Pullback „Kauf am unteren
|
||
Kanaldrittel/Demand-Zone" = der orange Pfeil: ØR −0,05…−0,11, PF<1, WR 40–43 %
|
||
(der Kanalrand HÄLT nicht zuverlässig — antizyklisch am Extrem = Münzwurf, P(break)-
|
||
Klasse); (A2) +Swing-Filter (HH/HL) macht es nicht besser (−0,06/−0,07); (B) BOS-
|
||
Continuation = breakeven-negativ (−0,011/−0,005, Momentum-Klasse). Damit ist auch
|
||
die Struktur-Idee empirisch durch — die Kachel bleibt Ist-Zustand-Anzeige (wie
|
||
TF-Ampel/Squeeze), steuert nichts.
|
||
- **⚠⚠ P(break) LIVE-AUSWERTUNG (`analyze_pbreak_live.py`, 2026-07-31, 2275 ausgewertete
|
||
Vorhersagen seit 23.07.): DAS MODELL HÄLT LIVE NICHT, WAS DER BACKTEST VERSPRACH —
|
||
B5-Fall.** Fällige Messung aus dem Reminder, jetzt erledigt.
|
||
| | Gesamt | M5-Level (bis 29.07.) | M30-Level (ab 30.07.) | entkoppelt |
|
||
|---|---|---|---|---|
|
||
| n | 2275 | 1850 | 425 | 307 |
|
||
| **AUC** | **0,539** | 0,570 | **0,399** | **0,443** |
|
||
| Treffer Modell | 58,5 % | 58,7 % | 57,6 % | 56,7 % |
|
||
| „immer Abprall" | 59,0 % | 57,7 % | 64,7 % | 59,9 % |
|
||
| Überschuss | −0,5 Pp | +1,0 Pp | −7,1 Pp | −3,3 Pp |
|
||
Backtest-Erwartung war **AUC 0,71**. **Kalibrierung systematisch nach unten verzerrt:**
|
||
Bucket 0–19 % (n=1234, über die Hälfte!) sagt **8 % voraus, real 39,1 %**; Bucket
|
||
65–100 % sagt 74,5 %, real 48,0 % (invertiert). Im Mittel prognostiziert das Modell
|
||
**23 % Durchbruch, real sind es 41 %**, und es sagt nur in **8 % der Fälle „break"**.
|
||
⚠ **DER KERNBEFUND — das Gate gatet nicht:** die **Bruchrate der GESCHLOSSENEN Gruppe
|
||
ist bei JEDER Schwelle 38–40 %**, also identisch zur Basisrate (thr 20 → 39,1 % ·
|
||
thr 35 → 37,8 % · thr 55 → 40,5 %). Es findet live keinerlei Auswahl statt. Bei der
|
||
ini-Schwelle 0,55 werden **91,2 % aller Level-Berührungen geschlossen** → der
|
||
„P(break)-gegatete Close" ist faktisch zum **pauschalen S/R-Close** degeneriert, und
|
||
DER ist 2× gemessen und verworfen (`backtest_srclose.py`: WR verdoppelt, PF/ΣR in
|
||
beiden Hälften schlechter = Gewinner-Kappen). **Das erklärt die kontrafaktische
|
||
Live-Messung vom Vortag** (`analyze_srclose_live.py`: −8 €/Trade, 43 % klar zu früh
|
||
geschlossen, nur 26 % klar richtig) — Symptom und Ursache passen zusammen.
|
||
⚠⚠ **URSACHE GEFUNDEN (2026-07-31, `backtest_pbreak_touches.py` + Rekonstruktion aus
|
||
`candles_m1`): das Modell ist NICHT verfallen — es bekommt live eine ANDERE
|
||
Merkmalsverteilung, als es trainiert wurde.** Beweiskette in drei Schritten:
|
||
(1) **Der Backtest reproduziert die 0,71** (M30-Level, 4 Merkmale, Fit H1 → **AUC
|
||
out-of-sample H2 0,715**) → Modell und Pipeline sind in Ordnung.
|
||
(2) **Die Populations-Hypothese ist widerlegt:** live nach Berührungszahl
|
||
aufgeschlüsselt ist ausgerechnet die 1. Berührung (= die trainierte Situation) mit
|
||
**AUC 0,445 die SCHLECHTESTE** Gruppe, die Chop-Fälle (5.+) mit 0,577 die beste.
|
||
Der Versatz „Modell sagt 23–25 %, real 40–42 %" ist in JEDER Teilmenge gleich groß
|
||
(~17 Pp) — Signatur eines Merkmals-Problems, nicht eines Edge-Verfalls.
|
||
(3) **Merkmale aus `candles_m1` rekonstruiert (n=2275) — der Versatz ist messbar:**
|
||
| Merkmal | Live-Mittel | Training-µ | Differenz |
|
||
|---|---|---|---|
|
||
| mom3 | 0,149 | 1,4523 | **−1,303** |
|
||
| mom6 | 0,181 | 1,6565 | **−1,476** |
|
||
Daraus rechnet sich der z-Versatz exakt: mom3 → 1,433×(−1,303)/2,418 = **−0,772**,
|
||
mom6 → **−0,275**, zusammen **−1,05**. Aus der Trainings-Basisrate 39,4 % (z=−0,429)
|
||
wird damit ~19 % — beobachtet werden 23 %. **Erklärt praktisch die gesamte
|
||
Fehlkalibrierung.**
|
||
⚠ **DER MECHANISMUS:** Im Training ist der Touch-Bar `jt` der Moment, in dem der Kurs
|
||
nach einem ECHTEN ANLAUF (Level bei Entry ≥0,3×ATR entfernt fixiert) zum ersten Mal
|
||
ans Level stößt → mom3 ≈ 1,45×ATR = ein Schub. **Live wählt `_draw_levels` das
|
||
nächstgelegene Level JEDE SEKUNDE NEU** (mit Hysterese) — der Kurs steht also oft
|
||
neben einem Level, das gerade erst zum nächstgelegenen WURDE, ohne je darauf
|
||
zugelaufen zu sein → mom3 ≈ 0,15. Gleicher Code, gleiche Formeln, aber **Training =
|
||
Level bei Entry fixiert, Live = Level wandert mit.** Konsequenz: das Modell antwortet
|
||
live konsistent „kein Schub → kein Durchbruch → 23 %", liegt damit IMMER unter der
|
||
Schwelle 0,55 → **das Gate war seit Inbetriebnahme faktisch nie aktiv.**
|
||
**Zwei Reparaturwege (beide ungemessen, erst prüfen):** (a) Ziel-Level beim Öffnen
|
||
der Position FIXIEREN statt laufend neu zu wählen (macht Live = Training), oder
|
||
(b) das Modell auf einer live-spiegelnden Stichprobe NEU trainieren (bei jedem Bar
|
||
im 0,15×ATR-Band um das dynamisch nächste Level sampeln, ohne Anlauf-Bedingung).
|
||
**TOUCH-ZAHL im Backtest (2 Halbjahre, `backtest_pbreak_touches.py`):** hebt die AUC
|
||
nur von **0,715 auf 0,725**, Gewicht +0,176 für die 2.–4. Berührung (User-Vorzeichen
|
||
bestätigt), aber der **R-Ertrag ist nicht robust** (H1 +131, H2 −43). ⚠ Im Trainings-
|
||
Datensatz sind **76 % Erst-Berührungen und es gibt KEINE 5+-Fälle** (live sind 63 %
|
||
fünfte oder später) — das live beobachtete umgekehrte U lässt sich dort nicht einmal
|
||
abbilden. Kleine Verbesserung, nicht der Hebel.
|
||
✅ **Pipeline gegen das Training geprüft, KEIN Bug gefunden:** `dist` ist in beiden
|
||
entry-basiert (`abs(level−entry)/atr`, `backtest_srclose_prob.py:86`), `mom6`/`mom3`
|
||
in beiden am Touch-Bar `jt`, `confirm`/`reject` dieselbe ±0,5×ATR-Definition, Timeout
|
||
zählt in beiden als Abprall. Plausibelste Ursache = **andere Stichproben-Population**:
|
||
live werden 2275 Berührungen geloggt, entkoppelt (max. 1 je Level und Stunde) bleiben
|
||
nur **307** — dieselbe Position pendelt vielfach um dasselbe Level, live dominieren
|
||
also Chop-am-Level-Fälle. ⚠ Das entlastet das Modell NICHT: entkoppelt ist die AUC mit
|
||
0,443 sogar schlechter. ⚠ Der M30-Teil (AUC 0,399) ist mit n=425 über 2 Tage zu klein,
|
||
um die Umstellung als Ursache zu belegen — die Wiedervorlage (≥30 S/R-Closes auf
|
||
M30-Leveln) läuft weiter.
|
||
**TOUCH-ZAHL als fehlendes Merkmal (User-Idee 2026-07-31 „je öfter der Kurs die Linie
|
||
berührt, desto wahrscheinlicher der Durchbruch") — auf den Live-Daten BESTÄTIGT, aber
|
||
mit Umkehr:** Bruchrate nach n-ter Berührung desselben Levels im 30-min-Fenster, in
|
||
BEIDEN Live-Hälften dasselbe Muster — 1. Berührung 38,3/41,7 % · 2. 40,8/45,6 % ·
|
||
3. 43,2/45,1 % · **4. 45,1/47,4 %** (monoton, +6/+4 Pp über Basis) · **5.+ fällt
|
||
zurück auf 37,8/41,8 %** (UNTER Basis, und mit n≈1400 die größte Gruppe). Also ein
|
||
**umgekehrtes U**, kein monotoner Anstieg: 2–4 Berührungen = Level wird mürbe, ab 5 in
|
||
30 min = Range, das Level HÄLT. ⚠ Das Modell sieht davon **nichts** (sagt über alle
|
||
Berührungszahlen konstant ~23 % voraus) → echte orthogonale Information. ⚠ Belastbarkeit
|
||
begrenzt: die beiden „Hälften" liegen nur 4 Tage auseinander (kein Regimewechsel),
|
||
Buckets n=82–139; und `backtest_srbreak.py` fand die Touch-Zahl früher „nicht robust"
|
||
(allerdings ohne Fenster-Definition und ohne die 5+-Umkehr). **Nächster Schritt: als
|
||
5. Merkmal in `backtest_srclose_prob.py` aufnehmen und über die 34.771 Touches / 2
|
||
echten Halbjahre neu fitten** — dort ist messbar, ob die AUC steigt.
|
||
**✅ BEHOBEN am selben Tag (Weg b, User-Entscheidung): MODELL NEU TRAINIERT.**
|
||
`backtest_pbreak_retrain.py` erzeugt eine **live-spiegelnde Stichprobe** — Level
|
||
dynamisch mit Hysterese wie `_draw_levels`, gesampelt an JEDEM Bar im 0,15×ATR-Band
|
||
(KEINE Anlauf-Bedingung), Ausgang/Fenster wie live. Kontrolle: die Merkmalsmittel
|
||
dieser Stichprobe (mom6 0,203 · mom3 0,202) treffen die live rekonstruierten Werte
|
||
(0,181 · 0,149) — sie bildet den Live-Pfad also wirklich ab.
|
||
| | ALT-Modell | NEU-Modell |
|
||
|---|---|---|
|
||
| AUC auf live-spiegelnder Stichprobe (H2, oos) | **0,368** | **0,654** |
|
||
| Ø-Vorhersage vs. real | 19,2 % vs 39,4 % | kalibriert |
|
||
**R-Ertrag (`backtest_pbreak_rvalue.py`, Echtkosten, Baseline = nur Trailing):** NEU
|
||
schlägt ALT in **BEIDEN Hälften bei JEDER Schwelle**; gegen die Baseline gewinnt es
|
||
bei 0,35/0,45/0,55 in beiden (bei 0,60 kippt H1). **Bestwert 0,35** — H1 −149 vs
|
||
Baseline −506 (**+357**), H2 +1308 vs −110 (**+1418**). ⚠ Nebenbefund: das ALTE
|
||
Modell war in H1 **schlechter als gar kein Auto-Close** (−653 vs −506), in H2 besser
|
||
(+576 vs −110) — genau die Regime-Abhängigkeit, die live als −8 €/Trade sichtbar war.
|
||
**Eingebaut:** neue `_PB_MU/_SD/_W` in `engine.py` (final auf allen 80k Bars gefittet,
|
||
n=76.542, Basisrate 37,8 %, nachdem H1→H2 validiert war; alte Werte als Kommentar für
|
||
den Rückweg) + **`sr_close_pbreak=0.35`** (Config-Wächter-Anker mitgezogen).
|
||
Verifiziert: Chart-Linien zeigen jetzt 43 %/36 % statt 8–15 %.
|
||
⚠⚠ **DAS NEUE MODELL INVERTIERT EINE ALTE PROJEKT-REGEL — bewusst so:** das Gewicht
|
||
von mom3 dreht das Vorzeichen (**+1,4330 → −0,5420**). Der alte Befund
|
||
(`backtest_srbreak.py`) lautete „Kurs KRIECHT ans Level → P(break) nur 26 %, mit
|
||
Schwung → laufen lassen (41–43 %)". Das neue Modell sagt das Gegenteil: Anlauf-Schub
|
||
→ 26,5 % (das Level ABSORBIERT den Schub und hält), Schwung weg vom Level → 51 %.
|
||
**Beides gilt in seiner eigenen Population** — die alte Messung betraf den Anlauf zu
|
||
einem FIXIERTEN Level, die neue das Verweilen an einem DYNAMISCH gewählten. Für den
|
||
Live-Pfad gilt die neue Lesart; die alte Faustregel weiter oben im Text bezieht sich
|
||
auf die Anlauf-Situation und ist dort nicht widerlegt.
|
||
⚠ **Weiterhin offen:** auch das neue Modell schließt bei 0,35 noch ~94–97 % der
|
||
Positionen (es wählt vor allem den BESSEREN Moment, ist also kein wirklich selektives
|
||
Gate). Und die **Touch-Zahl bringt im neuen Modell nichts mehr** (AUC 0,652 vs 0,654)
|
||
— die dynamische Level-Wahl erfasst denselben Effekt bereits; die User-Idee hat auf
|
||
den Fehler GEZEIGT, die Ursache lag eine Ebene tiefer. **B4/B5-Pflicht:** Live-AUC in
|
||
~2 Wochen gegen 0,65 halten (Reminder `pbreak_accuracy_v2`, 800 Vorhersagen ab 01.08.).
|
||
- **P(Trendumkehr) = GEMESSEN, NICHT BERECHENBAR (`analyze_reversal.py`, 2026-07-31,
|
||
User-Frage „können wir die Trendumkehr-Wahrscheinlichkeit berechnen?"):** Aufgebaut
|
||
exakt wie das erfolgreiche P(break)-Modell — mechanisch scharfes **Barriere-Rennen**
|
||
(erreicht der Kurs zuerst −X×ATR GEGEN oder +X×ATR MIT dem Trend?), logistische
|
||
Regression auf 8 kausalen Merkmalen (Überdehnung · Regressionswinkel · mom3/mom6 ·
|
||
Trendstärke · Trendalter · Abstand zum M30-Gegenlevel · Vola-Regime), Fit auf H1,
|
||
**AUC + Kalibrierung gemessen auf H2**. 80k M5-Bars, ~18,3k/18,5k Stichproben.
|
||
**Ergebnis: Münzwurf in JEDER von 5 Ereignis-Definitionen.**
|
||
| Barriere / Fenster | Basisrate | AUC oos | AUC **in-sample** |
|
||
|---|---|---|---|
|
||
| 0,5×ATR / 60 min | 54,0 % | 0,509 | 0,521 |
|
||
| 1,0×ATR / 120 min | 50,8 % | 0,506 | 0,523 |
|
||
| 1,0×ATR / 360 min | 51,0 % | 0,507 | 0,523 |
|
||
| 1,5×ATR / 180 min | 49,7 % | 0,499 | 0,530 |
|
||
| 2,0×ATR / 240 min | 47,8 % | 0,501 | 0,524 |
|
||
⚠ **Die diagnostisch wichtigste Zahl ist die IN-SAMPLE-AUC (0,52–0,53):** das Modell
|
||
erklärt nicht einmal seine eigenen Trainingsdaten. Das ist NICHT Overfitting (da wäre
|
||
in-sample 0,70+ und oos bräche ein) — es ist schlicht **keine Information vorhanden**.
|
||
Einzelmerkmale allein: alle zwischen AUC 0,489 und 0,515. Basisrate ~50 % → der Trend
|
||
hat auf diesen Skalen **keine Persistenz**. Kleines kohärentes Muster: kurz (0,5×ATR/
|
||
60 min) leichte Mean-Reversion (54 %), lang (2,0×ATR/240 min) leichte Trendpersistenz
|
||
(47,8 %) — aber 2–4 Pp und **unbedingt** (das WANN ist nicht vorhersagbar).
|
||
⚠ **Warum P(break) funktioniert und das hier nicht — die Kern-Lehre:** P(break) fragt
|
||
nicht „wohin geht der Kurs", sondern **„hält diese konkrete Struktur?"** — bedingt auf
|
||
ein reales Objekt (Level mit Orders dahinter). „Trendumkehr" ist die UNBEDINGTE
|
||
Richtungsfrage, überall im Chart gestellt, ohne Struktur zum Festmachen. Genau die
|
||
Klasse, an der das Projekt 18× gescheitert ist. **Die kalibrierte umkehr-nahe Größe
|
||
existiert bereits: `stop_approach`** (P(break) auf dem Gegen-/Stop-Level, AUC 0,68/
|
||
0,72) — strukturgebunden statt frei schwebend. Kein Modul gebaut.
|
||
- **Wahrscheinlichkeits-Kegel „Erwartete Spanne" (`core/cone.py` + Kachel `#card-cone`
|
||
+ MT5 `CN;…`, 2026-07-31): STREUUNG, NICHT RICHTUNG — und out-of-sample kalibriert.**
|
||
Der User wollte ein „Kursvorhersagemodul, das mögliche Verläufe in MT5 einzeichnet".
|
||
Eingezeichnete PFADE wurden bewusst NICHT gebaut (suggerieren eine Präzision, die es
|
||
nicht gibt — die Lehre aus `analyze_verdict_calibration.py`, wo conf_pct in H2
|
||
invertiert war). Stattdessen: wie WEIT der Kurs in 30/60/120 min plausibel gelaufen
|
||
sein kann. **Gemessen (`analyze_cone.py`, 80k M5, r(h)=(C[i+h]−C[i])/ATR[i], Fit auf
|
||
H1 → Abdeckung gemessen auf H2):**
|
||
| Band | 30 min | 60 min | 120 min |
|
||
|---|---|---|---|
|
||
| 80 % nominal | 76,7 % | 76,6 % | 75,3 % |
|
||
| 90 % nominal | 87,5 % | 87,2 % | 86,5 % |
|
||
⚠ Die Bänder sind konsistent **~3–5 Pp ZU ENG** (H2 war relativ zum ATR volatiler).
|
||
Das wird **NICHT weggefittet** — Kachel und Tooltip nennen die REAL gemessene
|
||
Abdeckung („trifft 77 %"), nicht den Nennwert. Damit ist der Kegel nach dem
|
||
P(break)-Modell die **zweite kalibriert geprüfte Komponente** im Projekt. Quantile
|
||
final auf allen 80k Bars gefittet (`_CONE` in `core/cone.py`; gleiches Vorgehen wie
|
||
beim P(break)-Modell: erst out-of-sample validieren, dann voll fitten), ATR-normiert
|
||
→ regime-übergreifend gültig. **Zentriert auf den AKTUELLEN Kurs, KEINE Drift
|
||
addiert** (Median lag bei ±0,0…0,16×ATR = klein gegen die Bandbreite; eine
|
||
Drift-Korrektur wäre eine Richtungsaussage und genau die ist nicht belegt).
|
||
Snapshot `cone {atr, price, levels[{minutes,bars,band,lo,hi,coverage}]}`; MT5-Export
|
||
`CN;<minuten>;<lo>;<hi>` (nur 80 %-Band) → Indikator **v1.31** zeichnet zwei sich
|
||
öffnende, gepunktete Pfade, verkettet über 30→60→120 (`g_coneT/g_coneLo/g_coneHi`,
|
||
je Redraw zurückgesetzt; Startpunkt aus der `PX`-Zeile, die deshalb `g_conePx` auch
|
||
dann setzt, wenn `InpShowPrice` aus ist). Farbe **neutral grau** (`InpConeColor`) —
|
||
bewusst keine Richtungsfarbe. `InpShowCone=false` schaltet ab. **Kein Signal, kein
|
||
Verdict-Gewicht.**
|
||
- **Kerzen-Anatomie (`core/candles.py` + Kachel `#card-candles`, 2026-07-31, User-Vorgabe
|
||
„Kerzengröße und Dochtlänge … im Kontext"): REINE ANZEIGE — und die Kachel widerspricht
|
||
bewusst der Lehrbuch-Deutung.** Erkennt mechanisch aus M5-OHLCV: Marubozu · Langer
|
||
Körper · Langer Docht oben/unten · Spinning Top · Doji · Engulfing (Schwellen identisch
|
||
zum Backtest, damit Anzeige und Messung dieselbe Sprache sprechen); Refresh ~20 s im
|
||
Trend-Loop unter `mt5_lock`, Snapshot `candle_anat {tf, items[], vol_hot_x}`.
|
||
**GEMESSEN (`backtest_candles.py`, 80k M5, 2 Halbjahre, Forward-Return 60 min): die
|
||
gängige Deutung ist in BEIDEN Hälften INVERTIERT.** ⚠ Methodik-Kern: gerichtete Klassen
|
||
werden gegen die **DRIFT-NULL** (bm×E[d]) gelesen, nicht gegen die Baseline — sonst
|
||
liest man reine Regime-Drift als Edge (H1 war Abwärts-, H2 Aufwärts-Regime).
|
||
Überschuss-Befunde: **„Langer Körper = Fortsetzung" → −0,059/−0,076** (Kurs läuft
|
||
GEGEN die Kerze); **„im Trend = Fortsetzung" → −0,132/−0,042** (es ist die KLIMAX-,
|
||
nicht die Fortsetzungskerze); **„2× Volumen = echt" → −0,117/−0,204** vs −0,065/−0,035
|
||
bei normalem Volumen (**monoton in die falsche Richtung** — mehr Volumen = stärkere
|
||
Gegenbewegung); **„Docht am Level zuverlässig" → in BEIDEN Hälften SCHLECHTER als in
|
||
der Range-Mitte**; „mit/gegen Trend" beim Docht kippt (−0,102/+0,089) = Rauschen.
|
||
**Folgetest als Handelssignal = ERNEUT VERWORFEN (`backtest_candle_fade.py`, Live-Exit
|
||
SL 2,0/Trail 1,5/BE 1,3/Time-Stop 120', Echtkosten):** der Fade der Klimax-Kerze
|
||
fällt durch — Kontrolle (ohne Volumen-Filter) −0,080/−0,046; Vol≥2,0 −0,059/**+0,103**;
|
||
Vol≥2,5 **+0,144**/−0,080; Vol≥3,0 +0,113/−0,064 → **benachbarte Schwellen exakt
|
||
gegenläufig** = Rauschmuster. Einzige beidseitig positive Zelle (Vol≥2,0+Marubozu
|
||
+0,036/+0,043) hat n=91/118 bei 7 getesteten Varianten = Mehrfachvergleich-Artefakt.
|
||
⚠ **Methodisch die wichtigste Lehre des Laufs: Informations-Überschuss ≠ handelbarer
|
||
Edge.** Dieselbe 2,0-Schwelle liefert im Forward-Fenster −0,117/−0,204 (also +0,12/+0,20
|
||
beim Fade), im echten Trade aber −0,059/+0,103 — die Gegenbewegung ist diffus, der
|
||
2×ATR-Stop wird unterwegs getroffen (WR nur 35–40 %). **Der Weg zum Ziel zählt, nicht
|
||
nur das Ziel.** **18. verworfener Signal-Eingriff.** ⚠ **Konsequenz für die UI:** die
|
||
Kachel ist bewusst **NEUTRAL** gefärbt (keine grün/rot-Ampel) und der Hinweistext nennt
|
||
die **gemessene** Lesart („⚠ Klimax-Kerze — der Kurs läuft danach eher GEGEN die
|
||
Kerze"), nicht die Lehrbuch-Lesart — eine Richtungsampel würde hier systematisch
|
||
falsch stupsen. Gleiche Korrektur wie bei der Bounce-Anzeige (heißt deshalb
|
||
„Überdehnung", bernstein statt grün/rot). Kein Verdict-Gewicht, kein Trade-Trigger.
|
||
- **Chart-Muster (Doppeltop/-boden) als Signal = VERWORFEN (gemessen `backtest_doubletop.py`,
|
||
2026-07-20, User-Idee „Mustererkennung SKS/Doppeltop"):** das am saubersten mechanisch
|
||
definierbare Umkehrmuster (2 ~gleiche Swing-Pivots + Nackenlinien-Bruch, aus derselben
|
||
Pivot-Erkennung; Einstieg beim Bruch, Live-Exit, 60k M30-Bars, 2 Halbjahre). ALLE
|
||
Toleranzen (0,3/0,6/1,0×ATR) fallen durch: WR nur 40–45 %, ØR −0,01…−0,15, PF<1;
|
||
die einzige leicht positive Zelle (H1 tol 0,3 +0,044) kippt in H2 auf −0,150 = Rauschen/
|
||
Regime, kein Edge. Deckt sich mit der schwachen Empirie (Lo/Mamaysky/Wang 2000: Effekt
|
||
klein & kaum handelbar; NY Fed: SKS nicht profitabel; out-of-sample schwach) UND der
|
||
Struktur-Klasse (Muster = Swings+Trendlinien = die gerade verworfene Klasse). **11.
|
||
verworfener Signal-Eingriff — am Signal ist nichts zu holen.**
|
||
- **Konsolidierungs-Setups (False Breakout · Distribution · Accumulation · Retest)
|
||
= VERWORFEN (gemessen `backtest_consolidation.py`, 2026-07-30, User-Vorgabe „lerne
|
||
für die Gesamtempfehlung die Konsolidierungen", 3 Chart-Beispiele):** Alle auf
|
||
DERSELBEN Box wie der validierte Squeeze (10 M5-Bars ≤2,5×ATR), 80k Bars, 2
|
||
Halbjahre, Live-Exit + Echtkosten.
|
||
· **A) False Breakout / Liquidity Sweep** (Ausbruch kehrt zurück → Gegenrichtung):
|
||
**fällt in JEDER Variante durch** (Rückkehr-Fenster 1/2/3 Bars × Live-/Setup-Exit ×
|
||
Session) — H1 durchgehend negativ (−0,127…−0,252), H2 leicht positiv = Regime-Kippen.
|
||
Der **Setup-eigene Exit** („Stop hinter dem Docht, Ziel = Gegenseite der Range") ist
|
||
in ALLEN 6 Kombinationen negativ — dieselbe Beobachtung wie bei den SMC-Sweeps und
|
||
den Chartmustern: der Lehrbuch-Exit trägt nicht, der Ertrag kommt (wenn überhaupt)
|
||
vom generischen Trailing. ⚠ **Wertvoller Nebenbefund:** **54 % aller Box-Ausbrüche
|
||
kehren binnen 3 Bars in die Box zurück** — der Squeeze-Ausbruch scheitert also
|
||
mehrheitlich (deckt sich mit seiner 43 %-Trefferquote). Die Gegenrichtung zu handeln
|
||
rettet das aber NICHT.
|
||
· **B/C) Distribution/Accumulation** (Ausbruchsrichtung aus dem Trend VOR der Box
|
||
vorhersagbar?): **die Basisrate widerlegt es direkt** — nur **48 %** der Boxen nach
|
||
Aufwärtstrend brechen nach unten, 52 % bei Accumulation (Zufall = 50 %). „Distribution"
|
||
sah mit ØR +0,168/+0,270 zunächst tragfähig aus; der **KONTROLLTEST** (derselbe
|
||
Vor-Trend, aber Bruch in die GEGENrichtung = Trendfortsetzung) liefert in H2 mit
|
||
**+0,364 sogar MEHR** als die Distribution-Hypothese (H1: −0,021). Beide Varianten
|
||
sind Box-Ausbrüche mit Trailing; welche führt, wechselt mit dem Regime. **Kein
|
||
Richtungs-Edge aus dem Vor-Trend.**
|
||
· **Retest-Einstieg** (statt beim Bruch am zurücklaufenden Level einsteigen):
|
||
in BEIDEN Hälften schlechter als der direkte Bruch (Distribution −0,071/+0,010,
|
||
Accumulation −0,184/−0,131) — der Retest kostet den besseren Einstiegspreis.
|
||
**Fazit: nichts davon in die Gesamtempfehlung.** Der Squeeze bleibt die einzige
|
||
validierte Box-Verwertung. **17. verworfener Signal-Eingriff.** ⚠ Methodisch wichtig:
|
||
Der Kontrolltest („trägt die Hypothese mehr als ein beliebiger Ausbruch?") ist genau
|
||
der, der bei `backtest_patterns.py` noch fehlt — hier hat er die Idee gekippt.
|
||
- **Volume Profile / „Liquiditätslinien" (POC/HVN/LVN, Value Area) = VERWORFEN
|
||
(gemessen `backtest_volume_profile.py`, 2026-07-30, User-Frage „System zur besseren
|
||
Vorhersage — Liquiditätsnachfragelinien / wahrsch. Verkaufslinie?"):** Getestet, WEIL
|
||
es die einzige Klasse mit einer **anderen Datenquelle** war (nicht „wo war ein Docht"
|
||
= Pivots/Swings, sondern **wo wurde tatsächlich gehandelt** = Volumen) — SMC/ICT-
|
||
„Order Blocks"/Liquidity-Linien selbst sind strukturell Pivot-Zonen und damit dieselbe
|
||
5× verworfene Klasse. Aufbau: feste Preis-Bins 0,05 $, rollierendes 24-h-Fenster
|
||
(288 M5-Bars), Bar-Volumen (`tick_volume` — CFD liefert `real_volume=0`) gleichmäßig
|
||
über [low, high], Value Area 70 %; 80k M5-Bars, 2 Halbjahre, Live-Exit + Echtkosten.
|
||
**Ergebnis: nichts trägt.** Am eindeutigsten fällt genau die *Kern-Idee* durch:
|
||
**Reversion am POC („der Kurs prallt an der Liquiditätslinie ab") ist in BEIDEN
|
||
Hälften negativ** (ØR −0,178/−0,121, PF 0,79/0,84, n≈400 je Hälfte) — der POC hält
|
||
NICHT. HVN-Reversion kippt (−0,067/+0,044), LVN-Momentum ist Breakeven-Rauschen
|
||
(−0,031/+0,003). **Informations-Test (Teil 1, Forward-Return 2 h ohne Exit/Kosten,
|
||
relativ zur `mid`-Baseline gelesen — H1 war Abwärts-, H2 Aufwärts-Regime, absolute
|
||
Werte sind daher Drift-verzerrt):** KEIN Bucket weicht konsistent ab — LVN H1 −0,11
|
||
/ H2 +0,09 relativ, HVN H1 +0,08 / H2 −0,06 → beide kippen. **Tiefere Einsicht: die
|
||
Volume-Profile-Position ist ein TREND-PROXY, keine unabhängige Information**
|
||
(„außerhalb Value Area" = Ausbruch läuft = folgt dem Regime: H2 +0,304 vs +0,092 in
|
||
VA, H1 umgekehrt −0,122 vs −0,057) — sie erbt damit exakt die Regime-Anfälligkeit von
|
||
Momentum/Struktur. **15. verworfener Signal-Eingriff.** ⚠ Was BLEIBT: das bereits
|
||
validierte **P(break)-Modell** ist die einzige belegt-prädiktive „Liquiditäts"-Aussage
|
||
im Projekt (AUC 0,71 out-of-sample, kalibriert; ~63 % der Level-Touches prallen ab) —
|
||
und auch das trägt nur als **EXIT**, nicht als Entry. Einzige noch ungetestete Klasse
|
||
wäre **echter Order-Flow/L2-Orderbuch** (Pepperstone = Market Maker, liefert keins;
|
||
Hyperliquid hätte `l2Book` — bräuchte erst wochenlange Datensammlung UND einen Beleg,
|
||
dass HL-Orderbuch-Signale auf den CFD übertragbar sind).
|
||
- **Chartmuster-Kachel `#card-patterns` (`core/patterns.py`, 2026-07-27, User-Idee
|
||
„Visuelle Muster-Erkennung"): REINE ANZEIGE, KEIN Signal.** Erkennt mechanisch aus
|
||
den M30-Swing-Pivots (k=3, wie `structure.py`): **Doppeltop/-boden, Kopf-Schulter/
|
||
inverse SKS, Tasse+Henkel/invers (heuristisch), auf-/absteigendes + symmetrisches
|
||
Dreieck.** Je Muster: Richtung (bull/bear/neutral), Nackenlinie/Trigger, Measured-
|
||
Move-Ziel, Status (bildet sich / bestätigt / ungültig→raus), Güte (Extrem-Symmetrie),
|
||
Bars-her. Refresh ~30 s im Trend-Loop unter `mt5_lock`; Snapshot `patterns
|
||
{patterns[], atr, cur}`. **Nur AKTIVE, kursnahe Muster** (2026-07-27, `_detect`-Filter):
|
||
abgearbeitete Muster (Ziel bereits erreicht) UND zu weit entfernte (`_MAX_DIST_ATR=5,0`,
|
||
Trigger >5×ATR vom Kurs) werden ausgeblendet — sonst zeigte die Kachel alte, nicht mehr
|
||
handelbare Muster. **Grafische Darstellung (2026-07-27, User „grafisch darstellen"):**
|
||
(a) Muster-Karte zeigt je Item eine **kompakte SVG-Skizze** der Form (`_patternSvg`,
|
||
viewBox 56×32, farbcodiert grün/rot/grau) — Form auf einen Blick; (b) im **Charts-Tab
|
||
(M30)** werden die **2 stärksten Muster als Preislinien** gezeichnet: Trigger = kräftige
|
||
Linie (grün bull/rot bear, Label „▶ bestätigt"/„◌ bildet sich"), Ziel = gepunktete Linie
|
||
gleicher Farbe. Dazu liefert `get_bars` bei M30 jetzt `patterns` (aus `patterns.snapshot`).
|
||
Frontend zeigt Karte + Disclaimer „kein Signal". ⚠ **Signal-
|
||
Einfluss ERST nach Backtest** (`backtest_patterns.py`, noch zu bauen) — die
|
||
Muster-KLASSE ist bereits 2× als Signal verworfen (`backtest_doubletop.py` oben +
|
||
`backtest_structure.py`), also **hohe Beweislast**; die Kachel macht das Muster nur
|
||
sichtbar (was ein Kommentator meint), steuert nichts. v2-Kandidaten (Flags/Wimpel,
|
||
Rechteck, Rundung) dokumentiert, noch nicht gebaut. Web-Recherche (strike.money,
|
||
IG, Axi): Umkehr (Doppel-, SKS, Rundung) vs. Fortsetzung (Dreieck, Flag, Tasse);
|
||
Kursziel stets = Muster-Höhe ab Ausbruch projiziert — überall mit dem Caveat
|
||
„keine Garantie, nur Indikation".
|
||
**Backtest gebaut & gemessen (`backtest_patterns.py`, 2026-07-27, 80k M30, 2
|
||
Halbjahre, Einstieg beim Trigger-Bruch, Live-Exit+Echtkosten):** Mehrere Typen sehen
|
||
ØR-positiv in BEIDEN Hälften aus (Doppeltop +0,43/+0,23 · Tasse +0,35/+0,26 · inv.
|
||
Tasse +0,36/+0,58 · aufst. Dreieck +0,16/+0,10). ⚠ **ABER das ist ein Exit-Artefakt,
|
||
KEIN Muster-Edge:** die **Ziel-Trefferquote (Measured-Move vor SL) ist nur 13–38 %**
|
||
— die eigene Muster-Prognose trifft also in ~⅔ der Fälle NICHT. Der positive ØR
|
||
kommt vom **Trailing-Exit auf einem Momentum-Bruch** (Trigger-Bruch = Breakout, und
|
||
Breakout+Trailing hat generisch Edge — s. Squeeze), nicht von der Muster-Vorhersage.
|
||
Deckt sich mit `backtest_doubletop.py` (dort mit Ziel-/Fix-Exit → negativ) — der
|
||
Unterschied ist NUR der Exit. **Fazit: NICHT reif für Signal-Einfluss** — es fehlt
|
||
die Kontrolle „generischer Breakout + gleicher Exit" (isoliert den Muster-Beitrag,
|
||
wie Squeeze vs. beliebige Box); ohne die ist „Muster wirkt" nicht belegt. Muster
|
||
bleiben **reine Anzeige**. Ziel%-Spalte = der ehrliche Muster-Check (13–38 % = Muster
|
||
sagen den Kursverlauf kaum voraus).
|
||
- **MQL5-Chart-Erweiterungen 2026-07-30 (Indikator v1.16–v1.18, alles REINE ANZEIGE):**
|
||
· **Trade-Marker** `TR;<O|C>;<broker_time>` — die Kerze der **Eröffnung wird
|
||
HELLGRÜN**, die des **Close DUNKELGRÜN** umrandet (OBJ_RECTANGLE über Bar-Spanne +
|
||
High/Low, `iBarShift` → sitzt auf jeder Chart-TF korrekt). Quelle: offene Position
|
||
(`trader.open_time`) + die letzten `_TRADE_MARKS`=6 geschlossenen Trades.
|
||
· **Liquiditätszonen** `EQ;<H|L>;lo;hi;<broker_time>` — **Equal Highs / Equal Lows**
|
||
aus den M30-Pivots (zwei Pivots innerhalb `_EQ_TOL_ATR`=0,15×ATR, 2–40 Bars
|
||
auseinander) als **goldenes Band** + gestrichelte Kante, nach rechts verlängert
|
||
(Darstellung nach User-Referenzbild). Berechnung in `wave_rec` (`_liq_zones`,
|
||
Snapshot `liq_zones`), Mindesthöhe `_EQ_MIN_H_ATR`=0,10×ATR. ⚠ **Als SIGNAL
|
||
gemessen und verworfen** (`backtest_liquidity_sweep.py`) — nur Kontext.
|
||
· **Aktueller Kurs** `PX;bid` — fett oben rechts (OBJ_LABEL, CORNER_RIGHT_UPPER,
|
||
Arial Black, `InpShowPrice`/`InpPriceSize`/`InpPriceColor`).
|
||
· ⚠ **Zwei Fallen beim Pfeil-Bug** („Pfeile werden nicht angezeigt", v1.17):
|
||
(1) **`ANCHOR_LEFT` ist für `OBJ_ARROW` UNGÜLTIG** — erlaubt sind nur `ANCHOR_TOP`/
|
||
`ANCHOR_BOTTOM`; (2) die Pfeile sitzen rechts NEBEN der letzten Kerze → ohne
|
||
**`CHART_SHIFT=true`** gibt es dort keinen sichtbaren Bereich. Beides im Theme/
|
||
Handler behoben.
|
||
· ⚠ **Blink-Fix in `core/hl_walls.py`** („Pfeile blinken nur manchmal auf"): bei
|
||
einem Fehlschlag (HTTP-Timeout, HL-Server kurz busy) wurde der Cache auf `None`
|
||
gesetzt → `LQ`-Zeilen fehlten in der CSV → der Indikator löschte die Pfeile beim
|
||
Redraw. Jetzt wird der **letzte gute Stand `_STALE_GRACE_S`=120 s weitergereicht**;
|
||
erst danach verschwinden die Pfeile (echter Ausfall).
|
||
- **Kurslücken-Kachel** (`#card-gaps`): offene D1-Gaps des laufenden Jahres
|
||
(`gaps.GapAnalyzer`, Refresh ~10 min im Trend-Loop unter `mt5_lock`). Je Gap:
|
||
Richtung, Zone, Fill-Level, Abstand zum Kurs. Die **Fill-Level fließen als
|
||
S/R-Magnete in `_sr_levels`** → damit in die Wellen-Konfidenz (`set_sr_context`,
|
||
`_SR_BONUS`/`_SR_PENALTY`), Close-Alarme und Verdict-Kontext — und so indirekt in
|
||
den **Autotrader** (der auf die Welle hört). **Keine** eigene Handelsrichtung.
|
||
Snapshot `gaps`. Mess-/Einzeltool: `find_gaps.py`.
|
||
- **Gap-Magnet als Handelssignal = VERWORFEN (gemessen, `backtest_gaps.py`):**
|
||
Signale WEG vom nächsten offenen Gap haben **+0,094** Ø-Edge, ZUM Gap nur
|
||
**+0,014** — der Magnet zieht NICHT (das Gap blieb offen, weil der Trend von
|
||
ihm weglief). „Richtung Gap handeln" wäre schädlich; „weg" ist nur
|
||
Trendbestätigung (macht die Welle schon). Gaps bleiben reiner Kontext.
|
||
- **Tagesziele — Dashboard-Kachel ENTFERNT (2026-07-13):** Das Frontend zeigt die
|
||
Web-Prognose nicht mehr. Das Modul `daily_levels` läuft **unverändert weiter**: die
|
||
Level fließen als **S/R-Kandidaten** in `_sr_levels` (Anzeige/Close/Verdict-Kontext)
|
||
und in den 07:30-Tagesreport. `snapshot.daily_levels` bleibt befüllt (nur ohne UI).
|
||
- **Täglicher Report** (07:30 Berlin, `engine._send_daily_report`): Web-Prognose +
|
||
**Vortags-Bilanz** (`stats_overview("yesterday")`) → **Telegram-Push** + **E-Mail**
|
||
(via `mailer.send_graph_mail`, `[graph]`-Creds, Absender `[graph] sender`=
|
||
mailagent@hocks.eu). Empfänger: `[graph] report_to` (kommagetrennt, Default
|
||
`trading@hocks.eu, axel@hocks.eu`). Ersetzt `scripts/send_daily_report.ps1` (das
|
||
hier nie lief — Microsoft.Graph-PS-Modul fehlt; Python nutzt Graph-REST direkt).
|
||
⚠ Graph/Exchange-Zustellung kann **mehrere Minuten verzögert** sein (202 = nur
|
||
angenommen). Fail-safe: kein Provider/Graph → Report übersprungen. **Versand-Status
|
||
persistiert** (`report_state.json`, `_load/_save_report_state`) — vorher schickte
|
||
jeder Server-Neustart nach 07:30 den Report ERNEUT (real 3× am 07.07.); gilt für
|
||
Tages- UND Wochenreport.
|
||
- **B4-Wochenreport (automatisch, `engine._send_weekly_report`):** **montags ab 07:30
|
||
Berlin, 1×/Woche** (gleicher `_daily_loop`, `_weekly_last_run`=(ISO-Jahr,KW)) →
|
||
**E-Mail + Telegram**. Ruft `weekly_review.build_report(m5=…)` (Live-Stats vs.
|
||
Backtest-Erwartung, Leckage-Flags — Track B/B4) und schickt es als `<pre>` (Betreff
|
||
„Oil Wochenreport (B4) — KW…", Empfänger = `[graph] report_to`). **Squeeze-Breakout-
|
||
B4-Monitor** (`_squeeze_section`): rechnet die Squeeze-Regel mit den Live-Params auf
|
||
den letzten ~30 Tagen M5 NEU (mechanischer Setup → Live-Perf. = Neuberechnung, keine
|
||
Trade-Attribution nötig) und hält ØR/PF gegen +0,14…+0,23. M5-Bars holt die Engine
|
||
unter `mt5_lock` und reicht sie durch (`build_report(m5=…)`; standalone lädt via
|
||
`_fetch_m5`, schließt MT5 nur bei Eigen-Init). Fail-safe wie der Tagesreport.
|
||
Manuell jederzeit: `python weekly_review.py`.
|
||
- **Position-Kachel zeigt SL/TP + Einsatz** (`pos-sl`/`pos-margin`;
|
||
`trader.snapshot()` liefert `sl`/`tp`/`margin`). **Kopfzeile der Kachel**
|
||
(`.pos-top`, 2026-07-15, User-Vorgabe): **Kurs · G/V** in EINER Zeile (18-px).
|
||
Das G/V (`#pnl`) zeigt **NUR NETTO** (Brutto entfernt, kein „n."-Präfix): bei
|
||
Gewinn P&L × (1 − `[trading] wht_pct`/100) = abzgl. WHT-Quellensteuer, bei Verlust
|
||
unverändert (keine Steuer). Snapshot `wht_pct`; Tooltip nennt den Satz. Swap ist
|
||
bereits im P&L (`trader.pnl = profit + swap`), Kommission beim Standard-Konto 0.
|
||
Einsatz/SL/TP/Richtung stehen in der Trade-Leiste, S/R im Chart (aus der Kachel
|
||
entfernt); `emg-lbl`-Beschriftungen ausgeblendet (Felder self-labeln via
|
||
Platzhalter/Button-Text). Fehlender SL → rot „⚠ kein SL
|
||
gesetzt!". „Einsatz" = gebundene Margin (`order_calc_margin`). **Notfall-Stop-
|
||
Feld** (`#emg-input` + setzen/aus): manueller Verlust-Wert → Server schließt
|
||
sofort (s. „Notfall-Stop"). Feld wird mit aktivem bzw. gemerktem Wert
|
||
(`emergency_loss`/`emergency_remember`) **vorbefüllt** (außer beim Tippen);
|
||
**Enter/„Go"** löst „setzen" wie der Button aus. Status `#emg-state`: „aktiv:
|
||
schließt bei −…" (rot) bzw. „vorgemerkt −… (aktiv beim Öffnen)". **Gewinn-Feld
|
||
(„Gewinn mitnehmen bei") aus dem Dashboard ENTFERNT (User-Vorgabe 2026-07-14)** —
|
||
das Backend bleibt vollständig erhalten (`engine.set_takeprofit`, kombinierter
|
||
`_check_auto_close`, `POST /api/takeprofit`, Telegram „💰 Gewinn mitgenommen",
|
||
Config `[trading] auto_takeprofit` Default 0=aus), ist ohne UI aber schlafend;
|
||
der P(break)-S/R-Auto-Close übernimmt die Gewinnmitnahme-Rolle.
|
||
- **Blinken:** LONG/SHORT-Button blinkt bei jeder Richtungs-Empfehlung (nicht mehr
|
||
an 70 % gekoppelt). **Ton bei Empfehlung** (`beepRecommendation`): **sofort beim
|
||
Signal-Wechsel** (WARTEN→LONG/SHORT oder Flip, 3-s-Flacker-Schutz) **+ Erinnerungs-
|
||
Ton alle 30 s** (`_REC_REBEEP_MS`), solange der Button blinkt — **nur wenn flat**
|
||
(keine Position). LONG = aufsteigend, SHORT = absteigend (unterscheidbar vom
|
||
Close-Alarm). Audio erst nach 1× Nutzer-Tap aktiv — ist es noch gesperrt, zeigt
|
||
ein einmaliger Toast „🔇 Ton gesperrt — einmal tippen". Kein Ton bei offener
|
||
Position. **Mute-Button** (`#mute-btn`, 🔊/🔇 rechts in der Tab-Leiste) schaltet
|
||
ALLE Töne (Gate in `_tone` + beiden beep-Funktionen), persistiert via
|
||
`localStorage("oil_muted")`. **CLOSE blinkt + Ton NUR wenn `pnl > sr_close_min_gain`**
|
||
(Feld leer/0 → `pnl>0` wie bisher; deckt sich mit dem Auto-Close-Gate — unter der
|
||
Schwelle schließt der Bot ohnehin nicht, also auch kein Alarm; Frontend `gainOK`,
|
||
gilt für Flip-Close UND S/R-Close-Blink/Ton). Der Telegram-Flip-Push (`_check_close_
|
||
alert`, Backend) bleibt bei `pnl>0`. **Flip-Close** = Signal-Flip gegen die Position.
|
||
**S/R-Ziel-Close-Hinweis
|
||
ENTFERNT** (stupste zum verfrühten Gewinner-Kappen → Verlust-Schiefe; Gewinner
|
||
führt das Trailing). Bei Verlust-Trade: kein Close-Alarm. **Jeder echte Auslöser
|
||
loggt** `🔔 Flip-Close-Alarm #N` + Zähler `snapshot.close_alert_count` (zum
|
||
objektiven Nachzählen, statt aus Trades/Recs zu rekonstruieren). Engine: `_check_close_alert` (Telegram max.
|
||
**1×/5 min**, `_CLOSE_ALERT_COOLDOWN_S` — kein Spam, wenn der Kurs ums Level
|
||
pendelt). ⚠ Cooldown ist **ticket-basiert** (`_close_alert_ticket`): Reset NUR
|
||
bei echter neuer Position; ein kurz flackerndes Ticket (transient leeres
|
||
`positions_get`) setzt den Cooldown NICHT mehr zurück (war die Spam-Ursache).
|
||
Frontend: `flipClose || srTarget`. Kein Auto-Close an S/R (Backtest).
|
||
- **Trade-Abschluss-Telegram ENTFERNT** (`history.log_trade_close` sendet kein
|
||
Telegram mehr; Import bereinigt). Close-bezogene Telegrams kommen jetzt **nur
|
||
noch** aus der Engine: **🔔 CLOSE-Signal** (echter Signal-Flip, `_check_close_
|
||
alert`) + **🚨 Notfall-Close / 💰 Gewinn-Close** (armierter Auto-Close,
|
||
`_check_auto_close`). Frontend-Blink/Ton weiterhin nur bei Flip im Plus.
|
||
- **Order-Bestätigungen:** Kein hartes Blockieren mehr. Eröffnen bei WARTEN/Gegen-
|
||
Signal → **Hinweis-Dialog „trotzdem eröffnen?"** (überschreibbar). CLOSE bei
|
||
**negativem** P&L → Sicherheitsabfrage; im Plus/Breakeven direkt. Sonst keine
|
||
Token-Dialoge (`require_token=false`). Audio braucht 1× Nutzer-Tap.
|
||
- Asset-Version aktuell **v=124** (in `web/index.html` hochzählen, siehe Workflows).
|
||
Schriftgrößen 2026-07-24 global **+4px** (2× je +2px auf User-Wunsch; Body-Basis
|
||
14→18px). ⚠ Betrifft in `style.css` sowohl `font-size:Npx` (71×) ALS AUCH die
|
||
`font:<weight> Npx/…`-**Shorthand** (3×: `.ms-chip`/`.ms-bos-lbl`/`.sqm-badge` —
|
||
die wurden zuerst übersehen, weil das erste Bump-Skript nur `font-size` erfasste)
|
||
+ Chart-`fontSize` in `app.js`. Bei künftigen globalen Font-Änderungen die
|
||
Shorthand nicht vergessen.
|
||
|
||
## Web-API (server.py)
|
||
`GET /api/snapshot` (inkl. `verdict`-Aggregat) `/api/health` `/api/logs` `/api/stats` `/api/news` `/api/bars?tf=&n=` (Charts, read-only) `/api/squeeze_monitor` (B4-Monitor, read-only, Engine-Cache 60 s) · `WS /ws` ·
|
||
`POST /api/order|auto|trail|emergency|takeprofit|sltp|srclose|srclose_min|autosqueeze|autosignal|auth/check`.
|
||
`/api/sltp {sl,tp}` = manuelles SL/TP der offenen Position → `engine.set_sltp` →
|
||
`trader.modify_sltp` (TRADE_ACTION_SLTP, prüft Seite/Mindestabstand) **und deaktiviert
|
||
das Trailing** (`trail.deactivate` — sonst zöge es SL/TP beim nächsten Tick zurück,
|
||
User-Vorgabe). ⚠ **Extern-Erkennung (Fix 2026-07-17):** Setzt der User SL/TP **direkt
|
||
im MT5-Terminal** (nicht über die App), greift `deactivate()` nicht — das Trailing
|
||
überschrieb die Handeingabe (real: TP 90,000 → 81,484). `trailing._do_modify` vergleicht
|
||
jetzt den Broker-SL/TP mit dem **zuletzt selbst gesetzten** (`_last_sl/_last_tp`, tol =
|
||
Modify-Schwellwert): weicht er ab → **Trailing schaltet sich ab, Handeingabe bleibt
|
||
stehen** (Log „Manuelle SL/TP-Änderung erkannt"). `_last_sl/_last_tp` werden bei
|
||
Aktivierung genullt (kein Altwert-Fehlalarm); Erkennung erst ab dem 2. Modify. Token via `[web] api_token`; bei
|
||
`require_token=false` ist die Token-Pflicht aus (Zugang nur via WireGuard).
|
||
- **Snapshot-Cache:** `/api/snapshot` UND `/ws` liefern `engine.snapshot_cached()`
|
||
(TTL `_SNAP_TTL_S=0,8 s`) — EIN berechneter Snapshot wird über alle Geräte
|
||
geteilt, SQLite/Verdict-Last bleibt unabhängig von der Client-Anzahl.
|
||
- **WS-Wakeup nach Sperrbildschirm/Hintergrund (Fix 2026-07-22, User-Meldung „G/V
|
||
großer Lag"):** Backend-Pipeline ist schnell (`_pos_loop` 1 s · Snapshot-Cache
|
||
0,8 s · WS-Push 1 s; gemessen: MT5-Lock-Last selbst im Worst-Case nur ~25 ms,
|
||
0 Lock-Timeouts/`_pos_loop`-Fehler im Log) — die Ursache lag im **Frontend**:
|
||
mobile Browser pausieren WS+Timer beim Sperren/Backgrounding, ohne
|
||
`visibilitychange`-Handling zeigte die Seite bis zu einem zufälligen Reconnect
|
||
den STALEN Stand (Minuten alt). Fix: `visibilitychange`/`pageshow`/`focus`-Listener
|
||
(`_wakeRefresh` in `app.js`) holen beim Zurückkommen sofort einen frischen
|
||
REST-Snapshot UND bauen die WS-Verbindung hart neu auf (statt auf den evtl.
|
||
hängenden alten Socket zu warten).
|
||
**REST-Polling als Sicherheitsnetz (2026-07-22, User-Wunsch „JSON-Feed"):**
|
||
zusätzlich zum WS-Push pollt `app.js` alle `POLL_MS=4000` ms `/api/snapshot` —
|
||
fängt den Fall auf, dass der WS still hängt (kein `onclose`, keine Zustellung
|
||
mehr, z. B. schwaches WireGuard-Signal) OHNE auf einen Reconnect zu warten;
|
||
pausiert bei `document.hidden` (Akku/Daten). **`lastTs`-Dedup** (`_applySnapshot`,
|
||
vergleicht `engine.snapshot_cached().ts` — dieselbe Quelle für WS UND REST):
|
||
nur die jeweils NEUERE Quelle rendert → kein Flackern/Doppel-Render, wenn beide
|
||
fast gleichzeitig eintreffen. Alle Snapshot-Konsumenten (initialer Load,
|
||
Wake-Refresh, Poll, WS) laufen jetzt über `_applySnapshot` statt direkt `render`.
|
||
v=108. `GET /api/snapshot` ist ohnehin öffentlich/token-frei nutzbar — externe
|
||
Tools (Shortcuts, Home Assistant, …) können ihn direkt pollen.
|
||
|
||
## Ausstehende Messungen — Erinnerung per Timer
|
||
|
||
`measurement_reminder.py` + Windows-Task **`OilMeasurementReminder`** (täglich 18:00)
|
||
meldet per Popup, welche Messung **genug Daten** hat. Jeder Punkt hat eine PRÜFBARE
|
||
Fälligkeitsbedingung (Datenmenge/Datum), nicht nur ein Datum — gemeldet wird je Punkt
|
||
1× (Marker in `.reminders/`, gitignoriert; Marker löschen = Wiedervorlage).
|
||
Status jederzeit: `python measurement_reminder.py --status`.
|
||
|
||
⚠ **Warum es das gibt:** mehrere Module sammeln still Daten für „später" — und das
|
||
blieb liegen. Die P(break)-Prognose-Genauigkeit war seit 2026-07-23 auswertbar und
|
||
hatte am 2026-07-30 bereits **2213 ausgewertete Zeilen**, ohne dass jemand hinsah.
|
||
|
||
| Messung | Bedingung | Stand 2026-07-30 |
|
||
|---|---|---|
|
||
| P(break)-Prognose-Genauigkeit | ≥500 ausgewertete Vorhersagen | **2213 → FÄLLIG** |
|
||
| Chartmuster-Kontrolltest (generischer Breakout) | jederzeit | **FÄLLIG** |
|
||
| Verdict-Module auf Prädiktivität (`verdict_votes`) | ≥25 000 Verdicts | 5961 (24 %) |
|
||
| Auto-Signal-Entry B4/B5 | ≥20 AUTOSIG-Trades | 0 |
|
||
| M30-Level-Umstellung live gegenprüfen | ≥30 S/R-Closes seit 30.07. | 8 (27 %) |
|
||
| M1-Squeeze Variante B | ≥180 Tage `candles_m1` | 13 (7 %) |
|
||
| News-Score-Prädiktivität | ≥90 Tage Sammlung | 7 (8 %) |
|
||
|
||
Verwandte Timer: **`HyperliquidBookflow`** (Order-Flow, 2 Stufen ~3 Tage/~2 Wochen),
|
||
**`HyperliquidLeadLag`** (erledigt: Pepperstone führt), Wochenreport montags 07:30 (B4).
|
||
|
||
## WTI-Trading-Handbuch — Abgleich mit dem Gemessenen (2026-07-30)
|
||
|
||
Der User reichte ein 19-seitiges WTI-Handbuch ein („lerne daraus Kontext für die
|
||
Empfehlung"). Ergebnis des Abgleichs: **der belegbare Teil ist längst umgesetzt, der
|
||
Rest ist in diesem Projekt bereits gemessen — und fiel durch.** Kein Konzept aus dem
|
||
Handbuch wurde neu ins Signal aufgenommen.
|
||
|
||
**Bereits UMGESETZT (Handbuch bestätigt den Ist-Zustand):**
|
||
| Handbuch | im Bot |
|
||
|---|---|
|
||
| „Nur mit dem HTF-Trend" | M30-Gegen-Trend-Filter (gemessen: Edge ×2) + Entry-Checkliste |
|
||
| „Keine Trades vor EIA" | EIA-Blackout Mi 15:30–16:30 (gemessen: beidhälftig negativ) |
|
||
| ATR-basierter Stop + Puffer | Initial-SL-Band 1,8–2,2×ATR (gemessen besser als 1,5) |
|
||
| „Nach TP1 trailen" | Trailing 1,5×ATR + Breakeven 1,3 (gemessen optimal) |
|
||
| „Nach 3 Losses Pause" | Mental-Check in der Entry-Checkliste (Verlust-Serie des Tages) |
|
||
| Journal + Kennzahlen | `trades`-DB, Statistik-Tab, Wochenreport (WR/PF/Verhältnis) |
|
||
| NY-Session am besten | gemessen: 21–22 Uhr sind die einzig robust positiven Stunden |
|
||
|
||
**Bereits GEMESSEN und VERWORFEN (Handbuch empfiehlt es trotzdem):**
|
||
Liquidity Sweeps (Equal Highs/Lows) · Order-Block-Klasse = Pivot-Zonen (6×) ·
|
||
Double Top/Bottom · H&S/Triangle/Flag/Cup (Ziel-Trefferquote nur 13–38 %) ·
|
||
Wyckoff Accumulation/Distribution · False Breakout · DXY-Korrelation
|
||
(`backtest_intermarket.py`) · Elliott (nur Verdict-Stimme, Gewicht 1,0).
|
||
|
||
**NEU gemessen (die echten Lücken, `backtest_handbook.py`) — alle durchgefallen:**
|
||
| Konzept | H1 | H2 | Urteil |
|
||
|---|---|---|---|
|
||
| **PDH/PDL** Bounce („WTI reagiert extrem stark") | −0,335 | −0,199 | ❌ beidseitig negativ |
|
||
| **PDH/PDL** Sweep | −0,462 | −0,184 | ❌ schlechtestes Ergebnis überhaupt |
|
||
| **Asian Range H/L** Bounce | −0,134 | −0,005 | ❌ |
|
||
| **Asian Range H/L** Sweep | −0,178 | −0,040 | ❌ |
|
||
| **Discount/Premium**-Filter | −0,097 (Basis −0,135) | +0,041 (Basis +0,053) | ❌ nicht robust, wirft 70 % Volumen weg |
|
||
| **Freitag ab 16:00 CET meiden** | −0,179 (Basis −0,135) | +0,020 (Basis +0,053) | ❌ **verschlechtert BEIDE** |
|
||
|
||
⚠ Zwei Befunde sind bemerkenswert: (a) **PDH/PDL ist der am klarsten schädliche
|
||
Level-Typ**, den das Projekt je gemessen hat — obwohl das Handbuch ihn als besonders
|
||
stark bewirbt. (b) Der **Freitagnachmittags-Filter verschlechtert beide Hälften**, die
|
||
dort geblockten Trades waren also überdurchschnittlich — dieselbe Lehre wie beim
|
||
Dead-Hours-Gate: pauschale Zeitfilter kosten mehr, als sie sparen.
|
||
|
||
**Nicht umgesetzt (bewusste User-Entscheidungen, nicht Messfrage):** 1–2 % Risk/Trade
|
||
und 3 % Tages-Limit (User fährt 95 % Margin, Circuit Breaker am 2026-07-30 erneut
|
||
abgelehnt) · Mindest-R:R 1:2 (der Bot nutzt Trailing statt festem Ziel — gemessen
|
||
besser) · Teil-Exit bei 1:1 (`_PARTIAL_TP_FRAC=0`, User-Vorgabe).
|
||
|
||
## Auch beachten
|
||
- **Startup-Schonfrist gegen Sofort-Close nach Neustart (User-Vorgabe 2026-07-20,
|
||
`[trading] startup_close_grace_s=60`):** In den ersten 60 s nach Bot-Start schließt
|
||
der Bot **keinen laufenden Trade selbst** — gilt für **S/R-Close** (`_check_sr_close`),
|
||
**Notfall-Stop/Gewinn-Ziel** (`_check_auto_close`, Armieren läuft weiter, nur der Close
|
||
ist gesperrt), **Time-Stop** (`trailing`, via `set_no_close_until`) und **Auto-Squeeze-
|
||
Reverse**. Grund: nach `restart_server.bat` sind Preis/ATR/P(break)/Level für einen
|
||
Moment instabil → ein adoptierter Trade, der gerade in S/R-Close- oder Notfall-Nähe
|
||
liegt, würde sonst in der ersten Sekunde auf halb-initialisierten Daten gekappt.
|
||
Engine setzt `_no_autoclose_until = start + grace`, `_in_startup_grace()` gated die
|
||
drei `_pos_loop`-Checks; die Trailing-Instanz bekommt denselben Stichtag. ⚠ Der
|
||
**Broker-SL (2×ATR) bleibt unberührt** — er läuft am Broker, kann NICHT verzögert
|
||
werden (dort ist auch keine Verzögerung erwünscht: ein Stop-Loss muss greifen). 0 = aus.
|
||
- **Update-Reboot-Lücke (Root-Cause der Nacht 2026-07-20, −95,48 €):** Windows
|
||
Update rebootete 01:30 (MoUsoCoreWorker/TrustedInstaller, 2×), der Server-Task
|
||
`OilTradingServer` hat nur einen **Logon-Trigger** → Server+MT5 down bis zum
|
||
Login 07:42; Time-Stop/S/R-Close/Notfall-Stop liefen nicht, nur der **Broker-SL**
|
||
(broker-seitig!) schützte. **Fix:** WU-Policy `AUOptions=4, Tag=Sa, 03:00`
|
||
(HKLM\...\WindowsUpdate\AU — Reboots nur noch Samstag früh, Markt zu); Standby/
|
||
Hibernate AC+DC=0. ⚠ Offen: nach einem Sa-Reboot startet der Server erst beim
|
||
nächsten Login — **Auto-Logon (netplwiz) wäre die letzte Lücke** (So-Nacht-Open),
|
||
braucht das Windows-Passwort des Users. Server-seitige Schutzschichten sind nur
|
||
so gut wie die PC-Laufzeit.
|
||
- **UI-Einstellungen neustart-fest (`runtime_state.json`, 2026-07-19):** App-gesetzte
|
||
Werte — 🚀-BRK-Toggle (`auto_squeeze`), Auto-Close-Schalter (`auto_sr_close`),
|
||
Gewinn-Close-Mindestgewinn (`sr_close_min_gain`) — werden bei jedem `set_*` atomar
|
||
persistiert (`_load/_save_runtime_state`) und überschreiben beim Start die
|
||
ini-Defaults (nur vorhandene Keys). ⚠ **`set_sr_close_min_gain` vergaß den `_save`
|
||
bis 2026-07-22** (Bug, jetzt gefixt) — der am Handy gesetzte Mindestgewinn überlebte
|
||
Änderungen nicht sauber; zusätzlich schickte Enter auf leerem Feld ungewollt 0
|
||
(`change`+`keydown` doppelt → Enter blurrt jetzt nur, `srclose_min`-Endpoint loggt `[WEB]`).
|
||
- **Log-Hygiene:** Trail-DEBUG-Zeile (`Trail: profit=…`) auf **1×/30 s gedrosselt**
|
||
(2026-07-19; vorher ~28k Zeilen je Rotation → 5×5 MB deckten nur ~2 Tage Forensik).
|
||
- **DB-Guard:** `history.log_trade_open` verwirft 0-Lot-/Preis-lose Einträge
|
||
(Reconcile-Artefakte; 6 Alt-Zeilen am 2026-07-19 bereinigt, Backup
|
||
`oil_widget_history.db.bak-2026-07-19`).
|
||
- Persönliches Auto-Memory unter `C:\Users\ah\.claude\projects\…\memory\`
|
||
(Beobachtungen/Feedback) ergänzt diese Datei.
|
||
- `oil_widget_config.ini` enthält **Live-Secrets** (Keys/Token) — nicht ins
|
||
Repo/Logs leaken.
|
||
- **DB** `oil_widget_history.db` (trades · intended_trades · recommendations · **candles_m1** · **pbreak_predictions** · **verdict_votes**).
|
||
Vor jedem Eingriff Backup (`*.bak-<datum>`); Zeitspalten sind lokale Epoch
|
||
(Broker-Offset bereits korrigiert) — **AUSNAHME `candles_m1.time` = ROHE Broker-Zeit
|
||
(UTC+3)**, deckungsgleich mit `copy_rates`.
|
||
- **M1-Candle-Logger (`core/candle_logger.py`, 2026-07-21, Daten-Sammlung, kein
|
||
Strategie-Eingriff):** schreibt abgeschlossene M1-Kerzen (OHLC + Spread-in-Preis +
|
||
tick_volume + symbol) nach `candles_m1` (PK `time`, INSERT OR IGNORE = dedupliziert,
|
||
self-healing). `engine.candles.log(sym)` im Trend-Loop, self-throttled ~55 s; erster
|
||
Lauf backfillt ~3000 Bars, danach die letzten 180. **Warum:** Broker-M1-History rollt
|
||
weg (kurz); M1 ist die Master-Auflösung → aus M1 jede höhere TF exakt aggregierbar.
|
||
⚠ Ticks/Echtzeit bewusst NICHT geloggt (~85k/Tag = 30M/Jahr, kein Bedarf — Strategien
|
||
sind M5+, Slippage aus echten Fills gemessen). ~370k Bars/Jahr = vernachlässigbar.
|