User-Vorgabe: Dreiecke/Rechtecke/Ellipsen ueber OBJ_* an den berechneten Swings verankern, mit leichtem Grauton gefuellt. PT-Zeile traegt jetzt die Geometrie: PT;<TF>;<name>;<dir>;<trigger>;<ziel>;<form>;<t1>;<p1>;<t2>;<p2>;... dreieck (3 Anker) -> OBJ_TRIANGLE Doppeltop/-boden, Dreiecke, Keile, Wimpel dreieck2 (5 Anker) -> 2x OBJ_TRIANGLE SKS, inv. SKS, Dreifach-Top/-Boden rechteck (2 Anker) -> OBJ_RECTANGLE Rechteck (Range), Flagge ellipse (3 Anker) -> OBJ_ELLIPSE Tasse+Henkel, inverse Tasse dreieck2 braucht ZWEI Objekte, weil OBJ_TRIANGLE nur drei Punkte kennt; die beiden Dreiecke (Schulter-Kopf, Kopf-Schulter) kacheln die Silhouette exakt. OBJPROP_FILL faerbt mit OBJPROP_COLOR - ein Objekt kann keine graue Flaeche UND einen farbigen Rand haben. Deshalb Flaeche neutral grau (InpMusterFill) und OBJPROP_BACK (hinter den Kerzen); die Richtungsaussage tragen Trigger- und Ziellinie darueber. Inhaltlich richtig: die Form ist eine Beobachtung, keine Richtungsprognose. Python (core/patterns.py): add() nimmt form + punkte, legt sie als form/anker in den Muster-dict. REIN ADDITIV - kein bestehender Schluessel aendert sich. Neuer Helfer _keil(): schneidet die beiden Linien und DECKELT den Apex auf last_idx+40 (ein fast paralleles Paar schneidet sich im Unendlichen); nach links begrenzt, damit der Apex nicht im Muster liegt. _anker() rechnet einen Index jenseits des letzten Bars ueber die Bar-Dauer hoch - sonst waere der Apex auf den letzten Bar geklemmt und jedes Dreieck haette einen senkrechten rechten Rand. Bar-Dauer aus den DATEN, nicht aus der TF-Konstante. Zeitstempel sind ROHE Broker-Zeit (rates["time"]) - genau das, was iTime() liefert. Keine Offset-Rechnung, gleiche Konvention wie EQ; und TR;. Vor dem Export wird geprueft, dass Form und Punktzahl zueinander passen - ein Dreieck mit zwei Punkten wuerde der Indikator still verwerfen. Abwaertsvertraeglich in beide Richtungen. Verifiziert: 6 synthetische Muster (Form, Punktzahl, aufsteigende Zeitstempel, Apex in der Zukunft) + live Snapshot patterns_tf.M5 form=dreieck anker=3 + CSV-Zeile mit Geometrie. Compile 0 errors, Pipeline gruen, deployt.
9567 lines
654 KiB
Markdown
9567 lines
654 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).
|
||
- ⚠⚠ **STATISTIK-PRÜFUNG 2026-08-05 (User-Auftrag): alle Aggregate KORREKT — aber
|
||
die NETTO-Rechnung überschätzt die Steuerlast um Faktor 4,7 und dreht das
|
||
Vorzeichen des Gesamtergebnisses.**
|
||
✅ **Geprüft und exakt:** `/api/stats` gegen unabhängige SQL-Rechnung, ~50 Werte
|
||
über `today`/`week`/`all` — n_trades, wins/losses/breakeven, total_pnl, avg_win,
|
||
avg_loss, gross_win/loss, winrate, profit_factor, dazu die inneren Konsistenzen
|
||
(wins+losses+be = n · gross_win−gross_loss = total_pnl · Σ closed_by = n ·
|
||
Σ hour_stats = wins/losses) und 8 Setup-Zeilen. **Alles stimmt bitgenau.**
|
||
Ebenso `stats_compact` (Dashboard-Kachel) und `day_pl` im Header.
|
||
⚠⚠ **DER DEFEKT liegt in `server._add_net` / `_add_net_setup`:**
|
||
`net_pnl = total_pnl − gross_win × 26,375 %`. Das unterstellt, die Quellensteuer
|
||
auf JEDEN Gewinn-Trade sei endgültig verloren. **Sie wird aber größtenteils
|
||
erstattet** — genau das steht zwei Absätze weiter unten („erstattet via tägl.
|
||
*Tax settlement*"), fließt in die Rechnung aber nicht ein.
|
||
**Empirisch aus den echten Broker-Buchungen** (`history_deals_get`, 08.05.–05.08.,
|
||
derselbe Zeitraum wie die Statistik):
|
||
| | |
|
||
|---|---|
|
||
| WHT einbehalten (685 Buchungen) | **−2.236,10 €** |
|
||
| erstattet (63 Tax-Buchungen) | **+1.789,65 €** |
|
||
| **tatsächlich verblieben** | **−446,45 €** |
|
||
| Modell der Statistik | −2.097,10 € |
|
||
| **angezeigtes Netto** | **−1.104,67 €** |
|
||
| **reales Netto** | **+545,98 €** |
|
||
**Abweichung +1.650,65 € — die Anzeige macht aus einem profitablen Konto ein
|
||
verlustreiches.** Real verbleiben nur **21 %** der modellierten Last.
|
||
✅ **Richtige Lösung: die TATSÄCHLICHEN Buchungen verwenden statt zu schätzen.**
|
||
Die WHT-/Tax-Deals sind über `mt5.history_deals_get` + Kommentar-Filter
|
||
(`WHT #…` / `Tax settlement`) exakt abrufbar; die Schätzung kann ganz entfallen.
|
||
⚠ Zu cachen (MT5-Abfrage gehört nicht in den 1-s-Snapshot) und unter `mt5_lock`.
|
||
⚠ **Der Header-G/V ist ein anderer Fall** (`app.js`: `raw × (1 − wht/100)` für die
|
||
OFFENE Position): dort ist der Abzug im Moment des Schließens sachlich richtig —
|
||
die Erstattung kommt erst später. Das ist eine Cashflow-Sicht, also vertretbar,
|
||
aber ebenfalls pessimistisch.
|
||
**Nicht gebaut** — Umfang war die Prüfung.
|
||
- **Steuer:** Broker behält **WHT 26,375 %** (DE-Abgeltungst.+Soli) je Gewinn ein
|
||
(`WHT #…`-Buchungen), erstattet via tägl. „Tax settlement". Stats sind **brutto**;
|
||
`[trading] wht_pct` zeigt eine Netto-Schätzung.
|
||
- **Python:** `C:\Users\ah\AppData\Local\Programs\Python\Python312\python.exe`
|
||
(hat fastapi/uvicorn/MetaTrader5). System32 fehlte mal in der PATH → Skripte
|
||
nutzen absolute Pfade.
|
||
- ⚠⚠ **LLM-PROVIDER-WECHSEL 2026-08-05: `[agent] provider=zai` (war `deepseek`).**
|
||
DeepSeek-Guthaben ist leer — `HTTP 402 "Insufficient Balance"`. **z.ai/GLM
|
||
(`glm-4.5-flash`) ist kostenlos**, antwortet in ~2,3 s (DeepSeek ~6 s) und wird im
|
||
Projekt ohnehin für `daily_levels` genutzt; live verifiziert („[zai] NEUTRAL (30%)").
|
||
Kimi (`kimi-k2.6`) antwortet ebenfalls (7,6 s), ist aber kostenpflichtig.
|
||
⚠⚠ **Der eigentliche Befund: die Fallback-Kette hatte NICHT gegriffen.** `_active_
|
||
provider` prüfte in `avail` nur das **Key-FORMAT** (`startswith("sk-")`), nicht ob der
|
||
Key noch funktioniert. Ein Provider ohne Guthaben galt damit als „verfügbar" und
|
||
**blockierte die gesamte Kette** (`deepseek→kimi→zai→local`) — der Copilot lief
|
||
**~18 h still ins Leere** (29 Fehlversuche ab 04.08. 14:52), ohne dass irgendetwas
|
||
außer einer WARNING-Zeile passierte.
|
||
✅ **BEHOBEN am selben Tag (`agent._mark_dead`/`_provider_chain`/`_chain_raw`):** ein
|
||
hart gescheiterter Provider wird zeitweise übersprungen und `analyze()` nimmt den
|
||
nächsten aus der Kette. **Hart = 401/402/403/404** (Key/Guthaben/Modell) → **30 min**;
|
||
**nicht erreichbar** (z. B. Ollama aus) → 5 min. **Bewusst NICHT hart: Timeout, 429,
|
||
5xx** — das sind vorübergehende Zustände; wer darauf wechselt, verliert bei jeder
|
||
Lastspitze den bevorzugten Anbieter und zahlt pro Zyklus die volle Timeout-Summe
|
||
ALLER Anbieter (90–120 s je Stück). Sichtbar im Snapshot als
|
||
**`agent.provider_dead {provider: Restminuten}`** — der Ausfall stand vorher nur im
|
||
Log, genau deshalb blieb er 18 h unbemerkt. `_dead` läuft absichtlich **ohne**
|
||
`self._lock` (`snapshot()` hält ihn bereits, nicht reentrant → Deadlock; dict-Zugriffe
|
||
sind unter dem GIL atomar). Mit **9 Szenarien getestet** (402→Fallback · zweiter Zyklus
|
||
überspringt direkt · zwei tote Anbieter · Timeout/429 lösen KEINEN Wechsel aus ·
|
||
ConnectionError 5 min · alle tot → sauberer Fehler · Erholung nach Ablauf · Snapshot).
|
||
Zurück auf DeepSeek nach Aufladen: `provider = deepseek`.
|
||
- **LLM-Provider (Historie):** **DeepSeek (`[deepseek]`) war der aktive
|
||
Copilot-Provider** (`[agent] provider=deepseek`, 2026-07-20 bis 2026-08-05). `api.deepseek.com`,
|
||
Modell **`deepseek-v4-flash`** (`/models` listet flash + pro; alte Namen
|
||
`deepseek-chat/-reasoner` gibt es NICHT mehr). ⚠ v4-flash ist ein **Reasoning-Modell**
|
||
(content nach `reasoning_content` → `max_tokens=4000` in `_call_deepseek`, sonst leer;
|
||
temperature 0.3 ok, schnell ~6 s). **⚠ KEINE Web-Suche** → NICHT für `daily_levels`
|
||
nutzbar. Fallback-Kette kimi→zai→local greift, wenn der Key ausfällt.
|
||
**Kimi/Moonshot (`[kimi]`, `kimi-k2.6`)** bleibt als Provider verfügbar. Key nur auf dem
|
||
**internationalen Endpoint `api.moonshot.ai`** gültig (`.cn` = „Invalid
|
||
Authentication"). Modell **`kimi-k2.6`** (`/v1/models` listet auch k2.7-code/k3;
|
||
k3 = thinking-only). ⚠ **Zwei Fallstricke:** (1) `temperature` MUSS **1** sein
|
||
(andere Werte → HTTP 400); (2) k2.6 ist ein **Reasoning-Modell** → schreibt
|
||
VARIABLE ~1000–1200 `reasoning_tokens` (in `message.reasoning_content`) VOR dem
|
||
`content` → `max_tokens` großzügig (**4000**, `_call_kimi`), sonst frisst das
|
||
Reasoning das Budget und `content` bleibt leer (2000 war teils leer). Moonshot
|
||
cached den System-Prompt (`cached_tokens`) → günstiger bei Wiederholung. Kimi ist
|
||
CN-Modell → CJK-Drift möglich, vom `_has_cjk`-Retry abgedeckt.
|
||
Frühere Option **z.ai/GLM** (`[zai]`, `glm-4.5-flash`, kostenlos) bleibt
|
||
reaktivierbar; ⚠ GLM ist Thinking-Modell → IMMER `"thinking":{"type":"disabled"}`.
|
||
⚠ **`_active_provider`-Fix 2026-07-20:** zai/kimi waren vorher NICHT in der
|
||
`avail`/`orders`-Map → `provider=zai` fiel still auf `local` (qwen) zurück (die
|
||
`[local]`-Logzeilen!). Jetzt beide routbar. OpenAI/Gemini-Keys = kein Guthaben (429).
|
||
- **KI-Copilot (`agent.py`):** `[agent] provider=deepseek`. CN-/lokale Modelle
|
||
(DeepSeek, Kimi, GLM, qwen) driften ins Chinesische → `_has_cjk`-Erkennung + 1×
|
||
Retry deckt `local`, `zai`, `kimi` UND `deepseek` ab. (alle bleiben als Provider-
|
||
Optionen wählbar.)
|
||
- **`daily_levels.py` bleibt auf z.ai (2026-07-20, gemessen):** Die Tagesprognose
|
||
BRAUCHT Live-Web-Suche. DeepSeek hat keine; Kimi-Web-Suche (`$web_search`-builtin)
|
||
läuft nur über einen Tool-Loop, ist >2 min langsam und lief im Test in `finish=length`
|
||
(Web-Ergebnisse+Reasoning sprengen selbst 4000 Tokens → leerer Content). z.ai macht
|
||
es in EINEM sauberen `web_search`-Call → kein Wechsel (Kontext-Feature, kein Edge).
|
||
⚠ **S/R-Kontext-Fix 2026-07-15:** `_tool_zones` füttert dem LLM jetzt die **echten
|
||
M5-Pivots** (`wave.pb_levels`, geclustert, nächste 3 je Seite) statt der stale
|
||
`[zones]`-Config — plus Prompt-Leitplanke „nutze NUR die übergebenen Level, erfinde
|
||
keine runden Marken". Reiner Kontext-Fix (keine Strategie): macht die Kommentar-Level
|
||
aktuell/konsistent zum Chart, macht die LLM-Einschätzung aber NICHT validierter
|
||
(Copilot bleibt unbelegte Verdict-Stimme, Gewicht nur bei Bias).
|
||
- **Tages-Prognose (`daily_levels.py`):** z.ai + Web-Suche, 1×/Tag 07:30 Berlin.
|
||
|
||
## Workflows / Befehle
|
||
- **Änderungen aktivieren:** `restart_server.bat` (lädt core neu). ⚠ Der Batch startet
|
||
das Oil-Backend im **VORDERGRUND** (Zeile 40) — also **nicht** mit Output-Umleitung
|
||
aus PowerShell aufrufen (`& .\restart_server.bat *>&1 | …`): der Aufruf blockiert bis
|
||
zum Timeout und der abgeschnittene Tail zeigt ein irreführendes „===== Server beendet
|
||
=====", obwohl der Server läuft.
|
||
⚠⚠ **NACHTRAG 2026-08-02: der Batch hat ZWEIMAL still NICHT neu gestartet** (auch als
|
||
`Start-Process cmd /c restart_server.bat -WindowStyle Minimized`). Der alte Prozess lief
|
||
weiter — und weil statische Dateien bei jedem Request frisch von der Platte gelesen
|
||
werden, **belegt eine korrekt ausgelieferte `app.js?v=N` GAR NICHTS** über den geladenen
|
||
Python-Code. Zuverlässig ist nur: Port-8000-Prozess killen und direkt starten
|
||
(`Start-Process python -ArgumentList '-X','utf8','server.py' -WorkingDirectory …`),
|
||
danach **an einem NEUEN Snapshot-Feld prüfen**, dass der Python-Code wirklich neu ist
|
||
(hier: `hl_live.basis_stale` fehlte → alter Code entlarvt). Zum Schluss verifizieren,
|
||
dass **genau eine** Instanz je Port läuft. Auto-Start:
|
||
Aufgabe `OilTradingServer` (Login, versteckt via VBS).
|
||
⚠ **Startet seit 2026-07-30 BEIDE Server** (User-Vorgabe): erst das
|
||
**Hyperliquid-Dashboard** (Port 8001, versteckt via dessen `start_hidden.vbs`,
|
||
wartet max. 10 s aufs Lauschen), dann das Oil-Backend (8000) im Vordergrund.
|
||
**Grund + behobener Bug:** die Kill-Zeile trifft mit `*server.py*` BEIDE Prozesse —
|
||
vorher wurde der HL-Server bei jedem MT5-Neustart still mit-erschlagen und nie wieder
|
||
gestartet. Das riss die **Lücken in die Lead-Lag-/Order-Flow-Datensammlung** (72 h
|
||
Spanne, aber nur 2292 Samples) UND ließ die Liquiditätslinien im Chart verschwinden.
|
||
Fail-safe: fehlt das HL-Projekt (`..\HyperLiquid-WTI Trader`), startet nur 8000 mit
|
||
Hinweis. Auto-Start des HL-Servers separat: Aufgabe `HyperliquidDashboard` (Login).
|
||
- **⚠ MQL5-Indikator: das RICHTIGE Terminal treffen (Fallstrick, 2026-07-31).**
|
||
Unter `%APPDATA%\MetaQuotes\Terminal\` liegen **mehrere** Instanz-Ordner. Wer das
|
||
Ziel über „hat `MQL5\Indicators`" + `Select-Object -First 1` bestimmt, erwischt
|
||
leicht den falschen — real passiert: v1.30 und v1.31 landeten in
|
||
`0BA5CADA…`, während der Bot in `D0E8209F…` schreibt und der Chart dort läuft.
|
||
Folge: der Kegel und die Konsolidierungs-Beschriftung waren einen halben Tag lang
|
||
„gebaut und kompiliert", aber unsichtbar — der Chart lief weiter auf v1.29.
|
||
**Richtig:** das Ziel über die **CSV** bestimmen, die der Bot tatsächlich schreibt:
|
||
```powershell
|
||
$term = Get-ChildItem "$env:APPDATA\MetaQuotes\Terminal" -Directory |
|
||
Where-Object { Test-Path "$($_.FullName)\MQL5\Files\sr_levels.csv" } |
|
||
Sort-Object { (Get-Item "$($_.FullName)\MQL5\Files\sr_levels.csv").LastWriteTime } -Descending |
|
||
Select-Object -First 1
|
||
```
|
||
Gegenprobe nach jedem Kompilieren: `#property version` im Terminal-`.mq5` **und**
|
||
der Zeitstempel der `.ex5` müssen zum erwarteten Stand passen.
|
||
- **✅⚠ AUFFANG-ELEMENT statt Disziplin (Fix 2026-08-07, v=150) — die Regel
|
||
direkt darunter wird jetzt vom CODE durchgesetzt.** `$()` gibt bei fehlendem
|
||
Element nicht mehr `null` zurück, sondern ein **detachiertes `<span>`**:
|
||
```js
|
||
const _FEHLT = document.createElement("span");
|
||
const $ = (id) => document.getElementById(id) || _FEHLT; // + 1× console.warn je ID
|
||
const $streng = (id) => document.getElementById(id); // gibt null
|
||
```
|
||
⚠⚠ **Anlass ist eine Messung, kein Gefühl: die Regel „neue Elemente per
|
||
`const el = $(…); if (el) {…}`" war bei 0 von 95 direkten Zugriffen befolgt.**
|
||
95 Stellen, an denen ein fehlendes Element den **GESAMTEN** Render abbricht;
|
||
0 davon abgesichert. Eine Regel mit 0 % Befolgung ist keine Regel — deshalb
|
||
löst es der Code an EINER Stelle statt an 95.
|
||
⚠ **`?.` war NICHT die Lösung** (mein erster Vorschlag, und er war falsch):
|
||
**80 der 95** Zugriffe sind ZUWEISUNGEN, und `$("x")?.y = v` ist ein
|
||
**Syntaxfehler** — Optional Chaining ist links vom `=` verboten.
|
||
⚠⚠ **`$streng` ist der wichtige Teil.** Alle Eingabefelder sind
|
||
`type="number"`, und **leer heißt dort ausdrücklich „Stop entfernen"**
|
||
(`sl: null`). Mit dem Auffang-Element wäre `.value` `undefined` → `parseFloat`
|
||
→ NaN → `JSON.stringify` macht daraus wieder **`null`** — eine fehlende
|
||
Eingabemaske hätte den **Broker-SL einer laufenden Position stillschweigend
|
||
gelöscht.** Ein lauter Absturz ist dort das bessere Verhalten. Betroffen war
|
||
genau **eine** Funktion (`sendSltp`); `setSrMin`/`setEmg`/`setManualMargin`/
|
||
`setMarginPct` fangen NaN bereits ab (geprüft, nicht angenommen).
|
||
⚠ Der Fehler wird **nicht verschluckt**, sondern **1× je ID** in die Konsole
|
||
gemeldet — ein stiller Fallback wäre derselbe Fehler wie der `catch {}` in
|
||
`pollSnapshot`, der Render-Fehler lautlos gefressen hat (behoben 01.08.).
|
||
✅ **Beide Richtungen verifiziert** an einem Minimal-DOM, das eine alte
|
||
`index.html` simuliert: mit der alten `$`-Definition bricht der Render beim
|
||
**zweiten** Feld ab, mit der neuen läuft er durch und nur die fehlenden Felder
|
||
bleiben leer; der Order-Pfad bricht in beiden Fällen sauber ab.
|
||
⚠ Die 40 vorhandenen `if (!el) return;`-Wächter feuern jetzt nicht mehr. Sie
|
||
bleiben stehen (kein Nutzen im Entfernen, und sie greifen weiter, wenn jemand
|
||
`$streng` benutzt) — beim Lesen aber mitdenken.
|
||
- **✅ G/V-LAG: 1-s-FALLBACK + EHRLICHER VERBINDUNGSPUNKT (2026-08-07, v=151).**
|
||
User: „G/V wird extrem lagged — 1 Sekunde".
|
||
⚠⚠ **ZUERST GEMESSEN, DANN GEDREHT — und das Backend war NICHT das Problem:**
|
||
| Stufe | gemessen |
|
||
|---|---|
|
||
| `/api/snapshot` (75 Abrufe / 20 s) | **21 verschiedene `ts`** = ~1/s |
|
||
| **WebSocket-Push, direkt am Socket** | **Median 1,04 s** (min 1,00 · max 1,06) |
|
||
| G/V-Änderungen | Ø 1,44 s — das ist der **Markt**, keine Verzögerung |
|
||
**Die ganze Server-Kette liefert bereits 1 s.** Der Lag entsteht erst am Handy:
|
||
bricht der WebSocket **still** weg (WireGuard-Aussetzer, App-Wechsel), trägt nur
|
||
noch der REST-Poll — und der stand auf **4 s**.
|
||
✅ **`POLL_MS` 4000 → 1000, aber MIT Sperre:** solange der WS liefert
|
||
(`lastWsMs`, Fenster `WS_FRISCH_MS`=2200 ms), überspringt der Poll den Abruf.
|
||
**Kein zusätzlicher Verkehr im Normalfall** (~14 KB/Abruf über WireGuard).
|
||
⚠ **Denkfehler, den erst die Simulation zeigte:** der erste Entwurf las die
|
||
WS-Frische aus `lastRecvMs` (jede Quelle) — damit **drosselte sich der Poll
|
||
selbst** und kam bei totem WS nur auf **2,9 s** statt 1 s. Jetzt zwei getrennte
|
||
Uhren: `lastWsMs` (nur `sock.onmessage`) entscheidet über das Überspringen,
|
||
`lastRecvMs` (jede Quelle) über die Stale-Anzeige.
|
||
**Simuliert:** WS gesund → **0** Zusatz-Abrufe, max. Alter **0,9 s** · WS tot →
|
||
1/s, max. Alter **0,9 s** · WS träge (5 s) → max. 2,9 s. Vorher bei totem WS:
|
||
4,0 s.
|
||
✅ **Der Verbindungspunkt log** — behoben. Er wurde **nur** bei `sock.onclose`
|
||
auf „off" gesetzt; genau der Fall, der den Lag erzeugt, feuert aber **kein**
|
||
`onclose`: der Socket hängt still, der Punkt blieb **grün**. Neu `.dot.stale`
|
||
(bernstein, pulsierend) ab **3,5 s** ohne frischen Snapshot — gemessen wird,
|
||
was ANKOMMT, nicht was der Socket über sich behauptet. Tooltip nennt das Alter.
|
||
⚠ Kein Konflikt mit `render()`, das `className` komplett überschreibt: läuft
|
||
`render()`, ist gerade ein Snapshot angekommen und `stale` gehört ohnehin weg.
|
||
`.dot.stale` steht in `style.css` **nach** `.on`/`.off` — gleiche Spezifität,
|
||
die Reihenfolge entscheidet.
|
||
- **⚠ NEUE HTML-Elemente IMMER null-sicher ansprechen (Fix 2026-08-01).**
|
||
`index.html` und `app.js` werden vom Browser **unabhängig** gecacht. Trifft neue JS
|
||
auf eine alte, gecachte HTML, wirft ein direkter Zugriff (`$("neu").textContent = …`)
|
||
eine TypeError — und weil das mitten in `render()` passiert, bricht der **GESAMTE
|
||
Render** ab: das Dashboard friert ein und zeigt Altwerte, obwohl Server und Snapshot
|
||
korrekt sind. Real passiert beim HL-Kurs-Einbau (User: „ich sehe die Änderungen
|
||
nicht"); Server lieferte nachweislich v=129 samt `#hdr-hl` aus, `Cache-Control:
|
||
no-store` war gesetzt, kein Service-Worker — die Ursache lag allein in dieser
|
||
Kombination. **Regel:** neue Elemente per `const el = $("id"); if (el) { … }`
|
||
ansprechen, dann läuft der Rest weiter und nur das neue Feld fehlt, bis die HTML
|
||
nachgeladen ist. (Das `?v=N`-Bump bustet nur `app.js`/`style.css` — NICHT die
|
||
`index.html` selbst.)
|
||
- **Web-Assets:** Browser cacht hart → in `web/index.html` `?v=N` **hochzählen**
|
||
bei jeder JS/CSS-Änderung; Server sendet `Cache-Control: no-store`. Hängt der
|
||
Handy-Cache: Inkognito oder Seite mit frischem `?xN` öffnen.
|
||
- **Empfehlungs-Edge messen:** `python backtest_signal.py [TF] [N] [K]`
|
||
(ereignisbasiert, nutzt die echte `_build`-Logik). Ø-Edge>0 = trägt.
|
||
- **Ausführungs-Kalibrierung:** `python analyze_execution.py` — (A) echtes
|
||
Spread-Profil je Berlin-Stunde aus dem Bar-`spread`-Feld, (B) Slippage bei
|
||
SL-Closes, (C) MAE/MFE der Live-Trades. **Kern-Befund 2026-07-13:** Spread ist
|
||
absolut ~konstant (0,0225), aber relativ **Ø 0,265×ATR** — die alte
|
||
Kostenannahme 0,1×ATR war v. a. in Niedrig-Vola-Phasen ~2,5× zu optimistisch
|
||
(erklärt Live-PF < Backtest-Erwartung). Stunden 15–17 am günstigsten (~0,13),
|
||
**Nacht 0–7 = Kostenfalle (0,32–0,50×ATR)**. Slippage-Tail real: die −35/−54-
|
||
Desaster hatten 0,75 Slippage ÜBER den Initial-SL hinaus (harter SL deckelt in
|
||
schnellen Märkten nicht). MAE/MFE live: Exits validiert (nur 7 % der Verlierer
|
||
waren je ≥1,3×ATR im Plus → Breakeven-Schwelle passt; Gewinner-MAE Median 0,28).
|
||
⚠ Künftige B3-Checks mit **stunden-echten Kosten** rechnen, nicht pauschal 0,1.
|
||
(WR/PF/Verhältnis/Worst je KW, Ausrichtungs-Split mit/gegen/ohne) gegen die
|
||
Backtest-Erwartung (WR~68 % · PF~1,3 · Verh~0,55) und flaggt Leckagen (Gegen-Signal-
|
||
Anteil, WR-Verfall, PF<1, Einzelverlust). **Wöchentlich** laufen lassen (B4/B5).
|
||
- **Vor „fertig":** immer `python -m py_compile <datei>`; JS via **`node --check`**
|
||
(Stufe B in `tools/check_nfalle.py`, läuft im pre-commit-Hook mit).
|
||
⚠⚠ **KORREKTUR 2026-08-07: „kein node im Env" stimmte nicht mehr** — Node
|
||
**v24.18.0** ist installiert. Die Zeile hat die Frage „können wir Node nutzen?"
|
||
jahrelang falsch beantwortet.
|
||
**Gemessen, warum das zählt:** die alte Klammern-Balance findet **1 von 5**
|
||
realistischen Fehlern — `const x = ;`, doppeltes `let`/`const` und `return`
|
||
ausserhalb einer Funktion laufen glatt durch; `node --check` findet **5 von 5**
|
||
in **86 ms**. Ein Syntaxfehler in `app.js` bricht den **GESAMTEN** Render
|
||
(Dashboard friert auf Altwerten ein) — bei **95 direkt dereferenzierten**
|
||
`$("id")`-Zugriffen ist das kein theoretisches Risiko.
|
||
⚠ **Geprüft wird als MODUL (`.mjs`), nicht als CommonJS:** als CJS hält Node ein
|
||
top-level `return` für legal, der Browser lädt `app.js` aber als klassisches
|
||
Script, wo es ein Syntaxfehler ist. Verifiziert, dass das echte `app.js` den
|
||
strengeren Modus **ohne Anpassung** besteht.
|
||
⚠ **Kein npm, keine `package.json`, kein `node_modules`** — `node --check` ist
|
||
die Laufzeit selbst. Damit entsteht NICHT die Divergenz „Prüfumgebung ≠
|
||
Laufumgebung" (die beim `.ps1`-BOM und beim `pre-commit`-Framework das Problem
|
||
war): Node und Chrome benutzen **denselben Parser (V8)**.
|
||
✅ **Drei Richtungen verifiziert:** sauber → grün · mit eingebautem Fehler →
|
||
**Exit 1**, Commit gestoppt · **ohne Node im PATH** → Rückfall auf die alte
|
||
Prüfung, Exit 0 (eine frische Maschine darf am Hook nicht hängenbleiben).
|
||
- **✅ BOOT-LÜCKE GESCHLOSSEN: Auto-Logon AKTIV (2026-08-07).**
|
||
`OilTradingServer` und `HyperliquidDashboard` hängen an einem **Logon**-Trigger
|
||
— nach einem Update-Reboot läuft nichts bis zur Anmeldung (real 20.07.:
|
||
Reboot 01:30, Server bis 07:42 tot, −95,48 €). ⚠ Ein Task „ohne angemeldete
|
||
Sitzung" hilft **nicht**: der Bot braucht das laufende **MT5-Terminal**, und das
|
||
ist eine GUI-Anwendung — es braucht eine echte Desktop-Sitzung. Auto-Logon ist
|
||
hier also tatsächlich das richtige Mittel, nicht nur das bequeme.
|
||
**Vorbereitet:** `tools/autologon_einrichten.ps1` + Sysinternals `Autologon`
|
||
(Microsoft-signiert, Signatur geprüft; `tools/autologon/` ist **gitignored** wie
|
||
`tools/piper/`).
|
||
⚠⚠ **Bewusst NICHT der Registry-Weg.** Der verbreitete Weg legt das Passwort als
|
||
`DefaultPassword` im **Klartext** unter `HKLM\…\Winlogon` ab — lesbar für jeden
|
||
lokalen Admin und in jedem Backup-Image. Sysinternals `Autologon` speichert es
|
||
als **verschlüsseltes LSA-Secret**. Das Skript prüft danach nach, dass
|
||
`DefaultPassword` NICHT im Klartext steht.
|
||
⚠⚠ **Das eigentliche Restrisiko ist nicht das Passwort, sondern die offene
|
||
Sitzung:** nach dem Reboot steht ein angemeldeter Desktop mit MT5 und
|
||
Live-Broker-Verbindung da. Deshalb legt das Skript zusätzlich die Aufgabe
|
||
`OilAutoLogonLock` an — 30 s nach der Anmeldung `LockWorkStation`. Die Sitzung
|
||
**läuft** (MT5 und beide Server arbeiten), der Bildschirm ist gesperrt.
|
||
⚠ **Muss ELEVATED ausgeführt werden** — `HKLM` ist aus einer normalen Sitzung
|
||
nicht schreibbar (getestet: „Requested registry access is not allowed"). Das
|
||
Passwort steht **nicht** im Skript und wird nirgends gespeichert; es wird im
|
||
Autologon-Dialog eingegeben. **Zurück:** `Autologon /accepteula` → Disable, und
|
||
`schtasks /Delete /TN OilAutoLogonLock /F`.
|
||
⚠⚠ **ZWEI FEHLER IM ERSTEN VERSUCH (07.08., beide behoben) — beide von der
|
||
Sorte, die Erfolg meldet, wo keiner war:**
|
||
**(1)** `$ErrorActionPreference = 'Stop'` PLUS `schtasks /Delete` auf eine noch
|
||
nicht existierende Aufgabe → native Programme schreiben nach stderr, PowerShell
|
||
macht daraus unter `Stop` einen **Abbruch**. Das Skript endete, BEVOR `/Create`
|
||
lief; die Sperr-Aufgabe existierte nie. Jetzt wird vorher per `/Query` geprüft.
|
||
**(2)** Die Erfolgsmeldung **log**: ein fehlendes `DefaultPassword` wurde als
|
||
„LSA-Secret ✅" ausgegeben — dabei war schlicht **gar nichts** gesetzt (der
|
||
Autologon-Dialog war ohne *Enable* geschlossen worden). Jetzt ist
|
||
**`AutoAdminLogon = 1` die Bedingung**, und erst danach wird über die
|
||
Speicherart geurteilt; ohne aktives Auto-Logon bricht das Skript mit Code 1 ab
|
||
und überspringt Schritt 2.
|
||
⚠⚠ **Der GUI-Weg führte zweimal zu nichts.** Diagnostiziert: der Dialog
|
||
**öffnet sich** (Fenstertitel „Autologon - Sysinternals", Prozess verifiziert)
|
||
— er muss aber mit **ENABLE** bestätigt werden; nur schließen tut nichts.
|
||
Deshalb gibt es jetzt **`-Direkt`**: Autologon 3.10 nimmt Benutzer/Domäne/
|
||
Passwort als Argumente, das Skript fragt es per `Read-Host -AsSecureString` ab
|
||
(unsichtbar, nicht in der History) und ruft es ohne Dialog auf.
|
||
**➜ `powershell -ExecutionPolicy Bypass -File toolsutologon_einrichten.ps1 -Direkt`**
|
||
⚠ Abwägung: bei `-Direkt` steht das Passwort für den Moment des Aufrufs in der
|
||
Kommandozeile des Kindprozesses (für Admins kurz sichtbar). Es landet **nicht**
|
||
in einer Datei, nicht in der Shell-History — und anschließend als
|
||
verschlüsseltes LSA-Secret, nicht im Klartext in der Registry.
|
||
⚠ **Und ein Design-Fehler: die Sperre hätte bei JEDER Anmeldung gefeuert.** Ein
|
||
`ONLOGON`-Trigger unterscheidet nicht. Deshalb prüft `tools/autologon_lock.ps1`
|
||
jetzt die **Systemlaufzeit** — gesperrt wird nur, wenn der Rechner **< 3 min**
|
||
läuft (= Auto-Logon nach Boot). Nach manueller Anmeldung passiert nichts.
|
||
⚠⚠ **ENDSTAND — der SICHERE Weg hat NICHT funktioniert.** Sysinternals
|
||
`Autologon` beendete sich mit **ExitCode 0**, schrieb aber **nichts** (weder
|
||
über den Dialog noch über die Argument-Form). Ausgeschlossen wurden: der
|
||
Windows-11-Blocker `DevicePasswordLessBuildVersion` (nicht gesetzt), fehlende
|
||
Elevation (Admin-Prüfung bestand), ein hängender Prozess. **Ursache ungeklärt.**
|
||
Aktiv ist deshalb der **Registry-Weg** — verifiziert:
|
||
| | |
|
||
|---|---|
|
||
| `AutoAdminLogon` | **1** |
|
||
| `DefaultUserName` / `DefaultDomainName` | `ah` / `HOMEBASE` |
|
||
| `DefaultPassword` | ⚠ **KLARTEXT** (Länge 4) |
|
||
| Aufgabe `OilAutoLogonLock` | **Bereit**, läuft als `ah`, ruft `autologon_lock.ps1` |
|
||
⚠ Das Passwort ist damit für **jeden lokalen Administrator und jedes
|
||
Backup-Image** lesbar. Entfernen (deaktiviert zugleich den Auto-Logon):
|
||
`Remove-ItemProperty -Path 'HKLM:\…\Winlogon' -Name DefaultPassword`.
|
||
⚠ **Offen:** die Wirkung ist erst beim **nächsten Reboot** belegt — bis dahin
|
||
ist nur die Konfiguration verifiziert, nicht das Verhalten.
|
||
⚠⚠ **DRITTER Fall derselben Fehlerklasse in diesem einen Skript** (Erfolg
|
||
melden, wo keiner ist): die Speicherart wurde aus einer **stale** Variable
|
||
gelesen, die VOR dem Registry-Schreiben gefüllt war — die Ausgabe widersprach
|
||
sich direkt („steht jetzt im KLARTEXT" … zwei Zeilen später „nicht im Klartext
|
||
✅"). Behoben: der Zustand wird nach jedem Schreibvorgang **frisch gelesen**.
|
||
Die drei Fälle zusammen sind die Lehre: **eine Erfolgsmeldung muss den Zustand
|
||
NACH der Aktion prüfen, nicht den davor.**
|
||
- **⚠⚠ .ps1 MIT NICHT-ASCII BRAUCHT EIN BOM — sonst bricht `powershell.exe` 5.1
|
||
(2026-08-07, real passiert).** Das Autologon-Skript ließ sich nicht ausführen:
|
||
„Schließende ) fehlt", „Zeichenfolge hat kein Abschlusszeichen". Es war **kein**
|
||
Syntaxfehler: **Windows PowerShell 5.1 liest `.ps1` als CP1252, wenn kein BOM da
|
||
ist.** Aus dem Gedankenstrich `—` werden drei Zeichen (`â€"`), das enthaltene
|
||
`"` schließt den String vorzeitig, und es kaskadiert bis zum Dateiende.
|
||
⚠⚠ **Warum es beim Schreiben nicht auffiel — und das ist der eigentliche Punkt:**
|
||
das hiesige PowerShell-Werkzeug ist **pwsh 7**, und das liest UTF-8 **ohne** BOM
|
||
klaglos. Die Datei war also in meiner Umgebung fehlerfrei und beim Nutzer kaputt.
|
||
Dieselbe Divergenz-Klasse wie der Deployment-Drift, nur eine Ebene tiefer: **die
|
||
Prüfumgebung war nicht die Laufumgebung.**
|
||
✅ Behoben (UTF-8 **mit** BOM) und mit dem **echten 5.1-Parser** gegengeprüft
|
||
(`Parser::ParseFile`, 0 Fehler). Dabei ein **zweiter** Fall gefunden:
|
||
`scripts/send_daily_report.ps1` (Umlaute, kein BOM).
|
||
✅ **Dauerhaft abgesichert:** `tools/check_nfalle.py` prüft es als Stufe **C2**.
|
||
⚠ `.bat` ist NICHT betroffen (`cmd.exe` verzeiht das in `echo`/Kommentaren —
|
||
`restart_server.bat` enthält zwar `─`/`⚠`, bricht aber nicht).
|
||
- **✅ STUFE 5 DER PIPELINE IST JETZT EIN SKRIPT (`tools/deploy.py`, 2026-08-07).**
|
||
Ersetzt das von Hand zusammengesetzte „killen, starten, warten, nachsehen" und
|
||
macht die dokumentierte Schwachstelle prüfbar.
|
||
```
|
||
python tools/deploy.py --feld <snapshot_feld> # Neustart + Beweis
|
||
python tools/deploy.py --nur-pruefen # nichts anfassen, nur Zustand
|
||
python tools/deploy.py --hl # HL-Dashboard (8001) mitnehmen
|
||
```
|
||
Fünf Schritte, **Rückgabecode 0 nur wenn alle durchlaufen** (verkettbar):
|
||
**(1)** Prozess **gezielt am Port** beenden — ⚠ NICHT per `*server.py*`-Muster
|
||
wie `restart_server.bat`, das trifft auch das HL-Dashboard auf 8001 (real: es
|
||
wurde bei jedem MT5-Neustart still mit-erschlagen und riss die Lücken in die
|
||
Datensammlung). **(2)** starten und warten, bis `/api/snapshot` **wirklich**
|
||
antwortet. **(3)** **genau EINE** Instanz je Port. **(4)** `--feld` prüfen.
|
||
**(5)** Log **ab der Startposition** auf ERROR/Traceback.
|
||
⚠⚠ **Schritt 4 ist der Kern.** `restart_server.bat` hat zweimal still nicht neu
|
||
gestartet, und weil statische Dateien je Request frisch von der Platte gelesen
|
||
werden, **belegt eine korrekt ausgelieferte `app.js?v=N` GAR NICHTS** über den
|
||
geladenen Python-Code. Ein neues Snapshot-Feld ist der einzige belastbare
|
||
Beweis — genau daran fiel der Fehler 2026-08-02 auf (`hl_live.basis_stale`).
|
||
Ohne `--feld` sagt das Skript das ausdrücklich dazu.
|
||
✅ **Beide Richtungen geprüft**: mit `--feld rec_track` → „Python-Code ist neu",
|
||
Exit 0; mit einem erfundenen Feld → „FEHLT — der alte Code läuft weiter!",
|
||
**Exit 1**. Eine Prüfung, die nicht scheitern kann, wäre wertlos.
|
||
- **⚠⚠ DER ENGPASS DES PROJEKTS WAR NIE DIE MATHEMATIK — er war eine
|
||
quadratische Schleife (Profil 2026-08-07, ~9 min → 8,9 s).** Anlass war die
|
||
Frage nach numpy. Statt zu vermuten wurde profiliert, und das Ergebnis war ein
|
||
anderes als erwartet: von 10,07 s Gesamtlaufzeit steckten **8,17 s in der
|
||
EIGENZEIT der Backtest-Schleife**, nur 0,44 s in `_build`. Ursache:
|
||
```python
|
||
[C[j] for j in PH if i - _PIV_LOOK <= j <= i - _PIV_K] # je BAR, ganze Liste
|
||
```
|
||
Das ist **O(Bars × Pivots)** — bei 80k Bars hunderte Millionen Vergleiche, und
|
||
genau daher kamen die 9–20-Minuten-Läufe. PH/PL sind **sortiert**, also genügen
|
||
zwei Binärsuchen (`bisect_left`/`bisect_right`) für **dieselbe Menge** in
|
||
O(log n).
|
||
**Gemessen: `backtest_signal_touchfill.py` über 80k Bars von ~9 min auf 8,9 s**
|
||
(~60×), bei 8k Bars 10,07 s → 1,43 s.
|
||
⚠⚠ **Die Migrations-Regel verlangt Bit-Gleichheit — und ein Vorher/Nachher-
|
||
Vergleich zweier LÄUFE taugt dafür NICHT**: `copy_rates_from_pos` ankert am
|
||
neuesten Bar, zwischenzeitlich eintreffende Live-Bars verschieben das Fenster
|
||
(real ein Trade Unterschied, s. 06.08.). Die Gleichheit wird deshalb **direkt
|
||
auf der Datenstruktur** bewiesen — `tests/test_pivotfenster.py` (4 Tests:
|
||
4.000 Bars × 400 Pivots, Randfälle, leere Liste, beidseitig inklusive Grenzen).
|
||
✅ Angewandt auf **7 Skripte** (`auto_signal_v3`, `breakout_gated`,
|
||
`dist_gated`, `htf_vola`, `metalabel`, `sl_width`, `trail_be`) — mechanisch
|
||
identische Transformation, alle kompilieren, Stichprobe gegengelaufen.
|
||
⚠ **Lehre:** die naheliegende Antwort („numpy") war die falsche. Ein Profil
|
||
kostet zwei Minuten und hätte diese Bremse jederzeit gezeigt — sie lag seit
|
||
Monaten in jedem gegateten Backtest.
|
||
- **⚠⚠ PIPELINE-STUFE 1 IST JETZT AUTOMATISCH — `pre-commit`-Hook (2026-08-07).**
|
||
Nach einer Web-Recherche zum 2026er-Python-Standard umgesetzt, aber **gefiltert**:
|
||
von uv/Ruff/pyproject/CI ist hier nur das sinnvoll, was zu einer Einzelmaschine
|
||
mit GUI-gebundenem MT5 und Live-Geld passt.
|
||
**(1) Abhängigkeiten festgehalten** — `requirements.txt` (Laufzeit) und
|
||
`requirements-dev.txt` (Werkzeuge/Messung), **Versionen gepinnt**. ⚠ Bis dahin
|
||
gab es **gar keine** Liste; nach einem Maschinenausfall wäre die Rekonstruktion
|
||
Raten gewesen. Ermittelt per AST über `core/`, `server.py`, `tools/` — nicht
|
||
geraten.
|
||
**(2) `tools/pre_commit.py`** fährt vor jedem Commit `check_nfalle` + `pytest`
|
||
(~5 s). Einrichten: `python tools/pre_commit.py --install`. Notausgang
|
||
`git commit --no-verify` ist Absicht — eine Prüfung, die einen dringenden Fix
|
||
blockiert, wird sonst deinstalliert.
|
||
⚠ **Bewusst OHNE das `pre-commit`-Framework**: dessen Nutzen ist Werkzeug-
|
||
Versionierung über mehrere Maschinen. Hier gibt es eine. Es würde stattdessen
|
||
eine ZWEITE ruff-Version in einer isolierten Umgebung installieren — genau die
|
||
Divergenz „Prüfumgebung ≠ Laufumgebung", die heute schon einmal zugeschlagen hat.
|
||
**(3) Ruff statt pyflakes** (`ruff check --select F821`) — gleiche Befunde,
|
||
ganzer Scan in 2,1 s. ⚠ **Kein `ruff format`**: das würde 161 Dateien
|
||
umformatieren, darunter alle Backtests → Migrations-Regel verletzt.
|
||
⚠⚠ **VIERTER Fall der Fehlerklasse „Erfolg melden, wo nichts geprüft wurde" —
|
||
und diesmal fiel er nur durch einen Test auf.** `--select F821,E999` lässt ruff
|
||
0.16 **komplett abbrechen** („Rule E999 was removed", Exit 2); die Meldung geht
|
||
nach stderr, und weil ich den **Rückgabecode nicht prüfte**, meldete die Stufe
|
||
„keine undefinierten Namen" — obwohl sie **gar nichts** geprüft hatte. Ein
|
||
absichtlich eingebauter `F821`-Fehler kam **glatt durch den Hook**. Behoben:
|
||
nur `F821`, und **Exit 2 = Werkzeugfehler** löst jetzt den pyflakes-Rückfall aus
|
||
statt als „sauber" zu gelten.
|
||
✅ **Beide Richtungen verifiziert** (wie beim Deploy-Skript): mit eingebautem
|
||
Fehler → Hook **stoppt** den Commit; ohne → grün. Der Test-Commit wurde
|
||
zurückgenommen (war nur lokal).
|
||
⚠ **Nicht umgesetzt und warum:** *Gitea Actions* — der Runner liefe auf
|
||
derselben Maschine wie der Live-Bot und konkurrierte um CPU, bei 5 s Prüfzeit
|
||
ohne Mehrwert gegenüber dem Hook. *Container/Blue-Green* — unmöglich, MT5
|
||
braucht eine Desktop-Sitzung, und es gibt EINE Broker-Verbindung.
|
||
*mypy/ty* — kaum Annotationen vorhanden, grosser Aufwand für wenig Ertrag.
|
||
⚠ **Struktureller Fund dabei:** `core/engine.py:3527` importiert
|
||
**`backtest_breakout_squeeze`** (ebenso `weekly_review.py`). Ein Backtest ist
|
||
damit Teil des **Live**-Abhängigkeitsgraphen — „Backtests sind nur Doku" stimmt
|
||
nicht, ein Fehler dort kann den laufenden Bot treffen.
|
||
- **⚠⚠ TEST-SUITE (`pytest`, `tests/`, seit 2026-08-07) — 17 Tests, ~1 s.**
|
||
Im Projekt sind über Monate **dutzende Szenario-Tests** entstanden („mit 10
|
||
Szenarien getestet", „mit 18 synthetischen Szenarien"), alle als
|
||
Wegwerf-Skripte. **Keiner davon lief je ein zweites Mal.** Bei einem System, das
|
||
man vor jeder Änderung neu starten und live verifizieren muss, ist das der
|
||
teuerste Teil der Schleife.
|
||
Aufruf: **`python -m pytest`** (Konfiguration in `pytest.ini`).
|
||
Erfasst sind zunächst die beiden jüngsten Pfade: **`_check_pending_fill`**
|
||
(6 Fälle, u. a. „fremde Position NICHT taggen" und „derselbe Fill zählt nur
|
||
einmal") und die **Selbst-Kalibrierung** (7 Fälle: Episoden-Dedup, WARTEN
|
||
re-armt, Auswertung in beide Richtungen inkl. Broker-Offset, fehlende Bar bleibt
|
||
offen, Aggregation).
|
||
⚠ **Drei harte Regeln in `tests/conftest.py`**, damit die Suite jederzeit
|
||
gefahrlos läuft: **keine Live-DB** (jeder Test bekommt eine frische Temp-Datei),
|
||
**kein MT5, kein laufender Server** (alles Broker-Nahe gestubbt), **kein Netz**.
|
||
⚠ Das Schema wird über die **echten** Konstruktoren angelegt — `HistoryLogger`
|
||
UND `CandleLogger` (`candles_m1` gehört nicht zum History-Schema). Ein von Hand
|
||
nachgebautes Test-Schema würde irgendwann vom Produktivstand abweichen.
|
||
⚠ **Warum die Temp-DB und nicht eine Kopie der Live-DB:** am 06.08. haben ECHTE
|
||
`candles_m1`-Zeilen in einem vermeintlich leeren Fenster einen Test verfälscht —
|
||
zweimal hintereinander sah es nach einem Code-Fehler aus, es waren Testfehler.
|
||
✅ **Mutations-geprüft** (eine Suite, die immer grün ist, ist wertlos): eine
|
||
absichtlich eingebaute „fremde Position wird doch getaggt"-Regression wird
|
||
gefangen. ⚠ Nebenbefund dabei: der Fill-Dedup ist **doppelt** gesichert
|
||
(`_pending_tagged` UND der `pop` aus `_pending_tickets`) — nur eines von beiden
|
||
zu entfernen fällt nicht auf, beides zusammen bricht sofort zwei Tests. Das ist
|
||
gewollte Redundanz, kein ungetesteter Zweig.
|
||
- **⚠⚠ KONFIDENZINTERVALLE statt Bauchgefühl (`backtest_cost_gate.ci95`, seit
|
||
2026-08-07).** Das Projekt entschied bis dahin über **Punktschätzungen** plus die
|
||
2-Stichproben-Regel; ob ein ØR von +0,12 bei n=30 überhaupt von null zu
|
||
unterscheiden war, musste jedes Mal im Kopf abgeschätzt werden. `kennzahlen()`
|
||
liefert jetzt zusätzlich **`ci`** = 95-%-Bootstrap-Intervall des Mittelwerts
|
||
(Perzentil, 2000 Resamples, `random_state` fest → reproduzierbar; ab n≥20, sonst
|
||
`None`). ⚠ **Rein additiv** — alle 11 Aufrufer lesen weiter nur `n/wr/oer/sr/pf`,
|
||
keine dokumentierte Zahl verschiebt sich.
|
||
✅ **Sofortiger Ertrag:** der erste Lauf hat das Ergebnis vom 06.08. korrigiert
|
||
(s. „Verworfene Setups auf der Ausführungs-Achse" — aus „3 von 7 tragen" wird
|
||
„1 gesichert, 2 unklar").
|
||
⚠⚠ **GRENZE, die man kennen muss:** der Bootstrap unterstellt **unabhängige**
|
||
Ziehungen. Für die sequentielle Sim mit EINEM Slot stimmt das näherungsweise
|
||
(jeder Trade beginnt nach dem Exit des vorigen). Für **überlappende**
|
||
Beobachtungen ist er **zu optimistisch** — stündliche Werte mit 24-h-Horizont
|
||
teilen 23 von 24 Stunden, das effektive n ist ~n/24. Genau daran ist
|
||
`analyze_hl_funding.py` am 06.08. aufgelaufen (dort wird **entüberlappt** statt
|
||
gebootstrappt). Für solche Fälle bräuchte es einen **Block-Bootstrap** —
|
||
`ci95` ist dafür NICHT gedacht.
|
||
- **⚠⚠ DIE `\n`-FALLE — und der Scan dagegen (`tools/check_nfalle.py`, 2026-08-07).**
|
||
Wird Code über ein Shell-**Heredoc** geschrieben, macht die Shell aus einem
|
||
gemeinten `\n` einen **echten Zeilenumbruch mitten im String-Literal**. Real an
|
||
EINEM Tag **dreimal** passiert: Telegram-Text in `engine.py`, Reminder-Eintrag in
|
||
`measurement_reminder.py`, und beim Dokumentieren dieser Regel selbst (dort
|
||
zusätzlich an den deutschen Anführungszeichen).
|
||
**➜ Regel: mehrzeilige Strings mit `\n` oder Text mit „ " NIE per Heredoc
|
||
schreiben — den Edit-Weg nehmen.**
|
||
Der Scan prüft in BEIDE Richtungen: **(A)** kompiliert alle `.py` · **(B)** sucht
|
||
in JS unabgeschlossene String-Literale · **(C)** den UMGEKEHRTEN Fall, literales
|
||
`\n` als sichtbarer Text in HTML-`title=` und Markdown · **(D)** die Signatur der
|
||
Fälle (Zeile endet offen, nächste besteht nur aus einem Quote).
|
||
⚠ **Fehlalarm-Falle im Scanner selbst, behoben:** Quotes zu ZÄHLEN meldet `'"'`
|
||
als offenes Literal (gültiges JS mit einem Anführungszeichen darin — real in
|
||
`app.js`). Der Scan verfolgt jetzt zeichenweise den AKTIVEN Quote-Typ.
|
||
Erster vollständiger Lauf: **161 Python-Dateien, `app.js`, alle HTML/MD sauber.**
|
||
- **⚠⚠ LINTER: `pyflakes` (seit 2026-08-07 installiert, Stufe A2 in
|
||
`tools/check_nfalle.py`) — und er hat beim ERSTEN Lauf zwei echte Fehler
|
||
gefunden.** `py_compile` prüft **nur die Syntax**; ein Tippfehler in einem
|
||
**Namen** ist syntaktisch einwandfrei. Dieses Projekt ist dafür besonders
|
||
anfällig, weil es sehr viele breite `except Exception`-Blöcke hat (bewusst, als
|
||
Fail-safe) — ein `NameError` darin ist **unsichtbar**: keine Meldung, kein
|
||
Logeintrag, nur ein Zweig, der nichts tut. Genau das war am 04.08. schon einmal
|
||
fast passiert (`wave_snap`), gefunden damals nur durch Lesen.
|
||
**Die beiden Funde in `core/trader.py`** — beide nutzten `log.`, den Namen gibt
|
||
es dort NICHT (nur `log_trade`/`log_hist`):
|
||
· **`_broker_offset_s()`** (seit dem Fix vom 01.08. drin): der `NameError` lief in
|
||
das `except Exception: pass` **und nahm die darunter stehende Zuweisung
|
||
`self._boff = int(off)` mit** → ein echter **Broker-Zeitzonen-Wechsel wäre nie
|
||
übernommen worden**, dauerhaft und lautlos. Ausgerechnet der Zweig, der als
|
||
Deployment-Drift **Fall 4** gebaut wurde (falscher Offset → `open_time` in der
|
||
Zukunft, **Time-Stop-Alter negativ**).
|
||
· **`modify_sltp()`** (seit dem **Initial-Commit**): steht **nicht** in einem
|
||
`try` → der Fehler lief bis in `/api/sltp` durch. Folge: der Broker hatte SL/TP
|
||
**bereits geändert**, der User bekam trotzdem eine Fehlermeldung, und
|
||
`set_sltp` kam nie bis `trail.deactivate()` — **das Trailing blieb an** und hätte
|
||
die Handeingabe beim nächsten Tick zurückgezogen, also genau das, was die
|
||
User-Vorgabe verhindern soll. Aufgefangen hat das nur die separate
|
||
Extern-Erkennung in `trailing._do_modify`, einen Tick später.
|
||
✅ Beide behoben (`log_trade`), dazu die Annotation `'HistoryLogger | None'` über
|
||
`if TYPE_CHECKING` sauber importiert. **Projektweit jetzt 0 undefinierte Namen.**
|
||
⚠ **Schweregrad-Trennung ist Absicht:** gemeldet werden nur `undefined name` /
|
||
`syntax error`; die ~105 kosmetischen Hinweise (ungenutzte Importe, f-Strings
|
||
ohne Platzhalter) werden unterdrückt. Auch `redefinition of unused` steht bewusst
|
||
NICHT in der Liste — es trifft das legitime Muster `hook = None` + bedingtes
|
||
`def hook` (geprüft in zwei Backtests, beide korrekt). **Eine Prüfung mit
|
||
Dauer-Treffern wird ignoriert, und mit ihr die eine echte Meldung.**
|
||
⚠ Backtest-Dateien wurden **nicht** angefasst — eine Änderung dort könnte ihre
|
||
dokumentierten Zahlen verschieben.
|
||
- **Konsole:** UTF-8 erzwingen (`PYTHONIOENCODING=utf-8`, `-X utf8`).
|
||
- **Uvicorn-Access-Log AUS** (`access_log=False` in `uvicorn.run`, 2026-07-22): seit
|
||
dem 4-s-REST-Polling + WS-Push flutete `GET /api/snapshot … 200 OK` die Konsole.
|
||
Betrifft NUR Uvicorns eigenes Zugriffs-Log, nicht die `oil.*`-Logger (eigene
|
||
Hierarchie, `core/logger.py`, unverändert in Konsole + Datei).
|
||
|
||
## Empfehlungs- & Trade-Logik (Stand aktuell)
|
||
- **Richtung = EMA12 vs EMA50** (`wave_rec.py`), NICHT mehr ZigZag-Momentum
|
||
(das hatte ~0 Edge). Totband (Chop→WARTEN), Anti-Überdehnung
|
||
(`_STRETCH_MAX=3,5×ATR`), +S/R-Konfidenz, +Session-Konfidenz. (TU **entfernt** —
|
||
s. u.)
|
||
- **✅ ABER SIE TAUGT ALS VETO — und DAS hält der 2-Stichproben-Regel stand
|
||
(2026-08-06, Ausrichtungs-Split über die GANZE Historie, 1.216 geschlossene
|
||
Trades).** Bisher stand dazu nur `alignment_stats(40)` = die letzten 40 Trades
|
||
im Order-Dialog. Über alles, je Lot normiert (Positionsgrößen schwanken stark,
|
||
s. die Lehre vom 04.08.):
|
||
| | n | WR | ΣP&L | **je Lot** | H1 / H2 je Lot |
|
||
|---|---|---|---|---|---|
|
||
| **MIT** Signal | 354 | 56,5 % | +555,66 € | **+3,22** | +0,37 / +5,62 |
|
||
| **GEGEN** Signal | 87 | **40,2 %** | −184,08 € | **−6,60** | **−7,01 / −5,68** |
|
||
| OHNE Signal (WARTEN) | 775 | 56,6 % | +186,17 € | +0,36 | −2,68 / +0,76 |
|
||
⚠⚠ **Die schärfste Zeile ist der Vergleich MIT gegen OHNE: 56,5 % zu 56,6 % —
|
||
identisch.** Der Empfehlung zu FOLGEN bringt gegenüber „ohne Empfehlung handeln"
|
||
gemessen **nichts**. Nur sie zu VERLETZEN kostet, und das **robust in beiden
|
||
Hälften** (−7,01 / −5,68 je Lot; die einzige Richtungsaussage über die
|
||
Gesamtempfehlung, die den 2-Stichproben-Test besteht).
|
||
✅ **Damit ist die Rolle präzise: die Gesamtempfehlung ist ein VETO, keine
|
||
Prognose.** Sie grenzt ein, was man nicht tun sollte; die Auswahl innerhalb des
|
||
Erlaubten leistet der Mensch (dessen Vorteil separat belegt ist).
|
||
⚠ Grenzen: `gegen` hat nur n=87 (73/14), und die Zuordnung ist konfundiert —
|
||
der Mensch wählt ja aus, wann er gegen das Signal geht. Die Richtung des Effekts
|
||
ist über beide Hälften konsistent und groß (~10 € je Lot Unterschied), die
|
||
Punktschätzung ist es nicht.
|
||
- **⚠⚠ DIE RICHTUNGS-AUSSAGE DER LIVE-EMPFEHLUNG IST EIN MÜNZWURF — an den LIVE
|
||
ausgegebenen Zeilen gemessen (`analyze_signal_live.py`, 2026-08-06, User-Frage
|
||
„die Empfehlungen heute waren fast immer gegenläufig, sollten wir das Konzept
|
||
überdenken?").** Kein Backtest: ausgewertet wurden die **tatsächlich
|
||
ausgegebenen** `recommendations`-Zeilen (also nach ALLEN Gates) gegen den echten
|
||
Kursverlauf. 20.365 Zeilen → **1.071 Episoden** (08.05.–06.08.; die Rohzeilen
|
||
zählen dieselbe Marktlage dutzendfach, deshalb Episoden), gegen die **Drift**
|
||
der jeweiligen Hälfte gelesen.
|
||
| Horizont | H1 Überschuss / Treffer | H2 Überschuss / Treffer |
|
||
|---|---|---|
|
||
| 30 min | +0,079×ATR / 49,7 % | +0,003×ATR / 51,0 % |
|
||
| 60 min | +0,085×ATR / 50,7 % | +0,034×ATR / 51,1 % |
|
||
| 120 min | +0,002×ATR / 50,5 % | **−0,259×ATR** / 48,3 % |
|
||
**Die Trefferquote liegt über alle sechs Felder zwischen 48,3 und 51,1 %.** Der
|
||
Überschuss über die Drift ist im besten Fall +0,085×ATR — das liegt **unter der
|
||
B3-Kostenhürde (0,1×ATR)** und weit unter den realen M5-Kosten (0,265×ATR). Als
|
||
**Richtungsprognose bezahlt das Signal nicht einmal seinen eigenen Spread.**
|
||
✅ **Der Tag, um den es ging, war kein Ausreißer, sondern typisch:** 11 gerichtete
|
||
Episoden, auf 30 min **8 von 10 gegenläufig**. Die fünf Vormittags-SHORTs liefen
|
||
gegen einen Tag, der von 75,5 auf 78 stieg (11:54 SHORT: −5,09×ATR auf 120 min).
|
||
Das ist die nachlaufende EMA im Trendtag — sie shortet die Rücksetzer.
|
||
⚠⚠ **DAS HEISST NICHT, DASS DAS SYSTEM WERTLOS IST — es verdient nur nicht mit
|
||
der Richtung.** Dasselbe Signal ist mit **ruhender Order am Bestätigungs-Level**
|
||
in BEIDEN Hälften klar positiv (`backtest_auto_signal_v3.py`: ØR +0,35/+0,32,
|
||
PF 2,2), am Markt dagegen negativ (−0,09/−0,11). Der Ertrag kommt aus
|
||
**Einstiegspreis und Exit-Asymmetrie**, nicht aus der Trefferquote — genau wie
|
||
beim Squeeze (43–46 % Treffer, trotzdem positiv, weil die Gewinner laufen).
|
||
✅ **Konsequenz für die Lesart:** die Headline ist **kein Kursurteil**, sondern
|
||
ein **Auslöser, an einem Level eine Order zu platzieren**. Der Konfidenz-Ring
|
||
darf erst recht nicht als P(Erfolg) gelesen werden (unabhängig belegt:
|
||
`analyze_verdict_calibration.py`, in H2 sogar invertiert).
|
||
⚠ **Was daraus NICHT folgt:** die Suche nach einem besseren Richtungssignal.
|
||
Die ist mit **23 verworfenen Eingriffen** über jede orthogonale Indikatorklasse
|
||
hinweg beantwortet (Momentum · Inter-Market · Volume Profile · SMC/Liquidity ·
|
||
Chartmuster · Fibonacci · Kerzen · Marktstruktur · Meta-Labeling).
|
||
- **Volatilitäts-Squeeze-Breakout = ADDITIVER SETUP, UMGESETZT (User-Idee
|
||
„Breakouterkennung" 2026-07-14, gemessen `backtest_breakout_squeeze.py`):**
|
||
struktureller Ausbruch aus einer **Kompression** (Box = Spanne der letzten `_SQ_N`=10
|
||
abgeschlossenen M5-Bars ≤ `_SQ_MULT`=2,5×ATR) um `_SQ_K`=0,1×ATR — NICHT der
|
||
EMA-Trend-Breakout. **2-Stichproben-positiv** (ØR **+0,227/+0,141** netto Echtkosten,
|
||
PF 1,35/1,20, 173/413 Trades; schlägt den Ausbruch aus *beliebiger* Box −0,045/+0,161
|
||
in BEIDEN Hälften) & **robust über Box 10–20 / k 0,1–0,2** (alle 8 Kombis positiv,
|
||
kein Parameter-Zufall). Andere Signalklasse als die Welle → komplementär. **Umsetzung:**
|
||
`wave._squeeze_one(highs,lows,closes,atr)` (M5). ⚡ **Erkennungs-Latenz ~5 s (Variante A,
|
||
2026-07-21):** der Squeeze läuft auf einem EIGENEN, schnellen 60-Bar-M5-Fetch
|
||
(`_sq_due`/`_SQ_REFRESH_S=5.0`, entkoppelt von der 15-s-TF-Ampel-Drossel) → Ausbruch wird
|
||
~15 s→5 s schneller erkannt, dann ~1 s Handeln (`_pos_loop`). Gleiche validierte M5-Regel,
|
||
reine Infra (kein Backtest nötig). **Variante B (Squeeze auf M1) = OFFEN/UNBELEGT** — erst
|
||
backtesten, wenn der M1-Logger (`candles_m1`, seit 2026-07-21) ab ~2027-01 genug History hat.
|
||
- **⚠⚠⚠ VERWORFENE SETUPS AUF DER AUSFÜHRUNGS-ACHSE NEU GEMESSEN — und das
|
||
Ergebnis stellt den LIVE-Squeeze mit in Frage (`backtest_verworfene_v2.py`,
|
||
2026-08-06, User: „backteste alle verworfenen Signale").**
|
||
⚠ Bewusst **kein** erneuter Komplettlauf — der lief am 04.08. (14 Klassen, alle
|
||
blieben verworfen). Neu ist genau **eine** Achse, der Einstiegspreis, und sie
|
||
wurde mit der heutigen Korrektur angewandt: **Fill bei BERÜHRUNG**, nicht bei
|
||
Schluss-Bruch. Geprüft wurde nur, wo die Achse überhaupt greifen kann —
|
||
**stehendes Level + Ausbruchsrichtung** (Momentum scheitert, weil sein Level
|
||
wandert; Reversions-Einstiege werden von einer ruhenden Order sogar schlechter,
|
||
`backtest_candle_fade2.py`).
|
||
| Setup | A) Markt | B) Close-Bruch | **C) Berührung (real)** | n |
|
||
|---|---|---|---|---|
|
||
| *KONTROLLE Squeeze* | −0,180/−0,017 | +0,560/+0,755 | **+0,133/+0,293** | 1094 |
|
||
| ORB EU 15/30 · US 15/30 | negativ | +0,45…+0,73 | **alle 4 ❌** | ~200 |
|
||
| PDH / PDL Bruch | −0,280/+0,044 | +0,906/+1,368 | **+0,246/+0,464** | 147 |
|
||
| Asian Range Bruch | +0,021/−0,116 | +0,928/+0,718 | **+0,142/+0,134** | 218 |
|
||
| Equal H/L Bruch | −0,279/+0,228 | +0,574/+0,905 | **+0,218/+0,170** | 168 |
|
||
✅ Die Kontrolle reproduziert `backtest_squeeze_touchfill.py` (+0,133/+0,293
|
||
gegen +0,124/+0,292) — Pipeline in Ordnung. ✅ **ORB bleibt verworfen** in allen
|
||
vier Varianten (bestätigt 2026-07-17). ✅ Die drei Überlebenden sind
|
||
**nachbar-robust** (PDH/PDL über die Gültigkeitsdauer, Asian Range über das
|
||
Fenster, Equal H/L über Toleranz und Gültigkeit — 11 Zellen, keine kippt).
|
||
⚠⚠ **NACHTRAG 2026-08-07 — mit 95-%-Konfidenzintervallen relativiert sich das
|
||
„3 von 7" deutlich.** Seit `backtest_cost_gate.ci95` (Bootstrap) hängt an jeder
|
||
ØR-Zahl ihr Intervall. Auf der entscheidenden Spalte C:
|
||
| Setup | H1 95-%-KI | H2 95-%-KI |
|
||
|---|---|---|
|
||
| **KONTROLLE Squeeze** | **[+0,037 … +0,229]** | **[+0,225 … +0,366]** |
|
||
| PDH / PDL | [−0,032 … +0,544] ⚠ enthält 0 | [+0,155 … +0,764] |
|
||
| Equal H/L | [+0,001 … +0,425] (haarscharf) | [−0,035 … +0,356] ⚠ enthält 0 |
|
||
| **Asian Range** | [−0,067 … +0,357] ⚠ | [−0,060 … +0,332] ⚠ |
|
||
**Nur der Squeeze hat in BEIDEN Hälften ein Intervall klar über null.** Bei
|
||
Asian Range enthalten **beide** die Null — als „Überlebender" war es zu stark
|
||
formuliert.
|
||
⚠ **Zwei verschiedene Fragen, beide nötig:** die 2-Stichproben-Regel prüft, ob
|
||
das **Vorzeichen über Regime hinweg stabil** ist; das KI prüft, ob eine einzelne
|
||
Schätzung **überhaupt von Rauschen zu unterscheiden** ist. Hier war Ersteres
|
||
erfüllt und Letzteres nicht. Und: zwei Hälften, die beide in dieselbe Richtung
|
||
zeigen, sind zusammen mehr Evidenz als jede für sich — „KI enthält 0" heißt
|
||
also **nicht widerlegt**, sondern **nicht belegt**.
|
||
✅ An der Schlussfolgerung ändert das nichts (es wurde ohnehin nichts gebaut),
|
||
aber die Formulierung „3 von 7 tragen" ist auf **„1 gesichert, 2 unklar"** zu
|
||
korrigieren.
|
||
⚠⚠ **Das sind KEINE drei neuen Signale.** PDH/PDL, Asian-Range-Extrem, Equal
|
||
Highs/Lows und die Squeeze-Box sind **dasselbe**: ein waagerechtes Level aus
|
||
früheren Kursextremen, gebrochen, mit ruhender Stop-Order betreten und mit dem
|
||
kanonischen Trailing verlassen. Deckt sich mit der Multi-Instrument-Kontrolle
|
||
(„beliebige Box" ist überall positiv, die Kompression trägt +0,009…+0,155).
|
||
**Der Edge liegt in der Kombination Ausführung + Exit, nicht in der Regel, die
|
||
das Level auswählt.**
|
||
⚠⚠⚠ **UND JETZT DER TEIL, DER AUCH DAS LAUFENDE SYSTEM BETRIFFT — mit der
|
||
gemessenen Exit-Slippage (`analyze_slippage_holdtime.py`) bleibt fast nichts
|
||
übrig:**
|
||
| Setup | 0 $ | **0,0124 $** (Tagfenster) | 0,0370 $ (alle Std) |
|
||
|---|---|---|---|
|
||
| **Squeeze (LIVE!)** | +0,133/+0,293 | **−0,002/+0,221** | −0,270/+0,080 |
|
||
| PDH / PDL | +0,246/+0,464 | **+0,099/+0,386** | −0,192/+0,231 |
|
||
| Equal H/L | +0,218/+0,170 | +0,077/+0,092 | −0,202/−0,062 |
|
||
| Asian Range | +0,142/+0,134 | −0,011/+0,065 | −0,316/−0,071 |
|
||
**Die Edges dieser ganzen Familie liegen in derselben Größenordnung wie die
|
||
Exit-Slippage.** Beim günstigsten gemessenen Wert hält nur **PDH/PDL** beide
|
||
Hälften klar; Equal H/L ist dünn, Asian Range fällt — und der **live laufende
|
||
Auto-Squeeze steht in H1 bei −0,002, also auf null**.
|
||
⚠ Die Slippage ist dabei eine **Untergrenze** (s. Herleitung dort) — der wahre
|
||
Abzug ist größer, nicht kleiner.
|
||
✅ **Konsequenz: KEIN neues Setup bauen.** Drei weitere Module derselben Klasse
|
||
addieren Frequenz zu einem Edge, der von den Ausstiegskosten aufgezehrt wird.
|
||
Der nächste Hebel ist **die Ausführungsqualität am EXIT** (dort ist der Posten,
|
||
der alles frisst), nicht ein weiteres Einstiegs-Signal.
|
||
⚠ **Nicht mitgemessen, ehrlich benannt:** (a) **Volume-Profile-LVN** — stehendes
|
||
Level, aber das Profil müsste je Bar neu gebinnt werden (Laufzeit); (b)
|
||
**Chartmuster-/Doppeltop-Nackenlinie** — stehendes Level und Ausbruchsrichtung,
|
||
qualifiziert sich also, braucht aber den Muster-Detektor kausal getrieben.
|
||
**Beide bleiben offen.** Alle übrigen Ablehnungen (Inter-Market, Chop-Gate,
|
||
Meta-Labeling, RSI/MACD/ADX, HTF-Winkel, H1-Stärke, Konfluenz-Strafe) haben gar
|
||
kein Level — die Achse kann dort nichts ändern.
|
||
- **⚠⚠ DER SQUEEZE TRÄGT AUCH AUF ANDEREN INSTRUMENTEN — 7 von 9 (gemessen
|
||
`backtest_squeeze_multi.py`, 2026-08-06).** Anlass: `analyze_signal_live.py` hat
|
||
gezeigt, dass die Richtung ein Münzwurf ist; das Grundproblem ist also nicht die
|
||
Qualität des Squeeze, sondern seine **Frequenz** (2–3×/Woche auf einem
|
||
Instrument). Auf dieses Problem hat das Projekt bisher zweimal reagiert — mehr
|
||
Signale auf demselben Instrument (23 Fehlschläge) oder feinere Zeitebene (Kosten
|
||
erledigen es). Die dritte Achse war unberührt: **dasselbe Signal, mehr Märkte.**
|
||
| Symbol | Spr/ATR | H1 ØR / PF | H2 ØR / PF | |
|
||
|---|---|---|---|---|
|
||
| **GER40** | 0,086 | +0,668 / 4,23 | +0,595 / 3,82 | ✅ |
|
||
| **SpotBrent** | 0,184 | +0,503 / 2,90 | +0,653 / 3,85 | ✅ |
|
||
| **XAUUSD** | 0,028 | +0,559 / 3,74 | +0,556 / 3,95 | ✅ |
|
||
| **NAS100** | 0,072 | +0,552 / 3,32 | +0,481 / 2,94 | ✅ |
|
||
| SpotCrude *(Kontrolle)* | 0,181 | +0,460 / 2,67 | +0,605 / 3,51 | ✅ |
|
||
| XAGUSD | 0,261 | +0,395 / 2,19 | +0,379 / 2,30 | ✅ |
|
||
| Copper | 0,327 | +0,300 / 1,83 | +0,375 / 2,11 | ✅ |
|
||
| NatGas | 0,651 | +0,229 / 1,52 | +0,240 / 1,56 | ✅ |
|
||
| **Gasoline** | **0,972** | −0,610 / 0,35 | −0,148 / 0,76 | ❌ |
|
||
| **USDX** | **1,972** | −1,269 / 0,13 | −1,317 / 0,14 | ❌ |
|
||
✅ **Die Kontrolle reproduziert** (SpotCrude +0,460/+0,605 gegen bekannt
|
||
+0,456/+0,611) — die Pipeline ist in Ordnung. ✅ **Alle 7 sind nachbar-robust**
|
||
(Box 10/15 × k 0,1/0,2, 28 Zellen, keine kippt) und halten Einstiegs-Slippage bis
|
||
+0,20×ATR.
|
||
✅✅ **Die beiden Durchfaller sind der beste Beleg, dass es kein Zufall ist:** es
|
||
sind exakt die zwei mit **Spread ≥ ATR** — dieselbe Kosten-Arithmetik, die auch
|
||
M1 erledigt hat. Der Test scheitert dort, wo er scheitern MUSS.
|
||
⚠ **ATR-Floor relativ gesetzt** (0,542 × Median-ATR statt der WTI-Dollar-Zahl
|
||
0,06) — ein absoluter Floor wäre auf Gold wirkungslos und auf NatGas ein
|
||
Selektions-Artefakt (Lehre aus `analyze_tageszeit.py`).
|
||
⚠⚠ **DREI Vorbehalte, die vor einem Bau geklärt gehören:**
|
||
(1) **„7 von 9" sind NICHT 7 unabhängige Tests.** Crude/Brent korrelieren ~0,9,
|
||
Gold/Silber und NAS100/GER40 ebenso — effektiv sind es eher 3–4 unabhängige
|
||
Fälle, und alle im **selben Makro-Zeitraum**. Mehrere Slots gleichzeitig wären
|
||
dann dasselbe Risiko doppelt, keine Diversifikation.
|
||
(2) **Die Kompression trägt weniger als der Ausbruch selbst.** Die Kontrolle
|
||
„beliebige Box" ist überall ebenfalls positiv (+0,144…+0,533); der Beitrag der
|
||
Kompression liegt bei **+0,009…+0,155** und ist bei XAGUSD (+0,009/+0,040) und
|
||
NAS100 (+0,034/+0,013) kaum vorhanden. Gemessen wird hier also zu einem großen
|
||
Teil **„Ausbruch + unser Exit-Stack"**, nicht der Squeeze im engeren Sinn.
|
||
(3) **Swap/Kommission je Symbol, Handelszeiten und Mindest-Lots sind NICHT
|
||
modelliert** — nur der Bar-Spread.
|
||
⚠ Und der Fill-Befund direkt darunter gilt hier genauso: die Zahlen stehen in
|
||
Variante (a), real ist (b) — also rund **0,3 R niedriger** anzusetzen.
|
||
- **⚠⚠⚠ FILL-ANNAHME: „am Level einsteigen" hat ZWEI Bedeutungen — und die
|
||
teurere ist die richtige (gemessen `backtest_squeeze_touchfill.py` +
|
||
`backtest_momentum_v2.py`, 2026-08-06).** Aufgefallen beim Momentum-Nachtest.
|
||
| | |
|
||
|---|---|
|
||
| **(a) Projekt-Standard** | einsteigen, WO das Signal ausgelöst hat — also dort, wo der **Bar-CLOSE** das Level gebrochen hat, zum Level-Preis |
|
||
| **(b) Realität** | einsteigen, WANN die liegende Order gefüllt hätte — bei der **ersten Berührung**, egal wo der Bar schließt |
|
||
**Live gilt (b).** (a) konditioniert auf den Bar-Schluss, den eine ruhende Order
|
||
nicht kennen kann — das ist **Look-ahead**, kein Modellierungsdetail.
|
||
**Gemessen auf dem Squeeze (80k M5, 2 Halbjahre, kanonischer Exit, Echtkosten):**
|
||
| Fill-Annahme | H1 ØR / PF / n | H2 ØR / PF / n |
|
||
|---|---|---|
|
||
| (a) Close bricht Level | +0,461 / 2,68 / 801 | +0,605 / 3,51 / 1280 |
|
||
| **(b) Berührung (LIVE)** | **+0,124 / 1,24 / 1092** | **+0,292 / 1,64 / 1807** |
|
||
**Aufschlag +0,337 (H1) / +0,312 (H2) R je Trade** — und **291 bzw. 527
|
||
zusätzliche Fills**, im Schnitt Verlierer (Dochte, die wieder in die Box
|
||
zurückfallen; passt zu „54 % aller Box-Ausbrüche kehren binnen 3 Bars zurück",
|
||
`backtest_consolidation.py`).
|
||
✅ **Der Squeeze hält trotzdem** — beidhälftig positiv, PF > 1. Und die
|
||
Umstellung auf ruhende Orders bleibt richtig: (b) +0,124/+0,292 gegen
|
||
**Market −0,158/−0,006**. Bemerkenswert: (b) trifft fast genau das ORIGINAL
|
||
validierte Band **+0,14…+0,23** aus `backtest_breakout_squeeze.py` — der
|
||
B4-Maßstab war also die ganze Zeit der realistischere.
|
||
⚠⚠ **KONSEQUENZ: die heute mehrfach zitierten +0,456/+0,611 sind als
|
||
Live-Erwartung ~0,32 R zu hoch.** Sie bleiben als Vergleich zwischen Market und
|
||
Level gültig (dafür waren sie gerechnet), taugen aber nicht als Ertragsprognose.
|
||
✅✅ **ERLEDIGT — der SIG-Pending-Pfad FÄLLT DURCH (`backtest_signal_touchfill.py`,
|
||
2026-08-06, 80k M5, voller Live-Gate-Stack, kanonischer Exit, Echtkosten).**
|
||
| Konf | A) Markt am Bar-Close | B) Level, Fill bei Close-Bruch | **C) Level, Fill bei BERÜHRUNG** |
|
||
|---|---|---|---|
|
||
| ≥55 | −0,015 / −0,172 | +0,381 / +0,315 | **−0,392 / −0,338** ❌ |
|
||
| ≥65 | −0,024 / −0,085 | +0,367 / +0,385 | **−0,478 / −0,471** ❌ |
|
||
| ≥75 | −0,148 / −0,310 | +0,288 / +0,136 | **−0,184 / −0,202** ❌ |
|
||
**C ist in ALLEN SECHS Feldern negativ** (PF 0,38–0,71). Der Look-ahead-Aufschlag
|
||
beträgt **0,77–0,85 R** — grösser als beim Momentum (0,45) und beim Squeeze (0,32).
|
||
⚠⚠ **C ist sogar schlechter als der MARKT-Einstieg** (−0,392 gegen −0,015). Der
|
||
Mechanismus ist im Trade-Zähler sichtbar: C hat **mehr** Fills als B (441 gegen
|
||
328 · 659 gegen 455) — die zusätzlichen sind genau die Berührungen, die
|
||
zurückfallen, und die sind die Verlierer. Eine Order an einem **wandernden**
|
||
Level fängt systematisch die Fehlausbrüche ein.
|
||
✅ **Damit ist die Vorhersage bestätigt:** entscheidend ist nicht „Level statt
|
||
Markt", sondern ob das Level **STILLSTEHT**. Squeeze-Box → hält (+0,124/+0,292);
|
||
`_pend` und Momentum → wandern → fallen durch.
|
||
⚠ **Die im Bau-Eintrag vom 05.08. zitierten +0,39/+0,36 sind damit als
|
||
Live-Erwartung ungültig** — sie stehen in Variante B. Der Eintrag bleibt als
|
||
Historie stehen, gilt aber nur noch für den Vergleich Markt↔Level.
|
||
✅✅ **ABGESCHALTET (User-Entscheidung 2026-08-06): `signal_pending_entry = false`.**
|
||
Backup `oil_widget_config.ini.bak-2026-08-06-sigpending`. ⚠ Der Schlüssel lebt
|
||
**nicht** in `runtime_state.json` (kein UI-Toggle) — die ini greift direkt.
|
||
Live verifiziert: `signal_pending=False`, `squeeze_pending=True`.
|
||
Neu im **Config-Wächter** (validiert = `false`), damit ein Zurückschalten
|
||
auffällt: es hiesse, SIG über den gemessen schlechtesten Einstiegsweg zu handeln
|
||
— schlechter als Market.
|
||
⚠ **`squeeze_pending_entry` bleibt begründet** — dort steht die Box still, und
|
||
der Touchfill-Test ist bestanden.
|
||
⚠ Zur Migrations-Prüfung: `lauf()` hat einen `fill`-Parameter bekommen
|
||
(Default `"close"`). Der Vorher/Nachher-Vergleich war **nicht bitgenau** —
|
||
Abweichung: EIN Trade in H2 (454→455). Ursache ist nicht der Patch (mit
|
||
`fill="close"` sind die ausgeführten Anweisungen identisch), sondern
|
||
**zwischenzeitlich eingetroffene Live-Bars**: `copy_rates_from_pos(0, 80000)`
|
||
ankert am neuesten Bar, das Fenster verschiebt sich zwischen zwei Läufen.
|
||
H1 war in beiden Läufen identisch.
|
||
|
||
- **⚠⚠ MOMENTUM-CONTINUATION MIT LEVEL-ORDER NEU GEMESSEN = BLEIBT VERWORFEN
|
||
(`backtest_momentum_v2.py`, 2026-08-06, User-Wunsch).** Kein bloßes Wiederholen:
|
||
der Lauf von 2026-07-17 stieg am **Bar-Close** ein, und genau diese Annahme hat
|
||
am 05.08. Squeeze und Auto-Signal von negativ auf positiv gedreht. Momentum hat
|
||
ein sauber definiertes Level (`mom_N` kreuzt X×ATR ⇔ Preis erreicht
|
||
**C[i−N] ± X×ATR**), die Frage war also offen.
|
||
| Variante | H1 | H2 |
|
||
|---|---|---|
|
||
| A) Bar-Close (der alte Lauf) | −0,10…−0,19 | −0,08…+0,00 |
|
||
| B) am Level, Fill bei **Kreuzung** | +0,35…+0,47 | +0,47…+0,56 |
|
||
| **C) am Level, Fill bei BERÜHRUNG (echt)** | **−0,04…−0,09** | **−0,02…+0,07** |
|
||
**Alle neun N/X-Kombinationen** sehen in B großartig aus und sind in C
|
||
beidhälftig ohne Edge. **Der Aufschlag von ~0,45 R ist reines Look-ahead.**
|
||
⚠⚠ **Der strukturelle Grund — und er ist die eigentliche Lehre:** das
|
||
Momentum-Level **wandert jede Bar** (`C[i−N]` rutscht mit). Es ist damit kein Ort,
|
||
an dem eine Order liegen bleiben könnte; sie müsste im 5-Minuten-Takt umgesetzt
|
||
werden und fängt dabei jede Zuckung ein. Die Squeeze-Box **steht** — deshalb hält
|
||
sie (+0,124/+0,292) und Momentum nicht. **Ein Level ist nur dann ein
|
||
Ausführungs-Vorteil, wenn es STILLSTEHT.**
|
||
⚠ **Zwei eigene Fehler auf dem Weg, beide dokumentiert:** (1) der erste Entwurf
|
||
prüfte nicht, ob das Level als Stop-Order überhaupt auf der **richtigen Seite des
|
||
Marktes** liegt — dadurch „füllte" die Sim zu längst passierten Preisen und
|
||
lieferte **ØR +2,2**, was schlicht unmöglich ist und den Fehler verriet.
|
||
(2) Variante B war überhaupt erst nötig, weil ich den Unterschied
|
||
Kreuzung/Berührung zunächst nicht gesehen hatte. **Merksatz: eine Zahl, die zu
|
||
gut ist, ist ein Befund über den Code, nicht über den Markt.**
|
||
✅ Der Befund bestätigt zugleich die heutige Regel aus dem Kerzen-Fade — „ruhende
|
||
Order hilft bei Momentum-Entries" — in **präzisierter** Form: sie hilft bei
|
||
**Ausbrüchen an stehenden Strukturen**, nicht bei Momentum allgemein.
|
||
- **⚠⚠ SCALPING-MODUL AUF M1 = ABGERATEN, bevor überhaupt gebaut wird (gemessen
|
||
`analyze_scalping_costs.py`, 2026-08-06, User-Frage „wie sinnvoll wäre ein
|
||
zusätzliches Modul Scalping auf 1M-Basis, über das Dashboard steuerbar?").**
|
||
Es ist zuerst eine **Kosten-**, keine Signalfrage: der Spread ist bei WTI absolut
|
||
~konstant und skaliert NICHT mit der Zeitebene, die Bewegung schon.
|
||
| TF | Ø ATR | Ø Spread | **Median Sp/ATR** | Ø Sp/ATR |
|
||
|---|---|---|---|---|
|
||
| **M1** | 0,0870 $ | 0,0254 $ | **0,319** | **0,416** |
|
||
| M5 | 0,1677 $ | 0,0226 $ | 0,193 | 0,256 |
|
||
| M15 | 0,2269 $ | 0,0250 $ | 0,133 | 0,171 |
|
||
Der Spread ist auf allen drei Ebenen praktisch derselbe Betrag — **eine mittlere
|
||
M1-Kerze ist nur 3,4× so groß wie der Spread.** Nötige Trefferquote bei 1:1
|
||
(Ziel/Stop je 1×ATR_M1): **65,9 %** statt 50 %; bei 0,5:0,5 sogar **81,9 %**.
|
||
Zum Vergleich: nichts in diesem Projekt hat je über ~46 % geliefert.
|
||
⚠ **Nachts ist es arithmetisch unmöglich:** um **01:00 beträgt der Spread
|
||
1,051×ATR_M1** — er ist größer als die durchschnittliche Bar-Spanne (00:00 0,801 ·
|
||
06:00 0,750). Handelbar wäre allenfalls **10–17 Uhr** (0,210–0,265), also dort, wo
|
||
M1 ungefähr die heutigen M5-Kosten erreicht.
|
||
⚠⚠ **DREI Gründe, die schwerer wiegen als die Kosten selbst:**
|
||
**(1) Ein Backtest nach Projektstandard ist NICHT möglich.** Die M1-Historie des
|
||
Brokers ist hart bei **80.000 Bars = 80 Tage** gedeckelt (`copy_rates_from_pos`
|
||
liefert darüber `None`, `copy_rates_range` ebenso). Zwei Stichproben à 40 Tage
|
||
liegen im **selben Regime** — genau die Konstellation, an der ORB und die
|
||
Dead-Hour-Stunde 13 gescheitert sind. Der eigene `candles_m1`-Logger steht bei
|
||
19 Tagen und erreicht die 180 erst ~2027-01.
|
||
**(2) Das Auflösungs-Artefakt trifft M1 am härtesten.** `analyze_trail_resolution.py`
|
||
(05.08.) hat gezeigt: eine Bar-Simulation **überschätzt enge Stops systematisch**,
|
||
Kipp-Punkt bei ~0,3×ATR. Ein Scalp lebt in 2–3 Bars — eine M1-Bar-Sim hat dort
|
||
praktisch keine Auflösung mehr. Ein positives Ergebnis wäre nicht überprüfbar; es
|
||
bräuchte Tick-Daten.
|
||
**(3) Der Slippage-Schwanz ist ABSOLUT und frisst deshalb den kleinen Stop.**
|
||
Gemessen (`analyze_execution.py`): Median 0,017 $ (unkritisch), **Ø 0,234 $, Max
|
||
4,192 $**. Ein 2×ATR_M1-Stop ist **0,174 $** breit — der MITTLERE Slippage-Wert
|
||
übersteigt ihn bereits. Der reale 0,751-$-Fall vom 30.06. entspricht **8,6×** einem
|
||
M1-Stop gegen 2,2× einem M5-Stop; er löscht rund **11** gewonnene Scalps statt 4.
|
||
✅ **Was FÜR die Idee spricht (fairerweise):** der Bau wäre billig — Pending-Stop-
|
||
Orders, `_auto_guard`, `_pending_ziel` mit Vorrang, Dashboard-Toggle (Muster
|
||
🚀 BRK / 🎯 SIG), `runtime_state.json`, B4-Monitor und die Setup-Kennzeichnung
|
||
(seit 06.08.) existieren alle. Und **höhere Frequenz = schnellere Validierung**:
|
||
der Squeeze braucht 20 Trades und hat nach 5 Tagen 8.
|
||
✅ **Empfehlung: KEIN eigenständiges Scalping-Modul, aber eine engere Variante ist
|
||
messbar** — der **Squeeze auf M1** (= die schon offene Variante B), beschränkt auf
|
||
10–17 Uhr, mit ruhender Stop-Order. Das ist kein neues Signal, sondern eine
|
||
validierte Regel auf einer feineren Ebene; die 80 Tage reichen für ein
|
||
**Ausschluss**-Urteil (nicht für eine Bestätigung).
|
||
- **⚠⚠ SQUEEZE AUF M1 (Variante B) GEMESSEN — NICHT ausgeschlossen, aber in JEDER
|
||
Variante schlechter je Trade als M5; die Entscheidung hängt an EINER ungemessenen
|
||
Zahl (`backtest_squeeze_m1.py`, 2026-08-06).** 80.000 M1-Bars (18.05.–06.08.),
|
||
echte `_squeeze_one`-Regel, Einstieg als ruhende Stop-Order am Level, kanonischer
|
||
Exit, Echtkosten, sequentiell EIN Slot, **M5-Kontrolle auf DEMSELBEN Fenster**
|
||
(weil beide Hälften im selben Regime liegen — der interne Vergleich ist der
|
||
belastbare Teil, nicht die Absolutwerte).
|
||
| Variante | H1 ØR / ΣR | H2 ØR / ΣR | n (H1) |
|
||
|---|---|---|---|
|
||
| **M5-Kontrolle** (live laufende Ebene) | **+0,605 / +163** | **+0,491 / +132** | 270 |
|
||
| A) M1, Stop aus ATR_M1, alle Stunden | +0,405 / **+473** | +0,385 / **+476** | 1168 |
|
||
| **C) A + nur 10–17 Uhr** (beste M1) | **+0,474 / +201** | **+0,387 / +189** | 424 |
|
||
| B) M1-Einstieg, Stop aus ATR_M5 | +0,207 / +176 | +0,137 / +118 | 848 |
|
||
| D) B + nur 10–17 Uhr | +0,231 / +73 | +0,130 / +44 | 316 |
|
||
| A ohne ATR-Floor (Selektions-Kontrolle) | +0,385 / +457 | +0,342 / +442 | 1186 |
|
||
**Nichts fällt durch:** alle Varianten beidhälftig positiv, alle 5 Nachbar-Kombis
|
||
(Box 10/15/20 × k 0,1/0,2) ebenfalls, Einstiegs-Slippage hält bis 0,20×ATR
|
||
(+0,254/+0,130). Auch die Selektions-Kontrolle ohne ATR-Floor bestätigt — der
|
||
massstabsgerecht skalierte Floor (0,024 statt 0,06) erzeugt den Befund nicht.
|
||
Kein Broker-Hindernis: `trade_stops_level = 0`, ein 2×ATR_M1-Stop (0,159 $) ist
|
||
platzierbar.
|
||
⚠⚠ **DER ENTSCHEIDENDE BEFUND IST DIE EXIT-SLIPPAGE — und sie trifft M1 doppelt
|
||
so hart, genau wie vorhergesagt.** Slippage ist eine **absolute** Größe; sie frisst
|
||
von einem kleinen M1-Stop einen viel größeren Anteil. In DOLLAR angesetzt, auf
|
||
beide Ebenen gleich:
|
||
| Exit-Slippage | M1 (Variante C) | M5-Kontrolle |
|
||
|---|---|---|
|
||
| 0,000 $ | +0,474 / +0,387 | +0,605 / +0,491 |
|
||
| **0,017 $** (gemessener **Median**) | +0,309 / +0,196 | +0,511 / +0,384 |
|
||
| **0,050 $** | **−0,012 / −0,175 ❌** | +0,330 / +0,176 |
|
||
| 0,100 $ | −0,499 / −0,737 ❌ | −0,139 (H2) ❌ |
|
||
**M1 kippt bei ~0,04 $, M5 erst bei ~0,09 $** — Faktor ~2,2, praktisch das
|
||
ATR-Verhältnis (0,198/0,079 = 2,5). Gemessen (`analyze_execution.py`):
|
||
**Median 0,017 $** (beide halten) · **Ø 0,234 $** (beide fallen, M1 weit
|
||
deutlicher) · Max 4,192 $.
|
||
✅✅ **DIE FEHLENDE ZAHL IST NACHGEMESSEN (`analyze_slippage_holdtime.py`,
|
||
2026-08-06, 317 SL-Closes).** Die bisher zitierten „Median 0,017 · Ø 0,234"
|
||
waren die Statistik **nur über die Ausreißer** — `analyze_execution.py` zählt
|
||
ausschließlich Fälle, die schlechter als der Initial-SL endeten (`if gap > 0`).
|
||
Sauber ist die Untergrenze **E[Slippage] ≥ P(gap>0) × Ø(gap | gap>0)**; sie ist
|
||
unkontaminiert, weil das **Trailing den gap nur negativer machen kann** (es zieht
|
||
den SL in Gewinnrichtung) — ein positiver gap ist damit zwingend Slippage.
|
||
| Haltedauer | alle Stunden | **nur 10–17 Uhr** |
|
||
|---|---|---|
|
||
| 0–15 min | **0,0470 $** | **0,0058 $** |
|
||
| 15–30 min | 0,0164 $ | 0,0238 $ |
|
||
| 60–120 min | 0,0502 $ | 0,0449 $ |
|
||
| >120 min | **0,1691 $** | 0,0464 $ |
|
||
| **<30 min gewichtet** | **0,0370 $** | **0,0124 $** |
|
||
⚠⚠ **Der Schwanz sitzt AUSSERHALB von 10–17** — bei kurzen Trades ist die
|
||
Slippage dort **8× kleiner** (0,0058 gegen 0,0470). Damit ist die
|
||
Stunden-Beschränkung nicht bloß eine Kosten-Optimierung: sie entfernt auch den
|
||
Slippage-Schwanz. Beides hat dieselbe Ursache (Buchtiefe), das ist ein
|
||
Mechanismus und kein Data-Mining.
|
||
**ENDVERGLEICH — jede Variante mit dem gemessenen Wert IHRES Fensters:**
|
||
| Variante | H1 ØR / ΣR | H2 ØR / ΣR | n (H1) |
|
||
|---|---|---|---|
|
||
| M1 A, alle Stunden · 0,0370 $ | **−0,107 / −125 ❌** | **−0,161 / −199 ❌** | 1168 |
|
||
| **M1 C, 10–17 Uhr · 0,0124 $** | +0,354 / **+150** | +0,247 / **+121** | 424 |
|
||
| M5, alle Stunden · 0,0370 $ | **+0,401** / +108 | **+0,261** / +71 | 270 |
|
||
| M5, 10–17 Uhr · 0,0124 $ | **+0,751** / +68 | **+0,444** / +45 | 90 |
|
||
✅ **M1 über alle Stunden fällt jetzt HART durch** (PF 0,83/0,76) — die
|
||
Nachtstunden fressen es auf, exakt wie die Kosten-Arithmetik vorhergesagt hat.
|
||
Der ΣR-Vorsprung von Variante A war reines Artefakt der Annahme „keine
|
||
Exit-Slippage".
|
||
⚠ **Auf demselben Fenster (10–17) bleibt es ein echter Zielkonflikt:** M5 liefert
|
||
je Trade **rund das Doppelte** (+0,751/+0,444 gegen +0,354/+0,247), M1 in der
|
||
Summe **rund das Doppelte** (+150/+121 gegen +68/+45) — weil es 4,7× so oft
|
||
handelt. Beide konkurrieren um denselben EINEN Positions-Slot.
|
||
✅ **Urteil: NICHT umstellen.** Drei Gründe: (a) der entscheidende Parameter ist
|
||
nur als **Untergrenze** bekannt, und M1 C hat davon bis zur Kippgrenze nur
|
||
**~3× Luft** (0,0124 → ~0,04), M5 dagegen ~7×; (b) die Stichprobe hinter „M5 ist
|
||
je Trade doppelt so gut" ist mit n=90/101 dünn, die hinter der Slippage-Schätzung
|
||
im 10–17-Fenster mit 17 Ereignissen ebenso; (c) 80 Tage = EIN Regime.
|
||
⚠ **Die Bedingung, unter der es kippen würde, ist benennbar** (für später): bleibt
|
||
die gemessene Exit-Slippage für <30-min-Trades im Fenster 10–17 bei größerer
|
||
Stichprobe unter ~0,02 $, dann ist der **Hybrid** (M1 während 10–17, M5 sonst)
|
||
rechnerisch ~1,9× der reinen M5-Variante (ΣR +190/+147 gegen +108/+71) und einen
|
||
kleinen Live-Test wert. Vorher nicht.
|
||
⚠ **Zwei Methodik-Lehren aus dem Lauf selbst:**
|
||
(1) **Der Härtetest lief zuerst auf der SCHWÄCHSTEN Variante.** `beste = "D" if …`
|
||
wählte nach Reihenfolge statt nach Ergebnis — D (+0,231/+0,130) statt C
|
||
(+0,474/+0,387). Ein Robustheitstest auf der falschen Variante beweist nichts.
|
||
(2) **Die vorab fixierte Regel „nicht schlechter als die M5-Kontrolle" war wieder
|
||
unterspezifiziert** — M1 verliert in ØR und gewinnt in ΣR um das Dreifache.
|
||
Dieselbe Falle wie am 05.08. beim Kosten-Gate. Die Regel wurde **nicht
|
||
nachträglich gelockert**; stattdessen zeigt `zeig()` jetzt beide Spalten, damit
|
||
die Auswahl nicht stillschweigend passiert.
|
||
⚠ **80 Tage = EIN Regime** (Broker-Deckel). Ein ✅ heißt hier „nicht
|
||
ausgeschlossen", **nicht** „bestätigt".
|
||
Snapshot **`wave.squeeze {state, dir, level, box_atr}`**: `active` = Ausbruch (echtes
|
||
Signal LONG/SHORT) · `armed` = komprimiert, Ausbruch steht bevor · None = keine
|
||
Kompression (transient — der Ausbruch weitet die Box, Signal klärt sich selbst).
|
||
**Verdict-Modul „Squeeze"** (`_verdict`, Gewicht 2,0, nur bei `active`; unter der
|
||
Welle=3,0). **UI Welle-Kachel** (`#wave-squeeze`): grün/rot bei `active`
|
||
(„🚀 Squeeze-Breakout ▲ LONG @ …"), blau bei `armed` („🎯 Kompression … Ausbruch
|
||
steht bevor"), grau sonst. ⚠ **Nur der ENTRY** — der User-Teil „Gegen-Trade schließen
|
||
+ Stop-&-Reverse" ist NICHT gebaut (nicht validiert, verdoppelt Kosten, Chop-Risiko).
|
||
Läuft normal über SL/Trailing/Notfall-Stop. Caveats: modeste Stichprobe (Box>12 =
|
||
12–22 Trades = Rauschen), Frequenz ~2–3/Woche → **B4-Wochenmonitor** (Live-ØR/PF
|
||
gegen +0,14…+0,23), Runner-abhängig (43 % Treffer). Verwerfen, wenn Live wegdriftet (B5).
|
||
- **Momentum-Continuation als Auto-Setup = VERWORFEN (gemessen `backtest_momentum.py`,
|
||
2026-07-17):** Kandidat für Autonomie (Einstieg in Richtung eines frischen
|
||
Momentum-Schubs, mom_N kreuzt ±X×ATR; seq. Sim, Live-Exit, Echtkosten). **Fällt
|
||
durch den 2-Stichproben-Test:** fast alle N/X in H1 negativ (−0,03…−0,22), in H2
|
||
positiv (+0,02…+0,07) → regime-abhängig. Einzige „beide positiv"-Kombi (N=24/X=2,0)
|
||
ist H1 **+0,002 / PF 1,00** = Breakeven-Rauschen, nicht der Squeeze-Maßstab
|
||
(+0,14…+0,23). Grund: Momentum-Continuation IST Trendfolge — erbt die Regime-
|
||
Anfälligkeit des Wave-Signals, kein distinkter Edge wie die Kompression→Ausbruch-
|
||
Klasse. **Autonomie-Fazit: der Squeeze bleibt der EINZIGE validierte Auto-Entry.**
|
||
(P(break)-Level-Bounce als Entry ist separat schon verworfen, s. „Range-Einstieg".)
|
||
- **Opening Range Breakout (ORB) als Auto-Setup = VERWORFEN (gemessen
|
||
`backtest_orb.py`, 2026-07-17, Recherche-Kandidat):** Box = erste 15/30 min nach
|
||
EU-Open (10:00 Brk) bzw. US-Open (16:30 Brk), Ausbruch ±0,1×ATR, one-shot, Live-
|
||
Exit, Echtkosten. **3 von 4 Varianten kippen zwischen den Hälften** (US-Open H1
|
||
−0,19…−0,27!); einzig EU-15min ist beidseitig positiv, aber nur ØR +0,069/+0,027
|
||
(PF 1,11/1,04) = Rauschen unter dem Kosten-Puffer, und die Nachbar-Parameter
|
||
kippen (keine Robustheit — beim Squeeze waren ALLE 8 Kombis positiv). ⚠ Lehrstück:
|
||
erster Lauf zeigte EU-Open ØR +1,3/PF 10 — war ein **Selektions-Artefakt** (ATR-
|
||
Skip statt Floor ließ in der Niedrig-Vola-H1 nur 9 Morgen durch); mit Floor
|
||
verschwand der „Edge". Zeit-Anker ersetzt keinen Struktur-Anker.
|
||
- **ATR-Breakout-Bestätigung** (`wave._confirm_breakout`, live `[trading]
|
||
breakout_k=0.3` — Split-validiert, s. u.; 0=aus): ein
|
||
Richtungssignal wird **erst durchgelassen, wenn der Kurs k×ATR in
|
||
Signalrichtung** gelaufen ist („X dynamisch"); läuft er vorher k×ATR DAGEGEN/
|
||
Timeout → neu verankern, solange **WARTEN** (Grund „warte auf Breakout …",
|
||
`snap.breakout`). **Stateful** (`_pend`), reset bei TF-Wechsel. Gemessen
|
||
(`backtest_breakout.py`): hebt Ø-Edge/Trade **~2×** (+0,185→+0,36 bei k=1,0),
|
||
Treffer 43→47 %, ~halb so viele Trades → **netto** klar besser (halbe Spread-/
|
||
Komm.-Kosten; lohnt ab Kosten >0,037×ATR, real ~0,1×ATR). = die „Zwei-Konten-
|
||
Hedge"-Idee des Users, auf EINEM Konto ohne doppelte Kosten. **k=0,3 seit
|
||
2026-07-08 (Split-validiert, 80k Bars in 2 Halbjahren):** k=0,3 ist in BEIDEN
|
||
Hälften das Beste — H1: ØR +0,119/netto +339 (0,5: +0,055/**−831**; 1,0:
|
||
+0,046/−805 — beide netto negativ!), H2: +0,362/+4239 (0,5: +3308; 1,0: +3838).
|
||
Grund: 0,3 filtert Fehlstarts, behält aber den besseren Einstiegspreis (höheres k
|
||
= späterer Einstieg, Anfang des Moves verschenkt). ⚠ Ein-Stichproben-Lauf hatte
|
||
„1,0 besser" gezeigt — Split-Prüfung drehte das (B2!). Nebeneffekt: kürzere
|
||
„warte auf Breakout"-Phasen.
|
||
- **Entry-Raum-Gate (AKTIV, `[trading] entry_room_atr=0.6` — kurz 1,0, am selben
|
||
Tag zurück auf 0,6, s. u.; gemessen `backtest_entryroom.py` 2026-07-16):**
|
||
Richtungssignal → WARTEN, wenn das
|
||
**Gegenlevel** (M5-Pivot in Trade-Richtung, trainingsgleich zum S/R-Auto-Close)
|
||
näher als X×ATR_M5 liegt. Befund (seq. Sim MIT S/R-Close-Exit, Echtkosten,
|
||
2 Halbjahre): **monoton in BEIDEN Hälften** — Raum <0,3: ØR −0,119/−0,067
|
||
(PF 0,42/0,66!) · 0,3–0,6: −0,100/−0,059 (⚠ 79–82 % WR und trotzdem PF<1 =
|
||
Klein-Close-Falle) · 0,6–1,0: −0,123/−0,060 · ≥1,0 bzw. „kein Gegenlevel" positiv.
|
||
Kosten-Arithmetik, kein Signal-Timing (deshalb robust, anders als die 9 verworfenen
|
||
Filter): Ertrag am Level gedeckelt (63 % Containment) − 0,265×ATR Kosten < 0.
|
||
Auslöser: Live-Trades mit Sofort-/Klein-Close (0-min-Trade +0,08 direkt am Level;
|
||
4 der 5 sr_closes desselben Tages waren dagegen RETTER — Exit NICHT anfassen).
|
||
Umsetzung: `wave._room_gate` (in `refresh_market` VOR `_confirm_breakout`;
|
||
`set_entry_room`, Grund „kein Raum: Widerstand X nur 0,4×ATR entfernt …").
|
||
Backtests via `_build` bleiben gate-frei (kein pb_levels-State). **0,6→1,0→0,6
|
||
(2026-07-24):** kurz auf 1,0 erhöht (gemessen in BEIDEN Hälften besser, H1 +415/
|
||
H2 +266 R; Live-Kurzcheck: S/R-Closes seit 16.07. +925 € bei nur 28 % Klein-Closes)
|
||
— aber der User empfand im gemischten Regime (M15/M30 ab, M5/H1 auf) **zu viel
|
||
WARTEN** und stellte noch am selben Tag auf 0,6 zurück. ⚠ Lehr-Fall dabei
|
||
festgehalten: „kein LONG trotz steigendem Kurs" lag NICHT am Raum-Gate, sondern am
|
||
**M30-Gegen-Trend-Filter** (M30 ab → LONG unterdrückt, gewollt); das Raum-Gate
|
||
mutete nur den daraus resultierenden SHORT (Support 0,09×ATR entfernt). 0,6 vs 1,0
|
||
ist reiner Frequenz-gegen-Edge-Kompromiss (gemessen 1,0 netto besser). ⚠ **P(break)-Freigabe des Gates = VERWORFEN (gemessen
|
||
`backtest_entryroom_pbreak.py`, 2026-07-24):** Idee war, Raum<1,0-Signale
|
||
durchzulassen, wenn P(break)@Entry hoch (Level bricht eh → Ertrag nicht gedeckelt).
|
||
Das Modell TRENNT zwar auch am Entry (Freigabe-Gruppen klar besser als der Rest),
|
||
aber JEDE Schwelle kippt zwischen den Hälften (p≥0,5: −0,016/+0,083 · p≥0,6:
|
||
+0,089/−0,006 · p≥0,7: +0,076/−0,029) → nicht robust, reines Distanz-Gate bleibt.
|
||
**13. verworfener Signal-Eingriff.** (Nebenbefund: geblockte Gruppe in beiden
|
||
Hälften klar negativ, −436/−251 ΣR — bestätigt die 1,0-Erhöhung.)
|
||
- **Mindest-Konfidenz-Gate** (`_MIN_CONF=55`): Signal < 55 % → WARTEN. Gemessen
|
||
(`backtest_conf.py`): Band **40–54 % = negativer Edge** (−0,025), ≥55 % = +0,058;
|
||
Gate hebt Ø-Edge ~+50 % und den Gesamtertrag (entfernt die schwachen Setups, bei
|
||
denen zu viele Strafen gestapelt sind — typisch der Lagging-Short am Boden).
|
||
- **⚠⚠ DER 43-%-MASSSTAB WAR UNGÜLTIG — der richtige liegt bei ~80 % WARTEN
|
||
(gemessen `backtest_dist_gated.py`, 2026-08-05).** `backtest_dist.py` misst
|
||
**`_build` ALLEIN**, ohne **Breakout-Bestätigung** und ohne **Entry-Raum-Gate**.
|
||
Genau die beiden sind live die größten Blocker. Über 80k M5-Bars:
|
||
| | gerichtet | WARTEN |
|
||
|---|---|---|
|
||
| nur `_build` (die alte Referenz) | **77,9 %** | 22,1 % |
|
||
| **voller Live-Stack** | **19,5 %** | **80,5 %** |
|
||
| LIVE (seit 01.08., ohne `stale`) | 7,2 % | 92,8 % |
|
||
Aufschlüsselung im vollen Stack: entry_room **41,4 %** · breakout_pending 17,0 % ·
|
||
stretch 13,8 % · deadband 7,4 % · min_conf 0,9 %.
|
||
✅ **Damit ist der „93 % statt 43 %"-Alarm entschärft**, der den TF-Churn-Fix
|
||
(2026-07-31) ausgelöst hat: verglichen wurden zwei verschiedene Systeme. Der reale
|
||
Abstand zum gültigen Maßstab ist **12,3 Pp**, nicht 50.
|
||
⚠ **Ein Rest bleibt** (19,5 → 7,2 %) und ist NICHT wegerklärt. Kandidaten, die eine
|
||
Bar-Simulation prinzipiell nicht abbildet: der **TF-Wechsel** (live wandert die
|
||
Basis-TF M5…M30, die Sim ist fix M5), Cooldowns, EIA-/News-Fenster, und die
|
||
**Abtastung** — live wird pro Sekunde gemessen, ein `breakout_pending`-Zustand
|
||
hält über eine ganze Bar an und ist deshalb live überrepräsentiert (26,5 % gegen
|
||
17,0 %). Der auffälligste Einzelposten ist **min_conf: live 16,3 %, Sim 0,9 %** —
|
||
das ist der nächste konkrete Ansatzpunkt, wenn jemand dem Rest nachgehen will.
|
||
⚠⚠ **NACHGEPRÜFT AM 05.08. (User: „heute nur sehr wenige Empfehlungen")** —
|
||
1.427 Zeilen geloggt (Soll ~1.440, die Erfassung selbst ist also intakt),
|
||
**5,0 % gerichtet**. Auffällig ist nicht die Menge, sondern die **Verteilung
|
||
über den Tag**: alle 71 gerichteten Empfehlungen fielen **vor 10:00**, danach
|
||
16 Stunden lang genau eine.
|
||
| Block | 05.08. live | Backtest (voller Stack) |
|
||
|---|---|---|
|
||
| entry_room | 36,5 % | 41,4 % |
|
||
| breakout_pending | 19,4 % | 17,0 % |
|
||
| **min_conf** | **16,1 %** | **0,9 %** |
|
||
| htf_counter | 7,8 % | — (aus M5 aggregiert) |
|
||
| stretch | 6,7 % | 13,8 % |
|
||
| eia / stale | 4,1 / 4,1 % | — (nicht modelliert) |
|
||
| **gerichtet** | **5,0 %** | **19,5 %** |
|
||
✅ **entry_room ist NICHT der Unterschied** — es ist live sogar seltener als im
|
||
Backtest. Die Lücke von 14,5 Pp wird von den **live-only-Gates** getragen, allen
|
||
voran **min_conf (+15,2 Pp)**, dazu htf_counter, EIA und `stale`. Zusammen sind
|
||
das 31 Punkte, also mehr als genug, um die Differenz zu erklären.
|
||
⚠ Tageszeitlich dominierte nachmittags/abends **entry_room** (21:00: 59 von 60
|
||
Minuten), ab 22:00 **min_conf** (100 %). Strukturelle Mitursache beim Raum-Gate:
|
||
es nutzt die **M5**-Pivots, deren Median-Abstand mit **0,61×ATR** praktisch auf
|
||
der Gate-Schwelle von 0,60 liegt — es blockt damit rund die Hälfte der Zeit
|
||
schon per Konstruktion (s. `backtest_level_tf.py`).
|
||
✅ **TELEMETRIE-LÜCKE GEFUNDEN UND GESCHLOSSEN:** `min_conf`-Zeilen schrieben
|
||
`conf_pct = 0` — die verworfene Konfidenz stand NUR als Fließtext in den Gründen
|
||
und war nicht auswertbar. Damit liess sich beim zweitgrössten Blocker nicht
|
||
sagen, ob die Signale **knapp** (50–54) oder **weit** (20–30) unter der Schwelle
|
||
lagen. `wave_rec._build` gibt den Wert jetzt mit (reine Telemetrie, das Signal
|
||
bleibt WARTEN). Erste Live-Zeile: **conf 52** — drei Punkte zu wenig.
|
||
⚠ **Was das noch NICHT beantwortet:** ob 55 die richtige Schwelle ist.
|
||
`backtest_conf.py` hat das Band 40–54 % als negativ gemessen (−0,025 gegen
|
||
+0,058), eine Senkung ist also nicht angezeigt. Die neue Zahl sagt nur, WIE VIEL
|
||
Signalmenge unmittelbar hinter der Schwelle steht — nach ein paar Tagen
|
||
auswertbar.
|
||
⚠ **Die im TF-Churn-Fix dokumentierte Erfolgskontrolle („WARTEN-Anteil Richtung
|
||
43 % bewegen") ist damit hinfällig** und sollte nicht weiterverfolgt werden.
|
||
- **Signalhäufigkeit** (`backtest_dist.py`): ~57 % der Zeit LONG/SHORT, ~43 % WARTEN.
|
||
WARTEN-Treiber: M30-Gegen-Trend (~27 % der Bars, gewollt, Edge×2) + Überdehnung
|
||
+ Totband. `_STRETCH_MAX` 2,5→**3,5** angehoben (`backtest_stretch.py`: +26 %
|
||
Signale, Gesamtertrag steigt; ab 3,5×ATR kippt der Edge negativ).
|
||
- **⚠ M30-FILTER NACH VOLA-REGIME GEPRÜFT = KEINE ÄNDERUNG (gemessen
|
||
`backtest_htf_vola.py`, 2026-08-04).** Anlass: Am 04.08. fiel WTI −4,12 $ (davon
|
||
−4,16 $ in EINER Stunde), der Bot stand zu 93 % auf WARTEN, `htf_counter` blockte
|
||
**160-mal** — scheinbar genau die Richtung, in die der Markt lief. Hypothese: die
|
||
nachlaufende M30-EMA sperrt in schnellen Umkehrungen das Richtige.
|
||
**Die Messung invertiert die Hypothese.** Geblockte Signale, kanonischer Exit,
|
||
Echtkosten, nach ATR-Terzil × 2 Halbjahre:
|
||
| ATR-Terzil | geblockt H1 | geblockt H2 | durchgelassen H1 | durchgelassen H2 |
|
||
|---|---|---|---|---|
|
||
| niedrig | −0,086 | +0,107 | −0,136 | −0,134 |
|
||
| mittel | −0,172 | +0,067 | −0,198 | −0,069 |
|
||
| **hoch** | **−0,625** | **−0,329** | −0,061 | **+0,014** |
|
||
Im HOHEN Terzil sind die geblockten Signale **mit Abstand die schlechtesten des
|
||
gesamten Datensatzes** (PF 0,15 / 0,42) — der Filter ist dort am wertvollsten, nicht
|
||
am schädlichsten. Die durchgelassenen Hoch-Vola-Signale in H2 sind das **einzige
|
||
positive Feld** der ganzen Tabelle. In den unteren Terzilen kippen die geblockten
|
||
Signale zwischen den Hälften = Rauschen. **Den Filter in schnellen Bewegungen zu
|
||
lockern wäre die schlechtestmögliche Änderung gewesen. 22. verworfener Eingriff.**
|
||
⚠⚠ **Auflösung des scheinbaren Widerspruchs — und eine Lehre über Populationen:**
|
||
Die 160 geblockten Signale und die 17 manuellen Gewinn-Trades desselben Tages sind
|
||
**verschiedene Grundgesamtheiten**. Die manuellen Trades liefen ALLE auf
|
||
`rec=WARTEN`, stammen also gar nicht aus der Signal-Logik — der Filter hat den User
|
||
nie gebremst, er handelte unabhängig. „Das Gate blockte X" und „der Mensch verdiente
|
||
Y" gehören nur dann zusammen, wenn Y aus X stammt. Die Ausgangsvermutung hat genau
|
||
das verwechselt.
|
||
- **Higher-TF-Gegen-Trend-Filter:** Signal wird verworfen (→WARTEN), wenn der
|
||
übergeordnete **M30**-Trend (EMA12/50, Totband `_HTF_DEADBAND=0,15×ATR`) klar
|
||
dagegen steht — kein Short im M30-Aufwärtstrend & umgekehrt. Nur für Basis-TF
|
||
< M30 aktiv (`_htf_sign`, holt M30-Bars im selben `mt5_lock`). Per Backtest
|
||
belegt: Ø-Edge ×2 (+0,012→+0,027), Treffer 52→54 %. Mess-Tool:
|
||
`python backtest_htf_filter.py M5 M30 4000 0.15`.
|
||
- **Einstiegsqualität (nur Konfidenz/Score, NICHT Richtung):** nach gemessenem
|
||
Edge (`backtest_pullback.py`) gestaffelt — **tiefer Pullback** (Kurs durch die
|
||
EMA zurück, `near<0`) = bestes CRV (+15, „⭐ tiefer Pullback"); laue Zone direkt
|
||
an der EMA (0–0,3×ATR) = schwächster Edge (−3); Trend-Momentum (1–2×ATR) +8.
|
||
**Kein** Spitzen-Gate: ein hartes „nur an Wellenspitze"-Filter senkte den
|
||
Gesamtertrag (−80 % Trades), darum verworfen. Konfidenz blockt NICHT das Ordern
|
||
(nur WARTEN/Gegen-Signal tut das).
|
||
- **Multi-TF-Konfluenz (nur Konfidenz/Anzeige):** M30 **und** H1 bestätigen die
|
||
Richtung → +12, „⭐⭐ Konfluenz M30+H1"; H1 dagegen → −8. Per Backtest belegt:
|
||
Ø-Edge ×3,5 (+0,016 nur-M30 → +0,056 M30+H1). `_htf_sign` holt zusätzlich
|
||
H1-Bars. Mess-Tool: `python backtest_improve.py 8000`.
|
||
- **„Morgens fällt der Kurs, nachmittags steigt er" = WIDERLEGT (gemessen
|
||
`analyze_tageszeit.py`, 2026-08-04, User-Beobachtung):** ROHE Kursdrift je
|
||
Berliner Stunde (nicht Signal-Edge wie `backtest_hourly_split.py`), 80k M5-Bars
|
||
(17.06.25–04.08.26), DST-korrekt via `zoneinfo`, 2 Hälften, Überschuss gegen den
|
||
jeweiligen Tagesschnitt (sonst färbt das Regime alle Stunden ein: H1 fiel, H2 stieg).
|
||
**ALLE VIER Tagesblöcke kippen zwischen den Hälften** — es gibt überhaupt kein
|
||
Tageszeit-Muster (Zehntel-Cent je Bar):
|
||
| Block | H1 | H2 | |
|
||
|---|---|---|---|
|
||
| Vormittag 08–12 | +0,06 | −0,43 | kippt |
|
||
| Nachmittag 14–18 | −1,26 | +1,96 | kippt |
|
||
| Abend 19–23 | −0,39 | +1,67 | kippt |
|
||
| Nacht 00–07 | +0,42 | −1,30 | kippt |
|
||
Robust auf: 2/5/20/21/23 Uhr · robust ab: 14/18/19 Uhr — verstreute Einzelstunden
|
||
aus 24 Tests, kein Vormittag/Nachmittag-Muster.
|
||
⚠ **Größenordnung macht es ohnehin unhandelbar:** die stärkste belastbare Stunde
|
||
(20:00, +1,9/+3,3) sammelt über eine volle Stunde ~0,02–0,04 $ — der Spread allein
|
||
ist **0,0225 $**. Selbst wenn die Drift echt wäre, ist sie kleiner als die Kosten.
|
||
⚠ Std 23 sticht mit +33,0 in H2 hervor, hat aber nur n=648 (22 Uhr: 1062, sonst
|
||
~1750) — das ist der Marktschluss-Rand, kein Befund.
|
||
⚠⚠ **ZWEI eigene Messfehler auf dem Weg dorthin — beide dokumentiert, weil sie
|
||
Fehlerklassen sind, keine Ausrutscher:**
|
||
**(1) Selbst gebauter ATR-Filter (04.08.).** Der erste Lauf meldete „Vormittag
|
||
robust negativ" — Artefakt von `atr < 0,06` übersprungen + ATR-Normierung. In den
|
||
ruhigen Morgenstunden der älteren Periode liegt der ATR unter dem Floor → von
|
||
1.740 Bars je Stunde blieben **138–391** übrig, und zwar die ungewöhnlich
|
||
volatilen Morgen. Gleiche Fehlerklasse wie das ORB-Selektions-Artefakt 2026-07-17.
|
||
**Regel: ein Filter, der Bars nach einer Eigenschaft verwirft, die mit der
|
||
untersuchten Achse korreliert, ERZEUGT den Befund.**
|
||
**(2) Zeitzone um 3 h verschoben (05.08., beim Squeeze-Nachgang aufgefallen).**
|
||
`datetime.fromtimestamp(rates["time"], UTC+3)` behandelt den Bar-Zeitstempel als
|
||
echten UTC-Epoch — MT5 liefert dort aber bereits die **Broker-Wallclock**. Ergebnis:
|
||
jede Stunde war **3 Stunden zu spät** einsortiert; der zweite Lauf meldete deshalb
|
||
„Nachmittag robust negativ", was in Wahrheit der Abend war. **Richtig:**
|
||
`fromtimestamp(ts, UTC).replace(tzinfo=UTC+3).astimezone(Berlin)` (Helfer `_berlin`
|
||
im Skript). ⚠ Der Rest des Projekts war NICHT betroffen (geprüft) — `core/gaps.py`
|
||
& Co. nutzen die richtige Konvention; der Fehler steckte nur in diesem neuen Skript.
|
||
**Plausibilitätstest, der ihn entlarvt hätte: die dünnen Stunden (n≈650/1060) müssen
|
||
am Marktschluss liegen (22/23 Uhr), nicht mitten in der Nacht.**
|
||
- **Tageszeit-Gate = AUS geschaltet (User-Vorgabe 2026-07-22, `[trading] dead_hours=`
|
||
leer — bewusst GEGEN die Messung).** Jetzt **config-steuerbar** (`wave.set_dead_hours`,
|
||
Komma-Liste von Stunden; leer=aus). Gemessener Default im Code = `0,1,2,3,4,5,6,7,12,16`
|
||
(die ini überschreibt auf leer). ⚠ 12/16 waren zuletzt am 2026-07-21
|
||
(`backtest_hourly_split.py`) noch beidhälftig negativ — der User schaltet das Gate
|
||
trotzdem ab. Reaktivieren = ini-Stunden eintragen. Der **EIA-Blackout (Mi 15:30–16:30)
|
||
bleibt separat aktiv** (eigenes Event-Gate, nicht Teil des `dead_hours`). Historie/
|
||
Messung (Gate war 11–14 → 12+16 → aus → zurück → jetzt wieder aus):
|
||
Nach der **Echtkosten-Messung** (`backtest_realcosts.py`, Kosten = Bar-Spread/ATR
|
||
statt pauschal 0,1×ATR — Ø real 0,265!): **Nacht 0–7 = Kostenfalle** (konstanter
|
||
Spread ÷ niedriger Nacht-ATR = 0,32–0,50×ATR, in beiden Hälften negativ; reine
|
||
Arithmetik) + **12/16 Uhr 3× unabhängig negativ**. Politik „0–7+12" verbessert
|
||
BEIDE Hälften (H1 −4038→−1870, H2 +100→+1191). **Robust positiv nur 21–22 Uhr.**
|
||
⚠ Die „15 robusten Negativ-Stunden" NICHT blocken — in-sample-zirkulär (Overfit).
|
||
breakout_k=0,3 auch unter Echtkosten bestätigt (H1 einziges positives k). Historie
|
||
(Gate war 11–14, dann 12+16, dann auf User-Wunsch aus, jetzt gezielt zurück):
|
||
- **Von 11–14 auf 12+16 umgestellt (gemessen+verifiziert, `backtest_hourly.py` +
|
||
`backtest_hourly_split.py`, 80k M5 in 2 Halbjahren):** Netto-R je Stunde zeigt
|
||
**12 Uhr (−0,11/−0,12) und 16 Uhr (−0,17/−0,10) in BEIDEN Hälften negativ**;
|
||
**Std 13 kippt das Vorzeichen** (−0,048/+0,039 = Rauschen), 11/14 ~breakeven. Das
|
||
alte Pauschal-Gate 11–14 blockte harmlose Stunden und ließ die real schlechte 16
|
||
laufen. **Gate 12+16 schlägt 11–14 im Gesamt-Netto-R in beiden Zeiträumen**
|
||
(H1 −1357→−1253, H2 +1562→+1799) und handelt dabei mehr Trades. Intraday-Edge
|
||
generell dünn; **Löwenanteil in US-Session 21–22 Uhr** (+0,25/+0,49).
|
||
- **Reversal-Ausnahme vom Gate = VERWORFEN (gemessen, `backtest_deadhour.py`):**
|
||
Reversal in der Dead-Hour nur **+0,062×ATR brutto** (vs +0,190 außerhalb) → nach
|
||
Kosten (~0,1×ATR) negativ. Trade-①-Gewinner war Varianz. Gate gilt auch für Reversal.
|
||
- **EIA-Blackout (kausales Event-Gate, AKTIV):** **Mittwoch 15:30–16:30 Berlin**
|
||
(Vorlauf der EIA-Lagerdaten 16:30 = 10:30 ET, Offset ganzjährig 6 h) → hartes
|
||
WARTEN („EIA-Blackout …"). Gemessen (`backtest_events.py`, 2 Halbjahre): dieses
|
||
Fenster in BEIDEN Hälften netto negativ (−0,147/−0,088 vs rest) — deckt sich mit
|
||
dem alten „16 Uhr"-Befund. eia_post/api_pre/api_post NICHT robust → nicht geblockt.
|
||
Nur im Live-Pfad (`hour≠None`), Backtests bleiben gate-frei. Best-Practice
|
||
„Event-Blackout" statt Pauschal-Gate.
|
||
- **Wirtschaftskalender-Blackout (`core/econ_calendar.py`, 2026-07-30, `[trading]
|
||
news_blackout=true`, ±15 min):** Erweiterung der EIA-Idee auf ALLE High-Impact-
|
||
Termine (FOMC/Zinsentscheid/NFP/CPI/GDP/PCE + alle Oil-/OPEC-Titel). Quelle
|
||
**Forex Factory JSON** (`nfs.faireconomy.media`, kostenlos, kein Key, mit
|
||
`impact`-Feld); geholt werden **beide Wochen** (`thisweek`+`nextweek` — „thisweek"
|
||
endet samstags, der Markt öffnet aber Sonntag 23:00 → sonst blinder Fleck), 1×/h
|
||
**im Daemon-Thread** (ein hängender HTTP-Call darf den Trend-Loop nicht blockieren),
|
||
dedupliziert über (ts, title). Filter: `impact=High` + `country=USD`, ODER Titel
|
||
enthält crude oil/opec/oil inventories/petroleum (der EIA-Report läuft bei FF teils
|
||
als Low/Medium). ⚠ **NICHT gemessen** — Events sind zu selten für eine
|
||
2-Stichproben-Aussage, das ist die Handbuch-Regel („keine Trades 15 Min. vor
|
||
High-Impact-News"), kein belegter Edge. Deshalb bewusst asymmetrisch verdrahtet:
|
||
**sperrt nur die AUTONOMEN Entries** (`_check_auto_squeeze` + `_check_auto_signal`,
|
||
jeweils OHNE Dedup-Marker → nach dem Fenster feuert das Setup, falls noch aktiv) und
|
||
erscheint in der Entry-Checkliste nur als **„warn"**, nicht „fail" (soll den Verdict
|
||
nicht allein auf `stop` kippen); **manuelle Orders bleiben frei.** Der gemessene
|
||
EIA-Blackout bleibt unabhängig davon aktiv — er greift auch, wenn der Kalender kein
|
||
Netz hat (Fail-safe: ohne Daten → kein Blackout, Log 1×, dann still). Snapshot
|
||
`calendar {enabled, n, blackout{title,impact,ccy,minutes}, next, age_min}`,
|
||
Frontend `#cal-note` (Meldungen-Karte, bernstein: laufendes Fenster bzw. Vorwarnung
|
||
≤60 min). Abschalten `news_blackout=false`; Fenster via `news_blackout_before_min`/
|
||
`_after_min`. Mit 4 synthetischen Szenarien getestet (5 min vor/nach Event, 50 min
|
||
davor = kein Blackout, disabled) + Live-Fetch verifiziert (EIA lag korrekt auf
|
||
Mi 16:30 Berlin).
|
||
- **⚠⚠ WINKEL-BESCHRIFTUNG WAR ÜBERALL INVERTIERT — Text korrigiert 2026-08-04, ZAHLEN
|
||
unverändert.** Beim Spiegeltest (User: „prüfe die Logik im umgekehrten Fall, fallender
|
||
Kurs") kam heraus: `calc_trend_angle` liefert **0°=AUFWÄRTS … 180°=ABWÄRTS**, also ist
|
||
`ad = angle−90` **negativ bei STEIGENDEM** Kurs. Damit sagten die Texte das Gegenteil
|
||
dessen, was passierte — ein LONG in einem steigenden Markt bekam „⚠ Winkel gegen EMA"
|
||
(−10), ein LONG in einem fallenden „Winkel bestätigt" (+5).
|
||
⚠ **Und `backtest_angle.py` benutzt DIESELBE verdrehte Beschriftung**
|
||
(`a5_dis = a5 < 90-dead` für LONG heißt „dagegen", ist aber STEIGEND). Der
|
||
dokumentierte Befund „Winkel dafür/neutral +0,055 vs dagegen +0,025" bedeutet richtig
|
||
gelesen: **LONG bei FALLENDEM Kurzfrist-Winkel (= Pullback) trägt doppelt so gut wie
|
||
bei bereits steigendem.** Das ist der im Projekt mehrfach belegte **Pullback-Effekt**
|
||
(vgl. „⭐ tiefer Pullback" und den RSI-Befund: *bestätigende* Oszillatoren sind
|
||
schlechter).
|
||
✅ **Konsequenz: die Gewichtung war von Anfang an NUMERISCH RICHTIG** — nur hieß der
|
||
Bonus „bestätigt", obwohl er den Gegen-Winkel belohnt. **Nicht umdrehen.** Geändert
|
||
wurden ausschließlich die Reason-Texte („⭐ Pullback (Kurzfrist-Winkel gegen den
|
||
Trend)" / „kein Pullback — Kurs läuft dem Signal schon davon") und die Kommentare.
|
||
Verifiziert: Konfidenzwerte vorher/nachher identisch (60/70/75 je Richtung).
|
||
⚠ **Lehre:** Der erste Verdacht lautete „systematisch invertierte Logik" — er war
|
||
falsch. Die Zahlen stimmten, die Sprache log. Bei einer verdrehten Konvention muss man
|
||
prüfen, ob die MESSUNG dieselbe Verdrehung teilt, bevor man den Code „repariert".
|
||
(Beim Reversal war es anders: dort hat die eigenständige Messung das Setup unabhängig
|
||
von der Beschriftung als negativ ausgewiesen.)
|
||
✅ **Spiegeltest bestanden:** Mit `reversal_enabled=false` ist das Verhalten über die
|
||
ganze Überdehnungs-Spanne **exakt symmetrisch** (steigend/LONG ↔ fallend/SHORT):
|
||
gleiche Signale, gleiche Konfidenz, gleicher Anti-Überdehnungs-Schnitt bei 3,5.
|
||
- **Winkel-vs-EMA-Warnung (nur Konfidenz):** Regressions-Winkel der Basis-TF gegen
|
||
die EMA-Richtung → −10 „⚠ Winkel gegen EMA (mögliche Wende)" (nachlaufende EMA!);
|
||
dafür → +5. `backtest_angle.py`: dafür/neutral +0,055 vs dagegen +0,025; als Gate
|
||
aber Gesamtertrag-negativ → bewusst nur Konfidenz/Warnung (Hilfe fürs manuelle
|
||
Überspringen der Lagging-Shorts).
|
||
- **Höher-TF-Winkel-Filter (M30+H1-Winkel) = VERWORFEN (gemessen,
|
||
`backtest_htf_angle.py`):** Signal verwerfen, wenn M30/H1-Regressionswinkel gegen
|
||
die Richtung steht → in JEDER Schwelle/Variante **schlechter**. Die entfernten
|
||
Signale (Short bei aufwärts-Winkel) haben **höheren** Edge (+0,04–0,05) als die
|
||
behaltenen (+0,02) — es ist der profitable Pullback-Setup. Filter kostet ~⅔ des
|
||
Gesamtertrags (+287→~+70). Gegen-Winkel-Shorts NICHT filtern; einzelne große
|
||
Verluste (z. B. −37 an einem H1-Auf-Tag) sind Varianz, Schutz über Notfall-Stop/
|
||
Sizing, nicht über Signal-Filter.
|
||
- **Auch als Konfidenz-STRAFE verworfen (`backtest_confluence_angle.py`):** statt
|
||
Hard-Filter den Konfluenz-Bonus streichen/Konf senken, wenn M30/H1-Winkel
|
||
dagegen → borderline-Signale fallen unters 55-Gate. Ergebnis: in JEDER Variante
|
||
**schlechter** (Δ −2500…−3467 R); die entfernten Gegen-Winkel-Signale haben
|
||
ØR **+0,26…+0,32** (die BESTEN), die behaltenen +0,12. **3× gemessen** (M5-Winkel,
|
||
Höher-TF-Winkel-Filter, Konfluenz-Strafe): „nicht gegen den Winkel shorten"
|
||
macht den Bot IMMER schlechter. **Nicht mehr am Signal drehen** — Edge ist klein
|
||
& varianzlastig; der Hebel ist **Sizing (risiko-basiert) + viele Trades**, nicht
|
||
der Filter.
|
||
- **H1-STÄRKE-Filter (nicht Winkel!) = VERWORFEN (gemessen `backtest_short_h1filter.py`,
|
||
2 Halbjahre, 2026-07-15):** Motiviert durch Live-Befund „SHORT-Empfehlungen 31 % WR
|
||
in Aufwärts-Regime" (LONG 62 %, SHORT 31 %, letzte 40 MIT-Trades). Idee: Signal
|
||
verwerfen, wenn es gegen einen **starken H1-Trend** (|H1 EMA12−EMA50| > thr×ATR_H1)
|
||
läuft. **Fällt durch:** die weggefilterten Signale flippen das Vorzeichen zwischen
|
||
den Hälften — thr 0,5: ØR **H1 −0,198 / H2 +0,001**; thr 1,0: **−0,069 / +0,013**;
|
||
thr 1,5: **+0,029 / −0,063**. In KEINER Schwelle robust negativ in BEIDEN → Filter
|
||
hätte in H2 (positive/breakeven drops) **Geld gekostet**. **7× belegt: die 31 %-SHORT-
|
||
Schwäche ist REGIME (aktueller Uptrend), kein Signalfehler** — in H2 tragen dieselben
|
||
Gegen-H1-Shorts. Kein Filter; der Hebel bleibt Sizing + Notfall-Stop + Diskretion
|
||
(User-WARTEN-Übersteuerungen liefen 68 % WR / +259 €).
|
||
- **ER-Chop-Gate = VERWORFEN (gemessen, `backtest_chopgate.py`, 2 Halbjahre):**
|
||
Signal→WARTEN wenn Efficiency Ratio der letzten N Bars < X. Chop-Signale sind NICHT
|
||
robust schlechter — Effekt kippt: H1 Trend leicht besser (Ø-R +0,070 vs +0,053),
|
||
H2 **umgekehrt** (Chop +0,138 vs +0,129). Gate wirft zudem ~70 % (profitables)
|
||
Volumen weg (ΣR +2299→+836). Das Totband filtert Extrem-Chop schon (ER-0,02-Tag =
|
||
71 % WARTEN); Rest-Signale = Varianz. **5× belegt: Signal-Filter tragen nicht.**
|
||
- **Vola-Abzug (nur Konfidenz):** hohes ATR (oberes Terzil, `_ATR_HIGH≈0,27`) =
|
||
schwächster/negativer Edge → −8 „hohe Vola". Schwelle regime-abhängig → kein Gate.
|
||
- **Reversal-Lockout/-Persistenz = VERWORFEN (gemessen, `backtest_revlock.py`):**
|
||
Gegen-Signale **kurz nach** einem Reversal sind sogar **besser** als normale
|
||
(+0,072 vs +0,018 bei N≤6) → Reversal-Signale NICHT sperren/halten. Einzelne
|
||
Verluste (Bottom-Short nach REV) sind Varianz, kein Muster.
|
||
- **Rec-Logging:** `engine._run_analysis` → `history.log_recommendation` (Throttle
|
||
intern) füllt die `recommendations`-Tabelle (Signal/Score/Conf/Winkel/Setup) für
|
||
Tages-Reviews (`analyze_today.py` rekonstruiert sonst aus M5-Bars).
|
||
- **Reversal-/BOUNCE-Einstieg (antizyklisch, markiertes ZWEITsignal):** **überdehnt**
|
||
(|stretch|≥`_REVERSAL_STRETCH`=**3,0**, **entkoppelt** von Anti-Überdehnung
|
||
`_STRETCH_MAX`=3,5) **UND** Regressions-Winkel hat gedreht → Einstieg in
|
||
**Winkel-Richtung, gegen die EMA** (überverkauft + Winkel auf → LONG; überkauft +
|
||
Winkel ab → SHORT). Setup `WAVE_REV_*`, Grund „🔄 Reversal"; hebt Anti-Überdehnung
|
||
**und** M30-Filter bewusst auf. **Schwelle 3,5→3,0 gesenkt** (User wollte mehr
|
||
Bounces; `backtest_bounce.py`): Ø-R +0,185, PF 1,35, Treffer 70 %, Worst −2×ATR
|
||
(SL-gedeckelt), ~1,5× mehr Bounces als 3,5.
|
||
⚠⚠ **WINKEL-BEDINGUNG LÄUFT GEGEN IHRE EIGENE DOKUMENTATION (gefunden 2026-08-04,
|
||
User-Frage „warum SHORT im starken M5-Aufwärtstrend?").** `calc_trend_angle` liefert
|
||
laut Docstring **0° = starker AUFWÄRTStrend, 90° = flach, 180° = ABWÄRTStrend** —
|
||
also invertiert. Damit heißt `ad = angle−90`: **ad < 0 = STEIGEND**, ad > 0 = fallend.
|
||
Der Code triggert aber `stretch >= +3.0 and ad <= -2` → SHORT, kommentiert als
|
||
„überkauft + Winkel ab". Tatsächlich feuert er bei überkauft **UND WEITER STEIGENDEM**
|
||
Winkel — also GEGEN den Winkel, während CLAUDE.md „Einstieg IN Winkel-Richtung"
|
||
beschreibt. Dasselbe Vorzeichen steckt im Konfidenz-Block (Zeile ~790): ein SHORT
|
||
bekommt „**Winkel bestätigt**" (+5), während der Kurs steigt. Mit synthetischen
|
||
Reihen direkt nachgestellt: überkauft + steigend → `WAVE_REV_SHORT`, Grund „Winkel
|
||
gedreht" + „Winkel bestätigt". **Erklärt zwei dokumentierte Befunde:** die ~50 %
|
||
Richtungstreffer der Bounce-Anzeige (`backtest_bounce_reliability.py`) und „blutet in
|
||
starken Trends".
|
||
⚠⚠ **GEMESSEN (`backtest_reversal_angle.py`, 80k M5, kanonischer Exit, Echtkosten,
|
||
2 Halbjahre) — ALLE drei Varianten fallen durch:**
|
||
| Variante | H1 ØR | H2 ØR | PF | n |
|
||
|---|---|---|---|---|
|
||
| **IST** (Live) überkauft + Winkel steigt → SHORT | −0,122 | −0,031 | 0,79/0,94 | 1100/1615 |
|
||
| **KORRIGIERT** (Doku-Absicht) Winkel fällt → SHORT | **−0,173** | **−0,061** | 0,68/0,89 | 106/163 |
|
||
| **KONTROLLE** ohne Winkelbedingung | **−0,099** | **−0,023** | 0,82/0,96 | 1153/1693 |
|
||
Drei Schlüsse: (a) **Die Vorzeichenkorrektur ist SCHLECHTER** — gut, dass sie nicht
|
||
blind eingebaut wurde; sie feuert zudem kaum (106 statt 1100 Trades, weil bei 3×ATR
|
||
Überdehnung die 14-Bar-Regression fast immer noch steigt). (b) **Die Kontrolle ohne
|
||
Winkel ist die BESTE der drei** — die Winkelbedingung entfernt Trades, die im Schnitt
|
||
besser waren als die behaltenen; sie trägt mit KEINEM Vorzeichen etwas bei.
|
||
(c) **Der Trigger ist in jeder Variante beidhälftig negativ.**
|
||
⚠ Gemessen wurde der ROHE Trigger (ohne Konfidenz-/Raum-Gate); der Legacy-Recheck der
|
||
gegateten Variante kam auf ~Breakeven (PF 0,93/1,06) — dieselbe Richtung. **Keine
|
||
Messung stützt die dokumentierten „+0,185 / PF 1,35" mehr.**
|
||
✅ **ABGESCHALTET (User-Entscheidung 2026-08-04, `[trading] reversal_enabled=false`).**
|
||
Ausschlag gab nicht nur die negative Zahl, sondern dass das Setup **zwei gemessen
|
||
POSITIVE Schutzmechanismen aushebelt** (Anti-Überdehnung UND den M30-Gegen-Trend-
|
||
Filter, Edge ×2) — ein Setup ohne eigenen Edge, das validierte Filter umgeht, ist der
|
||
ungünstigste Fall.
|
||
⚠ **Wichtig für Backtests: der Modul-Default in `wave_rec` bleibt `True`.** Sämtliche
|
||
Backtests rufen dasselbe `_build`; ein False-Default hätte alle bestehenden Messungen
|
||
still verändert. Abgeschaltet wird ausschließlich im Live-Pfad
|
||
(`engine` → `wave.set_reversal_enabled(...)`, Config-Default dort `false`).
|
||
Verhaltensänderung nur im Band **|stretch| 3,0–3,5**: dort kam vorher ein
|
||
antizyklisches REV-Signal, jetzt das normale Trendsignal (und ab 3,5 greift wie
|
||
gehabt die Anti-Überdehnung). Mit 3 Szenarien getestet — überkauft+steigend:
|
||
`SHORT/WAVE_REV_SHORT` → **`LONG/WAVE_LONG`**; nicht-überdehnter Trend unverändert.
|
||
Live verifiziert: bei 3,18×ATR Überdehnung jetzt `WARTEN · setup=WAVE ·
|
||
reversal=None` statt REV_SHORT; Log „Reversal-Setup AUS".
|
||
Überwacht via `measurement_reminder.py` (CONFIG_DEPS, 11 Werte).
|
||
**Zurückholen:** `reversal_enabled = true`. Die **Bounce-Anzeige** (`wave.bounce`,
|
||
Kachel „Überdehnung") bleibt unverändert — sie war ohnehin nur Warn-Kontext, kein
|
||
Trade-Trigger.
|
||
⚠⚠ **DIESE ZAHLEN GELTEN NICHT MEHR** (Legacy-Recheck 2026-07-31, hier eingetragen
|
||
am 2026-08-02): `backtest_bounce.py` nutzt den **vereinfachten Exit OHNE Kosten**
|
||
(`LEGACY_SIMPLE`: kein Breakeven, kein Lock, kein Time-Stop). Mit dem **echten**
|
||
Exit + Echtkosten liegt das Setup bei ~**Breakeven** (PF 0,93/1,06) — es ist
|
||
**kein +0,185-Ertragsbringer**. Was HÄLT, ist die *relative* Aussage: Reversal
|
||
schlägt Trend in beiden Hälften (+0,140/+0,102). Deshalb bleibt es ein sinnvolles
|
||
**nachrangiges, markiertes Zweitsignal** (so ist es live auch eingebaut), aber
|
||
nicht mehr als das. Details im Abschnitt „LEGACY-RECHECK".
|
||
Weiterhin gültig: **schwächer als Trend** & **regime-anfällig** (trägt in
|
||
Seitwärts/Mean-Reversion, blutet in starken Trends). Nachrangiges, markiertes Setup. **Bounce-Status — Multi-TF** im Wave-
|
||
Snapshot (`wave.bounce = {state, dir, tf}`): scannt **M1/M5/M15/M30** (`_bounce_one`,
|
||
Aggregat in `refresh_market`, ~15 s-Throttle via TF-Ampel-Fetch), nimmt den
|
||
**stärksten** (aktiv>erwartet, dann größte Überdehnung). **`expected`** = überdehnt
|
||
(≥`_REVERSAL_STRETCH`), Winkel noch NICHT gedreht · **`active`** = + Winkel gedreht.
|
||
`dir` LONG (überverkauft) / SHORT (überkauft), `tf` = Quelle. Frontend `#wave-bounce`
|
||
als **„Überdehnung"-Warn-Gauge** (NICHT „Bounce/Signal"): grau „keine" · bernstein
|
||
„⚠ überdehnt … Reaktion nach oben ↑ / nach unten ↓ möglich" · kräftiger bei
|
||
`active` (+ TF). **Kein
|
||
grün/rot** (kein Trade-Trigger). **Reversal-Konfidenz-Boni = GEPRÜFT & BEHALTEN
|
||
(gemessen `backtest_revconf.py`, 2 Halbjahre, 2026-07-15):** Befund war, dass
|
||
„starker Trend" (+10) und „⭐ tiefer Pullback" (+15) beim Reversal semantisch
|
||
GEGEN das Signal stehen und Reversals so leichter durchs 55er-Gate kommen.
|
||
Korrektur (Boni beim REV streichen) **fällt durch**: die dadurch rausfallenden
|
||
Reversals kippen das Vorzeichen — H1 ØR **−0,323** (127 St., Fix ok), H2 ØR
|
||
**+0,109/PF 1,16** (1608 St., Fix hätte die BESSEREN gekappt, WR 45 %). Nicht
|
||
robust → Konfidenz-Berechnung bleibt. **8. verworfener Signal-Eingriff.** Die
|
||
hohe REV-Conf-Anzeige (~75–80 %) ist also kein Gate-Fehler; wer will, liest die
|
||
Reason-Liste („Reversal" + „hohe Vola") als Risiko-Kontext. **Gemessen (`backtest_bounce_reliability.py`):
|
||
Richtungs-Trefferquote der „aktiv"-Anzeige ~50 % (Münzwurf), Ø-Vorlauf ~0×ATR über
|
||
alle TF & H1-Regime → reiner Überdehnungs-Kontext, KEIN Signal.** (Die früheren
|
||
„70 %" aus `backtest_bounce.py` waren exit-getrieben, nicht Richtungs-Vorhersage.)
|
||
- **⚠⚠ TF-CHURN-FIX (2026-07-31, zwei Eingriffe) — Ursache für „93 % WARTEN statt 43 %":**
|
||
Auslöser war die User-Frage „warum hat der Bot die Treppe nicht erkannt und long
|
||
eröffnet?" zur Rally am 31.07. (82,5 → 84,4 in 2 h). Befund: in dem Fenster gab es
|
||
**99× WARTEN und 1× SHORT** — und dieser SHORT wurde am Tief autonom eröffnet
|
||
(AUTOSIG, −26,31 €, per `adverse15` geschlossen); den LONG machte der User um 11:56
|
||
von Hand gegen die Empfehlung. **Der WARTEN-Anteil live ist strukturell zu hoch:**
|
||
| | WARTEN |
|
||
|---|---|
|
||
| Backtest-Erwartung (`backtest_dist.py`) | ~43 % |
|
||
| Live letzte 24 h | **93,2 %** |
|
||
| Live letzte 7 Tage | 88,0 % |
|
||
| Live gesamt (93.599 Zeilen) | 78,6 % |
|
||
**MECHANISMUS (im Log belegt):** `_choose_tf` setzt den Score einer TF **hart auf
|
||
0,0**, sobald sie überdehnt ist (`raw = sep if (sep>=deadband and stretch<=_STRETCH_MAX)
|
||
else 0.0`) — genau das passiert M5 IM Trend. Real am 31.07.: M5 sprang zwischen
|
||
**0,00 und 1,93**, die TF wechselte **33× am Tag, Median-Abstand 5 min**. Die
|
||
vorhandene 1,2×-Hysterese ist dagegen wirkungslos (gegen 0,0 kommt jeder Wert durch).
|
||
Und `set_timeframe()` verwarf bei JEDEM Wechsel die laufende Breakout-Bestätigung
|
||
(`_pend = None`) und verankerte sie beim AKTUELLEN Kurs neu. Bei `breakout_k=0,3` und
|
||
ATR_M30≈0,53 braucht sie ~0,16 $, der Kurs lief ~0,08 $ je 5 min → **die Bestätigung
|
||
konnte rechnerisch nie fertig werden.** Dazu: landet die Heuristik im Trend auf M30,
|
||
braucht das EMA-Kreuz dort Stunden — eine 2-h-Bewegung wird nie zum Signal.
|
||
⚠ **Gleicher Fehlertyp wie beim P(break)-Modell am selben Tag:** `backtest_breakout.py`
|
||
hat k=0,3 auf einer **FESTEN** Zeitebene validiert; live wandert sie — die Bedingung,
|
||
unter der die Messung gilt, existiert im Betrieb nicht.
|
||
**FIX 1 (`wave_rec.set_timeframe`): `_pend` wird NICHT mehr zurückgesetzt.** Der Anker
|
||
gehört zum Signal, nicht zur Zeitebene; dreht die Richtung, verankert
|
||
`_confirm_breakout` ohnehin neu (`p.get("dir") != d`). Das Level behält den ATR der
|
||
alten TF — bewusst, eine Neuberechnung wäre wieder ein Reset.
|
||
**FIX 2 (`engine._tf_loop`): Mindest-Verweildauer `[trading] tf_min_dwell_s=900`**
|
||
(0 = aus). Gibt einer TF Zeit, ihr Signal überhaupt fertig zu bestätigen.
|
||
**Verifiziert mit synthetischen Szenarien:** Fix 1 — Bestätigung überlebt den Wechsel
|
||
M5→M30 und feuert LONG, Richtungswechsel verankert korrekt neu; Fix 2 — an der ECHTEN
|
||
Score-Folge vom 31.07. nachgespielt: 4 Wechsel → 2.
|
||
⚠ **Ehrlich zur Beweislage:** das ist **NICHT backtestbar** — Backtests laufen
|
||
grundsätzlich auf einer festen TF, das Churning existiert dort per Konstruktion nicht.
|
||
Begründung ist also nicht „gemessen besser", sondern „stellt die Bedingung her, unter
|
||
der die vorhandene Messung gilt". **Erfolgskontrolle = der WARTEN-Anteil**: er muss
|
||
sich Richtung der gemessenen ~43 % bewegen (Abfrage s. Tabelle oben).
|
||
⚠ **Nicht behoben (bewusst):** der 0,0-Einbruch des Scores bei Überdehnung selbst —
|
||
das wäre eine Änderung der TF-Bewertung und damit messpflichtig. Fix 2 halbiert das
|
||
Churning, verhindert aber nicht den ersten Absturz auf M30.
|
||
- **Timeframe:** `[trading] tf_select=heuristic` → `engine._choose_tf` wählt jede
|
||
Minute die TF mit klarstem Trend aus dem **Band `[tf_min … tf_max]`** (leichter
|
||
Höher-TF-Bias). **Einstellung: `tf_min=M5`, `tf_max=M30`** (M1 raus: gemessen
|
||
~0 Edge; M30 zugelassen, damit der Bot in einem M30-Trend nicht festhängt, wenn
|
||
M5/M15 gegen-Bouncen — sonst Patt: Long geblockt, Short fehlt). **H1 bleibt nur
|
||
Kontext** (Filter/Konfluenz). Start-TF wird ins Band geklemmt. `agent` = LLM
|
||
wählt; `M1..H1` = fest. Der **Agent steuert die TF nicht mehr**.
|
||
- **Positionsgröße = margin-basiert 95 % (User-Vorgabe 2026-07-28, `risk_pct=0`,
|
||
`margin_buffer_pct=95`):** `calc_lots` setzt die Lots auf ~95 % der freien Margin.
|
||
Verlauf 90→95→75→(1,5 % risiko, wenige Stunden am 2026-07-20)→80 %→**95 % margin
|
||
(2026-07-28)** — der User hat die Margin-Wahl damit mehrfach bewusst getroffen (nicht
|
||
ungefragt umstellen). ⚠ 95 % ist noch aggressiver als die 80 %-Referenz unten.
|
||
⚠ Aggressiv: der −95,48-€-Nacht-Trade 2026-07-20 (News-ATR → SL ~1,0 $ breit ×
|
||
1,03 Margin-Lots ≈ 11 % Konto) ist der Referenzfall; mit `risk_pct=1.5` wären es
|
||
~0,15 Lots / ~−14 € gewesen (`backtest_sizing.py`: Margin-Sizing bis 36 %
|
||
Quasi-Ruin, 1–2 % = 0 %). Risiko-Modus bleibt gebaut & abgesichert (Guards s. u.),
|
||
Umschalten = `risk_pct=1.5`. Mechanik: `trader._send_locked`
|
||
bestimmt **erst den SL**; bei `risk_pct>0` → risiko-basiert (`calc_lots_risk`,
|
||
Verlust ≈ risk_pct % Equity, Margin als Deckel) = der gemessene Fix gegen die
|
||
Verlust-Asymmetrie. **Umschalten via `[trading] risk_pct` (z. B. 1.5 = überlebbar).**
|
||
⚠ **Risiko-Modus-Guards (Fix 2026-07-19):** `calc_lots_risk` rundet NICHT mehr auf
|
||
`volume_min` auf (unter Mindestlot → 0.0 statt still mehr Risiko), und
|
||
`_send_locked` fällt bei `risk_pct>0` NICHT mehr still auf Margin-Sizing zurück
|
||
(75 % wäre ein Vielfaches des gewollten Risikos) — der Trade wird stattdessen mit
|
||
Fehlermeldung abgelehnt. Bei `risk_pct=0` (aktuell) unverändert margin-basiert.
|
||
Harter Broker-SL immer mitgeschickt; adoptierte Fremd-Trades bekommen Schutz-SL.
|
||
- **Sizing-Simulation (`backtest_sizing.py`):** 90 % Margin = Wachstums-optimal,
|
||
ABER **36 % P(DD>80 %)** = Ruin-Risiko. Risiko 1–2 %/Trade = überlebbar (0 %
|
||
Quasi-Ruin). Exit-Asymmetrie gemessen `backtest_exit2.py` (Live-Exit Trail 1,5
|
||
ist OK; Problem war Sizing + manuelles Gewinner-Kappen, NICHT die Exit-Logik).
|
||
- **SL:** Band **1,8–2,2×ATR** (Ziel 2,0; `INIT_SL_MIN/MAX_ATR`). **Breakeven ab
|
||
+1,3×ATR** (`_BREAKEVEN_ATR`; war 0,6 → 21 % Breakeven-Scratches, gemessen
|
||
`backtest_exit.py be`: 1,3 = Plateau-Optimum → Scratch 5 %, Ø-R +15 %, kein
|
||
Tail-Risiko). **Früh-Ausstieg = AUS** (`_ADVERSE_EXIT_ATR=0`). Trailing
|
||
Init/Trail/Lock. **Teil-Exit AUS** (`_PARTIAL_TP_FRAC=0`, User-Vorgabe) → am TP
|
||
wird der **komplette** Trade geschlossen (kein 50 %-Runner mehr).
|
||
- **Früh-Ausstieg NUR bei Trades OHNE Signal-Deckung = VERWORFEN (gemessen
|
||
`backtest_adverse_nosignal.py`, 2026-07-23, Auslöser −57,34-€-Trade: manueller
|
||
LONG gegen WARTEN, KEIN Squeeze/Signal-Flip beteiligt):** Hypothese war, dass ein
|
||
Adverse-Exit bei signal-ungedeckten Trades (Totband/WARTEN-Zone, B0 zeigt live
|
||
39 % WR dort) helfen könnte, auch wenn er pauschal auf ALLE Trades schadet. **Fällt
|
||
durch — bei JEDER Schwelle (0,5/0,75/1,0/1,5×ATR), in BEIDEN Populationen (SIG/
|
||
NOSIG), fast durchgehend in BEIDEN Hälften negativ** (einzige Ausnahme: SIG H1
|
||
X=1,5 knapp +11 R, aber H2 dort −46). WR bricht von ~38–43 % auf 18–26 % ein — der
|
||
Früh-Ausstieg kappt zwar einige Trades vor dem vollen SL, schneidet aber weit öfter
|
||
Trades ab, die sich erholt hätten; NOSIG-H2 war ohne Eingriff sogar leicht profitabel
|
||
(ΣR +80, PF 1,15) und wird durch jede Adverse-Schwelle verschlechtert. Hypothese
|
||
„schwächere Population profitiert eher" widerlegt. Keine Änderung.
|
||
- **⚠⚠ TRAIL × BREAKEVEN GEMEINSAM GEPRÜFT = KEINE ÄNDERUNG — und ein
|
||
METHODEN-BEFUND, der einen alten Schluss entwertet (2026-08-05,
|
||
`backtest_trail_be.py` + `analyze_trail_resolution.py`).** Anlass: User-Frage zur
|
||
Kalibrierung „jetzt bei 3 Lot".
|
||
⚠ **Zur Prämisse: die Positionsgröße ändert das Trail-Optimum NICHT.** R ist
|
||
ATR-normiert; Lots skalieren nur das EUR-Ergebnis. Gemessen wurde deshalb die
|
||
echte offene Frage: **`be=1,3` stammt aus `backtest_exit.py`, als der Trail noch
|
||
1,5 war; `mult` wurde am 31.07. auf 1,0 gesenkt, OHNE das Breakeven neu zu
|
||
prüfen.** 2D-Sweep über beide Populationen (2.365 Squeeze- / 1.566 Wellen-Entries),
|
||
feste Entry-Listen, kanonischer Exit, Echtkosten, 2 Halbjahre.
|
||
**(1) Das Breakeven ist praktisch wirkungslos.** Bei mult ≤ 0,6 sind ALLE
|
||
be-Zeilen **identisch** — der enge Trail erreicht den Einstand ohnehin früher als
|
||
der Breakeven-Boden. Erst ab mult 0,8 wirkt er überhaupt, und dort ist be=0,6
|
||
marginal besser (+0,019 bzw. +0,018 ØR). ⚠ Das liegt **unter einem Standardfehler**
|
||
(≈0,026 bei n≈1.500) → **kein Handlungsbedarf**, be bleibt 1,3.
|
||
**(2) Der Trail ist monoton besser, je enger — bis 0,05×ATR.** Und genau das ist
|
||
das Problem: ein Trail von 0,05×ATR (≈3 Cent) wäre vom Spread allein ausgelöst.
|
||
Ein Optimum, das bis an die physikalische Unmöglichkeit monoton bleibt, misst
|
||
nicht den Markt, sondern die Methode.
|
||
⚠⚠ **AUFLÖSUNGS-ARTEFAKT NACHGEWIESEN.** `simulate()` prüft den Stop gegen
|
||
Bar-Hoch/-Tief. Ein Trail, der ENGER ist als die typische Intrabar-Spanne, ist
|
||
nicht auflösbar — das Modell nimmt den Bar-Hochpunkt als Gewinn mit und steigt
|
||
erst im NÄCHSTEN Bar aus. Test mit **in DOLLAR fixiertem** Trail auf M5 gegen M1
|
||
(597 gemeinsame Einstiege):
|
||
| Trail | M5 ØR | M1 ØR | Differenz |
|
||
|---|---|---|---|
|
||
| 0,10×ATR | +0,093 | +0,066 | **−0,027** |
|
||
| 0,20×ATR | +0,049 | +0,039 | −0,010 |
|
||
| 0,30×ATR | +0,013 | +0,016 | +0,003 |
|
||
| 1,00×ATR | −0,046 | −0,010 | **+0,036** |
|
||
| 1,50×ATR | −0,038 | +0,028 | **+0,066** |
|
||
**Das Vorzeichen kippt bei ~0,3×ATR:** die gröbere Auflösung **überschätzt enge
|
||
Trails und unterschätzt weite**. Eine Tick-Simulation würde denselben Weg noch
|
||
weiter gehen. ⚠ **Damit ist der Befund vom 31.07. („enger ist monoton besser",
|
||
`backtest_trailmult.py`) teilweise entwertet** — er ruht auf derselben
|
||
M5-Auflösung. Die damalige Entscheidung, NICHT auf 0,5 zu gehen, war trotzdem
|
||
richtig, nur aus dem falschen Grund (genannt wurde „Randwert + Slippage";
|
||
der eigentliche Grund ist die Auflösungsgrenze).
|
||
⚠ Nebenbefund gegen meine eigene Slippage-Vermutung: die Trade-ZAHL ist über alle
|
||
Multiplikatoren fast konstant (2.108 bei 0,3 gegen 2.083 bei 1,0), nur die
|
||
Haltedauer sinkt (12 → 18 min) und der Worst-Case ist überall identisch (−2,44,
|
||
der SL deckelt). Die Slippage-Exposition steigt also NICHT dramatisch — das
|
||
Auflösungs-Argument trägt, das Slippage-Argument kaum.
|
||
✅ **Ergebnis: Trail 1,0 / Breakeven 1,3 bleiben.** 1,0×ATR entspricht rund einer
|
||
M5-Bar-Spanne (die ATR IST die mittlere Bar-Spanne) und ist damit ungefähr die
|
||
feinste Einstellung, die diese Simulation ehrlich auflösen kann. Wer enger will,
|
||
braucht Tick-Daten — nicht mehr Bars.
|
||
- **Trailing-Optimierung geprüft = KEINE Änderung (gemessen, `backtest_trailing.py`,
|
||
2 Halbjahre, echte Phasen-Sim):** Die Live-Config (`_TRAIL_START_ATR=0,3` ·
|
||
mult 1,5 M5 · Breakeven 1,3) hat die **beste Verlustseite** (bestes H1, Worst nur
|
||
−1,5×ATR). Trailing trägt klar: ohne Trailing ΣR −1313/Worst −2,0 vs. Live +1777/
|
||
Worst −1,5 (~+3000 R **und** kleinerer Einzelverlust). **Breiter ziehen** (mult
|
||
2,0–2,5) oder **Trail später starten** (0,3→1,0) macht H1 schlechter/Worst größer;
|
||
der frühe Trail-Start ist **netto schützend** (holt „läuft-schief"-Trades billiger
|
||
raus). Einzige mithaltende Variante (start0,6) kippte zwischen den Läufen =
|
||
Rauschen. → Ausschütteln ist Varianz, nicht Logikfehler.
|
||
- **Time-Stop (AKTIV, gemessen `backtest_timestop.py`):** Position wird geschlossen,
|
||
wenn sie nach **120 min noch in Phase „Init"** hängt (HW-Profit nie ≥0,3×ATR =
|
||
Whipsaw-Opfer; `trailing._TIMESTOP_MIN=120`, 0=aus, `closed_by=timestop`).
|
||
Verbessert BEIDE Hälften (H1 +98→+162, H2 +2191→+2298 R), Worst unverändert;
|
||
kürzere Fenster (30/60 min) kippten zwischen den Hälften = Rauschen. Dank
|
||
Phasen-Ratsche ist „Init nach 2 h" exakt „nie Trail-Start erreicht".
|
||
- **ATR-Floor 0,12→0,06 gesenkt (gemessen, `backtest_atrfloor.py`, 2 Halbjahre):**
|
||
Die reale Vola fiel unter den alten Floor (H1: **81 % der Signale ATR<0,12** →
|
||
Floor band dauernd, Exit-Distanzen künstlich ~1,3–1,6× breit, dazu ~35k
|
||
Log-Warnungen je 5-MB-Rotation). Floor 0,06 in **beiden** Hälften besser
|
||
(H1 −153→−27 Pts, H2 +512→+554); ganz ohne Floor nur marginal besser → 0,06 als
|
||
Schutz behalten. Floor-Warnung jetzt **gedrosselt** (max. 1×/5 min).
|
||
- **Befund B — umgesetzt (Exit-Simulation `backtest_exit.py`, Doku
|
||
`docs/exit-simulation.md`):** altes Band 1,2–1,5 war zu eng (MAE ~1,7×ATR,
|
||
31–37 % Frühstopps). 2,0×ATR hebt Ø-R +32 %, PF 1,33→1,39, Frühstopps →25 %,
|
||
Worst-Case auf 2,0×ATR gedeckelt. Breiter (3–4×ATR) wäre im Schnitt noch
|
||
besser, aber Tail-/Drawdown-Risiko → verworfen. ⚠ Da Sizing margin-basiert
|
||
(90 %, nicht risiko-basiert), schlägt die SL-Breite voll auf den EUR-Verlust
|
||
durch. Qualität-abhängiger SL NICHT belegt (MAE über alle Buckets gleich).
|
||
- **S/R-Close (am Level schließen) = pauschal 2× VERWORFEN, als GEMESSENES
|
||
Segment aber AKTIV:** Pauschal (`backtest_srclose.py`, Echtkosten, 79k Signale)
|
||
verdoppelt Close@Level die WR (35→67 %), verschlechtert aber PF/ΣR in BEIDEN
|
||
Hälften (0,78→0,67 · 0,88→0,76) = Gewinner-Kappen. **ABER** (`backtest_srbreak.py`,
|
||
47k Touches, User-Idee „P(Durchbruch)>60 % → laufen lassen"): Basisrate
|
||
P(break)=36–37 %; das **Anlauf-Momentum trennt robust in beiden Hälften** —
|
||
Kurs KRIECHT ans Level (letzte 6 Bars <0,3×ATR) → P(break) nur 26 %, **Close
|
||
schlägt Laufenlassen Ø +0,12 R**; mit Schwung (≥0,3) → laufen lassen (41–43 %).
|
||
Volumen/Touch-Zahl trennen NICHT robust.
|
||
- **Kalibriertes P(Durchbruch)-Modell (2026-07-13, `analyze_srwaves.py` +
|
||
`backtest_srclose_prob.py`):** Containment-Befund — **~68 % der Wellen erreichen
|
||
das gegenüberliegende Level, davon brechen nur ~37 % durch → ~63 % prallen ab**
|
||
(robust beide Hälften). P(break) ist **vorhersagbar & kalibriert**: Logit auf 4
|
||
Merkmalen (Anlauf-Momentum 6/3 Bars ·d, mit-Trend, Level-Distanz), auf H1
|
||
trainiert, auf H2 **AUC 0,71 + Kalibrierung trifft** (pred 17→21 %, 49→50 %,
|
||
69→76 %). Dominant = 3-Bar-Beschleunigung (`_PB_W` mom3 +0,78). **R-Ertrag
|
||
(Echtkosten):** Regel „close wenn P(break) < X" schlägt reines Trailing in BEIDEN
|
||
Hälften massiv (+6000/+7600 R); **User-Schwelle 0,60 im Optimum-Plateau (0,50–0,60
|
||
~gleich), validiert.** ⚠ Impliziert Strategiewechsel: ~90 % der Level-Touches
|
||
werden geschlossen (Trendfolger→Level-Scalper) — trägt, weil echte Kosten (0,265×
|
||
ATR) + 63 % Containment das Laufenlassen teuer machen.
|
||
- **S/R-Close SIGNAL-gegated („bei mit-Trend halten statt schließen") = VERWORFEN
|
||
(gemessen `backtest_srclose_signal.py`, 2026-07-22, User-Idee):** Vorschlag war,
|
||
den S/R-Close zu unterdrücken, solange die Empfehlung noch in Trade-Richtung zeigt
|
||
(halten bis WARTEN). **Fällt klar durch — in BEIDEN Hälften ~−5300…−5700 R
|
||
schlechter** als der reine P(break)-Close. Grund/Datenbeleg: ~85 % ALLER Level-
|
||
Touches passieren, WÄHREND die (nachlaufende) EMA noch „in Richtung" zeigt
|
||
(23,8k/20,1k von 27,8k/26,2k Touches) — genau dort HÄLT das Level aber. Diese
|
||
Touches geschlossen = ΣR +11703/+13606, geHALTEN = nur +5978/+8347. Die
|
||
Trend-Empfehlung lagt → sie „bestätigt" ausgerechnet am Abpraller. Das kalibrierte
|
||
P(break)-Modell (hat „mit-Trend" als Merkmal) macht das schon richtig; es mit dem
|
||
groben EMA-Signal zu übersteuern kostet ~⅔ des S/R-Close-Edges. **12. verworfener
|
||
Signal-Eingriff.**
|
||
- **⚠⚠ AUTO-FLIP-CLOSE = TROTZ NEGATIVER MESSUNG GEBAUT (User-Vorgabe 2026-07-31
|
||
„schließe den Trade automatisch beim Close-Signal"), `[trading] auto_flip_close=true`,
|
||
`auto_flip_close_min_r=0.5`.** Dreht die Empfehlung GEGEN die offene Position und
|
||
steht sie ≥0,5×ATR im Plus → `trader.close(reason="flip_close")`.
|
||
**NEU GEMESSEN mit dem ECHTEN Exit (`backtest_flipclose2.py`, 80k M5, 2 Halbjahre,
|
||
Echtkosten, Exit aus `core/exit_model.py`) — bestätigt das alte Urteil, deutlicher:**
|
||
| Variante | Δ H1 | Δ H2 |
|
||
|---|---|---|
|
||
| Flip ab 0,0×ATR | −126 | −221 |
|
||
| Flip ab 0,3×ATR | −77 | −216 |
|
||
| **Flip ab 0,5×ATR** | **−92** | **−160** ← mildeste, deshalb Default |
|
||
| Flip auch im Minus | −205 | −364 |
|
||
Basis ohne Flip: H1 ΣR −537 · H2 −140. **JEDE Variante ist in BEIDEN Hälften
|
||
schlechter.** Mechanik: Trefferquote steigt 39 → 45 %, Ertrag fällt =
|
||
**Gewinner-Kappen** (die nachlaufende EMA dreht oft mitten im Pullback).
|
||
⚠ **Warum überhaupt neu gemessen:** das alte `backtest_flipclose.py` nutzte ein
|
||
**VIERTES Exit-Modell** (Trail fest 1,5 · KEINE Lock-Phase · KEIN Time-Stop ·
|
||
KEIN Initial-TP · `_MAXH` 288 · Phasen über den Bar-Close statt über das
|
||
High-Water) — auf dieser Grundlage wäre das Urteil für den Live-Betrieb nicht
|
||
bindend gewesen. Für die Neumessung bekam `exit_model.simulate()` einen
|
||
**`stop_when`-Hook** (+ `ret_bar`), damit die Phasen-Mechanik NICHT wieder kopiert
|
||
werden musste; Rückwärtskompatibilität verifiziert (ohne Hook bitgenau identisch).
|
||
**NACHRANG gegenüber dem S/R-Close (gemessen `backtest_exit_combo.py`,
|
||
2026-07-31, `auto_flip_close_subordinate=true`):** Beide Exits konkurrieren um
|
||
dieselben Trades — wer zuerst feuert, gewinnt. Die S/R-Freigabe lief OHNE
|
||
Flip-Close, die Baseline existierte also nach dem Einbau nicht mehr. Gemessen auf
|
||
**IDENTISCHEN Entries** (5 Varianten, 2 Halbjahre, Echtkosten, Exit-Kern):
|
||
| Variante | Δ H1 | Δ H2 | S/R-Closes H1/H2 |
|
||
|---|---|---|---|
|
||
| nur S/R-Close | +59 | −14 | 535 / 284 |
|
||
| nur Flip-Close | −1 | −43 | — |
|
||
| BEIDE | +56 | −50 | **507 / 252** |
|
||
| **BEIDE, Flip nachrangig** | **+62** | **−49** | **516 / 260** |
|
||
**Kollision belegt:** der Flip nahm dem S/R-Close 28 bzw. 32 Trades weg (5–11 %) —
|
||
genau die, für die dieser validiert ist. Mit Nachrang ist es in BEIDEN Hälften
|
||
besser als ohne. Umsetzung: liegt ein S/R-ZIEL ≤0,6×ATR entfernt (dieselbe
|
||
Schwelle wie der Live-Hinweis), hält der Flip sich zurück. Mit 4 Szenarien
|
||
getestet (in/außer Reichweite · kein Level · Nachrang aus).
|
||
⚠ **Ehrlich dazu:** auch MIT Nachrang kostet der Flip in H2 rund 35 R gegenüber
|
||
„nur S/R-Close" (−49 vs −14). Der Nachrang begrenzt den Schaden, er dreht ihn
|
||
nicht um. Abschalten des Features bleibt `auto_flip_close=false`.
|
||
⚠ **Methodik-Fehler beim Bau, korrigiert:** der erste Lauf ließ die sequentielle
|
||
Sim nach dem Exit bei `xb+1` weiterlaufen — dadurch hatte JEDE Variante eine
|
||
ANDERE Trade-Folge (sobald eine früher aussteigt, verschiebt sich alles
|
||
Nachfolgende) und die Zahlen waren nicht vergleichbar. Jetzt feste, geteilte
|
||
Entry-Liste wie in `backtest_pbreak_rvalue.py`.
|
||
Umsetzung `engine._check_flip_close` im `_pos_loop`: nur im PLUS, 1× je Ticket,
|
||
Startup-Schonfrist beachtet, Log 🔄 + Telegram, Snapshot `flip_close`/
|
||
`flip_close_min_r`/`flip_close_count`. Mit **10 synthetischen Szenarien** getestet
|
||
(beide Richtungen · aus · unter Schwelle · gleichgerichtet · WARTEN · im Minus ·
|
||
flat · Schonfrist · Ticket-Dedup). Der Alarm (Blinken/Ton/Telegram) bleibt
|
||
unabhängig davon. **B4/B5-Pflicht: Live-Ertrag gegen die Erwartung halten —
|
||
driftet es weg, `auto_flip_close=false`.**
|
||
- **Auto-Flip-Close = VERWORFEN (gemessen `backtest_flipclose.py`, 2 Halbjahre,
|
||
sequentielle 1-Positions-Sim, 2026-07-16):** User-Taktik-Kern „Position im Plus
|
||
automatisch schließen, wenn die Empfehlung dreht" (heute nur Alarm). KEINE Variante
|
||
(minR 0/0,3/0,5×ATR, auch-im-Minus) hält in beiden Hälften ØR & ΣR ≥ Basis:
|
||
H1 ΣR −278→−280…−417, H2 **+174→+119…+144** (kostet 17–32 % des Ertrags).
|
||
Mechanik: WR steigt 43→50 %, Ø-Gewinn schrumpft = **Gewinner-Kappen** — die
|
||
nachlaufende EMA dreht oft mitten im Pullback, der Trade wäre weitergelaufen.
|
||
Flip bleibt **Alarm + menschliche Entscheidung** (User-Übersteuerungen 68 % WR).
|
||
Der Rest der User-Taktik (Ziel=nächstes S/R, weiterlaufen wenn stark) IST der
|
||
P(break)-Auto-Close; der „3 %-Margin-Anker" = EUR-Schwelle, Klasse „verliert".
|
||
- **⚠ NACHGEMESSEN 2026-08-04 mit dem NACHTRAINIERTEN Modell — Ablehnung BESTÄTIGT,
|
||
deutlicher als zuvor** (User-Frage „es werden immer noch Mini-Trades mit geringen
|
||
Gewinnen geschlossen, wie verhindern wir das?"). Δ ΣR gegen minR=0:
|
||
| minR | H1 | H2 |
|
||
|---|---|---|
|
||
| 0,1×ATR | −6 | +10 |
|
||
| 0,2×ATR | **−720** | −42 |
|
||
| 0,3×ATR | −1136 | −157 |
|
||
| 0,5×ATR | −1998 | −683 |
|
||
| 0,8×ATR | −2887 | −1577 |
|
||
**Monoton schlechter in BEIDEN Hälften.**
|
||
⚠⚠ **Und die Prämisse der Frage stimmt so nicht.** Auswertung der 295 Trades seit
|
||
20.07.: die 28 Gewinne ≤2 € bringen zusammen **+21,11 €** = **0,8 %** der
|
||
Brutto-Gewinne (2.770 €) — sie sind kein Ertragsproblem. Entscheidend ist, WOHER
|
||
sie kommen:
|
||
| Schließgrund | Gewinner | Median | davon ≤2 € |
|
||
|---|---|---|---|
|
||
| `sr_close` (Bot) | 81 | **+14,65** | 10 (+7,65) |
|
||
| `manual` (User) | 64 | **+6,37** | **18** (+13,46) |
|
||
**Zwei Drittel der Mini-Gewinne sind manuelle Closes**, und der manuelle
|
||
Median-Gewinn ist **weniger als die Hälfte** des Bot-Medians. Die Klein-Closes des
|
||
BOTS sind gemessen richtig; die eigentliche Asymmetrie liegt im diskretionären
|
||
Früh-Kappen — genau die dokumentierte Kern-Leckage (B0-Checkliste: „Gewinner läuft
|
||
das Trailing"). Ein Code-Gate am Bot-Exit adressiert das falsche Ende.
|
||
⚠⚠ **KONTRAFAKTISCH NACHGERECHNET (`analyze_manual_close.py`, 2026-08-04) — DIE
|
||
„FRÜH-KAPPEN"-HYPOTHESE IST WIDERLEGT.** Für jeden real geschlossenen Trade wurde
|
||
ab dem echten Einstieg der kanonische Exit simuliert (EUR-Faktor 87,6 €/$/Lot,
|
||
robust aus 251 Trades geschätzt — `trader.pnl` enthält Swap, eine direkte
|
||
Rückrechnung wäre verfälscht):
|
||
| Schließgrund | n | IST | Bot-Exit | Δ |
|
||
|---|---|---|---|---|
|
||
| `manual` Gewinn | 143 | **+1428** | +962 | **−467** |
|
||
| `sr_close` Gewinn | 106 | +1591 | +1060 | −530 |
|
||
| `emergency` Verlust | 98 | −1750 | −1235 | +515 |
|
||
| **`sl` Verlust** | **69** | **−1926** | **−697** | **+1229** |
|
||
**Das diskretionäre Früh-Kappen kostet NICHTS — es bringt +467 € MEHR** als der
|
||
kanonische Exit gebracht hätte; in **62 %** der Fälle war der manuelle Close
|
||
besser. Dasselbe gilt für den S/R-Auto-Close (−530). Beide realisieren Gewinne
|
||
besser, als ein reines Trailing es täte.
|
||
✅ **Die Leckage liegt auf der VERLUSTSEITE:** die 69 per Broker-SL geschlossenen
|
||
Trades hätten mit dem kanonischen Exit **1.229 € weniger** verloren (−697 statt
|
||
−1926), die 98 Notfall-Closes 515 € weniger. Über ALLE Trades summiert wäre der
|
||
kanonische Exit rund **+700 €** besser gewesen — praktisch vollständig aus der
|
||
Verlustseite.
|
||
⚠ **Confounder, der das Ergebnis begrenzt:** `simulate()` setzt den Initial-SL auf
|
||
**2,0×ATR_M5**, live kommt er aus **ATR_M15** (`_calc_sl_tp`) und ist damit oft
|
||
deutlich weiter — unter Margin-Sizing schlägt das voll auf den EUR-Verlust durch.
|
||
Der +1.229-Wert misst also zu einem erheblichen Teil **SL-Breite**, nicht
|
||
Exit-Logik. `backtest_sl_tf_mismatch.py` hat die Umstellung auf M5-ATR bereits
|
||
geprüft und wegen H2 verworfen — dieser Befund gibt Anlass, das mit dem
|
||
kanonischen Exit ZU WIEDERHOLEN.
|
||
⚠ Zweite Grenze: der Vergleich gilt **pro Trade**, nicht als Kontostand-
|
||
Hochrechnung — ein länger laufender Trade hätte den Positions-Slot belegt und
|
||
Folge-Trades verhindert.
|
||
✅ **NACHGEMESSEN (`backtest_sl_width.py`, 2026-08-04): der Confounder war die
|
||
Erklärung — aber das Geld ist NICHT einfach zu holen.** Gegatete Live-Population
|
||
(1.224 Entries, Reversal aus, k=0,3), kanonischer Exit, variiert wird
|
||
AUSSCHLIESSLICH die Breite des Initial-SL:
|
||
**ATR_M15 / ATR_M5 = 1,80 im Median** → der Live-SL (2,0×ATR_M15) ist rund
|
||
**3,6×ATR_M5** breit, nicht 2,0. Genau daraus entstanden die 1.229 €.
|
||
| SL-Basis | H1 ØR / ΣR | H2 ØR / ΣR | Worst H1/H2 |
|
||
|---|---|---|---|
|
||
| 2,0×ATR_M5 | −0,092 / −44 | −0,024 / −18 | −2,33 / −2,29 |
|
||
| **2,0×ATR_M15 (LIVE)** | −0,145 / −69 | **+0,064 / +48** | **−4,77 / −5,64** |
|
||
| 1,5×ATR_M5 | −0,081 / −38 | −0,038 / −28 | **−1,83 / −1,85** |
|
||
| 2,5×ATR_M5 | −0,121 / −58 | −0,013 / −10 | −2,83 / −2,79 |
|
||
| 3,0×ATR_M5 | −0,130 / −62 | +0,018 / +13 | −3,33 / −3,29 |
|
||
**KEINE engere Variante schlägt den Live-SL in BEIDEN Hälften** — H2 bevorzugt den
|
||
weiten Stop klar (+48 gegen −18). Die vorab fixierte Regel („nur wenn beidhälftig
|
||
mehr ΣR UND Worst-Case nicht schlechter") ist damit **nicht erfüllt: keine
|
||
Änderung.** Das **reproduziert `backtest_sl_tf_mismatch.py`** (2026-07-23, dort auf
|
||
Squeeze-Entries mit eigenem Exit-Nachbau) jetzt auf der gegateten Population mit
|
||
dem kanonischen Exit — der alte Schluss war korrekt.
|
||
⚠⚠ **Der echte Unterschied ist das TAIL-RISIKO, und das ist eine SIZING-Frage:**
|
||
Worst-Case **−5,64×ATR** (live) gegen **−1,85×ATR** (bei 1,5×ATR_M5) — Faktor 3.
|
||
Bei ATR 0,30 sind das ~1,7 $ Bewegung; unter 95 %-Margin-Sizing auf ~800 € Konto
|
||
ist ein einzelner solcher Trade rund **19 % des Kontos**. Der weite Stop kauft die
|
||
H2-Performance mit genau diesem Tail. Damit landet die Frage dort, wo CLAUDE.md sie
|
||
ohnehin verortet: **„Der Hebel ist Sizing, nicht der Exit."** Wer den Tail begrenzen
|
||
will, senkt `margin_buffer_pct` — nicht die SL-Breite (die kostet gemessen H2).
|
||
- **Mindestgewinn für den S/R-Close = VERWORFEN (gemessen `backtest_srclose_prob.py`,
|
||
Mindestgewinn-Sektion):** „nur closen wenn ≥minR im Plus" verliert **monoton in
|
||
BEIDEN Hälften** (minR 0,2: −444/−213 · 0,5: −1425/−854 R vs minR=0). Die vielen
|
||
kleinen Gewinne SIND der Edge — übersprungene Klein-Closes prallen am (haltenden!)
|
||
Level ab und enden schlechter. Kleine EUR-Beträge = Positionsgröße, kein Exit-Thema.
|
||
2 Halbjahre, Echtkosten):** LONG an Support / SHORT an Resistance, gegated mit
|
||
P(Level hält), Ziel = Gegenlevel — **verliert in BEIDEN Hälften** (FIX H1 −3393/
|
||
H2 −2242, +Trailing/-nur-Trailing ebenso negativ; WR 46–53 %, PF<1). **Asymmetrie:
|
||
P(break) taugt zum EXIT** (Trade schon mit-Trend im Plus am Gegenlevel → kassieren),
|
||
**nicht zum ENTRY** (antizyklisch am Extrem = Münzwurf-Richtung, 6× belegt). Einstieg
|
||
bleibt mit-Trend (Pullback). **Trailing bleibt** — nicht mehr Standard-Exit (das ist
|
||
der S/R-Close), aber Manager für die ~10 % Durchbruch-Runner (P≥Schwelle) + Fallback
|
||
für Trades ohne erreichbares Gegenlevel.
|
||
- **Umsetzung:** `engine._p_break(mom6,mom3,wt,dist)` (eingebettete `_PB_MU/SD/W`);
|
||
`wave_rec.snapshot` liefert **`pb_feats` {mom6, mom3, ema_diff, atr} — FEST aus M5**
|
||
(aus dem ~15-s-Turn-Fetch gecacht, ATR-Floor 0,12 wie im Training). ⚠ **Bugfix
|
||
2026-07-13:** vorher kamen die Features aus der Wave-TF (Heuristik bis M30 → mom6
|
||
war 3-h- statt 30-min-Momentum, ATR 2–3× → P falsch skaliert) und das Level aus dem
|
||
Misch-Set `_sr_levels` (Zonen/Gaps/Tageslevel). **2. Bugfix gleicher Tag:** die
|
||
M15-Cluster-Level (`market.sr`, SR_MIN_TOUCHES) sind in frischen Trends oft LEER →
|
||
Hint verschwand komplett. Jetzt **trainingsgleiche Level**: `wave.pb_levels` = rohe
|
||
**M5-Pivots (k=3, ~300 Bars)** aus dem Turn-Fetch (M5 dafür 320 Bars tief), Highs
|
||
in `_m5_hl` gecacht. Am Rally-Hoch gibt es für LONGs korrekt KEIN Gegenlevel →
|
||
kein Hint (nichts zu closen). ⚠ **3. Bugfix 2026-07-15:** das Close-Level kommt
|
||
jetzt aus **`_draw_levels`** (dieselbe geclusterte+Hysterese-Linie wie Chart &
|
||
Dashboard) statt aus den rohen `pb_levels` — sonst feuerte die Meldung an einem
|
||
anderen Level als die gezeichnete Linie (User: „passt nicht zusammen"). P(break)-
|
||
Features (M5) unverändert, nur `level`/`dist` verschieben sich um Cents.
|
||
**`_sr_close_hint`** (Anzeige ≠ Auslösung, Fix
|
||
2026-07-14 — das 0,15×ATR_M5-Fenster ≈ 0,03 $ war als reine Anzeige unsichtbar
|
||
klein): Hinweis erscheint ab **≤0,6×ATR Annäherung** (blaue „nähert sich"-
|
||
Vorwarnung mit Live-P(break) + `gap_atr`); **Auslösung `close=true` nur AM Level**
|
||
(≤0,15×ATR = gemessener Touch) und `p_break < [trading] sr_close_pbreak`
|
||
(Code-Default 0,60, Band 0,30–0,90; **ini seit 2026-07-24 = 0,55** auf User-Wunsch —
|
||
liegt im validierten Optimum-Plateau 0,50–0,60, Hysterese-Zone damit 50/60).
|
||
Snapshot `sr_close_hint {level, p_break,
|
||
at_level, gap_atr, close, confirm}`. Hinweis existiert NUR mit offener Position.
|
||
**`confirm` = Bruch-BESTÄTIGUNGS-Preis** (Fix 2026-07-22, User-Frage nach einem
|
||
Trade, der am Level geschlossen wurde, obwohl der Kurs „durchgebrochen" war):
|
||
`level ± 0,5×ATR` in Trade-Richtung — **dieselbe Definition wie beim Modell-
|
||
TRAINING** (`_BRK_ATR=0,5`/`_BRK_W=12` in `backtest_srclose_prob.py`/
|
||
`analyze_srwaves.py`). Ein Docht, der die nackte Levelzahl nur um wenige Cent
|
||
überschreitet, zählt NICHT als Durchbruch — reales Fallbeispiel 2026-07-22:
|
||
Close bei Level 85,943 (P(break) 31 %), Kurs wischte auf 85,994 (< Bestätigung
|
||
86,017 = Level+0,5×ATR) und fiel zurück → nach Modell-eigener Definition eine
|
||
ABLEHNUNG, keine Bestätigung; die Vorhersage war korrekt. `confirm` jetzt in der
|
||
Log-Zeile („— Bestätigung erst ab X") UND im Frontend-Hinweistext (`#pos-srhint`,
|
||
alle 3 Zweige: Vorwarnung/Close/Laufen) sichtbar — macht den Unterschied
|
||
„Kurs > Levelzahl" vs. „strukturell bestätigter Bruch" auf einen Blick klar.
|
||
**Live-Prognose-Tracking (2026-07-23, User-Wunsch „mitloggen wie oft richtig/
|
||
falsch"):** jeder frische Level-Touch (`at_level` False→True) loggt EINMAL die
|
||
aktuelle Vorhersage (`bounce` wenn P(break)<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`.
|
||
⚠⚠ **AUTOMATISCHER S/R-CLOSE = ABGESCHALTET (User-Vorgabe 2026-08-06,
|
||
`[trading] auto_sr_close = false` UND `runtime_state.json`).** Das ist eine
|
||
**Abweichung von der Messung**, kein Nachvollzug einer: mit dem am 31.07.
|
||
nachtrainierten P(break)-Modell schlägt der gegatete S/R-Close die
|
||
Trailing-Baseline in BEIDEN Hälften (Live-Schwelle 0,35 — H1 −149 gegen −506 =
|
||
**+357 R**, H2 +1308 gegen −110 = **+1418 R**, `backtest_pbreak_rvalue.py`).
|
||
⚠ Nicht mit dem **pauschalen** S/R-Close verwechseln, der 2× verworfen wurde —
|
||
das P(break)-Gate ist genau der Unterschied zwischen „Gewinner-Kappen" und dem
|
||
gemessenen Edge.
|
||
**Was abgeschaltet ist:** nur die automatische Ausführung (`_check_sr_close`
|
||
steigt in Zeile 1 aus). **Was bleibt:** der Hinweis in der App (`#pos-srhint`),
|
||
die P(break)-Chart-Linien, der Copilot-Kontext und das Live-Prognose-Tracking
|
||
(`pbreak_predictions`) — `_sr_close_hint` wird in `snapshot()` unabhängig vom
|
||
Schalter berechnet. **Was weiter schützt:** Broker-SL 2×ATR, Trailing, Time-Stop.
|
||
⚠ **Beide Stellen** mussten gesetzt werden — `runtime_state.json` überschreibt
|
||
die ini beim Start (die Falle, durch die `auto_signal` am 31.07. unbemerkt
|
||
weiterlief). Neu im **Config-Wächter** (`measurement_reminder.py`, validiert =
|
||
`true`), damit die Abweichung sichtbar bleibt. Backups:
|
||
`oil_widget_config.ini.bak-2026-08-06-srclose`, `runtime_state.json.bak-…`.
|
||
**Zurück:** Dashboard-Schalter `#srclose-btn` (setzt beide Stellen) oder
|
||
`auto_sr_close = true` in ini **und** `runtime_state.json`.
|
||
Historie: **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.
|
||
- **⚠⚠ SL-BASIS M5 vs. M15 auf der NEUEN Level-Entry-Population (gemessen
|
||
`backtest_sl_basis.py`, 2026-08-05, User-Frage „wäre M5-ATR vertretbar?") —
|
||
DIFFERENZIERT, und dabei eine Mess-Betriebs-Lücke gefunden.**
|
||
⚠ **Zuerst der strukturelle Fund:** `exit_model.simulate()` nimmt **EINEN** `atr`
|
||
für SL, TP, Trail und Breakeven — und **alle Backtests übergeben ATR_M5**. Live
|
||
kommt der SL dagegen aus **ATR_M15** (`mt5_utils.atr_value` mit `SL_TF`), im
|
||
Median **1,80×** so groß. Praktisch jede Projekt-Messung modelliert damit einen
|
||
SL von 2,0×ATR_M5, während live **~4,0×ATR_M5** gilt. Einzige bisherige Ausnahme:
|
||
`backtest_sl_width.py` (04.08.), das genau das explizit verglichen hat.
|
||
**Ergebnis auf Level-Entries (80k, 2 Halbjahre, nur die SL-Basis variiert):**
|
||
| Population | Variante | H1 ØR / Worst | H2 ØR / Worst |
|
||
|---|---|---|---|
|
||
| **SQUEEZE** | LIVE (≈4,1×ATR_M5) | +0,455 / **−5,87** | +0,614 / **−8,83** |
|
||
| | 2,5×ATR_M5 | +0,459 / −2,82 | +0,620 / −2,94 |
|
||
| | 2,0×ATR_M5 | +0,452 / −2,32 | +0,610 / −2,44 |
|
||
| **WELLE** | LIVE (≈3,9×ATR_M5) | **+0,371** / −4,97 | **+0,356** / −6,00 |
|
||
| | 2,5×ATR_M5 | +0,362 / −2,79 | +0,343 / −2,72 |
|
||
| | 3,0×ATR_M5 | +0,358 / −3,29 | +0,352 / −3,22 |
|
||
✅ **Ertragsseitig ist es ein WASH auf der Squeeze-Population** — alle Varianten
|
||
liegen innerhalb eines Standardfehlers (≈0,035 bei n≈800), und die einzige Zelle,
|
||
die den Live-Stand in ØR beidhälftig schlägt (2,5), hat **schlechtere Nachbarn**
|
||
→ nach der Nachbar-Regel Rauschen. **Auf der Wellen-Population ist der weite
|
||
Live-SL dagegen in BEIDEN Hälften der beste** — jede Verengung kostet dort.
|
||
⚠⚠ **Der EINZIGE robuste Unterschied ist das TAIL-RISIKO — und der ist groß und
|
||
monoton:** Worst-Case **−8,83×ATR** (live, Squeeze H2) gegen **−2,94** bei
|
||
2,5×ATR_M5, also **Faktor 3**. Bei 3,5 Lots und ATR_M5 0,33 sind das ~**−890 €**
|
||
gegen ~−300 € im einzelnen schlechtesten Trade.
|
||
✅ **Antwort: „vertretbar" ja — aber es ist keine Ertrags-, sondern eine
|
||
Risiko-Entscheidung.** Für den Squeeze kostet die Verengung gemessen nichts und
|
||
drittelt den Tail; für Wellen-/Signal-Trades kostet sie Ertrag in beiden Hälften.
|
||
Eine saubere Umsetzung wäre **quellen-abhängig** (`_calc_sl_tp` nutzt M5-ATR nur
|
||
bei `source="auto_squeeze"`) — strukturell begründet (Breakout- gegen
|
||
Pullback-Einstieg haben verschiedene Nachlauf-Dynamik), nicht data-mined.
|
||
✅✅ **GEBAUT & LIVE (2026-08-05, User-Entscheidung „für Squeeze-Trades: ja").**
|
||
`trader._calc_sl_tp(sym, otype, entry, atr_tf=None)` bekommt die ATR-Zeitebene als
|
||
Parameter (Default `SL_TF` = M15); `_send`/`_send_locked`/`open_long`/`open_short`
|
||
und `place_stop` reichen sie durch. Gesetzt wird sie an genau zwei Stellen:
|
||
`engine._open` (`source == "auto_squeeze"` → M5) und `_manage_squeeze_pending`
|
||
(`quelle == "squeeze"` → M5). **Alles andere — manuelle Trades, Auto-Signal —
|
||
bleibt unverändert bei M15.**
|
||
⚠ **Die Multiplikatoren sind NICHT angefasst** (1,8–2,2×ATR): nur die Zeitebene
|
||
wechselt, aus der der ATR kommt. Der Log macht es sichtbar („… ATR aus M5").
|
||
⚠⚠ **DER TP IST BEWUSST NICHT BETROFFEN** (User-Vorgabe: „nur für SL, der TP soll
|
||
mit dem Runner mitwachsen"). Geprüft statt angenommen: der von `_calc_sl_tp`
|
||
zurückgegebene TP wird von **KEINEM** Aufrufer gesendet — `_send_locked` und
|
||
`place_stop` setzen ausschliesslich `req["sl"]`. Das Ziel verwaltet
|
||
`core/trailing.py` dynamisch (`_TP_INIT_ATR`=3,5 × ATR der **Wellen**-Zeitebene)
|
||
und zieht es mit dem Lauf nach. Ein Warnkommentar im Code hält fest, warum der TP
|
||
dort nicht mit in die Order darf: er wäre sonst an die SL-Breite gekoppelt und
|
||
würde genau die Runner kappen, von denen der Squeeze lebt (43 % Treffer, Ertrag
|
||
im Tail).
|
||
Mit 4 Szenarien getestet (M5 kappt auf 2,2×ATR_M5=0,66 · M15 greift auf den
|
||
Mindest-Deckel 1,8×ATR=0,99 · M5 enger als M15 · SHORT spiegelbildlich).
|
||
⚠ Beim Testen eine eigene Fehlannahme korrigiert: ohne Pivot greift der
|
||
1,2-%-Fallback, der beim M15 vom **Mindest**-Deckel aufgeweitet wird — die
|
||
2,2×-Obergrenze wird dort gar nicht erreicht.
|
||
Zurück: die beiden `mt5.TIMEFRAME_M5`-Zuweisungen in `engine.py` auf `None`.
|
||
⚠ Das reproduziert NICHT `backtest_sl_tf_mismatch.py` (23.07., dort war M5 in H2
|
||
schlechter): dort wurde am Bar-Close eingestiegen, hier am Level. Der
|
||
Einstiegspreis hat den Befund gedreht.
|
||
- **S/R-verankerter SL = KEIN Edge (gemessen, `backtest_sl_method.py`):**
|
||
Pivot+Band vs. „S/R ∓ ATR/0,10-Puffer" vs. fest 2,0×ATR — **gedeckelt alle
|
||
identisch** (Ø-R 0,196–0,201, PF 1,38–1,39, Worst −2,2×ATR): der **Band-Cap
|
||
[1,8–2,2]×ATR dominiert**, die Verankerung ist Rauschen. Nur **ungedeckelt**
|
||
sieht S/R besser aus (Ø-R 0,249) — aber Worst-Case **−11,4×ATR** (Kontokiller
|
||
bei 90 %-Sizing) → verworfen. Aktuelle SL-Logik bleibt.
|
||
- **SL-ATR-Timeframe bei Squeeze-Trades (M5-Signal, aber SL_TF fix M15) = GEPRÜFT,
|
||
KEINE Änderung (gemessen `backtest_sl_tf_mismatch.py`, 2026-07-23, Auslöser
|
||
−89,36-€-Nacht-Trade):** `_calc_sl_tp` sizt den Initial-SL IMMER aus M15-ATR,
|
||
unabhängig vom Signal-TF — bei Squeeze (M5-Signal) kann das stark divergieren
|
||
(real: M5-ATR fiel 0,29→0,10 — genau die Kompression, die den Squeeze auslöste —
|
||
während M15-ATR noch bei ~0,36 lag → SL 0,787 statt ~0,22 bei M5-Sizing). Der
|
||
validierte Squeeze-Backtest (`backtest_breakout_squeeze.py`) nutzt durchgehend
|
||
M5-ATR für den SL — Live weicht hier vom Backtest ab. **2-Stichproben-Test der
|
||
Umstellung auf M5-ATR-SL bei Squeeze-Trades: GEMISCHT** — H1 leicht besser
|
||
(ØR +0,167 vs +0,064, ΣR +35 vs +13), H2 (größere, aktuellere Stichprobe) klar
|
||
SCHLECHTER (WR 51 % vs 59 %, ΣR +281 vs +323) → fällt nach der beidhälftig-
|
||
robust-Regel durch. Der breitere M15-SL kassiert gelegentlich einen vollen
|
||
Verlust wie diese Nacht, verhindert aber mehr vorzeitige Ausstoppungen bei
|
||
normalen Rücksetzern — netto vorteilhaft. **Keine Änderung.**
|
||
- **Inter-Market (Brent-Bestätigung / DXY-Gegenwind) = VERWORFEN (gemessen
|
||
`backtest_intermarket.py`, 2026-07-19, letzte ungetestete Indikator-Klasse):**
|
||
1366 Trades, seq. Sim, Live-Exit, Echtkosten, 2 Halbjahre; Buckets nach Zustand
|
||
am Entry (Symbole: `SpotBrent`, `USDX`). **Beide Hypothesen kippen zwischen den
|
||
Hälften:** (A) Brent-Trend dagegen H1 −0,002 vs H2 −0,187 (H1 sogar BESSER als
|
||
„dafür"!), Brent-Mom-1h dagegen H1 **+0,311**/H2 −0,233 = Flip; (B) DXY-Gegenwind
|
||
H1 −0,13…−0,19 (schlechter, wie vermutet) aber H2 **+0,13…+0,17 = BESSER als
|
||
Rückenwind** — komplett invertiert, klassische Regime-Abhängigkeit (wie H1-Stärke-
|
||
Filter/Momentum). Einzig Brent-Mom-3h ist richtungs-konsistent, aber winzig
|
||
(Δ~0,06–0,14) und die Schwester-Varianten kippen → nach der vorab festgelegten
|
||
Verdict-Regel (beide Hälften UND Varianten-robust) **kein Einbau**. Damit ist auch
|
||
die letzte orthogonale Indikator-Klasse durch: **am Signal ist nichts mehr zu
|
||
holen — Hebel bleiben Sizing/Exit/Verhalten** (nicht gegen das Signal handeln:
|
||
live gemessen 39 % WR / −166 € vs. mit 55 % / +65 €).
|
||
`backtest_confluence.py` — MACD/ADX-Richtung praktisch neutral, ADX-Stärke leicht
|
||
negativ, ein MACD+ADX-Gate senkt den Edge (−78 % Trades). RSI *bestätigend* ist
|
||
kontraproduktiv (RSI *gegen* Richtung = doppelter Edge = der Pullback-Effekt, den
|
||
der „⭐ tiefer Pullback"-Bonus schon nutzt). Standard-Oszillatoren also nicht
|
||
draufsatteln. Mehrquellen-Redundanz läuft bereits über die Gesamtempfehlung.
|
||
- **TradersUnion (TU) aus der Empfehlung ENTFERNT (User-Vorgabe 2026-07-06):** TU ist
|
||
Standard-Indikator-Konsens (MA/Oszillator-Ratings einer Website) — **dieselbe Klasse
|
||
wie MACD/ADX/RSI = kein Edge**, plus **Latenz** (5m „Strong Buy" während 4h/1d „Strong
|
||
Sell") und **nicht backtestbar** (Live-Scrape, keine History). Floss vorher als
|
||
Konfidenz-Faktor (`_tu_check` ±6/−8 je 5m/15m) UND als Verdict-Stimme ein — **beides
|
||
neutralisiert** (`wave_rec._build` zieht `_tu_check` nicht mehr; `_verdict` ohne
|
||
TU-Vote). ⚠ Nebenbefund: der Konfidenz-Pfad hatte **keinen** `_STALE_S`-Check (nur
|
||
`trade_signal` hatte ihn) — mit der Entfernung erledigt. TU bleibt **reine Anzeige**
|
||
(Snapshot `tu`); `_tu_check`/`_TU_*` als Code erhalten (reaktivierbar). Fetch war
|
||
auf 5 min gedrosselt (`_TU_S=300`); **Loop seit 2026-07-19 ganz stillgelegt**
|
||
(`_tu_loop` nicht mehr in `start()` — TU ist aus Empfehlung/Verdict/Copilot raus
|
||
UND die Kachel ausgeblendet, der Scrape lief für niemanden; reaktivieren = Loop
|
||
wieder in die Liste). Elliott loggt nur noch **bei Änderung** (vorher
|
||
~6k identische Zeilen/Logrotation).
|
||
- **Order ohne Empfehlung = bestätigbarer HINWEIS, kein Block:** Bei WARTEN/
|
||
Gegen-Signal zeigt das Frontend einen **Bestätigungs-Dialog** („… trotzdem
|
||
eröffnen?") — bestätigen = Trade läuft, Abbrechen = kein Trade. **Kein** hartes
|
||
Blockieren (überschreibbar). `engine._open` blockt nicht (loggt nur).
|
||
⚠ **Log-Quellen-Attribution (Fix 2026-07-29):** `_open(fn, want, source)` bekommt
|
||
jetzt eine `source` (Default `"manuell"`); der Auto-Squeeze ruft `open_long/short
|
||
(source="auto_squeeze")`. Vorher schrieb `_open` bei JEDEM Auftrag gegen die
|
||
Empfehlung „— vom User bestätigt" — auch bei autonomen Squeeze-Entries (der User
|
||
fragte, ob ein Trade auto/manuell war; die Zeile log fälschlich „User"). Jetzt:
|
||
autonome Orders → „autonom vom Auto-Squeeze eröffnet", manuelle → wie bisher. Die
|
||
`🚀 AUTO-SQUEEZE-ENTRY`-Zeile bleibt die eindeutige Quelle der Wahrheit.
|
||
- **Entry-Checkliste im Order-Dialog (`engine._entry_checklist`, User-Vorgabe
|
||
2026-07-30 „blende einen Hinweis ein wenn die Checkliste gegen meine Eröffnung ist",
|
||
v=120):** Vor JEDER manuellen Eröffnung prüft der Server 7 Punkte mit LIVE-Daten und
|
||
zeigt die verletzten im Bestätigungs-Dialog. **Blockt nicht**, macht aber sichtbar,
|
||
was dagegen spricht — und zwar **auch dann, wenn ein Signal vorliegt** (der alte
|
||
Dialog kam nur bei WARTEN/Gegen-Signal). Punkte: (1) **Signal-Deckung** (die
|
||
gemessene Kern-Leckage), (2) **HTF-Trend M30+H1** („nur mit dem HTF-Trend, niemals
|
||
dagegen"), (3) **Entry-Raum** zum Gegenlevel (<0,6×ATR = gemessen negativ),
|
||
(4) **News-Konflikt** (|Sentiment|≥0,5 gegen die Richtung), (5) **Kosten**
|
||
(Spread/ATR, ab 0,32 = Kostenfalle), (6) **Mental-Check: Verlust-Serie heute**
|
||
(„nach 2 Losses Session beenden" — zeigt Anzahl, Tages-P&L und %-Anteil am Konto),
|
||
(7) **Nacht-Stunde** 0–7 Uhr. `verdict`: `stop` (≥2 „fail") · `warn` · `ok`.
|
||
Snapshot `checklist {long, short}` — nutzt nur bereits berechnete Snapshot-Teile;
|
||
die DB-Abfrage für die Verlust-Serie ist ~120 s gecacht (`_loss_streak_today`).
|
||
⚠ Die Gewichtung ist **NICHT backtest-optimiert**, sondern aus den im Projekt
|
||
gemessenen Leckagen + der User-Checkliste abgeleitet (Entscheidungshilfe, kein
|
||
validiertes Signal). Real beim Einbau (30.07. abends): beide Richtungen `stop` —
|
||
„keine Empfehlung" · „HTF unklar/dagegen" · „11 Verluste heute (−261 € = 80 % des
|
||
Kontos)".
|
||
- **Liquiditäts-Sweeps (SMC: Equal Highs/Lows, Swing Liquidity) = VERWORFEN
|
||
(gemessen `backtest_liquidity_sweep.py`, 2026-07-30, User-Vorgabe mit Chart-
|
||
Beispielen + WTI-Praxisregeln):** Getestet, weil der **Sweep** (Fehlausbruch: Level
|
||
kurz durchstochen, Close zurück in der Zone) eine ANDERE Mechanik ist als der schon
|
||
verworfene Nackenlinien-Bruch (`backtest_doubletop.py`). Achsen: 3 Muster × Level-TF
|
||
(M5/M30/H1, die „Liquidität auf höherem TF"-Regel) × 2 Exits × 2 Halbjahre, plus
|
||
Session-Filter (NY 14–16 / Asia 2–4 Uhr Berlin). **Kernbefunde:** (a) Der
|
||
**SMC-eigene Exit** („Stop über dem Sweep-Docht, Ziel = Gegen-Liquidität") ist in
|
||
**ALLEN 9 Kombinationen negativ** — die Lehre steht und fällt mit ihm, er trägt
|
||
nicht; mit dem generischen Live-Trailing sieht dasselbe Setup besser aus (derselbe
|
||
Exit-Artefakt wie bei `backtest_patterns.py`). (b) Equal Highs/Lows fallen fast
|
||
überall durch (Vorzeichen kippt zwischen den Hälften). (c) Einziger Treffer:
|
||
„Swing-Liquidity, M30-Level, Live-Exit" (+0,241/+0,358) — aber die **Nachbar-TFs
|
||
kippen** (M5 −0,152/+0,247 · H1 −0,111/+0,692) = **nicht parameter-robust**, exakt
|
||
das ORB-Muster von 2026-07-17. (d) Session-Filter: zu wenige Trades (n=3–31) für
|
||
eine Aussage. **Kein Einbau in die Gesamtempfehlung. 16. verworfener Signal-
|
||
Eingriff.** ⚠ Die DXY-Korrelations-Regel wurde bewusst NICHT erneut getestet — die
|
||
Inter-Market-Klasse ist 2026-07-19 gemessen durchgefallen.
|
||
- **Fibonacci-Retracements = VERWORFEN (gemessen `backtest_fibonacci.py`, 2026-08-04,
|
||
User-Frage zu einem USOIL-Kommentar „ein Fibonacci-Retracement sollte den Kurs auf
|
||
79 $ drücken"):** Fib-Level waren im Projekt bis dahin **nie gemessen** — weder als
|
||
S/R noch als Einstiegszone. Aufbau wie beim P(break)-Training: Schwünge aus
|
||
**M15**-Pivots (die genannte Zeitebene), Berührung ≤0,15×ATR, Ausgang ±0,5×ATR in
|
||
12 M5-Bars, 80k Bars, 2 Halbjahre — mit ZWEI Kontrollgruppen.
|
||
| Haltequote | H1 | H2 |
|
||
|---|---|---|
|
||
| **Fibonacci** | 54,3 % | 54,5 % |
|
||
| (a) Zufalls-Level gleicher Distanz | **55,7 %** | 53,8 % |
|
||
| (b) M30-Pivots (Live-Quelle) | **56,6 %** | **55,4 %** |
|
||
**Handelbarkeit (Bounce am Level, kanonischer Exit, Echtkosten):** H1 ØR −0,168 /
|
||
PF 0,71 · H2 ØR −0,082 / PF 0,85 — beidhälftig negativ.
|
||
⚠⚠ **Der aufschlussreichste Wert ist die Zufalls-Kontrolle: ~55 % Haltequote.**
|
||
Ein BELIEBIGES Level hält in diesem Markt in gut der Hälfte der Fälle — das ist die
|
||
Basisrate kurzfristiger Mean-Reversion, keine Eigenschaft von Fibonacci. Die Frage
|
||
war nie „hält das Level?", sondern „hält es BESSER als eine willkürliche Linie?" —
|
||
und Fib tut das nicht. **Damit ist auch das Selbsterfüllungs-Argument entkräftet**
|
||
(„so viele schauen darauf, also wirkt es"): dann müsste Fib über der Zufallslinie
|
||
liegen; es liegt darunter bzw. gleichauf.
|
||
✅ **Bestätigt umgekehrt die Struktur-These:** M30-Pivots — Stellen, an denen real
|
||
gehandelt wurde — halten in BEIDEN Hälften besser als beide gerechneten Linien.
|
||
Dieselbe Trennlinie wie beim Regressionskanal (`backtest_structure_tf.py`, 24/24
|
||
negativ) und beim P(break)-Modell: **„Linie im Chart" ist nicht „Struktur im Markt".**
|
||
**21. verworfener Signal-Eingriff.** ⚠ Grenzen: eine Schwung-Definition (M15-Pivots
|
||
k=3, ≥2×ATR); die Pivot-Kontrolle ist dünner (n=173/224). Der Vergleich ist aber
|
||
design-intern, die relative Aussage damit robuster als die Absolutwerte.
|
||
- **Meta-Labeling (López de Prado) auf dem Wellensignal = VERWORFEN (gemessen
|
||
`backtest_metalabel.py`, 2026-08-05, aus der Web-Recherche „Best Practices"):**
|
||
Kein 23. Filter, sondern ein **Zweitmodell**, das für ein bereits erzeugtes Signal
|
||
P(Gewinn) schätzt — dieselbe Machinerie wie das erfolgreiche P(break)-Modell
|
||
(12 kausale Merkmale: Konfidenz · Überdehnung · HTF/H1-Übereinstimmung · Kosten ·
|
||
Vola-Regime · mom3/mom6 · Efficiency Ratio · Nacht · Entry-Raum · Winkel;
|
||
logistische Regression, Fit auf einer Hälfte, Test auf der anderen, **in BEIDEN
|
||
Richtungen**). Population = der volle Live-Gate-Stack (929 bzw. 1316 Signale).
|
||
**Fällt klar durch:**
|
||
| | Fit H1 → Test H2 | Fit H2 → Test H1 |
|
||
|---|---|---|
|
||
| **AUC oos** | **0,508** | **0,479** |
|
||
| Kalibrierung | 49,3 % vorhergesagt vs. **38,6 %** real | 37,2 vs. 38,0 % |
|
||
| Top-Bucket (p≥0,55) | **74,1 % vorhergesagt, 39,1 % real** | (existiert nicht) |
|
||
Münzwurf in beiden Richtungen (P(break) erreicht 0,65), und **8 von 12
|
||
Merkmalsgewichten kippen das Vorzeichen** zwischen den Fits — das Modell lernt
|
||
nichts Stabiles. Die im H1→H2-Lauf scheinbar „besseren" Schwellen (p≥0,45…0,55)
|
||
sind reine **Handelsvermeidung** (ΣR steigt nur, weil n fällt; ØR bleibt negativ)
|
||
und reproduzieren in der Gegenrichtung nicht. **23. verworfener Signal-Eingriff.**
|
||
⚠ Deckt sich exakt mit dem Caveat der Quelle (Hudson & Thames): *„if the algorithm
|
||
is bad then meta-labeling would likely only reduce the downside."* Das Wellensignal
|
||
ist in H1 gemessen negativ — ein Zweitmodell rettet ein schwaches Primärsignal nicht.
|
||
✅ **Was das positiv zeigt:** die Trennung „am Signal ist nichts zu holen, der Hebel
|
||
liegt in **Ausführung** und Verhalten" hält auch gegen die aktuelle Lehrmeinung.
|
||
Am selben Tag hat der Ausführungs-Hebel (Stop-Order statt Market) **+0,61 ØR**
|
||
gebracht — mehr als alle 23 Signal-Ideen zusammen.
|
||
- **„Erst messen, dann umbauen":** Strategie-Änderungen per Backtest belegen, nicht
|
||
raten. Gegen-Trend verliert; nicht bei WARTEN handeln. **Stichproben ~28 Tage** —
|
||
ein Trend-Regime; Befunde später auf mehr/andere History gegenprüfen.
|
||
- **Wave-Auto-Trader (Dry-Run) KOMPLETT ENTFERNT (2026-07-23, Code-Review-Fund):** war
|
||
seit Langem Verlierer (WR 29 % / PF 0,64) vs. manuelle Trades (WR 56–76 %); UI-Toggle
|
||
bereits vorher entfernt (Dashboard-Button steuert seitdem Auto-Squeeze), lief aber
|
||
weiter dormant bei jedem Tick mit (toter Ballast). `core/auto_trader.py` gelöscht
|
||
(Sicherung: `.removed_backup/`), `engine.self.auto` + alle 4 Aufrufstellen
|
||
(`_price_loop`, `_run_analysis`, `toggle_auto`, Snapshot-Keys `auto`/`min_conf`)
|
||
sowie die tote `auto`-Referenz in `TradingAgent.__init__` entfernt, `/api/auto`
|
||
gelöscht. `min_conf` (Frontend-Warnschwelle „Order ohne Signal") lebt jetzt als
|
||
eigene Konstante `_MIN_CONF_DISPLAY=70` in `engine.py`. Die `intended_trades`-DB-
|
||
Tabelle + `history.intended_*`-Methoden bleiben (historische Daten, kein Schreiber
|
||
mehr aktiv). Manuelles Handeln bleibt der validierte Edge auf dem Wave-Signal.
|
||
- **RE-TEST 2026-07-30 (User-Wunsch „Trades bei Empfehlung automatisch eröffnen") =
|
||
ERNEUT VERWORFEN, gemessen `backtest_auto_signal.py`:** Auto-Entry auf dem Wave-
|
||
Signal mit den HEUTIGEN Gates, echte `_build`-Logik (kein Nachbau), M30-/H1-Trend
|
||
aus M5 aggregiert und in `_build` gefüttert, sequentielle 1-Positions-Sim, Live-Exit
|
||
(SL 2,0/Trail 1,5/BE 1,3/Time-Stop 120 min), Echtkosten (Bar-Spread/ATR), 80k M5-Bars
|
||
in 2 Halbjahren. **ALLE 8 Varianten fallen durch** (Konf-Schwelle 55/65/70/75 ×
|
||
Nacht-Sperre an/aus): **H1 durchgehend NEGATIV** (ØR −0,076…−0,182, PF 0,78–0,90),
|
||
**H2 durchgehend positiv** (+0,112…+0,167) → klassisches Regime-Kippen, n=718–1167
|
||
je Hälfte (keine Stichproben-Frage). ⚠ **Kontroll-Beleg, dass es am SIGNAL liegt und
|
||
nicht an der Sim:** der Squeeze läuft durch DIESELBE Exit-/Kosten-Maschinerie und ist
|
||
in H1 **+0,187/PF 1,29** — die Simulation funktioniert also, nur das Wave-Signal trägt
|
||
in H1 nicht. Deckt sich exakt mit dem Live-Dry-Run (WR 29 %/PF 0,64). Nebenbefunde:
|
||
(a) höhere Konfidenz hilft NICHT monoton (75 schlechter als 65) — bestätigt
|
||
`analyze_verdict_calibration.py` (conf_pct ist NICHT kalibriert, in H2 invertiert),
|
||
ein „nur bei hoher Konfidenz"-Auto-Entry ist damit auch erledigt; (b) die Nacht-Sperre
|
||
verbessert konsistent (H1 −0,172→−0,123), reicht aber nicht; (c) ⚠ **Methodik-Lehre:**
|
||
ein Vorlauf mit nur 6k Bars zeigte „Konf≥65+Nacht TRÄGT" — der 80k-Lauf drehte das
|
||
komplett (n war 60–99/Hälfte = Rauschen). **Kleine Stichprobe hätte hier zum Fehl-
|
||
Einbau geführt.** Warum manuelle „MIT Signal"-Trades trotzdem profitabel sind
|
||
(+118 €/WR 56 %, `analyze_entries`-Auswertung 2026-07-30): **menschliche AUSWAHL**
|
||
unter den Signalen (Kontext/Tageszeit/Marktlage), nicht „alle nehmen" — die
|
||
Grundgesamtheit ALLER Signale ist der Verlierer. **14. verworfener Signal-Eingriff.**
|
||
- **Auto-Signal-Entry = TROTZDEM GEBAUT auf ausdrücklichen User-Wunsch (2026-07-30,
|
||
„probieren wir es trotzdem mal"), `[trading] auto_signal`, Default FALSE.** ⚠ **Der
|
||
Backtest direkt darüber sagt NEIN** (H1 durchgehend negativ) — der User hat das nach
|
||
vollständiger Information (inkl. Live-Dry-Run-Historie WR 29 %/PF 0,64) bewusst
|
||
entschieden. Gebaut mit den **gemessen BESTEN** Parametern, nicht mit Default-Raten:
|
||
`auto_signal_min_conf=75` (mit Nacht-Sperre H1 −0,053 = mildester Verlust, H2 +0,141
|
||
= bestes Netto; ⚠ NICHT weil „mehr Konfidenz besser" — conf_pct ist unkalibriert, 75
|
||
ist empirisch) und `auto_signal_skip_night=true` (verbessert JEDE Variante).
|
||
**Mechanik** (`engine._check_auto_signal` im `_pos_loop`, 1 s, NACH den Close-Checks):
|
||
eröffnet nur **FLAT**, nur bei `wave.signal()` LONG/SHORT mit `conf_pct ≥ min_conf`,
|
||
**1× je Signal-Episode** (Dedup `_signal_last_sig`, re-armt bei WARTEN/Flip — nicht
|
||
über den Preis wie beim Squeeze, weil das Signal keine Level-Marke hat); erbt die
|
||
Squeeze-Guards: Circuit-Breaker, Nacht-Sperre, S/R-Close-Cooldown, allgemeine
|
||
Wiedereinstiegs-Sperre. **Kein Nachkauf** bei gleichgerichteter Position, **kein
|
||
Drehen** einer Gegen-Position (unbelegt, wie Squeeze-Reverse). Log `🎯 AUTO-SIGNAL-
|
||
ENTRY` + Telegram, Setup-Tag `AUTOSIG_<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`.
|
||
⚠⚠ **ABGESCHALTET am 2026-08-01 (`auto_signal=false`, via Toggle → `runtime_state.json`).**
|
||
Zwei Gründe, KEINER davon die Live-Bilanz (n=4 = +21,79 € sagt nichts): (1) gemessen in
|
||
H1 durchgehend negativ, gebaut gegen die Messung; (2) **beide Auto-Pfade konkurrieren um
|
||
den EINEN Positions-Slot** — ein AUTOSIG-Trade blockiert einen Squeeze-Einstieg und
|
||
umgekehrt. Solange beide laufen, ist die B5-Abbruchprüfung des Auto-Squeeze (s. u.)
|
||
nicht sauber messbar. ⚠ Er lief bis dahin **unbemerkt**, obwohl die ini `false` sagte:
|
||
ein Dashboard-Toggle hatte `true` in `runtime_state.json` persistiert, und das
|
||
überschreibt die ini beim Start. Der Config-Wächter prüft das jetzt mit.
|
||
Reaktivieren = Button 🎯 SIG; dann gilt wieder: ≥20 AUTOSIG-Trades, Live-ØR/PF gegen
|
||
die Erwartung halten (H2-Regime +0,14 vs H1-Regime −0,05).
|
||
⚠⚠ **NEU GEMESSEN 2026-08-04 nach dem Reversal-Abschalten
|
||
(`backtest_auto_signal_norev.py`, 80k M5, 2 Halbjahre, Nacht-Sperre an, keine
|
||
15-Min-Regel):** Frage des Users — trägt der Auto-Signal-Entry mit den heutigen
|
||
Änderungen? **Antwort: nein, aber deutlich näher dran.**
|
||
| conf ≥ 75 (LIVE-Schwelle) | ØR | PF | n |
|
||
|---|---|---|---|
|
||
| H1 | **−0,047** | 0,94 | 678 |
|
||
| H2 | **+0,120** | 1,17 | 710 |
|
||
H1 bleibt negativ → **2-Stichproben-Regel weiterhin NICHT erfüllt**, und kein
|
||
Konfidenz-Schwellwert rettet das (55: −0,103 · 65: −0,111 · 75: −0,047).
|
||
✅ **Das Reversal-Abschalten wirkt aber konsistent** — Δ ØR durch die Abschaltung:
|
||
conf 55 **+0,015/+0,099** · conf 65 +0,001/+0,047 · **conf 75 +0,099/+0,087**.
|
||
An der Live-Schwelle verbessern sich BEIDE Hälften um ~+0,09; H1 geht von −0,145
|
||
auf −0,047, PF von 0,82 auf 0,94. Erste Bestätigung der Abschaltung auf dem
|
||
GESAMTEN Signal (bisher nur der isolierte Trigger).
|
||
⚠⚠ **Dabei gefunden — die Messung vom 30.07. war strukturell unvollständig:**
|
||
`backtest_auto_signal.py` ruft `_build` **ohne `angle=`** auf → Default 90,0 →
|
||
`ad = 0`. Damit konnte dort **weder der Reversal-Zweig feuern noch der
|
||
Winkel-Konfidenzterm (±15 Punkte) greifen** — obwohl die Docstring „echte
|
||
`_build`-Logik inkl. Reversal-Zweig" behauptet. Wirkung: der Winkelterm allein hebt
|
||
H1 bei conf 75 von −0,123 auf −0,047. **Deployment-Drift Fall 5** (s. Tabelle).
|
||
Die Originaldatei bleibt unverändert (ihre Zahlen sind dokumentiert); die live-treue
|
||
Variante steht als `backtest_auto_signal_norev.py` daneben und sollte künftig die
|
||
Referenz sein.
|
||
- **⚠⚠ AUTO-SIGNAL NEU GEMESSEN 2026-08-05 (`backtest_auto_signal_v3.py`) — ZUM
|
||
ERSTEN MAL BESTEHT ER, ABER NUR MIT EINEM ANDEREN EINSTIEGSPREIS.**
|
||
Anlass: User-Wunsch nach einer ausführlichen Neumessung „mit den neuen Änderungen".
|
||
Geändert seit dem 04.08. waren Nacht-Fenster 0–7→3–6 und — der eigentliche Punkt —
|
||
der **Ausführungs-Befund vom selben Tag**: `_confirm_breakout` hat ein LEVEL
|
||
(`_pend["level"]` = Anker ± k×ATR), an dem eine ruhende Stop-Order liegen könnte.
|
||
ALLE bisherigen SIG-Messungen sind am Bar-Close eingestiegen.
|
||
Voller Live-Gate-Stack (echte `_build`/`_confirm_breakout`/`_room_gate`),
|
||
sequentiell EIN Slot, Live-Dedup (1× je Episode), kanonischer Exit, Echtkosten,
|
||
80k M5, 2 Halbjahre:
|
||
| Konf | **A) Market (Bar-Close)** H1 / H2 | **B) am Bestätigungs-Level** H1 / H2 |
|
||
|---|---|---|
|
||
| ≥55 | −0,098 / −0,093 | **+0,349 / +0,331** |
|
||
| ≥65 | −0,092 / −0,112 | **+0,357 / +0,317** |
|
||
| **≥75 (live)** | −0,050 / −0,189 | **+0,380 / +0,272** |
|
||
| ≥85 | −0,118 / −0,234 | **+0,351 / +0,279** |
|
||
**A ist in ALLEN acht Feldern negativ** (bestätigt die drei Vormessungen).
|
||
**B besteht in ALLEN acht** — Trefferquote 39,6 → 53,2 %, PF 0,83 → 2,20, und
|
||
**alle vier Nachbar-Schwellen halten mit** (Parameter-Robustheit, an der ORB und
|
||
die SMC-Sweeps gescheitert sind).
|
||
⚠ **HÄRTETEST Fill-Slippage** (Konf≥65) — eine Stop-Order füllt zum Markt, der
|
||
Idealpreis ist die Obergrenze: +0,05 → **+0,315/+0,249** · +0,10 → +0,273/+0,197 ·
|
||
**+0,20 → +0,148/+0,090** (noch beidseitig positiv) · **+0,31 → +0,026/−0,031**.
|
||
✅ **Die letzte Zeile ist die interne Kontrolle:** bei Slippage = dem gemessenen
|
||
Median-Abstand (0,311×ATR) konvergiert B gegen A — das Modell prüft sich selbst.
|
||
Der Kipp-Punkt liegt bei ~0,25–0,30×ATR Fill-Slippage.
|
||
**Abstand Bar-Close ↔ Bestätigungs-Level: Median +0,311×ATR** (Ø +0,422, Max
|
||
+5,29) — praktisch derselbe Wert wie beim Squeeze live (+0,275). Gleicher
|
||
Mechanismus, gleiche Größenordnung.
|
||
⚠ **Nacht-Fenster ist hier nahezu irrelevant:** 3–6 (+0,380/+0,272) · 0–7
|
||
(+0,371/+0,226) · keine Sperre (+0,359/**+0,302**) — alle drei bestehen, ohne
|
||
Sperre ist H2 sogar am besten. Deckt sich mit `backtest_cost_gate.py`.
|
||
⚠⚠ **GRENZEN — bewusst NICHTS umgeschaltet:**
|
||
(1) **Wo LIVE zwischen A und B liegt, ist für SIG NICHT verifizierbar** — beim
|
||
Squeeze gab es 34 echte Entries zum Nachrechnen, hier sind es 4. Die Empfehlung
|
||
macht das zwar gegenstandslos (eine ruhende Order IST B per Konstruktion), aber
|
||
behauptet ist es damit nicht.
|
||
(2) **Variante A ist STRENGER als live:** für die Vergleichbarkeit steigen beide
|
||
Varianten nur im Bar der frischen Bestätigung ein (`frisch`); live darf
|
||
`_check_auto_signal` auch später feuern. A ist ohnehin negativ, die Folgerung
|
||
bleibt — aber A ist nicht exakt das Live-Verhalten.
|
||
(3) **Operativ deutlich schwieriger als beim Squeeze:** die Squeeze-Box steht über
|
||
viele Bars still, `_pend` dagegen **verankert sich neu**, sobald der Kurs k×ATR
|
||
dagegen läuft, nach 12 Bars Timeout ODER bei Richtungswechsel. Eine ruhende Order
|
||
müsste ständig nachgezogen werden (Broker-Last, mehr Ablehnungen am
|
||
Mindestabstand) — und **wenn sie nicht platzierbar ist, landet man wieder bei A**,
|
||
also im gemessen negativen Fall. Wie oft das passiert, ist ungemessen.
|
||
(4) `_pend` wird aktuell **nicht im Snapshot veröffentlicht** — das wäre die erste
|
||
Bauarbeit.
|
||
✅✅ **PENDING-PFAD FÜR SIG GEBAUT & LIVE (2026-08-05, User-Vorgabe),
|
||
`[trading] signal_pending_entry=true`.** SIG läuft damit auf Variante B statt A.
|
||
⚠⚠ **Der heikelste Punkt war NICHT die Order, sondern die Kollision der beiden
|
||
Pfade.** `trader.pending_orders()` liefert ALLE magic-gleichen Orders — zwei
|
||
getrennte Manager (Squeeze/Signal) hätten sich **gegenseitig die Orders
|
||
storniert**, sporadisch und im Log kaum erkennbar. Deshalb **EIN gemeinsamer
|
||
Ziel-Zustand**: `engine._pending_ziel()` liefert `{order_type: preis}` + Quelle,
|
||
`_manage_squeeze_pending` gleicht nur noch ab. **VORRANG: Squeeze > Signal** — der
|
||
Squeeze ist unabhängig validiert und selten (~2–3×/Woche), das Signal feuert
|
||
dauernd; bei EINEM Positions-Slot ist die Reihenfolge damit eindeutig.
|
||
⚠ **Zwei additive Ergänzungen in `wave_rec._confirm_breakout` waren nötig:**
|
||
(1) Das Bestätigungs-Level wird jetzt **auch nach der Bestätigung** veröffentlicht
|
||
(`snap["breakout"]["level"]`, `confirmed=True`) — die Nachjagd-Bremse im
|
||
Market-Fallback braucht es genau dann. `pending` bleibt False, das Frontend rendert
|
||
nur bei `pending` und ist unberührt. (2) **`conf`** = die Konfidenz VOR dem
|
||
Stummschalten: der Rückgabewert setzt `conf_pct` auf 0 (das Signal gilt ja noch
|
||
nicht), die ruhende Order müsste sonst **blind** platziert werden. Damit wird die
|
||
Mindest-Konfidenz jeden Tick geprüft und die Order storniert, sobald sie fällt —
|
||
strenger als der Live-Check von vorher (der prüfte nur im Moment des Entries).
|
||
⚠ **Nachjagd-Bremse auch für SIG** (`squeeze_max_chase_atr`, 0,20): greift die
|
||
Pending nicht, wird nur noch bis 0,20×ATR über dem Bestätigungs-Level nachgekauft —
|
||
der Härtetest zeigt dort +0,148/+0,090, ab ~0,25–0,30 kippt es.
|
||
⚠ **Der wandernde Anker ist die operative Hauptlast:** `_pend` verankert sich bei
|
||
jeder k×ATR-Gegenbewegung, nach Timeout und bei Richtungswechsel neu — die Order
|
||
wird dann nachgezogen (Toleranz 0,02×ATR gegen Order-Churn). **Mit 12 Szenarien
|
||
getestet** (Vorrang · reiner Signal-Fall · SHORT · Konfidenz zu niedrig · Wechsel
|
||
Signal→Squeeze mit Storno · wanderndes Level · Toleranz · Gate · Fill · Feature aus
|
||
· Bestätigung eingetreten · Schonfrist). Live verifiziert am Snapshot:
|
||
`signal_pending=true`, `breakout` liefert `level`/`conf`/`confirmed`;
|
||
`pending_levels` leer, weil eine Position offen war = korrektes Verhalten.
|
||
Snapshot-Felder umbenannt: `squeeze_pending_levels` → **`pending_levels`** +
|
||
**`pending_quelle`**. Zurück: `signal_pending_entry=false`.
|
||
⚠ **B4-Pflicht:** dieselbe Erfolgskontrolle wie beim Squeeze — der Abstand
|
||
Einstieg↔Level muss gegen 0 gehen. AUTOSIG-Trades sind über `setup=AUTOSIG_*`
|
||
separierbar.
|
||
⚠⚠ **ZWEITER Methodik-Fehler, am selben Tag gefunden und die Messung WIEDERHOLT:
|
||
`set_entry_room(0.6)` fehlte.** `WaveRecommender.__init__` setzt
|
||
`_entry_room_atr = 0.0`, und `_room_gate` gibt dann `rec` **unverändert** zurück —
|
||
der Aufruf sieht im Code aus wie ein Gate, ist aber ein No-op. Betroffen waren
|
||
DREI am 05.08. neu gebaute Skripte (`backtest_auto_signal_v3`, `_metalabel`,
|
||
`_dist_gated`); die Referenz `backtest_breakout_gated.py` (04.08.) macht es richtig.
|
||
Aufgefallen ist es erst, weil `entry_room` in der Verteilung mit **0 %** auftauchte,
|
||
live aber 35 % blockt. **Neu gerechnet ändert sich die Population stark (n 967→295
|
||
bzw. 1327→524), die Schlussfolgerung aber NICHT:** Market bleibt in 7 von 8 Feldern
|
||
negativ, am Level bestehen weiterhin **alle vier** Konfidenz-Schwellen
|
||
(ØR +0,392/+0,356 · PF 2,37/2,39), und der Slippage-Härtetest hält bis 0,20×ATR
|
||
(+0,177/+0,118). **Die heutige Bau-Entscheidung ist damit gedeckt.**
|
||
⚠ Lehre: ein aufgerufenes Gate ist nicht dasselbe wie ein aktives Gate. Beim
|
||
Kopieren einer Backtest-Struktur gehört die **Konfiguration** mitkopiert — sonst
|
||
misst man einen Stack, den es nicht gibt.
|
||
⚠ **Methodik-Fehler beim Bau, gefunden und behoben:** der erste Entwurf liess
|
||
`pend["level"]` nach der Bestätigung über weitere Bars stehen — bei verzögertem
|
||
Einstieg (Slot belegt, Konfidenz zu niedrig) wäre er zu einem Preis eingestiegen,
|
||
den der Markt längst verlassen hatte. Kein Look-ahead, aber **unerreichbar**, und
|
||
sichtbar an einem unplausiblen Bruch zwischen Konf 65 und 75 (+0,33 → −0,12).
|
||
Behoben über das `frisch`-Flag. **Lehre: ein Preis, der einmal galt, ist keine
|
||
Einstiegsmöglichkeit — nur der Moment der Auslösung zählt.**
|
||
- **⚠⚠ 15-MINUTEN-REGEL GANZ ABGESCHALTET (`adverse_15min_atr = 0`, 2026-07-31,
|
||
User-Entscheidung nach der Messung).** Auf die User-Frage „vielleicht die Auto-Close
|
||
auf 30 min setzen?" wurde die **ZEIT-Achse** mitgemessen
|
||
(`backtest_adverse15_squeeze.py`, 15/30/45/60 min × 0,5/0,8×ATR, BEIDE Populationen,
|
||
kanonischer Exit, Echtkosten, 2 Halbjahre):
|
||
| Zeitpunkt | Schwelle | Squeeze Δ H1/H2 | Wave Δ H1/H2 |
|
||
|---|---|---|---|
|
||
| 15 min | 0,5 | −18 / −34 ← war live | −25 / −41 |
|
||
| 30 min | 0,5 | −20 / −35 | −22 / −13 |
|
||
| 45 min | 0,8 | −12 / −18 | −3 / −7 |
|
||
| 60 min | 0,8 | −8 / −10 | −3 / −3 |
|
||
**ALLE 16 Kombinationen sind in BEIDEN Hälften negativ** — die Regel wird nur
|
||
*monoton weniger schädlich*, je später und lockerer sie prüft. Die „beste" Variante
|
||
ist praktisch die, die nie feuert. **Es gibt keine Einstellung, bei der sie hilft**
|
||
→ 30 min hätte den Schaden halbiert, nicht beendet. Deshalb ganz aus.
|
||
⚠ **Damit ist auch die frühere Einschätzung „auf dem Auto-Signal-Pfad ~neutral"
|
||
korrigiert**: sie stützte sich auf `backtest_auto_signal.py` (eigenes Exit-Modell,
|
||
+0,001 vs +0,026); mit dem kanonischen Exit sind es −25/−41. Sechster Fall desselben
|
||
Musters an einem Tag (gemessen unter X, betrieben unter Y).
|
||
Der Schutz-Stack bleibt vollständig: **Broker-SL 2×ATR + Trailing + Time-Stop 120 min**.
|
||
Die Squeeze-Ausnahme (unten) bleibt im Code — falls die Regel je reaktiviert wird,
|
||
gilt sie weiterhin nicht für den Squeeze.
|
||
- **⚠ 15-MINUTEN-REGEL: SQUEEZE-TRADES AUSGENOMMEN (gemessen
|
||
`backtest_adverse15_squeeze.py`, 2026-07-31).** Die Schwelle 0,5×ATR wurde in
|
||
`backtest_auto_signal.py` auf **WAVE-SIGNAL-Entries** kalibriert und danach auf ALLE
|
||
Bot-Trades angewandt — wieder ein Kalibrierungs-Bruch (gemessen unter X, betrieben
|
||
unter Y). Auf SQUEEZE-Entries schadet die Regel bei **JEDER** Schwelle in **BEIDEN**
|
||
Hälften: Δ gegen „ohne Regel" 0,3 → −25/−38 · **0,5 → −18/−34** · 0,8 → −12/−39 ·
|
||
1,2 → −5/−18. **Monoton** — also ist die Regel selbst das Problem, nicht ihre
|
||
Einstellung. Grund: der Squeeze ist runner-abhängig (43 % Treffer, Ertrag im Tail),
|
||
ein Schnitt nach 15 min trifft genau den Tail. Ohne Regel H1 −54 / H2 **+220**, mit
|
||
0,5 H1 −72 / H2 +186.
|
||
**Anlass (real, 2026-07-31):** drei Bot-Trades wurden per `adverse15` geschlossen —
|
||
der **Auto-Signal**-Trade um 11:44 (−26,31 €) und die beiden **Squeeze**-Trades um
|
||
20:15/20:36 (−13,77 / −28,09 €).
|
||
⚠⚠ **DIE URSPRÜNGLICHE ANEKDOTE DAZU WAR FALSCH — korrigiert 2026-08-01.** Hier stand,
|
||
der 11:44-Trade sei „gerettet" worden (Kurs lief 32 € weiter dagegen) und die beiden
|
||
Squeeze-Trades seien „zu früh gekappt" worden (+19,28 / +17,60 € möglich). Beide Zahlen
|
||
waren **maximale Auslenkung** (bester bzw. schlechtester danach berührter Kurs) — also
|
||
das, was der Markt hergab, NICHT das, was der Exit gefangen hätte. Mit dem kanonischen
|
||
Exit nachgerechnet (`core/exit_model.py`, M5 aus `candles_m1`):
|
||
| Trade | IST (mit Regel) | ohne Regel, Trail 1,5 |
|
||
|---|---|---|
|
||
| 11:29 AUTOSIG | −26,31 | **−21,43** (also NICHT gerettet) |
|
||
| 20:00 SQUEEZE | −13,77 | **−23,27** (also NICHT zu früh) |
|
||
| 20:21 SQUEEZE | −28,09 | −25,02 |
|
||
Über alle 7 Bot-Trades des Tages hätte das Abschalten der Regel **−16,42 € gekostet**;
|
||
die Regel hat an DIESEM Tag also geholfen. ⚠ **Die Entscheidung, sie abzuschalten,
|
||
bleibt trotzdem richtig** — sie ruht auf `backtest_adverse15_squeeze.py` (alle 16
|
||
Kombinationen in BEIDEN Hälften negativ), nicht auf einem Tag mit n=3. Lehre: eine
|
||
Einzeltag-Anekdote taugt nicht als Beleg, auch nicht als *bestätigende* — und
|
||
„maximale Auslenkung" ist keine erzielbare Zahl. Der Gewinn des Tages kam woanders her:
|
||
die Umstellung **Trail 1,5 → 1,0** brachte auf denselben 7 Trades **+24,86 €**.
|
||
**Umsetzung:** `_bot_open_source` merkt sich beim Öffnen, WOMIT der Bot eröffnet hat
|
||
(`auto_squeeze` | `auto_signal`); `_check_adverse15` steigt bei `auto_squeeze` aus.
|
||
Mit 7 Szenarien getestet (Signal/Squeeze/manuell/altes Ticket · zu früh · unter
|
||
Schwelle · im Plus). **Für den Auto-Signal-Pfad bleibt sie aktiv** — dort ist sie
|
||
gemessen ~neutral (ØR +0,001 mit vs +0,026 ohne), also eine **Versicherung mit
|
||
kleiner Prämie**: hilft im schlechten Regime, kostet im guten. `=0` schaltet sie ganz aus.
|
||
- **15-Minuten-Regel (`[trading] adverse_15min_atr=0.5`, AKTIV, User-Wunsch 2026-07-30
|
||
„wenn der Trade nach 15 Minuten gegen die Richtung läuft schließe ihn"):** steht ein
|
||
**vom BOT eröffneter** Trade 15 min nach Entry ≥ X×ATR_M5 gegen die Richtung →
|
||
schließen (`engine._check_adverse15`, `closed_by=adverse15`, Log ⏱ + Telegram).
|
||
⚠ **Schwelle GEMESSEN kalibriert, NICHT die ursprüngliche Vorgabe** (`backtest_auto_
|
||
signal.py`, 15-Min-Achse): „sobald im Minus" (0,0) ist die **SCHLECHTESTE** Variante
|
||
(WR bricht 39→31 %, Netto-ØR −0,067 vs +0,026 ohne Regel) — sie kappt Trades, die
|
||
sich erholt hätten. **0,5×ATR ist die beste Regel-Variante** (H1 −0,111→−0,079).
|
||
Netto über beide Hälften bringt die Regel ~nichts (+0,001 vs +0,026 **ohne**) → sie
|
||
ist ein **REGIME-Schutz** (hilft im schlechten Regime, kostet im guten), bewusst als
|
||
Sicherheit akzeptiert. **2. unabhängige Bestätigung** von `backtest_adverse_nosignal.py`
|
||
(dort fiel der Adverse-Exit bei JEDER Schwelle durch). Greift **NICHT bei manuellen
|
||
Trades** (`_bot_open_ticket`-Marker — dort bleibt die Diskretion beim User, gemessen
|
||
sein Vorteil) und nicht in der Startup-Schonfrist; **1× je Ticket** geprüft
|
||
(`_adverse15_done_ticket`) genau beim 15-min-Zeitpunkt. Snapshot `adverse15_atr`/
|
||
`adverse15_count`. 0 = aus. Mit **18 synthetischen Szenarien getestet** (9 Entry:
|
||
Konf-Gate, Dedup, WARTEN-Re-Arm, Nachkauf, Gegen-Position, Cooldown, Toggle,
|
||
Nacht-Sperre · 9 Adverse: über/unter Schwelle, zu früh, im Plus, SHORT-Richtung,
|
||
manueller Trade, aus, 1×-je-Ticket, Startup-Grace) — alle korrekt.
|
||
- **⚠⚠ ZUVERLÄSSIGKEITS-PRÜFUNG 2026-08-11 (`backtest_squeeze_woche.py`, User:
|
||
„funktioniert der Breakout-Auto-Trader zuverlässig? simuliere die letzte Woche").**
|
||
⚠⚠ **BEFUND 0 beantwortet die Frage vorweg: DER TRADER IST AUS.**
|
||
`runtime_state.json` enthält `"auto_squeeze": false` und **überschreibt die ini**
|
||
(`= true`) beim Start — dieselbe Falle wie am 31.07. bei `auto_signal`, nur mit
|
||
umgekehrtem Vorzeichen. Letzter Squeeze-Trade **06.08. 13:52**,
|
||
`squeeze_entry_count = 0`. ✅ Der Config-Wächter meldet es korrekt („ist 'false'
|
||
— validiert gegen 'true'"); es wurde nur nicht gehandelt. Zurück: 🚀 BRK.
|
||
✅ **BEFUND 1 — die MECHANIK arbeitet korrekt.** Die Simulation nutzt die echte
|
||
`_squeeze_one` und reproduziert **10 von 11 Live-Trades binnen 10 min und auf
|
||
wenige Cent genau** (live 06.08 02:11 SELL 75,127 ↔ Sim 02:10 SHORT 75,132).
|
||
Die Erkennung ist nicht das Problem.
|
||
**BEFUND 2 — die Woche** (ruhende Stop-Order, Fill bei BERÜHRUNG, Nacht 3–6
|
||
gesperrt, EIN Slot, kanonischer Exit, Echtkosten):
|
||
| Fill-Slippage | n | Treffer | ØR | PF | ΣR |
|
||
|---|---|---|---|---|---|
|
||
| **0,00×ATR** | 47 | 57 % | **+0,196** | 1,37 | +9,20 |
|
||
| 0,10×ATR | 47 | 57 % | +0,100 | 1,17 | +4,69 |
|
||
| **0,20×ATR** | 46 | 50 % | **−0,013** | 0,98 | −0,58 |
|
||
≈ **+610 €** bei 2,5 Lots, ØR im validierten Band (+0,14…+0,23). ⚠⚠ **Drei
|
||
Vorbehalte:** **(1)** die Sim **überhandelt um das 2,4-fache** (26 statt 11
|
||
Einstiege in der An-Phase) — ihr fehlen Live-Dedup (dir, level), 3-min-
|
||
Wiedereinstiegssperre und S/R-Cooldown; ΣR ist nach oben verzerrt. **(2)** der
|
||
Edge **stirbt bei 0,20×ATR Fill-Slippage**, und live lag der Abstand
|
||
Einstieg↔Level vor dem Pending-Umbau bei Median **+0,275×ATR** — alles hängt
|
||
daran, ob die ruhenden Orders am Level füllen (`squeeze_entry_gap`: **8/12**).
|
||
**(3)** eine Woche ist Rauschen — die Phase, in der der Trader **AUS** war, ist
|
||
sogar die bessere (ØR +0,287 gegen +0,122).
|
||
⚠⚠ **BEFUND 3 — der GEPAARTE Vergleich widerspricht dem B4-Befund vom 01.08.**
|
||
Auf **denselben 10 Einstiegen**: **LIVE −109,52 €** gegen **kanonischer Exit
|
||
−179,27 €** — der Live-Exit war **70 € BESSER**. Am 01.08. hiess es „die Lücke
|
||
entsteht im EXIT"; hier ist es umgekehrt. Im Detail rettete der **S/R-Close**
|
||
drei Trades (05.08 08:33 **+12,56 gegen −113,64** · 06.08 13:51/52 je
|
||
**+0,26/+2,88 gegen −86,38**), während der kanonische Exit bei 06.08 09:04
|
||
(**−46,82 gegen −175,21**) und 02:11 (**+23,83 gegen −43,29**) klar besser war.
|
||
**Gemischt, n=10, von wenigen Trades dominiert — kein Urteil, aber ein
|
||
Warnschild gegen den umgekehrten Schluss.**
|
||
**BEFUND 4 — B5: 11 von 20 Trades (55 %).** Beide Bedingungen sind bereits weit
|
||
verletzt (je Lot ØGewinn 5,20 · ØVerlust 31,12 → **Verhältnis 0,17**, **PF
|
||
0,45**) bei **73 % Trefferquote** — die dokumentierte Squeeze-Signatur. ⚠ Formal
|
||
**nicht fällig**; die Latte nachträglich zu senken wäre genau der Fehler, gegen
|
||
den sie vorab fixiert wurde.
|
||
⚠ **OFFEN, nicht verifiziert:** am 06.08. **13:51 und 13:52** stehen ZWEI
|
||
Live-BUYs zum identischen Preis 76,315 auf EINEN Ausbruch (beide Pending-Fills,
|
||
beide binnen einer Minute per `sr_close` geschlossen) — sieht nach
|
||
**Doppel-Einstieg auf ein Signal** aus. Ungeprüft, ob Defekt im OCO-Ersatz oder
|
||
Teilfüllung.
|
||
- **⚠⚠ AUTO-SQUEEZE B4-BEFUND 2026-08-01: der EINSTIEG ist intakt, der EXIT hat den
|
||
Edge gefressen — deshalb NICHT zurückgebaut, aber mit vorab fixierter Abbruchregel.**
|
||
Anlass war die User-Frage zu den negativen Auto-Trades vom 31.07. Der B4-Monitor
|
||
trennt beides sauber:
|
||
| | Trefferquote | ØR | PF |
|
||
|---|---|---|---|
|
||
| **Mechanik** (Regel neu gerechnet, Backtest-Exit = isolierter Einstieg) | 46 % | **+0,102** | 1,15 |
|
||
| **Live** (n=31, 17.–31.07., Gesamtsystem mit echten Exits) | 45 % | **−0,242** | 0,49 |
|
||
Die **Trefferquote stimmt praktisch überein (46 vs 45 %)** — das Setup findet also
|
||
weiterhin, was es finden soll. Die Lücke von **0,34 R/Trade** entsteht komplett im
|
||
Exit: Ø-Gewinn **+11,02 €** gegen Ø-Verlust **−19,83 €** (Verhältnis 0,56, ΣEUR
|
||
**−162,99**). Der Squeeze ist runner-abhängig (43 % Treffer tragen nur, wenn die
|
||
Gewinner deutlich größer sind) — genau die Läufer wurden gekappt.
|
||
**Alle drei Ursachen sind am 31.07. behoben worden**, der Live-Zeitraum liegt also
|
||
fast vollständig VOR den Reparaturen: (a) das **P(break)-Modell** schloss 91 % aller
|
||
Level-Berührungen = faktisch pauschaler S/R-Close, und der ist 2× als Gewinner-Kappen
|
||
gemessen → nachtrainiert; (b) die **15-Min-Regel** schnitt Squeeze-Trades, auf dieser
|
||
Population in beiden Hälften schädlich → aus; (c) **Trailing 1,5–3,0 je nach TF**
|
||
statt der gemessenen 1,5 (Deployment-Drift Fall 3) → einheitlich 1,0.
|
||
⚠ **Ehrlich:** die Mechanik liegt mit ØR +0,102 **unter** dem validierten Band
|
||
+0,14…+0,23 — der Einstieg ist also auch selbst schwächer geworden (Regime). Das ist
|
||
kein Freibrief, sondern der Grund für eine harte Latte statt unbegrenztem Zuwarten.
|
||
✅ **B5-ABBRUCHREGEL, VORAB FESTGELEGT (User-Zustimmung 2026-08-01):** nach **≥20
|
||
Squeeze-Trades ab dem 01.08.** — **Verhältnis (ØGewinn/ØVerlust) < 1,0 ODER PF < 1
|
||
→ `auto_squeeze=false`.** Bewusst VOR der Beobachtung fixiert, damit die Latte
|
||
hinterher nicht verschoben wird; überwacht als Messung `squeeze_b5` im
|
||
`measurement_reminder.py`. Nacht-Anteil der Altdaten: 12 der 31 Trades / −78,92 € —
|
||
die stammen aus der Zeit vor dem Nacht-Guard (seit 24.07. wieder an).
|
||
- **⚠⚠ DEPLOYMENT-DRIFT FALL 7 — DER SQUEEZE WIRD ZU EINEM PREIS GEHANDELT, DEN DIE
|
||
VALIDIERUNG NIE UNTERSTELLT HAT (gemessen `backtest_squeeze_entry.py`, 2026-08-05).**
|
||
Anlass: User-Frage zu einem Auto-Trade, der „am höchsten Punkt einer aufsteigenden
|
||
Kerze" eröffnet wurde (05.08. 08:33:20 — Level 75,757, Einstieg **75,822**, also
|
||
über dem Hoch 75,808 der laufenden M5-Kerze; die schloss dann bei 75,693).
|
||
**`backtest_breakout_squeeze.py` steigt AM LEVEL ein** (`sim(lvl, …)`). Live schickt
|
||
`_check_auto_squeeze` eine **MARKET**-Order, sobald `state=='active'` — der Kurs ist
|
||
dann schon ein Stück weiter. **Aus 34 echten Live-Entries (Logs 16.07.–05.08.,
|
||
Level aus der Log-Zeile × Einstiegspreis aus der DB): Median +0,275×ATR über dem
|
||
Level**, Mittel +0,686 · 75 % +0,744 · Max +4,339; nur **18 %** liegen innerhalb
|
||
0,10×ATR, **53 % über 0,30×ATR**.
|
||
**Was dieser Preisunterschied kostet** (80k M5, 2 Halbjahre, sequentiell EIN Slot,
|
||
kanonischer Exit, Echtkosten):
|
||
| Variante | H1 ØR / ΣR | H2 ØR / ΣR | WR |
|
||
|---|---|---|---|
|
||
| **A Einstieg AM LEVEL** (Backtest-Annahme) | **+0,456 / +369,8** | **+0,611 / +777,8** | 56/62 % |
|
||
| **B Einstieg am Bar-Close** (≈ live) | **−0,158 / −125,9** | **−0,006 / −7,6** | 37/42 % |
|
||
**Allein der Einstiegspreis dreht das Setup — −0,61 ØR in BEIDEN Hälften, WR bricht
|
||
von 56 auf 37 %.** Modell B ist dabei **nicht pessimistisch**: sein Median-Abstand
|
||
(0,38×ATR) liegt nah am LIVE gemessenen (0,28×ATR); der Live-Betrieb liegt also
|
||
~80 % des Weges von A nach B, also grob bei **breakeven bis leicht negativ** —
|
||
nicht bei den dokumentierten +0,14…+0,23.
|
||
⚠⚠ **Das ordnet den B4-Befund vom 01.08. NEU ein.** Dort stand: „Mechanik +0,102 vs.
|
||
Live −0,242 → **die Lücke entsteht im EXIT**". Die Mechanik-Zahl unterstellt aber
|
||
denselben Level-Einstieg. Der Einstiegspreis erklärt den Großteil der 0,34-R-Lücke
|
||
— **es war überwiegend der ENTRY, nicht der Exit.**
|
||
⚠ **BEIDE naheliegenden Reparaturen fallen durch** (Regel vorab fixiert: ΣR und ØR
|
||
in beiden Hälften > B, Nachbarn ebenso):
|
||
· **Abstands-Bremse** („überspringen, wenn der Kurs schon > X×ATR über dem Level
|
||
steht", X = 0,10…0,40) — **alle 5 Schwellen durch.** Kontraintuitiv, aber konsistent:
|
||
die *nahen* Ausbrüche sind pro Trade die SCHLECHTEREN (0,10×ATR → ØR −0,254), weil ein
|
||
gerade eben überschrittenes Level oft ein Fehlausbruch ist (54 % der Box-Ausbrüche
|
||
kehren binnen 3 Bars zurück, `backtest_consolidation.py`). Die Bremse senkt den
|
||
Gesamtverlust nur, indem sie weniger handelt.
|
||
· **LIMIT-Order am Level** (Retest, 3/6/12 Bars Gültigkeit, ±0,1×ATR Puffer) —
|
||
**alle 5 Varianten SCHLECHTER als Market** (ØR −0,14…−0,27 gegen −0,006…−0,158);
|
||
151–430 Ausbrüche werden dabei verpasst. Deckt sich mit dem Retest-Befund aus
|
||
`backtest_consolidation.py`.
|
||
✅ **Schlussfolgerung: Variante A war nie erreichbar** — sie unterstellt einen Fill zu
|
||
einem Preis, den der Markt bereits verlassen hat. Die Frage ist damit nicht „wie
|
||
steigt man klüger ein", sondern **ob der Auto-Squeeze überhaupt bleibt**. Das ist die
|
||
bereits fixierte **B5-Abbruchregel** (≥20 Squeeze-Trades ab 01.08.; Verhältnis < 1,0
|
||
ODER PF < 1 → `auto_squeeze=false`) — dieser Befund stärkt sie erheblich.
|
||
**Nichts abgeschaltet** — Autonomie-Schalter sind User-Entscheidungen.
|
||
⚠ Grenzen: die Live-Stichprobe ist n=34 mit genäherter ATR-Zuordnung; A und B sind
|
||
zwei Modell-Enden, der Live-Betrieb liegt dazwischen (näher an B).
|
||
✅✅ **LÖSUNG GEFUNDEN — RUHENDE STOP-ORDER STATT MARKET (Variante E, gemessen
|
||
2026-08-05; Anstoß aus der Web-Recherche: „don't chase breakouts with market orders;
|
||
place orders just beyond the breakout level").** Eine PENDING `BUY_STOP`/`SELL_STOP`
|
||
liegt bereits am Ausbruchs-Level, WÄHREND der Squeeze `armed` ist (das Level ist da
|
||
schon bekannt = Box-Grenze), und wird vom Ausbruch selbst ausgelöst — anders als die
|
||
Limit-Order (D) wartet sie NICHT auf einen Rücklauf, verpasst also nichts.
|
||
| Fill-Annahme | H1 ØR / PF / ΣR | H2 ØR / PF / ΣR |
|
||
|---|---|---|
|
||
| Level exakt (= A) | +0,456 / 2,65 / +370 | +0,611 / 3,56 / +778 |
|
||
| **Level +0,05×ATR** | **+0,397 / 2,28 / +322** | **+0,553 / 3,06 / +697** |
|
||
| Level +0,10×ATR | +0,355 / 2,07 / +288 | +0,509 / 2,76 / +639 |
|
||
| **Level +0,20×ATR (pessimistisch)** | **+0,244 / 1,62 / +196** | **+0,426 / 2,25 / +531** |
|
||
| Market/Live (= B) | −0,158 / 0,74 / −126 | −0,006 / 0,99 / −8 |
|
||
**Selbst mit 0,20×ATR unterstellter Fill-Slippage — dem ~9-fachen des Median-Spreads
|
||
— bleibt es in BEIDEN Hälften klar positiv.**
|
||
⚠⚠ **Der Schlüssel ist nicht der Median, sondern der SCHWANZ.** Live ist der
|
||
Median-Abstand +0,275×ATR, der **Mittelwert aber +0,686** (Max +4,339). Eine
|
||
Stop-Order **deckelt den Einstiegspreis konstruktiv**; die Market-Order auf dem
|
||
5-s-Poll tut das nicht. Die Extremfälle (+3,5 / +4,3×ATR) stammen erkennbar aus
|
||
**blockiert-dann-freigegebenen** Ausbrüchen: feuert der Squeeze, während eine
|
||
Position offen ist (oder Nacht-/News-Fenster), wird KEIN Dedup-Marker gesetzt — der
|
||
Entry kommt später zum dann gültigen Marktpreis, u. U. weit hinter dem Level.
|
||
✅✅ **GEBAUT & LIVE (2026-08-05, User-Vorgabe „setze alle Vorschläge um"),
|
||
`[trading] squeeze_pending_entry=true`.** `engine._manage_squeeze_pending` (im
|
||
`_pos_loop`, VOR dem Market-Pfad) hält **beide Box-Grenzen** mit ruhenden
|
||
`BUY_STOP`/`SELL_STOP` bestückt, solange der Squeeze `armed` ist:
|
||
`trader.place_stop/cancel_pending/pending_orders` (Größe und SL aus **denselben**
|
||
Funktionen wie die Market-Order, nur bezogen auf den Trigger-Preis — sonst wäre der
|
||
SL-Abstand ein anderer als validiert). **OCO-Ersatz:** füllt eine Seite, existiert
|
||
eine Position → die Bedingung „soll liegen" wird falsch und die Gegenseite wird im
|
||
selben Tick storniert (MT5 kennt kein natives OCO).
|
||
⚠ **Der kritische Punkt bei einer RUHENDEN Order: die Gates.** Nacht-Fenster,
|
||
News-Blackout, Circuit-Breaker und der An/Aus-Schalter waren bisher Laufzeit-Checks
|
||
im Market-Pfad; eine liegende Order würde sie schlicht ignorieren. Deshalb neu
|
||
**`engine._squeeze_guard()` als EINE Quelle für beide Wege** — jeden Tick neu
|
||
bewertet, und sobald ein Gate zuschlägt, werden die Orders **storniert**. Zwei
|
||
Kopien derselben Gates wären genau die Divergenz, die hier siebenmal als
|
||
Deployment-Drift dokumentiert ist.
|
||
⚠ **Market bleibt nur noch FALLBACK — mit Nachjagd-Bremse** (`squeeze_max_chase_atr`
|
||
= **0,20**): greift keine Pending (Broker-Mindestabstand, Squeeze sprang direkt auf
|
||
`active`, Order-Fehler), wird nur noch eingestiegen, solange der Kurs **höchstens
|
||
0,20×ATR** über dem Level steht — genau der Bereich, der gemessen in beiden Hälften
|
||
positiv ist (+0,244/+0,426). Darüber: Ausbruch auslassen, Log „⏭ Auto-Squeeze
|
||
übersprungen". `_squeeze_last_sig` wird dabei NICHT gesetzt → läuft der Kurs zum
|
||
Level zurück, darf der Entry noch kommen.
|
||
⚠ Weitere Fallstricke, alle behandelt: **Broker-Mindestabstand**
|
||
(`trade_stops_level`) wird VOR dem Senden geprüft und der Fehler nur bei ÄNDERUNG
|
||
geloggt (sonst 1 Zeile/Sekunde, wenn der Kurs an der Box-Grenze klebt) · Order auf
|
||
der falschen Marktseite wird abgelehnt · **Toleranz 0,02×ATR**, damit nicht jede
|
||
ATR-Zuckung die Orders löscht und neu setzt · Startup-Schonfrist gilt auch hier.
|
||
**Mit 10 Szenarien getestet** (setzen · Idempotenz beim zweiten Tick · Fill →
|
||
Gegenseite weg · Squeeze klärt sich · Gate schlägt zu · Schonfrist · Feature aus ·
|
||
Box verschiebt sich · Broker lehnt ab · Nachjagd-Bremse in beide Richtungen).
|
||
Live verifiziert am Snapshot: `squeeze_pending=true`, `squeeze_max_chase_atr=0.2`,
|
||
`squeeze_pending_levels` (leer, weil eine Position offen war = korrektes Verhalten).
|
||
**Zurück zum alten Verhalten: `squeeze_pending_entry=false`.**
|
||
⚠ **B4-Pflicht + Werkzeug:** der Live-Abstand Einstieg↔Level muss jetzt gegen ~0
|
||
gehen (vorher Median +0,275×ATR). Dafür gibt es jetzt
|
||
**`analyze_squeeze_entry_gap.py`** (führt die zwei nötigen Quellen zusammen: das
|
||
Level steht NUR im Log, der Fill NUR in `trades`) — mit Vorher/Nachher-Split am
|
||
Umbau-Stichtag. ⚠ **Die Erfolgsmeldung ist paradox:** eine per Pending gefüllte
|
||
Order erzeugt **keine** `AUTO-SQUEEZE-ENTRY`-Logzeile (die entsteht nur im
|
||
Market-Fallback). Squeeze-Trades **ohne** Log-Treffer sind also der Erfolg, nicht
|
||
ein Fehler — das Skript weist sie deshalb getrennt aus. Überwacht als Messung
|
||
`squeeze_entry_gap` im `measurement_reminder.py` (≥12 Squeeze-Trades ab dem
|
||
Umbau; Stichtag exakt 05.08. 08:55, nicht Mitternacht — der 08:33-Trade lief noch
|
||
über Market). Bleibt der Abstand bei ~0,3×ATR, füllt die Pending nicht → Broker-
|
||
Mindestabstand und die Logzeile „Squeeze-Pending nicht platzierbar" prüfen.
|
||
⚠ **Zahlen-Korrektur (05.08.):** zuerst standen hier Median +0,306 / Ø +0,741 —
|
||
das war eine Scratchpad-Auswertung mit FESTEM Stundenversatz. `analyze_squeeze_
|
||
entry_gap.py` rechnet DST-korrekt über `zoneinfo` und liefert **+0,275 / +0,686**
|
||
(n=34 unverändert). Gültig sind die zweiten Werte; die Schlussfolgerung ändert
|
||
sich nicht.
|
||
- **⚠⚠ EIN PENDING-FILL LIEF AN DER GESAMTEN BUCHFÜHRUNG VORBEI (Fix 2026-08-06,
|
||
User-Auftrag „überprüfe die heutigen autotrades").** Der Befund kam nur zustande,
|
||
weil die Frage überhaupt gestellt wurde: die DB meldete für den 06.08. **null
|
||
Bot-Trades**, das Log dagegen vier gefüllte `SQZ-STOP`-Orders. Beides stimmte.
|
||
**Mechanismus:** eine ruhende Stop-Order füllt **im Broker**, nicht über
|
||
`engine._open`. Die Position wird erst später von `trader._refresh_locked` per
|
||
magic-match **adoptiert** — und dieser Pfad ruft `history.log_trade_open()` nur mit
|
||
Ticket/Symbol/Richtung/Lots/Preis auf. Es fehlten damit **alle** Felder, die der
|
||
Market-Pfad setzt: `setup`, `sl_at_entry` und die **16 `ctx_*`-Spalten** der
|
||
Telemetrie-Stufe 1. Vier reale Trades waren so von manuellen **nicht
|
||
unterscheidbar**.
|
||
⚠ **Die Tragweite ist größer als „ein NULL-Feld":** genau die Messpipeline, die den
|
||
Pending-Umbau vom 05.08. kontrollieren soll, war **blind für ihn** — B4-Monitor,
|
||
`squeeze_b5` (Abbruchregel!) und `squeeze_entry_gap` zählten null Trades, und
|
||
`sl_at_entry` fehlte ausgerechnet bei den Trades, an denen die M5-SL-Umstellung zu
|
||
prüfen war. **Deployment-Drift Fall 8**, und die erste Variante, bei der nicht die
|
||
Strategie driftet, sondern **ihre Beobachtbarkeit**. Ein Umbau, der den Bestellweg
|
||
ändert, ändert auch, welcher Code die Bücher führt — das gehört beim Umbau geprüft.
|
||
**Fix:** `history.tag_bot_trade(ticket, setup)` (UPDATE nur wenn `setup IS NULL`,
|
||
stiller Fail-open — `core/history.py` hat bewusst keinen Logger) +
|
||
`engine._pending_tickets` (Ticket → Quelle, beim Platzieren gesetzt, beim
|
||
Stornieren/Taggen entfernt) + `_check_pending_fill()` im `_pos_loop` VOR dem
|
||
Pending-Manager. Die Zuordnung ist eindeutig, weil bei einem Pending-Fill die
|
||
**Positions-Nummer identisch mit der Order-Nummer** ist (an allen vier
|
||
Broker-Datensätzen des 06.08. verifiziert) — kein Zeitfenster-Raten. Der Fix setzt
|
||
zusätzlich `_bot_open_ticket`/`_bot_open_source` und die Entry-Zähler, damit
|
||
15-Min-Regel, B4-Monitor und Snapshot wieder dieselbe Wahrheit sehen.
|
||
Mit **6 Szenarien getestet**, darunter die beiden gefährlichen: fremde/manuelle
|
||
Position wird NICHT getaggt, und derselbe Fill zählt bei wiederholten Ticks nur
|
||
**einmal**. Die vier Trades des 06.08. wurden nachgetragen (DB-Backup
|
||
`oil_widget_history.db.bak-2026-08-06`).
|
||
⚠ **Bewusst NICHT nachgetragen: `ctx_*` und `sl_at_entry`.** Die stehen für den
|
||
Zustand **im Moment des Fills** und lassen sich nachträglich nur rekonstruieren,
|
||
nicht messen — eine rekonstruierte Zahl in einer Telemetrie-Spalte wäre später von
|
||
einer gemessenen nicht mehr unterscheidbar. Ab jetzt sind sie für Pending-Fills
|
||
weiterhin leer; wer sie braucht, muss den Kontext beim **Platzieren** der Order
|
||
einfrieren (offen, nicht gebaut).
|
||
- **⚠ OFFEN: Pending-Churn im SIGNAL-Pfad (gefunden 06.08., nicht behoben).** Am
|
||
06.08. wurde dieselbe SELL_STOP viermal in 76 s gesetzt und storniert — **bei
|
||
identischem Level 74,981**, es wandert also nichts. Die Ursache steht in
|
||
`recommendations.block_reason`: der Grund **pendelt zwischen `entry_room` und
|
||
`breakout_pending`**. Sobald das Raum-Gate den Kurs kurz stummschaltet, liefert
|
||
`_pending_ziel()` `{}` und der Manager storniert; eine Sekunde später ist das Gate
|
||
wieder offen und die Order wird neu gelegt.
|
||
⚠ **Das ist wieder Deployment-Drift, nicht nur Log-Lärm:** `backtest_auto_signal_v3.py`
|
||
hat den Pending-Vorteil (ØR +0,35/+0,32 gegen −0,09/−0,11 am Markt) unter der
|
||
Annahme gemessen, dass die Order **durchgehend am Level liegt**. Flackert sie im
|
||
5-s-Takt, geht genau der Ausbruch verloren, der in eine Storno-Lücke fällt — und
|
||
das ist der Fall, für den sie da ist.
|
||
**Lösungsrichtung (ungebaut):** harte von weichen Storno-Gründen trennen. HART
|
||
(sofort stornieren) = Position offen · Startup-Schonfrist · Guard (Nacht/Circuit/
|
||
News) · Richtungswechsel · Level wandert über die Toleranz · Feature aus. WEICH
|
||
(Order liegen lassen) = kurzzeitiges Stummschalten durch `entry_room`/`min_conf`/
|
||
`breakout_pending`. Argument dafür: die Order füllt nur, wenn der Kurs das Level
|
||
**bricht** — dann hat er sich vom Gegenlevel wegbewegt, die Raum-Bedingung ist im
|
||
Fill-Moment also besser als im Flacker-Moment. Argument dagegen: das ist eine
|
||
Aussage über den Einstiegs-Filter und damit **nicht rein infrastrukturell** —
|
||
vor dem Bau messen (Variante „Order bleibt liegen" gegen „Order flackert").
|
||
- **Auto-Squeeze-Entry = LIVE AUTONOM (User-Vorgabe 2026-07-16, `[trading]
|
||
auto_squeeze=true`, Default FALSE):** der Bot eröffnet **selbständig eine echte
|
||
Order**, sobald der **gemessen-validierte** Squeeze-Ausbruch feuert (`wave.squeeze.
|
||
state=='active'`) UND **flat** ist — NICHT auf dem Wave-Signal (das ist der
|
||
Dry-Run-Verlierer), sondern nur auf dem 2-Stichproben-positiven Squeeze.
|
||
`engine._check_auto_squeeze` im `_pos_loop` (1 s): **1× je Ausbruch** (Dedup über
|
||
(dir, level); re-armt, wenn der Squeeze klärt), öffnet via `open_long/short` (→
|
||
Trailing an, Emergency-Stop armiert 2 % beim nächsten Tick), Log `🚀 AUTO-SQUEEZE-
|
||
ENTRY` + Telegram + Fehlschlag-Push. Snapshot `auto_squeeze`/`squeeze_entry_count`;
|
||
Toggle `POST /api/autosqueeze` (`set_auto_squeeze`) = Dashboard-Button **🚀 BRK**
|
||
(blau=AN, ersetzt den alten Dry-Run-AUTO-Button). **Dead-Hour-Guard = AN
|
||
(User-Vorgabe 2026-07-24, `[trading] auto_squeeze_skip_night=true`) — **Fenster
|
||
seit 2026-08-05 auf `3,4,5,6` VERKÜRZT** (`[trading] auto_squeeze_night_hours`,
|
||
User-Vorgabe „verkürze auf die wirklich toten Stunden"; war fest 0–7):** der Bot
|
||
eröffnet in diesen Stunden **KEINE** Squeeze-Trades (`_squeeze_night_hours` →
|
||
`_check_auto_squeeze` return; kein Entry UND kein Reverse; leere Liste = aus).
|
||
⚠ **`_SQUEEZE_NIGHT=(0..7)` bleibt bestehen, ist aber NICHT mehr das Gate** — es
|
||
ist jetzt nur noch die STATISTISCHE Nacht-Definition für die Entry-Checkliste
|
||
(dort gilt die Kostenfalle weiterhin, s. u.) und den Tag/Nacht-Split im
|
||
B4-Monitor (damit der Vergleich mit den Altdaten stabil bleibt). Der
|
||
Auto-Signal-Pfad nutzt dasselbe neue Fenster (ist ohnehin aus).
|
||
⚠⚠ **Die Verkürzung ist NICHT ertrags-begründet, sondern preis-begründet** —
|
||
gemessen ist **keine einzige Nachtstunde in beiden Halbjahren negativ**, jede
|
||
geblockte Stunde kostet Ertrag (Details beim Fallbeispiel unten). 3–6 ist
|
||
schlicht das **günstigste Fenster mit noch ≥4 h Schutz**: Preis ΣR
|
||
**−12,5/−62,9 R** gegen **−42,4/−242,6** bei 0–7 = **~27 %** der bisherigen
|
||
Kosten. Freigegeben sind **0, 1, 2 und 7 Uhr**. ⚠ Std 0 ist dabei die **beste
|
||
Stunde des ganzen Tages** für den Squeeze (ØR +1,48/+1,51) — sie zu blocken war
|
||
der teuerste Einzelposten. Live verifiziert am neuen Snapshot-Feld
|
||
`squeeze_night_hours` + Startup-Log „Auto-Squeeze-Nachtsperre: 3:00, 4:00, 5:00,
|
||
6:00". Zurück auf den alten Stand: `auto_squeeze_night_hours = 0,1,2,3,4,5,6,7`.
|
||
Doppelt begründet: (1) **gemessen** unrentabel (`backtest_realcosts.py`, Spread÷Nacht-
|
||
ATR 0,32–0,50×ATR, beide Halbjahre negativ = Kostenfalle) und (2) **kein Trade
|
||
soll unbeaufsichtigt nachts feuern, während der User schläft** — Auslöser: „müsste
|
||
automatisch schließen wenn ich nicht da bin, z. B. nachts". Der Guard LÖST das,
|
||
indem er den Trade gar nicht erst öffnet; tagsüber (User eher erreichbar) läuft der
|
||
bestehende Auto-Close-Stack (Broker-SL 2×ATR broker-seitig + Trailing + Time-Stop +
|
||
S/R-Close). ⚠ Historie: bis 2026-07-17 war der Guard AUS (User wollte rund um die
|
||
Uhr), am 2026-07-24 auf User-Wunsch **zurück auf AN**. `=false` schaltet ihn wieder
|
||
aus (dann auch nachts, unbeaufsichtigt). Einen engen Schutz-%-Stop NUR für Squeeze
|
||
wurde bewusst NICHT gebaut (enger %-Stop unter Margin-Sizing wird vom Squeeze-
|
||
Rauschen ausgelöst, −48,94-€-Tag — der Squeeze braucht Raum).
|
||
⚠⚠ **FALLBEISPIEL 05.08.2026 — der Guard hat 5 Ausbrüche geblockt, darunter den
|
||
Einstieg direkt vor einem 1,8-$-Rutsch (User-Frage „warum ist der Squeeze nicht
|
||
eingestiegen?").** Nachgerechnet mit der ECHTEN `_squeeze_one` auf den M5-Bars
|
||
(nicht aus dem Log geschlossen — der Squeeze-ZUSTAND wird nirgends geloggt, nur der
|
||
Entry): zwischen 02:00 und 07:30 Berlin feuerte 5× `active`, **alle in 0–7 Uhr**.
|
||
Der Bot war ab 01:15 flat, die Nachtsperre war also der ALLEINIGE Blocker (kein
|
||
Cooldown, keine offene Position). Kursverlauf: Hoch **76,373** um 03:30 → Tief
|
||
**74,548** um 04:40 = **−1,825 $ in 70 min**; der Ausbruch **04:00 SHORT @ 76,11**
|
||
lag praktisch auf dem Hoch.
|
||
**Mit dem kanonischen Exit** (`exit_model.LIVE`, Echtkosten aus dem Bar-Spread —
|
||
bewusst NICHT „maximale Auslenkung", s. die Korrektur bei der 15-Minuten-Regel):
|
||
02:00 SHORT **+3,28 R** · 03:00 LONG −0,24 · 04:00 SHORT **+1,36 R** · 06:15 LONG
|
||
−0,60 · 07:30 SHORT −0,24 → **Σ +3,56 R ≈ +95 € bei 2,66 Lots**.
|
||
⚠ **Trotzdem kein Argument gegen den Guard:** n=5 an EINEM Tag, und **92 % davon
|
||
stecken in zwei Trades** — ohne den 02:00er bleiben +0,29 R ≈ +12 €. Ein Einzeltag
|
||
taugt nicht als Beleg, auch nicht als widerlegender (dieselbe Lehre wie am 01.08.).
|
||
Die Guard-Begründung ruht auf 2 Halbjahren; dazu kommt der nicht-ökonomische Grund
|
||
(keine unbeaufsichtigten Nacht-Trades), der von Zahlen gar nicht berührt wird.
|
||
✅ **Ein messbarer Ansatz ergibt sich aber:** die Nacht-Begründung ist eine
|
||
**Kosten**-Aussage (Spread÷ATR 0,32–0,50). In dieser Nacht lagen die realen Kosten
|
||
bei **0,11–0,22×ATR**, also klar UNTER der Falle — weil der ATR hoch war. Ein
|
||
**kosten-basiertes statt stunden-basiertes Gate** („blocke, wenn Spread/ATR > X")
|
||
würde genau solche Nächte durchlassen und ruhige weiter sperren.
|
||
⚠⚠ **GEMESSEN am selben Tag (`backtest_cost_gate.py`, 80k M5, 2 Halbjahre,
|
||
kanonischer Exit, Echtkosten, ECHTE `_squeeze_one`) — und das Ergebnis fällt
|
||
anders aus als die Frage lautete.**
|
||
**(a) Kein Kosten-Gate ersetzt das Stunden-Gate** nach der vorab fixierten Regel
|
||
(ΣR **und** ØR in beiden Hälften ≥ Stunden-Gate, plus Nachbar-Schwellen): alle 7
|
||
Schwellen 0,15–0,50 fallen durch.
|
||
**(b) Der eigentliche Befund: das STUNDEN-Gate selbst kostet Geld — in BEIDEN
|
||
Hälften.** Sequentiell mit EINEM Positions-Slot (die konto-relevante Rechnung;
|
||
ein geblockter Nacht-Trade gibt den Slot frei):
|
||
| Variante | H1 ΣR | H2 ΣR |
|
||
|---|---|---|
|
||
| **ohne Gate** | **+369,5** | **+778,1** |
|
||
| Stunden-Gate 0–7 (live) | +327,1 | +535,5 |
|
||
| Kosten-Gate 0,30 | +356,3 | +760,1 |
|
||
| Kosten-Gate 0,35 | +369,3 | +781,9 |
|
||
→ das Live-Gate kostet **−42 R (H1) und −243 R (H2)**. Die von ihm GEBLOCKTEN
|
||
Ausbrüche sind in beiden Hälften **positiv** (ØR +0,496/+0,589) — in H1 sogar
|
||
besser als die behaltenen (+0,382). Es wirft gute Trades weg.
|
||
⚠⚠ **MECHANISMUS BELEGT — die Nachtsperre ist auf der FALSCHEN Population
|
||
begründet (Deployment-Drift, Fall 6).** Die Nacht-Kostenfalle ist echt und
|
||
reproduziert (`backtest_realcosts.py`): über ALLE Nacht-Bars Median **0,333** /
|
||
Ø **0,387** ×ATR gegen Tag 0,163/0,185. **Aber der Squeeze feuert dort gar nicht** —
|
||
er verlangt einen Ausbruch, also anziehenden ATR. Seine Nacht-Ausbrüche kosten nur
|
||
**Median 0,183 / Ø 0,191** (Tag 0,156/0,162), liegen also praktisch auf Tag-Niveau
|
||
und weit unter der Falle. **Der Squeeze selektiert sich aus der Kostenfalle
|
||
heraus.** Gemessen wurde die Falle am Wellensignal, angewandt wird das Gate auf den
|
||
Squeeze — dieselbe Fehlerklasse wie bei der 15-Minuten-Regel (auf Wave-Entries
|
||
kalibriert, auf alle Bot-Trades angewandt, auf Squeeze gemessen schädlich).
|
||
⚠ Nebenbefund: **ein Kosten-Gate TRENNT tatsächlich** in die richtige Richtung
|
||
(bei 0,30 hat die geblockte Gruppe H1 WR 44,9 % / ØR +0,175 gegen +0,485 behalten)
|
||
— nur ist auch sie noch positiv, deshalb kostet jedes Blocken ΣR. Bester ΣR-Wert
|
||
ist 0,35, und der blockt praktisch nichts mehr (1 bzw. 10 Trades) = faktisch „kein
|
||
Gate".
|
||
⚠ **Methodik-Lehre zur eigenen Regel:** „ΣR UND ØR ≥ Referenz" ist als
|
||
Entscheidungsregel schlecht spezifiziert, wenn die Varianten unterschiedlich viele
|
||
Trades handeln — ØR und ΣR laufen dann mechanisch gegeneinander (0,30 schlägt das
|
||
Stunden-Gate im ΣR um +29/+225, verfehlt das ØR in H2 um 0,004). Die Regel wurde
|
||
**nicht nachträglich gelockert**; stattdessen ist der Vergleich „Gate gegen KEIN
|
||
Gate" separat ausgewiesen — er stand ohnehin in derselben Tabelle.
|
||
⚠⚠ **DREI Gründe, warum daraus NICHT automatisch „Nachtsperre aus" folgt:**
|
||
(1) **Slippage ist nicht modelliert** — nachts ist das Buch dünner, der gemessene
|
||
Tail liegt bei 0,75×ATR über den Stop hinaus (`analyze_execution.py`). Das trifft
|
||
Nacht-Trades selektiv und ist die größte Bedrohung des Befunds. (2) **Live sagt das
|
||
Gegenteil**, wenn auch dünn: von den 31 Squeeze-Trades des B4-Fensters waren 12
|
||
Nacht-Trades mit **−78,92 €** — allerdings alle VOR den Exit-Reparaturen vom 31.07.
|
||
(P(break) nachtrainiert, 15-Min-Regel aus, Trail 1,5→1,0). (3) Der
|
||
**Beaufsichtigungs-Grund** („kein Trade soll feuern, während ich schlafe",
|
||
User-Vorgabe 2026-07-24) ist eine Präferenz und wird von keiner Zahl berührt.
|
||
✅ **Konsequenz (User-Vorgabe 2026-08-05 „verkürze auf die wirklich toten
|
||
Stunden"): Fenster von 0–7 auf 3–6 verkürzt** — Details oben beim Guard.
|
||
⚠⚠ **Die Prämisse der Vorgabe hält der Messung nicht stand: es gibt KEINE toten
|
||
Stunden.** Je Stunde einzeln, sequentiell, beide Halbjahre — **alle 24 Stunden
|
||
sind in beiden Hälften positiv**, keine einzige ist beidhälftig negativ. Die
|
||
ruhigsten Nachtstunden (n über beide Hälften): 5 (41 Ausbrüche) · 4 (56) · 3 (61)
|
||
· 6 (62); die aktivsten 7 (75) · 0/2 (72) · 1 (71). ⚠ **Std 0 ist die stärkste
|
||
Stunde des gesamten Tages** (ØR +1,483/+1,510, ΣR +22,2/+86,1) — ausgerechnet sie
|
||
lag im alten Gate.
|
||
Damit ist die Auswahl **keine Ertrags-Optimierung, sondern eine Preis-Minimierung
|
||
für eine Präferenz** — und deshalb auch nicht overfitting-gefährdet: es wird keine
|
||
prädiktive Behauptung aufgestellt. Preisliste (ΣR gegen „kein Gate"):
|
||
| Fenster | Std | Preis H1 | Preis H2 |
|
||
|---|---|---|---|
|
||
| 4–5 | 2 | −3,9 | −18,1 |
|
||
| 4–6 | 3 | −6,8 | −39,9 |
|
||
| **3–6 (gewählt)** | **4** | **−12,5** | **−62,9** |
|
||
| 2–5 | 4 | −12,8 | −77,2 |
|
||
| 0–7 (alt) | 8 | −42,4 | −242,6 |
|
||
Die 4-Stunden-Untergrenze ist **gesetzt, nicht gemessen** — darunter wäre der
|
||
Schutzzweck hinfällig. Wer weniger Schutz will, nimmt 4–5 (fast gratis); wer den
|
||
alten Stand will, trägt `0,1,2,3,4,5,6,7` ein.
|
||
⚠ **Was die Verkürzung NICHT ausräumt:** die drei Vorbehalte oben gelten
|
||
unverändert — vor allem die **nicht modellierte Slippage** (dünnes Nacht-Buch,
|
||
Tail 0,75×ATR über den Stop). Die freigegebenen Stunden 0/1/2/7 liegen mitten in
|
||
der dünnsten Zeit. **B4-Pflicht: die Squeeze-Trades aus 0/1/2/7 Uhr getrennt
|
||
mitzählen** — bleiben sie hinter der Erwartung (ØR ~+0,5 R), war die Slippage
|
||
der Grund und das Fenster gehört zurück auf 0–7.
|
||
⚠⚠ **PRÄZISIERT 2026-08-19 (User: „überprüfe: nie validiert — der Backtest
|
||
unterstellt Flat-Entry“).** Die Sache stimmt, die Begründung war ungenau.
|
||
`squeeze_scan.scan` hat **überhaupt keinen Positions-Zustand**: nach jedem
|
||
Ausbruch springt `i = j + cooldown` (3 Bars) — **nicht** bis zum Exit. Jeder
|
||
Ausbruch ist ein **isolierter** Trade, Trades dürfen sich überlappen. Der
|
||
Backtest unterstellt also *nicht* Flatness; die Lage „Gegen-Position offen,
|
||
wird gedreht“ kann darin schlicht **nicht vorkommen**. Gegengeprüft: **kein
|
||
einziger** `backtest_squeeze*.py` enthält das Wort `reverse`; gemessen wurde
|
||
nur die **Exit-only**-Halbvariante (3×, alle negativ).
|
||
⚠ **Zweite Folge, wichtiger:** weil kein Slot modelliert wird, unterstellen
|
||
die dokumentierten Squeeze-Zahlen, dass man **jeden** Ausbruch nehmen kann —
|
||
live gibt es EINEN Slot. Deckt sich mit dem Befund vom 11.08. (die Sim
|
||
überhandelt um das **2,4-fache**).
|
||
**Stop-&-Reverse = AUS (Default, `[trading] auto_squeeze_reverse=false`,
|
||
`_squeeze_reverse`; User-Vorgabe 2026-07-17):** War kurz scharf, dann abgeschaltet —
|
||
der Reverse ist unbelegt, verdoppelt
|
||
Kosten und dreht am **Ausbruch-Extrem** (real 2026-07-17 18:55: WAVE_REV_SHORT gegen
|
||
einen News-Rally-LONG-Ausbruch bei −29,64 € geschlossen, dann LONG am Erschöpfungs-Top
|
||
82,32 — Elliott „impulse_up/5 exhausted" im selben Moment). Bei `false` bleibt ein
|
||
Gegen-Trade **unangetastet** (Drehen = manuelle Entscheidung); `_squeeze_last_sig`
|
||
wird dann NICHT gesetzt → schließt der Gegen-Trade (SL/manuell), kann der flat-Entry
|
||
auf denselben Ausbruch noch feuern. Bei `true` (historisch, reaktivierbar): läuft ein
|
||
Trade GEGEN den Ausbruch (auch manueller/Fremd-Trade!) → sofort `close(reason=
|
||
"squeeze_reverse")` (Log 🔄 + Telegram, Dedup `_squeeze_reverse_sig`), nächster flat-Tick
|
||
dreht. ⚠ NICHT gemessen (der Squeeze-Backtest nahm flat-Entry an) — deshalb Default aus.
|
||
**Exit-only-Variante („Gegen-Position beim Ausbruch schließen, OHNE Re-Entry") =
|
||
VERWORFEN (gemessen `backtest_squeeze_exit.py`, 2026-07-22, User-Idee „Breakout
|
||
schneller schließen"):** die sicherere Halb-Variante (nur schließen, nicht drehen)
|
||
fällt ebenfalls durch — in BEIDEN Hälften schlechter als der Live-Exit (SL 2×ATR+Trailing):
|
||
ΔΣR H1 −15 (immer)/−5 (nur im Minus), H2 −27/−29. Grund: der Gegen-Ausbruch ist oft ein
|
||
Fehlausbruch/Whipsaw → die Position prallt zurück, der frühe Close kappt Trades, die
|
||
sich erholt hätten. Der 07:38-Fall (LONG lief zum SL während SHORT-Ausbruch) war Varianz.
|
||
**Fazit: Squeeze taugt als ENTRY (validiert), NICHT als Gegen-EXIT.** Der einzige
|
||
Speed-Hebel bleibt die schnellere Erkennung (Variante A, ~5 s).
|
||
⚠⚠ **DRITTE MESSUNG 2026-08-05 (`backtest_squeeze_exit3.py`) — jetzt mit dem
|
||
KANONISCHEN Exit, und der Grund für das Nein ist ein anderer als gedacht.**
|
||
Anlass: Trade T=49423144 (manueller SELL 3,49 @ 75,012) lief in 14 min 1,135 $
|
||
ins Minus und endete am Initial-SL bei **−343 €**; User-Wunsch: „der Breakout
|
||
hätte den Trade früher schließen sollen", nachgeschärft zu „bei einem Squeeze den
|
||
SL sofort eng setzen".
|
||
⚠ **Was an dem Tag wirklich passierte:** beim Einstieg war der Squeeze **active
|
||
SHORT** — er stand FÜR den Trade. Die Empfehlung war WARTEN (`block=entry_room`),
|
||
der Einstieg manuell. Der Gegen-Ausbruch (armed LONG @ 75,729) wurde erst ~3–5 min
|
||
später aktiv; ein Close dort hätte ≈**−219 €** statt −343 € bedeutet (~124 €).
|
||
**Gemessen (beide Varianten, kanonischer Exit, Echtkosten, 2 Halbjahre, zwei
|
||
Populationen — 2.365 Squeeze- und 1.566 Wellen-Entries):**
|
||
| Variante | Auslösungen H1/H2 |
|
||
|---|---|
|
||
| sofort schließen (Squeeze-Entries) | **1 / 1** von 809 bzw. 1.274 |
|
||
| **enger SL 0,1–0,5×ATR** (Squeeze-Entries) | **0 / 0** |
|
||
| sofort schließen (Wellen-Entries) | 8 / 8 von 386 bzw. 835 |
|
||
| enger SL (Wellen-Entries) | 2–3 / 2–3 |
|
||
✅✅ **DAS IST DER EIGENTLICHE BEFUND: der Mechanismus feuert praktisch NIE.**
|
||
Nicht weil die Idee schlecht wäre, sondern weil sie **konstruktionsbedingt zu spät
|
||
kommt**: ein Gegen-Ausbruch verlangt eine NEUE Kompression (Box über 10 M5-Bars =
|
||
50 min), der Ausbruch selbst weitet die Box aber gerade erst. Die mittlere
|
||
Haltedauer liegt bei **12–18 min**. Der Gegen-Ausbruch trifft also in rund **0,1 %
|
||
(Squeeze) bzw. 1 % (Welle)** der Trades ein — der Fall des Users war einer davon.
|
||
Die Kennzahl-Unterschiede in der Tabelle sind entsprechend Rauschen aus einer
|
||
Handvoll Fällen; keine Variante besteht auf beiden Populationen.
|
||
⚠ **Auch die abgeschwächte Form („enger SL statt Close") ändert nichts** — sie
|
||
adressiert zwar genau den dokumentierten Einwand (Gegen-Ausbrüche sind oft
|
||
Fehlausbrüche), kann ihn aber nicht ausspielen, wenn sie 0-mal auslöst.
|
||
⚠ Und sie wäre von dieser Simulation ohnehin **überschätzt**: ein enger Stop wird
|
||
von einer Bar-Simulation systematisch zu gut gerechnet (s. Auflösungs-Artefakt,
|
||
`analyze_trail_resolution.py`, 05.08.).
|
||
**Damit zum DRITTEN Mal verworfen — und diesmal mit einer Begründung, die nicht
|
||
vom Exit-Modell abhängt.**
|
||
**Re-Test unter Vola-Regimen = ERNEUT VERWORFEN (gemessen
|
||
`backtest_squeeze_exit_vol.py`, 2026-07-24, User-Frage „Markt gerade sehr volatil —
|
||
wirklich nicht profitabel?"):** Verschärfter Test mit echten SQUEEZE-Entries (statt
|
||
EMA-Crosses), 4 Varianten (immer/nur-im-Minus/nur-Verlust>0,5×ATR), ATR-Terzil-Split
|
||
je Hälfte + Extra-Fenster letzte ~30 Tage. Ergebnis: Effektgröße überall ~0
|
||
(±0,002–0,005 R/Trade bei ~1150 Trades/Hälfte), **Vorzeichen kippt zwischen den
|
||
Regimen** (MODL: H1-Hochvola −1,6 / H2-Hochvola +3,0 / letzte 30 Tage −0,3) → nach
|
||
der vorab festgelegten Regel (beide Hälften UND Hoch-Vola-Terzil ≥ BASE) fällt JEDE
|
||
Variante durch. **Kernbefund: Worst-Case ist in ALLEN Varianten identisch**
|
||
(−2,33/−2,47 = SL-Deckel) — der Gegen-Squeeze-Exit reduziert das Tail-Risiko um
|
||
exakt 0, weil bei echten schnellen Brüchen immer der SL zuerst greift; er kappt nur
|
||
langsame Kriecher, von denen sich >50 % erholt hätten. (Methodik-Hinweis: absolute
|
||
ØR pessimistischer als im validierten Squeeze-Backtest — Entry am Bar-Close statt
|
||
am Level, keine Flat-Bedingung; für den Varianten-VERGLEICH neutral, da alle Exits
|
||
denselben Entries folgen.) 3. Messung derselben Idee (Reverse · Exit-only ·
|
||
Vola-Regime-Re-Test) — gleiche Antwort.
|
||
**S/R-Close kappt Squeeze-Runner? = GEPRÜFT, KEINE Änderung (gemessen
|
||
`backtest_squeeze_srclose.py`, 2026-07-24, User-Beobachtung „Squeeze wird direkt am
|
||
nächsten S/R geschlossen"):** Der Log bestätigt, dass Auto-Squeeze-Trades regelmäßig
|
||
vom P(break)-S/R-Auto-Close am nächsten Gegen-Level geschlossen werden (der Squeeze
|
||
wurde mit reinem Trailing validiert, ist runner-abhängig — Verdacht: Runner gekappt).
|
||
Direkter Vergleich NUR auf Squeeze-Entries (Box≤2,5×ATR, k=0,1): **Exit A = nur
|
||
Trailing (validiert)** vs. **Exit B = +S/R-Close (aktuell live)** = **Wash, kein
|
||
robuster Schaden** — H1 ØR +0,190→**+0,224** (ΣR +40→+47, B besser), H2 ØR
|
||
+0,394→+0,377 (ΣR +290→+278, B marginal schlechter) = Δ ±0,02 R/Trade (Rauschen).
|
||
**Warum unkritisch:** der S/R-Close ist P(break)-gegated (schließt nur wenn Level
|
||
wahrscheinlich HÄLT, P<60 %) → echte, starke Ausbrüche haben hohe P(break) und
|
||
laufen weiter; **PF/Runner-Tail bleiben erhalten** (H2 PF 1,58=1,58), nur die WR
|
||
steigt (50→55 %, Abpraller früher als Klein-Gewinn kassiert). Squeeze-Trades vom
|
||
S/R-Close auszunehmen ist also NICHT belegt. (Reaktivierbar per Toggle, falls der
|
||
User trotzdem die Runner nie kappen will — kostet gemessen ~nichts, ist aber
|
||
Präferenz, kein Edge.)
|
||
**S/R-Close-Cooldown (2026-07-17, Koordinations-Fix, kein Backtest):** nach einem
|
||
S/R-Auto-Close eröffnet der Auto-Squeeze **~3 min (`_SQUEEZE_SR_COOLDOWN_S=180`)
|
||
nicht in GLEICHER Richtung** wieder. Grund: der S/R-Close ging von „Level hält"
|
||
(P(break)<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.
|
||
|
||
## ⚠⚠ TELEGRAM WAR NICHT KAPUTT — ES SENDETE NUR NICHTS MEHR (2026-08-11)
|
||
|
||
User: „ich bekomme keine Telegram-Nachrichten mehr."
|
||
✅ **Kanal nachweislich intakt:** `getMe` → `@AH_Oiltrader_bot`, `getChat` → privat
|
||
„Axel", **Testnachricht zugestellt** (message_id 1764). Im gesamten Log **keine
|
||
einzige** Fehlermeldung; der Tagesreport kam täglich (08.–11.08. je 07:3x, alle
|
||
`ok`). Letzte Nicht-Report-Nachricht **07.08. 22:49**, davor am 06.08. 19:33–19:58
|
||
im 5-Minuten-Takt = die Close-Alarm-Sperre.
|
||
⚠⚠ **DIE URSACHE: ALLE NEUN AUSLÖSER WAREN AUS** — Auto-Squeeze (über
|
||
`runtime_state.json`, unbemerkt), Auto-Signal, S/R-Auto-Close, Flip-Close,
|
||
15-Minuten-Regel, Circuit-Breaker und die **drei** Notfall-Stop-Modi. **Jede
|
||
Abschaltung war für sich begründet und gemessen; zusammen ergaben sie eine
|
||
Stille, die niemand beschlossen hatte.** Übrig blieb genau ein Live-Trigger, der
|
||
🔔 Close-Alarm — und der braucht eine **gerichtete** Empfehlung, die es nur in
|
||
**5 %** der Zeit gibt (letzte 7 Tage: 508 von 9.976 Zeilen).
|
||
⚠ Dazu kam: der Trade-Abschluss-Push war früher bewusst entfernt worden. Deshalb
|
||
kam auch der **−87-€-SL vom 10.08. wortlos**.
|
||
|
||
✅ **GEBAUT: Close-Benachrichtigung `engine._check_close_notify`**
|
||
(`[telegram] close_notify_min_eur = 50`, 0 = aus; Backup
|
||
`oil_widget_config.ini.bak-2026-08-11-closenotify`). Feuert bei **jedem**
|
||
geschlossenen Trade ab der Schwelle — **hängt an KEINER Automatik**, also auch
|
||
bei manuellem Close und Broker-SL. Meldet Betrag, Richtung, Lots, Ein-/Ausstieg,
|
||
**Schließgrund** und Dauer. Snapshot-Feld `close_notify_min`.
|
||
⚠ **Schwelle aus den Daten gewählt, nicht geraten:** bei ~17 Trades/Tag liefert
|
||
50 € rund **2,4–6,1 Meldungen/Tag** und erfasst **54–80 %** des bewegten Geldes.
|
||
⚠ **Eine absolute Euro-Schwelle altert mit der Positionsgröße** — Median |P&L|
|
||
über 30 Tage 14,65 €, über die letzten 7 Tage 35,80 €. Bei wachsendem Konto
|
||
kommen mehr Meldungen. Bewusst so: ein Mensch denkt in Euro.
|
||
⚠⚠ **Der Unterschied zum entfernten Push ist die SCHWELLE** (der alte feuerte bei
|
||
jedem Close), und sie liegt in der **Engine**, nicht in der DB-Schicht.
|
||
⚠ **Vorgemerkt statt sofort gesendet:** der Flat-Zustand kommt aus
|
||
`trader.snapshot()` und ist dem DB-Schreiber gelegentlich einen Tick voraus —
|
||
`exit_time IS NULL` heisst „noch nicht fertig", NICHT „kein Trade". Ohne diese
|
||
Unterscheidung meldete es einen Close **ohne P&L**. Aufgabe nach ~30 s.
|
||
✅ **7 Tests** (`tests/test_close_notify.py`, Netz abgefangen) + **Mutationsprobe
|
||
in beide kritischen Zweige**: Schwelle entfernt → `test_unter_schwelle` fällt ·
|
||
Retry-Fall als „kein Trade" behandelt → `test_db_noch_nicht_fertig` fällt.
|
||
|
||
✅ **AUTO-SQUEEZE WIEDER AN** (User-Entscheidung 2026-08-11) — über den offiziellen
|
||
Toggle `POST /api/autosqueeze`, damit ini UND `runtime_state.json` übereinstimmen
|
||
(`{"ok":true,"enabled":true}`). Der Config-Wächter meldet ihn seitdem nicht mehr.
|
||
⚠ Damit läuft die **B5-Abbruchregel** weiter (11/20 Trades) — ohne laufenden
|
||
Trader wäre sie nie abgelaufen. Latte unverändert: Verhältnis < 1,0 ODER PF < 1.
|
||
|
||
## Mobile-UI (web/) & Alarme
|
||
- Aufbau: **Tab-Navigation ganz oben** (sticky: Dashboard/**SIG Live**/**SIG Anzeige**/Logs/Statistik/News),
|
||
darunter Header.
|
||
- **Design-System (`style.css`, Pass 2026-07-15, KEIN Framework — Tailwind bewusst
|
||
verworfen wegen WireGuard/Offline + kein Node):** Tokens in `:root` — Flächen-Leiter
|
||
`--bg < --surface-2 < --card`, `--border`/`--line`, Text `--dim`/`--bright`
|
||
(+`--text-*`-Aliasse, waren undefiniert = Bug gefixt), Radien `--r-sm/-r/-r-lg`,
|
||
`--shadow(-sm)`, `--mono`. **Font:** UI = System-Sans + `tabular-nums`; Zahlen-Werte
|
||
(Kurs/PnL/Felder/Logs) = `--mono`. Karten mit Elevation-Schatten, Fokus-Ringe auf
|
||
Feldern, Transitions auf Tabs/Buttons. Neutrale Hex-Literale → Tokens (Konsistenz).
|
||
- **Trade-Leiste** (`#trade-bar`, über der Gesamtempfehlung, nur bei offener Position,
|
||
2026-07-15): **Laufzeit-Uhr** (2026-07-30, User-Wunsch „Uhr … wie lange diese schon
|
||
läuft"): `trader.snapshot()` liefert `open_time` = Öffnungszeit als **echte Epoch**
|
||
(Broker-Offset korrigiert, aus `position.time`); Frontend zeigt sie als Feld
|
||
`#tb-dur` **und** kompakt im Kachel-Titel (`#tb-runtime`, „⏱ 1:23:45"). Der Ticker
|
||
läuft **eigenständig 1×/s** (`_tickTradeDuration`), zählt also flüssig zwischen den
|
||
Snapshots; ab **2 h** bernstein (Time-Stop-Nähe: 120 min in Phase „Init"). ⚠ Zwei
|
||
Fallen beim Bau: (1) `_broker_offset_s()` liest `self.symbol`, die beim **Adoptieren**
|
||
einer Position noch NICHT gesetzt ist → Offset 0 → Zeit lag 3 h in der Zukunft; die
|
||
Funktion nimmt jetzt `sym` als Parameter (`_broker_offset_s(pos_sym)`). (2) Selbst-
|
||
heilung: liegt `open_time` >60 s in der Zukunft, wird sie neu berechnet; beim Close
|
||
auf 0 zurückgesetzt (kein stale Wert für den Folge-Trade). Berechnung nur 1× je
|
||
Position (nicht je 1-s-Refresh). Weiter in der Zeile: Richtung (▲LONG/▼SHORT) · Lots · Einsatz · **3 Eingabefelder
|
||
`.tb-field` (Label über Feld, rot=`.stop`/grün=`.gain`): SL · TP · Gewinn-Close
|
||
(`#srmin-input`)** (Notfall-Close-Feld **2026-07-17 entfernt** — Notfall-Stop ganz aus).
|
||
SL/TP: Enter/Verlassen →
|
||
`POST /api/sltp` → setzt am Broker **und schaltet Trailing ab** (`trail.deactivate`);
|
||
Vorbefüllung aus `position.sl/tp`, Broker-Ablehnung → Toast. Gewinn-Close
|
||
(2026-07-15 aus der Position-Kachel hierher verschoben, User-Vorgabe — gleiches Design)
|
||
behält ID/Handler (`/api/srclose_min`). **Auch der
|
||
Auto-Close-Schalter** (`#srclose-btn`, jetzt `.tb-btn`-Box, AN/AUS grün/grau) sitzt in
|
||
der Zeile. Titel „aktueller Trade läuft"; Richtung/Lots/Einsatz = Anzeige-Boxen
|
||
(`.tb-val`), Rest editierbar — alle im selben `.tb-field`-Look. Position-Kachel hält
|
||
nur noch `cur-near`-Warnung + `pos-srhint`. (`.emg*`/`.sr-toggle`-CSS entfernt.)
|
||
- ⚠⚠ **CHARTS-TAB KOMPLETT ENTFERNT (User-Vorgabe 2026-08-05, v=142).** Weg sind:
|
||
Tab-Button, `#view-charts`, der gesamte `app.js`-Chart-Block (Kartenaufbau,
|
||
`renderChart`, `loadCharts`, 20-s-Refresh, Resize-Handler), die Chart-CSS-Regeln,
|
||
die **vendorte Bibliothek** `web/lightweight-charts.standalone.production.js`
|
||
(164 KB) samt `<script>`-Tag — und die Backend-Kette dahinter: **`/api/bars`**
|
||
und **`engine.get_bars`** (60 Zeilen). Beide existierten ausschliesslich für
|
||
dieses Tab (geprüft: kein anderer Aufrufer).
|
||
⚠ **Nicht betroffen** — sie sahen nur ähnlich aus: die **Chartmuster**-Karte
|
||
(`#card-patterns`, Verdict-Gewicht 0,25, jetzt im Tab „SIG Live"), der
|
||
**MQL5-Indikator** samt `sr_levels.csv`-Export (eigener Pfad über
|
||
`_write_levels_file`, unabhängig von `get_bars`) und alle Analyse-Module
|
||
(`structure.py`, `patterns.py`, `cone.py`) — die speisen den Snapshot, nicht das
|
||
Chart. Der MT5-Chart bleibt also vollständig bedient.
|
||
Verifiziert am laufenden Server: `/api/bars` → **404**, Bibliothek → **404**,
|
||
`data-view="charts"` und `id="view-charts"` nicht mehr im ausgelieferten HTML,
|
||
6 Tabs zu 6 Views paarig, keine verwaisten JS-Referenzen, Klammern-Balance ok.
|
||
Zurückholen: alles liegt in der Git-Historie (Commit davor).
|
||
|
||
- **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).
|
||
⚠ **Bias-Fix DRITTER TEIL 2026-07-31 (Anlass: User-Frage „H1 aus der Gesamt-
|
||
empfehlung entfernen, da wir jetzt mit M30 arbeiten?"):** Antwort **NEIN** — die
|
||
Prämisse vermischt zwei Dinge (auf M30 umgestellt wurde die **S/R-Level-Quelle**,
|
||
nicht die Zeitebenen-Logik; H1 war nie Basis-TF, `tf_max=M30`). Und die vermutete
|
||
**Doppelzählung ist gemessen schwach** (`verdict_votes`, n=7.074): bei gerichteter
|
||
Welle stimmt **M30 nur zu 14,9 %** mit ihr überein, H1 zu 47 %, M30 und H1
|
||
untereinander nur zu **33,8 %** — keine redundanten Stimmen (Reversals heben den
|
||
M30-Filter bewusst auf, und M30 ist meist gar nicht gerichtet). H1 fließt zwar auch
|
||
als Konfluenz-Bonus in die Konfidenz — aber in eine ANDERE Kennzahl (Konfidenz-Ring
|
||
vs. Bias-Nadel), nicht zweimal in dieselbe.
|
||
**DER ECHTE DEFEKT lag woanders und ist behoben:** `add("M30", …)`/`add("H1", …)`
|
||
hatten **Gewicht 1,5 UNBEDINGT** — auch bei Stimme 0. Derselbe Fehler wie beim
|
||
Wellen-Modul (Teil 2), hier übersehen. Eine 0-Stimme heißt „EMA-Abstand im Totband"
|
||
= **keine Aussage**, nicht „neutral". Gemessen: **M30 ist in 57,9 % der Fälle ohne
|
||
Aussage** (H1 nur 1,8 %) → Ø-Bias-Betrag **0,371 → 0,441**, Median-Verstärkung
|
||
**1,30×**, in **33,3 %** der Zeilen deutlich stärkere Nadel. Jetzt
|
||
`1.5 if vote != 0 else 0.0`; `others` filtert bereits auf `weight>0`, die
|
||
„x/y einig"-Zählung zieht mit. Live verifiziert: M30 „flach" → Gewicht 0,00,
|
||
bias −1,5/4,5 = **−0,333** statt −1,5/6,0 = −0,250. REINE ANZEIGE.
|
||
⚠ **MODUL-AUDIT 2026-07-31 (User: „überprüfe alle Module auf Inkonsistenzen"):**
|
||
systematisch alle Verdict-Module gegen die Regel „keine Aussage → Gewicht 0"
|
||
geprüft, dazu Config-, Frontend- und Level-Quellen. **5 Befunde, alle behoben:**
|
||
**(1) KI-Copilot — der größte Rest des Musters.** Ein explizites „NEUTRAL" zählte
|
||
als echte Stimme mit Gewicht 1,0. Gemessen (`verdict_votes`, n=7.084): das ist
|
||
**in 93,1 % der Fälle** der Zustand (Elliott: 1,3 %) — also der Normalzustand des
|
||
Copiloten, kein abgewogenes Urteil. Damit hatte ausgerechnet das Modul mit der
|
||
schwächsten Beleglage (`analyze_verdict_calibration.py`: nicht robust prädiktiv)
|
||
den GRÖSSTEN dämpfenden Einfluss. Jetzt `1.0 if av != 0 else 0.0`.
|
||
**(2) Elliott** analog: Gewicht nur bei gerichtetem Ziel (1,3 % der Zeilen).
|
||
**Kumulierte Wirkung aller Gewichts-Fixes des Tages: Ø|bias| 0,370 → 0,441 →
|
||
**0,569** (= 1,54×), betrifft 79,5 % der Zeilen.** Live verifiziert: KI „NEUTRAL"
|
||
→ Gewicht 0,00, bias 2,5/4,5 = **0,556** statt 2,5/5,5 = 0,455.
|
||
**(3) Stale `[zones]` speisten die Wellen-Konfidenz — entfernt.** `_sr_levels` zog
|
||
beide Kanten jeder `[zones]`-Zone als S/R-Linien heran: **16 Kanten aus einem ~10 $
|
||
TIEFEREN Regime** (72,90–77,00 bei Kurs 85,3). Aus dem CHART waren sie am 14.07.
|
||
schon entfernt worden, WEIL sie stale sind — in die Konfidenz liefen sie weiter.
|
||
Realer Audit-Fall: `market.sr` hatte GAR KEINE Widerstände (M15-Cluster sind in
|
||
frischen Trends oft leer) → der „nächste Widerstand" kam aus der `fvg`-Zone bei
|
||
**88,00** (2,66 $ weg), der echte lag bei **85,315** (0,03 $ weg). Der Abzug „dicht
|
||
unter Widerstand" (−12) greift nur innerhalb 0,5×ATR und feuerte deshalb **nie**.
|
||
Entfernen ist keine Strategieänderung, sondern das Streichen von Fremdregime-Daten;
|
||
`parse_zones` bleibt in `config.py`, die ini-Sektion bleibt (reaktivierbar).
|
||
**(4) Tote Config entfernt** (Backup `oil_widget_config.ini.bak-2026-07-31`):
|
||
`[trading] trail_timeframe = M1` (nirgends gelesen, Name suggeriert fälschlich, er
|
||
steuere die Trailing-TF) und die komplette **`[setups]`-Sektion** (11 Schlüssel,
|
||
vollständig tot).
|
||
**(5) `raise StopIteration`** als Sprung innerhalb eines breiten `except Exception`
|
||
(Trade-Marker-Export) durch ein `if` ersetzt — funktionierte, wäre aber fragil,
|
||
sobald dort jemand Logging ergänzt (Phantom-Fehler im ~5-s-Takt).
|
||
✅ **Sauber:** Frontend↔Backend (0 verwaiste Element-IDs), alle übrigen
|
||
Verdict-Gewichte, alle anderen Config-Schlüssel.
|
||
⚠ **KONSENS-ZEILE korrigiert (2026-07-31, User-Fund „4/4 Module — dabei haben wir
|
||
doch 9"):** In `vd-consensus` steckten ZWEI Fehler.
|
||
**(a) Falscher Bezug:** der Text lautete `… Module für ${sig}` mit der HEADLINE als
|
||
Bezug. Seit dem `ref`-Fix vom 30.07. zählt `agree` bei WARTEN aber die Übereinstimmung
|
||
mit der **Bias-Richtung** — deshalb stand real dort „stark LONG · 4/4 Module für
|
||
**WARTEN**". Die Richtung kommt jetzt als **`verdict.ref_dir`** vom Backend, statt im
|
||
Frontend aus `bias` nachgebaut zu werden (dort galt ein ±0,05-Totband, im Backend
|
||
`bias > 0` — die Nachbildung wäre am Rand auseinandergelaufen; dieselbe Sorte
|
||
Divergenz wie die fünf Deployment-Drift-Fälle desselben Tages).
|
||
**(b) Anderer Nenner als die sichtbaren Chips:** `total` zählt nur Module MIT Aussage
|
||
(Gewicht > 0, ohne die Welle). Nach den Gewichts-Fixes des Tages schweigen deutlich
|
||
mehr Module → „4/4" bei 9 sichtbaren Chips. Neu ist **`verdict.silent`** (Module mit
|
||
Gewicht 0) dabei, sodass die Summe aufgeht. Live: „**3 von 5 stimmberechtigten für
|
||
LONG (4 ohne Aussage)**" — 5 + 4 = 9 ✅. Bei gar keiner Aussage: „kein Modul mit
|
||
klarer Aussage". v=125.
|
||
⚠ **„WARUM WARTEN?" jetzt an der Headline (2026-07-31, v=126, User-Fund „WARTEN,
|
||
aber Konsens stark LONG"):** Der scheinbare Widerspruch entstand, weil die Welle in
|
||
aller Regel eine RICHTUNG hat und nur an einem Gate hängt — der Grund stand aber
|
||
ausschliesslich als Textzeile in der Meldungen-Karte, also weit weg von der Stelle,
|
||
an der die Frage aufkommt. `wave.breakout {pending, dir, level, need}` wurde berechnet
|
||
und gespeichert, vom Frontend jedoch **nirgends gerendert**. Jetzt hängt es direkt an
|
||
der Headline: „◌ WARTEN · M15 · **LONG, noch 0.02 bis zur Bestätigung**".
|
||
⚠ **Erste Auswertung der neuen `block_reason`-Telemetrie (3 h):** `breakout_pending`
|
||
**63 %** · `entry_room` 20 % · `min_conf` 18 % — und **kein einziges Mal** Totband
|
||
oder HTF-Gegen-Trend. Das beantwortet die offene Frage aus dem TF-Churn-Fix: die
|
||
93 % WARTEN kommen NICHT daher, dass die Welle richtungslos wäre, sondern von den
|
||
Gates — allen voran der Breakout-Bestätigung (die bis zum Fix desselben Tages
|
||
gar nicht fertig werden konnte).
|
||
⚠ **Offen (dokumentiert, nicht geändert):**
|
||
(0) **Wellen-Konfidenz aus `_draw_levels` speisen** statt aus dem Misch-Set — würde
|
||
den −12-Abzug tatsächlich auslösen, also Konfidenz senken und das 55%-Gate
|
||
verschieben. **Strategieänderung → messpflichtig.**
|
||
(a) **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. (b) `tu` wird noch als Parameter durchgereicht, seit 2026-07-06 ungenutzt.
|
||
⚠⚠ **ERSTE AUSWERTUNG DER `verdict_votes` (2026-08-05, `analyze_verdict_modules.py`,
|
||
13.568 Zeilen 24.07.–05.08.) — DIE GEWICHTUNG IST IN DER PRAXIS INVERTIERT.**
|
||
Nominal-Gewicht sagt fast nichts darüber, wie stark ein Modul die Nadel bewegt;
|
||
entscheidend ist, **wie oft es überhaupt mitredet**. Gemessener Anteil am
|
||
Gesamtgewicht, gemittelt über alle Zeilen:
|
||
| Modul | Nominal | stumm | **realer Einfluss** |
|
||
|---|---|---|---|
|
||
| **H1** | 1,5 | **2 %** | **38,5 %** |
|
||
| **Elliott** | 1,0 | **1 %** | **26,0 %** |
|
||
| M30 | 1,5 | 62 % | 12,2 % |
|
||
| Squeeze | 2,0 | 81 % | 7,0 % |
|
||
| Liq-Trend | 0,5 | 45 % | 6,1 % |
|
||
| Orderbuch | 0,5 | 62 % | 4,1 % |
|
||
| **Welle** | **3,0** | **91 %** | **3,8 %** |
|
||
| Muster | 0,25 | 77 % | 1,8 % |
|
||
| KI-Copilot | 1,0 | 97 % | 0,5 % |
|
||
⚠⚠ **DIESE TABELLE ALLEIN FÜHRT IN DIE IRRE — Korrektur am selben Tag.** Der
|
||
Anteil „über alle Zeilen" mischt zwei Dinge: die Gewichtung UND die Häufigkeit.
|
||
Entscheidend für die Frage „ist die Gewichtung kaputt?" ist der Anteil **wenn das
|
||
Modul spricht**:
|
||
| Modul | alle Zeilen | **wenn es spricht** |
|
||
|---|---|---|
|
||
| **Welle** | 3,8 % | **44,4 %** ← höchster Wert im Feld |
|
||
| H1 | 38,5 % | 39,3 % |
|
||
| Squeeze | 7,0 % | **37,3 %** |
|
||
| M30 | 12,1 % | 31,9 % |
|
||
| **Elliott** | 26,0 % | **26,2 %** |
|
||
| KI-Copilot | 0,5 % | 16,2 % |
|
||
| Liq-Trend / Orderbuch / Muster | 6,2 / 4,1 / 1,8 % | 11,1 / 10,8 / 8,1 % |
|
||
✅ **Die Gewichtung ist NICHT invertiert.** Wenn die Welle spricht, dominiert sie
|
||
mit 44,4 % — genau wie entworfen; ihre 3,8 % im Alltag sind schlicht die 91 %
|
||
gewollte Stille der Gates. Dasselbe beim Squeeze (37,3 % wenn aktiv).
|
||
⚠ **Was BLEIBT, ist ein anderer Befund:** H1 und Elliott haben in beiden Spalten
|
||
fast denselben Wert (38,5/39,3 bzw. 26,0/26,2 %), weil sie **praktisch nie
|
||
schweigen**. Sie bilden einen **permanenten Hintergrund von ~65 %**, gegen den die
|
||
episodischen Module anschieben müssen. Der Ruhezustand der Nadel ist damit
|
||
faktisch „was H1 und Elliott sagen" — und Elliott ist über 1.163 Episoden flach.
|
||
⚠⚠ **DOKUMENTATIONS-FEHLER KORRIGIERT:** hier stand, Elliott habe „Gewicht nur bei
|
||
gerichtetem Ziel (1,3 % der Zeilen)". Die Daten sagen das Gegenteil —
|
||
**`elliott_w > 0` in 13.558 von 13.568 Zeilen (99,9 %)**. Der Fix vom 31.07.
|
||
(„keine Aussage → Gewicht 0") greift bei Elliott praktisch nie, weil praktisch
|
||
immer ein Ziel existiert. Elliott ist damit die **zweitlauteste Stimme im
|
||
gesamten Verdict**.
|
||
⚠ **Prädiktivität — Sichtung, KEIN Urteil** (Forward-Return 60 min, gegen die
|
||
Drift-Null, Minutentakt zu **Episoden** zusammengefasst, sonst zählt man dieselbe
|
||
Marktlage hundertfach): Elliott **−0,031/−0,020 bei 49 % Treffern über 1.163
|
||
Episoden** — die mit Abstand größte Stichprobe, und sie ist flach. Welle und M30
|
||
kippen zwischen den Hälften; KI-Copilot und Squeeze sind beidseitig positiv, aber
|
||
mit 53 bzw. 100 Episoden dünn. H1 hat nur **19** Episoden (es wechselt selten) —
|
||
prädiktiv nicht beurteilbar, sein Einfluss-Anteil ist aber reine Arithmetik und
|
||
damit belastbar.
|
||
⚠ **Grenzen:** 12 Tage = EIN Regime; die Reminder-Schwelle steht bewusst bei
|
||
25.000 Verdicts. **Die Einfluss-Tabelle ist der robuste Teil** (Auszählung über
|
||
13.568 Zeilen), die Return-Spalten sind der verrauschte.
|
||
✅ **Nichts geändert** — die Bias-Nadel ist **reine Anzeige**; die Order steuert
|
||
die Welle über die Headline, nicht der Konsens. Eine Umgewichtung wäre eine
|
||
Anzeige-Änderung mit Wirkung auf die MANUELLE Entscheidung und damit
|
||
messpflichtig, sobald genug Verdicts vorliegen.
|
||
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.
|
||
- ⚠⚠ **ZWEI NEUE TABS: „SIG Live" und „SIG Anzeige" (User-Vorgabe 2026-08-05,
|
||
v=141).** Sortiert wird nach **WIRKUNG, nicht nach Thema** — und die Zuordnung ist
|
||
**am Code prüfbar**, nicht Geschmackssache: `engine._verdict` ruft
|
||
`add(name, vote, gewicht, …)`; wer ein Gewicht > 0 bekommt, verschiebt die
|
||
Bias-Nadel und gehört nach „SIG Live".
|
||
| Tab | Karten | Begründung |
|
||
|---|---|---|
|
||
| **SIG Live** | KI-Copilot · Chartmuster | Verdict-Gewicht **1,0** bzw. **0,25** (`_PAT_W`) |
|
||
| **SIG Anzeige** | Marktstruktur · Kerzen-Anatomie | gemessen **ohne** Einfluss, als Signal verworfen |
|
||
⚠ **„Gewicht" heißt NICHT „belegt".** Der KI-Copilot hat Stimmrecht, ist aber
|
||
gemessen nicht robust prädiktiv — genau deshalb ist die Trennung nach Wirkung
|
||
sinnvoll: sie zeigt, was die Empfehlung TATSÄCHLICH bewegt, unabhängig davon, wie
|
||
gut es belegt ist. Chartmuster steht aus demselben Grund in „Live", obwohl die
|
||
Klasse als Signal verworfen wurde — seit dem 2026-08-01 hat sie 5 % Stimmanteil.
|
||
⚠ **Auf dem Dashboard BLEIBEN** Trade-Leiste, Gesamtempfehlung, Meldungen und
|
||
Statistik (live). Die „Meldungen"-Karte enthält zwar einflussreiche Teile
|
||
(Squeeze-Stimme, S/R-Close-Hinweis, Flip-Close), ist aber der **Live-Meldungsstrom
|
||
zum laufenden Trade** — sie gehört an die Stelle, an der gehandelt wird, nicht in
|
||
einen Modul-Tab. Wer das anders will, verschiebt `#card-pos`.
|
||
Umgesetzt als reiner Markup-Umzug (`showView` bildet `data-view="siglive"` auf
|
||
`#view-siglive` ab, keine JS-Änderung nötig); CSS-Regel `#view-siglive > .card`
|
||
analog zum Statistik-Tab, weil beide außerhalb des `<main>`-Grids liegen.
|
||
Verifiziert am ausgelieferten HTML: 7 Tabs, 7 Views, keine doppelten IDs, keine
|
||
verwaisten JS-Referenzen.
|
||
- **Karten** (Dashboard, von oben): Gesamtempfehlung · **Meldungen** · Marktstruktur ·
|
||
KI-Copilot (jetzt im Tab „SIG Live") ·
|
||
**Statistik (live) ganz unten**.
|
||
⚠ **Drei Karten am 2026-08-05 in den Statistik-Tab verschoben (User-Vorgabe,
|
||
v=138): „Manuelle Trades" (`#card-manual`), „Letzte Trades" (`#card-trades`) und
|
||
„Kurslücken" (`#card-gaps`)** — sie sind Rückschau, das Dashboard soll das
|
||
Handlungsrelevante zeigen. **Reiner Umzug im Markup**, keine Logik berührt: die
|
||
drei werden weiterhin von `render()` aus dem Snapshot gefüttert und hängen NICHT
|
||
an `loadStats()`; ein ausgeblendeter View ist nur `display:none`, die Elemente
|
||
bleiben im DOM (sonst wären ihre IDs beim Rendern weg — genau die Null-Falle aus
|
||
der Cache-Regel). ⚠ Dazu nötig: **`#view-stats > .card{margin:11px 10px}`** in
|
||
`style.css` — das Dashboard ist ein `<main>`-GRID mit eigenem Padding, `#view-stats`
|
||
eine schlichte `<section>`; ohne die Regel klebten die Karten randlos aneinander
|
||
und `.card.wide{grid-column:1/-1}` liefe ins Leere. Verifiziert am ausgelieferten
|
||
HTML (alle drei nach `id="view-stats"`, `#card-stats-live` bleibt im Dashboard),
|
||
keine doppelten IDs, Tag-Balance geprüft.
|
||
(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.
|
||
- **NACHTEST über ALLE Zeitebenen = ERNEUT VERWORFEN (`backtest_structure_tf.py`,
|
||
2026-08-03, User-Beobachtung im M5-Chart: „der Kurs folgt dem Kanal und bricht die
|
||
untere Linie nicht — können wir das nutzen?"):** Die alte Messung lief NUR auf M30,
|
||
M5 war streng genommen ungetestet. Jetzt mit dem **kanonischen Live-Exit**
|
||
(`exit_model.LIVE`: Trail 1,0 + Time-Stop + TP — die alte Datei nutzt eine Exit-KOPIE
|
||
mit Trail 1,5 OHNE Time-Stop) über M5/M15/M30, zwei Lesarten des M5-Kanals und drei
|
||
Regel-Varianten, 80k Bars je Zeitebene, 2 Hälften, Echtkosten:
|
||
| Zeitebene / Kanal | X=0,20 | X=0,30 | +Swing |
|
||
|---|---|---|---|
|
||
| **M5, 60 Bars** (=5 h, „was man im Chart sieht") | **−0,201/−0,017** | −0,170/−0,054 | −0,161/−0,082 |
|
||
| M5, 360 Bars (=30 h, zeitgleich zu M30) | −0,206/−0,017 | −0,179/−0,054 | −0,159/−0,082 |
|
||
| M15, 120 Bars | −0,032/−0,003 | −0,068/−0,034 | −0,043/−0,033 |
|
||
| M30, 60 Bars (die ALTE Messung) | −0,103/−0,019 | −0,094/−0,013 | −0,090/−0,024 |
|
||
**24 Zellen, ALLE negativ**, PF 0,66–0,99. Drei Befunde:
|
||
(a) **Trefferquote 37–43 %** über 1.200–2.400 Berührungen je Hälfte — der Kanalrand
|
||
hält in WENIGER als der Hälfte der Fälle. Die im Chart sichtbaren „Halter" sind
|
||
Selektion: die Durchbrüche sehen hinterher nicht mehr nach Kanal aus.
|
||
(b) **M5 ist die SCHLECHTESTE Zeitebene** (−0,201 vs. −0,103 auf M30) — feiner
|
||
auflösen erzeugt mehr Berührungen, und jede kostet Spread.
|
||
(c) **Keine Parameterfrage:** 60-Bar- und 360-Bar-Kanal liefern praktisch IDENTISCHE
|
||
Zahlen. Zwei so verschiedene Fenster mit gleichem Ergebnis → es liegt am Mechanismus.
|
||
✅ **Legacy-Recheck-Bonus:** die M30-Zeile **reproduziert** den Befund vom 20.07.
|
||
(−0,103/−0,019 gegen damals −0,05…−0,11) — der alte Schluss war NICHT durch das
|
||
abweichende Exit-Modell verzerrt.
|
||
**Mechanismus:** Der Kanalrand ist eine Regressionslinie durch die letzten N Closes —
|
||
dahinter liegt **nichts**: keine Order, kein Pivot, keine Struktur. Er hält nur,
|
||
solange der Trend ohnehin läuft. Genau darin liegt der Unterschied zu **P(break) auf
|
||
einem echten S/R-Level** (Pivot = Stelle, an der real gehandelt wurde), das
|
||
out-of-sample kalibriert ist (AUC 0,65 bzw. 0,68–0,72). „Linie im Chart" ist nicht
|
||
„Struktur im Markt". **19. verworfener Signal-Eingriff.**
|
||
- **⚠⚠ 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.
|
||
- **RICHTUNGS-PFEILE im MT5 statt des Kegels (`AR;<art>;<von>;<nach>;<text>`,
|
||
Indikator v1.32, 2026-07-31, User: „anstatt des Trichters hätte ich gerne einen
|
||
Pfeil, der die wahrscheinlichste Kursentwicklung anzeigt").**
|
||
⚠ **Randbedingung, die den Entwurf bestimmt:** die FREIE Richtungsfrage ist im
|
||
Projekt gemessen ein **Münzwurf** (`analyze_reversal.py`: AUC 0,499–0,509
|
||
out-of-sample, **in-sample nur 0,52** — das Modell erklärt nicht mal seine eigenen
|
||
Trainingsdaten). Ein frei schwebender Prognose-Pfeil wäre unbelegt — und gefährlicher
|
||
als der Kegel, weil er überzeugender aussieht. Deshalb DREI Pfeile mit jeweils
|
||
EIGENER, benannter Beleglage statt einer Prognose:
|
||
| Art | Farbe | Aussage | Beleg |
|
||
|---|---|---|---|
|
||
| **L** | blau | am NÄCHSTEN Level: Durchbruch oder Abprall | P(break), AUC 0,65 oos, live kalibriert (27 % vs 28 %) |
|
||
| **S** | neongrün | Squeeze-Ausbruch | das EINZIGE 2-Stichproben-validierte Richtungssignal (ØR +0,14…+0,23) |
|
||
| **K** | grau | Konsens der Verdict-Module | ⚠ **NICHT kalibriert** (in H2 sogar invertiert) → heißt „Konsens", nicht „Prognose" |
|
||
**L im Detail:** genommen wird das NÄHERE der beiden Level. P(break) > 55 % → Pfeil
|
||
DURCH das Level (Ziel = Level ± 0,5×ATR = die Bestätigungsmarke); < 45 % → Pfeil WEG
|
||
vom Level (Abprall); **45–55 % → WAAGERECHTER Pfeil „unentschieden"** — dort ist die
|
||
Zahl ein Münzwurf, und daraus eine Richtung zu zeichnen wäre genau der Fehler, den
|
||
dieser Entwurf vermeiden soll. **S** erscheint nur bei `state=='active'` und wird
|
||
dicker gezeichnet (Breite 3 statt 2) — der validierte Pfeil ist der auffälligste.
|
||
**K** erst ab |bias| ≥ 0,2, Länge proportional (max 2×ATR).
|
||
⚠⚠ **KORREKTUR NOCH AM SELBEN ABEND (v1.33, User: „Trichter anstatt Pfeil wird
|
||
angezeigt"):** L und K starten am SELBEN Punkt (aktueller Kurs) und zeigten real in
|
||
ENTGEGENGESETZTE Richtungen (`AR;L;…;85.110;63% Abprall` nach unten,
|
||
`AR;K;…;85.479;Konsens +1.00` nach oben). Zwei divergierende Linien aus einem Punkt
|
||
sehen aus wie **genau der Kegel, den sie ersetzen sollten** — der Nutzer sah keinen
|
||
Pfeil, sondern wieder einen Trichter. **Gefragt war EIN Pfeil, gebaut waren drei.**
|
||
Behoben: **`InpShowConsensus` per Default FALSE** (der Konsens steht ohnehin als
|
||
Bias-Nadel im Dashboard) und **`InpArrBars` 14 → 8** (kürzer, damit die Pfeilspitze
|
||
im sichtbaren Bereich liegt). Normalfall jetzt: **EIN Pfeil** (L, der kalibrierte);
|
||
S kommt nur beim Squeeze-Ausbruch dazu. `InpShowConsensus=true` holt K zurück.
|
||
⚠ Lehre: „sie messen Verschiedenes, also dürfen sie auseinanderzeigen" ist logisch
|
||
richtig und als BILD trotzdem falsch — zwei Linien aus einem Ursprung liest niemand
|
||
als zwei Aussagen.
|
||
Umsetzung MQL5: `OBJ_ARROWED_LINE` vom aktuellen Bar über `InpArrBars`=14 Bars nach
|
||
rechts, Label an der Spitze. ⚠ Das Label heißt `ALBL_*` und NICHT `T*` — sonst würde
|
||
`RepositionLabels()` es an den rechten Rand ziehen und von der Pfeilspitze lösen.
|
||
Schalter `InpShowArrows`, Farben einzeln. **Der Kegel bleibt im Code** und in der
|
||
Dashboard-Kachel; der Bot exportiert ihn nur nicht mehr ins Chart
|
||
(`[trading] export_cone=false`, `true` holt ihn zurück).
|
||
- ⚠ **KACHEL „Erwartete Spanne" ENTFERNT, Kernwerte in die Gesamtempfehlung
|
||
gezogen (User-Vorgabe 2026-08-05, v=140).** `#card-cone`/`#cone-list` sind raus;
|
||
neu ist die einzeilige `#vd-cone` **unter der Bias-Nadel** in `#card-verdict`:
|
||
„📐 erwartete Spanne · 30′ 75,99–76,77 · 60′ 75,82–76,94 · 120′ 75,55–77,21 ·
|
||
trifft 77 %". Gezeigt wird nur noch das **80 %-Band** je Horizont; das enge
|
||
50 %-Band und der Erklär-Absatz sind entfallen und stecken im `title`.
|
||
⚠ **Platzierung und Farbe sind Absicht:** die Zeile steht UNTER der Nadel und ist
|
||
neutral/randlos — sie zeigt **Streuung, nicht Richtung**, und darf optisch nicht
|
||
mit dem Bias konkurrieren. Beschriftet bleibt sie mit der **real gemessenen**
|
||
Abdeckung (~77 %), nicht mit dem Nennwert 80 %.
|
||
**Backend unverändert aktiv** (`core/cone.py`, Snapshot `cone`, MT5-Export `CN;…`).
|
||
Verifiziert am ausgelieferten HTML (`card-cone` weg, `vd-cone` da, keine verwaisten
|
||
JS-Referenzen).
|
||
- **Wahrscheinlichkeits-Kegel „Erwartete Spanne" (`core/cone.py` + ehem. 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.
|
||
⚠⚠ **DRITTE MESSUNG 2026-08-05 (`backtest_candle_fade2.py`, User-Wunsch „messe
|
||
erneut") — ALLE 40 ZELLEN NEGATIV.** Bewusst wurde nur EINE neue Achse geprüft, die
|
||
am selben Tag Squeeze UND Signal von negativ auf positiv gedreht hat: **der
|
||
Einstiegspreis** (Market am Bar-Close gegen ruhende **Limit-Order am
|
||
Kerzen-Extrem**), dazu der **kanonische Exit** (Trail 1,0 statt der alten Kopie mit
|
||
1,5). Sequentiell EIN Slot, Echtkosten, 80k M5, 2 Halbjahre, Kontrollgruppe „ohne
|
||
Volumen", Regel vorab fixiert (ØR>0 UND PF>1 beidhälftig · schlägt die Kontrolle ·
|
||
Nachbar-Schwellen kippen nicht · n≥100).
|
||
| Volumen | Market H1/H2 | **Limit am Extrem** H1/H2 |
|
||
|---|---|---|
|
||
| Kontrolle | −0,142 / −0,095 | −0,191 / −0,103 |
|
||
| ≥2,0× | −0,134 / −0,077 | −0,177 / −0,058 |
|
||
| ≥2,5× | −0,051 / −0,111 | −0,096 / −0,072 |
|
||
**Keine einzige Variante ist irgendwo positiv**, auch die Gültigkeitsdauer der
|
||
Limit-Order (3/6/12 Bars) ändert nichts.
|
||
⚠⚠ **DER LEHRREICHE TEIL — der Ausführungs-Hebel ist NICHT universell.** Beim
|
||
Squeeze und beim Signal dreht die ruhende Order das Vorzeichen; hier macht sie es
|
||
**schlechter**. Der Grund ist strukturell: ein **Breakout**-Einstieg wird von einer
|
||
Stop-Order genau im Moment der Bestätigung gefüllt — ein **Fade**-Einstieg per Limit
|
||
am Extrem füllt nur, wenn die Klimax sich weiter ausdehnt, also **genau in den
|
||
Fällen, in denen man falsch liegt**. Sichtbar an der Kennzahl-Schere: die
|
||
Trefferquote STEIGT (38,0 → 40,1 %, bei Vol≥2,5 sogar 43,2 → 48,5 %), der ØR fällt
|
||
trotzdem. Der bessere Preis wird mit der schlechteren Auswahl bezahlt.
|
||
**Merksatz: ruhende Order hilft bei Momentum-Entries, schadet bei Reversion-Entries.**
|
||
⚠ Nebenbefund zur Stichprobengröße: im 12k-Vorlauf gab es noch positive Zellen
|
||
(Vol≥2,5 H1 **+0,207**, Vol≥3,0 Limit H1 +0,291) — über 80k verschwinden sie
|
||
restlos. Dasselbe Muster wie beim Auto-Signal-Vorlauf am 30.07. **Ein kleiner Lauf
|
||
hätte hier zum dritten Mal zum Fehl-Einbau geführt.**
|
||
✅ **Kachel bleibt unverändert reine Anzeige.** Kein neuer Zähl-Eintrag in der
|
||
Verworfen-Liste — es ist dieselbe Klasse wie #18, jetzt zum dritten Mal bestätigt.
|
||
- **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`.
|
||
- **⚠⚠ ERKENNUNG WAR DEFEKT — zwei Fehler (Fix 2026-08-03, User-Meldung „die
|
||
Kurslückenerkennung funktioniert nicht richtig"):**
|
||
**(1) Die größte Lücke des Jahres wurde um 3 Cent verfehlt.** Die Definition
|
||
verlangte ein lückenloses **Vakuum zwischen den TAGESSPANNEN**. Am Wochenende
|
||
31.07.→03.08. fiel WTI von Schluss **86,33** auf Eröffnung **79,76** = **−6,57 $** —
|
||
unsichtbar, weil Montags Hoch (81,67) das Freitags-Tief (81,64) um **0,03 $**
|
||
überlappte. In einem 24/5-Markt überlappen die Tagesspannen fast immer; die
|
||
strenge Regel fand im ganzen Jahr nur **5** Gaps.
|
||
**(2) Alle Gap-Datumsangaben waren einen Tag zu früh.** `int(b["time"]) −
|
||
_BROKER_OFFSET_S` zog 3 h von einer Bar-Zeit ab, die MT5 bereits als naive
|
||
Broker-Zeit liefert → jeder D1-Bar rutschte auf den Vortag (die gemeldete Lücke
|
||
„2026-07-26" war real der **27.07.**). Am Jahreswechsel hätte es zusätzlich den
|
||
`year`-Filter verschoben.
|
||
**Fix:** zwei getrennte Gap-Begriffe im Snapshot (`kind`):
|
||
· **`vacuum`** = die alte, strenge Definition — **NUR diese speisen `zone_lines()`
|
||
→ `_sr_levels` → Wellen-Konfidenz**, denn nur auf ihnen wurde gemessen. Der
|
||
Strategie-Eingang ist damit **bitgenau unverändert** (verifiziert: `zone_lines()`
|
||
liefert vorher wie nachher `[88.288]`).
|
||
· **`close_open`** = Schluss→Eröffnung, also das, was ein Mensch „Kurslücke" nennt.
|
||
**REINE ANZEIGE**, ab `min_close_open`=0,50 $, füllt auch am selben Tag.
|
||
Ergebnis live: 4 offene Lücken statt 1 (1 Vakuum + 3 Eröffnungslücken), darunter
|
||
endlich die **−6,57-$-Wochenendlücke mit Fill bei 86,33**. Frontend kennzeichnet
|
||
beide Arten (`Vakuum` / `Eröffnung`) mit Tooltip, v=133.
|
||
⚠ **Bewusst NICHT getan:** die `close_open`-Lücken in die Konfidenz einzuspeisen.
|
||
Das wäre ein ungemessener Strategie-Eingriff — und der gemessene Befund unten
|
||
beruht auf der Vakuum-Definition. Wer sie einbeziehen will, misst vorher.
|
||
- **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.
|
||
- **⚠⚠ ERÖFFNUNGSLÜCKEN (`close_open`) NACHGEMESSEN = AUCH KEIN MAGNET
|
||
(`backtest_gaps_closeopen.py`, 2026-08-11, User: „vielleicht will der Kurs
|
||
erst die Kurslücke schliessen?").** Die Frage war **berechtigt offen**: der
|
||
Befund darüber beruht auf der **`vacuum`**-Definition, und `core/gaps.py`
|
||
sagt ausdrücklich, dass nur die gemessen wurde („wer `close_open` einbeziehen
|
||
will, misst vorher"). Die Klasse, die ein Mensch „Kurslücke" nennt
|
||
(Schluss → Eröffnung, typisch die Wochenendlücke), war **nie geprüft**.
|
||
124 Lücken ≥0,50 $ über 4.359 D1-Bars, 2 Hälften, 95-%-Bootstrap.
|
||
⚠⚠ **Die Füllquote sieht überzeugend aus — und ist nichtssagend:**
|
||
| Horizont | H1 Füllung / **Kontrolle** | H2 Füllung / **Kontrolle** |
|
||
|---|---|---|
|
||
| 3 Tage | 71,0 % / **69,4 %** | 74,2 % / **74,2 %** |
|
||
| 5 Tage | 80,6 % / **77,4 %** | 75,8 % / **75,8 %** |
|
||
| 10 Tage | 88,7 % / **83,9 %** | 80,6 % / **80,6 %** |
|
||
| 20 Tage | 90,3 % / **87,1 %** | 83,9 % / **87,1 %** ⚠ Kontrolle höher |
|
||
✅ **Die Kontrolle ist der ganze Befund: dieselbe Distanz, dieselbe Bar, aber
|
||
GEGENrichtung.** Damit sind Volatilität, Regime und Zeitraum automatisch
|
||
gleich — übrig bleibt nur die Frage, ob die Lücke eine RICHTUNG bevorzugt.
|
||
In H2 ist sie bei 3/5/10 Tagen **exakt identisch**. **Alle 24 Differenz-KI
|
||
enthalten die Null**, die vorab fixierte Regel ist in keiner Zelle erfüllt.
|
||
Kontrolle B bestätigt: an Nicht-Lücken-Tagen läuft der Kurs dieselbe Strecke
|
||
binnen 10 Tagen zu **73,5 % nach unten und 75,5 % nach oben** — die ~75–80 %
|
||
sind schlicht die Basisrate eines 1–2-$-Laufs in WTI, keine Eigenschaft der
|
||
Lücke. ⚠ **GROSSE Lücken ≥1,0 $ kippen zusätzlich das Vorzeichen** zwischen
|
||
den Hälften (H1 10/20 Tage: Kontrolle **schlägt** die Füllung 81,8/72,7 bzw.
|
||
86,4/72,7; H2 umgekehrt) — bei n=22/23 das klassische Rauschmuster.
|
||
⚠ **Fairerweise der einzige konsistente Punkt:** die **Ein-Tages**-Füllung
|
||
(„Lücke wird am selben Tag zugehandelt") ist in beiden Hälften UND beiden
|
||
Läufen richtungs-positiv (+4,9/+9,7 bzw. +4,6/+21,7 Pp). Zwei gleichgerichtete
|
||
Hälften sind mehr als eine — „KI enthält 0" heisst **nicht widerlegt, sondern
|
||
nicht belegt**. Für einen Eingriff reicht es bei n=22–62 nicht.
|
||
✅ **Dritte Bestätigung derselben Lehre**, nach Vakuum-Gaps und Fibonacci:
|
||
die Frage ist nie „füllt/hält es?", sondern **„häufiger als eine willkürliche
|
||
Linie gleicher Distanz?"**. Ohne diese Kontrolle misst man nur, dass Kurse
|
||
sich bewegen. **Keine Änderung** — `close_open` bleibt reine Anzeige und
|
||
fliesst weiterhin NICHT in `zone_lines()`.
|
||
⚠ **Der Anlassfall entschied sich noch während der Messung dagegen:** die
|
||
Lücke vom 10.08. (Füllung 77,383) war am 11.08. nicht geschlossen — der Kurs
|
||
stand bei **82,849**, also **5,47 $ in der Gegenrichtung**. Ein Einzelfall
|
||
belegt nichts, aber er illustriert den Mechanismus des Ursprungsbefunds: das
|
||
Gap blieb offen, **weil** der Trend von ihm weglief.
|
||
- **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.
|
||
- **UI-Vorbehalt an der Gesamtempfehlung (2026-08-06, v=143):** Unter der Headline
|
||
steht jetzt eine feste Zeile (`#vd-note`) — „Richtung ≈ Münzwurf (gemessen 48–51 %
|
||
Treffer) … lies das als **Auslöser für eine Order am Level**, nicht als
|
||
Kursprognose". Der Konfidenz-Ring ist mit **„Score"** beschriftet
|
||
(`.vd-ring-lbl`) statt nackt in Prozent. Grund: `analyze_signal_live.py` (48–51 %
|
||
Treffer über 1.071 Live-Episoden) und `analyze_verdict_calibration.py` (conf_pct
|
||
nicht kalibriert, in H2 invertiert). Solange dort „▲ LONG · 89 %" stand, lud die
|
||
Anzeige zu einer Erwartung ein, die die Messung nicht deckt.
|
||
⚠ Bewusst **statisches Markup, kein JS** — es ist ein Dauerhinweis, kein Zustand;
|
||
damit entfällt auch jedes Null-Risiko beim Render (s. Cache-Regel oben).
|
||
- **⚠⚠ VIER VERBESSERUNGEN AN DER GESAMTEMPFEHLUNG (User-Vorgabe 2026-08-06,
|
||
v=144) — alle aus dem Gemessenen abgeleitet, KEIN neues Signal.** Ausgangspunkt:
|
||
die Karte hat genau eine belegte Funktion, das **Veto** (s. Ausrichtungs-Split);
|
||
als Prognose ist sie ein Münzwurf. Verbessert wird also das Veto und die
|
||
Sichtbarkeit — nicht die Vorhersage.
|
||
**(1) SELBST-KALIBRIERUNG — die Karte zeigt ihre eigene Trefferquote.**
|
||
Neue Tabelle **`rec_outcomes`** (DB-Backup `…bak-2026-08-06-recoutcomes`):
|
||
`history.log_rec_outcome` schreibt **eine Zeile je EPISODE** (Richtungswechsel,
|
||
nicht je Minute — sonst zählt man dieselbe Marktlage dutzendfach);
|
||
`engine._evaluate_rec_outcomes` trägt ~alle 5 min im Trend-Loop den
|
||
Forward-Return nach 30/60 min aus `candles_m1` nach (⚠ Broker-Offset, wie bei
|
||
`_evaluate_pbreak_predictions`). `history.rec_accuracy(200)` → Snapshot
|
||
**`rec_track`** → Zeile **`#vd-selfcal`**.
|
||
⚠ **Warum das der grösste Hebel ist:** die Qualität der Empfehlung war bis heute
|
||
unsichtbar — sie musste auf Nachfrage rückwirkend gemessen werden. Dasselbe
|
||
Muster (`pbreak_predictions`) hat den Ausfall des P(break)-Modells LIVE
|
||
aufgedeckt, was kein Backtest konnte. Die Zeile ist **gegen 50 % zu lesen**,
|
||
nicht gegen 0. Fehlen Bars (Wochenende), bleibt die Zeile offen statt falsch
|
||
bewertet; nach 6 h wird sie geschlossen. **Mit 4 Szenarien getestet**
|
||
(`tests_rec_outcomes.py`): Episoden-Dedup · WARTEN re-armt · Auswertung inkl.
|
||
Offset in beide Richtungen · fehlende Bar → bleibt offen · Aggregation.
|
||
⚠ Zwei Fehler dabei waren MEINE Testfehler, nicht Code-Fehler: die Test-Kerzen
|
||
aus dem Vorabschnitt lagen im Fenster des nächsten, und die DB-Kopie enthält
|
||
**echte** `candles_m1` — der Fall „keine Bar" entsteht erst, wenn das Fenster
|
||
wirklich geleert wird.
|
||
**(2) AUSRICHTUNGS-SPLIT auf die volle Historie + je Lot.** `alignment_stats`
|
||
lief auf den **letzten 40** Trades und zeigte absolute Euro. Jetzt über **alle
|
||
1.216** (Default `last_n=0`) und zusätzlich **`pnl_lot`** — absolute
|
||
Euro-Vergleiche sind bei wechselnder Positionsgrösse ungültig (Lehre 04.08.).
|
||
Der Order-Dialog zeigt beides. Damit steht die einzige beidhälftig robuste
|
||
Aussage der Karte auf ihrer vollen Stichprobe: **gegen das Signal −6,60 €/Lot**.
|
||
**(3) BLOCK-GRUND SICHTBAR** (`engine._block_mix_cached` → Snapshot
|
||
`block_mix` → `#vd-blocks`): „heute X % ohne Block · entry_room … · min_conf …".
|
||
Auf die Frage „warum gab es so wenige Empfehlungen?" war bisher eine DB-Abfrage
|
||
nötig. ~120 s gecacht, reine Anzeige.
|
||
**(4) ELLIOTT ENTMACHTET — Verdict-Gewicht 1,0 → 0** (gemessen
|
||
`analyze_verdict_modules.py`, 15.544 Zeilen). Elliott hatte mit **1.384
|
||
Episoden die grösste Stichprobe der ganzen Tabelle** und ist darin **flach**:
|
||
Überschuss −0,075/+0,032 ×ATR, **50 % Treffer**. Gleichzeitig war es mit
|
||
**25,9 % die zweitlauteste Stimme** — weil es in nur 1 % der Zeilen schweigt.
|
||
Mit H1 (38,1 %) bildete es einen Dauerhintergrund von ~64 %. **Der Chip bleibt**
|
||
(das Ziel ist als Kontext nützlich), nur die Stimme entfällt.
|
||
⚠ **Sichtbare Folge: die Nadel schlägt stärker aus.** Real beim Umbau
|
||
+0,33 → **+1,00**, weil nur noch H1 (1,5) und Liq-Trend (0,5) sprachen. Mit
|
||
wenigen sprechenden Modulen kann ein einzelnes die Nadel auf ±1 ziehen — das ist
|
||
ehrlicher als eine künstlich gedämpfte Nadel, sieht aber dramatischer aus.
|
||
⚠ **Reine Anzeige-Änderung** — die Order steuert die Welle über die Headline,
|
||
nicht der Konsens. Zurück: `ew = 1.0 if ev != 0 else 0.0` in `_verdict`.
|
||
⚠ **Nicht angefasst, aber auffällig** (für die 25.000-Verdict-Messung):
|
||
**Orderbuch** ist über 2.688 Episoden beidhälftig NEGATIV (−0,054/−0,066,
|
||
49 % Treffer) bei Gewicht 0,5 — der einzige konsistent falsche Beitrag.
|
||
**Muster** (+1,27/+0,67, 58 %, n=88) und **Liq-Trend** (+0,20/+0,32, n=132)
|
||
sehen gut aus, sind aber zu dünn. Alle übrigen kippen zwischen den Hälften.
|
||
- **⚠⚠ HYPERLIQUID: WAS ES LIEFERT UND WAS DAVON TRÄGT (2026-08-06, User-Frage
|
||
„können wir mehr HL-Daten nutzen?").** Direkt gegen die API geprüft, nicht aus
|
||
dem Code geschlossen.
|
||
**Angebot:** vier Öl-Märkte, zwei mit echter Liquidität — `xyz:CL` (unser Feed,
|
||
OI 3,07 Mio, Tagesvolumen 316 Mio) und `xyz:BRENTOIL` (OI 2,25 Mio); `flx:OIL`
|
||
und `cash:WTI` sind tot (OI 0). Je Markt `mark_px` · **`oracle_px`** ·
|
||
**`funding`** · **`open_interest`** · `day_volume`, dazu `l2Book` mit 20 Levels
|
||
je Seite. **Historie:** `fundingHistory` gibt **5.095 Stundenwerte über 7 Monate**
|
||
(inkl. `premium`), `candleSnapshot` H1-Kerzen, `predictedFundings` venue-übergreifend.
|
||
⚠ **Open Interest hat KEINE Historie** — nur der Momentanwert; OI wäre
|
||
sammelpflichtig und bleibt offen.
|
||
✅ **Netz-Frage geklärt (sah zuerst nach einem Defekt aus):** `config.ini` sagt
|
||
`network = testnet`, und **Testnet hat ein LEERES Buch** (0 Bids/0 Asks, OI 2,55
|
||
gegen 3,07 Mio). Es sind aber **zwei** Schalter: `[hyperliquid] network` steuert
|
||
das **Handeln** (bewusst aus), `[sim] price_network` die **Daten** und steht per
|
||
Default auf mainnet — live bestätigt (`/api/market` meldet `network: mainnet`).
|
||
⚠ Bleibt eine Falle: ein Wort in der falschen Sektion, und der Bot zeichnet
|
||
stillschweigend Wände aus einem leeren Buch.
|
||
**(1) MEHR ORDERBUCH-DATEN = NEIN, zweimal unabhängig gemessen.** Das HL-Projekt
|
||
selbst (`bookflow_report.txt`): Bid/Ask-Druck auf den CFD −0,61/−0,52 bp und
|
||
**KIPPT**; Wände halten in 47–50 %; die eigene Schwelle dort lautet **„< ~3 bp
|
||
ist von den Kosten gefressen"** — gemessen sind es 0,2–1,0 bp. Dazu der
|
||
Lead-Lag-Befund: **Pepperstone führt** (Peak k=−1 ≈ 6 s), HL-Richtung trifft nach
|
||
6 s zu **51 %** (n=545). Alles Preis-Abgeleitete von HL läuft hinterher.
|
||
**(2) MODUL „ORDERBUCH" ENTMACHTET — Gewicht 0,5 → 0.** Unabhängige Bestätigung
|
||
aus `analyze_verdict_modules.py`: über **2.688 Episoden beidhälftig NEGATIV**
|
||
(−0,054/−0,066 ×ATR, 49 % Treffer) — der einzige konsistent falsche Beitrag im
|
||
Feld. Chip bleibt, Stimme entfällt. Zurück: `ow = 0.5`.
|
||
**(3) FUNDING/PREMIUM GEMESSEN = TRÄGT NICHT (`analyze_hl_funding.py`).**
|
||
Positionierung statt Preis, also nicht vom Lead-Lag-Einwand betroffen — und dank
|
||
`fundingHistory` sofort prüfbar. 5.095 Stundenwerte, 3.626 mit passender H1-Bar,
|
||
gegen den **Pepperstone-CFD**, 2 Hälften, gegen die Drift.
|
||
⚠⚠ **Der erste Lauf meldete mehrere „robuste" Buckets — und war falsch.** Zwei
|
||
Fehler in meinem eigenen Skript: **(a) überlappende Forward-Fenster** (stündliche
|
||
Beobachtungen, 4/12/24-h-Horizont → aufeinanderfolgende Zeilen teilen fast ihr
|
||
ganzes Fenster; aus n=506 werden ~21 unabhängige Fälle) und **(b) global
|
||
gebildete Quintile**, wodurch die Bucket-Zugehörigkeit mit der Zeit konfundiert
|
||
war (Q5 hatte 506 Fälle in H1, aber 219 in H2 = Regimewechsel, nicht Merkmal).
|
||
⚠ Dritter Hinweis, der stutzig machte: **Funding- und Premium-Tabelle waren
|
||
nahezu identisch** — HL rechnet das Funding AUS dem Premium; es war eine
|
||
Messung, doppelt berichtet.
|
||
✅ **Entüberlappt und mit Quintilen je Hälfte hält KEIN Bucket** die Regel; die
|
||
verbleibenden ✅ sitzen bei n=15–30 und sind Rauschen. **Kein Verdict-Gewicht.**
|
||
**(4) HL-KONTEXT IN DEN MELDUNGEN (reine Anzeige, v=145).** `hl_walls.market()`
|
||
(lokales Dashboard, keine neue externe Abhängigkeit) → Snapshot **`hl_ctx`** →
|
||
Zeile **`#hl-note`**: „Perp −0,03 $ zum Oracle · Funding −0,0083 %/h · OI
|
||
3,07 Mio". Kein Gewicht, kein Trigger; der Tooltip nennt die Messung.
|
||
⚠ **Zwei Bau-Fallen, beide behoben:** der Aufruf lief zunächst immer ins Leere,
|
||
weil er den **2-s-Timeout der Wände** erbte — `/api/market` fragt HL live ab und
|
||
braucht real **7,2 s** (eigener `_MARKET_TIMEOUT_S=12`); und der stille `except`
|
||
machte daraus ein wortloses `None`. Danach hätte er den **Trend-Loop 7 s
|
||
blockiert** → läuft jetzt im **Daemon-Thread**, wie der Wirtschaftskalender und
|
||
aus demselben Grund.
|
||
- **Gesamtempfehlung aufgeräumt + Tooltips überall (User-Vorgabe 2026-08-06,
|
||
v=147):**
|
||
· **Nur noch Module MIT Stimmrecht als Chip.** `_verdict.add()` hat ein
|
||
**`used`**-Flag; `used=False` heisst *dauerhaft* entmachtet (Elliott, Orderbuch —
|
||
beide heute gemessen ohne Inhalt), das Frontend filtert sie aus.
|
||
⚠ **Sie bleiben im Snapshot und in `verdict_votes`** — `log_verdict_votes` speist
|
||
daraus die Telemetrie, und die 25.000-Verdict-Messung braucht die Reihe weiter.
|
||
Ausgeblendet wird die Anzeige, nicht die Messung.
|
||
⚠ Module mit `weight=0` aus **situativer** Stille (Welle bei WARTEN, Squeeze ohne
|
||
Ausbruch) bleiben sichtbar — die sprechen wieder.
|
||
· **`#vd-note` entfernt** (der Münzwurf-Hinweistext) samt CSS. Die Aussage steht
|
||
weiter im Tooltip der Karte und in dieser Datei.
|
||
· **Tooltips auf allen Dashboard-Anzeigen** (59 `title`-Attribute): Karten-
|
||
Überschriften, Header-Boxen, Trade-Leiste und die Anzeige-Elemente. Wo eine
|
||
Messung existiert, steht sie im Tooltip (z. B. Kerzen-Anatomie: „Lehrbuch-Lesart
|
||
gemessen INVERTIERT"; Bounce: „~50 % Trefferquote, KEIN Signal"). Gesetzt über
|
||
`set_tooltips.py` — **idempotent**, ergänzt nur fehlende `title=`, damit ein
|
||
erneuter Lauf nichts überschreibt.
|
||
- **„Setup" und „Lauf" aus der Meldungen-Karte entfernt (User-Vorgabe 2026-08-06,
|
||
v=148).** Beide `.kv`-Zeilen samt der JS-Zuweisungen (`wave-setup`, `wave-move`)
|
||
sind raus. Begründung: das Setup steht ohnehin in der Verdict-Headline und in den
|
||
Gründen, der „Lauf" beschrieb die Vergangenheit ohne Handlungsbezug.
|
||
⚠ **Fallstrick beim Entfernen:** die Zeile `const w = d.wave_signal || {}, ws = …`
|
||
stand direkt über der Setup-Zuweisung und wäre mit rausgefallen — `w` wird aber
|
||
weiter unten noch **achtmal** gebraucht (Gründe-Zeile, Blinken der Order-Buttons,
|
||
Close-Alarm, News-Konflikt). Ein `ReferenceError` mitten in `render()` bricht den
|
||
GESAMTEN Render ab (s. Cache-Regel oben), das Dashboard wäre eingefroren.
|
||
Gegenprobe eingebaut: alle `$("id")`-Zugriffe der `app.js` gegen die IDs der
|
||
`index.html` geprüft — **0 verwaiste Referenzen**.
|
||
- **⚠⚠ „stark LONG" bei WARTEN — der scheinbare Widerspruch aufgelöst
|
||
(User-Fund 2026-08-07, v=149).** Reale Anzeige: Headline `◌ WARTEN · M30`,
|
||
darunter „Modul-Konsens: stark LONG · **2 von 2** stimmberechtigten für LONG
|
||
(7 ohne Aussage)". Beide Zahlen stimmten, die Kombination war trotzdem
|
||
irreführend. Zwei Ursachen, beide behoben:
|
||
**(1) Die Headline nannte den Grund nicht.** Der Block stand im Snapshot
|
||
(`wave_signal.block = min_conf`, conf 49 gegen die 55-%-Schwelle — die Welle
|
||
HATTE eine LONG-Richtung, ihr fehlten sechs Punkte), gerendert wurde er aber
|
||
**nur bei `breakout_pending`**. Bei jedem anderen Gate blieb ein nacktes
|
||
„WARTEN" stehen. ⚠ Das war meine Lücke vom 06.08.: ich habe die
|
||
**Tages-Verteilung** (`#vd-blocks`) gebaut und den **aktuellen** Grund
|
||
vergessen. Jetzt steht er an der Headline: „◌ WARTEN · M30 · Konfidenz 49 % <
|
||
55 % — Setup zu schwach" (Mapping für alle neun Gate-Codes).
|
||
**(2) „2 von 2" las sich wie Einstimmigkeit.** Es waren **2 von 7** sichtbaren
|
||
Modulen — H1 (1,5) und Liq-Trend (0,5) — und die **Welle**, die als einzige die
|
||
Order steuert, war nicht dabei. `bias = 1,0` ist arithmetisch korrekt (das
|
||
gesamte *sprechende* Gewicht war einig), aber die Basis war winzig. Seit dem
|
||
Entmachten von Elliott und Orderbuch (06.08.) tritt das häufiger auf — die
|
||
Änderung hat das Problem nicht erzeugt, aber verschärft.
|
||
**Neu:** die Zeile führt mit der Basis („2 von 7 Modulen sprechen, alle für
|
||
LONG"), das Wort „stark/leicht" entfällt, solange **weniger als die Hälfte**
|
||
der Module spricht, und eine stumme Welle wird ausdrücklich genannt
|
||
(„⚠ die Welle (steuert die Order) schweigt").
|
||
Live gegengerechnet, beide Zeilen erscheinen wie beabsichtigt.
|
||
⚠ Reine Anzeige — an Gewichten, Gates und Order-Logik ist nichts geändert.
|
||
- Asset-Version aktuell **v=185** (in `web/index.html` hochzählen, siehe Workflows).
|
||
- **✅ SCHRIFT SKALIERT MIT DEM FENSTER (2026-08-13, v=167).** User: „die Größe der
|
||
Schriftarten im Dashboard auf em einstellen, damit auf jedem Gerät oder je nach
|
||
Fenstergröße die Schriftgröße automatisch angepasst wird."
|
||
⚠⚠ **Umgesetzt mit `rem` + fluider WURZEL, NICHT mit `em` — der Wunsch war
|
||
richtig, die genannte Einheit nicht.** `em` ist relativ zum **Eltern**element und
|
||
reagiert auf die Fenstergröße **gar nicht**; verschachtelt multipliziert es sich
|
||
sogar (ein `h2` mit 1,2em in einer Karte mit 1,1em landet bei 1,32×). `rem` hängt
|
||
dagegen an genau EINER Stellschraube, und die wird fluide gemacht:
|
||
```css
|
||
html { font-size: clamp(15px, 14px + 0.5vw, 22px); } /* v=168, eine Stufe kleiner */
|
||
```
|
||
⚠⚠ **EINE STUFE KLEINER am selben Tag (v=168, User: „verkleinere alle
|
||
Schriftarten um 1em").** Wörtlich wäre `1em` = **100 % = null** — gemeint ist
|
||
also eine Stufe. Genommen wurden **−2px**, exakt der Schritt, in dem die Größe
|
||
am 24.07. zweimal HOCHgesetzt wurde; das Handy landet damit auf dem
|
||
Zwischenstand von damals (~16px).
|
||
| Fenster | Wurzel v=167 | **Wurzel v=168** | Basistext | klein (11px) | groß (26px) |
|
||
|---|---|---|---|---|---|
|
||
| 360px | 17,80 | **15,80** | 15,8 | 9,7 | 22,8 |
|
||
| **390px (Handy)** | 17,95 | **15,95** | **15,9** | **9,7** | **23,0** |
|
||
| 768px | 19,84 | **17,84** | 17,8 | 10,9 | 25,8 |
|
||
| 1440px | 23,20 | **21,20** | 21,2 | 13,0 | 30,6 |
|
||
| ab 1600px | 24,00 | **22,00** (Deckel) | 22,0 | 13,4 | 31,8 |
|
||
✅ **Das war eine EINZIGE Zeile** — die 96 rem-Werte blieben unberührt, alle
|
||
Größenverhältnisse untereinander ebenso. Genau dafür wurde umgestellt: der
|
||
Bump am 24.07. musste noch 96 Einzelwerte anfassen und übersah dabei drei.
|
||
**Eine Stufe = 2px in den beiden Zahlen** (Minimum und Steigungs-Basis), der
|
||
Deckel wandert mit. Zurück auf die 18er-Basis: `clamp(17px, 16px + 0.5vw, 24px)`.
|
||
⚠ Der ursprüngliche Anker (v=167) war bewusst so gesetzt, dass am Handy
|
||
**nichts** kleiner wird (17,95 gegen 18,00px) — der User hatte die Größe am
|
||
24.07. zweimal selbst hochgesetzt, und eine „Responsive-Umstellung", die das
|
||
still eindampft, wäre eine Rücknahme seiner Entscheidung gewesen. Die
|
||
Verkleinerung auf v=168 ist dagegen **ausdrücklich beauftragt**.
|
||
✅ **Umrechnung verlustfrei belegt**, nicht behauptet: alle **96** Deklarationen
|
||
gegen die 18px-Referenz zurückgerechnet, größter Fehler **0,0008 px**.
|
||
⚠ Die drei `font:<weight> Npx/…`-**Shorthands** (`.ms-chip`/`.ms-bos-lbl`/
|
||
`.sqm-badge`) sind **mit** umgestellt — sie waren beim Bump 2026-07-24 zuerst
|
||
übersehen worden, weil ein Skript nur `font-size` erfasste. Prüfsumme: 93
|
||
`font-size` + 3 Shorthand = 96, ausgeliefert **0** px-Schriftgrößen.
|
||
✅ **Zwei Vorbedingungen geprüft, nicht angenommen:** (a) `<meta name="viewport"
|
||
content="width=device-width…">` ist gesetzt — ohne ihn würde `vw` am Handy den
|
||
Layout-Viewport (~980px) messen und die Schrift wäre dort riesig; (b) die beiden
|
||
Media-Queries (360/560px) stehen in **px** und verschieben sich damit **nicht**
|
||
mit der Schrift. Wären sie in `em`, würde ein größerer Text die Breakpoints
|
||
wandern lassen — die klassische Falle.
|
||
⚠ **ABSTÄNDE bleiben in px** (Padding/Margin/Grid). Sie mitzuskalieren wäre ein
|
||
weit größerer Eingriff (Karten, Grid, Trade-Leiste) mit echtem Layout-Risiko; die
|
||
Anfrage betraf die Schrift. Folge nach der Verkleinerung: die Schrift sitzt jetzt
|
||
etwas **lockerer** in den unveränderten Polstern (bei v=167 war es umgekehrt —
|
||
auf breiten Fenstern wirkte der Text dominanter). Wer das nachziehen will, muss
|
||
Padding/Margin ebenfalls auf `rem` umstellen — nicht gemacht.
|
||
⚠ Die clamp-Grenzen stehen in **px**, nicht in `rem` — dadurch ist die Anzeige
|
||
vorhersagbar, respektiert aber die im Browser eingestellte Basis-Schriftgröße
|
||
nicht. Bewusst so, weil der User die Größe zweimal selbst kalibriert hat.
|
||
**Zurück:** `web/style.css.bak-2026-08-13`. Größe global ändern = **eine** Zeile
|
||
(die `clamp`), nicht mehr 96 Einzelwerte — genau das war die Schwäche vorher.
|
||
⚠ Chart-`fontSize` in `app.js` gibt es **nicht mehr** (mit dem Charts-Tab am
|
||
05.08. entfallen) — der alte Warnhinweis dazu ist damit gegenstandslos.
|
||
|
||
## Hyperliquid-Kurs neben dem Broker-Kurs (2026-08-01)
|
||
|
||
**User-Wunsch:** „wenn Pepperstone geschlossen ist trotzdem den aktuellen Kurs und die
|
||
Liquidität von Hyperliquid einbinden" → nachgeschärft zu „blende den HL-Kurs **neben**
|
||
den Pepperstone-Kurs ein" (ehrlicher: man sieht die Abweichung, statt dass still
|
||
getauscht wird) → plus „bei geschlossenem Markt auch den **G/V** mit HL berechnen".
|
||
|
||
### ⚠⚠ Die Basis-Korrektur war am Wochenende ZIRKULÄR (Fix 2026-08-02)
|
||
|
||
**User-Fund:** „Auf app.hyperliquid.xyz steht WTIOIL-USDC bei 81,392, im Dashboard 86,424."
|
||
Beides war erklärbar — und dahinter steckten **zwei** Fehler.
|
||
|
||
**(1) Falsches Label (mein Fehler vom 01.08.).** Angezeigt wurde `mid_mt5` = HL + Basis,
|
||
also der auf Pepperstone-Niveau umgerechnete Wert — unter der Beschriftung „HYPERLIQUID
|
||
KURS". Vorher hieß das Feld nur „HL" (unklar, aber nicht falsch); die Ausschreibung machte
|
||
daraus eine unzutreffende Behauptung. **Jetzt steht dort der ROHE HL-Kurs** (exakt die
|
||
Zahl von der HL-Seite), das CFD-Äquivalent wanderte in den Tooltip.
|
||
|
||
**(2) Der eigentliche Bug: die Basis saugt bei stehendem Broker den HL-Move auf.**
|
||
Die Basis ist `Median(mt5_mid − hl_mid)` über die letzten 60 Snapshots — **ohne Prüfung,
|
||
ob `mt5_mid` überhaupt noch lebt.** Steht Pepperstone (Wochenende/Feiertag), friert
|
||
`mt5_mid` ein, während HL 24/7 weiterläuft → die „Basis" wächst exakt um den HL-Move, und
|
||
`hl_mid + basis` rekonstruiert wieder den **EINGEFRORENEN Broker-Kurs**. Die Korrektur
|
||
wird zirkulär — also genau dann wertlos, wenn man sie braucht.
|
||
**Beleg aus `leadlag.db`** (beide Kurse nebeneinander, 46k Ticks):
|
||
| Zeitraum | Basis |
|
||
|---|---|
|
||
| 27.–31.07., Broker **live** | **+0,42 … +0,58** (bemerkenswert stabil) |
|
||
| ab 31.07. 22:55 (Freeze), Broker **steht** | −1,07 → **+5,03** |
|
||
Real angezeigt: **86,414** statt der echten ~81,89 — Abstand zum eingefrorenen Broker-Bid
|
||
nur noch **0,081**. Der Wochenend-Move war dabei ECHT, kein dünnes Buch: So 03:00→05:00
|
||
von 85,51 auf 80,84, danach 13 h stabil um 81 bei ~600 Messungen/Stunde.
|
||
|
||
**Fix (HL-Projekt, `server.py` `/api/walls`):** Der Endpoint sucht jetzt die **letzte
|
||
echte Broker-Bewegung** (Rückwärtslauf über `_BASIS_LOOKBACK`=40 000 Zeilen ≈ 2,3 Tage —
|
||
muss ein ganzes Wochenende überspannen) und bildet die Basis aus dem Fenster **davor**.
|
||
Steht der Broker länger als `_MT5_STALE_S`=180 s (gleiche Schwelle wie
|
||
`engine._TICK_STALE_S`), ist die Basis **eingefroren** und der Endpoint liefert
|
||
`basis_stale: true` + `mt5_still_s`. Verifiziert an den Echtdaten: letzte Bewegung
|
||
31.07. 22:55:06 (= der eingefrorene Tick), Basis **+0,4765** statt +5,0285, Antwortzeit
|
||
109 ms (Timeout des Oil-Bots ist 2 s).
|
||
|
||
⚠ **Materielle Folge, nicht nur Kosmetik:** Die offene SHORT-Position (0,57 Lots ab
|
||
86,163, TP 84,123) wurde mit **−15,02 €** angezeigt; mit eingefrorener Basis sind es
|
||
**+211 €**, und der TP lag 2,3 $ ÜBER dem echten Niveau. Die HL-G/V-Schätzung war vorher
|
||
zwangsläufig falsch, weil sie auf demselben zirkulären Wert rechnete.
|
||
⚠ **Vorbehalt bleibt:** HL ist ein Oracle-basierter HIP-3-Perp — dass er 81,3 zeigt, heißt
|
||
nicht, dass Pepperstone dort öffnet. Richtung belastbar, Höhe nicht. Deshalb weiter `≈`.
|
||
|
||
- **Snapshot:** `hl_live {mid_mt5, hl_mid, basis, basis_stale, age_s, bid, ask, bid_sz,
|
||
ask_sz, imb5, trend, slope_per_h, vs_mt5}` — IMMER befüllt, nicht nur am Wochenende.
|
||
`mid_mt5` = HL-Mid + gemessene Basis (HL `xyz:CL` notiert wegen anderem Kontrakt
|
||
~0,45 $ unter dem CFD; die Basis kommt serverseitig aus dem HL-Projekt als Median
|
||
über 60 Snapshots) → direkt mit dem Broker-Kurs vergleichbar.
|
||
Dazu `market_closed`, `tick_age_s`, `pnl_hl`.
|
||
- **Dashboard (v=129):** eigene Header-Box **HL** neben KURS; Tooltip nennt HL-Rohkurs,
|
||
Basis, Alter und Abweichung. Bei geschlossenem Markt heißt das Kurs-Label
|
||
„KURS · ZU", die Session-Zeile zeigt „🔒 MARKT ZU", und das **G/V wechselt auf die
|
||
HL-Schätzung** (mit `≈` markiert, Tooltip nennt den eingefrorenen Broker-Wert).
|
||
- **MT5 (Indikator v1.34):** `HL;<mt5_aequiv>;<alter_s>;<bid>;<ask>;<U|D|->` →
|
||
bernsteinfarbenes Label unter Kurs und G/V (`InpShowHL`/`InpHLColor`).
|
||
|
||
⚠ **DREI Sackgassen bei der „Markt zu"-Erkennung — dokumentiert, damit sie niemand
|
||
wiederholt:**
|
||
1. **`tick_local_ts`** ist die lokale **ABHOLZEIT**, nicht das Tick-Alter — sie
|
||
erneuert sich bei jedem Abruf, auch wenn der Broker denselben alten Tick liefert.
|
||
2. **`tick_server_ts` − `trader._broker_offset_s()`** ist **ZIRKULÄR**: die
|
||
Offset-Funktion leitet den Zeitzonen-Versatz selbst aus dem letzten Tick ab
|
||
(`off = tick.time − now`). Bei geschlossenem Markt misst sie die Tick-Veraltung
|
||
statt der Zeitzone (real **−7,41 h** statt +3 h) — das Alter hebt sich rechnerisch
|
||
auf (Ergebnis: **−347 s**).
|
||
3. **`pnl / Kursbewegung`** als EUR-je-Punkt-Faktor ergab **155 statt ~86 EUR** je
|
||
1,00 und Lot, weil `trader.pnl = profit + swap` den über das Wochenende
|
||
aufgelaufenen Swap enthält.
|
||
**Verwendet wird stattdessen:** Markt zu = **der Kurs hat sich seit `_TICK_STALE_S`
|
||
(180 s) nicht BEWEGT** (`_last_bid`/`_last_bid_move_ts`) — kein Zeitzonen-, Kalender-
|
||
oder Feiertagswissen nötig, deckt Wochenende, Feiertag und Broker-Ausfall gleich ab.
|
||
Und der G/V nutzt die vorhandene, korrekte Umrechnung **`trader.live_pnl(bid, ask)`**
|
||
(`tick_size`/`tick_value` inkl. Swap und Währung), gefüttert mit dem HL-Kurs ± halbem
|
||
Spread. ⚠ Bleibt eine **Schätzung** (anderer Kontrakt, Montags-Open-Spread unbekannt) —
|
||
deshalb das `≈` in der Anzeige.
|
||
|
||
### ⚠⚠ `_broker_offset_s()` lieferte bei geschlossenem Markt −9,5 h statt +3 h (Fix 2026-08-01)
|
||
|
||
Sackgasse (2) oben war **nur halb richtig** — die Zirkularität war nicht das eigentliche
|
||
Problem, sondern ein **echter Bug in `trader._broker_offset_s()`**, der weit über die
|
||
Anzeige hinausreichte. Gefunden bei der Frage „warum steht die Laufzeit auf 0:00?".
|
||
|
||
**Mechanismus:** `off = round((tick.time − now)/1800)*1800` misst die Zeitzone nur,
|
||
solange der Tick FRISCH ist. Steht der Markt, friert `tick.time` ein und die Differenz
|
||
misst die **Veraltung**. Real gemessen am 01.08. um 11:25 (letzter Tick Fr 23:54:59):
|
||
|
||
```
|
||
tick.time − now = −34210 s → gerundet −34200 (−9,5 h) statt +10800 (+3 h)
|
||
```
|
||
|
||
Die alte Plausibilitätsprüfung `[−12 h … +14 h]` **ließ das durch** — eine
|
||
Wochenend-Veraltung fällt genau in dieses Fenster. Ein noch längerer Stillstand (Mo
|
||
früh, ~49 h) wäre rausgefallen und hätte 0 geliefert, also *auch* falsch.
|
||
|
||
**Was still betroffen war** (die Laufzeit-Uhr war nur das Sichtbare):
|
||
`open_time` = `p.time − off` landete **12,5 h in der ZUKUNFT** → Laufzeit 0:00 ·
|
||
Time-Stop-Alter in `trailing.py:504` wurde negativ → **der 120-min-Time-Stop hätte
|
||
nicht ausgelöst** · `deal.time`-Umrechnung beim externen Close · alle MQL5-Chart-Anker
|
||
(Kanal, Liquiditäts-Trendlinie, Trade-Marker).
|
||
|
||
**Fix:** Der Offset wird **nur noch von einem frischen Tick** (neu) bestimmt, sonst gilt
|
||
der zuletzt gültige Wert (`self._boff`, vorbelegt mit `_BROKER_OFF_DEFAULT_S` = UTC+3 —
|
||
dieselbe Konstante nutzt `engine.get_bars` schon als `broker_utc_offset`). Die Frische
|
||
wird am **bereits bekannten** Offset gemessen (`stale = now + _boff − tick.time`), nicht
|
||
am Tick selbst — damit nicht zirkulär. Schwelle `_BROKER_TICK_FRESH_S = 900` liegt unter
|
||
dem halben Rundungsraster (1800/2), sodass die 30-min-Rundung immer auf den echten
|
||
Offset fällt. Ein echter Zeitzonen-Wechsel (Broker-DST) wird beim ersten frischen Tick
|
||
übernommen **und geloggt** (statt still zu passieren).
|
||
|
||
⚠ **Einordnung: Deployment-Drift** (Fall 4 in der Tabelle unten) — entworfen unter „Tick
|
||
ist frisch", betrieben unter „Tick kann beliebig alt sein". Kein Backtest hätte das
|
||
gefunden; Backtests rechnen nie mit Broker-Wallclock. Bestätigt die dortige Erwartung,
|
||
dass die Liste nicht vollständig war.
|
||
|
||
**Startwert der Stillstands-Uhr (gleicher Fix):** Weil der Offset jetzt verlässlich ist,
|
||
wird `_last_bid_move_ts` beim **ersten** Snapshot aus dem ECHTEN Tick-Alter vorbelegt
|
||
(`_now + _broker_offset_s() − tick_server_ts`, Plausibilität 0…14 Tage) statt bei null zu
|
||
beginnen. Vorher brauchte der Bot nach **jedem** `restart_server.bat` volle 180 s, um ein
|
||
längst geschlossenes Wochenende zu bemerken — real lief der User genau in dieses
|
||
Blindfenster („KURS" statt „KURS · ZU", G/V auf dem eingefrorenen Broker-Wert).
|
||
Im **laufenden** Betrieb bleibt die Erkennung bewusst bewegungs-basiert: das fängt auch
|
||
einen eingefrorenen Feed, dessen Zeitstempel weiterlaufen.
|
||
Verifiziert nach dem Neustart: `market_closed=True` sofort, `tick_age_s` 56 844 s
|
||
(= Rohwert-Gegenrechnung auf **0 s** genau), `open_time` → 31.07. 22:54, Laufzeit +15,8 h.
|
||
|
||
### ⚠ Stiller `catch` im REST-Poll verschluckte JEDEN Render-Fehler (Fix 2026-08-01)
|
||
|
||
`pollSnapshot()` hatte `try { fetch → _applySnapshot(d) } catch {}` — der `catch` sollte
|
||
Netz-Aussetzer schlucken, umschloss aber auch `render()`. Ein Render-Fehler wurde damit
|
||
**lautlos** verschluckt: der Poll feuerte weiter alle 4 s, jeder Durchlauf starb still,
|
||
das Dashboard fror auf dem letzten guten Stand ein **und die Konsole blieb leer**. Genau
|
||
diese Kombination (eingefrorene Anzeige ohne jede Fehlermeldung) hat die Fehlersuche
|
||
unnötig lang gemacht. Jetzt ist nur noch der `fetch` im `try`; Netzfehler bleiben still,
|
||
**Render-Fehler werden laut**. Ergänzt die Null-Sicher-Regel von oben: die verhindert den
|
||
Absturz, das hier macht ihn sichtbar, falls er doch passiert.
|
||
|
||
**Beschriftung (User 2026-08-01):** Die Header-Labels heißen ausgeschrieben
|
||
**„PEPPERSTONE KURS"** und **„HYPERLIQUID KURS"** (vorher „KURS"/„HL" — ließ offen,
|
||
welcher Kurs woher kommt). Bei geschlossenem Markt: „PEPPERSTONE KURS · ZU".
|
||
|
||
## 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.
|
||
|
||
## ⚠ Deployment-Drift — die strukturelle Schwachstelle (2026-07-31)
|
||
|
||
**Track B schützt gegen OVERFITTING** (ein Edge existiert gar nicht). Es schützt
|
||
**nicht** gegen **DEPLOYMENT-DRIFT**: ein real existierender Edge wird unter anderen
|
||
Bedingungen BETRIEBEN als VALIDIERT. An EINEM Tag wurden drei Fälle gefunden:
|
||
|
||
| # | Komponente | Gemessen unter | Betrieben unter | Schaden |
|
||
|---|---|---|---|---|
|
||
| 1 | P(break) | Anlauf zu **fixiertem** Level | **dynamisch** gewähltes Level | AUC 0,715 → **0,368** |
|
||
| 2 | `breakout_k=0,3` | **feste** Zeitebene | TF wechselt 33×/Tag, löscht den Anker | 43 % → **93 % WARTEN** |
|
||
| 3 | Trailing `mult` | **1,5** | **1,5–3,0** je nach TF | H1 ΣR −249 → **−1367**, Worst −1,50 → −2,50 |
|
||
| 4 | `_broker_offset_s()` | **frischem** Tick | Tick beliebig alt (Wochenende) | Offset **−9,5 h statt +3 h** → `open_time` 12,5 h in der Zukunft, **Time-Stop-Alter negativ** |
|
||
| 7 | Auto-Squeeze-**Einstiegspreis** | Fill **AM Ausbruchs-Level** (`backtest_breakout_squeeze.py`) | **MARKET**-Order, live median **+0,275×ATR** über dem Level (34 echte Entries) | ØR **+0,456 → −0,158** (H1) bzw. **+0,611 → −0,006** (H2); WR 56 → 37 % |
|
||
| 6 | `auto_squeeze_skip_night` | Nacht-Kostenfalle am **Wellensignal** (alle Nacht-Bars: 0,333×ATR) | **Squeeze**-Ausbrüche, die nur bei anziehendem ATR feuern (0,183×ATR = Tag-Niveau) | Gate kostet ΣR **−42 (H1) / −243 (H2)**; geblockte Ausbrüche sind in beiden Hälften positiv |
|
||
| 5 | `backtest_auto_signal.py` | **ohne Winkel** (`_build` ohne `angle=` → Default 90 → `ad=0`) | Winkel steuert einen **Signalzweig** (Reversal) UND **±15 Konfidenzpunkte** | Urteil vom 30.07. beschrieb weder das alte noch das neue System; der Winkelterm allein hebt H1 bei conf 75 von **−0,123 auf −0,047** |
|
||
| **8** | **Buchführung der Bot-Trades** (nicht die Strategie, ihre **Beobachtbarkeit**) | Einstieg läuft über `engine._open` → schreibt `setup` + `sl_at_entry` + 16 `ctx_*` | **Pending-Fill** füllt im Broker, `trader._refresh_locked` adoptiert und loggt nur 5 Felder | 4 reale Squeeze-Trades am 06.08. als `setup=NULL` = **von manuellen ununterscheidbar**; B4-Monitor, `squeeze_b5` (Abbruchregel!) und `squeeze_entry_gap` zählten **null** |
|
||
| **9** | **Pending-Order liegt am Level** (offen) | `backtest_auto_signal_v3.py`: Order liegt **durchgehend** (ØR +0,35/+0,32) | `entry_room`↔`breakout_pending` flackern → 4× setzen/stornieren in 76 s bei identischem Level | Ausbruch in einer Storno-Lücke = Entry ganz verloren; Ausmaß **ungemessen** |
|
||
|
||
Keiner wäre durch MEHR Backtesting gefunden worden — die Backtests waren korrekt.
|
||
**Ursachen (strukturell):** (a) Backtests sind NACHBAUTEN, keine Nutzer des Live-Codes
|
||
(`sim_run`/`_ema_series`/`_atr_series` liegen in ≥6 Skripten als Kopie); (b) es gibt
|
||
**Live-only-Gates**, die kein Backtest modelliert (Entry-Raum, EIA, Dead-Hours,
|
||
News-Blackout, Cooldowns, Startup-Grace); (c) Live-Parameter sind **kontextabhängig**
|
||
(Trail-mult je TF, SL-ATR fix M15, TF selbst per Heuristik), Backtest-Parameter
|
||
konstant; (d) es gab **keine Telemetrie über die Entscheidung selbst**.
|
||
|
||
**Behebung in 3 Stufen (User-Entscheidung 2026-07-31):**
|
||
- **Stufe 1 — ERLEDIGT:** `_MULT_BY_TF` auf durchgehend **1,5** und
|
||
`_MULT_BREAKOUT_ADD` auf **0,0** (`core/trailing.py`) — der einzige gemessene Wert.
|
||
⚠ **BEGRÜNDUNG NACHTRÄGLICH KORRIGIERT (beim Stufe-3-Refactor):** die zuerst
|
||
genannte „67 % größerer Einzelverlust" war ein **Artefakt** — `backtest_trailing.py`
|
||
setzt den Initial-SL auf `mult`×ATR statt fix 2,0, dort war der Worst-Case also
|
||
per Konstruktion gleich dem Multiplikator. Sauber nachgemessen mit fixem SL
|
||
(`backtest_trailmult.py`, `core/exit_model.py`, 80k Bars, 2 Halbjahre):
|
||
| Trail | H1 ΣR | H2 ΣR | Worst |
|
||
|---|---|---|---|
|
||
| 1,0 | **−73** | **+1346** | −2,00 |
|
||
| **1,5 (live)** | −325 | +1294 | −2,00 |
|
||
| 2,0 | −446 | +1195 | −2,00 |
|
||
| 2,5 | −1174 | +1223 | −2,00 |
|
||
| 3,0 | −1088 | +1397 | −2,00 |
|
||
**Der Worst-Case ist bei JEDEM Multiplikator identisch −2,00** (der SL deckelt).
|
||
**Die Entscheidung bleibt richtig:** 1,5 schlägt 2,0 UND 2,5 in beiden Hälften.
|
||
**➜ TRAIL AUF 1,0 GESETZT (User-Vorgabe „setze alle Vorschläge um", 2026-07-31),
|
||
in `core/exit_model.py` (`LIVE.mult`), live verifiziert (`AKTIVIERT … mult=1.0x`).**
|
||
Voller Sweep (`backtest_trailmult.py`, fixer SL 2,0, 8 Werte 0,5–3,0): **enger ist
|
||
MONOTON besser** — Wave-Signal H1/H2 ΣR: 0,5 **+1258/+2418** · 0,8 +852/+1668 ·
|
||
1,0 +80/+1360 · 1,5 −220/+1305 · 2,0 −394/+1198 · 2,5 −1072/+1327.
|
||
⚠ **Optimum am RAND des Suchbereichs = Warnsignal** („misst man besser oder nur
|
||
weniger?"). **Gegentest auf SQUEEZE-Entries mit Echtkosten** (`--squeeze`) — das
|
||
Setup lebt von LÄUFERN, müsste also dagegenhalten: **tut es nicht**, auch dort ist
|
||
enger monoton besser (1,0: H1 −0,202/H2 +0,010 · 1,5: −0,219/−0,032). Deshalb der
|
||
Schritt auf 1,0 — besser als 1,5 in beiden Hälften auf **beiden** Signalmengen —
|
||
aber **nicht** auf 0,5 (Randwert). ⚠ Zweiter Grund gegen 0,5: die Sim modelliert
|
||
**keine Exit-Slippage**; ein engerer Trail löst viel häufiger aus und ist stärker
|
||
betroffen (real gemessen bis 0,75×ATR über den Stop hinaus).
|
||
- **Stufe 2 — ERLEDIGT:** **Entscheidungs-Telemetrie.** (a) Jede Empfehlung loggt
|
||
jetzt einen maschinenlesbaren **`block_reason`** (`recommendations.block_reason`,
|
||
DB migriert mit Backup): `deadband · dead_hour · eia · htf_counter · stretch ·
|
||
min_conf · breakout_pending · entry_room · no_data · stale`; NULL = kein Block.
|
||
Ohne den war nicht feststellbar, WELCHES Gate die 93 % erzeugt. (b) **`analyze_
|
||
divergence.py`** hält vier Dinge gegen die Backtest-Erwartung: **A** Signal-Verteilung
|
||
(Soll ~43 % WARTEN), **B** Gate-Anteile inkl. Kennzeichnung „im Backtest modelliert?",
|
||
**C** TF-Wechsel-Rate (Soll: Median ≥15 min), **D0** Modell-Kalibrierung
|
||
(Ø-Vorhersage vs. echte Rate) und **D** Merkmals-Drift gegen `_PB_MU/_SD`.
|
||
⚠ **Lehre aus dem Bau selbst:** die Merkmals-Drift (D) hätte den realen Fehler
|
||
**NICHT** gefangen — mom3 lag nur **0,50σ** unter µ, bei Gewicht 1,43 wurden daraus
|
||
aber −0,71 im Logit (23 % statt 41 %). Deshalb ist **D0 der schärfere Test** und die
|
||
D-Schwelle auf 0,5σ gesetzt. Erster Lauf nach dem Fix: D0 zeigt **27,0 % vorhergesagt
|
||
vs. 28,0 % real** (vorher 23 vs 41) — die Reparatur ist damit live bestätigt.
|
||
- **Stufe 3 — ERLEDIGT (`core/exit_model.py`):** geteilter Exit-Kern. Enthält
|
||
**`LIVE: ExitParams`** (sl 2,0 · trail 1,5 · start 0,3 · be 1,3 · lock 3,5/0,6/1,2 ·
|
||
timestop 24 · tp 3,5 · max_hold 200) als **einzige Quelle** — `core/trailing.py`
|
||
importiert sie jetzt von dort (`_TRAIL_START_ATR`, `_BREAKEVEN_ATR`, `_PHASE4_*`,
|
||
`_MULT_BY_TF` sind abgeleitet, keine eigenen Zahlen mehr). Ändert jemand `LIVE.mult`,
|
||
ändern sich Live-Verhalten UND Messung gemeinsam → **Fall 3 ist konstruktiv
|
||
unmöglich geworden.** Dazu die kanonische `simulate()` für Backtests, mit Flags
|
||
`timestop`/`use_tp`, um Teilmodelle EXPLIZIT zu machen statt zu verstecken.
|
||
⚠⚠ **BEFUNDE BEIM REFACTOR — schlimmer als angenommen:**
|
||
(1) **Es gibt mindestens DREI materiell verschiedene Exit-Modelle** in den Backtests:
|
||
**(A)** Phasen-Modell ≈ live (`_trailing/_exit/_atrfloor/_candle_fade`) · **(B)
|
||
EINFACH** — `_TPTRAIL=0.5`, **kein** Breakeven, **kein** Lock, **kein** Time-Stop,
|
||
`_MAXH=240` (`_hourly`, `_hourly_split`, `_bounce`, `_events`, `_deadhour`,
|
||
`_chopgate`) · **(C)** Phasen mit **`be_on=1.0`** statt 1,3 (`_breakout`,
|
||
`_confluence_angle`). ⇒ **Mehrere dokumentierte Befunde (Dead-Hours, EIA-Blackout,
|
||
Bounce, Chop-Gate, Stunden-Analyse, breakout_k) ruhen auf einem Exit, der dem
|
||
Live-System NICHT entspricht.** Bewusst NICHT stillschweigend umgestellt — das
|
||
würde historische Schlüsse rückwirkend ändern; markiert als `LEGACY_SIMPLE` /
|
||
`LEGACY_BE10` in `exit_model.py`, Neubewertung = eigene Entscheidung.
|
||
(2) Selbst die „Phasen"-Skripte weichen voneinander ab: `_trailing` hat TP aber
|
||
**keinen** Time-Stop, `_candle_fade` Time-Stop aber **kein** TP, `_atrfloor` liefert
|
||
**Punkte statt R**. Live hat BEIDES → nur `timestop=True, use_tp=True` ist live-treu.
|
||
(3) **`backtest_trailing.py` koppelt den Initial-SL an `mult`** (`sl = entry −
|
||
d*mult*atr`) — dadurch war die Stufe-1-Begründung teilweise falsch (s. o.).
|
||
✅ **Äquivalenz verifiziert** (500 synthetische Zufalls-Kursreihen je Fall):
|
||
`_candle_fade` (`timestop=True, use_tp=False`) und `_atrfloor` (`timestop=False,
|
||
use_tp=True`, ×ATR) sind **bitgenau identisch** zur neuen `simulate()`.
|
||
**Migrations-Regel (in `exit_model.py` festgehalten):** ein Backtest darf nur auf
|
||
`simulate()` umgestellt werden, wenn er danach DIESELBEN Zahlen liefert — sonst ist
|
||
es keine Refaktorierung, sondern eine unbemerkte Änderung der Messgrundlage.
|
||
**LEGACY-RECHECK (`backtest_legacy_recheck.py`, 2026-07-31): die beiden Befunde
|
||
nachgerechnet, die live ECHTE Gates steuern.**
|
||
· ✅ **EIA-Blackout BESTÄTIGT** — auch mit dem echten Exit + Echtkosten ist das
|
||
Fenster Mi 15:30–16:30 in BEIDEN Hälften schlechter als der Rest (ØR-Differenz
|
||
−0,097 / −0,119). Das aktive Gate ist gedeckt.
|
||
· ❌ **DEAD-HOURS reproduzieren NICHT als selektiver Befund** — mit dem echten Exit
|
||
sind **19 von 22 Stunden in BEIDEN Hälften negativ**, es gibt **keine** robust
|
||
positive Stunde. Die alte Analyse hatte 0–7 sowie 12 und 16 als *die* schlechten
|
||
Stunden herausgehoben; wenn fast alles negativ ist, ist diese Auswahl nicht mehr
|
||
gestützt. (Praktisch folgenlos, weil `dead_hours` ohnehin leer ist — aber eine
|
||
Reaktivierung auf der alten Begründung wäre nicht gedeckt.)
|
||
· ⚠ **BOUNCE/Reversal — relative Aussage hält, absolute NICHT.** Reversal schlägt
|
||
Trend in BEIDEN Hälften (H1 −0,034 vs −0,174 · H2 **+0,029** vs −0,073, also
|
||
+0,140/+0,102) — robust. **Aber die dokumentierte Kennzahl „ØR +0,185 · PF 1,35 ·
|
||
70 % Treffer" REPRODUZIERT NICHT**: mit echtem Exit + Echtkosten liegt das Setup bei
|
||
~**Breakeven** (PF 0,93/1,06). Die alten Werte stammen aus dem vereinfachten Exit
|
||
OHNE Kosten. Das Setup bleibt sinnvoll als *nachrangiges, markiertes* Zweitsignal
|
||
(so ist es live auch eingebaut), aber es ist **kein +0,185-Ertragsbringer**.
|
||
· ✅ **ER-CHOP-GATE — Ablehnung BESTÄTIGT.** Alle vier ER-Bänder liegen gleichauf
|
||
(H1 −0,137…−0,172 · H2 −0,049…−0,067); die Efficiency Ratio trennt auch mit dem
|
||
echten Exit nicht. Gate bleibt zu Recht verworfen.
|
||
⚠ **Einschränkung des Rechecks (selbst benannt):** er handelt die **ungegatete**
|
||
EMA-Richtung — ohne Mindest-Konfidenz, Breakout-Bestätigung, HTF-Filter und
|
||
Raum-Gate. Für den VERGLEICH (Fenster gegen Rest, Stunde gegen Stunde) ist das
|
||
neutral, die absoluten Werte sind aber NICHT das Live-Signal.
|
||
⚠ **Konsequenz für die Erfolgskontrolle des TF-Churn-Fixes:** „WARTEN-Anteil
|
||
Richtung 43 % bewegen" ist als Zielgröße mit Vorsicht zu lesen — die 43 % stammen
|
||
aus `backtest_dist.py` (Signal-HÄUFIGKEIT), sie sind kein Ertragsversprechen. Mehr
|
||
Signale sind nur dann besser, wenn die freigegebenen Setups auch tragen.
|
||
|
||
⚠ **Erwartung: die drei Fälle sind nicht vollständig.** Sie wurden bei gezielter Suche
|
||
in ~20 min gefunden; die Trefferquote spricht für weitere. `analyze_divergence.py`
|
||
regelmäßig laufen lassen (Kandidat für den Wochenreport).
|
||
|
||
## ⚠⚠ News-Sentiment war GESÄTTIGT — Fix 2026-08-03
|
||
|
||
**Anlass:** User-Frage „können wir den X-Account anbinden, um den Copiloten mit mehr
|
||
Daten zu versorgen?" → erst prüfen, ob der vorhandene Textstrom überhaupt trägt.
|
||
|
||
**Befund: er trug nichts, weil der Score kaputt war.** `calc_news_sentiment` bildete
|
||
ein **reines Verhältnis** `(bull−bear)/total` — **ohne jeden Bezug zur Beweismenge.**
|
||
Eine einzige bullische Schlagzeile bei null bearischen ergab damit **+1,00**.
|
||
| Diagnose | Wert |
|
||
|---|---|
|
||
| Log-Einträge auf ±1,00 gesättigt | **173 von 173 = 100 %** |
|
||
| davon mit einer Seite exakt 0 | **100 %** |
|
||
| Median-Beweismenge (bull+bear) | **2,8** gewichtete Headlines |
|
||
| DB gesamt (54.548 Zeilen) auf exakt +1,00 | **63,9 %** |
|
||
Real am 03.08.: Sentiment **+1,00** („bull=1.8, bear=0.0"), während WTI **−5,9 %**
|
||
stand. Der Wert trug faktisch **ein Bit** (das Vorzeichen) — und löste im Entry-Check
|
||
dauerhaft falsche „News-Konflikt"-Warnungen aus (Schwelle |s|≥0,5).
|
||
⚠ Die Keyword-Listen sind NICHT die Ursache (56 bullisch / 48 bärisch, keine
|
||
Überschneidung) — es war allein die Formel.
|
||
|
||
**Fix:** Shrinkage `score = raw × total/(total+_EVIDENCE_K)`, `_EVIDENCE_K=4,0`
|
||
(gewählt, weil die reale Median-Beweismenge 2,8 beträgt → eine typische einseitige
|
||
3-Headline-Lage bleibt UNTER der 0,5-Warnschwelle; ~10 Headlines ergeben 0,71).
|
||
Zusätzlich liefert der Rückgabewert jetzt `raw_score` und `evidence`, damit im
|
||
UI/Copilot unterscheidbar ist, ob ein schwacher Score „ausgewogene Lage" oder „kaum
|
||
Daten" bedeutet. Der Leer-Fall gibt dieselben Felder zurück (sonst KeyError genau im
|
||
„keine News"-Fall). Mit 6 Szenarien getestet, live verifiziert: bei identischer
|
||
Beweislage (bull=1.8, bear=0.0) **+1,00 → +0,31**.
|
||
|
||
⚠⚠ **EHRLICH: Das repariert die KALIBRIERUNG, nicht die Aussagekraft.**
|
||
Gemessen wurde parallel die Prädiktivität (Forward-Return 120 min, 38.213 Paare über
|
||
84 Tage, 2 Hälften, **gegen die Regime-Drift gelesen** — H1 +0,005 $, H2 −0,079 $):
|
||
| Bucket | H1 Überschuss | H2 Überschuss |
|
||
|---|---|---|
|
||
| bearisch ≤−0,5 | −0,029 ✓ | **+0,113 ✗ (Vorzeichen kippt)** |
|
||
| leicht bear | +0,118 ✗ | +0,031 ✗ |
|
||
| leicht bull | +0,106 ✓ | +0,044 ✓ |
|
||
| **bullisch ≥+0,5** (grösster Bucket) | −0,003 | **−0,023 ✗** |
|
||
Der stark-bullische Bucket (16.241 / 11.818 Zeilen) liegt in BEIDEN Hälften bei null
|
||
bis negativ, der bearische kippt das Vorzeichen. **News-Sentiment ist nicht robust
|
||
prädiktiv** — auch mit korrigierter Skala. Es bleibt Kontext-Chip ohne Verdict-Stimme.
|
||
⚠ **Methodik-Falle beim Messen selbst:** der erste Lauf nutzte `candles_m1` (deckt nur
|
||
~14 Tage), wodurch `bisect` für alle älteren Zeitstempel denselben Randwert lieferte →
|
||
alle Forward-Returns exakt 0,0000. Erst der Lauf mit MT5-M5-Bars (Jan–Aug) und einer
|
||
Deckungsprüfung (|Δt| ≤ 15 min) war gültig.
|
||
|
||
**Konsequenz für die X-Anbindung:** einen ZWEITEN Textstrom in eine Pipeline zu legen,
|
||
deren erster gemessen nichts vorhersagt, ist nicht begründbar.
|
||
⚠⚠ **PREIS-ARGUMENT ÜBERHOLT (Stand 2026-08-17, nachrecherchiert).** Hier stand
|
||
„Basic ≈ 200 $/Monat, Pro ≈ 5.000 $ … ~25 % des Kontos". Seit **Februar 2026** ist X
|
||
auf **pay-per-use** umgestellt: **0,005 $ je gelesenem Post** (Deckel 2 Mio./Monat),
|
||
die alten Abo-Stufen sind für Neukunden geschlossen. 300 Posts/Tag wären ~45 $/Monat.
|
||
**Der Kosten-Einwand trägt damit nicht mehr — der MESSUNGS-Einwand schon.**
|
||
(Scraping bleibt ToS-Verstoß.) Erst müsste der reparierte Score über ein paar Wochen
|
||
zeigen, dass Textdaten hier überhaupt etwas beitragen.
|
||
|
||
## KI-Copilot: warum er neutral ist, und was 2026-08-02 repariert wurde
|
||
|
||
**User-Frage:** „Der Bias ist fast immer neutral — können wir konkretere Empfehlungen
|
||
bekommen? Weitere Quellen (Kerzen, Muster)?"
|
||
|
||
**Diagnose — es ist NICHT der Prompt, es ist der Provider.** Der Bruch ist exakt
|
||
datierbar (`recommendations.ai_sentiment` je Tag): bis **19.07.** war der Copilot zu
|
||
**42–90 %** gerichtet, ab **21.07.** nur noch zu **1,5–9,3 %**. Dazwischen liegt der
|
||
Wechsel auf **DeepSeek (2026-07-20)**. Der Prompt war die ganze Zeit unverändert.
|
||
⚠ Und er hedged **aus korrekter Schlussfolgerung**, nicht aus Schwäche: die
|
||
Begründungen sind konkret und zutreffend, und er bekommt zu ~90 % ein WARTEN-Signal,
|
||
einen negativen Track-Record UND die ausdrückliche Prompt-Anweisung, genau dann
|
||
NEUTRAL zu empfehlen. Er tut, was dort steht.
|
||
|
||
**Sind seine gerichteten Rufe etwas wert? Unentschieden — die Stichprobe trägt nicht.**
|
||
Forward-Return über `verdict_votes` × `candles_m1`: KI-LONG n=286 → **+0,29 $** in
|
||
Richtung / 76 % Treffer (60 min), KI-SHORT n=93 → −0,02 $ / 55 %. ⚠ **Das ist KEIN
|
||
Beleg:** die 286 Stimmen sind **20 Episoden an 5 Tagen** (Minutentakt-Logger), die 93
|
||
sind 16 Episoden — effektiv n≈20 gegen n≈16, alles in einem Regime. Dasselbe
|
||
Cluster-Muster, an dem ORB gescheitert ist. **Deshalb bewusst NICHT am Prompt gedreht,
|
||
um mehr Meinung zu erzwingen** — das würde eine korrekte Schlussfolgerung überschreiben
|
||
und einer unbelegten Stimme mehr Gewicht geben. Verdict-Gewicht bleibt 1,0 bei
|
||
gerichteter Aussage, 0 bei NEUTRAL.
|
||
|
||
**Zwei echte Prompt-FEHLER behoben (Korrektheit, keine Meinungsmache):**
|
||
1. `_SYS` beschrieb das Wellen-Signal als **„ATR-ZigZag"** — es ist seit Langem
|
||
**EMA12 vs. EMA50**; ZigZag wurde verworfen (~0 Edge). Das Modell missverstand also
|
||
die Natur des Hauptsignals.
|
||
2. `_SYS` nannte **„TradersUnion-Tachos"** als Input — TU ist seit 2026-07-19 gar nicht
|
||
mehr im Copilot-Kontext (`_tool_market`). Das Modell wurde auf Daten hingewiesen,
|
||
die es nie sieht.
|
||
|
||
**Die zwei kalibrierten Größen ergänzt.** Befund beim Nachsehen: `_tool_market` gab dem
|
||
Copiloten **nur Wellen-Signal + Session** — er war das schlechtest informierte Modul im
|
||
System und bekam ausgerechnet die einzigen beiden Komponenten nicht, deren Kalibrierung
|
||
out-of-sample nachgewiesen ist. Neu im Kontext: **`p_break_target` / `p_break_stop`**
|
||
(AUC 0,65 bzw. 0,68–0,72) und **`cone`** (80 %-Bänder, mit der REAL gemessenen Abdeckung
|
||
77 %, nicht dem Nennwert). Dazu eine Lese-Anleitung im Prompt — ohne sie liest ein LLM
|
||
„P(break) 30 %" als schwaches Signal statt als klare Aussage „Level hält mit 70 %";
|
||
45–55 % ist ausdrücklich als Münzwurf markiert.
|
||
⚠ **Zwei Fallen beim Bau, beide an Live-Daten korrigiert:** (a) `p_break` steht im
|
||
Snapshot **bereits in Prozent** (3 = 3 %, deckungsgleich mit der Chart-Zeile „97 %
|
||
Abprall") — die zuerst gebaute ×100-Umrechnung hätte 300 erzeugt; (b) die Engine-Felder
|
||
`_last_sr_close_hint`/`_last_stop_approach`/`_last_cone` und die `engine`-Referenz im
|
||
Agent **existierten gar nicht** — der Fail-safe hätte still nichts geliefert. Beides
|
||
verdrahtet, der Snapshot rechnet die Werte ohnehin und weist sie jetzt nur zu (kein
|
||
zweites Rechnen, anders als beim Konsens-Pfeil). Mit Stub-Test verifiziert (Werte
|
||
unverändert durchgereicht, nur 80 %-Bänder, ohne Engine kein Absturz).
|
||
|
||
⚠ **Kerzen/Muster bewusst NICHT eingespeist.** Ein LLM wendet darauf die Lehrbuch-Lesart
|
||
an — und die ist hier gemessen **invertiert**: „langer Körper im Trend = Fortsetzung"
|
||
→ −0,132/−0,042 (Klimax, läuft dagegen); „2× Volumen = echt" → −0,117/−0,204 (monoton
|
||
falsch herum); Muster-Kursziel trifft nur zu 13–38 %. Roh eingespeist würden sie den
|
||
Copiloten aktiv verschlechtern. Offener Kandidat: sie MIT der gemessenen Lesart
|
||
mitgeben („langer Docht oben; gemessen läuft der Kurs danach eher GEGEN die Kerze") —
|
||
noch nicht gebaut.
|
||
|
||
## Vollprüfung aller Skripte + Nachrechnung der verworfenen Modelle (2026-08-04)
|
||
|
||
**Auftrag:** „alle Skripte ausgiebig testen und die verworfenen Modelle erneut
|
||
gegenrechnen." Werkzeug: `run_all_backtests.py` (probiert die Aufruf-Signaturen
|
||
adaptiv durch — die sind nicht einheitlich, s. Audit unten).
|
||
|
||
**Phase 1 — Lauffähigkeit: ✅ 87 von 87 Skripten laufen fehlerfrei** (8 000 Bars,
|
||
Timeout 900 s je Skript). Kein Defekt durch die heutigen `wave_rec`-Änderungen.
|
||
|
||
**Phase 2a — `breakout_k`: die dokumentierte Begründung REPRODUZIERT NICHT.**
|
||
Offener Punkt aus dem Stufe-3-Refactor: k=0,3 war mit dem `LEGACY_BE10`-Exit
|
||
(Breakeven 1,0) validiert. Erstmals mit dem **kanonischen** Exit nachgerechnet
|
||
(`backtest_breakout_canon.py`, echte `_confirm_breakout`-Mechanik, 80k M5,
|
||
2 Halbjahre, Echtkosten):
|
||
| k | H1 ØR / ΣR | H2 ØR / ΣR |
|
||
|---|---|---|
|
||
| 0,0 | −0,180 / −569 | −0,054 / −250 |
|
||
| **0,3 (LIVE)** | **−0,170 / −356** | **−0,069 / −217** |
|
||
| 0,5 | −0,142 / −262 | −0,074 / −208 |
|
||
| 0,8 | −0,112 / −166 | −0,032 / −73 |
|
||
| 1,0 | −0,101 / −130 | −0,004 / −8 |
|
||
**k=0,5 · 0,8 · 1,0 schlagen den Live-Wert 0,3 in BEIDEN Hälften** (ΣR-Differenz
|
||
bis +226/+209). Die Aussage „0,3 ist in beiden Hälften das Beste" gilt mit dem
|
||
Live-Exit **nicht mehr**.
|
||
⚠ **Trotzdem KEINE Änderung empfohlen**, aus drei Gründen: (a) **alle** Werte sind
|
||
negativ (PF 0,70–0,99) — es ist eine Wahl zwischen Verlusten, kein Edge;
|
||
(b) das Optimum lag am Rand des Suchbereichs → Gegenprobe bis k=3,0 zeigt **kein
|
||
sauberes Optimum** (H2: −8 → −17 → −20 → −19, H1 schwankt) — es ist also weder
|
||
reine Handelsvermeidung noch ein echter Bestwert; (c) gemessen wurde das
|
||
**UNGEGATETE** Signal (ohne min_conf, HTF-Filter, Entry-Raum), live laufen die
|
||
davor. **Nächster Schritt wäre eine GEGATETE Neumessung**, bevor ein Live-Gate
|
||
angefasst wird, das 63 % der WARTEN-Zustände erzeugt.
|
||
|
||
**Phase 2a-II — die GEGATETE Messung, nachgeholt (`backtest_breakout_gated.py`):**
|
||
Voller Live-Stack über die ECHTEN Methoden — `_build` (Totband · M30-Filter ·
|
||
Anti-Überdehnung · Winkel · min_conf 55 · Reversal AUS), Breakout-Bestätigung
|
||
1:1 aus `_confirm_breakout`, `_room_gate` mit echten kausalen M5-Pivots.
|
||
| k | H1 ØR / ΣR | H2 ØR / ΣR |
|
||
|---|---|---|
|
||
| 0,0 (Gate aus) | −0,173 / −193 | +0,003 / +7 |
|
||
| 0,2 | −0,185 / −149 | −0,012 / −16 |
|
||
| **0,3 (LIVE)** | **−0,160 / −122** | **−0,036 / −47** |
|
||
| 0,5 | −0,127 / −85 | −0,041 / −47 |
|
||
| 0,8 | −0,101 / −54 | −0,012 / −11 |
|
||
| 1,0 | −0,096 / −45 | −0,016 / −12 |
|
||
**Bestätigt: k=0,5/0,8/1,0 schlagen 0,3 in BEIDEN Hälften**, die Nachbarn halten
|
||
mit (0,8 und 1,0 nebeneinander) — die vorab fixierte Regel ist erfüllt. Auch die
|
||
ØR verbessert sich in H1 monoton (−0,185 → −0,096), es ist also nicht nur
|
||
Handelsvermeidung.
|
||
⚠ **TROTZDEM keine Umstellung empfohlen — der Grund ist kein Messfehler, sondern
|
||
eine Zweckfrage:** (a) **alle** Werte bleiben negativ (PF 0,70–1,01); das Gate
|
||
begrenzt Schaden, es erzeugt keinen Edge. (b) Der Auto-Signal-Pfad ist ohnehin
|
||
AUS — das Signal ist ein **Vorschlag für den Menschen**, und dessen gemessener
|
||
Vorteil (+118 €/WR 56 % bei MIT-Signal-Trades) entstand auf der **k=0,3-Population**.
|
||
(c) k=0,8 kürzt die Empfehlungen um **~39 %** (H1 n=766 → 531) und treibt den
|
||
WARTEN-Anteil weiter hoch — der steht mit 93 % live ohnehin schon im Fokus, und
|
||
`breakout_pending` ist bereits die Ursache von 63 % aller Blocks.
|
||
**Vor einer Änderung ist zu klären, WOFÜR das Signal da ist:** als mechanischer
|
||
Auto-Entry wäre k=0,8 die belegte Wahl; als Auswahl-Angebot für den Menschen ist
|
||
weniger Auswahl nicht automatisch besser. Die Zahlen liegen vor, die Entscheidung
|
||
ist eine Nutzungs-, keine Messfrage.
|
||
⚠ **Zwei benannte Abweichungen der Simulation** (beide unvermeidbar):
|
||
(1) `_confirm_breakout` misst seinen Timeout mit `time.time()` gegen
|
||
`_breakout_timeout_s`=3600 s — in einem Bar-Loop vergeht keine Wall-Clock-Zeit,
|
||
der Timeout würde NIE feuern. 3600 s sind auf M5 exakt **12 Bars**, so wird hier
|
||
gezählt. **Merke: jede `time.time()`-Messung im Live-Code ist in einer
|
||
Bar-Simulation stumm.** (2) `hour=None`, weil die EIA-Prüfung in `_build` über
|
||
`datetime.now(_BERLIN)` läuft — mit Bar-Stunde würde ein Lauf am Mittwoch
|
||
15:30–16:30 **alle** Bars blocken. `dead_hours` ist live leer, es fehlt also nur
|
||
das EIA-Fenster.
|
||
|
||
**Phase 2b — verworfene Modelle bei vollen Bars nachgefahren: ALLE bleiben verworfen.**
|
||
14 Klassen, keine einzige kippt ins Positive:
|
||
| Klasse | Ergebnis |
|
||
|---|---|
|
||
| ORB (EU/US, 15/30 min) | H1 −0,018…−0,177 · H2 +0,013…+0,280 → Regime-Kippen |
|
||
| Doppeltop (3 Toleranzen) | 3/3 fallen durch |
|
||
| Volume Profile (POC/HVN/LVN) | 3/3 fallen durch |
|
||
| Liquidity Sweeps (SMC) | alle Varianten fallen durch |
|
||
| Konsolidierung (False Break) | alle Varianten fallen durch |
|
||
| Handbuch (PDH/PDL, Asian Range, Discount/Premium) | alle fallen durch |
|
||
| Marktstruktur | 5/5 fallen durch |
|
||
| Momentum · Inter-Market · Chop-Gate · Reversal-Lockout · S/R-Close-Signal · MACD/ADX/RSI · HTF-Winkel | unverändert verworfen |
|
||
⚠ **Bemerkenswert:** Bei ~14 Wiederholungstests wäre bei 5 % Fehlalarmquote rund
|
||
**ein** falsch-positives Ergebnis zu erwarten gewesen — es gab **keins**. Das
|
||
stützt die Ablehnungen zusätzlich. Die Regel wurde VOR dem Lauf fixiert (ØR>0 und
|
||
PF>1 in BEIDEN Hälften, Nachbarparameter müssen mithalten).
|
||
⚠ Das ist ausdrücklich **kein Freibrief für regelmäßiges Nachrechnen** — die
|
||
Begründung gegen wöchentliche Läufe (Mehrfachvergleich, ~47 Fehlalarme/Jahr)
|
||
steht unverändert. Anlass war hier die *einmalige* Signaländerung.
|
||
|
||
## Feste Einsatz-Margin + lokale Sprachausgabe (2026-08-04)
|
||
|
||
**Einsatz-Feld** (User-Wunsch „Feld, mit dem ich meine eingesetzte Margin selber
|
||
eingeben kann"): `[trading]`-unabhängiger Laufzeitwert `MANUAL_MARGIN` in
|
||
`core/config.py` (`set/get_manual_margin`, Muster wie `set_margin_buffer`).
|
||
**0 = automatisch** (bisheriges Verhalten: `margin_buffer_pct` % der freien Margin),
|
||
**> 0 = die Position wird auf genau diesen Margin-Einsatz gerechnet.**
|
||
· `calc_lots` setzt es um und **deckelt auf die freie Margin × Buffer** — ein zu
|
||
hoher Wunschwert wird begrenzt und geloggt, nicht an den Broker durchgereicht
|
||
(sonst Reject mit kryptischem retcode statt sauberer Meldung). Reicht der Betrag
|
||
nicht fürs Mindestlot → 0.0 + Warnung, der Trade wird sauber abgelehnt.
|
||
· **Vorrang vor dem Risiko-Modus** (`trader._send_locked`): eine ausdrückliche
|
||
Größenvorgabe schlägt die Rechenregel. Bei `risk_pct=0` (live) ohnehin der Fall.
|
||
· ⚠ **Wirkt auf ALLE neuen Positionen — auch die autonomen** (Squeeze/Signal).
|
||
· UI: Feld **„Einsatz"** in der **Order-Leiste** (`#margin-input`, v=136), bernstein
|
||
umrandet solange gesetzt. ⚠ **Bewusst NICHT in der Trade-Leiste** — die ist nur bei
|
||
OFFENER Position sichtbar, der Einsatz muss aber VOR dem Öffnen einstellbar sein.
|
||
Enter blurrt nur, `change` sendet einmal (gleicher Fix wie beim Mindestgewinn).
|
||
· `POST /api/manualmargin {value}` · Snapshot `manual_margin` · neustart-fest über
|
||
`runtime_state.json`. Ende-zu-Ende getestet (250 → Snapshot → 0 → persistiert).
|
||
|
||
**Einsatz in PROZENT (2026-08-05, v=137):** das Gegenstück zum festen Betrag —
|
||
`margin_buffer_pct` (live 95) hatte bisher **keine UI** und war nur in der ini
|
||
änderbar. Neu: Feld **„Einsatz %"** (`#marginpct-input`) direkt neben „Einsatz €" in
|
||
der Order-Leiste · `POST /api/marginpct {value}` → `engine.set_margin_pct` →
|
||
`config.set_margin_buffer` · Snapshot `margin_pct` · neustart-fest.
|
||
⚠ **VORRANG ist sichtbar gemacht:** steht links ein fester €-Betrag, wird das
|
||
Prozent-Feld **ausgegraut** (`.mg-field.muted`, gestrichelter Rand) und der Tooltip
|
||
sagt, dass der Prozentsatz dann nur noch die Obergrenze ist — zwei scheinbar
|
||
gleichrangige Felder ohne Hinweis wären die schlechtere Lösung.
|
||
⚠ Geklemmt auf **1–99 %** (`set_margin_buffer`); 100 % ließe keinen Puffer für
|
||
Spread/Swap. Ende-zu-Ende getestet: 50 → 50 · 150 → auf 99 geklemmt · 0 und −5
|
||
abgelehnt · 95 → 95, in `runtime_state.json` persistiert.
|
||
|
||
**Sprachausgabe: Piper, lokal** (`tools/speak.py`, `tools/piper/`). Anlass: die
|
||
Windows-Sprachausgabe funktioniert zwar, aber auf diesem Rechner ist **keine
|
||
deutsche Stimme installiert** — weder SAPI5 noch OneCore (nur David/Zira/Mark,
|
||
alle en-US). Deutscher Text käme mit englischer Aussprache. `Add-WindowsCapability`
|
||
scheitert ohne Adminrechte (elevated Fenster nötig).
|
||
Piper gewählt, weil es **lokal** läuft (passt zum Rest: WireGuard-only, Secrets in
|
||
der ini, keine Cloud-Abhängigkeit), **nichts kostet** (nach der 200-$/Monat-Absage
|
||
bei X relevant) und aus PowerShell/Python aufrufbar ist. Stimme *thorsten-medium*
|
||
(de_DE), Real-Time-Faktor **0,08** — 4,3 s Audio in 0,36 s.
|
||
⚠ **Das ZIP entpackt eine Ebene tiefer als erwartet** (`tools/piper/piper/piper.exe`)
|
||
— `speak.py` **sucht** die Binärdatei und das Modell deshalb, statt Pfade fest zu
|
||
verdrahten. Der erste Entwurf scheiterte genau daran; der Fail-safe hat die Pfade
|
||
sauber gemeldet statt stumm zu bleiben.
|
||
Aufruf: `python tools/speak.py "Text"` · Pipe · `--wav datei` (nur erzeugen).
|
||
Rückgabe 1 bei fehlendem Piper → ein Aufrufer im Bot läuft weiter, statt abzustürzen.
|
||
⚠ `tools/piper/` ist **gitignored** (Binaries + 60-MB-Modell gehören nicht ins Repo);
|
||
`speak.py` selbst ist versioniert.
|
||
|
||
## Modul „Manuelle Trades" + voller Entscheidungskontext (Stufe 1, 2026-08-04)
|
||
|
||
**User-Frage:** „Kann der Bot aus meinen manuellen Trades lernen — evtl. ein neues
|
||
Modul?" Auslöser: am 04.08. machten 18 manuelle Trades **+728 €**, der Bot −16,53 €,
|
||
bei 93 % WARTEN.
|
||
|
||
⚠ **Die exploratorische Vorprüfung ergab: noch NICHT lernbar** — und zwar aus drei
|
||
Gründen, die alle benannt gehören:
|
||
1. **Die entscheidenden Merkmale wurden nie gespeichert.** Beim Einstieg schrieb der
|
||
Bot 5 Felder; davon war `ai_sentiment` in **1,4 %** und `news_score` in **34 %** der
|
||
Trades befüllt. Die Marktlage eines manuellen Einstiegs war damit nicht
|
||
rekonstruierbar.
|
||
2. **Das naive Lernziel ist untauglich.** „Wann steigt er ein" bräuchte Gegenbeispiele
|
||
(Bars ohne Einstieg) — Millionen unbeschriftete Negative, und „nicht eingestiegen"
|
||
heißt nicht „hätte man nicht sollen". Die brauchbare Frage lautet: **welche seiner
|
||
Einstiege liefen gut** (saubere Labels über P&L).
|
||
3. **Die scheinbaren Muster sind Einzeltage.** Stunde 13:00 zeigt +478 € über n=69 —
|
||
davon stammen **+382 € (80 %) aus den fünf Trades des 04.08.** Genau die Falle, an
|
||
der ORB und die Dead-Hours gescheitert sind.
|
||
|
||
**Gebaut wurde deshalb Stufe 1 — Telemetrie, kein Verhalten:**
|
||
`trades` um **16 `ctx_*`-Spalten** erweitert (DB-Backup `…bak-2026-08-04`, 1.148
|
||
Zeilen unverändert): `p_break_target/-stop` · `dist_res_atr`/`dist_sup_atr` ·
|
||
`structure` · `squeeze` · `htf_trend`/`h1_trend` · `spread_atr` · `session` ·
|
||
`block_reason` · `conf_pct` · `atr_m5` · `bias` · `cone_pos` · `hour`. Alle Werte
|
||
werden im Snapshot ohnehin berechnet und nur eingesammelt
|
||
(`engine._run_analysis` → `trader.set_open_context(ctx=…)` → `history.log_trade_open`).
|
||
`log_trade_open` hängt die Spalten **dynamisch** an und ignoriert unbekannte Schlüssel
|
||
— eine ältere DB ohne Migration läuft weiter (fail-open).
|
||
⚠ **Fallstrick beim Bau:** `wave_snap` existiert in `_run_analysis` NICHT (nur in
|
||
`snapshot()`). Der erste Entwurf hätte einen `NameError` erzeugt, den das umgebende
|
||
`except` still geschluckt hätte — der Kontext wäre dauerhaft leer geblieben, ohne dass
|
||
es auffällt. Jetzt `self.wave.snapshot()`; `vd` zusätzlich per `locals()`-Guard
|
||
abgesichert (es entsteht in einem eigenen `try`).
|
||
Verifiziert auf einer DB-**Kopie**: alle 16 Spalten korrekt geschrieben, unbekannter
|
||
Schlüssel ignoriert, Basisfelder unberührt.
|
||
|
||
**Dashboard-Karte „Manuelle Trades"** (`#card-manual`, v=134): heute / 30 Tage /
|
||
gesamt / Bot-Vergleich, dazu der Split **mit vs. ohne Signal-Deckung**. Backend
|
||
`history.manual_stats(30)` → `engine._manual_stats_cached()` (~120 s Cache, wie
|
||
`_alignment_cached` — die Aggregation läuft über die ganze Tabelle und hat im
|
||
1-s-Snapshot nichts zu suchen). Bewusst **neutral gefärbt** (keine Ampel) und
|
||
ausdrücklich **deskriptiv**: die Karte zeigt eine Reifegrad-Zeile mit der
|
||
`ctx`-Abdeckung (heute 0 %), damit niemand sie für aussagekräftiger hält, als sie ist.
|
||
|
||
**Stufe 2 (später, NICHT gebaut):** wenn genug `ctx`-Daten vorliegen (~2–3 Monate),
|
||
messen was Gewinner von Verlierern unterscheidet — Fit auf H1, validiert auf H2, AUC
|
||
und Kalibrierung berichtet, wie beim P(break)-Modell. Ergebnis wäre ein **Hinweis im
|
||
Order-Dialog** („deine Trades in dieser Konstellation: X % WR, n=Y"), analog zum
|
||
bestehenden Ausrichtungs-Split. ⚠ **Ausdrücklich KEIN Auto-Entry daraus** — 22
|
||
Signal-Eingriffe sind gescheitert, und die gemessene Stärke des Users liegt in der
|
||
AUSWAHL, nicht in einer Regel. Ein Modul, das ihm seine eigene Historie zur aktuellen
|
||
Lage zeigt, spielt auf diese Stärke; eines das für ihn einsteigt, ersetzt sie.
|
||
|
||
## ⚠⚠ P(break) v2 LIVE GEPRÜFT (2026-08-07) — KALIBRIERUNG DANEBEN, TRENNSCHÄRFE UNENTSCHIEDEN
|
||
|
||
Fällige B4-Messung nach dem Nachtrainieren vom 31.07. (`analyze_pbreak_live.py`,
|
||
878 ausgewertete Vorhersagen ab 01.08.). Zwei Befunde, die auseinandergehalten
|
||
gehören — und einer davon betrifft die Messung selbst.
|
||
|
||
⚠⚠ **ZUERST: DAS SKRIPT MISCHTE ZWEI MODELLE.** Es trennte bei der
|
||
M30-Level-Umstellung (30.07.), **nicht** beim Nachtrainieren (31.07.); „GESAMT"
|
||
las damit altes und neues Modell zusammen. Genau der Fehler, der am 02.08. in
|
||
`analyze_divergence.py` schon behoben wurde (`_EPOCHS`) — hier stand er noch.
|
||
Neu: `MODELL_V2` mit demselben Stichtag wie die Fälligkeitsbedingung (01.08.),
|
||
plus eigener Abschnitt „NACHTRAINIERTES MODELL". Die alten Abschnitte bleiben.
|
||
|
||
**(1) KALIBRIERUNG — messbar daneben, und die Ursache ist benennbar.**
|
||
| P(break) | n | vorhergesagt | real |
|
||
|---|---|---|---|
|
||
| 0–19 | 56 | 15,1 % | **53,6 %** |
|
||
| 20–34 | 199 | 29,8 % | 43,2 % |
|
||
| 35–49 | 414 | 42,2 % | 56,8 % |
|
||
| 50–64 | 180 | 54,1 % | 61,7 % |
|
||
| **65–100** | 29 | **69,0 %** | **17,2 %** ⚠ invertiert |
|
||
⚠ **Die Basisrate ist gewandert** — Training **37,8 %**, live **53,2 %**, und das
|
||
**stabil über vier volle Tage** (03.08. 51,4 % · 04.08. 54,1 % · 05.08. 56,4 % ·
|
||
06.08. 53,8 %, je n=122–276). Das ist kein Rauschen. Ein Modell, dessen Niveau an
|
||
einer 37,8-%-Basisrate verankert ist, muss in einem 53-%-Regime systematisch zu
|
||
niedrig sagen — genau das steht in der Tabelle. Dieselbe Signatur wie beim alten
|
||
Modell (alle unteren Klassen „zu niedrig", oberste invertiert).
|
||
|
||
**(2) TRENNSCHÄRFE — NICHT entscheidbar, und das ist der wichtigere Befund.**
|
||
| | |
|
||
|---|---|
|
||
| AUC roh (n=878) | 0,541 |
|
||
| **AUC entkoppelt (n=87)** | **0,556** |
|
||
| **95-%-Bootstrap-KI** | **[0,434 … 0,674]** |
|
||
Das Intervall enthält **den Zufall (0,50) UND die Messlatte (0,654)**. Es ist
|
||
also **nicht** belegt, dass das Modell schlechter rankt als beim Nachtrainieren —
|
||
es ist nur nichts belegt. „AUC 0,54" als Durchfall zu lesen wäre falsch.
|
||
|
||
⚠⚠ **DIE FÄLLIGKEITSBEDINGUNG ZÄHLTE DIE FALSCHE GRÖSSE — behoben.** Sie stand
|
||
auf **800 ROHEN** Vorhersagen; die sind nicht unabhängig (dieselbe Position
|
||
pendelt vielfach um dasselbe Level). Aus 878 rohen werden **87 entkoppelte** —
|
||
die Bedingung hat die Beweismenge um **rund das Zehnfache** überschätzt und die
|
||
Messung als „fällig" gemeldet, obwohl sie nichts entscheiden konnte. Die
|
||
Abhängigkeit war im Skript seit jeher dokumentiert („entkoppelte Sicht"), nur die
|
||
Fälligkeit zählte daran vorbei. **Jetzt: 350 ENTKOPPELTE** (halbiert die
|
||
KI-Breite 0,12 → 0,06, erst dann sind 0,50 und 0,65 trennbar; bei ~17/Tag ≈ 3
|
||
Wochen). Stand: **87/350**.
|
||
⚠ Verwandt mit der Bootstrap-Grenze bei `backtest_cost_gate.ci95`
|
||
(„überlappende Beobachtungen → effektives n ist ~n/24") — dieselbe Klasse Fehler,
|
||
diesmal in einer Fälligkeitsbedingung statt in einem Konfidenzintervall.
|
||
|
||
✅ **Operativ kein Notfall:** `auto_sr_close = false` seit 06.08. — das Modell
|
||
schliesst **keine** Trades. Es speist nur den Hinweis in der App, die
|
||
Chart-Linien, den Copilot-Kontext und `stop_approach`. Die Fehlkalibrierung
|
||
kostet damit aktuell **kein Geld**, sie macht nur die Anzeige zu pessimistisch
|
||
(sie sagt „Level hält", wo es in 53 % der Fälle bricht).
|
||
⚠ **Vor einem erneuten Nachtrainieren:** der letzte Versuch hat live auch nicht
|
||
gehalten. Der Befund zeigt auf das **Niveau** (Basisrate/Achsenabschnitt), nicht
|
||
zwingend auf die Rangfolge — ein drittes Fitten derselben Bauart würde denselben
|
||
Weg gehen. Naheliegender wäre, das Niveau **laufend** an die jüngste Basisrate zu
|
||
koppeln oder ein Vola-/Regime-Merkmal aufzunehmen. **Nicht gebaut, nicht
|
||
gemessen.**
|
||
|
||
## ⚠⚠ P(break): NIVEAU-REPARATUR GEMESSEN — BEIDE VARIANTEN FALLEN DURCH (2026-08-07)
|
||
|
||
Folgeauftrag zur B4-Prüfung: die Vermutung war „das Modell RANKT brauchbar, nur
|
||
sein NIVEAU hängt an einer Basisrate, die wandert" — also entweder ein
|
||
Regime-Merkmal oder ein laufend nachgeführter Achsenabschnitt.
|
||
`backtest_pbreak_regime.py`, Regel **vorab** fixiert (beide Fit-Richtungen besser
|
||
kalibriert · AUC nicht schlechter, Toleranz 0,005 · Nachbarn kippen nicht).
|
||
⚠ `collect()` in `backtest_pbreak_retrain.py` hat dafür einen **additiven**
|
||
`REG`-Parameter bekommen (Default `None`) — Ausgabe ohne ihn **bitgenau** wie
|
||
vorher geprüft (4.371 Zeilen, 0 Abweichungen).
|
||
|
||
**(1) Auf BACKTEST-Daten besteht die Roll-Achse, das Regime-Merkmal nicht:**
|
||
| Variante | H1→H2 Kalib. / AUC | H2→H1 Kalib. / AUC |
|
||
|---|---|---|
|
||
| A) Basis (fester Achsenabschnitt) | 2,61 Pp / 0,659 | 10,51 Pp / 0,599 |
|
||
| B) + Regime (ATR/Median) | 2,51 / 0,658 | **11,20 ❌** / 0,599 |
|
||
| **C) Roll-Achse (n=400)** | **1,95 / 0,661** | **3,25 / 0,612** |
|
||
| *Kontrolle 1: Modell VOR dem 31.07.* | 25,81 / **0,364** | 20,25 / **0,406** |
|
||
| *Kontrolle 2: Modell wie HEUTE live* | 1,63 / 0,659 | 2,30 / 0,612 |
|
||
⚠ **Kontrolle 2 ist IN-SAMPLE** (die Live-Gewichte wurden final auf allen 80k
|
||
Bars gefittet) — sie muss gut aussehen und ist kein Verdienst.
|
||
✅ Kontrolle 1 reproduziert exakt, warum am 31.07. nachtrainiert wurde
|
||
(AUC **unter 0,5** = invertiert).
|
||
|
||
**(2) AUF LIVE-DATEN FÄLLT DIE ROLL-ACHSE DURCH — und das ist die Messung, die
|
||
zählt** (n=921 ab 01.08., echte Bruchrate 52,7 %):
|
||
| | AUC | Ø-Vorhersage | Kalib.-Fehler |
|
||
|---|---|---|---|
|
||
| IST | 0,531 | 41,8 % | **14,16 Pp** |
|
||
| Roll n=400 | 0,516 | 48,7 % | 10,64 Pp |
|
||
| **Roll n=800** | 0,510 | 42,8 % | **15,14 Pp ❌ schlechter als IST** |
|
||
Sie senkt den Fehler auf ~10,6 Pp — **repariert ihn also nicht** —, verliert AUC
|
||
über der Toleranz und **kippt beim Nachbarn n=800**. Regel verfehlt, **kein
|
||
Einbau**.
|
||
⚠ **Der Grund steht in der Klassentabelle: die oberste Klasse bleibt
|
||
INVERTIERT** (69 % vorhergesagt, **17,2 %** real, n=29). Eine Niveau-Verschiebung
|
||
kann eine invertierte Klasse prinzipiell nicht reparieren. Die Ausgangshypothese
|
||
(„nur das Niveau") ist damit **widerlegt**.
|
||
|
||
**(3) DREI ERKLÄRUNGEN FÜR DIE BASISRATEN-LÜCKE GEPRÜFT — ALLE WIDERLEGT.**
|
||
Live 52,7 % liegt **ausserhalb der gesamten 9-Monats-Spanne** des Backtests
|
||
(Zehntel-Blöcke: 33,6 … 41,1 %).
|
||
| Hypothese | Ergebnis |
|
||
|---|---|
|
||
| **Sammelart** — live loggt 1× je frischer Berührung, der Backtest JEDEN Bar in der Zone | 37,5 % gegen 39,1 % = **−1,7 Pp, falsche Richtung** ❌ |
|
||
| **Vola-Regime** — Bruchrate steigt mit ATR | über 5 Quintile **flach**: 37,0 · 37,0 · 39,2 · 38,9 · 37,4 % ❌ |
|
||
| **aktuelles Regime ungewöhnlich** | letzte 7 Tage Median **0,96** = normal (Gesamt 0,99) ❌ |
|
||
✅ Der flache Vola-Befund erklärt zugleich, **warum B nichts bringt**: in dieser
|
||
Achse steckt keine Information.
|
||
|
||
**FAZIT: nichts gebaut.** Der nächste Schritt ist **kein drittes Fitten** — der
|
||
Fehler sitzt nicht im Niveau. Die einzige nicht geprüfte Divergenz ist die
|
||
**Level-Auswahl selbst**: der Backtest bildet sie mit `pick_level`/`_cluster`
|
||
nach, live entscheidet `_draw_levels`. Ein Nachbau ist kein Nutzer desselben
|
||
Codes — genau die Fehlerklasse, die am 31.07. schon einmal zugeschlagen hat
|
||
(„Training = Level bei Entry fixiert, Live = Level wandert mit"). **Dort wäre als
|
||
Nächstes zu messen.**
|
||
⚠ Operativ weiterhin **kein Notfall**: `auto_sr_close = false` seit 06.08., das
|
||
Modell schliesst keine Trades.
|
||
⚠ Und die Live-AUC bleibt unentschieden (entkoppelt n=87, KI [0,434 … 0,674]) —
|
||
„die Rangfolge ist kaputt" ist **nicht** belegt; belegt ist nur die
|
||
Basisraten-/Kalibrierungslücke.
|
||
|
||
## ✅ KEGEL MIT ADAPTIVE CONFORMAL INFERENCE (2026-08-08, v=158)
|
||
|
||
Der Kegel war seit dem 31.07. dokumentiert **3–5 Pp ZU ENG** — 80 % nominal
|
||
gegen real **76,7 / 76,6 / 75,3 %** — und blieb es, weil Nachfitten das Problem
|
||
nur verschiebt: die nächste Periode hat wieder eine andere Vola. **ACI** ist die
|
||
Standardmethode dafür (Web-Recherche 08.08.): sie justiert das Niveau **online**
|
||
nach jedem aufgelösten Fenster und konvergiert nachweislich gegen die
|
||
Zielabdeckung, **auch unter Verteilungsdrift**.
|
||
|
||
⚠ **EHRLICH ZUR BAUART:** die Lehrbuch-Form justiert `α` und schlägt das
|
||
zugehörige Quantil nach. Hier liegt nur eine feste **Quantil-Tabelle** vor, kein
|
||
Verteilungsobjekt — umgesetzt ist deshalb die **multiplikative Variante**:
|
||
```
|
||
s ← s · exp(γ · (1{verfehlt} − α_ziel)) γ = 0,02 · Deckel [0,7 … 2,0]
|
||
```
|
||
Dieselbe Regelungsidee, aber **die Konvergenz-Garantie der Originalform ist
|
||
damit NICHT wörtlich übertragen** — es ist ACI im Geiste.
|
||
|
||
✅ **Verifiziert:** bei **exakt 20 % Verfehlern bleibt die Skala bei 1,0000** —
|
||
das ist der Fixpunkt, und er stimmt. Simulation aus der dokumentierten Lage
|
||
(76,6 % Abdeckung): nach ~50 Checks um 80 %.
|
||
⚠ Die Skala **pendelt** um das Ziel statt exakt zu treffen. Das ist das bekannte
|
||
ACI-Verhalten: garantiert ist die **Langfrist**-Abdeckung, nicht der Einzelwert.
|
||
|
||
⚠⚠ **NICHT-ÜBERLAPPEND geloggt** (`cone_checks`): ein 120-min-Fenster alle 5 min
|
||
wäre **24-fach überlappend** — genau die Cluster-Verzerrung, an der
|
||
`analyze_hl_funding.py` (06.08.) aufgelaufen ist und die die
|
||
Fälligkeitsbedingung `pbreak_accuracy_v2` um das Zehnfache danebenliegen liess
|
||
(07.08.). Je Horizont startet ein neuer Check erst, wenn der vorige abgelaufen
|
||
ist → 48/24/12 Checks je Tag.
|
||
✅ **Neustart-fest** über `runtime_state.json` — ohne Persistenz liefe die
|
||
erlernte Kalibrierung nach jedem Neustart wieder bei 1,0 los.
|
||
⚠ `build(price, atr)` OHNE `skalen` liefert **bitgenau** die alten Werte
|
||
(additiv geprüft).
|
||
|
||
⚠⚠ **ZWEI EIGENE PLATZIERUNGSFEHLER beim Bau, beide gefangen:** der Lade-Block
|
||
landete zuerst **hinter** den `except`-Klauseln von `_load_runtime_state`, wo
|
||
`st` gar nicht mehr existiert; und beim M15-Setup landete ein Block **mitten in
|
||
einem mehrzeiligen Aufruf**. Der erste wäre stillschweigend nie gelaufen
|
||
(`except Exception` hätte ihn geschluckt) — gefunden nur, weil ich die Funktion
|
||
danach gelesen habe statt dem grünen `py_compile` zu trauen.
|
||
|
||
## ✅ DIE M15-KARTE MISST SICH JETZT SELBST + AUSFÜHRUNGSQUALITÄT SICHTBAR (2026-08-08, v=157)
|
||
|
||
Nach Web-Recherche umgesetzt (User-Wahl aus drei Vorschlägen). ⚠ Der dritte
|
||
(Kegel mit **Adaptive Conformal Inference** nachkalibrieren) bleibt OFFEN — der
|
||
Kegel ist mit **76,7 / 76,6 / 75,3 %** gegen 80 % Nennwert dokumentiert zu eng,
|
||
und ACI ist die Standardmethode dafür (adjustiert das Niveau online, garantiert
|
||
Konvergenz auch unter Drift). Nicht gebaut.
|
||
|
||
**② SELBSTMESSUNG (`m15_states`).** Die Karte hatte **keine Rückkopplung** —
|
||
geprüft, es gab kein Logging. Das war die auffälligste Lücke: **jeder** Ausfall,
|
||
der in diesem Projekt je aufgedeckt wurde, kam aus einer Rückkopplung
|
||
(`pbreak_predictions` entlarvte den Modellausfall LIVE, was kein Backtest
|
||
konnte; der B4-Monitor die Squeeze-Drift). Die Karte hätte monatelang falsch
|
||
stehen können.
|
||
Neu: eine Zeile je **Zustandswechsel** (Status + Lesart + Level), nach 30/60 min
|
||
gegen `candles_m1` ausgewertet, Ergebnis als Zeile ⑥ in der Karte.
|
||
⚠ **Gewertet wird die LESART, nicht der Status** — und nur die gerichtete
|
||
(45–55 % zählt nicht mit). Gegen 50 % zu lesen.
|
||
⚠ **Nur bei Wechsel**, nie je Tick: sonst entsteht dieselbe Cluster-Verzerrung,
|
||
die am 07.08. elf widerlegte Hypothesen ausgelöst hat (roh 922 Zeilen 52,6 %,
|
||
entkoppelt 93 Zeilen 45,2 %) — und die mein erster Tagestest reproduzierte
|
||
(46 fortlaufende Bars als 46 Trades). Live verifiziert: **1 Zeile** trotz
|
||
laufendem Sekundentakt.
|
||
|
||
**③ AUSFÜHRUNGSQUALITÄT (Zeile ⑦).** `analyze_squeeze_entry_gap.py` existiert
|
||
seit dem 05.08. und misst die TCA-Kennzahl (Arrival-Price-Slippage) — **niemand
|
||
hat je hineingesehen.** Dabei war das der einzige Erfolgsmaßstab des
|
||
Stop-Order-Umbaus (Abstand Einstieg↔Level muss gegen 0, vorher Median
|
||
+0,275×ATR) und zugleich der **grösste je gemessene Hebel** (+0,32 R gegenüber
|
||
Market). Jetzt steht der Zählstand in der Karte: **3 von 12** Bot-Trades seit dem
|
||
Umbau.
|
||
|
||
⚠⚠ **BEIM BAU EIN NAMEERROR IM 1-SEKUNDEN-PFAD — vom LINTER gefangen.** Der
|
||
erste Entwurf berechnete `_m15` in `_run_analysis` und benutzte es in
|
||
`snapshot()`; dort gibt es weder `market` noch `wave_snap`. **`py_compile` sieht
|
||
das NICHT**, `ruff F821` meldete **4 Befunde**, darunter `_m15` selbst. Genau
|
||
die Fehlerklasse, für die der Linter am 07.08. eingebaut wurde (die beiden
|
||
`log.`-NameError in `core/trader.py` sassen Monate in `except`-Zweigen) — und
|
||
der erste echte Fang.
|
||
⚠ Zweiter Fehler beim Verschieben: der Block landete **mitten in einem
|
||
mehrzeiligen Aufruf** (`_cone = _cone_build(`) → SyntaxError. Beide behoben.
|
||
|
||
## ⚠⚠ DIE ZWEI GERATENEN M15-SCHWELLEN GEMESSEN — beide NICHT kalibrierbar (2026-08-08)
|
||
|
||
Auftrag: die Reichweite (0,5×ATR) und die Kosten-Schwelle (0,32, von M5) messen
|
||
statt raten. `backtest_m15_gates.py`, 2 Halbjahre, Level-Berührungen auf M15,
|
||
Einstieg AM Level in HTF-Richtung, **Fill bei Berührung**, kanonischer Exit,
|
||
Echtkosten, Episoden-entkoppelt, mit 95-%-Bootstrap je Zelle.
|
||
|
||
⚠⚠ **ZUERST EIN ARTEFAKT IM EIGENEN CODE — und es hätte zu einem absurden Rat
|
||
geführt.** Der erste Lauf stieg **sofort zum Level-Preis** ein, auch wenn der
|
||
Kurs 0,9×ATR entfernt stand — die Sim schenkte sich damit genau diesen Abstand.
|
||
Ergebnis: „je WEITER weg vom Level, desto besser" (oberster Bucket +0,293 in H2,
|
||
KI schloss die Null aus). Mit **Fill bei Berührung** verschwindet es:
|
||
| Abstand | vorher H2 | **mit Fill** |
|
||
|---|---|---|
|
||
| 0,30–0,50 | +0,142 | +0,216 |
|
||
| 0,75–1,00 | **+0,293** | **+0,179** |
|
||
Derselbe Fehler wie beim Momentum-Test (ØR +2,2 entlarvte den Code).
|
||
|
||
**② ERGEBNIS: KEINE der beiden Schwellen ist kalibrierbar.**
|
||
| Abstand ×ATR | H1 ØR | H2 ØR |
|
||
|---|---|---|
|
||
| 0,00–0,10 | −0,227 | +0,100 |
|
||
| 0,10–0,20 | −0,130 | −0,115 |
|
||
| 0,20–0,30 | −0,306 | −0,129 |
|
||
| 0,30–0,50 | −0,112 | **+0,216** |
|
||
| 0,50–0,75 | −0,039 | +0,042 |
|
||
| 0,75–1,00 | −0,040 | +0,179 |
|
||
**H1 ist über ALLE sechs Abstands-Buckets negativ.** Kein Bucket besteht die
|
||
vorab fixierte Regel (beidhälftig positiv + KI ohne Null). Beim Kosten-Gate
|
||
dasselbe: nur H2 0,06–0,10 schliesst die Null aus (+0,254/+0,307), H1 liegt dort
|
||
bei +0,016/−0,040.
|
||
|
||
✅ **Der eigentliche Befund ist nicht „welche Schwelle", sondern: es gibt nichts
|
||
zu optimieren.** Die Population selbst trägt in H1 nicht — Level-Berührungen auf
|
||
M15 in HTF-Richtung sind dort durchgehend negativ. Eine Schwelle kann keinen
|
||
Edge erzeugen, den die Grundgesamtheit nicht hat.
|
||
⚠ Konsequenz für die Karte: die 0,5×ATR bleiben eine **gesetzte** Zahl (ehrlich
|
||
so benannt), und das Kosten-Gate bleibt auf M15 **dekorativ** — beides ist jetzt
|
||
gemessen, nicht mehr nur vermutet. Einzige schwach gestützte Richtung: teuer ist
|
||
schlechter (H2 0,16–0,20 → −0,482, KI [−0,949 … +0,008]).
|
||
|
||
## ⚠⚠ M15-KARTE AN 7 TAGEN GETESTET — zwei eigene Fehler, einer davon fast veröffentlicht (2026-08-08)
|
||
|
||
**(1) BIAS WAR EIN RICHTUNGSGEBER — korrigiert zum VETO (v=155).** Der Tagestest
|
||
an 07.08. (`test_m15_karte_tag.py`) zeigte: 46 von 92 Bars grün, entkoppelt
|
||
**13 Episoden — ALLE LONG**, an einem Tag, der von 78,399 auf 77,383 **fiel**.
|
||
Trefferquote **0 %**, ØR −0,753.
|
||
⚠ Ursache strukturell, nicht Pech: gemessen ist „gegen den M30-Trend halbiert
|
||
den Edge" — eine **VERBOTS**-Aussage. Ich hatte daraus „M30+H1 einig → Bias
|
||
LONG" gemacht, also einen Richtungsgeber. **Exakt der Fehler, wegen dem die
|
||
Gesamtempfehlung entfernt wurde** (Richtung dort 48–51 % Treffer). Behoben:
|
||
`bias.richtung` ist dauerhaft `None`, das Feld liefert nur noch die **verbotene**
|
||
Richtung; das Banner nennt keine Richtung mehr.
|
||
|
||
**(2) FAST EINEN FEHLALARM VERÖFFENTLICHT.** Der Wochentest
|
||
(`test_m15_karte_woche.py`, 30.07.–07.08., 93 Episoden) meldete das Veto als
|
||
**falsch herum**: MIT dem HTF ØR −0,273 / PF 0,56, GEGEN den HTF **+0,258 /
|
||
PF 1,83** — Differenz −0,532 R gegen die erlaubte Richtung.
|
||
✅ **Über 2 Halbjahre auf DERSELBEN Population (Level-Berührungen, M15) hält das
|
||
Veto aber:**
|
||
| | MIT dem HTF | GEGEN den HTF | Differenz |
|
||
|---|---|---|---|
|
||
| **H1** (n=1504) | −0,072 / PF 0,87 | −0,116 / PF 0,80 | **+0,044 R** |
|
||
| **H2** (n=1617) | +0,028 / PF 1,06 | −0,012 / PF 0,98 | **+0,040 R** |
|
||
Beidhälftig konsistent, gleiche Größenordnung. **Die Woche war Rauschen** —
|
||
n=93 gegen n=3121. Dieselbe Falle wie bei der 15-Minuten-Regel (31.07.) und dem
|
||
Nacht-Guard (05.08.): ein Zeitraum, der zum Beleg erhoben wird.
|
||
⚠ Der Verdacht, der mich zur Gegenprüfung brachte, war trotzdem berechtigt: der
|
||
HTF-Filter ist auf **Signal**-Entries validiert (`backtest_htf_filter.py`), nicht
|
||
auf Level-Berührungen. Auf der Level-Population trägt er **+0,04 R** statt des
|
||
dokumentierten „Edge ×2" — richtig, aber klein.
|
||
|
||
**(3) WAS DER TEST SONST ZEIGT — die Karte ist zu permissiv:**
|
||
| | |
|
||
|---|---|
|
||
| grüne Bars je Tag | 22–56 von 92 (**24–61 %**) |
|
||
| Episoden je Tag | **14,7** |
|
||
| Kosten-Gate blockte | **0 von 644 Bars** |
|
||
Das Kosten-Gate ist auf M15 **dekorativ**: Spread/ATR liegt dort bei ~0,10, die
|
||
Schwelle bei 0,32. Und die Level-Einstiege selbst tragen kaum (ØR −0,072 / +0,028
|
||
über 2 Halbjahre) — das Veto macht sie nur **weniger schlecht**.
|
||
⚠ Die einzige diskriminierende Schwelle (Reichweite 0,5×ATR) ist **gesetzt, nicht
|
||
gemessen**.
|
||
|
||
⚠⚠ **Und ein Fehler im TEST selbst:** der erste Lauf zählte 46 fortlaufende
|
||
grüne Bars als 46 Trades und meldete 7 % Trefferquote — **eine Bewegung 46-mal
|
||
gezählt**. Erst die Entkopplung auf zusammenhängende Blöcke macht die Zahl
|
||
lesbar. Dieselbe Klasse wie die Clusterung bei `pbreak_predictions` (07.08.).
|
||
|
||
## ✅ M15-KARTE „SETUP-BEREITSCHAFT" GEBAUT (2026-08-08, v=153) — Ersatz der Gesamtempfehlung
|
||
|
||
Nach dem Entfernen der Gesamtempfehlung und drei negativen M15-Messungen
|
||
(Squeeze-Einstieg, VWAP-Bänder, Exit-ATR) steht fest: **auf M15 trägt keine
|
||
Richtungsquelle.** Gebaut ist deshalb bewusst **keine Empfehlung**, sondern eine
|
||
**sequenzielle Gate-Kette** nach dem Schema des Users:
|
||
```
|
||
[① HTF-BIAS M30/H1] → [② KOSTEN Spread/ATR_M15] → [③ LEVEL & KEGEL] → Order AM LEVEL
|
||
```
|
||
⚠ **Der Unterschied zur alten Karte ist grundsätzlich**: dort wurden Stimmen zu
|
||
einem Bias **verrechnet** (gemessen 48–51 % Treffer über 1.071 Episoden), hier
|
||
muss jedes Gate **einzeln** passieren. Ein gefallenes Gate lässt sich nicht durch
|
||
ein anderes ausgleichen.
|
||
|
||
**Nur BELEGTE Bausteine:** ① HTF-Trend (einziger robuster Filter, Edge ×2) ·
|
||
② Kosten (M15 gemessen **0,104** gegen M5 0,193 — hier gewinnt M15 wirklich) ·
|
||
③ nächstes STEHENDES Level aus `_draw_levels` + P(break) · ④ Kegel
|
||
(out-of-sample kalibriert, 80 %-Band trifft 77 %) · ⑤ Veto (gegen das Signal
|
||
−6,60 €/Lot, beidhälftig).
|
||
**Status-Banner:** 🟢 SETUP BEREIT @ Level · 🟡 WARTEN AUF LEVEL · 🔴 KEIN TRADE.
|
||
⚠ Das Banner bestätigt ausdrücklich auch das **Nicht**-Handeln — die gemessene
|
||
Kern-Leckage sind diskretionäre Abweichungen, nicht verpasste Gelegenheiten.
|
||
⚠⚠ **Die Order-Buttons werden BEWUSST NICHT gesperrt** (gegen den Vorschlag
|
||
„ausgrauen"): hartes Blockieren wurde im Projekt verworfen, und
|
||
User-Übersteuerungen liefen gemessen **68 % WR**. Sichtbar machen, nicht
|
||
bevormunden.
|
||
|
||
**Umsetzung:** `engine._m15_setup()` setzt nur zusammen, was der Snapshot ohnehin
|
||
erzeugt; NEU gerechnet wird allein das M15-Kosten-Verhältnis und P(break) für das
|
||
nächste Level. ⚠ Dafür veröffentlicht `wave_rec` jetzt **`tf_atr`** (ADDITIV) —
|
||
der ATR je Zeitebene wurde im Turn-Fetch längst berechnet, war aber nirgends
|
||
abrufbar; ihn neu zu holen wäre ein zweiter MT5-Fetch für vorhandene Zahlen
|
||
(derselbe Fehler wie beim Konsens-Pfeil).
|
||
⚠ P(break)-Merkmale bleiben auf **M5** — das Modell ist darauf trainiert (Fix
|
||
2026-07-13); auf M15 umzustellen würde es falsch skalieren.
|
||
Mit **5 Szenarien getestet** (alles passt · Bias uneinig · Kosten teuer · Level
|
||
zu weit · Kaltstart ohne ATR_M15 → kein Absturz), live verifiziert über
|
||
`deploy.py --feld m15_setup`.
|
||
|
||
### ⚠⚠ GATE ④ ZEIGTE DAS ZIEL IN DER VERBOTENEN RICHTUNG — behoben 2026-08-10
|
||
|
||
**User-Fund:** „Kurs ist ca. 79 und Ziel ist 78,7 bei Empfehlung LONG — ein
|
||
Widerspruch?" **Ja, und zwar ein echter.** Live nachgestellt:
|
||
| | |
|
||
|---|---|
|
||
| Kurs (bid) | **78,918** |
|
||
| ② Bias | `verboten: SHORT` → erlaubt ist **LONG** |
|
||
| ④ Ziel | **78,768** — 0,15 $ **unter** dem Kurs, `von: 79,033` |
|
||
|
||
Gelesen heisst das „am Widerstand einsteigen, Support anlaufen" = ein **SHORT**.
|
||
⚠ **Die Arithmetik war korrekt — nur für die verbotene Richtung.** Ursache war
|
||
eine Zeile:
|
||
```python
|
||
fern = max(zonen, key=lambda x: x["gap_atr15"]) # rein geometrisch
|
||
```
|
||
Das Ziel war schlicht das **entferntere** der beiden Level, ohne jede
|
||
Richtungsprüfung.
|
||
|
||
⚠⚠ **Der strukturelle Grund ist der wichtigere Teil: zwei unvereinbare
|
||
Handelskonzepte in einer Kette.** Gate ④ unterstellte stillschweigend einen
|
||
**Fade-Trade** (Level anlaufen, Gegenlevel als Ziel = Mean Reversion), Gate ② ist
|
||
aber ein **Trendfilter**. Damit ist es **derselbe Fehler, wegen dem die
|
||
Gesamtempfehlung entfernt wurde**: aus einem **VERBOT** wird über die Hintertür
|
||
wieder eine **RICHTUNG** — hier nicht über verrechnete Stimmen, sondern über die
|
||
Geometrie. Und es ist der zweite Fall in dieser Karte (der erste war der
|
||
Richtungs-Bias, korrigiert 08.08. nach 13 LONG-Episoden an einem fallenden Tag).
|
||
**Ein Verbot lässt sich offenbar auf mehr Wegen in eine Richtung verwandeln, als
|
||
man beim Bauen sieht.**
|
||
|
||
✅ **Fix — die gemessene Population ist der Maßstab.** `backtest_m15_gates.py`
|
||
misst: Einstieg am **nächstgelegenen** Level, Richtung = die vom HTF **erlaubte**;
|
||
der Exit ist der kanonische Trailing-Exit — **ein Level-Ziel war NIE gemessen**
|
||
(④ ist ausdrücklich „reine Arithmetik"). Jetzt gilt:
|
||
| Lage (LONG erlaubt) | Anzeige |
|
||
|---|---|
|
||
| nächstes Level = **Support** → Abpraller | Ziel = Widerstand darüber ▲ |
|
||
| nächstes Level = **Widerstand** → Ausbruch | **kein Ziel** + Grund |
|
||
| **kein Veto** (M30 flach) | **kein Ziel** — ein einzelnes wäre wieder Richtung |
|
||
⚠ **„Kein Ziel" ist ein eigener Zustand, keine Lücke** — die Karte nennt den
|
||
Grund, statt eine Zahl zu erfinden. Beim Ausbruchs-Einstieg ist das nächste Ziel
|
||
schlicht **nicht berechenbar**: `_draw_levels` liefert bewusst nur die nächste
|
||
Linie je Seite (Entscheidung 14.07., sonst tauchen beim Kurslauf laufend Linien
|
||
auf/ab).
|
||
⚠ Die **Richtung steht jetzt IM Text** (`④ Ziel ▲ 79,033 … Einstieg 78,768`).
|
||
Ein Ziel ohne genannte Richtung lässt sich still wieder verdrehen — mit genannter
|
||
nicht. Neue Felder: `ziel.richtung`, `ziel_grund`.
|
||
|
||
✅ **Mutationsprobe bestanden** (ein grüner Test belegt sonst nichts): mit der
|
||
alten Zeile zurückgebaut fallen **4 von 6** Tests, und die Fehlermeldung
|
||
reproduziert den Fund wörtlich (`{'level': 78.768, … 'von': 79.033}`).
|
||
⚠ Die zwei übrigen bleiben **korrekterweise** grün — dort liegt das entferntere
|
||
Level tatsächlich in der erlaubten Richtung, alt und neu stimmen überein. Ein
|
||
Test, der auch die richtigen Fälle rot färbt, wäre zu eng gefasst.
|
||
`tests/test_m15_ziel.py` (6 Tests) prüft die Invariante **„ein ausgewiesenes Ziel
|
||
liegt IMMER in der erlaubten Richtung"** über 32 Konstellationen × 2 Richtungen.
|
||
✅ **Live beide Zweige belegt**, weil der Kurs während der Prüfung lief: bei
|
||
bid 78,918 (nächstes = Widerstand) → kein Ziel + „Ausbruch"; bei bid 78,871
|
||
(nächstes = Support) → Ziel 79,033 ▲, Einstieg 78,768.
|
||
|
||
⚠ **Nebenbei behoben: `deploy.py --feld` konnte nur die oberste Snapshot-Ebene**
|
||
(`a.feld in snap`). Für `m15_setup.ziel_grund` war der Neuheits-Beweis damit gar
|
||
nicht führbar — und Schritt 4 ist der EINZIGE belastbare Nachweis, dass neuer
|
||
Python-Code läuft. Jetzt mit **Punktpfaden**; ein Feld mit Wert `None` zählt als
|
||
vorhanden (geprüft wird der Schlüssel, nicht der Inhalt).
|
||
|
||
### ⚠⚠ CHART UND M15-KARTE NANNTEN FÜR DASSELBE LEVEL VERSCHIEDENE ZAHLEN (Fix 2026-08-13, v1.35 / v=169)
|
||
|
||
User: „die Beschriftung für S/R im Chart stimmt nicht mit der Analyse in der
|
||
M15-Karte überein."
|
||
✅ **Die LEVEL stimmten — nur die Zahl daneben nicht.** Live gegengeprüft: die
|
||
CSV, die der Indikator liest, enthielt `R;82.069;69`, und Gate ③ der Karte
|
||
meldete `nah: res 82.069, p_break 69`. Beide kommen aus `_draw_levels()`, sind
|
||
also per Konstruktion identisch. Auch die Distanzen sind exakt gerechnet
|
||
(nachgerechnet 0,477≈0,48 · 0,909≈0,91 · 0,455≈0,45).
|
||
⚠⚠ **DER DEFEKT: das Chart schrieb die NACKTE Zahl, die Karte klappte still auf
|
||
den Gegenwert um.**
|
||
| P(Bruch) | Chart (v1.34) | M15-Karte (`app.js` `lesart()`) |
|
||
|---|---|---|
|
||
| 33 % | `Widerstand 82.069 · **33%**` | `Widerstand 82.069 → **67 % Abprall**` |
|
||
| 46 % | `… · **46%**` | `… → **unentschieden**` |
|
||
| 72 % | `… · **72%**` | `… → **72 % Bruch**` |
|
||
Beide Zahlen waren **richtig** — die eine P(Bruch), die andere P(Abprall) — aber
|
||
die Chart-Zahl sagte nicht, WAS sie ist. **Gemessen betraf das 88,1 % der Zeit**
|
||
(1.797 Vorhersagen/14 Tage: 66,3 % lagen unter 45 %, 21,9 % im unentschiedenen
|
||
Band; nur 11,9 % über 55 %, wo beide dieselbe Zahl zeigen).
|
||
✅ **Behoben im Indikator (v1.35), wortgleich zu `app.js`:** `69% Bruch` /
|
||
`unentschieden` / `67% Abprall`. Über **1.678 echte** P(break)-Werte gegengeprüft:
|
||
Übereinstimmung **0 % → 100 %**.
|
||
⚠ Die Formulierung lebt jetzt an ZWEI Stellen (MQL5 + `app.js`) — wer eine
|
||
ändert, muss die andere mitziehen; beide tragen einen Hinweis darauf. Ein
|
||
gemeinsamer Text aus dem CSV wäre sauberer, hätte aber das Format gebrochen.
|
||
⚠ **Kompiliert ins RICHTIGE Terminal** (der dokumentierte Fallstrick): es gibt
|
||
real **zwei** Ordner, nur `D0E8209F…` bekommt die `sr_levels.csv`
|
||
(`0BA5CADA…` nicht). `MetaEditor64.exe /compile` → **0 errors, 0 warnings**,
|
||
`.ex5` frisch, `#property version "1.35"` im Terminal gegengeprüft.
|
||
✅ **Zwei Verdachtsmomente ausgeschlossen, statt sie zu vermuten:** (a) eine
|
||
stehengebliebene Alt-Linie — der Indikator ruft `ObjectsDeleteAll(0, PFX)` bei
|
||
jedem Redraw; (b) ein falsch einsortiertes Level — `_draw_levels` wählt seit dem
|
||
Fix vom 23.07. nach **Lage zum Kurs**, nicht nach Entstehungstyp. ⚠ Nebenbefund:
|
||
82.069 stammt aus `pl` (einem Pivot-**Tief**) und wird korrekt als Widerstand
|
||
geführt, weil der Kurs darunter steht — das sieht im Chart nur ungewohnt aus.
|
||
|
||
**ZWEITER, unabhängiger Befund derselben Klasse — behoben:** Gate ③ und ③b
|
||
nannten Level aus **verschiedenen Zeitebenen**, beide nur als „×ATR":
|
||
| | Level-Quelle | ATR | im Chart gezeichnet? |
|
||
|---|---|---|---|
|
||
| **③** | M30-Pivots (`_draw_levels`) | **ATR_M15** | **ja** |
|
||
| **③b** | M5-Pivots (`room_info`) | **ATR_M5** | **nein** |
|
||
Real standen damit „0,48×ATR" und „0,45×ATR" nebeneinander, die **nicht
|
||
vergleichbar** sind — und die Karte nannte einen „Support 81,78", den man im
|
||
Chart vergeblich sucht. ⚠ Verschärfend: zum Prüfzeitpunkt lagen **alle 10
|
||
M30-Pivots über dem Kurs**, es gab also gar keine Support-Linie im Chart.
|
||
✅ Die Quellen-Trennung **bleibt** — `entry_room_atr=0,6` ist über 80k Bars auf
|
||
M5 kalibriert, sie umzustellen wäre eine Strategie-Änderung. Geändert wurde nur
|
||
die Beschriftung: `③ Widerstand M30 82.069 (Chart · 0,48×ATR_M15)` gegen
|
||
`③b … Raum ▲ 0,91×ATR_M5 / ▼ 0,45×ATR_M5`. Die Tooltips nennen den Unterschied
|
||
ausdrücklich. **Reine Anzeige — an Gates, Gewichten und Order-Logik nichts.**
|
||
|
||
### ⚠⚠ M15-KARTE AUTOMATISCH HANDELN = FÄLLT DURCH, und die Lesart ist schlechter als ihr eigenes Gegenteil (`backtest_m15_auto.py`, 2026-08-13)
|
||
|
||
User: „messe ob wir den Autotrader bei M15-Empfehlung automatisch traden lassen
|
||
können." Track-B-Frage (Signal), also hohe Beweislast; Regel **vorab** fixiert.
|
||
⚠ **Neu gemessen wurde genau EIN Zusatz.** Die Karte ist als Bedingungs-Anzeige
|
||
gebaut und hat keine Richtung — die entsteht erst aus der **`lesart`** (aus
|
||
P(break)), und die war als Handelsregel nie geprüft. `backtest_m15_gates.py`
|
||
(08.08.) handelt die vom **HTF erlaubte** Richtung, nicht die Lesart.
|
||
|
||
**80k M5-Bars, 2 Halbjahre, echte `core.sr_levels`/`_p_break`-Gewichte, Status +
|
||
Lesart inkl. der Hysterese vom 12.08., kanonischer Exit, Echtkosten, Fill bei
|
||
BERÜHRUNG, sequentiell EIN Slot, 1× je Episode:**
|
||
| Variante | H1 ØR / PF | H2 ØR / PF |
|
||
|---|---|---|
|
||
| **A) Karte (Lesart)** | **−0,162 / 0,72** | **−0,094 / 0,84** |
|
||
| B) Lesart + Veto-konform | −0,104 / 0,81 | −0,051 / 0,91 |
|
||
| **C) KONTROLLE HTF-Richtung** | **−0,085 / 0,85** | **−0,030 / 0,95** |
|
||
| D) KONTROLLE Gegenrichtung | −0,070 / 0,87 | +0,007 / 1,01 |
|
||
⚠⚠ **Die schärfste Zeile ist der Vergleich A gegen C und D: die Lesart ist in
|
||
BEIDEN Hälften schlechter als die HTF-Richtung UND schlechter als ihr eigenes
|
||
Gegenteil.** Alle vier Varianten laufen auf **denselben** grünen, gerichteten
|
||
Momenten — variiert wird nur die Richtung. Die Lesart zieht damit nicht bloß
|
||
nichts bei, sie **kostet** (−0,077 bzw. −0,064 ØR gegenüber C).
|
||
⚠ Und A ist nicht „unbelegt", sondern **belegt negativ**: das 95-%-KI schließt
|
||
in beiden Hälften die Null aus — [−0,222 … −0,099] und [−0,152 … −0,038].
|
||
✅ **Nachbar-robust in die falsche Richtung:** gap 0,40 / 0,50 / 0,60 sind alle
|
||
beidhälftig negativ und werden **monoton schlechter**, je weiter die Schwelle.
|
||
Kein Rauschmuster, sondern ein konsistenter Mechanismus.
|
||
✅ **Unabhängige LIVE-Gegenprobe** (`m15_states`, Selbstmessung seit 08.08.,
|
||
3.678 ausgewertete Zustände): der **handelbare** Zustand (grün + gerichtet)
|
||
trifft **entkoppelt 46,8 %** auf 30 min [37,9 … 55,6] und **42,6 %** auf 60 min
|
||
[33,6 … 51,6] — beide unter dem Münzwurf, beide KI enthalten 50. ⚠ Roh sähe es
|
||
mit 51,5 % besser aus; die Entkopplung (1.793 → 124 unabhängige Fälle) ist hier
|
||
der ganze Unterschied. Backtest und Live zeigen also in **dieselbe** Richtung.
|
||
⚠⚠ **MECHANISMUS — es ist zum VIERTEN Mal derselbe Fehler dieser Karte:** aus
|
||
einer Aussage über die **Struktur** („hält dieses Level?", P(break), AUC 0,65)
|
||
wird über die Hintertür eine **Richtungsprognose** („Level hält → Kurs fällt").
|
||
Das ist eine Mean-Reversion-Annahme am Extrem — und antizyklische Einstiege am
|
||
Level sind im Projekt mehrfach als Münzwurf gemessen (P(break) taugt zum
|
||
**EXIT**, nicht zum **ENTRY**, 6× belegt). Dazu kommt, dass das Modell live
|
||
~7 Pp fehlkalibriert ist. Die drei Vorgänger: Code (08.08.), Geometrie (10.08.),
|
||
Lesart im Text (10.08.) — jetzt die Lesart als Handelsregel.
|
||
✅ **URTEIL: NICHT bauen.** Bedingung (1) und (4) verfehlt. ⚠ Und **kein
|
||
Umschwenken auf C oder D**: die sind nur *weniger schlecht*, ebenfalls negativ
|
||
(D ist in H1 −0,070 mit KI ohne Null). Es gibt hier nichts zu handeln, nur etwas
|
||
zu lassen. Die Karte bleibt, was sie ist — eine **Bedingungs**-Anzeige.
|
||
⚠ Grenzen, ehrlich: eine Schwung-/Level-Definition; die Sim modelliert **keine
|
||
Exit-Slippage** (die würde alle Varianten weiter senken, A am stärksten, weil es
|
||
am häufigsten handelt); und der Fill am Level ist für den Ausbruchs-Fall leicht
|
||
optimistisch (eine Stop-Order löst knapp jenseits aus).
|
||
⚠ **Nebenbefund für später:** die Karte steht in **6.035 bzw. 7.329 Episoden**
|
||
grün UND gerichtet — sie ist als Auslöser also alles andere als selektiv. Wer
|
||
sie schärfen will, setzt dort an, nicht an der Richtung.
|
||
|
||
### ⚠⚠ DER ENTRY IST REPARIERT — DER DEFEKT IST IN DEN EXIT GEWANDERT (Zwischenstand 2026-08-13)
|
||
|
||
Erhebung auf die Frage „wie erreiche ich das Ziel Autotrading?". **Nichts
|
||
geändert**, aber die Diagnose verschiebt sich, und das ist der Punkt.
|
||
✅ **Der Pending-Umbau vom 05.08. hat gewirkt** (`analyze_squeeze_entry_gap.py`):
|
||
von 8 Bot-Trades seit dem Umbau füllten **7 per ruhender Order** (keine
|
||
Market-Log-Zeile); der Market-Fallback ist praktisch weg — **n=1 bei +0,148×ATR**,
|
||
also innerhalb der Nachjagd-Bremse (0,20). Vorher lag der Live-Median bei
|
||
**+0,275×ATR** über dem Level. **Deployment-Drift Fall 7 ist damit geschlossen.**
|
||
⚠⚠ **Und trotzdem verliert der Squeeze — die Ursache ist also eine andere
|
||
geworden.** B5-Stand (ab 01.08., n=11/20): **Trefferquote 73 %**, aber
|
||
**Ø-Gewinn 13,62 € gegen Ø-Verlust 94,15 € = Verhältnis 0,14** (Latte 1,0),
|
||
**PF 0,39** (Latte 1,0), Summe **−173,46 €**.
|
||
⚠ Die Signatur ist im Trade-Protokoll direkt ablesbar: **drei der sieben
|
||
Pending-Trades wurden per `sr_close` bei +0,26 / +2,88 / +0,28 € geschlossen**,
|
||
während die Verlierer bis zum vollen Stop liefen (−43 / −64 / −175 €).
|
||
**Der Squeeze lebt gemessen von LÄUFERN** (43 % Treffer, Ertrag im Tail) — genau
|
||
die werden gekappt.
|
||
⚠⚠ **Die alte Entlastung des S/R-Closes ist NICHT mehr bindend.**
|
||
`backtest_squeeze_srclose.py` (24.07.) maß auf Squeeze-Entries einen **Wash**
|
||
(+0,190→+0,224 / +0,394→+0,377) — aber mit dem **alten** P(break)-Modell und
|
||
Schwelle **0,55/0,60**. Heute läuft das am 31.07. **nachtrainierte** Modell mit
|
||
Schwelle **0,35**, und das schließt **94–97 %** aller Level-Berührungen. Gemessen
|
||
unter X, betrieben unter Y — dasselbe Muster wie die acht dokumentierten Fälle.
|
||
➜ **NÄCHSTE MESSUNG (billig, hoher Hebel, noch nicht gelaufen):** Squeeze-Trades
|
||
**mit gegen ohne** S/R-Auto-Close unter dem HEUTIGEN Modell und 0,35.
|
||
⚠ **Kontext, der zur Ehrlichkeit gehört** (seit 01.08., je Lot normiert, weil
|
||
absolute Euro bei wechselnder Größe ungültig sind):
|
||
| | n | Σ P&L | **je Lot** |
|
||
|---|---|---|---|
|
||
| **AUTONOM** (`SQUEEZE_*`) | 11 | −173,46 € | **−5,85** |
|
||
| **MENSCH** (alles übrige) | 146 | +994,59 € | **+3,20** |
|
||
⚠ Die `setup`-Spalte taugt **NICHT** als Bot/Mensch-Trennung — `engine._open`
|
||
schreibt die Wellenlage auch bei manuellen Trades mit (`WAVE`, n=124). Nur
|
||
`SQUEEZE_*` ist autonom eröffnet. Ein erster Auswertungsversuch hat genau das
|
||
verwechselt und dem Bot 152 Trades mit +730 € zugeschrieben.
|
||
⚠ **Zustand beim Erheben:** `auto_squeeze=False` (User 11.08. 14:10) — damit
|
||
läuft die B5-Regel **nie ab**, der schlechteste Zustand. `auto_sr_close=True`,
|
||
Circuit-Breaker **aus** (`limit_pct=0`), Sizing 95 % Margin, Balance 1.279 €.
|
||
Bei Ø-Verlust 94 € sind das **7,4 % des Kontos je Verlusttrade**.
|
||
|
||
### ✅⚠ „DER S/R-CLOSE KAPPT DIE SQUEEZE-LÄUFER" = WIDERLEGT (`backtest_squeeze_srclose2.py`, 2026-08-13)
|
||
|
||
Meine eigene Hypothese vom selben Tag (drei von sieben Pending-Trades per
|
||
`sr_close` bei +0,26 / +2,88 / +0,28 € beendet) — **gemessen falsch, und zwar
|
||
doppelt.**
|
||
|
||
**(1) BACKTEST** (80k M5, 2 Halbjahre, Einstiege über den GETEILTEN
|
||
`squeeze_scan.scan(on_entry=…)`, ruhende Order am Level mit Fill bei Berührung,
|
||
kanonischer Exit, Echtkosten, EIN Slot; identische Entry-Liste über alle
|
||
Varianten, variiert wird NUR der Exit):
|
||
| Variante | H1 ØR / PF | H2 ØR / PF | Δ gegen ohne |
|
||
|---|---|---|---|
|
||
| **A) ohne S/R-Close** | −0,151 / 0,72 | −0,079 / 0,86 | — |
|
||
| **B) mit, p<0,35 (LIVE)** | **−0,119 / 0,76** | **−0,049 / 0,91** | **+0,032 / +0,030** |
|
||
| B) p<0,45 | −0,088 / 0,82 | −0,046 / 0,92 | +0,063 / +0,033 |
|
||
| B) p<0,55 | −0,086 / 0,82 | −0,038 / 0,93 | +0,065 / +0,041 |
|
||
**Der S/R-Close ist in BEIDEN Hälften BESSER als ohne** — die vorab fixierte
|
||
Regel sagt damit: **anlassen.** ⚠ Und die von mir vorgeschlagene „Abhilfe"
|
||
(Close erst ab Mindestgewinn) macht H1 **schlechter** (Δ −0,006 / −0,011 bei
|
||
0,5 bzw. 1,0×ATR) — der Eingriff hätte geschadet.
|
||
|
||
**(2) LIVE, 11 Squeeze-Trades ab 01.08. — die Zerlegung ist eindeutig:**
|
||
| Schließgrund | n | Σ | Ø |
|
||
|---|---|---|---|
|
||
| **`sl`** (Stop/Trailing) | 5 | **−198,17 €** | −39,63 |
|
||
| `sr_close` | 4 | **+15,98 €** | +4,00 |
|
||
| `manual` | 2 | +8,73 € | +4,37 |
|
||
⚠⚠ **Der verdächtigte Exit hat +15,98 € BEIGETRAGEN, nicht gekostet.** Und der
|
||
größte Gewinner des Zeitraums (+77,34 €) kam ebenfalls über `sl` — also über das
|
||
**Trailing**, nicht trotz ihm.
|
||
⚠ Die Größe steckt allein auf der Verlustseite: **−175,21 € bei 2,91 Lots** =
|
||
0,695 $ Bewegung — das ist der **planmäßige 2×ATR-Stop** bei dem ATR jenes
|
||
Moments, kein Ausreißer und kein Exit-Defekt. Ø-Verlust 94,15 € auf 1.279 €
|
||
Konto = **7,4 % je Verlusttrade**.
|
||
✅ **Damit zeigt der Hebel auf SIZING, nicht auf den Exit** — genau dorthin, wo
|
||
CLAUDE.md ihn ohnehin verortet („Der Hebel ist Sizing, nicht der Exit").
|
||
|
||
⚠⚠ **GRENZE, die nicht überspielt werden darf: die ABSOLUTEN Werte reproduzieren
|
||
den dokumentierten Kontrollwert NICHT.** `backtest_squeeze_touchfill.py` liefert
|
||
für denselben nominellen Aufbau **+0,124/+0,292** bei 1.092/1.807 Trades, hier
|
||
sind es −0,151/−0,079 bei 273/422. Die Referenz hat also **mehr Trades als ich
|
||
Einstiege habe** — ihre Auswahl ist lockerer (Fenster/Cooldown/Slot-Behandlung).
|
||
**Deshalb ist der Befund „der Squeeze ist negativ" hier NICHT belegt**; belastbar
|
||
ist allein der **relative** Vergleich, weil alle Varianten auf derselben
|
||
Entry-Liste laufen und sich nur im Exit unterscheiden. Wer die Absolutwerte
|
||
braucht, muss zuerst die Kontrolle reproduzieren (die Projekt-Regel, die
|
||
`backtest_verworfene_v2.py` vorbildlich befolgt).
|
||
|
||
⚠⚠ **EIGENER FEHLER auf dem Weg, dokumentiert weil es eine Fehlerklasse ist:**
|
||
der erste Lauf nahm den Modul-Default `squeeze_scan.ATRMIN_STD = 0,12`, während
|
||
live und die Referenz **0,06** nutzen (der Floor wurde gemessen gesenkt). Folge:
|
||
**H1 127 gegen H2 483 Einstiege** — der hohe Floor filtert die Niedrig-Vola-
|
||
Hälfte weg und erzeugt eine ganz andere Population. Aufgefallen nur durch die
|
||
Schieflage der Entry-Zahlen. Dieselbe Klasse wie das fehlende
|
||
`set_entry_room(0.6)` am 05.08.: **ein aufgerufenes Gate ist nicht dasselbe wie
|
||
ein aktives Gate; beim Wiederverwenden eines Kerns gehört die Live-KONFIGURATION
|
||
mitkopiert.** ⚠ Die Schlussfolgerung war in beiden Läufen dieselbe.
|
||
⚠ Zweiter eigener Fehler: der erste Entwurf hängte den S/R-Close an den
|
||
`stop_when`-Haken, der zum **Bar-Close** prüft — bei einem „am Level"-Fenster von
|
||
0,15×ATR ≈ 0,018 $ feuerte er fast nie (Δ ≈ 0,000), während er live 94–97 % aller
|
||
Berührungen schliesst. Live prüft den **laufenden** Kurs im Sekundentakt; das
|
||
Analogon ist die **Bar-Spanne**. Danach feuert er in 21–23 % der Trades (live
|
||
4 von 11 = 36 %, bei n=11 kein Widerspruch).
|
||
|
||
### ⚠⚠ SIZING AUF DER ECHTEN HISTORIE GERECHNET (`analyze_sizing_live.py`, 2026-08-13)
|
||
|
||
Folge der S/R-Widerlegung: der Hebel zeigt auf die GRÖSSE. Pfad-Simulation über
|
||
**1.278 geschlossene Trades (08.05.–13.08.)**, `pnl/lots` als größenunabhängiges
|
||
Ergebnis, Lots aus der jeweils simulierten Equity abgeleitet.
|
||
⚠ Zwei dokumentierte Zahlen stimmen nicht mehr und wurden **aus den Daten neu
|
||
geschätzt**: EUR je $ und Lot = **86,90** (notiert 87,6).
|
||
⚠⚠ **KORREKTUR MEINER EIGENEN KORREKTUR (2026-08-17, vom Konstanten-Wächter
|
||
gefunden):** hier stand „Margin je Lot **~405 €** (notiert 670)". **Die 405 waren
|
||
falsch.** Ich hatte sie aus `Balance × 95 % / Lots` abgeleitet — das unterstellt,
|
||
jede Position sei auf volle 95 % gesized. Der **exakte** Wert kommt vom Broker
|
||
(`position.margin / position.lots`) und beträgt **~713 €/Lot**; die ursprünglich
|
||
notierten 670 lagen also deutlich näher als meine „Korrektur".
|
||
**Lehre: eine abgeleitete Zahl ist keine gemessene.** Wo der Broker die Größe
|
||
selbst ausrechnet, gehört sie von dort geholt — `tools/check_konstanten.py`
|
||
macht das jetzt.
|
||
|
||
**(1) DER IST-PFAD IST DIE EIGENTLICHE BOTSCHAFT:**
|
||
| | |
|
||
|---|---|
|
||
| Start (rekonstruiert) | 1.115,70 € |
|
||
| **Hoch** | **2.724,16 €** (05.08.) |
|
||
| **Tief** | **261,36 €** (30.07.) |
|
||
| **Max-Drawdown** | **82,8 %** |
|
||
| schlechtester Einzeltrade | **−381,16 €** |
|
||
| Ende | 1.279,04 € |
|
||
⚠⚠ Das Konto hat **zweimal** den Großteil seines Werts verloren — −82,8 % bis
|
||
zum 30.07., danach vom Hoch am 05.08. nochmals **−53 %**. **42 von 71 Tagen sind
|
||
negativ**; getragen wird alles von wenigen Tagen (04.08. **+1.297 €**). Für
|
||
**unbeaufsichtigten** Betrieb ist das die entscheidende Zahl, nicht der Endstand.
|
||
|
||
**(2) VARIANTEN** (Ende / MaxDD / schlechtester Trade):
|
||
| Regel | Ende | MaxDD | schlecht. Trade |
|
||
|---|---|---|---|
|
||
| **IST** | 1.279 € | **82,8 %** | **−381 €** |
|
||
| Lot-Deckel 1,5 | 1.362 € | 82,8 % | −345 € |
|
||
| Risiko 1,5 %/Trade | 680 € | **54,4 %** | **−77 €** |
|
||
| Risiko 0,5 %/Trade | 960 € | **22,2 %** | **−27 €** |
|
||
| **IST + Breaker 8 %/Tag** | **2.018 €** | **59,2 %** | −381 € |
|
||
| IST + Breaker 5 %/Tag | 1.976 € | 65,5 % | −381 € |
|
||
✅ **Lot-Deckel bringen fast nichts** — die realen Lots lagen meist darunter.
|
||
✅ **Risiko-Sizing drittelt den schlechtesten Trade und halbiert den Drawdown**,
|
||
kostet aber Endstand. ⚠ **Confounder: die Risiko-Varianten laufen auf 1.118 der
|
||
1.278 Trades** (nur die mit `sl_at_entry`) — nicht direkt vergleichbar.
|
||
⚠⚠ **Der Breaker verbessert BEIDES** (Ende +739 €, DD −23,6 Pp) — aber:
|
||
|
||
**(3) ⚠⚠ EIN TAG TRÄGT 70 % DES BREAKER-VORTEILS.** Vermiedene Beträge je
|
||
Auslösetag: **07.08. +556,82** · 30.07. +200,48 · 24.07. +106,61 · 28.07. +81,24
|
||
· 23.07. +63,23 · Rest klein — und an **drei Tagen kostet er**: 11.08. −106,85 ·
|
||
29.07. −96,00 · 27.07. −50,77. Summe +800,19 € über 11 Auslösetage.
|
||
⚠ Ohne den 07.08. bleiben **+243 €** über 10 Tage. **Das ist kein Makel der
|
||
Messung, sondern das Wesen einer Versicherung** — ihr Wert sitzt im Tail. Aber
|
||
die +739 € sind eine **Realisierung**, keine Erwartung.
|
||
⚠⚠ **UND EINE GRENZE, die für HEUTE entscheidend ist:** die Sim sperrt nach dem
|
||
Auslösen **alle** Trades des Tages. Live sperrt `_check_circuit_breaker` nur die
|
||
**autonomen** (`_check_auto_squeeze`), manuelle Orders bleiben frei. Bei einem zu
|
||
94 % manuell gehandelten Konto misst die Sim damit eher eine **Disziplin-Regel**
|
||
als das heutige Code-Verhalten. **Für das Ziel Autotrading ist sie dagegen das
|
||
richtige Modell** — dort greift die Sperre wirklich.
|
||
⚠ Weitere Grenzen: EIN Pfad ist keine Verteilung (für die Ruin-Frage bleibt
|
||
`backtest_sizing.py` das Werkzeug: Margin-Sizing 36 % P(DD>80 %) — was der
|
||
gemessene IST-Pfad mit 82,8 % **bestätigt**); die Sim unterstellt dieselben
|
||
Trades bei anderer Größe, was bei **manuellen** Trades eine starke Annahme ist;
|
||
und der Breaker wird auf **realisierte** Tagesverluste geprüft, während er live
|
||
auch die offene Position kappt → die Sim **unterschätzt** ihn.
|
||
|
||
### ⚠⚠ ENGER STOP FÜR DIE ERSTEN N BARS = KEINE ÄNDERUNG (`backtest_sl_staged.py`, 2026-08-13)
|
||
|
||
User-Idee: „SL beim Eröffnen sehr eng, damit falsch laufende Trades früh
|
||
geschlossen werden; läuft er richtig, den SL etappenweise nachziehen."
|
||
⚠ Der **zweite** Teil ist längst gebaut (Phasen-Ratsche in `core/trailing.py`);
|
||
neu und ungemessen war allein der **zeitlich befristete** enge Stop. Nicht zu
|
||
verwechseln mit einem durchgehend engen Stop — der ist 2× gemessen
|
||
(`backtest_sl_width.py`, `backtest_sl_basis.py`).
|
||
✅ **Ohne Exit-Nachbau gerechnet:** Lauf A (`sl_atr=eng`) und Lauf B
|
||
(`sl_atr=weit`) laufen auf DEMSELBEN Pfad; innerhalb der ersten N Bars ist der
|
||
einzige Unterschied der SL-Abstand, jeder Nicht-SL-Ausstieg fällt identisch aus.
|
||
Also exakt `R = R_A wenn A vor/bei Bar N aussteigt, sonst R_B`. Keine Näherung,
|
||
keine fünfte Exit-Kopie.
|
||
|
||
**80k M5, Squeeze-Entries, ruhende Order am Level, kanonischer Exit, Echtkosten,
|
||
EIN Slot. Basis = durchgehend 3,4×ATR_M5 (= der Live-Stop 2,0×ATR_M15, aus 121
|
||
echten Trades als Median gegengerechnet):**
|
||
| Variante | H1 ØR / WR / worst | H2 ØR / WR / worst |
|
||
|---|---|---|
|
||
| **Basis (heute)** | −0,248 / 37,0 % / **−3,73** | −0,107 / 44,4 % / **−3,74** |
|
||
| N=3, eng 0,5 | −0,263 / 23,3 % / −3,66 | −0,043 / 30,4 % / −0,96 |
|
||
| **N=6, eng 0,5** | **−0,242** / 22,9 % / **−0,89** | **−0,045** / 30,1 % / **−0,96** |
|
||
| N=12, eng 0,5 | −0,240 / 22,9 % / **−0,83** | −0,044 / 30,1 % / −0,96 |
|
||
| N=6, eng 0,8 | −0,258 / 27,2 % / −3,15 | −0,066 / 35,0 % / −1,26 |
|
||
|
||
⚠⚠ **DER ENTSCHEIDENDE BEFUND IST STRUKTURELL: die Staffelung hat gar keinen
|
||
Raum.** Die Spalte „früh" zeigt, wie viele Trades schon in Phase 1 aussteigen —
|
||
bei **N=12: 269 von 279 (96 %)**. Die mittlere Haltedauer liegt bei 12–18 min
|
||
(2–4 M5-Bars); „die ersten 30–60 Minuten" sind also **der ganze Trade**. Wo die
|
||
Staffelung überhaupt etwas anderes tut als ein dauerhaft enger Stop (N=3), ist
|
||
sie in H1 bei **allen vier** eng-Werten schlechter. Wo sie „gewinnt" (N=6/12),
|
||
ist sie **kein gestaffelter Stop mehr, sondern ein permanent enger** — und der
|
||
war die Ausgangsfrage nicht.
|
||
❌ **Regel verfehlt.** Zwei Kombinationen schlagen die Basis nominell in beiden
|
||
Hälften (N=6/12 mit eng 0,5: Δ H1 **+0,007/+0,008**), aber (a) das liegt bei
|
||
n≈279 **weit** unter einem Standardfehler (~0,055), (b) die **Nachbarn kippen**
|
||
(N=3 gleiche Enge → −0,014; eng 0,8 → −0,009 in H1), (c) es ist der degenerierte
|
||
Fall. Der ganze Ertrag steckt in H2 (+0,063), H1 ist ein Wash.
|
||
⚠ Und: **alle** Werte sind negativ — verglichen werden Verlustgrade, kein Edge.
|
||
|
||
✅ **WAS ECHT IST — der Tail.** Worst-Case **−3,73 → −0,83/−0,96**, also **Faktor
|
||
4**. Genau das ist für unbeaufsichtigten Betrieb die relevante Größe. Bezahlt
|
||
wird es mit der Trefferquote: **37,0 → 22,9 %** bzw. 44,4 → 30,1 %.
|
||
⚠⚠ **Aber die Bar-Simulation ÜBERSCHÄTZT enge Stops systematisch** (Kipp-Punkt
|
||
~0,3×ATR, `analyze_trail_resolution.py`) — die Verzerrung geht **zugunsten**
|
||
dieser Idee. Ein ✅ wäre also abzuschlagen; das ❌ ist umso belastbarer.
|
||
⚠ Nebenbefund: `durchgehend 2,0×ATR` schlägt die Basis in beiden Hälften
|
||
(Δ +0,019/+0,029) und in H1 sogar die gestaffelten Varianten. Das widerspricht
|
||
`backtest_sl_width.py` NICHT — dort war die Population die gegatete Wellen-Seite
|
||
mit Bar-Close-Einstieg, hier sind es Level-Einstiege. Falls jemand die SL-Breite
|
||
je anfasst, gehört diese Zelle nachgemessen, nicht die gestaffelte.
|
||
⚠ **Gleiche Grenze wie beim S/R-Lauf:** die ABSOLUTEN Werte reproduzieren den
|
||
dokumentierten Squeeze-Kontrollwert (+0,124/+0,292) nicht. Belastbar ist allein
|
||
der **relative** Vergleich — alle Varianten laufen auf derselben Entry-Liste.
|
||
|
||
### ✅ TELEGRAM BEI GRÜNER M15-KARTE (2026-08-14, `[telegram] m15_alert`)
|
||
|
||
User: „wenn es eine Empfehlung gibt (grün), schicke eine Telegram-Nachricht."
|
||
`engine._check_m15_alert`, aufgerufen direkt hinter `_log_m15_state` — **dieselbe
|
||
Quelle** wie Anzeige und Selbstmessung, damit die drei nicht auseinanderlaufen
|
||
können (die Divergenz-Falle, die am 12.08. schon einmal zugeschlagen hat).
|
||
|
||
⚠⚠ **DIE ENTPRELLUNG IST DER GANZE BAU, nicht ein Detail.** Roh springt die
|
||
Karte **85× am Tag** auf grün (`m15_states`, 3 Tage nach der Hysterese vom
|
||
12.08.) — das wäre Spam und damit nach zwei Tagen stummgeschaltet. Alle Stufen
|
||
sind an den echten Daten kalibriert, nicht geraten:
|
||
| Stufe | Meldungen/Tag |
|
||
|---|---|
|
||
| nur Flanke | **85,0** |
|
||
| + Level & Richtung als Schlüssel | 23,7 |
|
||
| + 30 min Mindestabstand | 13,3 |
|
||
| **+ Fenster 8–20 Uhr (Default)** | **7,7** |
|
||
| *(Vergleich: Close-Meldung ab 50 €)* | *2,4–6,1* |
|
||
⚠ Nach 4 h (`_ALERT_NEU_S`) darf dasselbe Level erneut melden — sonst
|
||
verschweigt der Alert ein Setup, das den ganzen Tag wiederkommt.
|
||
Schalter: `m15_alert` (true) · `m15_alert_cooldown_min` (30) ·
|
||
`m15_alert_hours` (8-20). Backup `oil_widget_config.ini.bak-2026-08-14-m15alert`.
|
||
|
||
⚠⚠ **DER TEXT SAGT AUSDRÜCKLICH, DASS ES KEINE PROGNOSE IST.** Die Karte
|
||
automatisch zu handeln fällt gemessen durch (`backtest_m15_auto.py`, 13.08.:
|
||
ØR −0,162/−0,094, KI ohne Null, schlechter als die HTF-Richtung UND als ihr
|
||
eigenes Gegenteil). Die Meldung ist ein **Hinweis zum Hinsehen** für den
|
||
Menschen, dessen Auswahl-Vorteil unabhängig belegt ist (+3,20 €/Lot über 146
|
||
Trades) — und sie schreibt das in die Nachricht. Die P(break)-Formulierung ist
|
||
**wortgleich** zu Karte und Chart (v1.35-Fix).
|
||
✅ **13 Tests** (`tests/test_m15_alert.py`), Schwerpunkt auf dem, was NICHT
|
||
gesendet werden darf. **Mutationsprobe in vier Zweige** — alle gefangen.
|
||
⚠⚠ **Die Probe hat ein echtes TESTLOCH aufgedeckt:** das Entfernen der
|
||
Flankenerkennung liess zunächst **alle** Tests grün, weil Cooldown und
|
||
4-h-Schlüssel dasselbe mit abfangen. Zwei Schutzschichten, die sich gegenseitig
|
||
maskieren — dasselbe Muster wie beim Fill-Dedup. Geschlossen durch
|
||
`test_flankenerkennung_ISOLIERT` (Cooldown aus, Level wandert je Tick).
|
||
✅ Live verifiziert: seit dem Serverstart **genau 1 Alert**, obwohl die Karte
|
||
seitdem mehrfach grün↔gelb gewechselt hat.
|
||
|
||
**⚠⚠ NEBENBEFUND — TESTS SCHRIEBEN INS PRODUKTIONS-LOG (behoben).** Nach den
|
||
Testläufen standen **über 150** `📨 M15-Alert`-Zeilen in `oil_widget.log`, dazu
|
||
`Close-Push #4711` mit dem synthetischen Ticket aus `test_close_buchung.py`. Ich
|
||
habe daraufhin zuerst geglaubt, die Entprellung sei live defekt, und musste die
|
||
Herkunft an der Level-Folge (82.00, 82.01, …) nachweisen. In einem Projekt,
|
||
dessen Forensik am Log hängt (18-h-Copilot-Ausfall, stille Fehlbuchung), sind
|
||
erfundene Zeilen ein echtes Risiko. `tests/conftest.py` entfernt den
|
||
`FileHandler` des `oil`-Loggers jetzt für die Sitzung — **vierte Grundregel der
|
||
Suite.** Verifiziert: Log-Größe vor und nach einem Komplettlauf **identisch**.
|
||
|
||
### ⚠⚠ „WARUM WURDEN MEINE TRADES AUTOMATISCH GESCHLOSSEN?" — es war das TRAILING (2026-08-14)
|
||
|
||
User-Frage. Am Log und an der DB beantwortet, nicht vermutet. 11 Closes mit
|
||
Grund `sl` seit dem 13.08., **richtungsrichtig** aufgeschlüsselt (bei SELL zieht
|
||
das Trailing den Stop nach UNTEN — ein Exit darüber ist Slippage, kein Trailing):
|
||
| Ursache | n | Summe |
|
||
|---|---|---|
|
||
| **Trailing-Stop (nachgezogen)** | **9** | **−75,90 €** |
|
||
| echter Initial-SL | 1 | −113,94 € |
|
||
| ⚠ **manuelle SL-Änderung** | 1 | −215,60 € |
|
||
⚠ **`closed_by='sl'` heisst nur „Broker-Stop erreicht", nicht „Initial-Stop".**
|
||
|
||
**DER MECHANISMUS IST ARITHMETIK, KEIN DEFEKT.** Der Trail sitzt `mult`×ATR
|
||
(=1,0) hinter dem Bestkurs und erreicht den Einstieg damit erst bei **1,0×ATR
|
||
Gewinn** — aktiviert wird er aber schon bei **0,3×ATR** (`trail_start`). Im Band
|
||
dazwischen liegt der Stop zwangsläufig auf der VERLUSTSEITE. Real (50023965):
|
||
Einstieg 82,283 · Bestkurs 82,141 (0,47×ATR im Plus) · Stop 82,141+0,305 =
|
||
**82,456** = 0,173 ÜBER dem Einstieg.
|
||
⚠ Der grösste Verlust war **nicht** automatisch: Log 13.08. 16:30:59 „Manuelle
|
||
SL/TP-Änderung erkannt (SL 81.468≠81.151) → Trailing abgeschaltet". Ab da
|
||
verfolgt die Engine SL-Änderungen nicht mehr — ob der Exit bei 81,758 eine
|
||
weitere Handeingabe war oder Slippage, ist aus dem Log **nicht** entscheidbar.
|
||
|
||
**GEMESSEN: späterer `trail_start`?** (`backtest_trail_start.py`, 80k M5,
|
||
Squeeze-Entries, nur `trail_start` variiert)
|
||
| trail_start | H1 „war im Plus, schloss im Minus" | H2 |
|
||
|---|---|---|
|
||
| **0,3 (live)** | **47,6 %** | **38,2 %** |
|
||
| 0,8 | 27,7 % | 19,4 % |
|
||
| 1,0 | 12,0 % | 6,9 % |
|
||
| **1,3** | **0,0 %** | **0,5 %** |
|
||
✅ **Das beobachtete Verhalten ist damit bestätigt und quantifiziert** — es ist
|
||
eine Auszählung, keine Schätzung, und sie fällt monoton.
|
||
|
||
⚠⚠ **ABER DER ERTRAG TRÄGT NICHT — und der Weg dahin ist die eigentliche
|
||
Lehre.** Sequentiell sahen **vier** Werte in beiden Hälften besser aus (0,5 · 0,8
|
||
· 1,3 · 1,5, Δ bis +0,101). **Der GEPAARTE Test dreht das:**
|
||
| gegen 0,3 | H1 Δ je Trade | H2 Δ je Trade |
|
||
|---|---|---|
|
||
| 0,8 | +0,048 [−0,047 … +0,150] | **−0,006** [−0,083 … +0,074] |
|
||
| 1,3 | +0,077 [−0,062 … +0,219] | **−0,023** [−0,127 … +0,088] |
|
||
| 1,5 | +0,143 [−0,014 … +0,299] | **−0,023** [−0,137 … +0,098] |
|
||
**Alle 12 gepaarten Intervalle enthalten die Null**, und in H2 kippt das
|
||
Vorzeichen gegenüber dem sequentiellen Lauf.
|
||
⚠ **Grund: ein späterer Trail-Start hält Trades länger, also passen ANDERE
|
||
Trades in den EINEN Slot.** Der scheinbare Gewinn war ein **Selektionseffekt**,
|
||
kein Exit-Effekt — exakt die Falle aus `backtest_exit_combo.py` (31.07.):
|
||
verschiedene Trade-Folgen sind nicht vergleichbar. Der gepaarte Vergleich
|
||
(gleiche Entries, nur der Exit variiert) ist hier der einzig gültige Test, und
|
||
er ist zugleich **trennschärfer** als die Einzel-Intervalle.
|
||
✅ **KEINE ÄNDERUNG.** `trail_start` bleibt 0,3.
|
||
⚠ Wer die kleinen Verluste trotzdem loswerden will, tauscht sie gegen **weniger
|
||
Trefferquote im Ertrag** ein — es ist eine Komfort-, keine Edge-Entscheidung.
|
||
Zahlen dafür liegen jetzt vor (1,3 → 0 % solcher Fälle, WR 36 → 56 %).
|
||
|
||
### ⚠⚠ LÄUFT HYPERLIQUID DEM BROKER VORAUS? NEIN — ES IST UMGEKEHRT (`analyze_leadlag_hl.py`, 2026-08-17)
|
||
|
||
User-Frage. Im Projekt stand dazu ein Befund aus einer kleinen Stichprobe
|
||
(„Pepperstone führt, Peak k=−1 ≈ 6 s, 51 % Treffer, n=545"). Neu gerechnet auf
|
||
**245.073 Ticks / 20,4 Tagen** — und er reproduziert, deutlich schärfer.
|
||
|
||
**KREUZKORRELATION der Renditen** (k>0 = HL führt · k<0 = Pepperstone führt):
|
||
| Lag | GESAMT | H1 | H2 |
|
||
|---|---|---|---|
|
||
| k=−1 (~6 s, **Pepperstone voraus**) | **+0,455** | +0,460 | +0,447 |
|
||
| k=0 (gleichzeitig) | +0,505 | +0,507 | +0,503 |
|
||
| k=+1 (~6 s, **HL voraus**) | **+0,020** | +0,029 | +0,005 |
|
||
⚠⚠ **Die Asymmetrie IST die Antwort: +0,455 gegen +0,020, Faktor 23.** Die
|
||
Pepperstone-Bewegung von jetzt sagt die HL-Bewegung 6 s später vorher; umgekehrt
|
||
sagt HL praktisch **nichts**. Beidhälftig identisch.
|
||
✅ Kontrolle bestanden: k=0 liegt bei +0,505 — beide handeln erkennbar WTI, die
|
||
Pipeline funktioniert.
|
||
|
||
**RICHTUNGSTEST, entkoppelt** (nicht überlappende Fenster — die Cluster-Falle,
|
||
die im Projekt schon elf Hypothesen ausgelöst hat): über **12 Zellen**
|
||
(Vorlauf 6–30 s × Horizont 6–30 s × Schwelle 0,5–2,0 bp) liegt die Trefferquote
|
||
zwischen **47,6 und 49,8 %** — durchweg **am oder unter dem Münzwurf**. Fünf
|
||
Zellen haben ein Intervall **komplett unter 50 %**, d. h. wenn überhaupt, läuft
|
||
HL leicht GEGENläufig.
|
||
⚠ **Und die ökonomische Hürde entscheidet ohnehin:** die Ø-Bewegung in
|
||
HL-Richtung beträgt **+0,00 bis +0,14 bp** (bei 30 s sogar −0,09). Der
|
||
Pepperstone-Spread ist **~2,7 bp** — das ist **20- bis 270-mal** so groß.
|
||
|
||
✅ **URTEIL: HL taugt NICHT als Vorlaufindikator.** Das deckt sich mit allem
|
||
Übrigen zu dieser Quelle: der Orderbuch-Druck ist mit 0,2–1,0 bp gegen ~3 bp
|
||
Kostenhürde gemessen, das Modul „Orderbuch" wurde am 06.08. wegen beidhälftig
|
||
negativer Prädiktivität entmachtet, und Funding/Premium tragen ebenfalls nicht.
|
||
⚠ **Was HL weiterhin kann und bleibt:** der 24/7-Kurs am **Wochenende**, wenn
|
||
Pepperstone geschlossen ist (Header-Anzeige + G/V-Schätzung) — das ist eine
|
||
Verfügbarkeits-, keine Vorhersage-Eigenschaft.
|
||
|
||
⚠ **Drei methodische Punkte, ohne die das Ergebnis anders ausgefallen wäre:**
|
||
(1) **32 % der Zeit steht Pepperstone still** (Wochenende). Ein stehender Kurs
|
||
erzeugt zwangsläufig den Eindruck, HL laufe voraus — nur die 65 % verwertbaren
|
||
Ticks bei lebendem Broker gehen ein. (2) Gerechnet wurde auf **rohen** Kursen:
|
||
`hl_mid + basis` ist bei stehendem Broker zirkulär (dokumentiert 02.08.).
|
||
(3) Nur **Renditen**, nie Niveaus — zwei kointegrierte Preisreihen korrelieren
|
||
immer nahe 1 und sagen nichts über Vorlauf.
|
||
⚠ **Grenze:** die Abtastung liegt bei ~6 s, ein Vorlauf **unter** 6 s wäre
|
||
unsichtbar. Praktisch aber irrelevant — bei 1-s-Poll, WireGuard und Market-Order
|
||
ist er nicht ausbeutbar, und die Asymmetrie bei ±1 zeigt die Richtung bereits.
|
||
|
||
### ⚠⚠ ORDERBUCH FÜR DIE EMPFEHLUNG? NEIN — ES IST REAKTIV, NICHT PRÄDIKTIV (`analyze_orderbook_pred.py`, 2026-08-17)
|
||
|
||
User-Frage. Neu gerechnet auf **196.720 Buch-Schnappschüssen / 17,4 Tagen** mit
|
||
dem MT5-Kurs in derselben Zeile (64 % davon bei lebendem Broker verwertbar).
|
||
Merkmale `imb1`/`imb5`/`imb20`, Horizonte 60 s und 300 s, Quintile **je Hälfte**,
|
||
**nicht überlappende** Fenster, 95-%-Bootstrap.
|
||
|
||
**Ergebnis: alle 12 Zellen haben ein Intervall, das die Null enthält.** Keine
|
||
Vorhersagekraft belegt.
|
||
|
||
⚠⚠ **DER AUFSCHLUSSREICHE TEIL IST DIE RÜCKWÄRTS-KONTROLLE.** Berichtet wird
|
||
neben der künftigen auch die **vergangene** MT5-Bewegung je Quintil — in **10 von
|
||
12 Zellen ist die RÜCKWÄRTS-Spanne grösser als die vorwärts:**
|
||
| Merkmal · Horizont | vorwärts | **rückwärts** |
|
||
|---|---|---|
|
||
| imb1 · 60 s (H1/H2) | −0,05 / +0,08 bp | **+0,72 / +0,50** |
|
||
| imb1 · 300 s (H1/H2) | +0,06 / −0,91 | **+1,31 / +1,73** |
|
||
| imb5 · 60 s (H1/H2) | −0,09 / +0,10 | **+0,53 / +0,78** |
|
||
| imb20 · 60 s (H1/H2) | +0,05 / +0,06 | **+0,63 / +0,65** |
|
||
**Das Buch spiegelt die Bewegung, die auf Pepperstone SCHON PASSIERT ist** — es
|
||
ist reaktiv. Die beiden Ausnahmen (imb5/imb20 · 300 s · H1) reproduzieren in H2
|
||
nicht (dort +0,44 gegen rückwärts +1,83).
|
||
✅ **Damit passt es exakt zum Lead-Lag-Befund vom selben Tag:** Pepperstone führt
|
||
HL um ~6 s (r(k=−1)=+0,455 gegen r(k=+1)=+0,020). Ein Orderbuch, dessen Börse
|
||
hinterherläuft, kann den Broker nicht vorhersagen — der Befund ist damit nicht
|
||
nur gemessen, sondern **mechanistisch erklärt**.
|
||
|
||
⚠ **Und die ökonomische Hürde entscheidet ohnehin:** die grösste
|
||
Vorwärts-Spanne über alle Zellen ist **+1,63 bp**, der Pepperstone-Spread liegt
|
||
bei **~2,7 bp**. Selbst die beste Zelle — statistisch nicht von null zu trennen
|
||
und in der anderen Hälfte nicht reproduziert — läge **unter** den Kosten.
|
||
|
||
✅ **URTEIL: Das Orderbuch taugt nicht für die Empfehlung.** Das bestätigt
|
||
unabhängig die Entmachtung des Verdict-Moduls „Orderbuch" am 06.08. (Gewicht
|
||
0,5 → 0, über 2.688 Episoden beidhälftig negativ) — damals aus der
|
||
Verdict-Telemetrie, jetzt aus den Rohdaten der Quelle selbst.
|
||
⚠ **Grenzen, ehrlich:** nach der Entkopplung bleiben bei 300 s nur 1.252/1.254
|
||
Fälle, die Intervalle sind entsprechend breit; gemessen wurde die **Imbalance**,
|
||
nicht die Wände (deren Preis-Zuordnung bräuchte die Basis-Korrektur und wäre
|
||
damit zirkulär); und 17,4 Tage sind EIN Regime.
|
||
|
||
### ⚠⚠ NEUE DATENQUELLEN RECHERCHIERT — der Filter ist die FREQUENZ, nicht der Preis (2026-08-17)
|
||
|
||
User: „welche Tools (Websites, Newsfeed, Telegram, X) könnten wir noch nutzen, um
|
||
die Vorhersage zu verbessern."
|
||
|
||
⚠⚠ **DER ENTSCHEIDENDE FILTER IST DIE HALTEDAUER: 12–18 Minuten.** Damit fällt
|
||
praktisch die gesamte Fundamentaldaten-Welt weg — sie ist **wöchentlich**:
|
||
| Quelle | Frequenz | brauchbar für einen 15-Min-Trade? |
|
||
|---|---|---|
|
||
| EIA-Lagerbestände · API-Bulletin | wöchentlich (Mi/Di) | ❌ nur als Event-Blackout — **gebaut** |
|
||
| CFTC Commitments of Traders | wöchentlich (Fr) | ❌ |
|
||
| Baker-Hughes Rig Count | wöchentlich (Fr) | ❌ |
|
||
| OPEC-Produktion / IEA-Bericht | monatlich | ❌ |
|
||
| Tanker-Tracking / Satelliten-Lager | täglich | ❌ zu grob **und** Enterprise-Preis |
|
||
✅ Als **Event-Gate** ist genau dieser Teil bereits umgesetzt (EIA-Blackout Mi
|
||
15:30–16:30 + `core/econ_calendar.py` über Forex Factory, ±15 min, kostenlos).
|
||
Mehr Fundamentaldaten erhöhen dort nichts — die Termine sind schon abgedeckt.
|
||
|
||
⚠ **`oilpriceapi.com` (10.000 Anfragen/Monat gratis)** bündelt EIA, Baker Hughes
|
||
und OPEC bequem — löst aber kein Problem, das wir haben: es sind dieselben
|
||
wöchentlichen Zahlen, nur einfacher abzurufen.
|
||
|
||
⚠⚠ **KORREKTUR EINER PROJEKTZAHL: die dokumentierten „X-API: Basic 200 $/Monat,
|
||
Pro 5.000 $" gelten NICHT mehr.** Seit **Februar 2026** ist X auf
|
||
**pay-per-use** umgestellt: **0,005 $ je gelesenem Post**, gedeckelt bei 2 Mio.
|
||
Lesevorgängen/Monat; die alten Abo-Stufen sind für Neukunden geschlossen. Bei
|
||
z. B. 300 relevanten Posts/Tag wären das **~45 $/Monat** statt 200 — der
|
||
**Kosten**-Einwand von damals ist damit hinfällig.
|
||
✅ **Der MESSUNGS-Einwand steht aber unverändert** und ist der eigentliche: der
|
||
bereits vorhandene Textstrom (News-Sentiment) ist **gemessen nicht robust
|
||
prädiktiv** (03.08.: der stark-bullische Bucket liegt in BEIDEN Hälften bei null
|
||
bis negativ, der bearische kippt das Vorzeichen). Einen ZWEITEN Textstrom in
|
||
eine Pipeline zu legen, deren erster nichts vorhersagt, ist erst begründbar,
|
||
wenn der erste etwas zeigt. Fälligkeit dafür steht im Reminder
|
||
(`≥90 Tage Sammlung`, Zähler läuft seit dem Skalen-Fix vom 03.08.).
|
||
|
||
⚠ **Telegram-Kanäle (Bookmap/ATAS o. Ä.) bleiben verworfen** — sie transportieren
|
||
**Screenshots**. Ein Orderbuch-Zustand ist vorbei, sobald ihn jemand
|
||
abfotografiert. Und das Orderbuch selbst ist am 17.08. als **reaktiv** gemessen.
|
||
|
||
✅ **GESAMTURTEIL: keine neue Quelle empfohlen.** Begründung nicht „keine Lust",
|
||
sondern die Beleglage: **25 Signal-Eingriffe** über jede orthogonale
|
||
Indikatorklasse sind gescheitert; die Hyperliquid-Quelle ist am 17.08. in allen
|
||
drei Ausprägungen durchgemessen (Kurs läuft hinterher, Orderbuch reaktiv,
|
||
Funding trägt nicht); und der einzige je gemessene grosse Hebel war die
|
||
**Ausführung** (+0,32 R durch die ruhende Order — mehr als alle Signal-Ideen
|
||
zusammen). Eine neue Datenquelle müsste diese Latte schlagen, nicht nur „auch
|
||
Information enthalten".
|
||
|
||
### ✅✅ SIEBEN VERBESSERUNGEN UMGESETZT (User-Auftrag 2026-08-17)
|
||
|
||
**1 · CIRCUIT-BREAKER SCHARF** — `[trading] daily_loss_limit_pct = 0 → 8`
|
||
(Backup `…bak-2026-08-17-breaker`). Live verifiziert: `enabled=True`,
|
||
`limit_eur` **89,44 €**. ⚠ Steht **nicht** in `runtime_state.json` — die ini
|
||
greift direkt (die Falle, durch die `auto_signal` am 31.07. unbemerkt lief).
|
||
Begründung: der gemessene IST-Pfad hat **MaxDD 82,8 %** und einen schlechtesten
|
||
Einzeltrade von **−381 €**; der Breaker kostet an normalen Tagen nichts und
|
||
greift nur in der Serie. ⚠ Ehrlich: 70 % des gemessenen Nutzens stammten aus
|
||
EINEM Tag — das ist das Wesen einer Versicherung, aber die +739 € sind eine
|
||
Realisierung, keine Erwartung. **Zurück: `= 0`.**
|
||
|
||
**2 · B5-DEADLOCK RECHNERISCH AUFGELÖST — die Regel ist ENTSCHIEDEN.**
|
||
Stand 11/20: ØGewinn 13,62 gegen ØVerlust 94,15 = **Verhältnis 0,14** (Latte
|
||
1,0), **PF 0,39** (Latte 1,0). Nachgerechnet, was die 9 Rest-Trades leisten
|
||
müssten:
|
||
| Rest-Szenario | nötiger Ø-Gewinn | ר bisher |
|
||
|---|---|---|
|
||
| 9 Gewinner / 0 Verlierer | **165,72 €** | **12,2×** |
|
||
| 7 / 2 | 186,17 € | 13,7× |
|
||
| 5 / 4 | 222,99 € | 16,4× |
|
||
⚠ Der **grösste** Squeeze-Gewinn des Zeitraums war **77,34 €** — selbst der
|
||
beste Fall verlangt **2,1× den Rekord**, neunmal in Folge. **Die Latte ist nicht
|
||
mehr erreichbar.** Auf 20 zu warten hätte den Zustand nie entschieden, zumal der
|
||
Trader seit 11.08. aus ist und der Zähler damit einfror (der Deadlock).
|
||
`auto_squeeze` bleibt **FALSE**. ⚠ Eine Wiederaufnahme wäre eine **NEUE**
|
||
Entscheidung mit **NEUER, vorab fixierter** Latte — nicht die Fortsetzung dieser.
|
||
|
||
**3 · `core/kontrolle.py` — Kontroll-Fixture.** Referenzwerte (Squeeze
|
||
Touchfill +0,124/+0,292 · Close-Bruch +0,456/+0,611 · Markt −0,158/−0,006) plus
|
||
`pruefe_squeeze()`. Anlass: zwei Messungen am 13.08. konnten die Referenz nicht
|
||
reproduzieren, wodurch ihre Absolutwerte unbrauchbar waren. ⚠ Es bricht **nicht
|
||
ab** — ein Backtest darf abweichen, er muss es nur WISSEN und sagen; ein harter
|
||
Abbruch würde legitime Varianten unmöglich machen, eine stille Abweichung
|
||
erzeugt genau die Fehlschlüsse.
|
||
|
||
**4 · `core/live_config.py` — EINE Quelle der Live-Parameter.** Liest ini +
|
||
`runtime_state.json` in derselben Vorrangfolge wie der Engine-Start. ⚠ Anlass
|
||
ist die **dritte** Instanz derselben Fehlerklasse: `ATRMIN_STD`=0,12 statt live
|
||
0,06 (13.08.), `set_entry_room(0.6)` vergessen (05.08.), nachgebauter
|
||
Level-Cluster (07.08.). **Ein aufgerufenes Gate ist nicht dasselbe wie ein
|
||
aktives.** Aufruf: `python -m core.live_config`.
|
||
|
||
**5 · `stichprobe.paarweise()` / `zeig_paarweise()`.** Anlass: beim
|
||
`trail_start`-Sweep (14.08.) sahen sequentiell vier Werte beidhälftig besser aus
|
||
— gepaart kippte in H2 das Vorzeichen, weil ein anderer Exit ANDERE Trades in
|
||
den EINEN Slot lässt (Selektions- statt Exit-Effekt). ⚠ Der gepaarte Test ist
|
||
nicht nur richtiger, sondern **trennschärfer**: er misst die Streuung des
|
||
UNTERSCHIEDS, nicht die der Trades. Selbsttest: identisch → KI [0,0000 … 0,0000],
|
||
+0,30 → KI ohne Null, Rauschen → KI mit Null.
|
||
|
||
**6 · `tools/check_konstanten.py` — Wächter für veraltete Zahlen.** Leitet vier
|
||
tragende Grössen aus den Daten ab und hält sie gegen CLAUDE.md.
|
||
⚠⚠ **Er hat beim ersten Lauf zwei Befunde geliefert — beide waren FEHLER IN
|
||
MEINER EIGENEN ABLEITUNG**, also genau die Fehlalarme, gegen die Punkt 7 gebaut
|
||
ist: „Margin je Lot" war aus `Balance × 95 % / Lots` geschätzt (unterstellt volle
|
||
Auslastung), „Elliott-Stimmanteil" zählte über ALLE Zeilen statt über das
|
||
dokumentierte Fenster vor der Entmachtung. Beide korrigiert.
|
||
✅ **Danach fand er einen ECHTEN veralteten Wert — meinen:** die am 13.08. von
|
||
mir notierten „~405 €/Lot" sind falsch; der exakte Broker-Wert
|
||
(`position.margin/lots`) ist **712,84 €/Lot**, die ursprünglich notierten 670
|
||
lagen also näher als meine „Korrektur". **Lehre: eine abgeleitete Zahl ist keine
|
||
gemessene.** Stand jetzt: **0 Befunde** über alle vier Grössen.
|
||
|
||
**7 · Puls-Wächter bedingungs-bewusst.** Er meldete `pbreak_predictions` als
|
||
„63,5 h still" — die Tabelle schreibt aber nur bei **frischem Level-Touch auf
|
||
der Ziel-Seite einer OFFENEN Position**. `PULS` trägt jetzt je Zeile ihre
|
||
**Vorbedingung**; bedingte Tabellen erscheinen als **ⓘ Hinweis** statt als
|
||
Ausfall. ⚠ **0 Zeilen bleibt IMMER ein Befund** — „hat nie geschrieben" lässt
|
||
sich nicht wegerklären (das war der `rec_outcomes`-Fall). Grund: ein Wächter mit
|
||
Fehlalarmen wird ignoriert, und mit ihm die eine echte Meldung — genau das ist
|
||
dem Divergenz-Wächter passiert (6 Tage Dauerwarnung).
|
||
|
||
✅ Pipeline grün (85 Tests), Deployment über `tools/deploy.py --feld
|
||
circuit_breaker` verifiziert, genau eine Instanz je Port.
|
||
⚠ **Nicht angefasst:** das Sizing (95 % Margin) — Ihre mehrfach bestätigte
|
||
Entscheidung; und die 44 Backtests mit eigener Exit-Kopie (Migrations-Regel:
|
||
ihre publizierten Zahlen würden sich verschieben).
|
||
|
||
### ⚠⚠ „DER PFEIL SOLL DIE NÄCHSTE KERZE SCHÄTZEN" — gemessen: geht, zahlt aber nicht (`backtest_naechste_kerze.py`, 2026-08-17)
|
||
|
||
User: der Pfeil an der aktuellen Kerze zeigt die Abprall-Wahrscheinlichkeit des
|
||
S/R — er soll stattdessen die Richtung der nächsten Kerze schätzen.
|
||
✅ **Die Beobachtung stimmt und ist Absicht.** Live steht in der CSV
|
||
`AR;L;81.815;81.983;62% Abprall` — der **L**-Pfeil, seit v1.32 (31.07.) bewusst
|
||
so gebaut: er zeigt eine **bedingte, kalibrierte** Grösse (hält DIESES Level?,
|
||
AUC 0,65 oos) statt einer freien Prognose.
|
||
⚠ Trotzdem neu gemessen statt abgelehnt: die bisherigen Richtungs-Messungen
|
||
liefen über **60–360 Minuten**. Die **nächste M5-Kerze** ist ein anderer,
|
||
kürzerer Horizont — auf Ein-Bar-Ebene gibt es Mikrostruktur-Effekte, die dort
|
||
anders wirken können.
|
||
|
||
**80k M5-Bars, 2 Halbjahre, 5 Prädiktoren, 95-%-Intervall je Zelle:**
|
||
| Prädiktor | H1 Treffer | H2 Treffer | Ø Bewegung in Richtung |
|
||
|---|---|---|---|
|
||
| EMA-Trend (Wellen-Signal) | 49,6 % | 49,6 % | −0,0008 / +0,0004 $ |
|
||
| Momentum 3 Bars | 48,3 % ❌ | 48,3 % ❌ | ~0 |
|
||
| letzte Kerze **setzt fort** | 48,6 % ❌ | 48,8 % ❌ | ~0 |
|
||
| **letzte Kerze KEHRT UM** | **51,4 % ✅** | **51,2 % ✅** | **−0,0001 / +0,0002 $** |
|
||
| langer Docht (Lehrbuch) | 49,5 % | 49,3 % | +0,0010 / −0,0014 $ |
|
||
|
||
✅ **Es GIBT einen Effekt — und zwar den umgekehrten:** die 1-Bar-**Mean
|
||
Reversion** trifft in BEIDEN Hälften über 50 %, beide Intervalle schliessen die
|
||
50 aus. Trendfolge und Momentum sind dagegen **unter** 50.
|
||
⚠⚠ **ABER die ökonomische Bedingung fällt katastrophal durch: die Ø-Bewegung in
|
||
Vorhersagerichtung ist ±0,0002 $ — der halbe Spread beträgt 0,0100 $.** Der
|
||
Effekt ist damit **50–100× zu klein**. Eine M5-Kerze bewegt sich im Median nur
|
||
**0,0400 $**, der Spread ist **0,0200 $ = 50 % davon.**
|
||
**Trefferquote real, Geld keins:** Mean Reversion fängt viele kleine Rücksetzer
|
||
und wird von den wenigen Fortsetzungen überfahren.
|
||
|
||
✅ **URTEIL: der Pfeil bleibt, wie er ist.** Ein „nächste Kerze"-Pfeil wäre
|
||
**technisch wahr und praktisch wertlos** — und gefährlicher als nützlich, weil er
|
||
autoritativ aussieht. ⚠ Dazu käme, dass er nach der Messung **gegen** die letzte
|
||
Kerze zeigen müsste (Mean Reversion), was jeder Mensch als Fehler lesen und
|
||
übersteuern würde.
|
||
⚠⚠ **EIGENER MESSFEHLER auf dem Weg, dokumentiert weil er eine Klasse ist:** der
|
||
erste Lauf zählte **unveränderte** Schlusskurse als Fehltreffer — dadurch lagen
|
||
ein Prädiktor UND sein eigenes Gegenteil beide unter 50 % (43,4 + 45,8 = 89,2),
|
||
was arithmetisch unmöglich ist und den Fehler verriet. Real sind **~11 %** der
|
||
M5-Kerzen unverändert. **Merksatz: wenn A und Nicht-A sich nicht zu 100 %
|
||
addieren, ist die Messung kaputt, nicht der Markt.**
|
||
|
||
### ⚠⚠ KERZENMUSTER FÜR DIE RICHTUNG DER NÄCHSTEN KERZE = NEIN (`backtest_kerzenmuster_richtung.py`, 2026-08-17)
|
||
|
||
User-Anschlussfrage. Gemessen mit den **echten Muster-Schwellen** aus
|
||
`core/candles.py` (1:1 importiert, kein Nachbau), 80k M5-Bars, 2 Halbjahre,
|
||
unveränderte Schlusskurse ausgeschlossen, 95-%-Intervall je Zelle.
|
||
Bewertet wird die **Lehrbuch-Lesart** (±1), damit sichtbar wird, ob sie stimmt.
|
||
|
||
| Muster | H1 Treffer | H2 Treffer | Ø Bewegung |
|
||
|---|---|---|---|
|
||
| Marubozu | **46,5 % ❌** | **45,2 % ❌** | −0,0019 / +0,0077 $ |
|
||
| Engulfing | **47,4 % ❌** | 49,5 % | −0,0018 / −0,0009 |
|
||
| Outside Bar | **47,3 % ❌** | 48,9 % | −0,0019 / +0,0055 |
|
||
| langer Körper | 48,5 % | **47,6 % ❌** | +0,0009 / +0,0027 |
|
||
| langer Docht OBEN | 49,4 % | **47,8 % ❌** | +0,0011 / +0,0020 |
|
||
| langer Docht UNTEN | 50,6 % | 50,6 % | +0,0004 / **−0,0037** |
|
||
|
||
✅ **KEIN Muster erfüllt die vorab fixierte Regel.** Die Trefferquoten liegen
|
||
zwischen **45,2 und 50,6 %** — am oder **unter** dem Münzwurf; **fünf Zellen**
|
||
haben ein Intervall komplett unter 50, sind in der Lehrbuch-Lesart also
|
||
**verlässlich falsch**. Die einzige beidhälftig über 50 (langer Docht UNTEN,
|
||
50,6 %) hat in beiden Hälften ein Intervall MIT der 50 und eine Ø-Bewegung, die
|
||
das **Vorzeichen kippt**.
|
||
✅ **Das reproduziert den 60-Minuten-Befund vom 31.07.** („die Lehrbuch-Lesart
|
||
ist in BEIDEN Hälften invertiert") jetzt auch auf dem **1-Bar-Horizont** — mit
|
||
den echten Klassen statt der groben Proxys. Damit ist die Kerzen-Klasse zum
|
||
**vierten** Mal gemessen (Forward-Return 60 min · Fade als Handelsregel ·
|
||
Fade mit Ausführungs-Achse, 40 Zellen · jetzt 1-Bar-Richtung).
|
||
⚠ **Und selbst wenn man sie umdreht, zahlt es nicht:** die grösste Ø-Bewegung
|
||
über alle Zellen ist **0,0077 $** — und die kippt in der anderen Hälfte auf
|
||
−0,0019. Der halbe Spread beträgt **0,0100 $**, eine M5-Kerze bewegt sich im
|
||
Median **0,0400 $**.
|
||
|
||
✅ **URTEIL: der Pfeil bleibt bei der kalibrierten Level-Aussage.** Es gibt
|
||
keine Kerzen-Grundlage für eine Richtungsschätzung der nächsten Kerze — weder
|
||
in der Lehrbuch- noch in der umgedrehten Lesart.
|
||
⚠ Konsistent mit der Kachel-Entscheidung vom 31.07.: die Kerzen-Anatomie ist
|
||
bewusst **neutral** gefärbt und nennt die gemessene statt der Lehrbuch-Lesart.
|
||
|
||
### ✅ MELDUNGEN OBEN LINKS IM MT5-CHART (2026-08-17, Indikator v1.36)
|
||
|
||
User-Wunsch. Neue CSV-Zeile **`MS;<text>`**, im Indikator als gestapelte
|
||
`OBJ_LABEL` an `CORNER_LEFT_UPPER` (oben rechts sind Kurs, G/V und HL).
|
||
Inhalt = dieselben Zeilen wie die Dashboard-Karte „Meldungen": Squeeze-Zustand,
|
||
Überdehnung, S/R-Ziel mit P(break), Stop-Seite, News-Blackout.
|
||
|
||
⚠⚠ **NUR ZUSAMMENGESETZT, NICHT NEU GERECHNET.** Alles kommt aus bereits
|
||
berechneten Snapshot-Teilen bzw. `_last_sr_close_hint`/`_last_stop_approach`;
|
||
`calendar.blackout()` ist ein reiner Speicher-Zugriff. Der Konsens-Pfeil hat im
|
||
~5-s-Export-Takt einmal ein **komplettes zweites `_verdict()`** gezogen — doppelte
|
||
Last UND die Gefahr, dass Chart und Dashboard auseinanderlaufen.
|
||
✅ **Zwei Felder vorher geprüft statt angenommen:** `_last_calendar` und
|
||
`_export_meldungen` gab es **nicht** — ohne die Prüfung hätte das umgebende
|
||
`except Exception` es still verschluckt und die Meldungen wären dauerhaft leer
|
||
geblieben (dieselbe Klasse wie der `wave_snap`-Fallstrick vom 04.08.).
|
||
|
||
⚠⚠ **ASCII-SICHER — real nötig, nicht vorsorglich.** Der erste Lauf lieferte
|
||
`MS; Kompression Ausbruch steht bevor`: Emoji und Gedankenstrich waren zu
|
||
Leerzeichen geworden. Der Indikator liest die CSV **nicht** als UTF-8 — genau
|
||
deshalb steht dort auch „Unterstuetzung" statt „Unterstützung". Jetzt werden
|
||
Umlaute/Sonderzeichen ersetzt und alles ausserhalb 32–126 entfernt.
|
||
Verifiziert: `MS;Squeeze-Ausbruch LONG @ 82.413`, **0 Nicht-ASCII-Zeichen**.
|
||
|
||
⚠ Deckel **5 Zeilen** (der Chart soll lesbar bleiben), je 70 Zeichen; `;` im Text
|
||
wird zu `,`. Eigener Objekt-Präfix `SRB_MSG_*`, damit die Labels beim Redraw
|
||
verschwinden, wenn keine Meldung mehr ansteht (die Alt-Objekt-Falle der
|
||
HL-Pfeile). Abschaltbar: `[trading] export_meldungen=false` **oder**
|
||
`InpShowMeldungen=false`; Größe/Farbe über `InpMeldungSize`/`InpMeldungColor`.
|
||
✅ Kompiliert ins **richtige** Terminal (0 errors, 0 warnings), deployt und am
|
||
laufenden Server verifiziert.
|
||
|
||
### ⚠⚠ VERDICT-MODULE, ZWEITER DURCHGANG (2026-08-17) — und die Fälligkeitsschwelle war die falsche Größe
|
||
|
||
Fällige Messung (30.282 Zeilen, Schwelle 25.000). Horizont 60 min, gegen die
|
||
Drift-Null, **Episoden** statt Rohzeilen.
|
||
| Modul | Gew | Episoden | H1 | H2 | Treffer | |
|
||
|---|---|---|---|---|---|---|
|
||
| **Welle** | 3,0 | 254 | +0,158 | +0,419 | **61 %** | ✅ beide + |
|
||
| **Liq-Trend** | 0,5 | 297 | +0,310 | +0,175 | 52 % | ✅ beide + |
|
||
| **Squeeze** | 2,0 | 249 | +0,110 | +0,254 | 48 % | ✅ beide + |
|
||
| **Orderbuch** | 0,5 | **2.712** | −0,039 | −0,100 | 49 % | ❌ beide − |
|
||
| Elliott | 1,0 | 1.388 | −0,030 | +0,002 | 49 % | KIPPT |
|
||
| M30 | 1,5 | 236 | +0,631 | −0,074 | 51 % | KIPPT |
|
||
| Muster | 0,25 | 207 | +0,778 | −0,384 | 48 % | KIPPT |
|
||
| **H1** | 1,5 | **50** | −1,165 | +1,937 | 66 % | KIPPT |
|
||
| KI-Copilot | 1,0 | 53 | +0,215 | — | 58 % | n zu klein |
|
||
|
||
✅ **Meine Vermutung vom 07.08. war FALSCH: Liq-Trend besteht** (+0,310/+0,175
|
||
über 297 Episoden). Ich hatte ihn verdächtigt, weil er dieselbe HL-Quelle nutzt
|
||
wie das entmachtete Orderbuch — das war Analogie, keine Messung. Er bleibt bei
|
||
Gewicht 0,5.
|
||
✅ **Orderbuch bestätigt: beidhälftig negativ über 2.712 Episoden** — die mit
|
||
Abstand grösste Stichprobe und der einzige konsistent falsche Beitrag. Die
|
||
Entmachtung am 06.08. war richtig; er wird weiter geloggt, aber ohne Stimme.
|
||
✅ **Elliott bestätigt** (1.388 Episoden, kippt, 49 %).
|
||
|
||
⚠⚠ **DER SCHÄRFSTE BEFUND IST EIN MISSVERHÄLTNIS: H1 trägt 41,2 % des
|
||
Nadel-Einflusses — auf 50 Episoden, die das Vorzeichen kippen** (−1,17/+1,94).
|
||
Höchster Einfluss, dünnste Beleglage. ⚠ Live folgenlos, weil die Bias-Nadel
|
||
**reine Anzeige** ist (die Order hängt an der Wellen-Headline) — aber wer sie
|
||
liest, sollte es wissen.
|
||
|
||
⚠⚠ **DIE FÄLLIGKEITSSCHWELLE ZÄHLTE DIE FALSCHE GRÖSSE — behoben.** Sie stand
|
||
auf **25.000 ZEILEN**; die sind bei Minutentakt in 12 Tagen erreicht. Die
|
||
auswertbare Einheit sind aber **Episoden**, und davon gibt es je Modul nur
|
||
**50 bis 2.712** — bei H1 also 50. Dieselbe Klasse wie bei
|
||
`pbreak_accuracy_v2` (zählte roh statt entkoppelt und überschätzte die
|
||
Beweismenge um das Zehnfache). **Neu: ≥150 Episoden im schwächsten
|
||
stimmberechtigten Modul.** Zusätzlich bleibt der Vorbehalt des Skripts gültig:
|
||
**12 Tage = EIN Regime**, das ist Sichtung, kein Urteil.
|
||
|
||
**Punkt 2 — Stop-Order-Umbau (8/12): DERSELBE DEADLOCK wie B5.** Alle acht
|
||
Bot-Trades stammen vom **06.08.**; seit `auto_squeeze` am 11.08. aus ist, kommt
|
||
keiner mehr dazu — der Zähler steht dauerhaft bei 8/12. ⚠ Was die 8 zeigen,
|
||
bleibt gültig und positiv: **7 von 8 füllten per ruhender Order**, der
|
||
Market-Fallback ist praktisch weg (n=1 bei +0,148×ATR, innerhalb der
|
||
Nachjagd-Bremse); vorher lag der Live-Median bei +0,275×ATR. Der Umbau hat also
|
||
gewirkt — nur die formale Fälligkeit ist unerreichbar, solange der Trader aus
|
||
ist. **Sie hängt an derselben Entscheidung wie B5, nicht an weiteren Daten.**
|
||
|
||
### ✅ S/R ALS ZONE STATT LINIE (2026-08-17, Indikator v1.37)
|
||
|
||
User-Wunsch nach einem TradingView-Bild: Resistance Zone und eine untere Zone
|
||
als farbige Bänder. Neu: `RZ;lo;hi` (Widerstand) und `SZ;lo;hi` (Support),
|
||
im Indikator als `OBJ_RECTANGLE` mit Beschriftung, nach rechts verlängert.
|
||
|
||
⚠⚠ **DIE BREITE IST ECHT, NICHT ERFUNDEN.** Sie ist die Spanne der M30-Pivots,
|
||
die den Cluster gebildet haben (`sr_levels.zonen()`, additiv — `cluster()`
|
||
bleibt unverändert, weil es den S/R-Auto-Close speist). Live verifiziert:
|
||
`R;82.786` liegt in `RZ;82.776;82.797`, `S;82.15` in `SZ;82.14;82.16`.
|
||
⚠ Ein Cluster aus EINEM Pivot hätte Breite 0 → Mindestbreite 0,10×ATR, damit
|
||
das Band sichtbar bleibt.
|
||
|
||
⚠⚠ **ZWEI ABWEICHUNGEN VOM VORBILD, beide begründet:**
|
||
**(1) KEIN Fibonacci.** Das Bild leitet seine Zone aus 0,618/0,658 ab. Fib ist
|
||
hier gemessen und **verworfen** (04.08.): die Level halten mit 54,3/54,5 %
|
||
**nicht besser** als eine Zufallslinie gleicher Distanz (55,7/53,8 %) — und
|
||
schlechter als M30-Pivots (56,6/55,4 %). Eine Fib-Zone wäre eine hübsche Linie
|
||
ohne Beleg; die Pivot-Zone ist dieselbe Optik mit Substanz.
|
||
**(2) Die untere Zone heißt SUPPORT, nicht „Cumulative Long Liquidation".**
|
||
Echte Liquidationsdaten hat das Projekt **nicht** — was es gibt, sind die
|
||
HL-Liquiditätswände (`LQ;`, echte ruhende Orders, bereits als Pfeile gezeichnet),
|
||
und die sind am 17.08. als **reaktiv** gemessen. Bricht die Support-Zone, liegen
|
||
dort die Long-Stops — dieselbe Stelle, aber ehrlich benannt.
|
||
|
||
Abschaltbar `InpShowZonen=false`; Farben `InpZoneResColor`/`InpZoneSupColor`.
|
||
Kompiliert ins richtige Terminal (0 errors, 0 warnings), deployt und verifiziert.
|
||
|
||
### ✅ S/R-AUTO-CLOSE WIEDER AN (User-Entscheidung 2026-08-11)
|
||
|
||
Frage war: „sollten wir Trades bei einem S/R-Bounce automatisch schliessen?"
|
||
⚠ Die Funktion existierte bereits und stand seit dem 06.08. auf `false`; der
|
||
Config-Waechter flaggte die Abweichung durchgehend.
|
||
⚠⚠ **Entscheidend ist die Unterscheidung:** der **pauschale** S/R-Close ist
|
||
**2× verworfen** (Trefferquote 35→67 %, aber PF/ΣR in beiden Haelften schlechter
|
||
= Gewinner-Kappen); der **P(break)-gegatete** ist positiv (H1 +357 R, H2 +1.418 R
|
||
gegen reines Trailing, `backtest_pbreak_rvalue.py`).
|
||
|
||
✅ **LIVE KONTRAFAKTISCH NACHGERECHNET (43 Trades ab dem Nachtrainieren 31.07.):**
|
||
| | |
|
||
|---|---|
|
||
| IST (sr_close) | **+2.171,72 €** |
|
||
| HALTEN (kanonischer Exit ab demselben Einstieg) | +1.820,91 € |
|
||
| **Differenz** | **+350,81 €**, besser in **27 von 43** Faellen (63 %) |
|
||
⚠⚠ **Die 100-%-Trefferquote der sr_close-Statistik ist KEIN Befund, sondern
|
||
Konstruktion** — `_check_sr_close` schliesst ausschliesslich im Plus (`pnl>0`).
|
||
Aussagekraeftig ist allein der Vergleich oben.
|
||
✅ **Das kehrt `analyze_manual_close.py` (04.08.) um** (dort war sr_close −530 €
|
||
schlechter) — jene Messung lief auf dem **alten** Modell, vor dem Nachtrainieren.
|
||
⚠ **Vorbehalte:** n=43 in EINEM Regime · EUR-Faktor 87,6 geschaetzt · das
|
||
P(break)-Modell ist live **fehlkalibriert** (14,3 Pp zu niedrig, AUC unentschieden
|
||
bei 87 entkoppelten Vorhersagen) · und das Gate **gatet kaum** (schliesst 94–97 %
|
||
aller Beruehrungen, ist also nah an der pauschalen Variante). Dass es trotzdem
|
||
besser abschneidet, spricht dafuer, dass es den besseren **Moment** waehlt, nicht
|
||
die besseren Level.
|
||
✅ Eingeschaltet ueber `POST /api/srclose`; **alle drei Stellen angeglichen**
|
||
(Snapshot · `runtime_state.json` · ini) — sonst bliebe dieselbe latente
|
||
Inkonsistenz, durch die `auto_squeeze` unbemerkt aus war. Backup
|
||
`oil_widget_config.ini.bak-2026-08-11-srclose`.
|
||
⚠ Beim Einschalten stand eine Position bei −55,62 € — der Auto-Close greift nur
|
||
im Plus, hatte also keinen Sofort-Effekt.
|
||
⚠ **Nicht verwechseln:** `auto_squeeze` wurde am selben Tag um **14:10 vom User
|
||
ueber das Dashboard wieder AUSgeschaltet** (`[WEB] Auto-Squeeze-Entry AUS`) —
|
||
die Herkunfts-Kennzeichnung im Log (eingebaut 02.08.) hat das eindeutig belegt.
|
||
|
||
### ⚠ ENTRY-DIALOG ENTFERNT (User-Vorgabe 2026-08-11, v=163)
|
||
|
||
„Entferne auch die Warnung beim Eröffnen von Trades." LONG/SHORT feuern jetzt
|
||
**sofort**, ohne Bestätigung.
|
||
⚠ **Was wegfällt:** die 7-Punkte-Checkliste (Signal-Deckung · HTF-Trend ·
|
||
Entry-Raum · News · Kosten · Verlust-Serie · Nacht) **und** der
|
||
Ausrichtungs-Split im Moment der Entscheidung — letzterer zeigte die **einzige
|
||
beidhälftig robuste Zahl des Projekts**: gegen das Signal **−6,60 € je Lot**
|
||
über 1.216 Trades. Genau diese diskretionären Abweichungen sind die gemessene
|
||
Kern-Leckage; der Dialog war 2026-07-30 dagegen gebaut worden.
|
||
⚠ **Bewusste User-Entscheidung, kein Messergebnis.** Sie liegt aber auf der
|
||
Projekt-Linie „sichtbar machen, nicht bevormunden": hartes Blockieren war nie
|
||
eingebaut, und User-Übersteuerungen liefen gemessen **68 % Trefferquote**.
|
||
✅ **Backend UNBERÜHRT:** `engine._entry_checklist` rechnet weiter und steht als
|
||
`checklist` im Snapshot (live verifiziert) — die Zahlen bleiben auswertbar.
|
||
✅ **Die Verlust-Close-Abfrage bleibt** („Position im Minus schließen?") — sie
|
||
schützt vor einem versehentlichen Klick, nicht vor einer Entscheidung.
|
||
**Zurück:** `const ENTRY_DIALOG = true` in `app.js` (Einzeiler).
|
||
|
||
### ✅ KLARTEXT-KOPFZEILE (2026-08-11, v=162) — und warum NICHT „LONG zu 78 %"
|
||
|
||
User: „kannst du die M15-Empfehlung noch etwas vereinfachen, wie: jetzt
|
||
long/short zu xxx Prozent?" ✅ **Der Wunsch ist berechtigt** — die Karte war
|
||
sieben Zeilen Technik. ⚠⚠ **Die Form nicht:** eine Richtungs-Prognose ist über
|
||
**1.071 Live-Episoden mit 48–51 % Trefferquote** gemessen (frei sogar AUC **0,52
|
||
IN-sample** — es steckt keine Richtungsinformation in den Daten), und die
|
||
Prozentzahl war als `conf_pct` **nicht kalibriert, in H2 invertiert** (85–100 % →
|
||
40 % Trefferquote). **Genau daran ist die Gesamtempfehlung gescheitert** und
|
||
wurde am 08.08. auf User-Wunsch entfernt. Es wäre der **vierte** Rückfall dieser
|
||
Karte in die Veto→Richtung-Falle gewesen (Code 08.08. · Geometrie 10.08. ·
|
||
Lesart 10.08.) — diesmal auf Bestellung.
|
||
|
||
✅ **Gebaut: eine Klartext-Zeile, die sagt WAS ZU TUN IST**, mit der **einen**
|
||
kalibrierten Prozentzahl, die es gibt — P(break) des Levels, an dem eingestiegen
|
||
würde (AUC 0,65 oos). Das ist eine Aussage über die **Struktur**, nicht über den
|
||
Trade.
|
||
| Lage | Kopfzeile |
|
||
|---|---|
|
||
| Abpraller | `🟢 JETZT: LONG-Order bei 78,768 (Abpraller) · hält zu 75 % · Ziel 79,033 · ⛔ SHORT meiden` |
|
||
| Ausbruch | `🟢 JETZT: LONG-Order bei 79,033 (Ausbruch) · Level unentschieden` |
|
||
| Level zu weit | `🟡 JETZT: warten auf das Level · erlaubt wäre LONG` |
|
||
| **kein Veto** | `🟡 JETZT: kein Trend-Veto — die Karte gibt hier keine Richtung` |
|
||
| Spread hoch | `🔴 JETZT: nicht handeln — Spread frisst den Edge (0,35×ATR)` |
|
||
⚠ **„Erlaubt" heisst NICHT „empfohlen"** — belegt ist nur, dass die
|
||
Gegenrichtung kostet (−6,60 €/Lot, beidhälftig). Die 45–55-%-Zone bleibt
|
||
bewusst ohne Richtung („unentschieden").
|
||
⚠ Der **kein-Veto**-Fall ist der ehrlichste: bei flachem M30 ist keine Richtung
|
||
ausgeschlossen, und eine zu nennen wäre wieder Prognose. Die Karte sagt dann
|
||
ausdrücklich nichts.
|
||
⚠ **Reine Formulierung, KEINE Änderung der Gate-Semantik.** Ob der Raum (③b) das
|
||
Banner auf gelb ziehen soll, ist ungemessen und bleibt offen.
|
||
⚠ Frontend-only (`app.js` + `v`-Bump) → **kein Neustart nötig**, die offene
|
||
Position behielt Trailing und Time-Stop.
|
||
⚠⚠ **Beim Bau in die dokumentierte Anführungszeichen-Falle gelaufen:** ein
|
||
ASCII-`"` als schliessendes deutsches Zitat (`„LONG zu 78 %"`) beendet den
|
||
JS-String vorzeitig → `Unexpected token ':'`. **`node --check` hat es gefangen**
|
||
(Stufe B) — genau dafür wurde es am 07.08. eingebaut. In Fliesstext gehören
|
||
einfache Quotes oder `"` als Schlusszeichen.
|
||
|
||
### ⚠⚠ GATE ③b — DIE KARTE PRÜFTE „LEVEL NAH", NIE „RAUM DAHINTER" (2026-08-10)
|
||
|
||
**Anlass: Trade #49671601, −87,03 €** (User: „SL geschlossen trotz LONG-Empfehlung").
|
||
⚠ **Die Prämisse stimmte nicht — es gab keine LONG-Empfehlung.** 138 Zeilen
|
||
zwischen Ein- und Ausstieg, **100 % WARTEN**: `entry_room` 76 % · `breakout_pending`
|
||
19 % · `min_conf` 5 %, **kein einziges durchgelassenes Richtungssignal**. Gesehen
|
||
wurde `verboten: SHORT` — ein **Veto**.
|
||
⚠⚠ Damit ist es der **dritte** Fall derselben Umwandlung in dieser Karte: am 08.08.
|
||
machte der **Code** aus dem Verbot eine Richtung (13 LONG-Episoden an einem
|
||
fallenden Tag), am 10.08. früh die **Geometrie** (Gate ④), jetzt die **Lesart**.
|
||
Wenn eine Anzeige dreimal zur selben Fehldeutung einlädt, liegt es an der Anzeige.
|
||
|
||
⚠⚠ **DER EIGENTLICHE BEFUND: zwei Bauteile lesen DIESELBE Tatsache mit
|
||
umgekehrtem Vorzeichen.**
|
||
| Bauteil | bei „Level sehr nah" | Beleg |
|
||
|---|---|---|
|
||
| **`entry_room`** (Signal-Motor) | **WARTEN** — Ertrag am Level gedeckelt | ØR −0,119/−0,067 · **PF 0,42/0,66** |
|
||
| **Gate ③** (M15-Karte) | **GRÜN** — Level in Reichweite | Ausführungs-Edge der ruhenden Order |
|
||
✅ **Beide sind gemessen und beide haben recht — über VERSCHIEDENE Trades:**
|
||
`entry_room` misst den **Markt**-Einstieg mit dem Level als *Hindernis voraus*,
|
||
die Karte die **ruhende Order AM** Level. Der Unterschied ist real.
|
||
**Die Karte zeigte nur die grüne Hälfte** — die Block-Grund-Zeile war beim
|
||
Entfernen der Gesamtempfehlung mit verschwunden.
|
||
|
||
**Der Trade war beim Einstieg entschieden:** Widerstand **0,07×ATR = 0,008 $**
|
||
voraus, Stop **0,560 $ = 4,7×ATR_M5** zurück (CRV ~1:67), **MFE +0,016 $** — nie
|
||
im Plus. `ctx_atr_m5 = 0,12` = **der ATR-Floor**, der Markt stand still; der Stop
|
||
kam trotzdem aus ATR_M15 (bei manuellen Trades Absicht, M5 nur für Auto-Squeeze
|
||
seit 05.08.). −87 € = `0,568 $ × 1,77 Lots × ~87,6 €` — reine Geometrie, kein
|
||
Ausreißer. ⚠ Stunde 6 „vor DE-Open": autonom hätte der Bot dort nicht eröffnet.
|
||
|
||
**Ist es ein Muster? Gemessen (77 Trades mit `ctx_*` seit 04.08., je Lot):**
|
||
| Block beim Einstieg | n | WR | EUR/Lot |
|
||
|---|---|---|---|
|
||
| **`entry_room`** | **29** | 55 % | **−11,45** |
|
||
| `min_conf` | 16 | 44 % | −16,08 |
|
||
| `htf_counter` | 10 | 80 % | +22,83 |
|
||
| `breakout_pending` | 9 | 89 % | +17,68 |
|
||
|
||
Direkt auf der Raum-Achse (Abstand zum Level in der **eigenen** Trade-Richtung):
|
||
| | n | WR | EUR/Lot | 95-%-KI |
|
||
|---|---|---|---|---|
|
||
| Raum < 0,6 (geblockt) | 24 | **62 %** | −9,91 | [−29,4 … +6,6] ⚠ |
|
||
| Raum ≥ 0,6 (frei) | 52 | **62 %** | +3,24 | [−5,8 … +13,5] ⚠ |
|
||
⚠⚠ **Beide Intervalle enthalten die Null — mit n=76 ist das NICHT belegt**, nur
|
||
konsistent mit dem über 80k Bars gemessenen Gate. ✅ Die schärfste Zeile ist die
|
||
**identische Trefferquote 62 % gegen 62 %**: die Richtungswahl war gleich gut, der
|
||
Unterschied liegt **allein in der Asymmetrie** (Gewinn am Level gedeckelt, Verlust
|
||
läuft bis zum Stop) — die dokumentierte Kern-Leckage an neuer Stelle.
|
||
|
||
✅ **GEBAUT: Zeile ③b** (`#m15-motor`, v=160) — zeigt den aktuellen `block_reason`
|
||
im Klartext **plus den Raum je Richtung** („③b Motor blockt: gegen den M30-Trend ·
|
||
Raum ▲ 0,17×ATR ⚠ / ▼ 0,07×ATR ⚠ (Gate 0,6)"). Beide Richtungen, weil die Karte
|
||
bewusst keine vorgibt.
|
||
⚠⚠ **Das Status-Banner bleibt BEWUSST unverändert.** Es auf 🟡 zu ziehen wäre eine
|
||
Strategie-Änderung an einer gemessenen Gate-Kette — und die einzige Live-Zahl dafür
|
||
(−9,91 gegen +3,24 €/Lot) hat ein KI, das die Null enthält. **Sichtbar machen,
|
||
nicht bevormunden**; die Order-Buttons bleiben ohnehin bedienbar (Übersteuerungen
|
||
liefen gemessen 68 % WR).
|
||
✅ **`wave_rec.room_info(cur)` ist EINE Implementierung für Gate UND Anzeige** —
|
||
`_room_gate` rechnet jetzt darüber. Ein Nachbau wäre die Divergenz-Falle, die im
|
||
Projekt dreimal zugeschlagen hat (`sim_run`/`_ema_series` in ≥6 Skripten · der
|
||
nachgebaute Level-Cluster · das fünfte Exit-Modell).
|
||
⚠ Quelle ist trainingsgleich **M5**, nicht die M30-Level der Karte — `entry_room_atr
|
||
=0,6` ist auf M5 kalibriert (die drei divergierenden Level-Quellen sind Absicht).
|
||
✅ **Bitgleichheit bewiesen** (`tests/test_room_info.py`, 6 Tests): die alte Fassung
|
||
liegt wörtlich eingefroren im Test und läuft über **2.000 Zufallslagen × 3 Signale**
|
||
gegen die neue — identisch, und das Gate feuerte dabei >300× (ein Test, in dem es
|
||
nie feuert, belegt nichts). ⚠ Der gefährlichste Fall hat einen eigenen Test:
|
||
`frei` MUSS auf dem **ungerundeten** Abstand entschieden werden — bei 0,5951 würde
|
||
die gerundete 0,60 den Fall fälschlich durchlassen.
|
||
|
||
## ⚠⚠ NUR DAS TRAILING BREITER (Richtung M15)? = FÄLLT DURCH, und der
|
||
## Auflösungs-Einwand trägt nicht (`backtest_trail_tf.py`, 2026-08-11)
|
||
|
||
**Anlass:** User „sollten wir das Trailing auf M15 setzen?" nach einem Trade, der
|
||
bei Trail 1,0×ATR_M5 nach rund **1×ATR Rücklauf auf Einstand** ausgestoppt wurde
|
||
(#49850541 — bei genau diesem Abstand ist das zwangsläufig).
|
||
|
||
⚠ **Warum die Frage NICHT schon beantwortet war.** `backtest_exit_atr_tf.py`
|
||
(08.08.) stellte den **gesamten** Exit auf ATR_M15 — `simulate()` nimmt EINEN
|
||
`atr` für SL, Trail, TP **und** Breakeven, dort wurde also alles mitverbreitert
|
||
(daher das verdreifachte Tail). `backtest_trailmult.py` (31.07.) traf die Frage
|
||
(fixer SL 2,0, nur `mult` gesweept), lief aber auf **M5-Bars** — und genau davor
|
||
warnt `analyze_trail_resolution.py` (05.08.): eine Bar-Simulation **überschätzt
|
||
enge Trails und unterschätzt weite**, bei 1,5×ATR kippte dort sogar das
|
||
Vorzeichen (M5 −0,038 gegen M1 **+0,028**). Der Sweep, auf dem „enger ist besser"
|
||
ruht, war also mit einer Verzerrung **GEGEN** weite Trails gemessen.
|
||
|
||
**Diese Messung kontrolliert das:** dieselben Einstiege, derselbe ATR (aus M5) →
|
||
Trail-Abstand in **Dollar identisch**, nur die Exit-Auflösung unterscheidet sich;
|
||
Zeitgrenzen **wall-clock gleich** (200 M5-Bars = 1000 M1-Bars, Time-Stop 24 = 120
|
||
— der Altbestand liess 200 stehen und kappte M1-Trades nach 200 statt 1000 min).
|
||
Zwei Populationen, Echtkosten.
|
||
| mult | Squeeze M5 H1/H2 | **Squeeze M1** | Welle M5 | **Welle M1** |
|
||
|---|---|---|---|---|
|
||
| 0,80 | +0,123 / +0,557 | **+0,077 / +0,292** | −0,131 / +0,022 | −0,237 / −0,183 |
|
||
| **1,00 (live)** | +0,019 / +0,495 | **−0,067 / +0,206** | −0,242 / +0,031 | −0,302 / −0,127 |
|
||
| 1,50 | −0,155 / +0,624 | −0,095 / +0,446 | −0,275 / +0,063 | −0,405 / −0,112 |
|
||
| **1,86 („M15")** | **−0,355 / +0,557** | **−0,327 / +0,379** | −0,297 / +0,053 | **−0,392 / −0,067** |
|
||
**1,86 fällt in H1 auf BEIDEN Populationen und BEIDEN Auflösungen klar durch**
|
||
(Trefferquote 25–30 %, PF 0,52–0,64). Regel nicht ansatzweise erfüllt.
|
||
|
||
✅✅ **DER EIGENTLICHE ERTRAG: der Auflösungs-Einwand ist entkräftet — er wirkt
|
||
nur bei ENGEN Trails.** Δ (M1 − M5) auf der Welle: **−0,092/−0,236 bei 0,50**,
|
||
−0,060/−0,158 bei 1,00, −0,095/−0,120 bei 1,86; auf dem Squeeze bei 2,00
|
||
**+0,049/−0,016 = praktisch null**. Bei weiten Trails verschwindet der
|
||
Unterschied — logisch, ein weiter Stop ist nicht auflösungsempfindlich.
|
||
⚠ **Das PRÄZISIERT den Befund vom 05.08.**: die feinere Auflösung **bestraft enge
|
||
Trails**, sie **belohnt weite nicht**. Der dortige Vorzeichenwechsel bei 1,5 kam
|
||
aus n=597 **willkürlichen** Einstiegen (jeder 20. Bar, immer LONG) — mit echten
|
||
Einstiegen reproduziert er nicht.
|
||
|
||
⚠ **Grenzen, ehrlich:** Squeeze nur **n=46/47** je Hälfte; die M1-Historie deckt
|
||
~55 Tage, die „zwei Hälften" sind also **zwei Hälften EINES Regimes**, nicht zwei
|
||
Regime. Ein ✅ wäre hier wenig wert — das ❌ bei 1,86 ist es, weil es in **jeder**
|
||
Zelle auftritt. Die Wellen-Population ist in diesem Fenster durchgehend negativ
|
||
und kann deshalb gar nicht diskriminieren.
|
||
⚠ **Nebenbefund, ausdrücklich KEINE Empfehlung:** auf M1-Auflösung sieht **0,8
|
||
besser aus als 1,0** (Squeeze +0,077/+0,292 gegen −0,067/+0,206) — also eher
|
||
*enger*, die Gegenrichtung der Frage. Für eine Umstellung reicht das nicht: ein
|
||
Regime, dünne Stichprobe, und die Sim modelliert **keine Exit-Slippage**, die
|
||
enge Trails härter trifft (real bis 0,75×ATR über den Stop hinaus).
|
||
**Keine Änderung — Trail bleibt 1,0×ATR_M5.**
|
||
|
||
## ⚠⚠ EXIT-ABSTAND AUS M15-ATR = FÄLLT DURCH (2026-08-08)
|
||
|
||
Zweiter Teil der M15-Idee: M5-Einstieg behalten, nur den EXIT gröber führen.
|
||
Gemessen (`backtest_exit_atr_tf.py`, 80k M5, feste geteilte Entry-Liste,
|
||
kanonischer Exit, Echtkosten). Schliesst zugleich den seit dem 31.07. in
|
||
`core/trailing.py` offenen Punkt („bisher misst er nur M5").
|
||
| | H1 ØR / PF / Worst | H2 ØR / PF / Worst |
|
||
|---|---|---|
|
||
| **M5-ATR (heute)** | **+0,219 / 1,41 / −2,17** | **+0,117 / 1,25 / −2,23** |
|
||
| M15-ATR (1,86× breiter) | +0,096 / 1,14 / **−4,26** | +0,019 / 1,02 / **−6,73** |
|
||
**Schlechter in BEIDEN Hälften — und das Tail-Risiko verdreifacht sich.**
|
||
Dasselbe Muster wie bei `backtest_sl_basis.py`: ein breiterer Exit kauft nichts
|
||
und kostet Tail. **Der Exit bleibt am M5-ATR.**
|
||
|
||
⚠⚠ **METHODEN-FALLE, die die Messung fast entwertet hätte** — zweimal:
|
||
**(1)** `simulate()` normiert R mit DEMSELBEN ATR, den es für SL/Trail/TP nutzt.
|
||
Mit dem 1,86× grösseren M15-ATR wäre dieselbe Dollar-Bewegung ein kleineres R —
|
||
die M15-Variante hätte mechanisch schlechter ausgesehen. Beide werden deshalb in
|
||
**M5-Risikoeinheiten** zurückgerechnet (`R × ATR_verwendet / ATR_M5`).
|
||
**(2)** Der erste Lauf sammelte die Entry-Liste über `exit_fn` — das wird von
|
||
`scan()` bei **JEDEM** Box-Ausbruch gerufen, nicht nur bei den komprimierten:
|
||
**519 statt 132 Einstiege**, also die Kontrollgruppe statt des Squeeze. Behoben
|
||
über den additiven Rückruf **`on_entry(j, d, lvl, atr, komprimiert)`** in
|
||
`core/squeeze_scan.py` (Bitgleichheit weiter durch `tests/test_squeeze_scan.py`
|
||
belegt). ⚠ **Ein Haken, der mehr liefert als gedacht, ist eine stille
|
||
Populations-Änderung.**
|
||
|
||
## ⚠⚠ SQUEEZE AUF M15 = FÄLLT DURCH — kein M15-Modul gebaut (2026-08-08)
|
||
|
||
Anlass: User „baue mir das M15-Empfehlungsmodul". Der Squeeze ist der einzige
|
||
beidhälftig validierte Einstieg; gemessen auf **M5** und **M1**, nie auf **M15**.
|
||
Die Live-Parameter ungeprüft zu übernehmen wäre Deployment-Drift, also erst
|
||
gemessen (`backtest_squeeze_m15.py`, 26.000 M15-Bars, kanonischer Exit,
|
||
Echtkosten, Touch-Fill, 2 Hälften).
|
||
|
||
⚠⚠ **DIE KONTROLLE HAT ZUERST VERSAGT — und das war der wichtigste Teil.** Meine
|
||
M5-Kontrolle lieferte −0,133/+0,044 statt der dokumentierten +0,124/+0,292.
|
||
Ursache diagnostiziert: **allein der ATR-Floor.**
|
||
| | Floor | Floor/Median | M5 H1 / H2 |
|
||
|---|---|---|---|
|
||
| mein Ansatz (0,542×Median) | 0,060 | 0,54× | −0,108 / +0,036 |
|
||
| dokumentiert | 0,120 | **1,08×** | **+0,224 / +0,087** |
|
||
`max_hold` war irrelevant (bitgleich). Ich hatte auf M15 also einen **halb so
|
||
strengen** Filter angelegt wie auf M5 — der „relative Floor" aus
|
||
`backtest_squeeze_multi.py` diente dort der Vergleichbarkeit zwischen
|
||
INSTRUMENTEN, nicht zwischen Zeitebenen. **Ein Filter, dessen Strenge man frei
|
||
wählt, erzeugt den Befund** (die Lehre aus `analyze_tageszeit.py`).
|
||
|
||
**Deshalb über die ganze Floor-Spanne gemessen statt einen Wert zu wählen:**
|
||
| Floor/Median | H1 ØR / PF / n | H2 ØR / PF / n |
|
||
|---|---|---|
|
||
| 0,50 | −0,253 / 0,59 / 143 | −0,024 / 0,96 / 193 |
|
||
| 0,75 | −0,290 / 0,56 / 108 | +0,047 / 1,08 / 184 |
|
||
| 1,00 | −0,030 / 0,96 / **39** | +0,086 / 1,15 / 164 |
|
||
| **1,08 (M5-äquivalent)** | **+0,216 / 1,35 / 33** | +0,080 / 1,14 / 170 |
|
||
| 1,25 | −0,309 / 0,47 / **16** | +0,097 / 1,18 / 163 |
|
||
| 1,50 | −0,279 / 0,70 / **7** | +0,151 / 1,28 / 148 |
|
||
**Die einzige beidhälftig positive Zelle hat n=33 in H1 — und BEIDE Nachbarn
|
||
kippen** (−0,030 und −0,309). Vorab fixierte Regel (2) verfehlt: Rauschen, exakt
|
||
das ORB-Muster. Dazu: alle sechs Nachbar-Kombis Box 10/15/20 × k 0,1/0,2 sind
|
||
mit den Live-Parametern in mindestens einer Hälfte negativ, die Kontrolle
|
||
„beliebige Box" ist BESSER als der Squeeze (−0,052/+0,054 gegen −0,121/−0,022),
|
||
und der Slippage-Härtetest verschlechtert monoton.
|
||
|
||
⚠ **Der strukturelle Grund ist in der n-Spalte sichtbar:** bei einem Filter, der
|
||
streng genug ist, um zu wirken, bleiben in H1 nur **33–39 Ausbrüche**. Der
|
||
Squeeze feuert auf M5 2–3×/Woche; M15 ist für dieses Setup schlicht zu grob.
|
||
**Kein M15-Modul gebaut.**
|
||
|
||
✅ **EIN echter Positiv-Befund bleibt: die Kosten.** Median Spread/ATR auf M15
|
||
gemessen **0,104** — noch besser als die erwarteten 0,133 (M5 0,193 · M1 0,319).
|
||
Die Kosten-Arithmetik spricht weiterhin für gröbere Zeitebenen; es fehlt nur ein
|
||
Signal, das dort trägt.
|
||
|
||
## ⚠⚠ ECHTES CME-VOLUMEN (CL=F) TRÄGT NICHT MEHR ALS DIE CFD-TICK-ZÄHLUNG (2026-08-08)
|
||
|
||
Anlass: User-Idee eines Telegram-Alerts auf WTI-Volumen-Spikes (`CL=F` via
|
||
yfinance). Die berechtigte Frage dahinter: das Projekt kennt bisher nur
|
||
`tick_volume` vom CFD — eine Zählung von Preisänderungen bei einem
|
||
**Market-Maker**-Broker, kein gehandeltes Volumen. Echtes NYMEX-Kontraktvolumen
|
||
wäre die erste Datenquelle, die **nicht aus dem Preis abgeleitet** ist.
|
||
|
||
**Gemessen (`backtest_cme_volumen.py`): beide Quellen auf DEMSELBEN Fenster**
|
||
(28.05.–07.08.26, 13.855 CME- gegen 14.178 CFD-Bars), gleiche Methode,
|
||
Überschuss über die Drift, zwei Hälften.
|
||
⚠ Bewusst NICHT gegen den dokumentierten Wert −0,117/−0,204 gestellt — der
|
||
stammt aus einem anderen Zeitraum. Genau dieser Vergleich („kurzes Fenster gegen
|
||
Langfrist-Mittel") war am 07.08. der Messfehler, der elf Hypothesen ausgelöst hat.
|
||
| Volumen | CME H1 / H2 | CFD H1 / H2 |
|
||
|---|---|---|
|
||
| 1,5–2,0× | +0,116 / **−0,203** | −0,011 / +0,152 |
|
||
| 2,0–2,5× | −0,221 / −0,282 | −0,101 / **+0,589** |
|
||
| **≥2,5×** | **+0,102 / +0,096** | −0,267 / **−0,881** |
|
||
**Trefferquote in ALLEN Zellen 44–53 %** — Münzwurf, in beiden Quellen.
|
||
Kein monotoner Zusammenhang, und die Vorzeichen kippen zwischen den Hälften.
|
||
Die einzige beidhälftig konsistente Zelle (CME ≥2,5×) ist mit
|
||
**[−0,234 … +0,428] bzw. [−0,265 … +0,461]** klar Rauschen.
|
||
|
||
⚠⚠ **EHRLICHE GRENZE: meine CFD-Kontrolle REPRODUZIERT den alten Befund NICHT** —
|
||
und der Grund ist ein Populations-Unterschied, kein Widerspruch.
|
||
`backtest_candles.py` maß „2× Volumen" **bedingt auf ein Kerzen-MUSTER** (langer
|
||
Körper / Marubozu), hier läuft es unbedingt über alle Kerzen. Der alte Befund
|
||
ist damit weder bestätigt noch widerlegt; er gilt weiter für seine Population.
|
||
⚠ 60 Tage = EIN Regime (yfinance deckelt 5-min-Bars). Ein ✅ hieße hier „nicht
|
||
ausgeschlossen"; das ❌ ist belastbar.
|
||
|
||
✅ **Konsequenz: kein Volumen-Spike-SIGNAL.** Die Datenquelle funktioniert
|
||
(CL=F liefert echte Kontrakte, ~4 s je Abruf), sie trägt nur keine
|
||
Richtungsinformation. Ein Alert ist allenfalls als **Aufmerksamkeits-Hinweis**
|
||
vertretbar („hier passiert etwas"), ausdrücklich NICHT als Richtungsaussage —
|
||
und dann mit derselben Ehrlichkeit beschriftet wie die Kerzen-Anatomie-Karte.
|
||
⚠ **Telegram-KANÄLE als Eingabe (Bookmap/ATAS) = verworfen, ohne Messung:** sie
|
||
transportieren **Screenshots**. Ein Orderbuch-Zustand ist vorbei, sobald ihn
|
||
jemand abfotografiert. Dasselbe Argument wie bei der X-API — und der vorhandene
|
||
Textstrom (News-Sentiment) ist bereits gemessen nicht robust prädiktiv.
|
||
⚠ **Nebenbefund zum eingereichten Skript:** das `.iloc[0]`-Muster ist **korrekt**
|
||
(yfinance liefert MultiIndex-Spalten), nicht wie zunächst von mir vermutet ein
|
||
Fehler. Echte Mängel wären: kein Dedup (die „letzte geschlossene Kerze" bleibt
|
||
5 min stehen → 5 Alerts je Spike), kein Handelszeit-Gate, voller Tagesabruf im
|
||
60-s-Takt.
|
||
|
||
## ⚠⚠ VWAP-BÄNDER AUF M15 = VERWORFEN (2026-08-08) — die letzte ungetestete Zutat
|
||
|
||
Anlass: User-Wunsch nach einem **M15-Daytrading-Modul** auf Profi-Methodik
|
||
(Order-Flow, Volume Profile, VWAP, Session-Struktur, Liquidity Sweeps, MSS,
|
||
Mean-Reversion). Abgleich mit dem Gemessenen ergab: **fast alles davon ist in
|
||
diesem Projekt bereits durchgefallen** — Volume Profile (POC-Reversion
|
||
beidhälftig negativ), Liquidity Sweeps (alle 9 Kombis), Market Structure (24/24
|
||
Zellen), ORB (mit realer Fill-Annahme alle 4), RSI/MACD, Inter-Market.
|
||
Footprint/CVD/DOM existieren hier nicht (Market-Maker-Broker ohne Orderbuch; das
|
||
HL-Orderbuch ist gemessen 0,2–1,0 bp gegen ~3 bp Kostenhürde und läuft dem CFD
|
||
**hinterher**). Kpler/Bloomberg scheitern am Preis, CoT und Cushing an der
|
||
wöchentlichen Frequenz.
|
||
**VWAP war die EINZIGE nie gemessene Zutat** (`calc_vwap` existiert in
|
||
`core/analysis/indicators.py`, taucht in CLAUDE.md nirgends als Signal auf).
|
||
Jetzt gemessen: `backtest_vwap_m15.py`, 26.000 M15-Bars (02.07.25–07.08.26),
|
||
session-verankert auf den Berliner Kalendertag, 2 Hälften, kanonischer Exit,
|
||
Echtkosten, **Fill bei Berührung**.
|
||
|
||
⚠⚠ **ZUERST DER EIGENE FEHLER — er ist die halbe Lehre.** Der erste Lauf zeigte
|
||
Reversion bei k=2,0–3,0 mit **ØR +0,13 bis +0,27 und PF bis 1,71 in BEIDEN
|
||
Hälften** — verdächtig sauber. Ursache: das Band an Bar `i` wird **mit** Bar `i`
|
||
berechnet (der VWAP kumuliert sie), und getestet wurde, ob genau diese Bar es
|
||
berührt. **Ein Ausschlag zieht das Band zu sich hin und erzeugt seine eigene
|
||
Berührung.** Mit Band aus `i−1` (live zwangsläufig der Fall) bleibt:
|
||
| k | vorher H1/H2 | **nach dem Fix** |
|
||
|---|---|---|
|
||
| 2,0 | +0,130 / +0,130 | **+0,041 / −0,009** |
|
||
| 2,5 | +0,189 / +0,235 | **+0,046 / +0,087** |
|
||
| 3,0 | +0,162 / +0,272 | **+0,024 / +0,010** |
|
||
**Der Look-ahead war 0,10–0,25 R wert** — dieselbe Größenordnung wie beim
|
||
Squeeze (0,32), Momentum (0,45) und SIG (0,77–0,85).
|
||
|
||
**ALLE DREI vorab fixierten Kriterien verfehlt:**
|
||
· **(b) Haltequote gegen Zufallskontrolle** — eine **willkürliche Linie gleicher
|
||
Distanz** hält in **9 von 10 Zellen genauso gut oder besser** (VWAP 70,3–75,1 %,
|
||
Zufall 73,9–76,6 %). Exakt der Fibonacci-Befund reproduziert: die Frage ist nie
|
||
„hält das Level?", sondern „hält es BESSER als eine beliebige Linie?".
|
||
· **(c) Ertrag** — k=2,0 kippt in H2 (−0,009), also kippt der **Nachbar** der
|
||
besten Zelle. Und **alle sechs 95-%-Bootstrap-Intervalle enthalten die Null**
|
||
(k=2,5 H2, die beste Zelle: +0,087 [−0,012 … +0,185]).
|
||
· **(a) Beweglichkeit** — ⚠ **die Regel war von mir unterspezifiziert**
|
||
(„≤ Squeeze-Box-Niveau" = 0 ist für ein gleitendes Band unerfüllbar). Gemessen
|
||
rückt das Band im Median nur 0,03–0,05×ATR je Bar, im **90-Perzentil aber bis
|
||
0,34×ATR** — mehr als die Einstiegstoleranz von 0,15. Eine ruhende Order müsste
|
||
also laufend nachgezogen werden: dasselbe operative Problem wie bei `_pend`.
|
||
|
||
✅ **Kohärenz-Kontrolle:** der **Ausbruch** an den Bändern ist spiegelbildlich
|
||
negativ (H1 −0,107…−0,230 über alle k). Reversion leicht positiv, Ausbruch klar
|
||
negativ — ein stimmiges Bild, kein Zufallsrauschen.
|
||
**24. verworfener Signal-Eingriff.** Damit ist aus dem eingereichten
|
||
Profi-Werkzeugkasten **keine ungetestete Zutat mehr offen.**
|
||
|
||
## ⚠⚠ VERLUSTBEGRENZUNG BEI „FALSCHEN" EINSTIEGEN — BEIDE IDEEN GEMESSEN, BEIDE DURCHGEFALLEN (2026-08-07)
|
||
|
||
User: „um meine Verluste bei falschen Einstiegen zu begrenzen würde ich gerne den
|
||
Notfall-Close automatisch setzen — 30 %?" und „oder doch lieber ein Flip-Close
|
||
bei entgegengerichtetem Trend?"
|
||
|
||
**(1) AUTOMATISCHER NOTFALL-STOP — 30 % wäre ohnehin wirkungslos gewesen.**
|
||
Maßstab zuerst: Einsatz **670 €/Lot**, Median-Verlust **20 €/Lot = 3 % der
|
||
Margin**. 30 % (=201 €/Lot) erreichen **1 %** aller Verluste.
|
||
**Kontrafaktisch über 1.022 echte Trades** (MAE aus M5-Bars zwischen Ein- und
|
||
Ausstieg; 87 €/$/Lot als Median aus den Trades selbst geschätzt):
|
||
| Schwelle | gefeuert | Σ P&L | Δ gegen IST |
|
||
|---|---|---|---|
|
||
| **ohne Stop (IST)** | — | **+358 €** | — |
|
||
| 3 % | 492 | +65 € | −293 € |
|
||
| 5 % | 317 | −280 € | −639 € |
|
||
| 8 % | 165 | +345 € | −14 € |
|
||
| 15 % | 70 | −58 € | −416 € |
|
||
| 30 % | 28 | +27 € | −331 € |
|
||
**Keine Schwelle schlägt „gar kein Stop".** Nach Einstiegsart (Δ gegen IST):
|
||
mit Signal −162…−278 · gegen Signal −69…−148 · **ohne Signal −292 / +298 / +290
|
||
/ −106** (5/8/10/15 %). Die +298 sehen gut aus, aber **beide Nachbarn kippen** →
|
||
Rauschen, dieselbe Regel, an der ORB und die SMC-Sweeps gescheitert sind.
|
||
|
||
**(2) FLIP-CLOSE AUF DEN HTF-TREND — war nie gemessen, jetzt schon
|
||
(`backtest_flipclose_htf.py`).** Abzugrenzen vom Flip auf das
|
||
**Empfehlungs**-Signal (gemessen, am 05.08. abgeschaltet): der HTF ist träger,
|
||
dreht seltener, und als FILTER ist er belegt (Edge ×2). Als EXIT nicht:
|
||
| Variante | H1 ΣR (Δ) | H2 ΣR (Δ) |
|
||
|---|---|---|
|
||
| **BASIS (kein Flip)** | **−548** | **−154** |
|
||
| M30 dagegen, ab +0,0R | −629 (−80) | −247 (−93) |
|
||
| M30 dagegen, ab +0,5R | −574 (−25) | −234 (−79) |
|
||
| M30 dagegen, ab +1,0R | −549 (−0) | −183 (−29) |
|
||
| M30 dagegen, auch im Minus | −616 (−68) | −327 (−172) |
|
||
| M30+H1, ab +0,5R | −570 (−22) | −222 (−67) |
|
||
| M30+H1, auch im Minus | −616 (−68) | −290 (−136) |
|
||
**Alle sechs Varianten in BEIDEN Hälften schlechter.** Die „mildeste" ist die,
|
||
die kaum feuert (ab +1,0R, Δ −0/−29) — exakt das Muster der 15-Minuten-Regel.
|
||
✅ **Kontrolle:** die Basis (−548/−154) reproduziert `backtest_flipclose2.py`
|
||
(−537/−140) — die Pipeline ist in Ordnung.
|
||
⚠ Methodik: **feste, geteilte Entry-Liste** über alle Varianten.
|
||
`backtest_flipclose2.py` läuft nach jedem Exit bei `xb+1` weiter, wodurch jede
|
||
Variante eine ANDERE Trade-Folge bekommt (der am 31.07. bei
|
||
`backtest_exit_combo.py` korrigierte Fehler). Hier von vornherein vermieden.
|
||
|
||
⚠⚠ **DER MECHANISMUS IST IN ALLEN FÄLLEN DERSELBE: Trefferquote STEIGT
|
||
(38→44 %, 42→51 %), Ertrag FÄLLT = Gewinner-Kappen.** Ein Früh-Ausstieg kappt
|
||
einige Trades vor dem vollen SL, schneidet aber weit öfter Trades ab, die sich
|
||
erholt hätten. Damit ist es die **fünfte** unabhängige Messung dieser Klasse
|
||
(15-Minuten-Regel · `backtest_adverse_nosignal` · Flip auf das Signal ·
|
||
Notfall-Stop · Flip auf den HTF-Trend) — **alle negativ.**
|
||
|
||
✅✅ **UMGESETZT auf User-Entscheidung (2026-08-07): `auto_emergency_margin_pct
|
||
= 8`** — bewusst **gegen** die Messung, wie beim Auto-Squeeze. Gewählt wurde die
|
||
**mildeste** Variante: Δ **−14 €** über 1.022 Trades ist praktisch neutral, sie
|
||
feuert bei ~16 % der Trades. Sie schützt wenig und kostet wenig — eine
|
||
Versicherung mit kleiner Prämie, die im schlechten Regime hilft und im guten
|
||
kostet. **Sizing bleibt bei 95 %** (ausdrücklich nicht mitgeändert).
|
||
Backup `oil_widget_config.ini.bak-2026-08-07-emergency`.
|
||
⚠ **Bezugsgröße ist die MARGIN DIESER Position, nicht das Konto** — bei ~670 €/Lot
|
||
sind 8 % rund **54 €/Lot** (bei den aktuellen 2,33 Lots ≈ **124 €**). Der Modus
|
||
hat **Vorrang** vor `auto_emergency_pct` (Konto-%) und `auto_emergency_loss`
|
||
(Fixwert); beide bleiben 0. Der Vertipper-Schutz `emergency_min_eur=3` gilt nur
|
||
für manuelle UI-Eingaben, nicht für diesen Pfad.
|
||
⚠⚠ **AKTIVIERUNG braucht einen NEUSTART** (`_emergency_margin_pct` lebt im
|
||
Speicher) — **und die bereits offene Position bekommt ihn auch danach NICHT.**
|
||
Grund (verifiziert an `engine.py:884`): der Wiederherstellungs-Zweig setzt für
|
||
ein wiedererkanntes Ticket `_emergency_margin_armed_ticket = ticket` und
|
||
`_emergency_loss` auf den persistierten Wert (hier `null`) — bewusst so, damit
|
||
ein Neustart keine gesetzte Entscheidung überschreibt (Fix 2026-07-17). **Der
|
||
8-%-Stop greift also ab der NÄCHSTEN neuen Position.** Daraus folgt: den
|
||
Neustart am besten fahren, wenn das Konto **flat** ist — er nützt der laufenden
|
||
Position ohnehin nichts und vermeidet das Risikofenster.
|
||
✅ Neu im **Config-Wächter** (`measurement_reminder.py`, validiert = `0`), damit
|
||
die Abweichung sichtbar bleibt. **Zurück: `auto_emergency_margin_pct = 0`.**
|
||
|
||
✅ **Was stattdessen belegt ist:** der Verlust-Hebel liegt in der **SL-Breite**
|
||
(live 2,0×ATR_M15 ≈ 3,6×ATR_M5, Worst bis −5,6×ATR; per Broker-SL geschlossene
|
||
Trades hätten mit dem kanonischen Exit **1.229 €** weniger verloren) und damit
|
||
im **Sizing**, nicht im Exit. Gegen „falsche Einstiege" ist die gemessene Bremse
|
||
das **Veto** (gegen das Signal −6,60 €/Lot, beidhälftig) — es steht bereits im
|
||
Order-Dialog. **Nichts gebaut.**
|
||
|
||
## ⚠⚠ DIE PRÜFUNG HING AM COMMIT, NICHT AM DEPLOYMENT — behoben 2026-08-07
|
||
|
||
User-Frage „wird die Pipeline bei jedem Deployment ausgeführt?". **Antwort war
|
||
NEIN**, und die Lücke war größer als sie aussah:
|
||
| Weg zum Deployment | Prüfung vorher |
|
||
|---|---|
|
||
| `git commit` | ✅ `check_nfalle` + `pytest` (Hook installiert) |
|
||
| `tools/deploy.py` | ⚠ prüfte nur den **laufenden Server** — nicht den Code |
|
||
| `restart_server.bat` | ❌ gar nichts |
|
||
| Aufgabe `OilTradingServer` (Logon/Reboot) → `start_server_hidden.vbs` | ❌ gar nichts |
|
||
**Commit und Deployment sind zwei Ereignisse, und sie fallen beliebig
|
||
auseinander:** eine geänderte, nie committete Datei ging über
|
||
`restart_server.bat` ungeprüft live; nach einem Reboot startet die Aufgabe, was
|
||
auf der Platte liegt.
|
||
⚠ **„Der Server kommt hoch" ist KEIN Ersatz für die Prüfung** — die beiden
|
||
`log.`-`NameError` in `core/trader.py` sassen **Monate** in `except`-Zweigen
|
||
(einer seit dem Initial-Commit), und der Server startete jedes Mal sauber.
|
||
|
||
✅ **GEBAUT: `tools/deploy.py` bekommt Schritt 0** — `check_nfalle` + `pytest`
|
||
**VOR** dem Kill; bei Rot Abbruch mit Rückgabecode 1. Die Reihenfolge ist der
|
||
Punkt: ein laufender, funktionierender Server darf für kaputten Code nicht
|
||
abgeschossen werden. Notausgang `--ohne-pruefung` (Philosophie wie
|
||
`git commit --no-verify`).
|
||
✅ **Beide Richtungen verifiziert:** mit eingebautem `F821`-Fehler → „❌ ABBRUCH",
|
||
Exit **1**, und die **Server-PID vorher/nachher identisch** (20892) — der
|
||
laufende Prozess wurde nachweislich nicht angefasst; ohne Fehler läuft es durch.
|
||
|
||
✅ **AUCH `restart_server.bat` prüft jetzt** (Korrektur am selben Tag): mein
|
||
erster Entwurf hatte es als „Wiederanlauf-Pfad" ausgenommen — **falsch**. Die
|
||
Aufgabenplanung startet `start_server_hidden.vbs` → **`start_server.bat`**;
|
||
`restart_server.bat` wird von **nichts** automatisch aufgerufen (geprüft) und
|
||
ist damit ein **absichtliches Deployment**. Es prüft jetzt vor dem Kill,
|
||
Notausgang `restart_server.bat --ohne-pruefung`.
|
||
⚠⚠ **NUR `start_server.bat` bleibt ungeprüft** — das ist der echte
|
||
**Wiederanlauf**-Pfad (Reboot/Logon). Würde ein fehlschlagender Test dort den
|
||
Start verhindern, stünde eine offene Position ohne Trailing, Time-Stop und
|
||
Notfall-Stop da — nur mit dem Broker-SL. **Eine Prüfung, die den Bot offline
|
||
lässt, ist schlimmer als der Fehler, den sie sucht.**
|
||
**Regel: absichtliches Deployment blockieren, Wiederanlauf niemals.**
|
||
|
||
⚠⚠ **ZWEI cmd-FALLEN DABEI, beide nur durch den POSITIV-Test gefunden:**
|
||
**(1)** `if errorlevel 1` **innerhalb eines geklammerten `if/else`-Blocks** wird
|
||
zur **PARSE-Zeit** ausgewertet, nicht zur Laufzeit — der erste Entwurf brach
|
||
deshalb auch bei **sauberem** Code ab, der Neustart hätte **nie mehr
|
||
funktioniert**. Behoben mit **Sprungmarken statt Klammer-Blöcken**.
|
||
**(2)** `echo ... ^(x^)` maskiert im Block anders → „Der Befehl [0] ist …
|
||
falsch geschrieben". Text jetzt ohne runde Klammern.
|
||
⚠ **Der Negativ-Test allein sah in BEIDEN Fällen völlig in Ordnung aus** —
|
||
kaputter Code → Abbruch, Server unangetastet, Exit 1. Erst der Positiv-Test
|
||
(sauberer Code muss DURCHLAUFEN) hat es entlarvt. **Eine Sperre muss man in
|
||
beide Richtungen prüfen; „blockiert korrekt" ist nur die Hälfte.**
|
||
✅ Alle drei Zweige verifiziert (sauber → weiter/rc 0 · kaputt → Abbruch/rc 1 ·
|
||
`--ohne-pruefung` → weiter/rc 0), gegen eine **Attrappe**, deren Kill/Start
|
||
durch ein `echo` ersetzt ist — so liess sich der Durchlauf-Zweig prüfen, ohne
|
||
den laufenden Server anzufassen.
|
||
⚠ Auch der Testaufbau selbst hatte einen Fehler: die Attrappe lag zuerst in
|
||
`%TEMP%`, dort macht `cd /d "%~dp0"` das Temp-Verzeichnis zum Arbeitsordner und
|
||
`%~dp0tools\pre_commit.py` existiert nicht → scheinbarer Fehlschlag. **Der Test
|
||
gehört ins Projektverzeichnis.**
|
||
|
||
## ✅ LIVE↔BACKTEST-VERGLEICH IST JETZT TEIL DER PIPELINE (2026-08-07)
|
||
|
||
Konsequenz aus dem Messfehler direkt darunter: der Vergleich lief nur, wenn
|
||
jemand daran dachte — und als er lief, war er falsch gebaut. Beides behoben.
|
||
|
||
**(1) `core/stichprobe.py` — die Prävention ist KONSTRUKTIV, nicht ermahnend.**
|
||
`entkoppeln()` · `anteil_ci()` · `unterscheidbar()` · `schluessel_level_stunde()`.
|
||
⚠ **Regel: vor jeder berichteten Rate entkoppeln, jede Aussage über einen
|
||
Unterschied mit Konfidenzintervall belegen.** Ein Punktschätzer ist auf
|
||
`pbreak_predictions`/`recommendations` keine Aussage — die Zeilen sind nicht
|
||
unabhängig. 8 Tests, darunter einer, der die reale Konstellation vom 07.08.
|
||
nachspielt (roh 77 %, entkoppelt 33 %).
|
||
⚠ Fallstrick beim Bau, gefunden weil ich es laufen liess: ein sqlite3-Cursor
|
||
liefert **Tupel**, keine dicts (`TypeError: tuple indices must be integers`) —
|
||
die Feldangabe akzeptiert deshalb Namen **und** Spaltennummern.
|
||
|
||
**(2) `analyze_divergence.sec_d0` war selbst der Fehler — behoben.** Es mittelte
|
||
`AVG(p_break)` über ALLE Rohzeilen und flaggte bei Δ > 8 Pp.
|
||
| | vorher | jetzt |
|
||
|---|---|---|
|
||
| Grundmenge | 1.058 rohe Zeilen | **110 entkoppelte** |
|
||
| Urteil | Δ +8,7 Pp → **ALARM** | 37,6 % liegt in **[37,3 … 55,5] %** → **OK** |
|
||
Der Wächter hätte heute also einen Fehlalarm produziert — genau den, dem ich
|
||
elf Hypothesen lang nachgegangen bin.
|
||
|
||
**(3) NEU: Abschnitt E „GLEICHES FENSTER"** (`--backtest`). Rechnet den Backtest
|
||
über **genau den Live-Zeitraum** und hält ihn gegen das Intervall der
|
||
entkoppelten Live-Rate. Erster Lauf: live 46,4 % [37,3 … 55,5] gegen Backtest
|
||
**43,9 %** → im Intervall, kein Befund. ⚠ Braucht MT5 und ~1–2 min, deshalb
|
||
opt-in — der Rest des Wächters bleibt DB-only und schnell.
|
||
|
||
**(4) EINGEBAUT IN DEN WOCHENREPORT** (`weekly_review._divergenz_section`,
|
||
montags 07:30 per Mail + Telegram). CLAUDE.md nannte den Wächter seit dem 31.07.
|
||
als „Kandidat für den Wochenreport" — jetzt ist er drin, mit demselben
|
||
Fail-safe wie der Squeeze-Abschnitt (schlägt er fehl, fehlt der Abschnitt, der
|
||
Report läuft weiter).
|
||
⚠ **Bewusst NICHT in den pre-commit-Hook**: der braucht MT5 und Minuten; die
|
||
Stufe-1-Prüfung muss bei ~5 s bleiben, sonst wird sie deinstalliert.
|
||
|
||
**Damit deckt die Pipeline drei verschiedene Fehlerklassen ab:**
|
||
| Stufe | schützt gegen | Takt |
|
||
|---|---|---|
|
||
| pre-commit (`check_nfalle` + `pytest`) | Syntax, undefinierte Namen, `\n`-Falle | jeder Commit |
|
||
| Track B (2 Stichproben, Nachbarn, KI) | **Overfitting** — Edge existiert nicht | je Strategie-Idee |
|
||
| **Divergenz-Wächter D0/E** | **Deployment-Drift** — Edge existiert, läuft aber anders | **wöchentlich** |
|
||
| `tools/deploy.py --feld` | Aktivierung ohne Wirkung | jeder Rollout |
|
||
|
||
## ✅⚠⚠ DIE „BASISRATEN-LÜCKE" WAR EIN MESSFEHLER — MEINER (2026-08-07, aufgelöst)
|
||
|
||
Nach elf geprüften Hypothesen ist der Befund: **es gab keinen Fehler im System.**
|
||
Die 14,1 Pp („Backtest 38,6 % gegen live 52,7 %") entstanden dadurch, dass ich
|
||
**eine geclusterte 6-Tage-Stichprobe gegen ein 208-Tage-Mittel** gehalten habe.
|
||
| Ursache | Beitrag |
|
||
|---|---|
|
||
| **Zeitraum** — Backtest auf dieselben Tage (03.–07.08.) eingeschränkt: 38,6 → **43,4 %** | **+4,8 Pp = 34 %** |
|
||
| **Clusterung** — live entkoppelt (max. 1 je Level und Stunde): 52,7 → **45,2 %** | **−7,5 Pp = 53 %** |
|
||
| Rest | +1,8 Pp |
|
||
**Entkoppelt: live 45,2 % [95-%-KI 35,5 … 54,8] gegen Backtest 43,4 % — der
|
||
Backtest-Wert liegt IM Intervall. Kein belegbarer Unterschied.**
|
||
|
||
⚠⚠ **Das ist exakt der Fehler, den ich am selben Tag in der Fälligkeitsbedingung
|
||
korrigiert hatte** (dort zählte sie 800 ROHE statt entkoppelte Vorhersagen) —
|
||
und ich habe ihn in der Auswertung selbst noch einmal gemacht. **Regel: bei
|
||
`pbreak_predictions` IMMER entkoppeln, bevor irgendeine Rate berichtet wird.**
|
||
Dieselbe Klasse wie die Bootstrap-Grenze bei `ci95` (überlappende Beobachtungen).
|
||
|
||
**WAS DAVON ÜBRIG BLEIBT — und es ist deutlich weniger dramatisch:**
|
||
| Befund | Stand nach der Entkopplung |
|
||
|---|---|
|
||
| Basisrate „gewandert 37,8 → 53,2 %" | ❌ **zurückgezogen** — entkoppelt 45,2 %, Zeitraum-Effekt |
|
||
| Kalibrierungsfehler | ✅ **bleibt**: 14,3 Pp entkoppelt (roh 14,1) — KI **[6,8 … 25,5]**, also klar > 0. Das Modell sagt im Mittel **38,1 %**, real sind es **45,2 %** |
|
||
| „oberste Klasse invertiert" (69 % → 17,2 %) | ⚠ **NICHT belegt** — beruhte auf n=29 ROHEN Zeilen ≈ wenige unabhängige Fälle; entkoppelt bleibt die Klasse unter der Mindestbesetzung |
|
||
| AUC | unverändert **unentschieden** (entkoppelt n=93, KI [0,434 … 0,674]) |
|
||
|
||
✅ **Konsequenz: nichts umbauen.** Es bleibt eine **modeste Unterschätzung von
|
||
~7 Pp** auf einer Stichprobe, die für eine Reparatur zu klein ist — genau
|
||
deshalb steht die Fälligkeitsbedingung jetzt auf **350 entkoppelten**
|
||
Vorhersagen (Stand 93). Vorher ist jede Korrektur Kurvenanpassung an Rauschen.
|
||
|
||
**DIE ELF GEPRÜFTEN HYPOTHESEN** (alle einzeln gemessen, keine trägt):
|
||
| # | Hypothese | Ergebnis |
|
||
|---|---|---|
|
||
| 1 | Niveau/Achsenabschnitt driftet | Roll-Achse fällt live durch |
|
||
| 2 | Regime-Merkmal (ATR/Median) | H2→H1 schlechter |
|
||
| 3 | Vola-Regime treibt Bruchrate | über 5 Quintile **flach** (37,0–39,2 %) |
|
||
| 4 | aktuelles Regime ungewöhnlich | Median 0,96 = normal |
|
||
| 5 | Sammelart (je Berührung ↔ je Bar) | −1,7 Pp, falsche Richtung |
|
||
| 6 | ATR-Floor-Artefakt | +2,8 Pp (⚠ **53,9 %** der Backtest-Bars liegen auf dem Floor, live 5,4 %) |
|
||
| 7 | Level-Auswahl (gemischter Pool) | +0,4 Pp — **echter Bug, s. u., aber nicht die Ursache** |
|
||
| 8 | Berührung per Close statt Bar-Spanne | −3,9 Pp, falsche Richtung |
|
||
| 9 | Timeout zählt als Abprall | nur **2,3 %** im Backtest |
|
||
| 10 | Auswertungs-Auflösung M1 ↔ M5 | M5 **überschätzt** Brüche um +8,0 Pp (falsche Richtung) |
|
||
| 11 | Richtung (Signal ↔ diskretionär) | 38,0 / 37,6 / 37,5 % — **irrelevant** |
|
||
✅ **Zwei Kontrollen, die die Werkzeuge bestätigen:** mein M1-Nachspiel
|
||
reproduziert die Live-Auswertung zu **100 %** (die Auswertung ist korrekt), und
|
||
`pb_levels30` ist live befüllt (ph=6/pl=5) — der dokumentierte M5-Fallback ist
|
||
**nicht** aktiv.
|
||
⚠ Der ATR-Floor-Befund bleibt als Nebenwirkung bemerkenswert: **53,9 % aller
|
||
Backtest-Bars sitzen auf dem Floor 0,12**, live nur 5,4 %. Der Backtest-Zeitraum
|
||
ist also viel ruhiger als die Live-Woche — genau die Verzerrung, die den
|
||
Zeitraum-Effekt (Punkt „Zeitraum" oben) erzeugt.
|
||
|
||
## ⚠⚠ LEVEL-AUSWAHL: NACHBAU BEHOBEN (2026-08-07)
|
||
|
||
Auftrag „behebe das" zum benannten nächsten Schritt: die Backtests bauten die
|
||
Live-Level-Auswahl nach, statt sie zu benutzen.
|
||
|
||
✅ **DREI ECHTE ABWEICHUNGEN GEFUNDEN — und die erste ist ein Lehrstück:**
|
||
| # | Nachbau (`backtest_pbreak_retrain.py`) | LIVE (`_draw_levels`) |
|
||
|---|---|---|
|
||
| 1 | clustert den **GEMISCHTEN** Rohpool (`sorted(ch+cl)`) | ph und pl **GETRENNT**, dann zusammenführen |
|
||
| 2 | keine Rundung | `round(…, 3)` je Linie |
|
||
| 3 | EINE Hysterese je Trade-Richtung | Widerstand und Unterstützung **getrennt** |
|
||
⚠⚠ **Punkt 1 ist genau der Fehler, den der Live-Code als BEHOBEN dokumentiert:**
|
||
„ph/pl bleiben GETRENNT geclustert (**nicht vorher zusammenlegen!**) — ein erster
|
||
Versuch clusterte den kombinierten Rohpool direkt, was in dichten Zonen zu
|
||
**Ketten-Mega-Clustern** führte." Der Nachbau machte also weiter das, was live
|
||
erkannt und repariert worden war. **Gemessen: 4,5 % aller Zeitpunkte hatten einen
|
||
anderen Widerstand** (13.283 Vergleichspunkte, Ø-Linienzahl 8,1 statt 8,4).
|
||
|
||
✅ **GELÖST: `core/sr_levels.py`** (`cluster` · `linien` · `naechste` · `halten`).
|
||
`engine._draw_levels` ruft es auf; **Verhalten bitgenau unverändert**
|
||
(`tests/test_sr_levels.py`, 6 Tests über je 300–400 Zufallsfälle inkl. Hysterese
|
||
in beide Richtungen). ⚠ Der Test enthält bewusst auch die **Gegenprobe**, dass der
|
||
Nachbau wirklich abwich — ohne sie wäre nicht belegt, dass es etwas zu beheben gab.
|
||
⚠ `backtest_pbreak_retrain.build_levels` bleibt **unverändert** (Migrations-Regel:
|
||
seine Zahlen und die daraus gefitteten Live-Gewichte `_PB_*` dürfen sich nicht
|
||
rückwirkend verschieben). Daneben steht jetzt **`build_levels_live`** — **neue Fits
|
||
gehören dorthin**, zusammen mit `core.sr_levels.linien` statt `_cluster`.
|
||
|
||
⚠⚠ **ABER: DIE KORREKTUR SCHLIESST DIE LÜCKE NICHT — +0,4 Pp.**
|
||
| | Basisrate |
|
||
|---|---|
|
||
| Nachbau (gemischter Pool) | 38,0 % |
|
||
| korrigiert (live-treu) | **38,3 %** |
|
||
| **LIVE ab 01.08.** | **52,7 %** |
|
||
|
||
⚠⚠⚠ **FÜNF HYPOTHESEN, FÜNF WIDERLEGUNGEN — die 15-Pp-Lücke ist OFFEN:**
|
||
| Hypothese | Ergebnis |
|
||
|---|---|
|
||
| Niveau/Achsenabschnitt driftet | Roll-Achse fällt live durch, oberste Klasse bleibt invertiert |
|
||
| Vola-Regime | Bruchrate über 5 Quintile **flach** (37,0–39,2 %) |
|
||
| Sammelart (je Berührung ↔ je Bar) | **−1,7 Pp**, falsche Richtung |
|
||
| **ATR-Floor-Artefakt** | **+2,8 Pp**; bei live-gleichem ATR (≥0,20) erst **40,1 %** |
|
||
| **Level-Auswahl (dieser Fix)** | **+0,4 Pp** |
|
||
⚠ Der ATR-Befund ist trotzdem bemerkenswert: **53,9 % aller Backtest-Bars sitzen
|
||
auf dem Floor 0,12**, live nur **5,4 %** — der Backtest-Zeitraum ist also viel
|
||
ruhiger als die Live-Woche. Der ATR selbst stimmt dagegen überein (Median live
|
||
0,2167 gegen letzte 7 Tage 0,2174, Verhältnis **1,00**).
|
||
|
||
**Stand: der Fix ist richtig und gebaut, erklärt den Live-Fehler aber nicht** —
|
||
✅ **und das musste er auch nicht: die Lücke war ein Messfehler in meiner eigenen
|
||
Auswertung** (s. Abschnitt darüber). Der Bug im Nachbau ist davon unberührt echt
|
||
und bleibt behoben.
|
||
⚠ (erledigt) Die M1-gegen-M5-Auflösung bei der
|
||
Ausgangsbestimmung (live wertet gegen `candles_m1` aus, der Backtest gegen
|
||
M5-Bars — innerhalb eines M5-Bars prüft der Backtest `confirm` VOR `reject`,
|
||
was ihn *zugunsten* von „break" verzerrt, also in die falsche Richtung), und die
|
||
Frage, ob `wave.pb_levels30` live überhaupt dieselben Pivots liefert wie
|
||
`build_levels`. **Beides nicht gemessen.**
|
||
⚠ Weiterhin **kein Notfall**: `auto_sr_close = false`, das Modell schliesst keine
|
||
Trades.
|
||
|
||
## ⚠⚠ LIVE-CODE HING AN EINEM BACKTEST-SKRIPT — behoben 2026-08-07
|
||
|
||
**Befund:** `core/engine.py` (`squeeze_monitor`) und `weekly_review.py` importierten
|
||
**`backtest_breakout_squeeze`**. Die Abhängigkeitsrichtung war damit verkehrt herum:
|
||
„Backtests sind nur Dokumentation" stimmte nicht — ein Fehler dort konnte den
|
||
laufenden Bot treffen.
|
||
|
||
⚠ **Der schwerere Teil war nicht der Import, sondern wie die Parameter hineinkamen:**
|
||
```python
|
||
BQ._N = _SQ_N; BQ._K = _SQ_K # Modul-Globale eines Backtests mutieren
|
||
```
|
||
Das wirkt **prozessweit** und damit auf jeden anderen Nutzer desselben Moduls im
|
||
selben Prozess. Dass die Werte real abweichen, ist keine Theorie: der Backtest-Default
|
||
ist `_N=12`, live gilt `_SQ_N=**10**`. Wer das Skript nach dem Monitor-Aufruf im selben
|
||
Prozess benutzt hätte, hätte still mit Live- statt Backtest-Parametern gerechnet.
|
||
|
||
✅ **Gelöst: `core/squeeze_scan.py`** — `atr_series` · `exit_legacy` · `scan`, alle
|
||
Parameter in der **Signatur** statt in Modul-Globalen. `engine`/`weekly_review` rufen
|
||
`core` auf; `backtest_breakout_squeeze.py` leitet nur noch weiter, damit es **eine**
|
||
Implementierung gibt statt zweier, die auseinanderdriften (der Fehler, den das Projekt
|
||
bei `sim_run`/`_ema_series` — Kopien in ≥6 Skripten — schon einmal gemacht hat).
|
||
|
||
⚠⚠ **DER EXIT WURDE BEWUSST NICHT MITREPARIERT.** `exit_legacy` ist eine bitgenaue
|
||
Kopie inklusive seiner Abweichungen vom kanonischen Live-Exit (`exit_model.LIVE`):
|
||
| | Squeeze-Backtest / B4-Monitor | LIVE |
|
||
|---|---|---|
|
||
| Trail | **1,5** | **1,0** (seit 31.07.) |
|
||
| Lock-Phase | keine | ja |
|
||
| Time-Stop | keiner | 120 min |
|
||
| Initial-TP | keiner | 3,5×ATR |
|
||
| max_hold | 288 | 200 |
|
||
**Das ist ein FÜNFTES Exit-Modell** neben den drei aus dem Stufe-3-Refactor und dem
|
||
kanonischen. Der B4-Monitor („Entry-Edge · Mechanik") misst also gegen einen
|
||
**veralteten** Exit. Das hier stillschweigend mitzuändern hätte die Vergleichsgrundlage
|
||
des Monitors verschoben, ohne dass es jemand merkt — **offener Befund, eigene
|
||
Entscheidung.** ⚠ Beim Umstellen ist die Erwartungsspanne (+0,14…+0,23 ØR) mit
|
||
neu zu bestimmen; sie stammt aus Messungen mit genau diesem Exit.
|
||
|
||
✅ **Bitgenauigkeit BEWIESEN, nicht behauptet** (`tests/test_squeeze_scan.py`, 5 Tests):
|
||
die alte Fassung liegt **eingefroren** im Test (wörtlich aus `git show HEAD:…`) und
|
||
läuft gegen die neue über **500 synthetische Kursreihen × 3 Multiplikatoren**.
|
||
⚠ Ein Vorher/Nachher-Lauf des Backtests wäre **ungültig** gewesen: er zieht seine Bars
|
||
live aus MT5, das Fenster verschiebt sich mit jeder Minute — gleiche Zahlen wären
|
||
Zufall, verschiedene kein Beleg. Die Reihen wechseln blockweise die Vola, sonst liegt
|
||
der ATR immer über oder immer unter `_ATRMIN` und Kompressions-Bedingung, Filter und
|
||
Cooldown werden nie durchlaufen. Zusätzlich prüft der Test, dass überhaupt >500
|
||
Ausbrüche entstehen — „beide finden nichts" ist kein Nachweis von Gleichheit.
|
||
✅ **Der Test wurde selbst geprüft (Mutationsprobe):** 6 gezielte Fehler in
|
||
`squeeze_scan.py` eingebaut — Trail 1,5→1,4 · Breakeven 1,3→1,2 · Cooldown +1 Bar ·
|
||
Ausbruchs-Level +0,1 % · ATR-Fenster +1 Bar schlagen **alle** an. Der sechste
|
||
(`d > 0` → `d >= 0`) nicht — **kein Test-Loch, sondern beweisbar äquivalent**: `d` ist
|
||
im ganzen Pfad nur ±1, die beiden Vergleiche unterscheiden sich allein bei `d == 0`.
|
||
⚠ Ein grüner Test ohne Mutationsprobe belegt nichts — dieselbe Fehlerklasse
|
||
(„Erfolg melden, wo nichts geprüft wurde") ist am 07.08. viermal aufgetreten.
|
||
|
||
Live-Gegenprobe auf 9.000 M5-Bars mit den echten Live-Parametern: n=112, Treffer 44 %,
|
||
**ØR +0,178, PF 1,26** — im dokumentierten Erwartungsband.
|
||
⚠ **Aktivierung steht aus:** zum Zeitpunkt des Umbaus lief eine offene Position
|
||
(2,47 Lots); ein Neustart hätte ihr 10–20 s Trailing/Time-Stop/Notfall-Stop genommen
|
||
für eine Änderung, die per Test **nichts** am Verhalten ändert. Greift beim nächsten
|
||
regulären Neustart.
|
||
|
||
## Backtest-Audit 2026-08-04 — rechnen die Skripte noch korrekt?
|
||
|
||
**Anlass:** User-Frage nach dem `angle=`-Fund in `backtest_auto_signal.py`. Prüfung
|
||
aller **87** Backtest-/Analyse-Skripte.
|
||
|
||
**Lauffähigkeit: ✅ in Ordnung.** Alle 87 kompilieren; 12 repräsentative (alle, die
|
||
`WaveRecommender` konstruieren) laufen fehlerfrei. Die heutigen `wave_rec`-Änderungen
|
||
(`_reversal_enabled`) brechen nichts — der Modul-Default `True` hält die Altmessungen
|
||
stabil. ⚠ Drei Skripte nehmen **`[TF] [N]`** statt einer Bar-Zahl als erstes Argument
|
||
(`backtest_signal.py`, `backtest_pullback.py`, `backtest_htf_filter.py`) — mit einer
|
||
Zahl als TF quittieren sie mit `KeyError`. Kein Defekt, nur die Signatur beachten.
|
||
|
||
**Der `angle=`-Befund: 11 Skripte, nicht 26.**
|
||
⚠ **Methodik-Fehler bei mir selbst, korrigiert:** Die erste Prüfung per Regex
|
||
(`_build\([^)]*angle\s*=`) meldete **26** Treffer — die Zeichenklasse `[^)]*` bricht an
|
||
der ersten Klammer ab, sodass Aufrufe wie
|
||
`w._build(..., htf_trend=m30s(T[i]), angle=ang)` fälschlich als „ohne Winkel" galten.
|
||
Die AST-Prüfung ergibt **11** echte Fälle (und erkennt zusätzlich Aufrufe, die `angle`
|
||
positional übergeben). **Regex ist für Aufruf-Analysen das falsche Werkzeug.**
|
||
Betroffen: `backtest_angle.py:78` · `backtest_auto_signal.py:177` ·
|
||
`backtest_confluence.py:144` · `backtest_dist.py:63` · `backtest_exit.py:150` ·
|
||
`backtest_htf_filter.py:106` · `backtest_improve.py:91` · `backtest_pullback.py:89` ·
|
||
`backtest_rev_exit.py:72` · `backtest_signal.py:91` · `backtest_stretch.py:73`.
|
||
|
||
**Wie schlimm ist es? Gemessen über 37.577 M5-Bars:**
|
||
| | mit Reversal (= bis 03.08.) | **ohne Reversal (= live seit 04.08.)** |
|
||
|---|---|---|
|
||
| Signal weicht ab | **19,1 %** | **1,1 %** |
|
||
| Ø-Konfidenz-Differenz | +7,7 | −4,4 |
|
||
| über dem 55 %-Gate | 79,4 → 90,6 % | 79,4 → 78,5 % |
|
||
| WARTEN-Anteil (ohne HTF) | — | 20,6 → 21,5 % (+1 Pp) |
|
||
⚠ **Das Reversal-Abschalten von heute hat die Lücke nebenbei fast geschlossen.** Solange
|
||
das Reversal aktiv war, sahen diese 11 Skripte eine Signal-Population, die sich in jedem
|
||
fünften Bar unterschied und systematisch 7,7 Konfidenzpunkte danebenlag — das betrifft
|
||
den Großteil der Projekt-Historie. **Ab jetzt sind sie näherungsweise gültig.**
|
||
⚠ Die WARTEN-Zeile ist NICHT mit der 43-%-Referenz vergleichbar: für die Messung wurde
|
||
`htf_trend` nicht übergeben, der M30-Filter (allein ~27 % der WARTEN) fehlt also. Nur
|
||
die **Differenz** (+1 Pp) ist aussagekräftig.
|
||
|
||
**Bewusst NICHT getan:** die 11 Skripte „repariert". Ein `angle=` zu ergänzen ändert
|
||
ihre Zahlen (~1 %) — das wäre laut Migrations-Regel keine Refaktorierung, sondern eine
|
||
Neumessung. Sie bleiben unverändert und reproduzierbar; wo ihre Schlussfolgerung wieder
|
||
relevant wird, gehört sie live-treu **neu gerechnet** (Muster:
|
||
`backtest_auto_signal_norev.py`).
|
||
|
||
## ⚠⚠ TAG P/L WAR FALSCH — Fehlbuchungen beim Schliessen (2026-08-12)
|
||
|
||
User: „überprüfe ob Tag P/L richtig berechnet wird." **Die Formel war richtig, die
|
||
Datenbasis nicht.**
|
||
| | |
|
||
|---|---|
|
||
| Anzeige „TAG P/L" | **+14,26 €** |
|
||
| Broker, realisiert heute | **−74,08 €** |
|
||
| **Abweichung** | **81,71 €, mit falschem Vorzeichen** |
|
||
✅ `realisiert + offen` stimmt bitgenau gegen die DB (0,0000). Der Fehler lag
|
||
darunter: **die DB stimmte nicht mit dem Broker überein.**
|
||
|
||
⚠⚠ **URSACHE — eine Race Condition beim Eröffnen**, im Log im Sekundentakt
|
||
sichtbar (#49926460): `20:04:10 SELL eröffnet` → `20:04:10 „extern geschlossen"`
|
||
→ `20:04:12 „kein OUT-Deal in 1 Deals"` → Fallback bucht **0,00** →
|
||
`20:11:42` der ECHTE Close **lief ins Leere**. Drei Defekte griffen ineinander:
|
||
**(1)** `positions_get` sieht die frische Position einen Tick lang nicht.
|
||
**(2)** `_log_external_close` buchte **trotz** „kein OUT-Deal" einen Close — dabei
|
||
ist genau das der Beweis, dass die Position NOCH OFFEN ist.
|
||
**(3)** `log_trade_close` fasste nur `exit_time IS NULL` an → die Fehlbuchung
|
||
blockierte den echten Close **dauerhaft**.
|
||
Bei #49852362 kostete das **+5,87 statt −95,38 €** und buchte den Trade auf den
|
||
**falschen Tag**.
|
||
|
||
✅ **BEHOBEN:** (1) `_log_external_close` bricht bei „kein OUT-Deal" ab (der
|
||
Fallback bleibt nur für den wirklich blinden Fall, in dem gar keine Deals
|
||
abrufbar sind — dort ist `reconcile_open_trades` das Netz). (2) `log_trade_close`
|
||
darf eine **erkennbare Fehlbuchung** korrigieren — eng gefasst: nur wenn die
|
||
Zeile `closed_by='unknown'` trägt UND die neue Buchung echte Deal-Daten hat.
|
||
✅ **4 Tests** (`tests/test_close_buchung.py`), inkl. der Gegenprobe „eine gute
|
||
Zeile wird NICHT überschrieben" — sonst wäre der Fix schlimmer als der Fehler.
|
||
⚠ Zwei Testfehlschläge waren **meine**: synthetische Zeitstempel (1000/2000) vor
|
||
der Eröffnungszeit lösten die Defensive „exit < entry → jetzt" aus.
|
||
|
||
✅ **ALTLAST REPARIERT** — `tools/repair_closes.py` (Trockenlauf ist Standard,
|
||
Schreiben nur mit `--schreiben`, Backup `…bak-2026-08-12-repair`). Korrigiert
|
||
werden **nur eindeutige Fälle**: `closed_by='unknown'` UND echter OUT-Deal beim
|
||
Broker. **18 Zeilen** über 60 Tage. Heute: von **+7,63 auf −72,67 €**, Restfehler
|
||
**1,41 €**. Gesamt-P&L (`all`): von **−282,78 auf +169,98 €**.
|
||
⚠⚠ **ZEITFALLE dabei, zweimal:** `history_deals_get` filtert nach BROKER-Wallclock
|
||
— `datetime`-Objekte verschieben das Fenster; und `fromtimestamp(d.time, BROKER)`
|
||
rendert **3 h zu spät**. Aufgefallen, weil eine Deal-Zeit „23:11" lautete, das Log
|
||
aber „20:11:42 CLOSE" sagte. ➜ **Epochs übergeben (lokal + Offset), beim Lesen
|
||
`d.time − Offset`.**
|
||
⚠ **20 weitere Trades** weichen um ±1–4 € ab (DB nutzt den Schwebe-P&L im
|
||
Moment des Closes, der Broker den Fill). Die Summe hebt sich fast auf (+1,66 €
|
||
über 19 Trades) → nur berichtet, **nicht** automatisch korrigiert.
|
||
|
||
## ✅✅ NETTO-RECHNUNG AUS DEN ECHTEN STEUER-BUCHUNGEN (2026-08-12)
|
||
|
||
Der seit dem 05.08. dokumentierte Defekt ist behoben. `server._add_net` schaetzte
|
||
`net_pnl = total_pnl − gross_win × 26,375 %` und unterstellte, die Quellensteuer
|
||
sei endgueltig verloren. Sie wird aber taeglich per `Tax settlement` erstattet.
|
||
| Zeitraum | total_pnl | wht (ECHT) | net_pnl | einbehalten / erstattet |
|
||
|---|---|---|---|---|
|
||
| today | −32,72 | −68,93 | **+36,21** | −21,03 / **+89,96** |
|
||
| week | −1.235,26 | −432,88 | −802,38 | −425,71 / +858,59 |
|
||
| **all** | **+209,93** | **+28,93** | **+181,00** | −2.754,56 / **+2.725,63** |
|
||
⚠⚠ **Vorher stand bei `all` −2.449,95 €** — eine Verzerrung von rund **2.630 €**,
|
||
die aus einem profitablen Konto ein verlustreiches machte.
|
||
✅ Die Schaetzung der EINBEHALTENEN Summe war fast exakt (2.620 gegen 2.755);
|
||
falsch war allein die Annahme, sie bleibe weg — real werden **99 %** erstattet.
|
||
|
||
**Umsetzung:** `trader.steuer_buchungen(von, bis)` liest die `WHT #…`- und
|
||
`Tax settlement`-Deals direkt (position_id == 0, damit sauber von den Trade-Deals
|
||
getrennt — kein Doppelzaehlen). Unter `mt5_lock`, **5 min gecacht** (die Abfrage
|
||
ueber lange Zeitraeume braucht Sekunden und gehoert nicht in jeden Aufruf).
|
||
⚠ **Fail-safe:** antwortet MT5 nicht, faellt es auf die alte Schaetzung zurueck —
|
||
das Feld **`wht_quelle`** sagt, welche Zahl drinsteht („echt" / „geschaetzt").
|
||
⚠ **KURZE ZEITRAEUME sind verzerrt, in BEIDE Richtungen:** die Erstattung kommt
|
||
erst am Folgetag. Bei „today" oben ist `wht` sogar **negativ** (−68,93), weil die
|
||
Erstattung von GESTERN heute einging — das Netto liegt damit ueber dem Brutto.
|
||
Das ist korrekt gebucht, aber als Tageszahl irrefuehrend. Das Flag `wht_lag`
|
||
faengt bisher nur die eine Richtung (einbehalten, nichts erstattet).
|
||
⚠ Zeitfalle beachtet: Epochs mit Broker-Offset uebergeben, beim Lesen abziehen.
|
||
|
||
## ✅ STATISTIK-MODUL: ARITHMETIK KORREKT, NETTO-RECHNUNG WEITER DEFEKT (2026-08-12)
|
||
|
||
Gegen unabhängige SQL geprüft, alle drei Zeiträume: **Abweichung 0,00 in jeder
|
||
Zelle** (n · wins+losses+be = n · gross_win − gross_loss = total_pnl).
|
||
| | today | week | all |
|
||
|---|---|---|---|
|
||
| n | 6 | 91 | 1.267 |
|
||
| total_pnl | −72,67 | −1.259,12 | **+169,98** |
|
||
✅ Das Modul rechnet also korrekt — es las nur eine fehlerhafte DB (jetzt repariert).
|
||
|
||
⚠⚠ **Der am 05.08. dokumentierte NETTO-Defekt besteht unverändert — und ist
|
||
grösser als damals gemessen:**
|
||
| | |
|
||
|---|---|
|
||
| Modell-Steuerlast (`gross_win × 26,375 %`) | **2.619,93 €** |
|
||
| **angezeigtes `net_pnl`** | **−2.449,95 €** |
|
||
| ECHT einbehalten / erstattet (90 Tage) | −2.625,89 / **+2.608,83** |
|
||
| **tatsächlich verblieben** | **−17,06 €** |
|
||
✅ Das Modell schätzt die *einbehaltene* Steuer fast perfekt (2.620 gegen 2.626).
|
||
Der Fehler ist die Annahme, sie sei **verloren** — real werden **99 %** erstattet
|
||
(am 05.08. waren es 79 %). Die Anzeige macht damit aus **+153 € rund −2.450 €**.
|
||
✅✅ **ERLEDIGT am 2026-08-12** (Stand dieser Zeile korrigiert am 17.08. — sie
|
||
stand fälschlich noch auf „Noch nicht gebaut"): `trader.steuer_buchungen()` liest
|
||
die echten `WHT #…`/`Tax settlement`-Deals (unter `mt5_lock`, 5 min gecacht),
|
||
`server._add_net` nutzt sie mit Fail-safe auf die alte Schätzung und dem Feld
|
||
**`wht_quelle`** („echt"/„geschätzt"). Ergebnis: `all` von **−2.449,95 auf
|
||
+181,00 €**. Details im Abschnitt „NETTO-RECHNUNG AUS DEN ECHTEN
|
||
STEUER-BUCHUNGEN".
|
||
|
||
## ✅ AUTO-SIGNAL-ENTRY (🎯 SIG) KOMPLETT ENTFERNT (User-Entscheidung 2026-08-12)
|
||
|
||
User: „den Signal-Autotrader brauchen wir im Moment nicht mehr — sollten wir den
|
||
inkl. Button entfernen?" **Ja.** Er war seit dem 01.08. aus und ist auf **beiden**
|
||
Ausführungs-Achsen gemessen negativ:
|
||
| Variante | H1 / H2 |
|
||
|---|---|
|
||
| Markt-Einstieg | negativ in **allen 8** Feldern (`backtest_auto_signal.py` 30.07., `_norev` 04.08.) |
|
||
| ruhende Order, Fill bei BERÜHRUNG | **−0,392/−0,338**, negativ in **allen 6** Feldern (`backtest_signal_touchfill.py` 06.08.) |
|
||
⚠⚠ **Der zweite Punkt gibt den Ausschlag:** die ruhende Order — genau der Umbau,
|
||
der den Squeeze gerettet hat — macht SIG sogar **schlechter als den
|
||
Markt-Einstieg**. Die +0,35/+0,32 aus `backtest_auto_signal_v3.py` galten in
|
||
Variante B (Fill beim Close-Bruch) und waren **Look-ahead**; der Aufschlag betrug
|
||
0,77–0,85 R. Ursache ist strukturell und nicht reparierbar: `_pend` **wandert**
|
||
(neu verankert bei jeder k×ATR-Gegenbewegung, nach Timeout, bei Richtungswechsel)
|
||
— „ein Level ist nur dann ein Ausführungs-Vorteil, wenn es STILLSTEHT".
|
||
✅ Nebenbei entfällt der **offene Churn-Defekt** (4× setzen/stornieren in 76 s bei
|
||
identischem Level, gefunden 06.08., nie behoben).
|
||
|
||
**Entfernt:** `_check_auto_signal` · `set_auto_signal` · der Signal-Zweig in
|
||
`_pending_ziel` · `POST /api/autosignal` · Button 🎯 SIG (HTML + Handler) · die
|
||
vier Snapshot-Felder · `signal_pending_entry` und die `auto_signal*`-Leser.
|
||
**Geblieben:** die **AUTOSIG_\*-Trades in der DB** (Historie), die Backtests (sie
|
||
dokumentieren das Warum), `wave_rec.breakout` (speist `block_reason`) und
|
||
`_confirm_breakout` selbst (das ist das Gate, nicht der Trader).
|
||
Sicherung: **`.removed_backup/auto_signal_2026-08-12.py`** + Git-Historie
|
||
(Vorbild: der Wave-Auto-Trader, 23.07. genauso entfernt).
|
||
|
||
⚠⚠ **DAS RISIKO WAR NICHT SIG, SONDERN DER GETEILTE MANAGER.** Squeeze und Signal
|
||
teilten sich `_pending_ziel` — bewusst, weil zwei getrennte Manager über
|
||
`trader.pending_orders()` die Orders des jeweils anderen sehen und sich
|
||
**gegenseitig stornieren** würden. Deshalb wurde das Squeeze-Verhalten **VOR** dem
|
||
Eingriff in `tests/test_pending_ziel.py` festgenagelt (7 Tests: beide Box-Grenzen ·
|
||
nicht-armed · Guard · Feature aus · kein ATR · fehlende Box · ATR-Skalierung) —
|
||
sie liefen vorher grün und laufen **unverändert** weiter.
|
||
✅ **Zwei Folgefehler fing die Prüfkette sofort:** `ruff F821` meldete ein
|
||
verwaistes `_t` (im entfernten Block definiert, zwei Zeilen später noch benutzt),
|
||
und `py_compile` ein dangling `if` im runtime_state-Laden. Beide behoben.
|
||
✅ **Zwei Tests mitgezogen statt gelöscht:** `test_signal_fill_long` wurde
|
||
**umgedreht** — ein Pending-Fill mit unbekannter Quelle darf jetzt NICHT
|
||
stillschweigend als Squeeze durchgehen (das wäre Deployment-Drift Fall 8 mit
|
||
umgekehrtem Vorzeichen); und die Systemzustand-Zeile prüft nun ausdrücklich, dass
|
||
„SIG" **nicht wieder auftaucht**.
|
||
✅ **Live verifiziert:** alle vier Snapshot-Felder weg, `/api/autosignal`
|
||
antwortet nicht mehr, `auto_squeeze`/`squeeze_pending` unverändert, 67 Tests grün.
|
||
|
||
## ⚠⚠ WIEDEREINSTIEG NACH S/R-CLOSE — 2-HALBJAHRES-BACKTEST: FÄLLT DURCH (2026-08-12)
|
||
|
||
`backtest_srclose_reentry.py`, 80k M5-Bars. Nachfolger des Live-Laufs (n=14, von
|
||
einem Crash-Tag getragen). **Live-treu**: Level über `core.sr_levels.linien`
|
||
(ph/pl GETRENNT geclustert), P(break) mit den **echten** `_PB_*`-Gewichten aus
|
||
`core.engine`, Schliess-Moment = am Level (≤0,15×ATR) UND `p_break < 0,35`,
|
||
Bruch = **Barrieren-Rennen**, Wiedereinstieg als ruhende Stop-Order mit Fill bei
|
||
Berührung, kanonischer Exit, Echtkosten.
|
||
| Bestätigung | H1 ØR / 95-%-KI | H2 ØR / 95-%-KI |
|
||
|---|---|---|
|
||
| **0,3×ATR** | **+0,119** [−0,138 … +0,387] ⚠ | **+0,196** [+0,065 … +0,332] ✅ |
|
||
| 0,5×ATR | −0,043 [−0,289 … +0,212] ⚠ | +0,043 [−0,086 … +0,174] ⚠ |
|
||
| 0,7×ATR | +0,079 [−0,161 … +0,334] ⚠ | +0,014 [−0,110 … +0,134] ⚠ |
|
||
✅ **Bruchquote 32–45 %** — nahe der Basisrate (~38 %). Die Plausibilitätsprüfung
|
||
besteht; die Bruch-Definition ist diesmal richtig.
|
||
|
||
❌ **Die vorab fixierte Regel ist in KEINER Zeile erfüllt.** Bei 0,3 ist der ØR
|
||
zwar beidhälftig positiv, aber H1s Intervall enthält die Null — und der Nachbar
|
||
0,5 dreht H1 ins Negative. Keine Parameter-Robustheit.
|
||
⚠ **Fairerweise:** zwei Hälften, die in dieselbe Richtung zeigen, sind mehr als
|
||
eine — „KI enthält 0" heisst **nicht widerlegt, sondern nicht belegt**. Und H1
|
||
ist dünn (285 Closes gegen 1.168 in H2; der ATR-Floor filtert die Niedrig-Vola-
|
||
Periode stärker).
|
||
✅ **Kohärenter Nebenbefund:** die ENGERE Bestätigung (0,3) ist besser als die
|
||
weiteren — dieselbe Mechanik wie bei `breakout_k` („höheres k = späterer
|
||
Einstieg, Anfang des Moves verschenkt"). Wer auf einen „richtigen" Bruch wartet,
|
||
zahlt ihn.
|
||
|
||
✅ **URTEIL: nicht bauen.** Bedingung für eine Wiedervorlage: die 0,3-Variante
|
||
muss auch in H1 ein Intervall über null liefern — dafür braucht es mehr
|
||
Niedrig-Vola-Daten, nicht mehr Ideen.
|
||
⚠ Nicht modelliert: der Positions-Slot ist während des Wiedereinstiegs belegt.
|
||
|
||
## ⚠⚠ WIEDEREINSTIEG NACH EINEM „FALSCHEN" S/R-CLOSE — gemessen, NICHT gebaut (2026-08-12)
|
||
|
||
User: „falls die Linie trotz Abprall-Berechnung durchbrochen wird — logisch wäre,
|
||
den Trade wieder neu zu starten, oder?"
|
||
⚠ Der Fall ist **kein Randfall**: P(break) liegt live bei 38,7 %, das Gate
|
||
schliesst 94–97 % aller Berührungen — in rund vier von zehn Closes bricht die
|
||
Linie doch. `analyze_srclose_reentry.py`, 43 echte Closes ab dem Nachtrainieren.
|
||
|
||
⚠⚠ **ERST EIN EIGENER MESSFEHLER, und er ist die halbe Lehre.** Der erste Lauf
|
||
meldete **93 % Brüche** bei einer Basisrate von 38,7 %. Ursache: ich prüfte nur
|
||
EINE Barriere („erreicht der Kurs Level +0,5×ATR?"). Das Training entscheidet per
|
||
**Rennen** — Bestätigung *gegen* Ablehnung, wer zuerst kommt. Eine 0,5×ATR-
|
||
Bewegung binnen 60 min passiert fast immer. Mit dem Rennen: **33 % Brüche**,
|
||
praktisch die Basisrate. ✅ Die Plausibilitätsprüfung („die Bruchquote MUSS nahe
|
||
38 % liegen") hat den Fehler gefangen.
|
||
|
||
**Ergebnis (ruhende Stop-Order an der Bestätigungsmarke, kanonischer Exit):**
|
||
| | |
|
||
|---|---|
|
||
| n | 14 |
|
||
| Trefferquote | 71 % |
|
||
| ØR | **+0,859** |
|
||
| **95-%-KI** | **[−0,084 … +1,788]** ⚠ enthält die Null |
|
||
| grob EUR | +501 |
|
||
⚠⚠ **ABER: drei Trades tragen alles.** Die drei grössten Gewinne stammen ALLE vom
|
||
**04.08. 13:06/13:10/13:44** und liegen exakt am TP-Deckel (+3,44/+3,44/+3,45 R):
|
||
| | |
|
||
|---|---|
|
||
| diese drei | **+529,92 €** |
|
||
| **die übrigen 11** | **−28,63 €** |
|
||
Der 04.08. ist der dokumentierte Ausreisser-Tag (WTI −4,12 $, davon −4,16 $ in
|
||
EINER Stunde). **Ohne ihn ist die Regel negativ.**
|
||
|
||
✅ **URTEIL: nicht bauen — aber die beste Idee seit Längerem.** Die vorab fixierte
|
||
Regel (ΣR positiv UND KI ohne Null) ist verfehlt. ⚠ Dazu die dokumentierte
|
||
Gegenevidenz: der **Wiedereinstiegs-Cooldown** (`_SQUEEZE_SR_COOLDOWN_S`=180 s,
|
||
seit 17.07.) wurde gebaut, WEIL ein sofortiger Gleichrichtungs-Wiedereinstieg
|
||
nach einem S/R-Close real schädlich war (1-Sekunden-Roundtrip, doppelter Spread).
|
||
⚠ Fairerweise: das ist nicht dasselbe — gemessen wurde hier der **bestätigte**
|
||
Bruch, nicht der ungegatete Sofort-Wiedereinstieg. Der Cooldown widerlegt die
|
||
Idee also nicht, er warnt nur vor ihrer schlampigen Variante.
|
||
✅ **Der saubere nächste Schritt** ist ein Backtest über 2 Halbjahre statt 43
|
||
Live-Closes — die Maschinerie existiert (`backtest_pbreak_rvalue.py` simuliert
|
||
den P(break)-Close bereits). Erst dann ist die Frage entscheidbar.
|
||
|
||
## ⚠⚠ DAS FLACKERN DER M15-KARTE — Ursache gemessen, Hysterese eingebaut (2026-08-12)
|
||
|
||
Die Karte wechselte den Zustand **alle 12 Sekunden** (~1.030/Tag). Das ist der
|
||
Grund, warum ihre Selbstmessung so stark geclustert war (roh 60 %, entkoppelt
|
||
53 %).
|
||
|
||
⚠⚠ **MEIN ERSTER VERDACHT WAR FALSCH — und die Messung war schnell.** Ich hatte
|
||
das Level im Wechsel-Schlüssel (`round(level, 3)`) im Verdacht. Ausgezählt über
|
||
3.525 Zeilen:
|
||
| Was ändert sich zwischen zwei Zeilen? | Anteil |
|
||
|---|---|
|
||
| **`status`** | 32,9 % |
|
||
| **`lesart`** | 32,7 % |
|
||
| lesart+level | 20,3 % |
|
||
| **level allein** | **7,6 %** |
|
||
Das Level aus dem Schlüssel zu nehmen brächte 3.525 → 3.248 Zeilen — praktisch
|
||
nichts. **Der Zustand selbst zappelt**: `gap ≤ 0,5` und das 45/55-Band von
|
||
P(break) sind stetige Grössen, die um ihre Schwelle pendeln.
|
||
|
||
✅ **Das Projekt hatte die Lösung schon** (`analyze_pbreak_flicker.py`, 14.07.:
|
||
Zeit-Glättung + **Hysterese ±5 Pp**). Die Karte umging sie, weil sie P(break)
|
||
selbst neu rechnet. Jetzt dieselbe Hysterese auf beide Schwellen:
|
||
· **Status**: grün ab `gap ≤ 0,50`, zurück auf gelb erst **über 0,60**
|
||
· **Lesart**: gerichtet erst ausserhalb **40/60**, zurück auf „unklar" erst
|
||
innerhalb **45/55**
|
||
Simuliert an 4.000 Ticks direkt auf den Schwellen: **3.084 → 1.103 Wechsel
|
||
= 64 % weniger.**
|
||
|
||
⚠⚠ **ZWEITER, GRÖSSERER BEFUND: Status und Lesart wurden an DREI Stellen
|
||
unabhängig gebildet** — Frontend, `_log_m15_state` und die Karte selbst. **Sie
|
||
wichen bereits ab**: der Logger kannte den „kein Veto"-Fall des Frontends nicht,
|
||
hat also einen anderen Zustand protokolliert als angezeigt wurde. Genau die
|
||
Divergenz-Falle, die im Projekt schon dreimal zugeschlagen hat (`sim_run` in ≥6
|
||
Skripten · der nachgebaute Level-Cluster · das fünfte Exit-Modell).
|
||
✅ Jetzt **EINE Quelle**: `_m15_setup` liefert `status`/`lesart`/`nah` im
|
||
Snapshot, Logger und Frontend lesen sie. Anzeige und Messung können nicht mehr
|
||
auseinanderlaufen.
|
||
|
||
⚠ **Die alten 3.525 Zeilen sind mit den neuen NICHT vergleichbar** — sie wurden
|
||
unter dem flackernden Regime geschrieben. Die Selbstmessung beginnt faktisch neu;
|
||
das ist kein Verlust, weil sie vorher ohnehin kein Urteil zuliess (KI enthielt
|
||
die 50). Stichtag: 12.08.
|
||
|
||
## ⚠⚠ DIE M15-KARTE HAT SICH ZUM ERSTEN MAL SELBST GEMESSEN (2026-08-12)
|
||
|
||
Anlass: User-Frage „könntest du mir auch eine **M5** · Setup-Bereitschaft bauen?
|
||
Ist das sinnvoll?" — die Antwort hängt daran, ob die BESTEHENDE Karte trägt.
|
||
Seit dem 08.08. misst sie sich selbst (`m15_states`), 2.346 ausgewertete Zustände.
|
||
|
||
⚠⚠ **DIE KARTE ZEIGTE 60 % — UND DAS WAR IRREFÜHREND.**
|
||
| | |
|
||
|---|---|
|
||
| Anzeige (roh, letzte 200 Zeilen) | **60 %** |
|
||
| **entkoppelt** (max 1 je Level+Stunde) | **53–54 %**, KI **[46 … 63 %]** ⚠ enthält 50 |
|
||
| davon **grün + gerichtet** (der handlungsrelevante Zustand) | **45,5 %** |
|
||
Zwei unabhängige Gründe:
|
||
**(1) Geclustert.** `m15_accuracy` nahm `ORDER BY id DESC LIMIT 200` ohne
|
||
Entkopplung. Der Zustand wechselt **~990×/Tag** — 200 Zeilen sind rund fünf
|
||
Stunden DERSELBEN Marktlage. Aus 2.302 rohen Zeilen bleiben **114 unabhängige**.
|
||
**(2) Regime.** Das Messfenster (10.–12.08.) lief **+4,93 $** (78,74 → 83,68).
|
||
Roh liest die Karte „auf" zu **62 %** richtig und „ab" zu **46 %** — das ist der
|
||
Trend, nicht die Karte.
|
||
✅ **BEHOBEN:** `m15_accuracy` entkoppelt jetzt (Schlüssel Level+Stunde, wie
|
||
`core.stichprobe.schluessel_level_stunde`) und meldet beide Zahlen —
|
||
„114 unabh. Zustände aus 2302". Angezeigt werden jetzt **53 %** statt 60 %.
|
||
⚠ Dieselbe Cluster-Falle hat im Projekt schon die Fälligkeitsbedingung
|
||
`pbreak_accuracy_v2` um das ZEHNFACHE danebenliegen lassen und am 07.08. elf
|
||
Hypothesen ausgelöst. Sie ist hier dieselbe — und sie steckte in einer Zeile,
|
||
die ich am 08.08. selbst gebaut habe.
|
||
|
||
✅ **ANTWORT AUF DIE M5-FRAGE: NEIN, jetzt nicht.**
|
||
· **Die bestehende Karte ist nicht belegt** (KI enthält 50, handlungsrelevanter
|
||
Zustand 45,5 %). Eine zweite nach demselben Muster zu bauen, bevor die erste
|
||
ein Urteil hat, ist genau das Vorgehen, das dieses Projekt sonst zurückweist.
|
||
· **M5 wäre teurer**: Spread/ATR **0,193** gegen 0,104 auf M15 — die Kostenseite
|
||
ist der einzige gemessene Vorteil der M15-Karte.
|
||
· **Die M5-Information ist grösstenteils schon da**: der Squeeze-Zustand
|
||
(armed/active) steht in der Meldungen-Karte, Gate ③b rechnet **bereits auf M5**
|
||
(`room_info`), ebenso die P(break)-Merkmale. Die „M15-Karte" ist längst ein
|
||
Hybrid (M30-Level · ATR_M15 · M5-Raum/P(break)).
|
||
· **Doppelte Drift-Fläche**: allein diese Woche steckten in der EINEN Karte drei
|
||
Defekte (Bias als Richtungsgeber · Ziel in der verbotenen Richtung · fehlende
|
||
Raum-Zeile). Eine zweite verdoppelt die Stellen für die Veto→Richtung-Falle.
|
||
⚠ **OFFEN, benannt:** die Karte **flackert** — ~990 Zustandswechsel/Tag, also
|
||
alle 1,5 min. Das ist selbst ein Befund und der nächste sinnvolle Schritt.
|
||
✅ **Bedingung für ein Ja:** wenn die entkoppelte Trefferquote bei ≳350 Fällen
|
||
(dieselbe Latte wie `pbreak_accuracy_v2`) über 50 % liegt und das Intervall die
|
||
Null-Linie ausschliesst. Vorher ist eine zweite Karte Zierde, kein Werkzeug.
|
||
|
||
## ✅✅ SYSTEMZUSTAND IM TAGESREPORT (2026-08-11) — der fehlende letzte Meter
|
||
|
||
**Selbständige Empfehlung nach der Gesamtprüfung, vom User beauftragt.**
|
||
|
||
⚠⚠ **DER BEFUND: die Erkennung funktioniert, die ZUSTELLUNG nicht.** Das Projekt
|
||
hat sieben Wächter, jeden nach einem eigenen Vorfall gebaut — und jeder tut, was
|
||
er soll. Trotzdem blieben allein in dieser Woche **sechs** Zustände tagelang
|
||
unbemerkt, **vier davon waren bereits erkannt**:
|
||
| Vorfall | unbemerkt | erkannt? |
|
||
|---|---|---|
|
||
| `auto_sr_close` aus | **5 Tage** | ✅ 1× gemeldet, dann Marker → Stille |
|
||
| `auto_squeeze` aus | seit 11.08. | ✅ — Marker von **02.08.** → **gar keine** Meldung |
|
||
| `rec_outcomes` tot | **5 Tage** | ❌ (Puls-Wächter heute gebaut) |
|
||
| Telegram still | **4 Tage** | ❌ |
|
||
| Divergenz-Wächter Dauerwarnung | **6 Tage** | er *war* der Wächter |
|
||
| `squeeze_entry_gap` 8/12 | seit 05.08. | ✅ „sammelt noch" |
|
||
|
||
✅ **BELEGT AN DEN MARKER-DATEIEN:** `cfg_auto_sr_close` trägt **06.08. 18:10**,
|
||
`cfg_auto_squeeze` trägt **02.08. 18:00**. Der Config-Wächter meldet je Schlüssel
|
||
**genau einmal** und schweigt danach für immer. `auto_sr_close` war damit fünf
|
||
Tage aus — und wurde am 11.08. mit **+350,81 € über 43 Trades** gemessen
|
||
(grob 150–300 € entgangener Wert, je nach Vergleichbarkeit der Bedingungen).
|
||
⚠⚠ **Die Ursache ist ein Kategorienfehler:** die Marker-Logik behandelt einen
|
||
**Dauerzustand** wie ein **Einmal-Ereignis**.
|
||
|
||
✅ **GEBAUT: `engine._system_status()` → Block im Tagesreport (07:30, Telegram +
|
||
E-Mail).** Der Report ist der einzige Kanal, der nachweislich **jeden Tag**
|
||
ankommt (Telegram-Log lückenlos `ok`); der Popup verstummt nach dem ersten Mal.
|
||
**Saubere Trennung: Popup = „etwas Neues ist passiert", Tagesreport = „so steht
|
||
das System gerade".**
|
||
Realer erster Block:
|
||
```
|
||
⚙ Systemzustand:
|
||
Automatik: BRK aus · SIG aus · S/R-Close AN · Flip aus
|
||
⚠ Puls: rec_outcomes: 0 Zeilen — Logger hat NIE geschrieben
|
||
⚠ Config: auto_squeeze steht auf 'false' (validiert: 'true')
|
||
Messungen: pbreak_accuracy_v2 143/350 · squeeze_b5 11/20 · squeeze_entry_gap 8/12
|
||
```
|
||
⚠ **Reine Anzeige** — es wird nichts geschaltet und nichts entschieden.
|
||
⚠ **`measurement_reminder` wird LAZY und GEKAPSELT importiert** (1×/Tag, nicht im
|
||
Live-Pfad): ein Auswertungs-Skript gehört NICHT in den Live-Abhängigkeitsgraphen
|
||
— derselbe Fehler wie der `backtest_breakout_squeeze`-Import. Ein Fehlschlag wird
|
||
**sichtbar** gemeldet, nicht still verschluckt (ein fehlender Block sähe aus wie
|
||
„alles gut" — exakt die Klasse, die `rec_outcomes` fünf Tage verbarg).
|
||
✅ **`telemetrie_puls()` liegt in `core/history.py`** (DB-Schicht), **beide**
|
||
Nutzer rufen dieselbe Funktion — Skripte importieren `core`, nie umgekehrt.
|
||
✅ **7 Tests** (`tests/test_system_status.py`), Schwerpunkt auf den **Ausfallpfaden**:
|
||
ein Report, der wegen der Statuszeile gar nicht ankommt, wäre schlimmer als keine
|
||
Statuszeile. Geprüft: kein Befund wird ausdrücklich bestätigt · ein Puls-Fehler
|
||
wird gemeldet statt verschluckt · ohne `history` kein Absturz · die Methode wirft
|
||
**nie** und liefert immer mindestens eine Zeile · die Puls-Tabellenliste ist
|
||
vollständig.
|
||
⚠ **Verifikation der Zustellung steht aus** — sie erfolgt mit dem Report am
|
||
nächsten Morgen um 07:30. Die Bausteine sind einzeln live gegengeprüft.
|
||
|
||
## ✅ UI AUFGERÄUMT, TELEGRAM AUF CLOSES, ZWEI SCHALTER (User 2026-08-19, v=178)
|
||
|
||
**(1) Einsatz-Felder entfernt.** ⚠ Backend bleibt vollständig; die Werte leben
|
||
in `runtime_state.json` UND der ini (40 %). **Folge: ohne UI ändert man den
|
||
Prozentsatz nur über die ini + Neustart.**
|
||
|
||
**(2) BRK-Schalter raus, STOPP (Circuit-Breaker) rein.**
|
||
⚠⚠ **Bewusster Tausch mit Konsequenz:** der An/Aus-Schalter des **autonomen**
|
||
Pfads ist aus der App verschwunden — BRK läuft, stoppen lässt er sich nur noch
|
||
per `POST /api/autosqueeze` oder ini+Neustart. Sein **Zustand** bleibt in der
|
||
Breakout-Karte sichtbar. Dafür ist die **Notbremse** bedienbar, die am 17.08.
|
||
real ausgelöst hat. `set_circuit_breaker` + `POST /api/circuit` + Persistenz
|
||
(`cb_limit_pct`/`cb_remember`) — ⚠ der Breaker hat keinen eigenen Bool, er lebt
|
||
in `_cb_limit_pct` (0 = aus); **ohne das Gedächtnis wäre die Prozentzahl nach
|
||
einmal Ausschalten für immer weg.**
|
||
|
||
**(3) S/R-Auto-Close jetzt auch in der Aktionsleiste** (`#btn-sr`). Den Schalter
|
||
gab es nur in der **Trade-Leiste** — die ist aber nur bei OFFENER Position
|
||
sichtbar, also genau dann nicht, wenn man ihn **vor** einem Trade setzen will.
|
||
Der alte Knopf bleibt; beide lesen `auto_sr_close` aus demselben Snapshot und
|
||
können nicht auseinanderlaufen.
|
||
|
||
**(4) Telegram nur noch bei geschlossenen Trades.**
|
||
✅ **EINE Filterstelle statt 17 Einzeleingriffe:** `send_telegram` ist jetzt ein
|
||
Filter vor `_send_telegram_roh`, `_TG_ERLAUBT = ("Trade geschlossen",)`. Alle
|
||
Sender bleiben stehen. **Umkehren = Liste erweitern.** Rückgabe `False` für
|
||
Unterdrücktes, damit der Report-Marker nichts fälschlich als zugestellt bucht.
|
||
⚠ **Tages-/Wochenreport werden nur auf dem TELEGRAM-Weg still — die E-Mail
|
||
läuft weiter.**
|
||
⚠⚠ **Eine Ausnahme mit Bypass:** „<ok_word> FEHLGESCHLAGEN“ beim Notfall-Close
|
||
geht am Gate vorbei — dort ist die Position **NICHT** zu und es braucht eine
|
||
Handeingabe. Die erfolgreichen Closes von Breaker, Notfall-Stop und S/R meldet
|
||
ohnehin `_check_close_notify`; es geht also keine *Schliessung* verloren.
|
||
|
||
⚠⚠ **ZWEITER TOTALVERLUST DERSELBEN URSACHE AM SELBEN TAG — diesmal CLAUDE.md.**
|
||
Ein Emoji, geschrieben als ZWEI getrennte \u-Escapes, erzeugt in Python
|
||
**lone surrogates**, die sich nicht nach UTF-8 kodieren lassen — und
|
||
`io.open(p,"w")` kürzt die Datei **vor** dem Schreiben. Ergebnis: **0 Bytes**,
|
||
und der Commit ging **grün durch** (der Hook prüft Python und JS, nicht die
|
||
Doku) und wurde gepusht. Wiederhergestellt aus `HEAD~1`.
|
||
**Regel, ab hier ausnahmslos:** (a) Emoji nie als getrennte \u-Surrogate —
|
||
Zeichen direkt oder gar nicht; (b) IMMER erst nach `.tmp`, Grösse gegen das
|
||
Original prüfen, dann `os.replace`; (c) **eine Syntaxprüfung ersetzt die
|
||
Grössenprüfung nicht** — eine LEERE Datei besteht `node --check` und jeden Hook.
|
||
|
||
## ✅✅ EINSATZ 95 % → 40 % + BREAKOUT-KARTE AKTIV (User 2026-08-19, v=176)
|
||
|
||
Nach dem Schliessen der offenen Position ausgeführt (User: „trade is closed“).
|
||
⚠ Der Trade schloss mit rund **−152 €** (Equity 877 → 725) — genau die
|
||
Grössenordnung, die diese Änderung adressiert.
|
||
|
||
| | vorher | **jetzt** |
|
||
|---|---|---|
|
||
| `margin_buffer_pct` (ini **und** `runtime_state`) | 95 | **40** |
|
||
| Lots bei 725 € freier Margin | ~0,97 | **~0,41** |
|
||
| Risiko je 2×ATR-Stop (~0,55 $) | ~46 € | **~20 €** |
|
||
✅ **Beide Stellen angeglichen** — `set_margin_pct` persistiert nach
|
||
`runtime_state.json` (Schlüssel `margin_pct`, NICHT `margin_buffer_pct`), und
|
||
`_load_runtime_state()` (Z. 487) läuft **nach** dem ini-Lesen (Z. 176), gewinnt
|
||
also. Die ini wurde trotzdem mitgezogen, damit ein gelöschtes
|
||
`runtime_state.json` nicht still auf 95 % zurückfällt — dieselbe Falle wie bei
|
||
`auto_signal` am 31.07. Backups: `…bak-2026-08-19-margin40`.
|
||
⚠ Gilt für **alle** neuen Positionen, auch die autonomen. Zurück: Feld
|
||
„Einsatz %“ auf 95 (wirkt sofort) **und** die ini-Zeile.
|
||
|
||
✅ **Karte `#card-brk` ist live** — `squeeze_b5` liefert `{n:11, ziel:20,
|
||
ratio:0.14, pf:0.39, sum_eur:−173.46}`. Deploy über `tools/deploy.py --feld
|
||
squeeze_b5`: Pipeline grün, genau eine Instanz je Port, Feld vorhanden, Log
|
||
ohne ERROR. Angezeigt: *AN · ruhende Stop-Order · Nachtsperre 3–6 · keine
|
||
Kompression (Box 2,89×ATR) · keine liegenden Orders · B5 11/20, Verhältnis
|
||
0,14, PF 0,39, −173,46 €*.
|
||
|
||
⚠ **Offen bleibt Punkt ② der User-Idee** (50 % für Breakout reservieren):
|
||
technisch möglich (RETAIL_HEDGING), aber Kern-Umbau („nur FLAT“ + 17
|
||
Einzel-Positions-Annahmen) und gegen die Messung (autonom −4,71 €/Lot gegen
|
||
Mensch +1,56 €/Lot). Erst wieder aufgreifen, wenn der autonome Pfad auf einem
|
||
NEU vorab fixierten Fenster nicht negativ ist.
|
||
|
||
## ⚙ BREAKOUT-KARTE VORBEREITET — aktiv erst nach dem nächsten Neustart (2026-08-19, v=176)
|
||
|
||
User-Idee: (1) manuell fest 40 % Margin, (2) 50 % für Breakout reservieren,
|
||
(3) neue Dashboard-Karte. **Bewertung:**
|
||
| Punkt | Urteil |
|
||
|---|---|
|
||
| ① 40 % fest | ✅ grösster gemessener Hebel (MaxDD **82,8 %**, schlechtester Trade −381 €). **Ein Schalter, kein Umbau** — Feld „Einsatz %“, ohne Neustart, nur auf neue Positionen. |
|
||
| ② 50 % Reserve | ⚠ Konto ist **RETAIL_HEDGING**, also technisch möglich — aber: `_check_auto_squeeze` feuert **„nur FLAT“**, dazu **17 Einzel-Positions-Annahmen** in `engine.py` (Trailing, Notfall-Stop, S/R-Close, Time-Stop, Breaker verwalten je EINE Position). Kern-Umbau, kein Parameter. |
|
||
| ③ Karte | ✅ gebaut, s. u. |
|
||
|
||
⚠⚠ **Der Live-Befund macht ① dringlich:** Balance 937,40 € · Equity 877,41 € ·
|
||
Margin belegt 883,62 € → **freie Margin −6,21 €, Margin-Level 99,3 %**. EINE
|
||
Position (1,19 Lot) frisst das ganze Konto. **Es gibt derzeit nichts zu
|
||
reservieren** — die Reserve aus ② entsteht überhaupt erst durch ①.
|
||
⚠⚠ **Und die Zuteilung steht gegen die Messung:** seit 01.08. je Lot
|
||
**autonom −4,71 €** (n=11) gegen **Mensch +1,56 €** (n=185). ② gäbe dem
|
||
gemessen schlechteren Pfad den grösseren Anteil; 40+50 lassen zudem nur 10 %
|
||
Puffer. **Empfehlung: ① und ③ jetzt, ② erst wenn freie Margin existiert UND
|
||
der autonome Pfad auf einem NEU vorab fixierten Fenster nicht negativ ist.**
|
||
|
||
**Gebaut (inaktiv):** `history.fetch_squeeze_since` · `engine._squeeze_b5_cached`
|
||
(~120 s gecacht, Muster `_alignment_cached`) · Snapshot-Feld `squeeze_b5` ·
|
||
Karte `#card-brk` (Zustand · Squeeze jetzt · liegende Orders · **B5-Stand**).
|
||
⚠ **Eigenes Fenster ab 01.08.** — `squeeze_monitor.live` rechnet über **alle 42**
|
||
Trades, B5 zählt **11**. Beide ungetrennt zu zeigen wäre dieselbe stille
|
||
Fenster-Verwechslung wie bei `pbreak_accuracy_v2`.
|
||
⚠ Die Karte nennt beide Zahlen: der Monitor meldet **`ontrack`** aus der
|
||
**Mechanik** (ØR +0,163 / PF 1,24), während live **Verhältnis 0,14 / PF 0,39**
|
||
steht. Die Lücke IST der B4-Befund — eine der beiden allein wäre irreführend.
|
||
✅ **Live ändert sich nichts:** die Karte ist `hidden` und `renderBrk` blendet
|
||
sie aus, solange `squeeze_b5` fehlt. Aktiv nach dem nächsten Neustart — der
|
||
wartet auf User-Wunsch, bis die offene Position zu ist.
|
||
|
||
⚠⚠ **BEINAHE-SCHADEN, dokumentiert weil es eine Fehlerklasse ist:** ein
|
||
`UnicodeEncodeError` (Emoji-Escapes) brach mitten im Schreiben ab und liess
|
||
**`web/app.js` mit 0 Bytes** zurück — `io.open(p,"w")` kürzt VOR dem Schreiben.
|
||
⚠ **`node --check` bestand trotzdem: eine LEERE Datei ist gültiges JS.** Die
|
||
Syntaxprüfung allein hätte den Totalverlust durchgewinkt; gefangen hat ihn erst
|
||
der Grössenvergleich. **Regel: Datei erst in `.tmp` schreiben, Grösse gegen das
|
||
Original prüfen, dann `os.replace`** — so ist es jetzt gemacht.
|
||
|
||
## ✅ BRK BLEIBT AN (User 2026-08-19) — und die 13-tägige Flaute war kein Defekt
|
||
|
||
User: „Breakout-Squeeze soll an bleiben. Rechne und überprüfe erneut.“
|
||
|
||
✅ **Zustand konsistent, nichts umzuschalten:** ini `true` · `runtime_state.json`
|
||
`true` · Snapshot `true`. **Die Doku-Zeile vom 17.08. („auto_squeeze bleibt
|
||
FALSE“) war stale** und ist hiermit korrigiert.
|
||
|
||
⚠⚠ **AUFFÄLLIG WAR: seit dem 06.08. kein einziger Squeeze-Trade** — 13 Tage.
|
||
Erst als Marktphänomen geprüft, dann als Defekt, beides falsch:
|
||
| Prüfung | Ergebnis |
|
||
|---|---|
|
||
| Feuert die Regel überhaupt? (echte `squeeze_scan`, 19,5 Tage) | **57 Ausbrüche**, 53 ausserhalb 3–6 Uhr ≈ **19/Woche** — zuletzt 14./17./18.08. |
|
||
| Ist der Slot belegt? | **flat zu 57,8 %** — reichlich Gelegenheit |
|
||
| Pending-Pfad defekt? | `SQZ-STOP` **0** · „nicht platzierbar“ **0** · „übersprungen“ **0** · **0 ERROR/Traceback** |
|
||
| **Log-Zeile** | **`2026-08-19 11:03:18 [WEB] Auto-Squeeze-Entry AN`** |
|
||
**Er war schlicht AUS** — vom 11.08. 14:10 bis heute 11:03. Kein Defekt, kein
|
||
blockierter Pfad. ⚠ Seit 11:03 gab es noch keinen Ausbruch (`box_atr` 3,77 =
|
||
keine Kompression) und eine Position ist offen — er hätte also ohnehin nicht
|
||
feuern können.
|
||
|
||
✅ **NEBENBEFUND: der Circuit-Breaker hat am 17.08. 18:35 ZUM ERSTEN MAL
|
||
ausgelöst** — `Tages-P&L −82,42 ≤ −81,45 € (8 % von 1018) → Position
|
||
schliessen`. Zwei Tage nach dem Scharfstellen. Die **schliessende** Hälfte
|
||
wirkt also, unabhängig davon, dass die sperrende nur 6 % der Trades erreicht.
|
||
|
||
**B5 NEU GERECHNET (unverändert, weil seit 06.08. nichts dazukam):**
|
||
| | |
|
||
|---|---|
|
||
| n | **11 / 20** |
|
||
| Trefferquote | 73 % (8 Gew. / 3 Verl.) |
|
||
| ØGewinn / ØVerlust | **+13,62 / −94,15 €** |
|
||
| Verhältnis / PF | **0,14** / **0,39** (Latte je 1,0) |
|
||
| Summe | **−173,46 €** |
|
||
⚠⚠ **Die Latte ist rechnerisch unerreichbar:** selbst bei 9 Gewinnern und 0
|
||
Verlierern bräuchte es **+165,72 € je Gewinner = 12,2× den bisherigen
|
||
Durchschnitt** und **2,1× den Rekord** (77,34 €), neunmal in Folge.
|
||
✅ **Und die −173 € kommen NICHT vom Exit:** die drei Verluste sind −175,21 /
|
||
−63,94 / −43,29 € bei 2,9–3,3 Lots — also der **planmässige 2×ATR_M15-Stop mal
|
||
Positionsgrösse**. Der `sr_close` hat auf denselben 11 Trades **+15,98 €
|
||
beigetragen** (gemessen 13.08.). Es ist die dokumentierte Sizing-Frage.
|
||
|
||
⚠⚠ **DAMIT IST B5 IN DER VORLIEGENDEN FORM ERLEDIGT — nicht erfüllt, sondern
|
||
gegenstandslos.** Sie kann nur noch „abschalten“ sagen, und der User will BRK
|
||
an. **Die Latte nachträglich zu senken wäre genau der Fehler, gegen den sie
|
||
vorab fixiert wurde** — also wird sie NICHT gesenkt, sondern als **durch
|
||
User-Entscheidung überstimmt** vermerkt. Wer sie ersetzen will, fixiert eine
|
||
NEUE Latte **vor** der Beobachtung, auf einem NEUEN Zählfenster ab heute.
|
||
|
||
## ✅ M15-AUTO-ENTRY KORRIGIERT + ABBRUCHREGEL VERANKERT (2026-08-19, abends)
|
||
|
||
**(1) Richtung kommt jetzt aus dem VETO, nicht aus der Lesart.** Der Auto-Entry
|
||
liest damit genau das, was die Karte **anzeigt** (`app.js:504` nimmt `erl`, die
|
||
erlaubte Richtung). Kein Veto → **kein Entry** (die Karte sagt dort
|
||
ausdrücklich „gibt hier keine Richtung“).
|
||
**Gegenprobe an der Live-Lage:** `verboten=SHORT, lesart=ab` → alt **SHORT**
|
||
(die verbotene!), **neu LONG** — identisch zur Anzeige.
|
||
⚠ Damit entspricht der Pfad der gemessenen **Kontrolle C** (HTF-Richtung,
|
||
ØR **−0,085 / −0,030**) — der **am wenigsten schlechten** der vier Varianten,
|
||
aber weiterhin **negativ**. Die Korrektur behebt den Widerspruch, nicht den
|
||
fehlenden Edge.
|
||
|
||
**(2) Abbruchregel verankert** — `measurement_reminder.py`: neue Messung
|
||
`m15_abbruch` (≥ 20 `AUTOM15_*`-Trades, Latte Verhältnis < 1,0 ODER PF < 1) und
|
||
neuer Config-Anker `auto_m15` (validiert = `false`). Dazu der Helfer
|
||
`_zaehle_setup()`. ⚠ Der Zähler zählt nur **geschlossene** Trades — der eine
|
||
Trade vom 18:03 läuft noch, deshalb steht er korrekt auf 0.
|
||
**Damit kann die Regel nicht mehr unbemerkt verfallen** — genau das war bei B5
|
||
wochenlang der Fall.
|
||
|
||
⚠ **Aktivierung steht aus:** die Änderung ist Backend und braucht einen
|
||
Neustart. `auto_m15` ist **aus**, es besteht also keine Eile — der Neustart
|
||
wartet auf User-Wunsch, bis das Konto flat ist.
|
||
|
||
### Nachtrag: die drei falschen Trades liefen ALLE über den alten Code
|
||
|
||
User meldete „es wurde wieder falsch herum eröffnet“. Die Zeitachse:
|
||
```
|
||
18:03:25 AUTOM15_SHORT <- alter Code
|
||
18:11:04 AUTOM15_SHORT <- alter Code
|
||
18:13:17 Auto-M15 AN (User) -> 18:13:18 SHORT <- alter Code
|
||
18:13:31 Engine gestartet <- HIER erst wurde der Fix scharf
|
||
18:13:59 Auto-M15 AUS
|
||
```
|
||
**Beweis in der Logzeile von 18:13:17:** `(Lesart ab, Level 86.263)` — das ist
|
||
das ALTE Format; das neue schreibt `(erlaubt, verboten=…, Lesart …)`. Der
|
||
Neustart kam **14 Sekunden zu spät**.
|
||
|
||
⚠⚠ **Zwei Fehler, beide meine:** (1) der erste Bau nahm die Lesart statt des
|
||
Vetos; (2) ich habe den Schalter **testbereit stehen lassen**, während der Fix
|
||
noch nicht deployt war — der User schaltete ein, und der alte Code feuerte
|
||
sofort. **Regel: ein autonomer Pfad mit bekanntem Defekt gehört hart aus
|
||
(`auto_m15=false` in ini UND runtime), nicht nur „nicht eingeschaltet“.**
|
||
|
||
⚠ **Noch NICHT live belegt:** dass die neue Richtung korrekt eröffnet. Seit
|
||
dem Neustart gab es keinen Entry. Belegt ist nur: bei fehlendem Veto macht er
|
||
**gar nichts** (richtig), und die Gegenprobe vor dem Neustart ergab LONG statt
|
||
SHORT. **Der erste echte Entry ist die Verifikation** — die Logzeile muss
|
||
`erlaubt, verboten=…` enthalten.
|
||
|
||
**Stand:** `auto_m15` **aus** · AUTOM15-Bilanz +1,51 / −3,31 / eine offene
|
||
SHORT-Position (auf User-Entscheidung weiterlaufend).
|
||
|
||
## ⚠⚠⚠ M15-AUTO-ENTRY HANDELTE DIE VERBOTENE RICHTUNG — ABGESCHALTET (2026-08-19, 18:05)
|
||
|
||
User: „der auto trader hat short eröffnet obwohl long angezeigt wurde“.
|
||
**Bestaetigt, echter Fehler, sofort abgeschaltet** (`auto_m15 = false`, auch in
|
||
`runtime_state.json`). Ein Trade betroffen:
|
||
`18:03:24 AUTO-M15-ENTRY: SHORT (Lesart ab, Level 86.263)` — 0,39 Lot @ 86,266.
|
||
|
||
**Der Widerspruch:**
|
||
| Quelle | Feld | Ergebnis |
|
||
|---|---|---|
|
||
| **Anzeige** (`app.js:504`, „JETZT: …-Order“) | `bias.verboten` → die **erlaubte** Richtung | **LONG** |
|
||
| **Auto-Entry** (`_check_auto_m15`) | `lesart` (aus P(break)) | **SHORT** |
|
||
⚠⚠ **Damit hat der Bot die VERBOTENE Richtung eröffnet** — und das Veto ist die
|
||
**einzige beidhälftig robuste Aussage** der ganzen Karte (gegen das Signal
|
||
**−6,60 €/Lot** über 1.216 Trades). Schlimmer als eine Anzeige-Abweichung.
|
||
|
||
**URSACHE — mein Fehler beim Bau.** Ich habe die Richtung gegen
|
||
`backtest_m15_auto.py` geprueft (`d_les = 1 if lesart == "auf" else -1`) und
|
||
damit dessen **Variante A** gebaut. Übersehen: derselbe Backtest hat eine
|
||
**Variante B — „Lesart + veto-konform“** (−0,104/−0,051), und die **Karte zeigt
|
||
B**. Ich habe A gebaut und B angezeigt.
|
||
⚠ Ich hatte die Ableitung vor dem Bau ausdrücklich verifiziert — aber **nur
|
||
gegen den Backtest, nicht gegen die Anzeige**. Exakt die Divergenz-Klasse, vor
|
||
der dieses Projekt an sieben Stellen warnt: zwei Quellen fuer dieselbe Aussage.
|
||
**Lehre: bei einem Auto-Entry auf eine ANGEZEIGTE Empfehlung ist die ANZEIGE
|
||
die zu prüfende Referenz, nicht der Backtest, aus dem sie stammt.**
|
||
|
||
**Stand:** `auto_m15` **aus**. Die Position läuft auf User-Entscheidung weiter
|
||
(Trailing an, SL 87,055 / TP 85,463). **Offen und noch nicht entschieden:** ob
|
||
der Trader auf die **angezeigte** Richtung (= Variante B, veto-konform)
|
||
umgebaut wird. ⚠ B ist gemessen **ebenfalls negativ**, aber rund halb so
|
||
schlecht — und vor allem widerspruchsfrei zu dem, was der User sieht.
|
||
|
||
⚠ **Zweiter offener Punkt aus demselben Bau:** die am 19.08. vorab fixierte
|
||
M15-Abbruchregel ist **noch NICHT** in `measurement_reminder.py` verankert (der
|
||
Patch-Versuch brach ab). Ohne Verankerung ist sie genau das, was bei B5
|
||
wochenlang unbemerkt blieb — **nachzutragen**.
|
||
|
||
## ⚠⚠ M15-AUTO-ENTRY GEBAUT — GEGEN DIE MESSUNG, auf User-Wunsch (2026-08-19, v=181)
|
||
|
||
User: „ich möchte das auto trade testen. Baue den Button.“ — nach vollständiger
|
||
Information über die Beleglage. **Default AUS**, Schalter **M15** in der
|
||
Aktionsleiste, `[trading] auto_m15 = false`.
|
||
|
||
⚠⚠ **DIE MESSUNG SAGT NEIN, und zwar deutlich** (`backtest_m15_auto.py`, 13.08.):
|
||
| Variante | H1 ØR / PF | H2 ØR / PF |
|
||
|---|---|---|
|
||
| **A) die gehandelte Lesart** | **−0,162 / 0,72** | **−0,094 / 0,84** |
|
||
| C) Kontrolle HTF-Richtung | −0,085 / 0,85 | −0,030 / 0,95 |
|
||
| D) Kontrolle **Gegenrichtung** | −0,070 / 0,87 | **+0,007 / 1,01** |
|
||
Beide KI schliessen die Null aus ([−0,222…−0,099] / [−0,152…−0,038]), die
|
||
Nachbar-Schwellen werden **monoton schlechter**, und A ist schlechter als die
|
||
HTF-Richtung **und als ihr eigenes Gegenteil**. Live-Gegenprobe (`m15_states`,
|
||
entkoppelt): **46,8 %** Treffer auf 30 min. Die Karte steht in **46 %** der Zeit
|
||
grün UND gerichtet — als Auslöser also alles andere als selektiv.
|
||
|
||
**Umsetzung** — `engine._check_auto_m15` im `_pos_loop`:
|
||
• Auslöser `status=gruen` + `lesart` in (auf, ab); Richtung **exakt wie im
|
||
Backtest**: `auf → LONG`, `ab → SHORT`. • Dedup 1× je Episode über
|
||
**(Richtung, Level)** — derselbe Schlüssel `(d_les, nah["level"])`.
|
||
• **Nur FLAT.** • Erbt die **volle** Guard-Kette: `_auto_guard("m15")` (Nacht,
|
||
Circuit-Breaker, News-Blackout) — der Parameter `pfad` war laut Docstring genau
|
||
dafür vorgesehen, **nachgebaut wurde nichts**. • Plus Startup-Schonfrist und
|
||
beide Wiedereinstiegs-Sperren. • Setup-Tag **`AUTOM15_*`**.
|
||
|
||
⚠⚠ **ABWEICHUNG VON DER GEMESSENEN VARIANTE, ausdrücklich benannt:** gemessen
|
||
wurde der Einstieg **AM Level mit Fill bei Berührung**, gebaut ist der
|
||
**MARKT**-Einstieg. Der Markt-Einstieg ist hier mehrfach als der schlechtere
|
||
gemessen (Squeeze +0,456 → −0,158 **allein durch den Preis**). Die Live-Zahlen
|
||
sind also **eher noch unter** −0,16 / −0,09 zu erwarten. Eine ruhende Order
|
||
hätte den geteilten `_pending_ziel`-Manager gebraucht (Squeeze hat dort
|
||
Vorrang) — eigener Umbau, bewusst nicht Teil dieses Tests.
|
||
|
||
✅ **ABBRUCHREGEL, VORAB FIXIERT** (ohne sie wäre es kein Test, sondern nur ein
|
||
Schalter): **nach ≥ 20 `AUTOM15_*`-Trades — Verhältnis (ØGewinn/ØVerlust) < 1,0
|
||
ODER PF < 1 → `auto_m15 = false`.** Dieselbe Latte wie B5 beim Squeeze, damit
|
||
sie vergleichbar und **nicht nachträglich verschiebbar** ist.
|
||
⚠ Erwartung aus der Messung: die Regel **wird** greifen. Das ist kein Argument
|
||
gegen den Test — aber es ist der Grund, warum die Latte **jetzt** steht.
|
||
|
||
✅ Verifiziert: Toggle an→aus, Snapshot `auto_m15`/`m15_entry_count`, Deploy mit
|
||
`--feld auto_m15` (alle 5 Schritte), `ruff F821` sauber (der neue Code sitzt in
|
||
einem `except` — der dokumentierte blinde Fleck), 85 Tests, `node --check`.
|
||
**Zurück:** Knopf **M15** bzw. `auto_m15 = false`.
|
||
|
||
## ⚠ CIRCUIT BREAKER BLEIBT AUS — User-Entscheidung (2026-08-19, abends)
|
||
|
||
Auf den Hinweis, dass BRK **an** und die Notbremse **aus** ist, kam ein
|
||
ausdrückliches **„nein“**. Der Breaker bleibt **AUS**.
|
||
|
||
⚠ **Der Zustand ist damit gespalten — bewusst, aber zu merken:**
|
||
| Stelle | Wert |
|
||
|---|---|
|
||
| `runtime_state.json` → `cb_limit_pct` | **0** (= aus; gewinnt beim Start) |
|
||
| `runtime_state.json` → `cb_remember` | 8 (kehrt beim Einschalten zurück) |
|
||
| ini `daily_loss_limit_pct` | **8** |
|
||
Beim nächsten Start gilt **runtime_state** (`_load_runtime_state` läuft nach dem
|
||
ini-Lesen) — der Breaker bleibt also aus. Der **Config-Wächter** wird die
|
||
Abweichung melden; das ist richtig so und keine Störung.
|
||
|
||
**Betriebszustand:** BRK **an** (autonome Entries, sobald flat) · S/R-Auto-Close
|
||
**an** · Einsatz **40 %** · Tagesverlust-Stopp **aus**. ⚠ Der Breaker hatte am
|
||
17.08. real ausgelöst (−82,42 €), der gemessene MaxDD liegt bei **82,8 %**.
|
||
Einschalten: der Knopf CIRCUIT BREAKER im Dashboard.
|
||
|
||
⚠⚠ **DRITTER Encoding-Abbruch desselben Musters am selben Tag — aber diesmal
|
||
OHNE Schaden.** Die am Nachmittag notierte Regel (erst nach `.tmp`, Grösse
|
||
prüfen, dann `os.replace`) hat gehalten: CLAUDE.md blieb unversehrt, es
|
||
entstand nur eine leere `.tmp`-Leiche — die prompt mitcommittet wurde.
|
||
**`*.tmp` steht jetzt in `.gitignore`.** Die Regel funktioniert; die eigentliche
|
||
Ursache (Emoji als getrennte Unicode-Escapes) muss man trotzdem lassen.
|
||
|
||
## ✅ ZEHNTES BUCH PER OCR GEPRÜFT + WARUM DIE 50-%-RESERVE NICHT SCHNELL GEHT (2026-08-19)
|
||
|
||
**(1) `[Main title here].pdf` ist jetzt geprüft statt ungeprüft.** Reiner Scan
|
||
ohne Textebene, Metadaten leer. OCR aufgesetzt: **Tesseract 5.4.0** (winget),
|
||
`pymupdf` + `pytesseract`. ⚠ Die deutschen Sprachdaten mussten in ein
|
||
**schreibbares** Verzeichnis (`TESSDATA_PREFIX`) — `C:\Program Files\` ist ohne
|
||
Elevation gesperrt.
|
||
**Identifiziert: „TRADING 1X1 — Der komplette Leitfaden für ANFÄNGER“ (Philipp
|
||
Kozalla).** Stichprobe (38 Seiten OCR, 6.049 Wörter): Psychologie 14×,
|
||
**Steuern/Broker/Depot 8×**, Risiko-je-Trade 5×, Kerzen 1× — kein einziger
|
||
potenziell neuer Begriff. **Zehn Bücher, keine neue prüfbare Idee.**
|
||
|
||
**(2) Die 50-%-Breakout-Reserve — Umfang jetzt exakt vermessen, NICHT gebaut.**
|
||
| Was | Menge |
|
||
|---|---|
|
||
| Einzel-Positions-Zustand in `engine.py` | **42 Stellen** (`_last_pos_ticket` 9 · `_emergency_*_ticket` 8 · `_open_pos_dir` 5 · `_adverse15_done` 5 · `_bot_open_ticket`/`_source` je 4 · `_sr_close_min_gain_armed` 4 · `_close_alert_ticket` 3) |
|
||
| Einzel-Positions-Zugriffe in `core/trader.py` | **25** |
|
||
| Aufrufe von `trader.snapshot()/pnl/close()` in `engine.py` | **21** |
|
||
|
||
⚠⚠ **DER ENTSCHEIDENDE PUNKT IST KEIN AUFWAND, SONDERN EINE GEFAHR.**
|
||
`trader._refresh_locked` holt die Position mit `positions_get(symbol=sym)` und
|
||
bestimmt daraus **die eine** Position. Liegen ZWEI auf demselben Symbol, hängen
|
||
Trailing, Notfall-Stop, S/R-Close, Time-Stop und Circuit-Breaker — **alle 21
|
||
Aufrufstellen** — an genau einer davon. Praktische Folge: der Bot könnte sein
|
||
Trailing auf **Ihre manuelle** Position legen und seine eigene Squeeze-Position
|
||
**ungeschützt** laufen lassen. Das ist kein Randfall, sondern der Normalfall
|
||
eines zweiten Slots.
|
||
✅ **Die richtige Reihenfolge ist deshalb nicht „FLAT-Bedingung entfernen“,
|
||
sondern erst `trader` + Schutz-Stack von „DIE Position“ auf „die Position mit
|
||
Ticket X“ umstellen** — ein Refactor am Kern, mit echtem Geld dahinter. Er
|
||
gehört in eine eigene Sitzung mit flachem Konto, nicht ans Ende einer langen.
|
||
⚠ Und die ökonomische Frage bleibt offen: die Reserve gäbe dem gemessen
|
||
schlechteren Pfad den grösseren Anteil (autonom −4,71 €/Lot gegen Mensch
|
||
+1,56 €/Lot). **Erst messen, ob der autonome Pfad auf einem neu fixierten
|
||
Fenster trägt — dann bauen.**
|
||
|
||
## ⚠⚠ DREI WEITERE BÜCHER — nichts Neues, und eines ist gar nicht lesbar (2026-08-19)
|
||
|
||
User: „schaue dir die neuen Bücher an und optimiere den Trader damit“. Drei
|
||
PDFs kamen um 16:32–16:35 dazu. Gleiches Vorgehen: Text extrahiert und gegen
|
||
das Gemessene gescannt.
|
||
|
||
| Buch | Umfang | Befund |
|
||
|---|---|---|
|
||
| **Trading — Dein sicherer Weg** | 149 S. / 21k W. | ❌ Einsteiger-Einführung. Psychologie **166×**, Risiko-je-Trade 9×, CRV 10×, RSI/MACD 14× — alles hier gemessen oder User-Entscheidung. |
|
||
| **Trading Strategien** (Saage Media) | 281 S. / 36k W. | ❌ **Fibonacci 77×**, Swing-Trading 40×, RSI/MACD/ADX 33×, SMC/Orderflow 15×. Fibonacci ist hier gemessen **schlechter als eine Zufallslinie**; Swing-Trading ist bei **12–18 min Haltedauer** strukturell gegenstandslos. |
|
||
| **[Main title here].pdf** | 230 S. / **2,5k W.** | ⚠ **Bild-PDF ohne Textebene** — 230 Seiten, aber nur 2.530 extrahierbare Wörter. Ohne OCR nicht scannbar; **ungeprüft**, nicht „geprüft und nichts gefunden“. |
|
||
|
||
✅ **Ergebnis: keine einzige neue, prüfbare Idee.** Damit sind **neun** Bücher
|
||
ausgewertet und der Bestand als Ideenquelle erkennbar erschöpft: jedes
|
||
technische Konzept daraus ist im Projekt bereits gemessen (28 verworfene
|
||
Signal-Eingriffe), und was die Bücher wirklich betonen — **Psychologie** (166,
|
||
197 und 6 Nennungen) und **Risiko je Trade** — ist entweder am 19.08. auf den
|
||
eigenen 1.318 Trades **widerlegt** worden oder längst Ihre Entscheidung
|
||
(Sizing, heute von 95 auf **40 %** gesenkt).
|
||
|
||
⚠ **Nichts am Trader geändert.** Eine „Optimierung“ aus einer Quelle abzuleiten,
|
||
die nichts Neues enthält, wäre genau der Fehler, gegen den die
|
||
2-Stichproben-Regel gebaut ist — sie hat heute allein sechs Buch-Konzepte
|
||
abgewiesen (Bollinger, RSI-Divergenz, Drei Soldaten, Revenge-Trading,
|
||
Overtrading, „Langeweile“).
|
||
|
||
## ⚠⚠ VIER WEITERE BÜCHER (`ebooks/`) — drei ohne Ertrag, das vierte
|
||
## widerlegt eine Regel, die IM PROJEKT steht (2026-08-19)
|
||
|
||
User: „nutze alle Bücher im Verzeichnis ebooks als Ideenquelle“. Sechs PDFs,
|
||
zwei davon am selben Tag schon ausgewertet. Die vier neuen als Text extrahiert
|
||
(346k Wörter, `pypdf`) und gegen das Gemessene gescannt statt gelesen.
|
||
|
||
| Buch | Umfang | Befund |
|
||
|---|---|---|
|
||
| **Day-Trading Attention** (Vaynerchuk) | 333 S. | ❌ **kein Trading-Buch** — Social-Media-Marketing („build brand and sales in the new Social Media World“). Der Titel ist ein Wortspiel. **Null** TA-Begriffe im ganzen Text. |
|
||
| **Die Kunst des techn. u. algo. Tradings** (Fatih AK) | 656 S. | ⚠ breites Lehrbuch, FX-/Makro-lastig. RSI/MACD/ADX **165×**, Elliott 35×, Kerzen 37×, Fibonacci 18×, Bollinger 17× — **alles hier bereits gemessen und verworfen**. Kein CRV-Kapitel, keine Risiko-Zahlen, keine prüfbare Regel. |
|
||
| **KDP968 4 in 1** (Lakefield) | 401 S. | ❌ ausdrücklich „für Einsteiger“ (ETF/Aktien/Dividenden). Unter dem Projektstand. |
|
||
| **Ein Fuchs namens Risiko** | 62 S. | ✅ **das einzige mit prüfbaren Aussagen** — s. u. |
|
||
|
||
✅ **Warum das vierte:** seine Kapitel sind Aussagen über **Reihenfolge und
|
||
Abstand** von Trades („Revenge Trading“, „Overtrading fühlt sich an wie
|
||
Kontrolle“, „warum Langeweile profitabel ist“) — und genau das steht in
|
||
`trades`. **Gemessen auf 1.318 eigenen Trades** (`analyze_verhalten.py`),
|
||
je Lot normiert, 95-%-Bootstrap je Zelle.
|
||
|
||
| Behauptung | Gemessen |
|
||
|---|---|
|
||
| „Revenge: gleich nach einem Verlust wieder rein ist teuer“ | ❌ **umgekehrt**: < 5 min −1,53 [−5,22 … +2,18] · **> 2 h −16,16 [−36,08 … −0,33]** |
|
||
| „Overtrading“ — späte Trades des Tages sind schlechter | ❌ **umgekehrt**: 1.–2. −1,04 · **3.–5. −7,23 [−12,77 … −2,36]** · 6.–10. +0,52 · 11.+ **+1,00** |
|
||
| „Langeweile ist profitabel“ — ruhige Tage besser | ❌ 1–3 Trades +1,83 (n=11) · **4–8 −36,52** · 16+ −0,63 |
|
||
| *(Projekt-eigene Regel)* „nach 2 Losses Session beenden“ | ❌ **nicht gestützt**: 0 Verl. +1,12 · 1 −4,31 · 2 −4,98 · **3+ wieder +1,87** — alle vier KI enthalten die Null |
|
||
|
||
⚠⚠ **DER EIGENTLICHE BEFUND ist die letzte Zeile.** „Nach 2 Losses Session
|
||
beenden“ steht seit dem 30.07. als Punkt 6 in der Entry-Checkliste
|
||
(`_entry_checklist`, „Mental-Check: Verlust-Serie heute“) — **übernommen aus
|
||
der Handbuch-Literatur, nie auf den eigenen Trades geprüft.** Auf 1.318 Trades
|
||
gibt es keine monotone Verschlechterung; der 3.-Verlust-Bucket ist wieder
|
||
positiv. Damit ist es dieselbe Klasse wie die 28 verworfenen Signal-Eingriffe:
|
||
plausibel klingend, gemessen ohne Inhalt.
|
||
|
||
⚠⚠ **NICHT ÜBERINTERPRETIEREN — Mehrfachvergleich.** Getestet wurden ~20
|
||
Zellen; bei 5 % ist **etwa eine** falsch-positive zu erwarten. **Zwei**
|
||
schliessen die Null aus („> 2 h nach Verlust“ und „3.–5. Trade“). Dass beide
|
||
in dieselbe, dem Buch **entgegengesetzte** Richtung zeigen, ist ein **Hinweis,
|
||
kein Beleg**. Als Befund taugt allein: **keine der vier Behauptungen
|
||
reproduziert.**
|
||
|
||
⚠ **Zwei Grenzen, ehrlich:** (a) Beobachtungsdaten, keine Zuweisung — wer nach
|
||
einem Verlust weiterhandelt, wählt das selbst; ein Unterschied kann auch
|
||
„andere Marktlage“ heissen. (b) Der Block „Langeweile“ vergleicht **Tages-
|
||
SUMMEN** über Gruppen mit verschieden vielen Trades — mehr Trades = grössere
|
||
Summe, die Buckets sind damit nicht sauber vergleichbar. Die Richtung deckt
|
||
sich mit Block (3), der das sauber je Trade rechnet.
|
||
|
||
✅ **Nichts gebaut.** Die Checklisten-Zeile bleibt stehen (sie **blockiert
|
||
nichts**, seit dem 11.08. gibt es ohnehin keinen Dialog mehr) — sie ist jetzt
|
||
aber als **unbelegt** gekennzeichnet statt als Regel.
|
||
|
||
## ✅ RAUM JE RICHTUNG AUF DEN ORDER-KNÖPFEN (2026-08-19, v=175)
|
||
|
||
Die einzige umgesetzte Empfehlung aus der Ideen-Runde — und sie stammt nicht
|
||
aus einem Buch, sondern aus den **eigenen Trades**: 27 manuelle Einstiege mit
|
||
Raum < 0,6×ATR liefen mit **79 % Trefferquote und trotzdem −7,57 bzw.
|
||
−11,51 €/Lot** (Klein-Close-Falle, über 80k Bars als PF 0,42/0,66 belegt).
|
||
⚠ Diese Zahl stand bis heute **nur in Gate ③b der M15-Karte** — nicht dort,
|
||
wo geklickt wird. Seit der Entry-Dialog am 11.08. entfernt wurde, war sie im
|
||
Moment der Entscheidung unsichtbar.
|
||
|
||
**Neu:** zweite Zeile auf dem LONG- bzw. SHORT-Knopf — `⚠ 0,07×ATR` /
|
||
`1,49×ATR`, bernstein sobald der Abstand unter dem Gate liegt.
|
||
|
||
⚠⚠ **REINE ANZEIGE — kein Block, keine Sperre, kein Dialog.** Hartes
|
||
Blockieren ist im Projekt nie eingebaut worden, und User-Übersteuerungen
|
||
laufen gemessen **68 % WR**. Sichtbar machen, nicht bevormunden.
|
||
|
||
✅ **Kein Backend, kein Neustart.** Die Daten lagen bereits im Snapshot
|
||
(`m15_setup.motor.raum`, gebaut am 10.08. für Gate ③b) — sie werden hier
|
||
**nicht neu gerechnet**. Ein zweiter Rechenweg wäre genau die Divergenz-Falle,
|
||
die im Projekt dreimal zugeschlagen hat (`sim_run` in ≥6 Skripten · der
|
||
nachgebaute Level-Cluster · das fünfte Exit-Modell).
|
||
|
||
⚠ **Bewusst KEIN eigener Layout-Block:** `#actions` ist ein flex **ohne**
|
||
`wrap` — eine zusätzliche Zeile darin hätte die Knopfbreiten verschoben. Die
|
||
Zahl sitzt deshalb auf dem Knopf, den sie betrifft.
|
||
⚠ Quelle ist trainingsgleich **M5** (`room_info`), nicht die M30-Level der
|
||
Karte — `entry_room_atr=0,6` ist auf M5 kalibriert.
|
||
✅ Verifiziert: `node --check` als Modul, `check_nfalle` sauber, beide IDs im
|
||
ausgelieferten HTML, Live-Werte LONG **⚠ 0,07×ATR** / SHORT 1,49×ATR.
|
||
Fehlt das Element (alte gecachte HTML), greift das Auffang-`$` — kein
|
||
Render-Abbruch. **Zurück:** `web/style.css.bak-2026-08-19-raum` + `v`-Bump.
|
||
|
||
## ⚠⚠ KI-TRADING-BUCH (Illab) AUSGEWERTET — es geht um WORKFLOW, nicht um
|
||
## Signale; die eine echte Lücke ist die VALIDIERUNGS-METHODE (2026-08-19)
|
||
|
||
User reichte „Trading mit KI meistern 2026“ ein. Anderes Genre als das
|
||
Risueño-Buch: keine Chartmuster, sondern Co-Piloten, No-Code-Bots, Scanner,
|
||
Agenten-Pipelines, Broker-APIs. **Triage zuerst.**
|
||
|
||
| Buch-Kapitel | Stand im Projekt |
|
||
|---|---|
|
||
| 1 KI-Co-Pilot mit Live-Feeds, Makro-Prompts | ✅ `core/agent.py` + `daily_levels.py` (z.ai mit Web-Suche) |
|
||
| 2 Kill-Switch, Tagesstopp 2 % | ✅ Circuit-Breaker `daily_loss_limit_pct = 8` (scharf seit 17.08.) |
|
||
| 2 Ausführungs-Logs, CSV-Export | ✅ `trades`-DB + 16 `ctx_*`-Spalten + Wochenreport |
|
||
| 3 Mustererkennung, visuelles Backtesting | ❌ Muster 3× gemessen, Ziel-Trefferquote 13–38 % |
|
||
| 4 Pre-Trade-Checkliste mit Score | ⚠ `_entry_checklist` rechnet weiter, **Dialog am 11.08. entfernt** (User) |
|
||
| 4 Slippage je Trade messen | ✅ `analyze_slippage_holdtime.py` (Median 0,017 $, Tail 4,19) |
|
||
| 5 Sentiment-Filter | ⚠ gebaut, gemessen **nicht robust prädiktiv** (03.08.) |
|
||
| 5 Alt-Daten: Insider, Optionsflüsse | ❌ Aktien-spezifisch, für einen WTI-CFD ohne Entsprechung |
|
||
| 6 Ranking, Rebalancing | ❌ EIN Instrument, EIN Slot — gegenstandslos |
|
||
| 7 Secrets nicht im Skript, Idempotenz | ✅ `oil_widget_config.ini` (gitignored) · Dedup über `_pending_tickets` |
|
||
| 8 Pre-Market-Coach-Agent 08:30 | ✅ Tagesreport 07:30 (Telegram + E-Mail) |
|
||
| 9 Fallback nach 2 Fehlern in Folge | ✅ `agent._mark_dead` (gebaut 05.08. nach dem 18-h-Ausfall) |
|
||
| 10 Momentum-Breakout-Fallstudie | ❌ 2× gemessen, verworfen |
|
||
|
||
⚠ **Was das Buch NICHT liefert:** eine einzige belegte Zahl. Seine Beispiele
|
||
(„Trefferquote steigt von 55 % auf 70 %“, „12 Trades, 9 Gewinner“) sind
|
||
Illustrationen ohne Stichprobe — dieselbe Schwäche wie die S.-203-Tabelle des
|
||
Risueño-Buchs. Als Ideenquelle taugt es, als Beleg nicht.
|
||
|
||
✅✅ **DIE EINE ECHTE LÜCKE — Kapitel 9.2 empfiehlt WALK-FORWARD, dieses Projekt
|
||
prüft mit ZWEI STICHPROBEN.** Das Wort kommt in 8.000 Zeilen CLAUDE.md kein
|
||
einziges Mal vor. Und der Einwand ist nicht akademisch: zwei Hälften haben
|
||
genau **zwei Vorzeichen**. Ein Setup, das in der Hälfte aller Zeitblöcke
|
||
negativ ist, kann trotzdem „beidhälftig positiv“ aussehen — die Regel winkt es
|
||
durch. Genau dieser Fall ist hier live eingetreten: das P(break)-Modell wurde
|
||
am 31.07. auf H1 gefittet, auf H2 mit **AUC 0,715** bestätigt — und lief live
|
||
auf **0,368**. Der Split hielt, die Zeit nicht.
|
||
|
||
**GEMESSEN (`backtest_walkforward.py`): dieselben Daten, dieselbe Regel,
|
||
derselbe Exit — variiert wird NUR die Aufteilung der Zeitachse.**
|
||
| Sicht | Ergebnis |
|
||
|---|---|
|
||
| **(a) 2 Hälften** (Projekt-Methode) | H1 −0,134 · H2 **+0,036** |
|
||
| **(b) 8 Blöcke** (~35 Tage) | **− − − + + + − +** — **4 von 8 positiv** |
|
||
| Spanne ØR über die Blöcke | **−0,301 … +0,214** |
|
||
⚠⚠ **Die schärfste Zeile: die einzigen ZWEI Blöcke, deren 95-%-KI die Null
|
||
ausschliesst, sind BEIDE negativ** (Block 2 [−0,524 … −0,005], Block 3
|
||
[−0,576 … −0,020]). Kein einziger Block ist gesichert positiv.
|
||
✅ Die Hälften-Sicht **verdeckt hier nichts** (H1 entspricht den negativen
|
||
Blöcken 1–3) — aber sie **zeigt auch nichts**: dass das Vorzeichen über die
|
||
Zeitachse **viermal kippt**, ist eine Information, die zwei Stichproben
|
||
prinzipiell nicht liefern können.
|
||
|
||
⚠ **Grenze, vorab benannt:** kürzere Blöcke → breitere KI; ein einzelner
|
||
positiver Block ist NICHT dasselbe wie eine positive Hälfte. Belastbar ist das
|
||
**Vorzeichenmuster**, nicht die Einzelzelle. Und wie schon am 13.08.: die
|
||
ABSOLUTWERTE reproduzieren den dokumentierten Kontrollwert (+0,124/+0,292)
|
||
nicht — meine Auswahl ist strenger (362/559 gegen 1.092/1.807 Trades). Gültig
|
||
ist allein der **interne** Vergleich (a) gegen (b), weil beide auf derselben
|
||
Pipeline laufen.
|
||
|
||
✅ **KONSEQUENZ (keine Code-Änderung):** die 2-Stichproben-Regel bleibt die
|
||
**Mindest**hürde — sie hat 28 Eingriffe korrekt abgewiesen. Aber sie ist eine
|
||
Aussage über **zwei Regime**, keine über **Stabilität über die Zeit**. Für
|
||
alles, was live Geld bewegt, gehört die Blocksicht daneben. `ci95` und
|
||
`stichprobe.paarweise` sind 2026-08-07/17 aus demselben Grund entstanden:
|
||
**jede zusätzliche Achse hat bisher etwas gefunden, das die vorige verdeckte.**
|
||
|
||
⚠ **Vier weitere Buch-Ideen, benannt und NICHT gebaut** (jeweils mit Grund):
|
||
**(1) Gestaffelte Einstiege** (30/30/40 %) — ungemessen; bei EINEM Slot und
|
||
95-%-Margin-Sizing hiesse das kleinere Positionen, also eine Sizing-Frage.
|
||
**(2) Wöchentlicher Drawdown-Stopp** (5 %) — der Tagesbreaker existiert, der
|
||
Wochenbreaker nicht; bei MaxDD 82,8 % nicht abwegig, aber ungemessen.
|
||
**(3) Abgelehnte Signale protokollieren** — der Entry-Dialog wurde am 11.08.
|
||
entfernt, **ohne die Ablehnungen je zu loggen**; das „Warum“ fehlt seitdem.
|
||
**(4) Versioniertes `playbook_rules.json`** — CLAUDE.md IST das Playbook, nur
|
||
unversioniert ausser in git.
|
||
|
||
## ⚠⚠ TRADING-BUCH (Risueño Gómez) AUSGEWERTET — 17 von 20 Konzepten waren
|
||
## schon gemessen, die übrigen DREI fallen durch (`backtest_buch_luis.py`, 2026-08-19)
|
||
|
||
User reichte „Praktisches Trading & Analyse 2.0“ ein (35 Chart-Übungen, SMC +
|
||
Wyckoff + klassische Muster). **Triage zuerst, nicht alles nachmessen.**
|
||
|
||
| Buch-Konzept | Stand im Projekt |
|
||
|---|---|
|
||
| Doppeltop/-boden · Kopf-Schulter · Dreieck · Rechteck · Flagge | `backtest_doubletop` / `_patterns` — Ziel-Trefferquote **13–38 %** |
|
||
| Kanal · Marktstruktur HH/HL · V-Muster | `backtest_structure_tf` — **24/24 Zellen negativ** |
|
||
| Kerzen (Hammer · Engulfing · Abend-/Morgenstern · Pin Bar · Doji) | **4× gemessen, Lehrbuch-Lesart beidhälftig INVERTIERT** |
|
||
| Fibonacci | 04.08. — hält **schlechter als eine Zufallslinie** gleicher Distanz |
|
||
| Order Blocks · FVG · Liquidity Sweeps | SMC = Pivot-Zonen, 6× verworfen |
|
||
| Wyckoff Akkumulation/Distribution | `backtest_consolidation` — Basisrate **48/52 %** |
|
||
| RSI-**Richtung** | bestätigender RSI kontraproduktiv (Pullback-Effekt) |
|
||
| Pullback-Einstieg | ✅ **positiv belegt** — läuft als „⭐ tiefer Pullback“ (+15) |
|
||
| S/R als **Zone** | ✅ seit 17.08. im Chart (`RZ`/`SZ`) |
|
||
| **R:R ≥ 1:2 / 1–2 % Risiko** (Kap. 39) | ✅ **stärkste Übereinstimmung** — s. unten |
|
||
|
||
⚠⚠ **Die Erfolgsraten-Tabelle auf S. 203 ist unbelegt und widerspricht der
|
||
Messung.** Sie behauptet Kopf-Schulter **83 %**, Engulfing 70–75 %, Doppelboden
|
||
70–75 % — ohne Quelle, ohne n, ohne Zeitraum. Gemessen sind hier 13–38 %
|
||
Ziel-Trefferquote und eine invertierte Kerzen-Lesart. **Nicht als Erwartung
|
||
verwendet**; gemessen wurde gegen null.
|
||
|
||
**DIE DREI ECHTEN LÜCKEN — 40k M5, 2 Hälften, kanonischer Exit, Echtkosten,
|
||
Fill bei BERÜHRUNG, 95-%-KI. Alle drei mit KONTROLLGRUPPE:**
|
||
|
||
| | H1 ØR / PF | H2 ØR / PF |
|
||
|---|---|---|
|
||
| **(1) Bollinger-Squeeze** p15 | +0,128 / 1,23 | **−0,133 / 0,80** |
|
||
| *KONTROLLE ATR-Box (live)* | *+0,101 / 1,18* | ***−0,031 / 0,95*** |
|
||
| **(2) RSI-Divergenz** k=5 | **−0,150 / 0,75** | +0,085 / 1,17 |
|
||
| *KONTROLLE Gegenrichtung* | ***+0,251 / 1,59*** [+0,030 … +0,471] | −0,052 / 0,91 |
|
||
| **(3) Drei Soldaten/Krähen** | −0,088 / 0,85 | −0,125 / 0,81 |
|
||
| *KONTROLLE Gegenrichtung* | *+0,031* | *+0,140 / 1,27* |
|
||
|
||
❌ **Keines besteht.** Alle 30 Zellen haben ein KI, das die Null enthält — mit
|
||
**einer** Ausnahme, und die ist die **Gegenrichtung** der RSI-Divergenz.
|
||
|
||
⚠⚠ **BEFUND 1 — die Bollinger-Definition ist SCHLECHTER als die ATR-Box, die
|
||
live läuft.** Das war der wertvollste Test, weil es kein neues Signal ist,
|
||
sondern eine Variante des **einzigen validierten Setups**: der Squeeze misst
|
||
Kompression über die Box-Spanne (≤ 2,5×ATR), das Buch über die Bandbreite
|
||
(SMA20 ± 2σ). In H2 liegt Bollinger bei −0,133 gegen −0,031 der Box — und
|
||
handelt dabei nur halb so oft (324 gegen 657). **Kein Grund zu wechseln.**
|
||
✅ Nebenbefund: die ATR-Box-Kontrolle kippt hier ebenfalls (H1 +0,101 / H2
|
||
−0,031) — erwartbar, weil dieser Lauf **ohne** Nacht-Gate, Cooldown und
|
||
Dedup rechnet. Belastbar ist der **interne** Vergleich, nicht der Absolutwert.
|
||
|
||
⚠⚠⚠ **BEFUND 2 — DIE FÜNFTE BESTÄTIGUNG DERSELBEN INVERSION.** In **allen
|
||
drei** Konzepten ist die Kontrollgruppe (Gegenrichtung) besser als die
|
||
Buch-Lesart; bei der RSI-Divergenz schliesst sie in H1 sogar die Null aus
|
||
(+0,251, KI [+0,030 … +0,471]). Dasselbe Muster wie bei Kerzen (4×), beim
|
||
Winkel-Bonus und beim Reversal: **die Lehrbuch-Deutung von Umkehr- und
|
||
Fortsetzungssignalen zeigt in diesem Markt systematisch in die falsche
|
||
Richtung.** ⚠ Daraus folgt NICHT „einfach umdrehen“ — die Kontrollen kippen
|
||
zwischen den Hälften genauso.
|
||
|
||
✅✅ **WAS DAS BUCH BESTÄTIGT, IST SEIN KAPITEL 39 — und das ist die eigentliche
|
||
Ausbeute.** „Der Erfolg hängt nicht von der Trefferquote ab, sondern vom
|
||
Verhältnis ØGewinn/ØVerlust“ und „nie mehr als 1–2 % je Trade“. Genau das
|
||
misst dieses Projekt seit Monaten unabhängig: der Squeeze hat **73 %
|
||
Trefferquote und trotzdem PF 0,39**, weil das Verhältnis bei **0,14** liegt;
|
||
`analyze_sizing_live.py` misst **MaxDD 82,8 %** und einen schlechtesten
|
||
Einzeltrade von **−381 €** bei 95-%-Margin-Sizing, gegen **22,2 % MaxDD** bei
|
||
0,5 % Risiko je Trade. **Das Buch und die eigenen Zahlen sagen dasselbe — und
|
||
es ist der einzige Punkt, an dem noch nichts umgesetzt ist.**
|
||
**26., 27. und 28. verworfener Signal-Eingriff.**
|
||
|
||
⚠ **Grenzen:** 40k Bars (nicht die üblichen 80k) — für ein **Ausschluss**-Urteil
|
||
ausreichend, für eine Bestätigung wäre es zu wenig. Je eine Definition pro
|
||
Konzept. Exit-Slippage nicht modelliert.
|
||
|
||
## ⚠⚠ DIE KLEIN-CLOSE-FALLE REPRODUZIERT AUF DEN EIGENEN TRADES (2026-08-19)
|
||
|
||
Suche nach weiteren Optimierungen. Erstmals die `ctx_*`-Telemetrie (seit 04.08.)
|
||
auf der Ausführungs-Achse ausgewertet — **121 manuelle Trades** mit Kontext,
|
||
P&L **je Lot** (absolute Euro sind bei wechselnder Größe ungültig, Lehre 04.08.).
|
||
|
||
| Raum zum Level IN Trade-Richtung | n | Treffer | EUR/Lot |
|
||
|---|---|---|---|
|
||
| **< 0,3×ATR** | 14 | **79 %** [57…100] | **−7,57** |
|
||
| **0,3–0,6×ATR** | 13 | 54 % | **−11,51** |
|
||
| 0,6–1,2×ATR | 30 | 67 % | −0,17 |
|
||
| > 1,2×ATR | 60 | 52 % | −0,74 |
|
||
|
||
⚠⚠ **Die erste Zeile ist die dokumentierte Klein-Close-Falle, live und auf
|
||
eigenem Geld: 79 % Trefferquote — und trotzdem der zweitschlechteste Ertrag.**
|
||
`backtest_entryroom.py` hatte genau diese Signatur über 80k Bars gemessen
|
||
(„79–82 % WR und trotzdem PF<1“). Der Gewinn ist am Level gedeckelt, der
|
||
Verlust läuft bis zum Stop. **Beide Gate-Zonen (< 0,6) sind die einzigen mit
|
||
zweistellig negativem Ertrag je Lot.**
|
||
✅ **Kontrolle bestanden:** derselbe Split auf der **Gegen**-Seite (Stop-Level)
|
||
zeigt kein solches Muster (+0,35 / −3,91 / −4,23) — es ist also nicht „irgendein
|
||
Level in der Nähe“, sondern spezifisch die **Ziel**-Seite. Genau das misst das Gate.
|
||
⚠ **n=14 und n=13, die Intervalle sind breit** — das ist eine Bestätigung des
|
||
über 80k Bars gemessenen Musters, kein eigenständiger Beleg.
|
||
|
||
⚠⚠ **Und die Information ist seit dem 11.08. im Moment der Entscheidung
|
||
UNSICHTBAR:** der Entry-Dialog wurde auf User-Wunsch entfernt, und mit ihm der
|
||
Ausrichtungs-Split. Der Raum steht heute nur in Gate ③b der M15-Karte — nicht
|
||
dort, wo geklickt wird. **Vorschlag (nicht gebaut): den Raum je Richtung in die
|
||
Order-Leiste neben LONG/SHORT** — kein Dialog, kein Klick, keine Sperre, nur eine
|
||
Zahl an der Stelle der Entscheidung. Passt zur Projektlinie „sichtbar machen,
|
||
nicht bevormunden“ (User-Übersteuerungen liefen gemessen 68 % WR).
|
||
|
||
## ⚠⚠ KORREKTUR: der Circuit-Breaker sperrt NUR die autonomen Trades (2026-08-19)
|
||
|
||
Beim Scharfstellen am 17.08. auf 8 % habe ich `analyze_sizing_live.py` zitiert
|
||
(+739 € Endstand, MaxDD −23,6 Pp). **Diese Simulation sperrt nach dem Auslösen
|
||
ALLE Trades des Tages — der Live-Code sperrt nur `_squeeze_guard`, also die
|
||
autonomen.** Gemessen: **von 461 Trades der letzten 30 Tage sind 36 autonom
|
||
(7,8 %)** — die sperrende Hälfte des Breakers ist damit heute praktisch **inert**.
|
||
✅ **Was WEITER wirkt und real ist: das SCHLIESSEN.** `_check_circuit_breaker`
|
||
schließt die offene Position unabhängig davon, wer sie eröffnet hat — und der
|
||
größte Posten der Simulation (07.08., +556,82 €) war genau das.
|
||
⚠ Die Einschränkung stand in CLAUDE.md, ich habe sie beim Scharfstellen aber
|
||
nicht in die Nutzen-Zahl übersetzt. **Die 8 % bleiben richtig gesetzt; die
|
||
erwartete Wirkung ist kleiner als zitiert.** Manuelle Orders zu sperren wäre eine
|
||
User-Entscheidung gegen die Projektlinie, keine Messfrage.
|
||
|
||
## ⚠⚠ KONFIDENZ-SCHWELLE 55 GEMESSEN: SIE TRENNT NICHTS — keine Änderung
|
||
## (`backtest_conf_gated.py`, 2026-08-19)
|
||
|
||
Punkt (2) der Verbesserungsliste. `min_conf` blockt live **11,4 %**. Die 55
|
||
stammen aus `backtest_conf.py` — gemessen an `_build` **allein**, ohne
|
||
Breakout-Bestätigung und Raum-Gate (also eine andere Population, 77,9 % gegen
|
||
19,5 % gerichtet) und mit älterem Exit. Neu: voller Gate-Stack, `_MIN_CONF`
|
||
für die Messung auf 0, kanonischer Exit, Echtkosten, 2 Halbjahre, 95-%-KI,
|
||
**beide Ausführungs-Achsen**.
|
||
|
||
**A) Markt — der Verlauf ist NICHT gleichgerichtet, also Rauschen:**
|
||
|
||
| Band | H1 ØR | H2 ØR |
|
||
|---|---|---|
|
||
| 0–44 | **−0,014** (bestes H1!) | −0,072 |
|
||
| 45–54 | −0,141 | −0,082 |
|
||
| **55–64** | **−0,259** (schlechtestes) | −0,046 |
|
||
| 65–74 | −0,196 | −0,017 |
|
||
| 75–100 | −0,180 | +0,018 |
|
||
|
||
⚠⚠ **Die Schwelle bewegt gar nichts:** ≥ 0 liefert H1 −0,189, ≥ 55 liefert
|
||
**−0,197** — also minimal SCHLECHTER. Über alle fünf Schwellen liegt der ØR in
|
||
H1 zwischen −0,180 und −0,197. **11,4 % der Signalmenge werden für eine
|
||
Wirkung von 0,017 R weggeworfen.**
|
||
|
||
**B) ruhende Order am Level — hier trennt die OBERE Hälfte, aber:**
|
||
|
||
| Schwelle | H1 ØR / ΣR | H2 ØR / ΣR |
|
||
|---|---|---|
|
||
| ≥ 0 | −0,062 / −83,6 | +0,078 / **+165,6** |
|
||
| **≥ 55 (live)** | **−0,070 / −82,1** | **+0,081 / +146,1** |
|
||
| ≥ 65 | −0,074 / −77,8 | +0,103 / +133,0 |
|
||
| **≥ 75** | **−0,001 / −0,5** | **+0,147 / +82,8** |
|
||
|
||
✅ **≥ 75 schlägt die Live-Schwelle im ØR in BEIDEN Hälften** (−0,001 gegen
|
||
−0,070 · +0,147 gegen +0,081) und ist die einzige Zelle, in der H1 den
|
||
Breakeven erreicht. ❌ **Aber ΣR fällt** (+82,8 gegen +146,1) — die vorab
|
||
fixierte Regel verlangte **beides**. **Nicht geändert.**
|
||
⚠ Das ist wieder der dokumentierte Spezifikations-Konflikt aus
|
||
`backtest_cost_gate.py`: ØR und ΣR laufen mechanisch gegeneinander, wenn die
|
||
Varianten verschieden viele Trades handeln (416 gegen 1.175). Die Regel wurde
|
||
**nicht nachträglich gelockert**; beide Spalten stehen deshalb oben.
|
||
|
||
✅ **Dritte unabhängige Bestätigung des Ausführungs-Hebels am selben Tag:** bei
|
||
≥ 55 liefert der Level-Einstieg −0,070/+0,081 gegen −0,197/−0,015 am Markt =
|
||
**+0,127 / +0,096 R**, größengleich zu `backtest_entryroom_level.py`
|
||
(+0,109/+0,088). **Die Achse trägt reproduzierbar, das Signal darunter nicht.**
|
||
**27. verworfener Signal-Eingriff.**
|
||
|
||
## ⚠ M15-KARTE: SELEKTIVITÄT GEMESSEN — weiter kein Urteil möglich (2026-08-19)
|
||
|
||
Punkt (3). Ausgewertet ab dem Hysterese-Stichtag 12.08. (5.979 Wechsel).
|
||
⚠ **In ZEIT statt in Wechseln gerechnet** — die Wechsel-Zahl (49 % grün)
|
||
überschätzt, weil grüne Phasen kürzer sind:
|
||
|
||
| Zustand | Anteil der Zeit |
|
||
|---|---|
|
||
| gelb | **62,2 %** (104,0 h) |
|
||
| **grün** | **37,8 %** (63,1 h) |
|
||
| rot | 0,0 % (0,1 h) |
|
||
|
||
| handlungsrelevant (grün + gerichtet) | Treffer | n | 95-%-KI |
|
||
|---|---|---|---|
|
||
| 30 min roh | 51,2 % | 2.869 | [49,3 … 53,0] |
|
||
| **30 min entkoppelt** | **54,1 %** | **205** | **[47,3 … 60,5]** ⚠ |
|
||
| 60 min entkoppelt | 51,5 % | 204 | [44,1 … 58,3] ⚠ |
|
||
|
||
**Beide Intervalle enthalten die 50 %** — unverändert **nicht belegt**, aber
|
||
auch nicht widerlegt. Aus 2.869 rohen Zeilen bleiben **205** unabhängige; die
|
||
Latte von 350 (wie bei `pbreak_accuracy_v2`) ist zu **59 %** erreicht, also in
|
||
gut zwei Wochen entscheidbar. ✅ Die Hysterese vom 12.08. wirkt: der
|
||
entkoppelte Wert stieg von 53 auf 54,1 %.
|
||
⚠⚠ **Die naheliegende Schärfung ist damit schon beantwortet:** Gate ③b
|
||
(Entry-Raum) ins Banner zu ziehen würde auf einer Dimension filtern, die am
|
||
selben Tag als **nicht trennend** gemessen wurde — was `entry_room`
|
||
durchlässt, ist selbst beidhälftig negativ. **Nichts geändert.**
|
||
|
||
## ⚠⚠ ENTRY-RAUM-GATE GEGEN AUSFÜHRUNG = NICHT GEBAUT — aber der Lauf
|
||
## zeigt etwas Wichtigeres (`backtest_entryroom_level.py`, 2026-08-19)
|
||
|
||
Anlass: User-Frage nach Verbesserungen der Empfehlung. `entry_room` blockt
|
||
über 7 Tage **60,6 %** (gerichtet nur **2,7 %**) und ist damit der mit Abstand
|
||
größte Blocker — kalibriert aber am 2026-07-16 mit **Markt**-Einstieg. Seit dem
|
||
05./06.08. ist belegt, dass eine **ruhende Order AM Level** etwas anderes ist:
|
||
Markt-Einstieg unter einem Widerstand = in die Wand kaufen, BUY_STOP am
|
||
Widerstand = Ausbruch daraus. **Gegenteilige Trades, das Gate blockt beide.**
|
||
Gemessen wurde deshalb die GEBLOCKTE Gruppe getrennt (Muster `backtest_htf_vola`),
|
||
80k M5, voller Gate-Stack, `room_info` = die echte Gate-Methode, Fill bei
|
||
BERÜHRUNG, kanonischer Exit, Echtkosten, 95-%-KI.
|
||
|
||
| Gruppe | H1 ØR / PF | H2 ØR / PF |
|
||
|---|---|---|
|
||
| GEBLOCKT · Markt | −0,228 / 0,64 | +0,008 / 1,01 |
|
||
| **GEBLOCKT · Level (ruhend)** | **−0,119 / 0,81** | **+0,096 / 1,18** |
|
||
| durchgelassen · Markt | *n=1* | *n=3* |
|
||
| **freie Bahn · Markt (= was heute durchkommt)** | **−0,145 / 0,75** | **−0,071 / 0,87** |
|
||
|
||
❌ **Regel verfehlt** — H1 bleibt in **allen** Varianten negativ, auch über alle
|
||
vier Nachbarn (Puffer 0,00/0,05/0,10 × Gültigkeit 6/12/24). H2 allein würde
|
||
bestehen (KI [+0,015 … +0,180]) — klassisches Regime-Kippen. **Nichts gebaut.**
|
||
|
||
✅ **BEFUND 1 — die Ausführung trägt auch hier, beidhälftig:** auf **denselben**
|
||
Signalen ist die ruhende Order besser als der Markt, **+0,109 (H1) / +0,088 (H2)**
|
||
R je Trade, Trefferquote 35→40 % bzw. 43→48 %. Gleiche Richtung und
|
||
Größenordnung wie beim Squeeze (+0,32) — nur reicht es hier nicht, um H1 ins
|
||
Plus zu drehen. **Der Ausführungs-Hebel ist real, das Signal darunter trägt nicht.**
|
||
|
||
⚠⚠ **BEFUND 2 — DAS GATE IST IN WAHRHEIT BINÄR, und das war unbekannt.** Von
|
||
4.258 Signalen fallen **4** (!) in „Level vorhanden, aber weit genug“. Alles
|
||
andere ist entweder geblockt (2.624) oder **freie Bahn ohne jedes Level**
|
||
(1.630). Das Gate fragt praktisch nur: *gibt es überhaupt ein M5-Pivot in
|
||
Trade-Richtung?* — passend zum dokumentierten Median-Abstand von **0,61×ATR**,
|
||
der exakt auf der Schwelle 0,60 liegt.
|
||
|
||
⚠⚠⚠ **BEFUND 3 — was das Gate DURCHLÄSST, ist selbst beidhälftig negativ**
|
||
(−0,145 / −0,071, PF 0,75/0,87). In H2 ist es sogar **invertiert**: die
|
||
geblockte Gruppe steht bei +0,008, die durchgelassene bei −0,071 — dort blockt
|
||
das Gate die BESSERE Hälfte. In H1 ist es umgekehrt richtig. **Das Gate
|
||
sortiert Verluste um, es wählt keine profitable Population aus.**
|
||
⚠ Daraus folgt **nicht** „Gate raus“ — ohne Gate käme die in H1 klar schlechtere
|
||
Gruppe dazu. Es folgt: **an dieser Stellschraube ist nichts zu gewinnen**, weil
|
||
die Grundgesamtheit keinen Edge hat. Deckt sich mit der Selbstmessung
|
||
`rec_outcomes` (49,2 % / 48,3 % Treffer, n=61, 13.–18.08.), die den
|
||
Münzwurf-Befund von `analyze_signal_live.py` unabhängig **live** reproduziert.
|
||
|
||
⚠ Abgrenzung zu `backtest_entryroom.py` (2026-07-16): dort lief der **S/R-Close-
|
||
Exit**, hier der kanonische — die absoluten Zahlen sind nicht vergleichbar,
|
||
belastbar ist der **interne** Vergleich (alle Gruppen, gleicher Exit).
|
||
⚠ Nicht modelliert: Exit-Slippage; und es ist **kein** sequentieller Ein-Slot-Lauf
|
||
(verglichen werden Populationen, nicht Kontostände).
|
||
**26. verworfener Signal-Eingriff.**
|
||
|
||
## Gesamtprüfung 2026-08-11 (nach `docs/review-prompt.md`)
|
||
|
||
Zweiter vollständiger Durchlauf. **Drei echte Defekte**, alle behoben; keine
|
||
Strategie-Änderung.
|
||
|
||
⚠⚠ **BEFUND 1 — `rec_outcomes` war seit dem Einbau (06.08.) STILL TOT: 0 Zeilen
|
||
in 5 Tagen.** `engine._run_analysis` las `s.get("pb_feats")` — `s` ist aber der
|
||
**MT5Data**-Snapshot (Preis/Trend/Konto); `pb_feats` lebt im **Wave**-Snapshot.
|
||
Ergebnis: `_atr` immer 0,0 → die Schreibbedingung `_atr > 0` nie erfüllt.
|
||
✅ **Live belegt:** `market` hat **kein** `pb_feats`, `wave` hat es (ATR 0,12).
|
||
⚠⚠ **KORREKTUR 2026-08-12: der Fix hat NICHT gewirkt.** Nach dem Neustart am
|
||
11.08. 22:45 gab es bis 09:00 mindestens **10 gerichtete Episoden** — `rec_outcomes`
|
||
stand weiter bei **0 Zeilen**. Die Prämisse (falsches dict) ist belegt, die
|
||
**Wirkung war es nicht** — ich habe „behoben" geschrieben, ohne sie zu prüfen.
|
||
⚠ Warum es von aussen nicht feststellbar war: der `except`-Zweig loggte auf
|
||
**DEBUG**, der Logger steht auf INFO — genau die Blindheit, die den Ausfall fünf
|
||
Tage getragen hat. **Jetzt instrumentiert** (2026-08-12): Erfolg UND Fehlschlag
|
||
melden sich auf INFO/WARNING, inkl. `atr`/`px`/`pb_feats`-Zustand. Es feuert nur
|
||
bei einem echten Richtungswechsel, ist also kein Lärm. Die Ursache ist **offen**
|
||
bis zur nächsten Episode.
|
||
✅ Der **Telemetrie-Puls** meldet den Zustand seitdem täglich — er ist der Grund,
|
||
dass die ausbleibende Wirkung überhaupt auffiel.
|
||
⚠ **Warum keine Prüfung es fing:** es ist **kein** `NameError` (ruff F821 sieht
|
||
nichts), sondern ein falsches dict — syntaktisch einwandfrei. Und das umgebende
|
||
`except` loggt auf **DEBUG**, der Logger steht auf INFO. Exakt die Fehlerklasse,
|
||
vor der der `wave_snap`-Fallstrick vom 04.08. schon einmal gewarnt hat.
|
||
✅ Aufgefallen NUR, weil die Tabelle in Durchgang 4 mit 0 Zeilen **neben ihren
|
||
Schwestern** stand (`m15_states` 3.143 · `cone_checks` 263 ·
|
||
`pbreak_predictions` 3.775). Damit hat die Selbst-Kalibrierung der Empfehlung
|
||
fünf Tage lang nichts gemessen.
|
||
✅✅ **GENERALISIERT: `measurement_reminder.telemetrie_puls()`** — jede
|
||
Telemetrie-Tabelle muss einen PULS haben; schweigt eine, während die anderen
|
||
schreiben, ist der Logger tot. 7 Tabellen überwacht, Toleranz Faktor 20 der
|
||
erwarteten Kadenz (ein Wächter, der oft warnt, wird ignoriert). ⚠
|
||
`candles_m1.time` ist ROHE Brokerzeit → Offset abgezogen, sonst 3 h Fehlalarm.
|
||
**Erster Lauf meldet genau diesen einen Fund**, alle übrigen grün.
|
||
|
||
⚠⚠ **BEFUND 2 — DER DIVERGENZ-WÄCHTER WAR SELBST DRIFTEND.** Er verglich
|
||
Abschnitt A gegen **43 % WARTEN** — ein Maßstab, den CLAUDE.md seit dem
|
||
**2026-08-05 ausdrücklich für ungültig erklärt** (`backtest_dist.py` misst
|
||
`_build` ALLEIN, ohne Breakout-Bestätigung und Entry-Raum-Gate; mit vollem
|
||
Live-Stack sind es **80,5 %**). Zusätzlich zählte er `entry_room` als
|
||
„live-only", obwohl es seit dem 04./05.08. in `backtest_breakout_gated.py` mit
|
||
dem **echten** `_room_gate` modelliert wird.
|
||
| | vorher | nachher |
|
||
|---|---|---|
|
||
| A) Abweichung WARTEN | **+50,8 Pp ⚠** | **+13,3 Pp OK** |
|
||
| B) „Nur-Live-Gates" | **63,9 %** | **5,0 %** |
|
||
Der Wächter behauptete also einen Deployment-Drift, den es dort nicht gibt —
|
||
und „ein Wächter, der immer warnt, wird ignoriert" ist die eigene Lehre vom
|
||
02.08. (damals für die Modell-Epochen, hier für die Konstanten).
|
||
|
||
✅ **BEFUND 3 — P(break) ist wieder kalibriert.** Δ **−1,7 Pp** (Vorhersage
|
||
40,4 %, real 38,7 %, n=106 entkoppelt) nach **+14,3 Pp** am 07.08. Das stützt
|
||
den damaligen Schluss, dass die „gewanderte Basisrate" ein **Zeitraum-Effekt**
|
||
war, kein Modellversagen.
|
||
|
||
**Sauber (keine Befunde):** `check_nfalle` · `ruff F821` · 53 Tests ·
|
||
`node --check` · **0 fehlende Frontend-IDs** von 93 · der einzige Thread-Aufruf
|
||
von `refresh_market` nimmt `mt5_lock` selbst · alle `.time`-Nutzungen sind
|
||
Vergleiche unter Broker-Zeiten oder die dokumentierte `candles_m1`-Ausnahme ·
|
||
die 3 Ticket-Wechsel-Blöcke haben ihre Retry-Wächter · TF-Churn Median 23 min.
|
||
|
||
**Performance:** Snapshot-Median **15,9 ms** (p90 19,4), gegen 13 ms am 02.08. —
|
||
der Zuwachs deckt sich mit den seither gebauten Feldern und liegt weit unter dem
|
||
1-s-Budget. Alle heißen Abfragen laufen über einen Index (`recommendations`
|
||
1,2 ms · `candles_m1` über den PRIMARY KEY 1,9 ms · `trades` 0,1 ms).
|
||
**Keine Optimierung nötig** — Micro-Tuning ohne Messung wäre hier wertlos.
|
||
|
||
⚠ **Toter Code: keine Löschempfehlung.** 13 Snapshot-Felder haben keinen Leser
|
||
in `app.js` — davon habe ich am selben Tag **vier** benutzt, um Deployments zu
|
||
verifizieren (`close_notify_min`, `squeeze_pending`, `squeeze_night_hours`,
|
||
`squeeze_max_chase_atr`). Der Snapshot ist die **Diagnose-Oberfläche** und laut
|
||
`tools/deploy.py` Schritt 4 der einzige belastbare Beweis, dass neuer Code
|
||
läuft — sie zu entfernen würde die Verifikation kaputtmachen. Einzig
|
||
`cost_ratio` ist ein echter Waise (speiste den Kosten-Chip in `vd-consensus`,
|
||
den es nicht mehr gibt); er kostet eine Division und bleibt.
|
||
⚠ **Bekannt und unverändert:** 47 Backtests haben eine eigene Exit-Kopie (36
|
||
nutzen `core/exit_model`), `_atr_series` existiert in **86** Dateien. Umstellen
|
||
verletzt die Migrations-Regel — ihre publizierten Zahlen würden sich
|
||
verschieben. Alle 2026-08-11 neu gebauten Skripte nutzen den geteilten Kern.
|
||
⚠ **Benannt, nicht geändert:** `auto_squeeze` steht in `runtime_state.json` auf
|
||
`false`, in der ini auf `true`. Verhalten ist korrekt (runtime gewinnt), der
|
||
Zustand aber irreführend — wird `runtime_state.json` gelöscht, schaltet sich der
|
||
autonome Einstieg wieder ein. **User-Entscheidung vom 11.08., 14:10.**
|
||
|
||
## Gesamtprüfung 2026-08-02 (nach `docs/review-prompt.md`)
|
||
|
||
Erster vollständiger Durchlauf des Review-Prompts. **Keine Strategie-Änderung** — alle
|
||
Fixes betreffen Telemetrie, Doku und toten Code.
|
||
|
||
**Behoben:**
|
||
1. **`analyze_divergence.py` kannte keine Epochen** — und war damit selbst
|
||
driftanfällig. D0 mischte Vorhersagen des ALTEN und des am 31.07. nachtrainierten
|
||
P(break)-Modells und meldete dessen Fehlkalibrierung als aktuellen Alarm (das neue
|
||
Modell hatte **n=0**, weil der Markt seit Fr 22:55 zu ist); B las die
|
||
Prä-Migrations-`NULL`s von `block_reason` als blinden Fleck („None 66,8 %"); C
|
||
druckte bei 0 Zeilen ein „OK", obwohl es fehlende Daten waren. Neu: `_EPOCHS` +
|
||
`_clamp()` schneiden jede Auswertung auf den Beginn ihrer eigenen Datenlage.
|
||
⚠ **Beim Nachtrainieren eines Modells das Datum in `_EPOCHS` mitziehen** — sonst
|
||
schlägt der Wächter wochenlang Fehlalarm, und ein Wächter, der immer warnt, wird
|
||
ignoriert.
|
||
2. **Konsens-Pfeil: doppelte Arbeit für eine unsichtbare Linie.** Der MQL5-Export
|
||
rechnete im ~5-s-Takt ein **komplettes zweites `_verdict()`** für `AR;K` — der
|
||
Indikator verwirft die Zeile aber seit v1.33 per Default (`InpShowConsensus=false`).
|
||
Neu `[trading] export_consensus_arrow` (Default **false**, spiegelt den Indikator).
|
||
Nebeneffekt beseitigt: das zweite Verdict konnte vom Verdict im Snapshot abweichen.
|
||
3. **`/api/autosqueeze` loggt jetzt die Herkunft** (`[WEB] …`) wie `/api/autosignal`.
|
||
Vorher war bei einem Zustandswechsel nicht belegbar, ob Dashboard oder Code —
|
||
bei einem Schalter für autonome Echtgeld-Einstiege zu wenig.
|
||
4. **Reversal-Kennzahl an der Fundstelle korrigiert.** „ØR +0,185 · PF 1,35 · 70 %
|
||
Treffer" stand unverändert in der Reversal-Sektion, die Widerlegung 2 000 Zeilen
|
||
später im Legacy-Recheck. Wer die Sektion liest, sah die Korrektur nie.
|
||
5. Zwei tote `import datetime` in `core/notify.py` entfernt (beide Funktionen nutzen
|
||
`_time`), funktional nachgetestet.
|
||
|
||
**Geprüft und sauber:** 0 fehlende Frontend-IDs (von 86) · nur 2 Config-Schlüssel ohne
|
||
Leser (beide dokumentiert dormant) · Snapshot-Median **13 ms**, alle DB-Abfragen <13 ms,
|
||
Indizes vorhanden → **keine Performance-Maßnahme nötig** · 124 Datei- und 83
|
||
Funktionsreferenzen in CLAUDE.md stimmen · Asset- und Indikator-Version konsistent.
|
||
|
||
**Zwischenverdacht zurückgezogen:** „`block_reason` erklärt nur 33 % der WARTEN" war
|
||
ein reines Migrations-Artefakt (Spalte kam am 31.07.); seit 01.08. **100 % Abdeckung**.
|
||
|
||
**Offen (nicht behoben, bewusst):**
|
||
- ✅ **ERLEDIGT 2026-08-05: `auto_flip_close` ist jetzt SICHTBAR** (`#flip-note` in
|
||
der Meldungen-Karte, v=139). Zeigt Schwelle und Auslöse-Zähler und **nennt
|
||
ausdrücklich die Messung** („⚠ gemessen in beiden Halbjahren negativ", −92/−160 R)
|
||
— ein blanker Zähler hätte wie ein Erfolg ausgesehen. Der Exit bleibt AN
|
||
(User-Wunsch), aber die Entscheidung ist jetzt informiert.
|
||
⚠ **Live-Gegenprobe 2026-08-05 (n=4):** die 4 Auslösungen brachten **+284,22 €**;
|
||
mit dem kanonischen Exit ab demselben Einstieg wären es **+183,51 €** gewesen —
|
||
der Flip war auf diesen Trades also **+100,71 € BESSER**. ⚠ Das ist KEIN Beleg:
|
||
n=4, und **+101 € stecken in EINEM Trade** (04.08. 16:15: +188,64 gegen +87,68).
|
||
Der 04.08. ist genau der Ausreißertag, der allein die 30-Tage-Bilanz trägt. Gegen
|
||
~2.000 Trades je Hälfte im Backtest (jede Variante, beide Hälften negativ) wiegt
|
||
das nichts — dieselbe Falle wie der 12k-Vorlauf beim Kerzen-Fade, der +0,291
|
||
zeigte und bei 80k verschwand. **Der Einwand ist geprüft, nicht bestätigt.**
|
||
✅✅ **ABGESCHALTET am 2026-08-05 (User-Entscheidung nach Vorlage der Messung),
|
||
`[trading] auto_flip_close = false`.** Erwarteter Gewinn 35–250 R je nach
|
||
Messung, Nachteil praktisch keiner — **der Alarm bleibt unverändert**
|
||
(Blinken/Ton/Telegram bei Signal-Flip im Plus). Der Alarm-Pfad
|
||
`_check_close_alert` hängt an `_run_analysis`, NICHT am `_pos_loop`-Aufruf von
|
||
`_check_flip_close` — nur die automatische Ausführung entfällt. Damit ist wieder
|
||
der dokumentierte Ursprungszustand hergestellt: „Flip bleibt Alarm + menschliche
|
||
Entscheidung" (User-Übersteuerungen liefen gemessen 68 % WR).
|
||
Live verifiziert nach Neustart: `flip_close=False`, Alarm-Zähler unberührt.
|
||
⚠ **Kein `runtime_state.json`-Override vorhanden** (anders als bei `auto_signal`,
|
||
wo ein UI-Toggle die ini still überschrieb) — die ini-Änderung greift also direkt.
|
||
Neu im **Config-Wächter** (`measurement_reminder.py`, validiert = `false`): wird
|
||
der Schalter je wieder auf `true` gesetzt, läuft ein gemessen negativer Exit auf
|
||
echtem Geld, und das soll auffallen. Zurückholen: `auto_flip_close = true`.
|
||
- **`breakout_k=0,3` ruht weiter auf dem `LEGACY_BE10`-Exit** (Breakeven 1,0 statt
|
||
live 1,3). Der Legacy-Recheck hat EIA und Dead-Hours nachgerechnet, `breakout_k`
|
||
nicht. Kandidat für den nächsten Recheck.
|
||
- Die 5 Zähler-Snapshot-Felder (`close_alert_count` usw.) werden von nichts abgefragt.
|
||
|
||
## 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-08-01 |
|
||
|---|---|---|
|
||
| P(break) erneut auswerten (nach Nachtraining) | ≥800 Vorhersagen ab 01.08. | 0 |
|
||
| **Modul-Audit: zweiter Durchgang** | ≥28 Tage seit dem 1. Audit | 2 (6 %) |
|
||
| Verdict-Module auf Prädiktivität (`verdict_votes`) | ≥25 000 Verdicts | 8142 (33 %) |
|
||
|
||
⚠ **Offene Modul-Fragen für diesen Lauf (Stand 2026-08-07, aus dem Zwischenstand über 15.544 Zeilen):** **(a) Liq-Trend** (0,5) — +0,20/+0,32 über nur 132 Episoden; ⚠ **dieselbe Quelle wie das am 06.08. entmachtete Orderbuch-Modul**, und HL-Preisdaten laufen gemessen hinter Pepperstone her (51 % Treffer nach 6 s). Als **einziges verbliebenes HL-Modul mit Stimmrecht** zuerst prüfen. **(b) Muster** (0,25) — +1,27/+0,67 bei n=88, zu dünn. **(c) H1** (1,5) — nur 19 Episoden, trägt aber 38 % des Nadel-Einflusses; prädiktiv nicht beurteilbar. **(d)** Elliott und Orderbuch sind entmachtet (`used=False`), werden aber **weiter geloggt** — die Reihe darf nicht abreißen.
|
||
| **Manuelle Trades: Stufe 2** (was trennt Gewinner von Verlierern?) | ≥300 manuelle Trades **mit `ctx_*`** | 0 (Sammlung ab 04.08.) |
|
||
| **Auto-Squeeze: B5-Abbruchprüfung** (Latte vorab fixiert) | ≥20 Squeeze-Trades ab 01.08. | 2 (10 %) |
|
||
| ~~M30-Level-Umstellung live gegenprüfen~~ | **ERLEDIGT 04.08.** — s. u. | ✅ |
|
||
| M1-Squeeze Variante B | ≥180 Tage `candles_m1` | 14 (8 %) |
|
||
| News-Score-Prädiktivität | ≥90 Tage Sammlung | 9 (10 %) |
|
||
|
||
⚠ **Der Config-Wächter liest jetzt auch `runtime_state.json` (Fix 2026-08-01).** Er
|
||
verglich vorher nur die **ini** gegen die validierten Werte — die UI-Schalter leben aber
|
||
in `runtime_state.json` und werden beim Start **über** die ini gelegt. Dadurch meldete er
|
||
„alles auf dem gemessenen Stand", während live etwas anderes lief. Real: `auto_signal`
|
||
stand in der ini auf `false`, war aber durch einen Dashboard-Toggle dauerhaft `true` —
|
||
4 der 7 Bot-Trades vom 31.07. kamen aus diesem gemessen-negativen Pfad. `_cfg_now()`
|
||
bildet den Vorrang jetzt genauso ab wie der Engine-Start; mit einem Szenario verifiziert
|
||
(runtime `true` gegen validiert `false` → wird gemeldet). Neu überwacht: `auto_signal`
|
||
(soll `false`) und `auto_squeeze` (soll `true`).
|
||
|
||
### ✅ M30-Level-Umstellung live gegengeprüft (04.08.) — bestätigt, mit einem Vorbehalt
|
||
|
||
| Zeitraum | n | Med. Lots | **EUR/Lot** | Ø Move |
|
||
|---|---|---|---|---|
|
||
| **vorher** (M5-Level) | 89 | 0,81 | **+17,53** | 0,202 $ |
|
||
| **nachher** (M30-Level) | 28 | 0,53 | **+36,92** | 0,427 $ |
|
||
|
||
Ertrag **je Lot mehr als verdoppelt**, Ø-Kursbewegung je Trade ebenfalls — deckt sich
|
||
mit der Backtest-Erwartung („R-Ertrag rund doppelt") und mit dem Mechanismus (M30-Level
|
||
liegen 1,95×ATR auseinander statt 0,61×ATR, der Trade läuft also länger).
|
||
|
||
⚠⚠ **Die NORMIERUNG war entscheidend, sonst wäre der Schluss falsch gewesen.**
|
||
Unnormiert sah der 04.08. wie der Treiber aus (Ø **+55,41 €** je Trade gegen +19,30 €
|
||
im Rest). **Je Lot ist heute aber praktisch identisch zum Rest** (+38,81 gegen +35,87)
|
||
— der Unterschied war reine Positionsgröße (Median 1,36 statt 0,46 Lots, weil das
|
||
Konto an diesem Tag von ~800 auf ~1.400 € wuchs). Ø-EUR-Vergleiche über Zeiträume mit
|
||
unterschiedlicher Positionsgröße sind ungültig; das gehört bei jeder künftigen
|
||
Live-Auswertung mitgedacht.
|
||
|
||
⚠ **Nicht der Level-Quelle ALLEIN zuzuordnen:** am 31.07. kamen das
|
||
P(break)-Nachtraining UND die Schwellensenkung 0,55 → 0,35 dazu. Getrennt:
|
||
30.07. (nur M30-Level, altes Modell) n=**8**, +19,88 €/Lot · ab 31.07. (M30 + neues
|
||
Modell) n=20, +43,73 €/Lot. Das Fenster mit nur der Umstellung ist zu klein für eine
|
||
Zuordnung. **Der kombinierte Effekt ist bestätigt, die Ursachenaufteilung nicht.**
|
||
Beide Änderungen waren im Backtest validiert und zeigen live in dieselbe Richtung.
|
||
|
||
*(erledigt und ausgetragen: P(break)-Genauigkeit → `analyze_pbreak_live.py` 31.07.,
|
||
Modell daraufhin nachtrainiert · Chartmuster-Kontrolltest → 30.07., Muster schlagen
|
||
die Kontrolle in beiden Hälften, Gewicht 1,0)*
|
||
|
||
⚠ **Der Task `OilMeasurementReminder` wurde am 2026-07-31 testweise ausgelöst und
|
||
lief mit Ergebnis 0 durch** (Arbeitsverzeichnis korrekt gesetzt) — ein Erinnerungs-
|
||
system, das still scheitert, wäre schlimmer als keins.
|
||
|
||
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 · **m15_states** · **cone_checks** · **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.
|
||
|
||
## ✅ EINSATZ JE AUTONOMEM PFAD + MELDUNGS-ABSTAND IM CHART (2026-08-19, v=182 / MQL5 v1.44)
|
||
|
||
**(1) Eingabefeld `Einsatz je Pfad` in der Breakout-Karte** (User-Wunsch
|
||
"Margin Verhaeltnis von M15 / BRK manuell setzen"). Zwei Zahlenfelder
|
||
`BRK / M15`, **0 = globaler Wert** (`margin_buffer_pct`, live 40).
|
||
`POST /api/automargin {brk, m15}` · `engine.set_auto_margin` · Snapshot
|
||
`margin_brk`/`margin_m15` · neustart-fest.
|
||
|
||
⚠⚠ **ES IST KEINE GLEICHZEITIGE RESERVIERUNG — das war der Wunsch, aber es
|
||
geht heute nicht.** Der Bot haelt **EINE** Position (`_check_auto_squeeze`
|
||
feuert "nur FLAT"), und Trailing, Notfall-Stop, S/R-Close, Time-Stop und
|
||
Circuit-Breaker verwalten je EINE Position — **42 Einzel-Positions-Annahmen in
|
||
`engine.py`, 25 in `trader.py`**. Bei zwei Positionen auf demselben Symbol
|
||
haengt der GESAMTE Schutz-Stack an einer davon, die andere liefe ungeschuetzt.
|
||
**Das ist ein Sicherheits-, kein Aufwandsargument.** Das Feld steuert die
|
||
**GROESSE** der jeweiligen Position, wenn dieser Pfad eroeffnet.
|
||
⚠ **Manuelle Trades bleiben beim globalen Wert** — Absicht: der Mensch ist die
|
||
gemessen bessere Population (+1,56 gegen −4,71 EUR/Lot).
|
||
Umsetzung quellen-abhaengig wie die SL-Zeitebene seit 05.08.: `_open()` setzt
|
||
`config.MARGIN_BUFFER` fuer die Dauer des einen Aufrufs, `finally` stellt ihn
|
||
zurueck. ⚠ Direkt gesetzt statt ueber `set_margin_buffer()` — das loggt je
|
||
Trade eine Zeile und wuerde den globalen Wert dauerhaft ueberschreiben.
|
||
✅ Ende-zu-Ende: 50/25 gesetzt → Snapshot → persistiert · 150 abgelehnt · 0/0
|
||
zurueck · `deploy.py --feld margin_brk`, alle 5 Schritte.
|
||
|
||
⚠⚠ **ZWEI ECHTE FEHLER VOM LINTER GEFANGEN, die `py_compile` durchwinkte:**
|
||
`config` war in `engine.py` **gar nicht importiert** (nur Einzelnamen via
|
||
`from core.config import ...`), und in `server.py` hiess die Engine `eng` statt
|
||
`app.state.engine`. Beide sassen in Pfaden, die erst beim **ERSTEN autonomen
|
||
Trade** bzw. beim ersten Klick gefeuert haetten — genau die Klasse, fuer die
|
||
`ruff F821` am 07.08. eingebaut wurde. **Zweiter echter Fang seit dem Einbau.**
|
||
|
||
**(2) MQL5 v1.44 — Meldungs-Zeilenabstand MESSEN statt schaetzen.**
|
||
User: "die zeilenabstaende sind immer noch zu gering" (nach v1.43). Zwei
|
||
Ursachen: **(a)** `OBJPROP_FONTSIZE` wurde **nur beim Erzeugen** gesetzt — ein
|
||
bestehendes Label behaelt seine Groesse, die Erhoehung 10→11 pt kam auf dem
|
||
Chart nie an (gleiche Klasse wie die X-Position, die genau deshalb schon jeden
|
||
Redraw neu gesetzt wird). **(b)** Der Abstand wurde aus der **Punkt**groesse
|
||
hochgerechnet (Faktor 2,0, dann 2,6) — Punkt→Pixel haengt an der DPI-Skalierung
|
||
und ist vom Quelltext aus **nicht bekannt**. Jetzt `TextGetSize` (+8 px Luft),
|
||
Fallback 2,6. ⚠ `TextSetFont` mit **negativer** Groesse = 1/10 pt, nur diese
|
||
Form wird DPI-korrekt skaliert.
|
||
⚠⚠ **Und ein eigener Fehler der bekannten Klasse:** der erste v1.44-Compile
|
||
**schlug fehl** (`error 246: TextGetSize verlangt uint&`) und **loeschte dabei
|
||
die `.ex5`** — der Chart hatte kurzzeitig **gar keinen Indikator**. Ich habe
|
||
trotzdem "0 errors" gemeldet, weil ich einen **stale Logfile** gelesen habe
|
||
(erkennbar an identischen 894 ms). Exakt der dokumentierte Fall *"eine
|
||
Erfolgsmeldung muss den Zustand NACH der Aktion pruefen, nicht den davor"*.
|
||
Aufgefallen nur durch die `.ex5`-Zeitstempelpruefung danebenan.
|
||
**➤ Regel: je Compile einen EIGENEN Logpfad, und auf `Result: N errors` PLUS
|
||
`.ex5`-Alter pruefen — nie auf den Default-Log des Terminals.**
|
||
|
||
⚠⚠ **ZWEI COMMIT-FALLEN, beide neu und beide dokumentierenswert:**
|
||
**(a)** **Backticks in einer Bash-Commit-Nachricht** werden als
|
||
Kommando-Substitution ausgefuehrt — alle eingeklammerten Namen verschwanden
|
||
aus dem Text (`_check_auto_squeeze` → leer). **(b)** Ein **Bash-Pfad**
|
||
(`/c/Users/...`) an Python uebergeben schlaegt fehl, und
|
||
`git commit --amend -F <fehlende Datei>` nahm daraufhin eine **STALE
|
||
`COMMIT_EDITMSG`** — also die Nachricht eines **fremden** Commits.
|
||
✅ Der Code war nie betroffen (Arbeitsbaum sauber, Remote synchron), nur die
|
||
Beschriftung. **➤ Commit-Nachrichten mit Backticks oder deutschen Quotes ueber
|
||
eine DATEI (`-F`) schreiben, und die Datei vorher auf `-s` pruefen** — dieselbe
|
||
Familie wie die dokumentierte Heredoc-Regel.
|
||
|
||
## ✅ M5-KONTEXTZEILE STATT EINER ZWEITEN SETUP-KARTE (2026-08-19, v=183)
|
||
|
||
User-Frage: „was hältst du von einer M1- oder M5-Setup-Karte?“
|
||
⚠⚠ **Die Frage war für M5 schon am 12.08. gestellt worden — diesmal
|
||
entscheidet eine frische Messung, nicht eine Meinung.** Entkoppelte
|
||
Selbstmessung der BESTEHENDEN Karte (`m15_states`, grün + gerichtet, ab dem
|
||
Hysterese-Stichtag 12.08.):
|
||
| Horizont | entkoppelt | Treffer | 95-%-KI | |
|
||
|---|---|---|---|---|
|
||
| 30 min | **n=342** | 53,5 % | [48,2 … 58,8] | ⚠ enthält 50 % |
|
||
| 60 min | n=341 | **49,3 %** | [44,0 … 54,5] | ⚠ liegt DARUNTER |
|
||
⚠⚠ **Am 12.08. war die Bedingung für ein Ja VORAB fixiert:** „≥350 Fälle,
|
||
über 50 %, KI ohne die 50“. **Die Stichprobe ist jetzt da, die Bedingung ist
|
||
NICHT erfüllt** — das ist kein „noch zu wenig Daten“ mehr, sondern ein
|
||
Ergebnis. Dazu die Messung vom 13.08.: als **Handelsregel** fällt die Karte
|
||
durch (`backtest_m15_auto.py`, ØR −0,162/−0,094, beide KI ohne Null, schlechter
|
||
als die HTF-Richtung UND als ihr eigenes Gegenteil). Eine zweite Karte
|
||
derselben Bauart würde ein als Signal gemessen **negatives** Muster
|
||
vervielfachen — und die Veto→Richtung-Falle, die in EINER Woche dreimal
|
||
zuschlug, auf eine zweite Fläche ausdehnen.
|
||
⚠ **M1 scheitert zusätzlich an der Arithmetik:** Spread/ATR Median **0,319**
|
||
gegen 0,193 (M5) und **0,104** (M15) — feiner auflösen macht die EINZIGE
|
||
gemessene Stärke der M15-Karte (die Kosten) **monoton schlechter**. Um 01:00
|
||
ist der Spread **1,051×ATR_M1**, größer als die mittlere Kerze; und die
|
||
M1-Historie ist bei 80 Tagen gedeckelt → zwei Stichproben im **selben Regime**.
|
||
|
||
✅ **GEBAUT: Zustand ohne Urteil** (`m15_setup.m5` → Zeile `#m15-m5`).
|
||
Squeeze-Lage (armiert / Ausbruch / keine Kompression + Box), **Kosten in
|
||
×ATR_M5** mit den Schwellen aus `backtest_realcosts.py` (Median 0,193, ab 0,32
|
||
Kostenfalle) und der ATR. Live: „M5 · keine Kompression (Box 3,52×ATR) · Kosten
|
||
0,15×ATR_M5 günstig · ATR 0,165“.
|
||
⚠ Der M5-**Raum** wird bewusst NICHT wiederholt — Gate ③b rechnet ohnehin auf
|
||
M5 (`room_info`).
|
||
⚠ **NUR ZUSAMMENGESETZT, nichts neu gerechnet** — `wave.squeeze` und
|
||
bid/ask/ATR_M5 stehen im Snapshot ohnehin. Ein zweiter Rechenweg war der Fehler
|
||
des Konsens-Pfeils (komplettes zweites `_verdict` im 5-s-Takt) und ist die
|
||
Quelle jeder Divergenz zwischen Karte und Chart.
|
||
⚠ **Kein Verdict-Gewicht, kein Trade-Trigger, keine Farbampel** — dieselbe
|
||
Zurückhaltung wie bei der Kerzen-Anatomie und der Überdehnungs-Anzeige.
|
||
⚠ `deploy.py --feld m15_setup.m5` nutzt den **Punktpfad** (seit 10.08.).
|
||
|
||
## ⚠⚠ `auto_m15` ABGESCHALTET — es verdrängte BRK aus dem einen Slot (2026-08-19)
|
||
|
||
User-Frage „warum wurde kein BRK-Trade eröffnet?“ → am Zeitstrahl beantwortet,
|
||
nicht vermutet. **BRK hat heute FÜNFMAL ruhende Orders gesetzt** und wurde
|
||
jedes Mal binnen Sekunden storniert, weil eine Position offen war:
|
||
| gesetzt | Level | storniert nach | wodurch |
|
||
|---|---|---|---|
|
||
| 18:03:09 | 86,360 / 85,849 | **16 s** | `AUTOM15_SHORT` |
|
||
| 18:11:04 | 86,408 / 85,851 | **1 s** | `AUTOM15_SHORT` |
|
||
| 18:12:58 | 86,408 / 85,851 | **20 s** | `AUTOM15_SHORT` |
|
||
| 18:43:52 | 86,441 / 85,992 | **81 s** | manuell |
|
||
| 19:02:14 | 86,514 / **86,080** | **9 s** | manuell |
|
||
**Zusammen rund 2 Minuten am Markt** — an einem Tag mit 24 Trades. Die Mechanik
|
||
arbeitet einwandfrei, sie kommt nur nie zum Zug.
|
||
⚠⚠ **DREI der fünf löste der M15-Trader aus**; um 18:11:04 wurde die Position
|
||
in **derselben Sekunde** eröffnet, in der die Pendings gesetzt wurden. Damit
|
||
verdrängte ein **gemessen negativer** Pfad (ØR −0,162/−0,094, beide KI ohne
|
||
Null) den **einzigen 2-Stichproben-validierten** Einstieg — exakt der Konflikt,
|
||
der am 01.08. schon für `auto_signal` dokumentiert wurde („beide Auto-Pfade
|
||
konkurrieren um den EINEN Positions-Slot“), mit derselben Konsequenz.
|
||
⚠ Der SELL_STOP von 19:02 lag bei **86,080**, das Tief danach bei **86,012** —
|
||
die Order hätte gefüllt. Ein Einzelfall belegt keinen Edge, zeigt aber den
|
||
Mechanismus. Tagesbilanz: `AUTOM15_*` n=4 **−0,02 €** (belegt nur den Slot),
|
||
manuell n=20 **−167,39 €**.
|
||
✅ Abgeschaltet über den **offiziellen Toggle** `POST /api/autom15`, damit alle
|
||
drei Stellen übereinstimmen (Snapshot · `runtime_state.json` · ini je `false`).
|
||
⚠ Die laufende AUTOM15-Position bleibt offen — der Schalter stoppt nur NEUE
|
||
Entries. Zurück: Knopf **M15**.
|
||
|
||
### ⚠ Und die Vorbedingung für den Parallelbetrieb ist GEMESSEN — sie fällt durch
|
||
|
||
User-Wunsch „BRK soll auch eröffnen wenn M15 aktiv ist“. Der **kleine** Umbau
|
||
wäre an einem Abend gemacht (Pendings nicht mehr stornieren, `open_*` nicht mehr
|
||
bei `self.ticket` abweisen) — aber der zweite Trade hätte nur den **Broker-SL**:
|
||
`trader` hält EIN Ticket, und Trailing, Breakeven, Time-Stop, Notfall-Stop und
|
||
S/R-Close hängen alle daran (**29 Stellen** in `engine.py`).
|
||
**Gemessen** (`backtest_brk_slonly.py`, 80k M5, 2 Halbjahre, Fill bei
|
||
Berührung, Echtkosten, feste geteilte Entry-Liste):
|
||
| Variante | H1 ØR / PF | H2 ØR / PF | WR |
|
||
|---|---|---|---|
|
||
| A) kanonisch | −0,145 / 0,76 | +0,036 / 1,07 | 40 / 44 % |
|
||
| **B) SL only** | −0,132 / 0,92 | **+0,464** / 1,27 | **21 / 19 %** |
|
||
| C) SL + Time-Stop | −0,157 / 0,87 | +0,138 / 1,12 | 36 / 39 % |
|
||
| D) SL + TP | −0,188 / 0,86 | +0,073 / 1,06 | 38 / 40 % |
|
||
| E) SL + Time + TP | −0,212 / 0,81 | +0,064 / 1,06 | 39 / 43 % |
|
||
**Keine Variante erfüllt die vorab fixierte Regel** — H1 durchgehend negativ,
|
||
**alle zehn KI enthalten die Null**. Vorab festgelegte Antwort: **großer Umbau**.
|
||
⚠⚠ **SL-only sieht im ØR nicht schlechter aus — das ist ein VARIANZ-Befund, kein
|
||
Argument:** Trefferquote **40 → 21 %** bzw. 44 → 19 % (vier von fünf Trades
|
||
laufen in den vollen −2R-Stop), und die KI-Breite **vervierfacht** sich
|
||
([−0,108 … +1,115] gegen [−0,081 … +0,148]). Der Ertrag hängt an wenigen
|
||
Läufern bis 16,7 h **ohne jede Absicherung**.
|
||
⚠⚠ **GRENZE: die Kontrolle reproduziert NICHT** (A liefert −0,145/+0,036 gegen
|
||
dokumentierte +0,124/+0,292; 362/559 Einstiege gegen 1.092/1.807). Die
|
||
**Absolutwerte sind unbrauchbar**, belastbar ist allein der **interne**
|
||
Vergleich — alle Varianten laufen auf derselben Entry-Liste. Gleicher Vorbehalt
|
||
wie am 13.08.
|
||
✅ **Keine Exit-Kopie:** SL-only entsteht aus `LIVE.with_(trail_start=999,
|
||
be=999)` — der Trade bleibt in Phase „Init“ und verlässt sie nur über SL oder
|
||
`max_hold`. Derselbe kanonische Code; eine eigene Schleife wäre das **fünfte**
|
||
Exit-Modell gewesen.
|
||
➤ **Offen: der ticket-basierte Umbau** (Trailing je Position) — eigene Sitzung
|
||
mit **flachem Konto**. Ein halb fertiger Umbau lässt den Schutz-Stack an der
|
||
falschen der beiden Positionen hängen.
|
||
|
||
## ✅✅ BRK HAT EINEN EIGENEN POSITIONS-SLOT (2026-08-19, v=185)
|
||
|
||
User-Vorgabe: *„der brk trade soll komplett unabhängig von m15 oder manuellen
|
||
trades laufen“*. Entwurf: `docs/brk-eigener-slot.md`.
|
||
|
||
**Anlass war eine Messung, keine Vermutung:** BRK setzte am 19.08. **fünfmal**
|
||
ruhende Orders und wurde jedes Mal binnen Sekunden storniert, weil eine Position
|
||
offen war (16 s · 1 s · 20 s · 81 s · 9 s = zusammen **~2 Minuten am Markt** an
|
||
einem Tag mit 24 Trades). **Drei der fünf** löste der M15-Trader aus — einmal in
|
||
**derselben Sekunde**. Der SELL_STOP von 19:02 lag bei 86,080, das Tief danach
|
||
bei **86,012**: die Order hätte gefüllt.
|
||
|
||
```
|
||
engine.trader ─ TradeManager() ─ trail ─ manuell + M15
|
||
engine.trader_brk ─ TradeManager(nur_ticket=True) ─ trail_brk ─ nur BRK
|
||
```
|
||
✅ **Der Umbau blieb klein**, weil `TrailingManager.__init__` bereits SEINEN
|
||
Trader nimmt und nur über dessen `snapshot()` liest — es hängt nicht an einer
|
||
globalen Position. Der validierte Pfad bleibt damit **unangetastet** (kein
|
||
Regressionsrisiko auf der gemessen besseren Population, +1,56 gegen −4,71 €/Lot).
|
||
|
||
⚠⚠ **DIE ZWEI KRITISCHEN STELLEN:**
|
||
**(1) Adoption.** `TradeManager.refresh()` greift per Default jede Position auf
|
||
dem Symbol (gewollt: ein von Hand eröffneter Trade bekommt so den Schutz-Stack).
|
||
Mit zwei Managern würden sich **beide dieselbe** Position schnappen. Neu:
|
||
`nur_ticket=True` sucht sich nichts, und der adoptierende Manager bekommt über
|
||
**`fremde_tickets`** eine Sperrliste (BRK-Ticket + liegende Pendings).
|
||
**(2) Der Fill.** Eine ruhende Order füllt **im Broker**; der ticket-gebundene
|
||
Slot kann sie nicht finden. Neu **`TradeManager.bind(ticket, sym)`**, gerufen aus
|
||
`_check_pending_fill` — es sieht direkt am Broker nach, welches der liegenden
|
||
Tickets zu einer Position geworden ist.
|
||
|
||
**Weiter:** `_check_auto_squeeze`/`_manage_squeeze_pending` lesen den EIGENEN
|
||
Slot · `trail_brk` wird in `_price_loop` mit Preisen versorgt · Snapshot
|
||
**`position_brk`** · Dashboard-Zeile **`#brk-pos`** in der Breakout-Karte ·
|
||
**Circuit-Breaker und Tages-P&L summieren BEIDE Slots**, und der Breaker
|
||
schließt beide (eine Notbremse, die nur die Hälfte schließt, ist keine).
|
||
⚠ Die Dashboard-Zeile warnt ausdrücklich **„⚠ TRAILING AUS — nur Broker-SL!“**:
|
||
ohne `trail_brk` liefe BRK in der gemessen **durchgefallenen** SL-only-Variante
|
||
(`backtest_brk_slonly.py`: alle zehn KI enthalten die Null; WR fällt 40 → 21 %).
|
||
|
||
⚠⚠ **REIHENFOLGE-FEHLER, real passiert:** die Defaults für `_margin_brk`/
|
||
`_margin_m15` standen in Zeile 631, `_load_runtime_state()` läuft in Zeile 553 —
|
||
der geladene Wert wurde also sofort mit 0 überschrieben. Die Eingabe WIRKTE, war
|
||
aber nach jedem Neustart weg (User: „die Eingabe bleibt wirkungslos, in den
|
||
Feldern erscheint global“). **Regel: erst Default setzen, dann `runtime_state`
|
||
darüber laden.**
|
||
⚠ Beim Verifizieren dazu ein **eigener Fehlalarm**: mein Test setzte 55/30, nach
|
||
dem Neustart standen 85/10 — ich las das als „verloren“ und sogar als
|
||
Vertauschung. Die Logspur zeigte, dass der Browser des Users währenddessen zwei
|
||
weitere POSTs geschickt hatte. **Bei einer Live-Verifikation gegen ein System,
|
||
das der Nutzer gleichzeitig bedient, gehört die Logspur mitgelesen.**
|
||
|
||
⚠ **NOCH NICHT BEOBACHTET:** der erste echte Parallelbetrieb. Beweis beim
|
||
nächsten Squeeze mit offener Position — Log `🧷 BRK-Slot uebernimmt
|
||
Pending-Fill` und `position_brk.trail == true`.
|
||
|
||
## ✅⚠⚠ G/V-TRENNUNG GEPRÜFT — die Sperre riss bei JEDEM Neustart auf (2026-08-20, v=201)
|
||
|
||
User: „überprüfe ob wirklich die G/V und G/V BRK Anzeigen stimmen, besonders bei
|
||
gleichzeitig laufenden Trades. Die Anzeige darf jeweils nur den eigenen Slot
|
||
anzeigen."
|
||
|
||
✅ **Das Frontend ist sauber**: `#hdr-pnl` liest ausschließlich `d.position`,
|
||
`#hdr-pnl-brk` ausschließlich `d.position_brk`. Keine Summe, kein Fallback,
|
||
keine gemeinsame Variable. ⚠ Aber das ist auch die *falsche* Stelle zum Suchen:
|
||
stehen im Backend beide Felder auf derselben Position, kann keine Anzeige das
|
||
mehr richten. Geprüft wurde deshalb die **Auswahl** in
|
||
`TradeManager._refresh_locked`.
|
||
|
||
⚠⚠ **DER FUND — die Adoptions-Sperre war unvollständig, und sie riss bei JEDEM
|
||
Neustart auf.** Ein BRK-Ticket kann aus **drei** Quellen kommen; die Sperrliste
|
||
kannte nur zwei:
|
||
| Quelle | in `fremde_tickets`? | lebt |
|
||
|---|---|---|
|
||
| `trader_brk.ticket` | ✅ | nur im Speicher |
|
||
| `_pending_tickets` | ✅ | nur im Speicher |
|
||
| **`_brk_restore`** (aus `runtime_state.json`) | ❌ **fehlte** | überlebt den Neustart |
|
||
Nach einem Neustart sind die ersten beiden **leer**. Und die Reihenfolge im
|
||
`_pos_loop` besiegelt es: `trader.refresh()` läuft, und erst **drei Zeilen
|
||
später** `_rebind_brk()`. Slot 1 greift die BRK-Position also, **bevor** sie
|
||
zurückgebunden werden kann.
|
||
⚠ `_rebind_brk` erkennt das zwar und bricht ab („hängt bereits an Slot 1") —
|
||
aber damit ist der Schaden nur festgestellt, nicht behoben: die BRK-Position
|
||
hängt am falschen Slot, ihr **G/V steht links statt rechts**, und „G/V BRK"
|
||
zeigt „—". Danach sieht alles normal aus, was es besonders schwer sichtbar macht
|
||
— dieselbe Signatur wie der Fehler vom Vortag, der `_rebind_brk` überhaupt erst
|
||
nötig gemacht hat.
|
||
✅ **Behoben**: `_brk_restore` steht jetzt mit in der Sperrliste. Der Zugriff
|
||
läuft über `getattr(..., None)`, ist also unabhängig davon, WANN das Attribut
|
||
gesetzt wird — genau die Reihenfolge-Falle, die in zwei Tagen zweimal
|
||
zugeschlagen hat.
|
||
|
||
✅✅ **ZWEITER FUND — zwei verschiedene Rechnungen nebeneinander.** Bei
|
||
geschlossenem Markt zeigt Slot 1 seit dem 01.08. eine **HL-Schätzung**
|
||
(`pnl_hl`), weil der Broker-P&L stillsteht. Der BRK-Slot bekam die nie — er
|
||
zeigte den **eingefrorenen** Broker-Wert, ohne dass man es der Anzeige ansieht.
|
||
Neu `pnl_hl_brk` (dieselbe `live_pnl`-Rechnung auf dem eigenen Slot, mit ≈
|
||
markiert). ⚠ Geprüft statt angenommen, dass `_tick_size`/`_tick_value` am
|
||
BRK-Slot überhaupt gesetzt sind — `_refresh_locked` zieht sie bei gesetztem
|
||
Ticket jeden Tick nach.
|
||
|
||
✅ **`day_pl.open` summiert korrekt BEIDE Slots** — die Summe gehört dorthin,
|
||
die Trennung in die beiden G/V-Felder.
|
||
|
||
**BELEG — `tests/test_slot_gv.py`, 6 Tests auf dem ECHTEN `TradeManager`**
|
||
(Broker gestubbt, Sperrliste wortgleich zur Engine nachgebaut): zwei Positionen
|
||
gleichzeitig landen je im eigenen Slot · Slot 1 adoptiert die BRK-Position nicht
|
||
· ein Pending-Fill wird nicht vorher weggeschnappt · der BRK-Slot adoptiert
|
||
**nie** · und die **Regressionsprobe**, die den alten Zustand festhält: ohne den
|
||
Merker in der Sperrliste greift Slot 1 zu.
|
||
✅ **Neue Pipeline-Stufe (E, `tools/check_slots.py`)**: die Sperrliste muss alle
|
||
**drei** Namen erwähnen. Mutationsprobe: Merker entfernt → Stufe E **Exit 1**.
|
||
⚠⚠ **Ehrlich zur Arbeitsteilung der beiden Prüfungen:** der pytest belegt den
|
||
**Mechanismus** (eine Sperrliste ohne das Ticket lässt die Adoption zu) — er
|
||
baut die Lambda selbst nach und bleibt bei der Mutation deshalb grün. Erst die
|
||
statische Stufe belegt, dass die **Engine** sie richtig verdrahtet. **Keine der
|
||
beiden allein hätte gereicht.**
|
||
|
||
⚠ Live nach dem Deploy verifiziert: Slot 1 `T=50396824` (+10,34), BRK flat,
|
||
`day_pl.open` 10,34 — kein gemeinsames Ticket. Der Neustart adoptierte das
|
||
Ticket korrekt in Slot 1 (BRK war flat, die Sperre blockt also nicht zu viel).
|
||
⚠ **Noch nicht beobachtet**: ein Neustart *mit* offener BRK-Position — der Fall,
|
||
den der Fix adressiert. Beweis wäre dann „BRK-Slot nach Neustart
|
||
zurueckgebunden" **ohne** vorangehendes „magic-match" auf dasselbe Ticket.
|
||
|
||
⚠⚠ **NEBENBEFUND, DER KEINER WAR — und die Selbstkorrektur gehört dazu.**
|
||
Beim Nachrechnen der Dateigröße fiel auf, dass mein Schreib-Helfer sechs
|
||
Dateien von LF auf **CRLF** gedreht hatte, und ich habe sie „zurückgesetzt“.
|
||
Das war **falsch**: `core.autocrlf = true` — der Arbeitsbaum dieses Repos ist
|
||
**CRLF**, git speichert intern LF. Die Kontrolle an unberührten Dateien
|
||
(`core/trailing.py`, `server.py`, `tests/conftest.py`) zeigt überall CRLF; mein
|
||
Helfer hat also genau das erzeugt, was ein `git checkout` erzeugt. Meine
|
||
„Reparatur“ machte die sechs Dateien als Einzige **inkonsistent** zum Rest.
|
||
Zurückgedreht. **Kein Schaden in keiner Richtung**, weil git ohnehin
|
||
normalisiert — aber es war eine erfundene Baustelle.
|
||
⚠ Zwei echte Lehren bleiben: (1) ein `grep` auf das Zeilenende-Zeichen meldete **0**, obwohl 8.959
|
||
CR-Bytes drinstanden — **Zeilenenden nur über die BYTE-Zählung prüfen**;
|
||
(2) `len(str)` gegen `os.path.getsize()` zu halten vergleicht **Zeichen mit
|
||
Bytes** — als Schrumpf-Sicherung taugt es, als Größendifferenz nicht.
|
||
|
||
⚠⚠ **VIERTER Surrogat-Abbruch beim Schreiben dieses Abschnitts — und wieder
|
||
OHNE Schaden.** Ein Emoji als ZWEI getrennte Unicode-Escapes erzeugt lone
|
||
surrogates; das Öffnen im Schreibmodus kürzt die Datei aber schon VOR dem
|
||
Schreiben. Die Regel vom 19.08. (erst `.tmp`, Größe prüfen, dann `os.replace`)
|
||
hat gehalten: CLAUDE.md blieb unversehrt, es entstand nur eine leere `.tmp`.
|
||
✅ Konsequenz: dieser Abschnitt wurde als **reiner UTF-8-Text** geschrieben,
|
||
ohne jedes Escape — das ist der Weg, der die Falle gar nicht erst aufstellt.
|
||
|
||
## ⚠⚠ „TAGESTREND AUFWÄRTS, NEWS BULLISH — WARUM KEINE EMPFEHLUNG?" (2026-08-20)
|
||
|
||
User-Frage. **Die Prämisse stimmt** (Tageskerze +2,47 $ = **+2,89 %**, H1
|
||
monoton 85,31 → 87,72). Die Schlussfolgerung stimmt nicht:
|
||
|
||
✅✅ **DIE EMPFEHLUNG WAR LONG — achtmal, zwischen 09:13 und 09:51 bei
|
||
85,97–86,53** (conf 62). Der Kurs steht jetzt 87,70, also **+1,73 $ höher**. Das
|
||
System hat den Trend also gesehen, und zwar nahe am Anfang der Bewegung.
|
||
Was jetzt passiert, ist etwas anderes: es steigt **nicht mehr spät ein**.
|
||
|
||
**DER BLOCKER IST `stretch`** (`_STRETCH_MAX = 3,5`) — der Kurs steht weit über
|
||
der EMA50, auf **jeder** Zeitebene:
|
||
| TF | Überdehnung | |
|
||
|---|---|---|
|
||
| M5 | **+5,18×ATR** | gesperrt |
|
||
| M15 | +4,63×ATR | gesperrt |
|
||
| **M30** (Basis-TF) | **+4,36×ATR** | gesperrt |
|
||
| H1 | +4,39×ATR | gesperrt |
|
||
Reason-Text live: „überdehnt: +4.4×ATR über EMA — kein Spät-Long".
|
||
⚠ Der Block ist **nicht** der Dauerzustand: er läuft am Stück erst seit 12:58,
|
||
davor stand dort `breakout_pending`. Verteilung heute (789 Zeilen):
|
||
`entry_room` 284 · **`stretch` 188** · `min_conf` 173 · `stale` 55 ·
|
||
`breakout_pending` 41 · `deadband` 40 · gerichtet 8.
|
||
|
||
**GEMESSEN, statt die Sperre zu verteidigen** (`backtest_stretch_extrem.py`,
|
||
60k Bars, M30 **und** M5, kanonischer Exit, Echtkosten, 2 Halbjahre,
|
||
Episoden-entkoppelt, 95-%-Bootstrap): trägt ein Einstieg **jenseits** des Gates?
|
||
| Band | M30 H1 / H2 | M5 H1 / H2 |
|
||
|---|---|---|
|
||
| 3,0–3,5 (erlaubt) | −0,024 / −0,077 | −0,074 / −0,119 |
|
||
| **3,5–4,5 (gesperrt)** | **−0,053 / −0,061** | **−0,074 / −0,058** |
|
||
| **4,5+ (gesperrt)** | **−0,037 / −0,149** | **−0,110 / −0,059** |
|
||
**Alle acht gesperrten Zellen sind negativ** (PF 0,83–0,95). Die vorab fixierte
|
||
Regel (beidhälftig positiv UND Intervall ohne die Null) ist in **keiner**
|
||
erfüllt → **die Sperre bleibt.**
|
||
⚠⚠ **Die aufschlussreichste Spalte ist die Trefferquote: 57–60 % — und trotzdem
|
||
PF < 1.** Das ist exakt die dokumentierte Klein-Close-Signatur: man hat oft
|
||
recht, aber der Rücksetzer kostet mehr, als die vielen kleinen Gewinne
|
||
einbringen. Genau das heißt „Spät-Long".
|
||
⚠ Ehrlich dazu: **nichts** in der Tabelle ist überzeugend positiv, auch die
|
||
erlaubten Bänder nicht. Die 3,5 sind also keine magische Grenze — auf der
|
||
Überdehnungs-Achse ist überhaupt nichts zu holen; das Gate entfernt nur das
|
||
schlechtere Ende.
|
||
|
||
⚠⚠ **NEWS HABEN IN DER EMPFEHLUNG BUCHSTÄBLICH KEIN GEWICHT** — geprüft, nicht
|
||
vermutet: **0 Treffer** für „news" in `core/wave_rec.py`, **0** News-Stimmen im
|
||
Verdict. Der Score (heute **+0,42**) ist reiner Kontext-Chip und liegt zudem
|
||
unter der 0,5-Schwelle, ab der die Konflikt-Warnung überhaupt erscheint.
|
||
✅ Das ist eine **Messentscheidung**, kein Versehen: am 03.08. über 38.213 Paare
|
||
gemessen liegt der stark-bullische Bucket in **BEIDEN** Hälften bei null bis
|
||
negativ (−0,003 / −0,023), der bärische **kippt das Vorzeichen**. Ein Kanal, der
|
||
nichts vorhersagt, bekommt keine Stimme — dieselbe Begründung wie bei TU (2026-07-06)
|
||
und beim Orderbuch (06.08.).
|
||
|
||
## ⚠⚠ „TAGESTREND MEHR GEWICHT?" — VIER VARIANTEN GEMESSEN, KEINE BESTEHT (2026-08-20)
|
||
|
||
User-Frage nach dem Stretch-Befund. Vier konkrete Vorschläge, **alle vorher
|
||
gemessen statt gebaut** (`backtest_tagestrend.py`, 60k M5 = 244 Handelstage,
|
||
voller Live-Gate-Stack — echtes `_build` + Breakout k=0,3 + `_room_gate`,
|
||
Reversal aus —, kanonischer Exit, Echtkosten, 2 Halbjahre, 95-%-Bootstrap,
|
||
Schwellen 0,15 / **0,30** / 0,60 % als Nachbar-Probe).
|
||
**Tagestrend = Kurs gegen die TAGESERÖFFNUNG** (Berlin-Tag, DST-korrekt) —
|
||
bewusst so und nicht als D1-EMA: das ist, was ein Mensch sieht, wenn er sagt
|
||
„der Tagestrend ist aufwärts", und es braucht keinen 16.000-Bar-Vorlauf.
|
||
|
||
| Variante | H1 ØR / ΣR | H2 ØR / ΣR | n (H1/H2) |
|
||
|---|---|---|---|
|
||
| **BASIS (wie live)** | −0,062 / −34,9 | −0,107 / −88,9 | 563 / 831 |
|
||
| A) VETO gegen den Tagestrend | −0,058 / −31,5 | **−0,114** / −89,5 | 543 / 785 |
|
||
| B) NUR MIT dem Tagestrend | −0,010 / −4,8 | **−0,126** / −86,1 | 483 / 683 |
|
||
| C) Spät-Einstieg erlaubt (stretch → 5,0) | −0,031 / −20,3 | −0,089 / −84,8 | 656 / 953 |
|
||
| **D) Pullback im Tagestrend** | **−0,008 / −1,6** | **−0,012 / −3,5** | **196 / 291** |
|
||
|
||
**A und B fallen sofort:** beide sind in H2 an **allen drei** Schwellen
|
||
schlechter als BASIS. Die von ihnen entfernte Gruppe kippt zwischen den Hälften
|
||
(A: −0,105 / **+0,032**; bei 0,15 % sogar **+0,208** in H2) — sie werfen also
|
||
teils die *besseren* Trades weg. Damit ist die Klasse zum **vierten Mal**
|
||
gemessen (Höher-TF-Winkel · H1-Stärke · Konfluenz-Strafe · jetzt Tagestrend).
|
||
|
||
⚠⚠ **C sieht in der Tabelle gut aus und ist trotzdem kein Beleg.** Es ist in
|
||
**beiden** Hälften und an **allen drei** Schwellen besser als BASIS — aber die
|
||
Trades, die es zusätzlich nimmt, sind **H1 −0,134 / H2 +0,060**, also
|
||
gegenläufig. Der Widerspruch löst sich in der Mechanik auf: die Sim hat EINEN
|
||
Slot (`i = xj + _COOL`), ein zusätzlicher Trade **verschiebt die gesamte
|
||
Folge**. C ist damit keine Obermenge von BASIS, sondern eine andere
|
||
Trade-Folge — der scheinbare Gewinn ist ein **Selektionseffekt**, exakt die
|
||
Falle vom 14.08.
|
||
✅ Nebenbefund, der zum Stretch-Lauf desselben Tages passt: der Spät-Einstieg
|
||
zahlt auch dann nicht, wenn der Tagestrend ihn stützt.
|
||
|
||
⚠⚠ **D ist die einzige Variante mit der RICHTIGEN Signatur — und trotzdem kein
|
||
Bau.** Sie ist beidhälftig besser als BASIS, an allen drei Schwellen, und die
|
||
Gruppe, die sie **entfernt**, ist an **allen sechs** Stellen negativ
|
||
(H1 −0,068/−0,060/−0,063 · H2 −0,072/−0,080/−0,053) — sie wirft also
|
||
konsistent Schlechtes weg, nicht Gutes. Das ist die Form, die A, B und C fehlt.
|
||
❌ **Aber Bedingung (2) ist verfehlt: JEDES Intervall enthält die Null**
|
||
(D selbst: [−0,266 … +0,257]).
|
||
❌ Und der ΣR-Sprung (−34,9 → **−1,6**) kommt fast vollständig daher, dass D nur
|
||
noch **35 %** der Trades nimmt. Das ist **Handelsvermeidung**, kein Edge —
|
||
dasselbe Muster, an dem `backtest_metalabel.py` gescheitert ist („ΣR steigt nur,
|
||
weil n fällt; ØR bleibt negativ"). D verwandelt eine verlierende Population in
|
||
eine **breakeven** Population, indem sie zwei Drittel davon nicht handelt.
|
||
|
||
✅✅ **DIE INHALTLICHE ANTWORT AUF DIE FRAGE steckt im Vergleich C gegen D:** wenn
|
||
der Tagestrend etwas beiträgt, dann **nicht als Erlaubnis, spät einzusteigen**
|
||
(C: die Zusatz-Trades kippen), sondern als **Kontext für den Rücksetzer**
|
||
(D: die entfernte Gruppe ist an allen sechs Stellen negativ). Das deckt sich mit
|
||
dem einzigen im Projekt belegten Einstiegs-Effekt — dem **Pullback-Bonus**
|
||
(„⭐ tiefer Pullback" +15, `backtest_pullback.py`) — und mit dem invertierten
|
||
Winkel-Befund vom 04.08. („LONG bei fallendem Kurzfrist-Winkel trägt doppelt
|
||
so gut").
|
||
⚠ Es heißt **nicht**, dass D gebaut werden sollte. Es heißt: **die Richtung, in
|
||
der man suchen müsste, ist der Pullback, nicht der späte Einstieg.**
|
||
|
||
⚠ **Der ehrlichste Satz zum ganzen Lauf:** die BASIS-Population ist mit
|
||
−0,062/−0,107 selbst negativ. Kein Umgang mit dem Tagestrend macht daraus einen
|
||
Edge — das deckt sich mit dem Ausrichtungs-Split (MIT Signal 56,5 % gegen OHNE
|
||
Signal 56,6 % = identisch) und mit `analyze_signal_live.py` (Richtung 48–51 %).
|
||
**29. verworfener Signal-Eingriff** (A, B, C zusammen als eine Klasse gezählt).
|
||
|
||
⚠⚠ **METHODEN-BEFUND, der über diesen Lauf hinausgeht: `paarweise` war hier das
|
||
FALSCHE Werkzeug — und es hat es selbst gezeigt.** Der erste Entwurf verglich
|
||
gepaart auf gemeinsamen Einstiegen und lieferte in **allen acht** Zellen
|
||
`Delta = +0,000, KI [0,000 … 0,000]`. Der Grund ist offensichtlich, sobald man
|
||
ihn sieht: diese Varianten ändern nicht den **Exit**, sondern **welche Einstiege
|
||
genommen werden** — auf gemeinsamen Fällen sind die R-Werte per Konstruktion
|
||
identisch.
|
||
➜ **Regel: `paarweise` für EXIT-Varianten (gleiche Einstiege, anderes Ergebnis),
|
||
die Auswertung der ENTFERNTEN bzw. HINZUGEFÜGTEN Gruppe für AUSWAHL-Varianten**
|
||
(Muster `backtest_htf_vola.py`). Ein Intervall von exakt [0,000 … 0,000] ist
|
||
kein Ergebnis, sondern die Fehlermeldung eines falsch gewählten Tests.
|
||
|
||
## ⚠⚠ BRK EROEFFNET NICHT — die Ursache liegt eine Ebene ueber dem Feld (2026-08-20, v=202)
|
||
|
||
User zweimal: „breakout trades eröffnen immer noch nicht". Die am selben Tag
|
||
eingebaute Klartext-Meldung sagt es jetzt wörtlich:
|
||
```
|
||
Pending-Entry (squeeze) nicht platzierbar (87.142):
|
||
Einsatz 10 % von 58.49 EUR freier Margin = 5.85 EUR,
|
||
Mindestlot 0.01 braucht aber 7.46 EUR
|
||
```
|
||
|
||
**DIE ARITHMETIK:**
|
||
| | |
|
||
|---|---|
|
||
| Balance | 728,87 € |
|
||
| davon **belegt** (Slot 1, 0,92 Lots manuell) | **688,50 €** |
|
||
| **frei** | **97,55 €** |
|
||
| Mindestlot 0,01 braucht | 7,52 € |
|
||
| ➜ **nötiger Prozentsatz** | **8 %** |
|
||
|
||
⚠⚠ **`margin_brk` ist ein Prozentsatz der FREIEN Margin, nicht des Kontos** —
|
||
und die freie Margin schwankt im Minutentakt mit dem G/V der offenen Position
|
||
(gemessen 57,4 … 97,6 € im selben Fenster). Bei 9 % sind das 5,2 … 8,8 € gegen
|
||
7,5 € Bedarf: **es funktioniert mal und mal nicht.** Die Änderung von 10 auf 9
|
||
hat die Lage damit **verschlechtert**, nicht verbessert.
|
||
|
||
⚠⚠⚠ **DIE EIGENTLICHE URSACHE LIEGT EINE EBENE HÖHER — und sie ist ein echter
|
||
Defekt:** `margin_pct` steht in `runtime_state.json` auf **95 %**, in der ini
|
||
auf **40 %** (dem am 19.08. dokumentierten User-Stand). `runtime_state` gewinnt
|
||
beim Start. **Und das UI-Feld „Einsatz %" wurde am 19.08. ENTFERNT (v=178)** —
|
||
es gibt seitdem **keinen Weg mehr, den persistierten Wert zu ändern**; nur der
|
||
Endpoint `/api/marginpct` existiert noch. Der Wert überstimmt die ini also
|
||
**dauerhaft und still**.
|
||
Folge in der Kette: ein manueller Trade nimmt 95 % der freien Margin → übrig
|
||
bleiben ~5 % → BRK wendet seine 9 % auf **diesen Rest** an. Aus „BRK bekommt
|
||
9 %" wird real **9 % von 5 % des Kontos**.
|
||
⚠ Dieselbe Falle wie `auto_signal` am 31.07. Der Unterschied: dort gab es einen
|
||
Schalter, um es zu bemerken und zu drehen — hier nicht mehr.
|
||
|
||
✅ **DREI DINGE GEBAUT, KEINE DAVON ÄNDERT DIE POSITIONSGRÖSSE** (die ist
|
||
User-Entscheidung, s. Dauerregel):
|
||
**(1) `mt5_utils.lot_grund()`** — „Lot-Fehler" erklärt sich selbst, mit Zahlen.
|
||
Beide Aufrufer (Market `_send`, Pending `place_stop`) nutzen ihn. ⚠ Anlass: der
|
||
Trader meldete einen ganzen Tag lang nur zwei Wörter, während die Ursache ein
|
||
Zusammenspiel aus drei Zahlen war.
|
||
**(2) Start-Warnung, wenn `runtime_state` die ini überstimmt** — live
|
||
verifiziert: *„⚠ Einsatz-%: runtime_state (95 %) ueberstimmt die ini (40 %). Es
|
||
gibt KEIN UI-Feld mehr dafuer."* Ohne sie ist der Zustand von aussen
|
||
unsichtbar.
|
||
**(3) Snapshot `squeeze_block` + BRK-Karte** — statt „keine — Slot frei" steht
|
||
dort jetzt der Grund. ⚠ **Ein leerer Slot und ein blockierter Slot sahen vorher
|
||
identisch aus**, und der Grund stand nur im Log, zwischen 269 gleichlautenden
|
||
Zeilen.
|
||
|
||
⚠⚠ **VIERTER FUND: die Log-Drossel griff NIE.** Der Merker lag in EINER
|
||
Variable, die beiden Order-Seiten scheitern aber im selben Tick mit
|
||
VERSCHIEDENEN Fehlern („Level passiert" oben, „Lot-Fehler" unten) — der Wert
|
||
wechselte bei jedem Aufruf. Real 269 Zeilen an einem Tag, 2 je Sekunde. Jetzt
|
||
je Order-Seite gemerkt; nach dem Deploy **0** Zeilen. **Eine Drossel, die nie
|
||
drosselt, ist schlimmer als keine — sie verdeckt genau die Meldung, die zählt.**
|
||
|
||
**DIE DREI WEGE HERAUS (User-Entscheidung, Zahlen zum aktuellen Stand):**
|
||
| Weg | Wirkung |
|
||
|---|---|
|
||
| `margin_brk` auf **25–30 %** | 24–29 € → 0,03 Lots, robust gegen die G/V-Schwankung |
|
||
| `margin_pct` auf **40** (= ini, dokumentierter Stand) | wirkt erst auf NEUE manuelle Trades, macht die Reserve dauerhaft |
|
||
| offene Position kleiner halten | die einzige Variante, die BRK sofort Luft gibt |
|
||
⚠ **Nichts davon wurde eigenmächtig gesetzt.**
|
||
|
||
## ⚠⚠⚠ SLOT-PRÜFUNG, FÜNFTER DURCHGANG — drei Lücken, eine davon gravierend (2026-08-20)
|
||
|
||
User-Auftrag (zum fünften Mal). Diesmal **systematisch statt punktuell**: alle
|
||
Methoden von `TradingEngine` per AST danach klassifiziert, welche Slot-Objekte
|
||
sie berühren. **36 Funktionen** fassen einen Slot an; 20 davon **nur Slot 1**.
|
||
|
||
⚠⚠⚠ **LÜCKE 1 — DIE GRAVIERENDSTE: EINE FRISCH GEFÜLLTE BRK-POSITION HATTE
|
||
KEIN TRAILING.** `trail_brk.toggle()` wurde an genau **zwei** Stellen gerufen —
|
||
`_rebind_brk` (nach einem Neustart) und `toggle_trail` (der TRAIL-Knopf). Der
|
||
**frische Pending-Fill kam in keiner davon vor** — und das ist der häufigste
|
||
Weg, auf dem eine BRK-Position überhaupt entsteht (**7 von 8** Trades seit dem
|
||
Pending-Umbau vom 05.08.).
|
||
Eine so gefüllte Position lief damit **nur mit dem Broker-SL**, also exakt in
|
||
der Variante, die `backtest_brk_slonly.py` am 19.08. **verworfen** hat: alle
|
||
zehn Konfidenzintervalle enthalten die Null, die Trefferquote fällt **40 →
|
||
21 %**, vier von fünf Trades laufen in den vollen −2R-Stop.
|
||
⚠ **Von aussen unsichtbar** — die Position sieht in jeder Anzeige normal aus,
|
||
ihr fehlt nur der Schutz. ⚠ Ironie: `_rebind_brk` loggt „Trailing des BRK-Slots
|
||
wiederhergestellt"; wiederhergestellt wurde etwas, das für einen frischen Fill
|
||
**nie an war**.
|
||
|
||
⚠⚠ **LÜCKE 2 — der Market-Fallback schaltete das FALSCHE Trailing ein.** `_open`
|
||
rief pauschal `self.trail.toggle(sym)`. Bei `source == "auto_squeeze"` heisst
|
||
das: **zwei Schäden auf einmal** — (a) die BRK-Position bleibt ungeschützt und
|
||
(b) auf der **manuellen** Position wird das Trailing wieder **eingeschaltet**,
|
||
auch wenn der Nutzer es dort gerade bewusst abgeschaltet hatte (etwa nach einer
|
||
SL-Handeingabe, die `deactivate()` auslöst). Sein handgesetzter Stop wäre danach
|
||
vom Trailing weitergezogen worden.
|
||
⚠ Noch nicht eingetreten, weil alle acht Market-Fallback-Versuche des 20.08.
|
||
schon an der Order scheiterten (`Lot-Fehler`) — der Pfad bricht davor ab.
|
||
|
||
⚠⚠ **LÜCKE 3 — ein geschlossener BRK-Trade löste KEINE Telegram-Meldung aus.**
|
||
`_close_notify_pending` wird in `_check_auto_close` armiert, und das liest
|
||
`self.trader.snapshot()`. Das wiegt schwer, weil seit dem 19.08. **nur noch
|
||
„Trade geschlossen"** durch den Telegram-Filter kommt: der autonome Pfad hätte
|
||
also **vollständig still** gehandelt — genau der Zustand, den der Befund vom
|
||
11.08. („alle neun Auslöser waren aus") beseitigen sollte.
|
||
✅ Behoben in `_check_brk_close_cooldown`, das den BRK-Flat-Übergang ohnehin
|
||
schon erkennt.
|
||
|
||
✅ **ALLE DREI BEHOBEN**, dazu die **Reichweiten-Warnung korrigiert**: sie sagte
|
||
„Broker-SL, **Trailing** und Circuit-Breaker greifen weiter" — das Trailing tat
|
||
es eben NICHT. Die Warnung selbst war also irreführend.
|
||
|
||
**ABGESICHERT — zwei Ebenen, beide mutationsgeprüft:**
|
||
· **`tests/test_pending_fill.py`** + 2 Tests: der Fill **muss** `trail_brk`
|
||
einschalten, und er **darf** das Trailing von Slot 1 **nicht** anfassen.
|
||
Mutation (Fix zurückgebaut) → Test fällt.
|
||
· **Pipeline-Stufe E** (`tools/check_slots.py`, Regel **E**): jeder Pfad, der
|
||
eine BRK-Position eröffnet (`_check_pending_fill`, `_open`), **muss**
|
||
`trail_brk` erwähnen. Mutation → **Exit 1**.
|
||
⚠ Dafür bekam die Test-Fixture erstmals **beide** Trailing-Attrappen — ohne die
|
||
war der gefährlichste Slot-Fehler überhaupt nicht prüfbar.
|
||
|
||
✅ **GEPRÜFT UND SAUBER:** Circuit-Breaker (schliesst beide), `close()` (beide),
|
||
`_check_open_notify` (beide), `_price_loop` (füttert beide), `_write_levels_file`
|
||
(Marker beider Slots), `snapshot` (beide), `_check_pending_fill` (Bindung
|
||
korrekt), `_pos_loop`-Reihenfolge (Fill **vor** Pending-Manager).
|
||
|
||
⚠ **BEWUSST NICHT GEÄNDERT — Slot-1-only, aber alle Features AUS:**
|
||
`_check_auto_close` (Notfall-Stop · Gewinn-Ziel · Trail-Restore),
|
||
`_check_sr_close`, `_check_adverse15`, `_check_flip_close`, `_check_auto_m15`,
|
||
`set_sltp` (die SL/TP-Felder der Trade-Leiste wirken nur auf Slot 1),
|
||
`_check_close_alert`. `_check_slot_reichweite` meldet das im Log, sobald eines
|
||
davon eingeschaltet wird und gleichzeitig eine BRK-Position läuft. Sie
|
||
zweislot-fähig zu machen ist ein Umbau der Notfall-Zustandsmaschine
|
||
(`_emergency_*_ticket`), kein Einzeiler — **offen, benannt**.
|
||
|
||
⚠⚠ **ZWEI CONFIG-WERTE SIND AUF 0 ZURÜCKGEDRIFTET** (nur berichtet, nicht
|
||
geändert — Sizing/Alarm sind User-Entscheidungen):
|
||
| Schlüssel | dokumentiert | ist |
|
||
|---|---|---|
|
||
| `close_notify_min_eur` | **50** (11.08.) | **0 = Telegram ganz still** |
|
||
| `auto_emergency_margin_pct` | **8** (07.08.) | **0** |
|
||
⚠ Bei `close_notify_min_eur = 0` nützt auch die soeben geschlossene Lücke 3
|
||
nichts: die Vormerkung hängt an `_close_notify_min > 0`. **Der Bot meldet
|
||
derzeit gar keinen Trade-Abschluss.**
|
||
|
||
## ⚠⚠ WEITERE PYTHON-BIBLIOTHEKEN? — ZWEI ERTRÄGE, KEINE NEUE LAUFZEIT-ABHÄNGIGKEIT (2026-08-20)
|
||
|
||
User-Frage. **Nicht nach Katalog beantwortet, sondern gegen die dokumentierten
|
||
Schwachstellen des Projekts** — und die beiden Kandidaten, die durchkamen,
|
||
wurden vorher **gemessen**, nicht empfohlen.
|
||
|
||
✅✅ **ERTRAG 1 — BLOCK-BOOTSTRAP (`core/stichprobe.block_ci95`), und er braucht
|
||
GAR KEINE Bibliothek.** Die Lücke stand seit dem 07.08. wörtlich in
|
||
`backtest_cost_gate.ci95`: *„für überlappende Beobachtungen ist er ZU
|
||
OPTIMISTISCH … dafür bräuchte es einen Block-Bootstrap — `ci95` ist dafür NICHT
|
||
gedacht."* Behelf war bisher `entkoppeln()`. **Auf echten Projektdaten
|
||
(`pbreak_predictions`, Bruchrate) kostet der Behelf dramatisch:**
|
||
| Verfahren | n | 95-%-KI | Breite |
|
||
|---|---|---|---|
|
||
| naiv, alle Zeilen | 4.702 | [0,413 … 0,442] | **0,028** ⚠ 3× zu eng |
|
||
| **entkoppelt (bisheriger Weg)** | **552** | [0,357 … 0,440] | 0,083 |
|
||
| **Block-Bootstrap L=30** | **4.702** | [0,387 … 0,469] | **0,081** |
|
||
**Der Block-Bootstrap trifft die Breite des entkoppelten Verfahrens — behält
|
||
aber alle 4.702 Beobachtungen statt 552 (88 % Datenverlust).**
|
||
⚠⚠ **Und das Entkoppeln kostet nicht nur Genauigkeit, es VERSCHIEBT die
|
||
Schätzung**: 0,399 gegen 0,427. Es nimmt systematisch die ERSTE Berührung je
|
||
Level+Stunde — eine verzerrte Teilstichprobe, kein neutraler Ausschnitt.
|
||
|
||
✅ **KALIBRIERUNG GEMESSEN statt behauptet** (AR(1)-Reihen mit bekanntem Mittel,
|
||
200 Läufe je Zelle, Soll 95 %):
|
||
| ρ | naiv | L=10 | L=30 | L=60 |
|
||
|---|---|---|---|---|
|
||
| 0,00 | 94 % | 94 % | 91 % | 92 % |
|
||
| 0,50 | **76 %** | 92 % | 91 % | 91 % |
|
||
| 0,90 | **40 %** | 80 % | 89 % | **90 %** |
|
||
| 0,97 | **18 %** | 55 % | 74 % | **83 %** |
|
||
⚠⚠ Die naive Zeile ist der eigentliche Befund: bei ρ=0,97 behauptet sie 95 %
|
||
Sicherheit und liegt in **18 %** der Fälle richtig.
|
||
⚠ **Ehrlich zu den Grenzen:** (a) bei UNABHÄNGIGEN Daten kostet der Block ~3
|
||
Punkte Überdeckung — dort bleibt `ci95` die bessere Wahl; (b) bei ρ=0,97 deckt
|
||
auch er nur 83 %. **Eine Verbesserung, keine Garantie** — genau so steht es im
|
||
Docstring.
|
||
⚠ Blocklänge wird aus der gemessenen Lag-1-Autokorrelation gewählt (auf den
|
||
Live-Daten: ρ=0,762 → L=95). 4 Tests, darunter die Überdeckungs-Probe.
|
||
|
||
✅✅ **ERTRAG 2 — `hypothesis` (dev-only), aufgenommen NACH einer Gegenprobe.**
|
||
Die Suite prüft bisher **Beispiele**, und die wähle ich selbst aus — genau dort
|
||
sitzt der blinde Fleck. Die teuersten Fehler des Projekts saßen auf **Grenzen**:
|
||
`room_info` (10.08.) musste `frei` auf dem UNGERUNDETEN Abstand entscheiden,
|
||
weil 0,5951 gerundet durchs 0,6-Gate rutscht.
|
||
**Gegenprobe: hypothesis findet genau diesen Fall — `dist=0.59765625` — in
|
||
Sekunden.** Erst danach aufgenommen.
|
||
⚠ **Neu `tests/test_invarianten.py`** (4 Eigenschafts-Tests auf REINEN
|
||
Funktionen: `sr_levels.cluster`, `stichprobe.entkoppeln`, `autokorrelation`).
|
||
Per `importorskip` — **die Suite läuft auch ohne die Bibliothek**.
|
||
⚠⚠ **Beim ersten Lauf fielen zwei Tests — beide waren MEINE Fehler, nicht die
|
||
des Codes**, und das ist genau der Nutzen: (1) `cluster` rundet auf 3 Stellen
|
||
(dokumentiert), das Ergebnis darf also 0,0005 ausserhalb der Eingabespanne
|
||
liegen — meine Invariante war zu streng; (2) zwei inhaltsgleiche dicts sind
|
||
`==`, meine Reihenfolge-Prüfung musste über die **Identität** laufen.
|
||
✅ Mutationsprobe: Rundung in `sr_levels` verbogen → **2 Tests fallen**.
|
||
|
||
⚠⚠ **WAS BEGRÜNDET ABGELEHNT WURDE — jeweils mit Messung oder Regelbezug:**
|
||
| Kandidat | Urteil |
|
||
|---|---|
|
||
| **numba** (installiert, ungenutzt) | Backtests laufen 11 s / 12k Bars — **nicht der Engpass**. Und ein Loop-Rewrite verletzt die Migrations-Regel (Bit-Gleichheit). |
|
||
| **statsmodels / arch** | hätten HAC bzw. Block-Bootstrap — jetzt in **15 Zeilen stdlib** erledigt, mit eigener Kalibrierung. Kleinere Oberfläche schlägt fremde Abhängigkeit. |
|
||
| **polars / pyarrow** | es gibt keinen DataFrame-Workload; die Backtests laufen auf Listen. |
|
||
| **loguru / structlog / rich** | das Logformat ist **forensisch tragend** (18-h-Copilot-Ausfall, stille Fehlbuchung, die 269 Pending-Zeilen). Es umzustellen kostet Beweismaterial. |
|
||
| **tenacity** | Wiederholungen haben hier eigene Semantik (`agent._mark_dead`: hart 401/402/403/404 → 30 min, Timeout/429 bewusst NICHT). |
|
||
| **httpx** (schon da als openai-Abhängigkeit) | `requests` läuft; ein Wechsel ohne Anlass ist Bewegung ohne Ertrag. |
|
||
| **pandas_ta** (installiert!) | alle Indikatoren sind handgeschrieben und **validiert**; ein Austausch würde jede dokumentierte Zahl verschieben. |
|
||
| **mypy** | kaum Annotationen vorhanden; `ty` deckt seit 19.08. die Aritäts-Fälle ab. |
|
||
|
||
✅ **`requirements.txt` (Laufzeit) bleibt unverändert** — das war die Vorgabe an
|
||
mich selbst: auf einer frischen Maschine soll der Bot mit möglichst kleiner
|
||
Oberfläche wieder laufen. Ergänzt wurde ausschliesslich `requirements-dev.txt`.
|
||
|
||
## ⚠⚠ SECHSTER DURCHGANG: SLOTS · JAVASCRIPT · BIBLIOTHEKEN (2026-08-20)
|
||
|
||
User-Auftrag. Diesmal wurde aus **jeder** der drei Achsen eine **Pipeline-Stufe**
|
||
gemacht — die vorigen Durchgänge waren Handarbeit und liefen nur, wenn jemand
|
||
daran dachte.
|
||
|
||
### JavaScript — die Render-Probe deckte 2 von 15 Funktionen ab
|
||
|
||
⚠⚠ **`tools/render_check.mjs` schnitt per Regex genau ZWEI Funktionen heraus**
|
||
(`renderM15`, `renderBrk`). **`renderKontext` und 13 weitere waren nie
|
||
abgedeckt** — und genau in `renderKontext` ist mir am selben Tag der
|
||
`d`-statt-`snap`-Beinahe-Fehler unterlaufen. Die Probe hätte ihn **durchgelassen**.
|
||
✅ **Neu geschrieben**: die **komplette** `app.js` läuft in einem VM-Kontext gegen
|
||
ein Minimal-DOM, und **jede** gefundene `render*`-Funktion wird aufgerufen —
|
||
zweimal: mit dem Fixture **und mit einem leeren Snapshot** (der Kaltstart, in dem
|
||
jedes Feld fehlt; dort schlagen fehlende Null-Prüfungen zu, nicht bei vollen
|
||
Daten). **13 Funktionen laufen durch.**
|
||
✅ **Mutationsprobe mit genau dem heutigen Fehler:**
|
||
```
|
||
❌ renderKontext(Fixture): ReferenceError: d is not defined
|
||
❌ render(Fixture): ReferenceError: d is not defined
|
||
❌ renderKontext(leerer Snapshot): …
|
||
```
|
||
⚠ **`node --check` meldet dabei NICHTS** — die Datei ist syntaktisch einwandfrei.
|
||
Das ist der blinde Fleck, den diese Stufe schliesst.
|
||
⚠ Kein npm, kein jsdom: das DOM ist eine Attrappe von 20 Zeilen. Prüf- und
|
||
Laufumgebung bleiben derselbe Parser (V8).
|
||
|
||
### Zwei neue Pipeline-Stufen
|
||
|
||
**F) ELEMENT-IDs** — greift die JS auf IDs zu, die es in der HTML nicht gibt, und
|
||
gibt es doppelte? Stand: **133 IDs, keine verwaist, keine doppelt.**
|
||
⚠ Kommentare werden vorher entfernt: `$("neu")` und `$("x")` stehen als
|
||
**Beispiele** im Kopfkommentar von `app.js` und wären sonst zwei Dauer-Fehlalarme
|
||
— und eine Prüfung mit Dauer-Treffern wird ignoriert (die Lehre vom Linter).
|
||
**G) ABHÄNGIGKEITEN** — ist jeder Import aus `server.py`/`core/` in
|
||
`requirements.txt`? Stand: **39 Dateien gelesen, alle 9 externen Importe
|
||
aufgeführt**, keine Versions-Drift zwischen gepinnt und installiert.
|
||
✅ Beide **mutationsgeprüft**: ID umbenannt → Befund; `feedparser` aus der Liste
|
||
gestrichen → Befund.
|
||
|
||
⚠⚠ **UND DIE PIPELINE HAT SICH SELBST ERWISCHT.** Mein erster Entwurf von Stufe G
|
||
importierte `ast` nicht. Der stille `except Exception: continue` liess dadurch
|
||
**jede** Datei durchfallen — und die Stufe meldete *„alle 0 externen
|
||
Laufzeit-Importe sind aufgeführt."* Ein grüner Haken, hinter dem **nichts**
|
||
geprüft wurde: die **fünfte** Wiederholung dieser Fehlerklasse im Projekt.
|
||
Gefangen hat es **`ruff F821` in derselben Pipeline**.
|
||
✅ Behoben doppelt: `ast` importiert **und** eine Plausibilitätsprüfung
|
||
(„weniger Dateien gelesen als vorhanden, oder 0 externe Importe → Befund"). Eine
|
||
Stufe, die nichts gelesen hat, darf nicht „sauber" melden.
|
||
|
||
### Slots — keine neuen Defekte, drei Punkte präzisiert
|
||
|
||
✅ **Kein geteilter Modul-Zustand**: die modulweiten Container in `trailing.py`
|
||
und `trader.py` (`_TF_LABELS`, `_PHASE_RANK`, `_RETCODE_MSG`) sind reine
|
||
Nachschlagetabellen, kein `global`-Statement. Die beiden Instanzen sind
|
||
tatsächlich unabhängig — das war die Voraussetzung des ganzen Umbaus.
|
||
⚠ **`set_sltp` bleibt Slot-1-only, verhält sich aber SICHER**: liegt nur eine
|
||
BRK-Position, gibt `modify_sltp` einen Fehlertext zurück (`if not self.ticket`).
|
||
Es fasst also **nicht** die falsche Position an — die SL/TP-Felder der
|
||
Trade-Leiste sind lediglich auf Slot 1 beschränkt. Bekannte Grenze, kein Fehler.
|
||
⚠ **`trail_brk.enabled` wird NICHT persistiert** (nur `brk_ticket`).
|
||
`_rebind_brk` schaltet es nach einem Neustart bedingungslos ein. Das ist die
|
||
sichere Richtung — aber wer es bewusst abgeschaltet hat, bekommt es nach einem
|
||
Neustart zurück. Bewusst so gelassen: eine BRK-Position ohne Trailing ist
|
||
gemessen die schlechtere Variante.
|
||
|
||
### Bibliotheken
|
||
|
||
✅ **Nichts fehlt, nichts driftet.** `pandas` und `scikit-learn` stehen in
|
||
`requirements-dev.txt` und werden in **0** Dateien importiert (so dokumentiert);
|
||
`numba`, `joblib`, `tqdm`, `pandas_ta`, `rich`, `httpx` sind als Beifang anderer
|
||
Pakete installiert und ebenfalls ungenutzt. Kein Handlungsbedarf — sie stehen
|
||
nicht im Laufzeit-Pfad.
|
||
⚠ **Struktureller Rest, jetzt benannt**: `core/engine.py` importiert
|
||
`weekly_review` und `measurement_reminder` — zwei Skripte der obersten Ebene.
|
||
Dieselbe Klasse wie der frühere `backtest_breakout_squeeze`-Import, der am 07.08.
|
||
nach `core/squeeze_scan.py` gezogen wurde. **Hier entschärft**, weil beide
|
||
**lazy** und in `try` importiert werden (1×/Tag bzw. 1×/Woche) und ein
|
||
Fehlschlag sichtbar gemeldet wird. In Stufe G als projekteigen ausgenommen —
|
||
mit Begründung im Code, damit es nicht als Versehen gelesen wird.
|
||
|
||
**Pipeline jetzt: A · A2 · A3 · B · B2 · C · C2 · D · E · F · G** — alle grün,
|
||
97 Tests, deployt und verifiziert.
|
||
|
||
## ⚠⚠ RUHENDE ORDERS: AUFRÄUMEN STATT LIEGENLASSEN (User-Vorgabe 2026-08-20)
|
||
|
||
User: *„was sind ruhende Orders. Lösche die, wenn der Trade nicht eröffnet
|
||
werden kann."* — Anlass war, dass MT5 SL/TP-Linien zeichnete, obwohl keine
|
||
Position lief. **Das waren die liegenden Stop-Orders des Breakout-Traders**;
|
||
MT5 stellt jede Pending-Order samt ihrem mitgeschickten SL selbst dar. ✅ Der
|
||
Bot exportiert **nie** SL/TP ins Chart (0 Treffer im `.mq5`, keine `SL;`-Zeile
|
||
in der CSV) — kein Anzeigefehler, sondern eine Broker-Funktion.
|
||
|
||
⚠⚠ **DIE PRÜFUNG DER FRAGE FÖRDERTE ZWEI ECHTE VERWAISUNGS-PFADE ZUTAGE.**
|
||
`_manage_squeeze_pending` stieg **VOR** dem Aufräum-Block aus, wenn
|
||
`squeeze_pending_entry=false` **oder** kein Symbol da war (MT5-Aussetzer). Die
|
||
Orders sind `ORDER_TIME_GTC` — **sie verfallen nicht**. Eine so verwaiste Order
|
||
hängt beim Broker, wird von niemandem mehr bewertet und kann Stunden später
|
||
füllen: **ohne Dedup, ohne Guard-Prüfung** und (bis zum Fix vom selben Tag)
|
||
**ohne Trailing**.
|
||
✅ Neu `engine._pendings_abraeumen(sym, grund)` — beide Pfade räumen jetzt ab,
|
||
statt nur zurückzukehren.
|
||
|
||
✅ **UND DIE EIGENTLICHE VORGABE UMGESETZT: ein halbes Setup wird aufgelöst.**
|
||
Scheitert eine Seite mit **Lot-Fehler** (die freie Margin reicht nicht einmal
|
||
fürs Mindestlot), wird auch die **Gegenseite** storniert. Grund: der gemessene
|
||
Squeeze ist ein Ausbruch aus der Box in **beide** Richtungen; läge nur eine
|
||
Seite, handelte der Bot ihn nur noch in **eine** — das ist nicht das Setup, das
|
||
validiert wurde.
|
||
⚠ **Ausdrücklich NICHT bei „Kurs hat das Level bereits passiert"**: das ist
|
||
vorübergehend und betrifft naturgemäß nur die eine Seite (der Kurs steht gerade
|
||
an der Box-Kante); die Gegenseite bleibt ein gültiger Einstieg. Der Lot-Fehler
|
||
dagegen betrifft das **Konto** und damit beide Seiten.
|
||
⚠ Erkannt über die **Konstante `mt5_utils.LOT_ZU_KLEIN`**, nicht über ein
|
||
deutsches Wort im Fließtext: eine Erkennung, die an einer Formulierung hängt,
|
||
hört still auf zu funktionieren, sobald jemand den Satz umschreibt — und fällt
|
||
dabei auf die **stille** Seite (kein Aufräumen), nicht auf die laute.
|
||
|
||
⚠⚠⚠ **DIE LOG-DROSSEL WAR AM SELBEN TAG ZUM ZWEITEN MAL AUSGEHEBELT — durch
|
||
meine eigene Verbesserung.** Vormittags lag der Merker in EINER Variable für
|
||
beide Order-Seiten (behoben). Dann bekam die Meldung über `lot_grund` ihre
|
||
Zahlen — und die **freie Margin ändert sich mit jedem Tick**. Der Text war also
|
||
jedes Mal neu, der Vergleich `!= err` traf immer zu: wieder **2 Zeilen pro
|
||
Sekunde**.
|
||
✅ Gedrosselt wird jetzt auf die **URSACHE**, nicht auf ihre Formulierung
|
||
(`_err_key`: alle Zahlen → `#`). Gemessen: **7 Zeilen in 90 s** statt ~180 —
|
||
und die verbliebenen sind echte Wechsel zwischen den beiden Ursachen, kein Lärm.
|
||
⚠ **Lehre: eine Drossel, die einen TEXT vergleicht, ist so stabil wie der Text.**
|
||
Sobald eine Meldung Messwerte enthält, muss auf einen normalisierten Schlüssel
|
||
gedrosselt werden.
|
||
|
||
✅ Live verifiziert nach dem Deploy: **0 liegende Orders**, keine verwaisten
|
||
Reste, Startup-Schonfrist räumt korrekt ab und die Orders kommen danach zurück,
|
||
sobald die Margin reicht.
|
||
|
||
## ⚠⚠⚠ P(break) IST LIVE **INVERTIERT** — und es steuert echtes Geld (2026-08-19)
|
||
|
||
Die fällige Messung ist entscheidbar geworden: **n=530 entkoppelt** gegen die
|
||
Latte 350. Ergebnis:
|
||
|
||
| | |
|
||
|---|---|
|
||
| **AUC live** | **0,452** 95-%-KI **[0,405 … 0,504]** |
|
||
| Messlatte (Nachtrainieren 31.07.) | **0,654** — weit ausserhalb des Intervalls |
|
||
|
||
⚠⚠ **Die Klassentabelle zeigt eine MONOTONE UMKEHRUNG — das ist kein
|
||
Kalibrierungsfehler mehr, das Modell rankt rückwärts:**
|
||
| P(break) | n | sagt | real | |
|
||
|---|---|---|---|---|
|
||
| 0–19 | 188 | 8,3 % | **45,2 %** | **+37,0 Pp** |
|
||
| 20–34 | 118 | 27,9 % | 44,1 % | +16,2 Pp |
|
||
| 35–49 | 133 | 42,0 % | 32,3 % | −9,7 Pp |
|
||
| 50–64 | 62 | 56,9 % | 37,1 % | −19,8 Pp |
|
||
| **65–100** | 29 | **72,5 %** | **37,9 %** | **−34,6 Pp** |
|
||
Die Klasse, in der das Modell am sichersten „hält“ sagt, bricht **am
|
||
häufigsten**. Die Klasse, in der es „bricht“ sagt, bricht am seltensten.
|
||
|
||
⚠⚠⚠ **OPERATIV BRISANT, und die alte Entwarnung ist STALE.** CLAUDE.md
|
||
entwarnt an zwei Stellen mit *„operativ kein Notfall: `auto_sr_close = false`
|
||
seit 06.08.“* — **er wurde am 11.08. wieder eingeschaltet.** Das Gate feuert
|
||
bei `p_break < 0,35` = „Level hält, Gewinn mitnehmen“; genau die Klassen
|
||
darunter haben real **44–45 %** Bruchrate, also die höchste im Feld. **Das Gate
|
||
wählt systematisch die falschen Fälle aus.**
|
||
⚠ Damit ist der S/R-Auto-Close faktisch **ungegatet** — und der **pauschale**
|
||
S/R-Close ist im Projekt **2× gemessen und verworfen** (Trefferquote verdoppelt
|
||
sich, PF/ΣR fallen in beiden Hälften = Gewinner-Kappen).
|
||
⚠ Die Live-Bilanz (13 `sr_close` seit 11.08., **+226,71 €**) ist **kein
|
||
Gegenbeleg**: `_check_sr_close` schliesst konstruktiv **nur im Plus** (`pnl>0`),
|
||
die Zahl ist also per Bauart positiv. Aussagekräftig wäre allein der
|
||
kontrafaktische Vergleich gegen den kanonischen Exit.
|
||
|
||
⚠ **Zweiter Versuch derselben Reparatur wäre der dritte Fit.** Am 07.08. wurde
|
||
die Niveau-Hypothese gemessen und **widerlegt** (Roll-Achse fällt live durch,
|
||
Regime-Merkmal ebenso; die oberste Klasse blieb invertiert). Eine Inversion
|
||
lässt sich durch Niveau-Verschiebung prinzipiell nicht beheben.
|
||
➤ **Naheliegendste Konsequenz: `auto_sr_close = false`**, bis das Modell
|
||
entweder neu gebaut oder die Level-Auswahl als Ursache ausgeschlossen ist. Das
|
||
ist eine **User-Entscheidung** — nicht eigenmächtig geschaltet.
|
||
|
||
### ✅ S/R-AUTO-CLOSE ABGESCHALTET (User 2026-08-19, nach dem Inversions-Befund)
|
||
|
||
Alle drei Stellen konsistent: Snapshot **False** · `runtime_state.json` **False**
|
||
· ini **false** (Backup `oil_widget_config.ini.bak-2026-08-19-srclose-aus`).
|
||
⚠ Die ini stand noch auf `true`, während `runtime_state` bereits `false` sagte —
|
||
verhalten korrekt (runtime gewinnt), aber genau die **latente Inkonsistenz**,
|
||
durch die `auto_squeeze` am 11.08. unbemerkt aus und `auto_m15` am 19.08.
|
||
unbemerkt AN war. Ein gelöschtes `runtime_state.json` hätte SR zurückgeholt.
|
||
✅ **Config-Wächter-Anker gedreht**: `auto_sr_close` validiert jetzt gegen
|
||
**`false`** (war `true`). Begründung ist der Befund darunter — solange das
|
||
P(break)-Modell live invertiert rankt (AUC 0,452, Klassen monoton rückwärts),
|
||
ist der gegatete S/R-Close faktisch ein **pauschaler**, und der ist 2× gemessen
|
||
und verworfen. **Ein Wiedereinschalten wäre damit eine Abweichung von der
|
||
Messung und soll auffallen.**
|
||
|
||
**Was bleibt aktiv:** der Hinweis in der App (`#pos-srhint`), die
|
||
P(break)-Chart-Linien, der Copilot-Kontext, `stop_approach` und das
|
||
Live-Prognose-Tracking (`pbreak_predictions`) — `_sr_close_hint` wird
|
||
unabhängig vom Schalter berechnet. ⚠ Diese Anzeigen tragen dieselbe Inversion;
|
||
sie sind Kontext, kein Urteil.
|
||
**Was weiter schützt:** Broker-SL 2×ATR, Trailing, Time-Stop.
|
||
**Zurück:** Knopf **S/R** — dann bitte den Wächter-Anker mitdrehen.
|
||
|
||
## ✅⚠⚠ BESTÄTIGTES CHARTMUSTER IM MT5-CHART (2026-08-21, Indikator v1.45/1.46)
|
||
|
||
User: „kannst Du das bestätigte Chartmuster in das jeweils passende Chart
|
||
(M1, M5, M15, H1) im MT5 einzeichnen".
|
||
|
||
**Neue CSV-Zeile `PT;<TF>;<name>;<richtung>;<trigger>;<ziel>`** — gespeist aus
|
||
`engine.patterns_tf` (fünf `PatternDetector`, je Zeitebene einer). Exportiert
|
||
werden **nur `status == "confirmed"`**-Muster. Der Indikator zeichnet die
|
||
Trigger-/Nackenlinie durchgezogen und das Measured-Move-Ziel gepunktet, dazu ein
|
||
Label mit Name und Richtung.
|
||
|
||
⚠ **Das Label nennt die Messung mit: „(trifft 13–38 %)".** Die
|
||
Muster-Klasse ist im Projekt **3× als Signal verworfen**
|
||
(`backtest_doubletop.py`, `backtest_structure_tf.py`, `backtest_patterns.py`) —
|
||
der positive ØR dort kam nachweislich vom **Trailing auf einem Momentum-Bruch**,
|
||
nicht von der Muster-Prognose. Eine Linie im Chart ohne diese Zahl würde
|
||
autoritativer aussehen, als sie ist. **Kein Verdict-Gewicht, kein Trigger.**
|
||
|
||
⚠ **Round-Robin statt fünf Aufrufe am Stück.** Der erste Bau ließ alle fünf
|
||
Detektoren in EINEM Trend-Loop-Durchlauf unter `mt5_lock` laufen — real hängte
|
||
danach `_trend_loop` beim Serverstart, `_pos_loop` 70 s später, und alle 15 s
|
||
stand „Time-Stop-Close fehlgeschlagen: MT5 belegt" im Log (User: „G/V
|
||
aktualisiert nicht mehr"). Jetzt wird **je Durchlauf genau EINE** Zeitebene
|
||
aufgefrischt.
|
||
|
||
⚠⚠ **DER FEHLER, der es unsichtbar machte — und er traf JEDE Zeitebene, nicht
|
||
nur M30 (v1.46, User: „in M30 müsste ,Tasse mit Henkel' zu sehen sein, ist aber
|
||
nicht sichtbar").** Der TF-Filter verglich gegen `TFName(PERIOD_CURRENT)`.
|
||
**`PERIOD_CURRENT` ist der WERT 0.** MT5-Funktionen wie `iTime()` deuten die 0
|
||
selbst als „dieser Chart" — ein `switch` trifft damit aber **keinen case**, und
|
||
`TFName()` fiel auf `EnumToString()` zurück und lieferte den String
|
||
`"PERIOD_CURRENT"`. Der Vergleich war deshalb **immer** wahr, jede `PT`-Zeile
|
||
wurde übersprungen.
|
||
✅ **Fix: `TFName((ENUM_TIMEFRAMES)_Period)`** — `_Period` liefert die
|
||
tatsächliche Chart-Periode als Wert. Die übrigen `PERIOD_CURRENT`-Vorkommen im
|
||
Indikator sind **korrekt** und bleiben: sie stehen als Argument in
|
||
`iTime()`/`Bars()`, wo MT5 die 0 auflöst.
|
||
⚠ **Merksatz: `PERIOD_CURRENT` ist ein Argument-Platzhalter, kein Wert.** Wer
|
||
darauf vergleicht oder `switch`t, prüft gegen die 0 — und die gehört zu keiner
|
||
Zeitebene.
|
||
⚠ Von außen sah alles richtig aus: CSV-Zeile vorhanden, Snapshot `confirmed`,
|
||
`.ex5` frisch, `0 errors`. **Nur das Ergebnis fehlte** — dieselbe Klasse wie die
|
||
vier dokumentierten Fälle „Erfolg melden, wo nichts geprüft wurde".
|
||
Abschaltbar: `InpShowMuster=false`; Farben `InpMusterBullColor`/`…BearColor`.
|
||
|
||
## ✅ MUSTER ALS GEFÜLLTE GEOMETRISCHE FORMEN IM MT5-CHART (2026-08-21, v1.47)
|
||
|
||
User-Vorgabe: Dreiecke/Rechtecke/Ellipsen über `OBJ_*` an den berechneten
|
||
Swings verankern, **„mit leichtem Grauton gefüllt"**.
|
||
|
||
**Die `PT;`-Zeile trägt jetzt die Geometrie mit:**
|
||
```
|
||
PT;<TF>;<name>;<dir>;<trigger>;<ziel>;<form>;<t1>;<p1>;<t2>;<p2>;…
|
||
```
|
||
| `form` | Anker | MT5-Objekt | Muster |
|
||
|---|---|---|---|
|
||
| `dreieck` | 3 | `OBJ_TRIANGLE` | Doppeltop/-boden, Dreiecke, Keile, Wimpel |
|
||
| `dreieck2` | 5 | **2×** `OBJ_TRIANGLE` | SKS, inverse SKS, Dreifach-Top/-Boden |
|
||
| `rechteck` | 2 | `OBJ_RECTANGLE` | Rechteck (Range), Flagge |
|
||
| `ellipse` | 3 | `OBJ_ELLIPSE` | Tasse+Henkel, inverse Tasse |
|
||
|
||
⚠ **`dreieck2` braucht ZWEI Objekte**, weil `OBJ_TRIANGLE` nur drei Punkte
|
||
kennt. Die Dreiecke (Schulter–Kopf und Kopf–Schulter) kacheln die Silhouette
|
||
exakt — kein Annähern, keine Überdeckung.
|
||
|
||
⚠⚠ **Die Füllung kann KEINEN andersfarbigen Rand haben:** `OBJPROP_FILL`
|
||
färbt mit `OBJPROP_COLOR`. Deshalb ist die Fläche neutral grau
|
||
(`InpMusterFill`, `C'205,205,205'`) **und `OBJPROP_BACK`** — sie liegt hinter
|
||
den Kerzen; die Richtungsaussage tragen die farbige Trigger- und Ziellinie
|
||
darüber. Das ist auch inhaltlich richtig: die **Form** ist eine Beobachtung,
|
||
keine Richtungsprognose.
|
||
|
||
**Umsetzung Python** (`core/patterns.py`): `add()` nimmt `form` und `punkte`
|
||
(Bar-Index + Preis) und legt sie als `form`/`anker` in den Muster-dict — **rein
|
||
additiv**, kein bestehender Schlüssel ändert sich (das Verdict-Gewicht liest
|
||
ohnehin nur `type`/`dir`/`status`/`quality`).
|
||
⚠ Neuer Helfer **`_keil()`** für die konvergierenden Muster: er schneidet die
|
||
beiden Linien und **deckelt den Apex** auf `last_idx + 40` — ein fast
|
||
paralleles Linienpaar schneidet sich sonst im Unendlichen und spännte ein
|
||
Dreieck über den halben Chart. Nach links begrenzt auf einen Bar hinter dem
|
||
letzten Stützpunkt, sonst läge der Apex INNERHALB des Musters und die Form
|
||
würde sich selbst durchdringen.
|
||
⚠ `_anker()` rechnet einen Index **jenseits des letzten Bars** über die
|
||
Bar-Dauer hoch (`T` hat dafür keinen Eintrag). Ohne das wäre der Apex auf den
|
||
letzten Bar geklemmt und **jedes Dreieck hätte einen senkrechten rechten Rand**.
|
||
Die Bar-Dauer kommt aus den DATEN (`T[-1]-T[-2]`), nicht aus der TF-Konstante —
|
||
so stimmt sie auch, wenn der Broker Bars auslässt.
|
||
⚠ Die Zeitstempel sind **rohe Broker-Zeit** aus `rates["time"]` — genau das,
|
||
was `iTime()` liefert und was ein `OBJ_*`-Anker erwartet. **Keine
|
||
Offset-Rechnung**, gleiche Konvention wie `EQ;` und `TR;`.
|
||
|
||
✅ **Vor dem Export geprüft, dass Form und Punktzahl zueinander passen** — ein
|
||
Dreieck mit zwei Punkten würde der Indikator still verwerfen, und der Fehler
|
||
wäre von außen unsichtbar (dieselbe Klasse wie der `PERIOD_CURRENT`-Fall).
|
||
✅ **Abwärtsverträglich in beide Richtungen:** eine alte CSV ohne Geometrie
|
||
fällt still auf „nur Linien" zurück, ein alter Indikator ignoriert die
|
||
Zusatzfelder.
|
||
✅ **Gegenprobe an sechs synthetischen Mustern** (Doppeltop, Doppelboden, SKS,
|
||
Tasse, auf-/absteigendes Dreieck): Form, Punktzahl und aufsteigende Zeitstempel
|
||
stimmen; der Dreiecks-Apex landet korrekt in der Zukunft.
|
||
✅ **Live verifiziert:** Snapshot `patterns_tf.M5 = {form: dreieck, anker: 3}`,
|
||
CSV-Zeile
|
||
`PT;M5;Doppeltop;bear;87.384;86.814;dreieck;1787327400;87.954;1787328000;87.384;1787330100;87.914`.
|
||
Abschaltbar wie bisher `InpShowMuster=false`; Füllfarbe `InpMusterFill`.
|