User: "ueberpruefe 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." FRONTEND SAUBER: #hdr-pnl liest nur d.position, #hdr-pnl-brk nur d.position_brk - keine Summe, kein Fallback. Aber das ist die falsche Stelle zum Suchen: stehen im Backend beide Felder auf derselben Position, kann keine Anzeige das mehr richten. Geprueft wurde deshalb die AUSWAHL in TradeManager._refresh_locked. ⚠⚠ FUND 1 - die Sperrliste kannte nur zwei von DREI Quellen eines BRK-Tickets: trader_brk.ticket ✅ nur im Speicher _pending_tickets ✅ nur im Speicher _brk_restore ❌ FEHLTE - und nur das ueberlebt den Neustart Nach einem Neustart sind die ersten beiden leer, und die Reihenfolge im _pos_loop besiegelt es: trader.refresh() laeuft DREI Zeilen vor _rebind_brk(). Slot 1 greift die BRK-Position also, bevor sie zurueckgebunden werden kann. _rebind_brk erkennt das zwar und bricht ab - damit ist der Schaden aber nur festgestellt, nicht behoben: die BRK-Position haengt 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. Behoben ueber getattr(..., None), also unabhaengig davon, WANN das Attribut gesetzt wird - genau die Reihenfolge-Falle, die in zwei Tagen zweimal zugeschlagen hat. ✅✅ FUND 2 - zwei verschiedene Rechnungen nebeneinander. Bei geschlossenem Markt zeigt Slot 1 seit dem 01.08. eine HL-Schaetzung (pnl_hl), der BRK-Slot bekam die nie und zeigte den EINGEFRORENEN Broker-Wert, ohne dass man es der Anzeige ansieht. Neu pnl_hl_brk, mit ≈ markiert. Geprueft statt angenommen, dass _tick_size/_tick_value am BRK-Slot gesetzt sind. ✅ day_pl.open summiert korrekt BEIDE Slots - die Summe gehoert dorthin, die Trennung in die beiden G/V-Felder. BELEG - tests/test_slot_gv.py, 6 Tests auf dem ECHTEN TradeManager (Broker gestubbt): zwei Positionen gleichzeitig je im eigenen Slot · Slot 1 adoptiert die BRK-Position nicht · Pending-Fill wird nicht weggeschnappt · der BRK-Slot adoptiert nie · plus die Regressionsprobe, die den alten Zustand festhaelt. NEUE PIPELINE-STUFE E (tools/check_slots.py): die Sperrliste muss alle drei Namen erwaehnen. Mutationsprobe: Merker entfernt -> Exit 1. ⚠⚠ Ehrlich zur Arbeitsteilung: der pytest belegt den MECHANISMUS, baut die Lambda aber selbst nach und bleibt bei der Mutation gruen. Erst die statische Stufe belegt, dass die ENGINE sie richtig verdrahtet. Keine der beiden allein haette gereicht. ⚠⚠ NEBENBEFUND, korrigiert: mein Schreib-Helfer hat SECHS Dateien still von LF auf CRLF gedreht (io.open(...,"w") uebersetzt auf Windows). Mein erster Check mit `grep -c $'\r$'` meldete faelschlich 0 - erst die BYTE-Zaehlung zeigte 8959 CR in CLAUDE.md. Alle sechs auf LF zurueckgesetzt. core.autocrlf=true haette es im Repo normalisiert, im Arbeitsbaum aber nicht. Live verifiziert: Slot 1 T=50396824 (+10,34), BRK flat, day_pl.open 10,34 - kein gemeinsames Ticket. Deploy mit --feld pnl_hl_brk, alle 5 Schritte gruen. ⚠ Noch nicht beobachtet: ein Neustart MIT offener BRK-Position - der Fall, den der Fix adressiert. v=201. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8960 lines
618 KiB
Markdown
8960 lines
618 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.
|
||
|
||
⚠⚠ **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.
|
||
|
||
## ⚠⚠⚠ 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.
|