Ursache der P(break)-Live-Degradation gefunden: Merkmals-Diskrepanz

backtest_pbreak_touches.py (M30-Level wie live, Touch-Zahl als 5. Merkmal)
plus Rekonstruktion der Live-Merkmale aus candles_m1.

Beweiskette:
1) Der Backtest reproduziert die 0,71 (Fit H1 -> AUC oos H2 0,715).
   Modell und Pipeline sind in Ordnung.
2) Populations-Hypothese widerlegt: live ist ausgerechnet die 1. Beruehrung
   (die trainierte Situation) mit AUC 0,445 die SCHLECHTESTE Gruppe, die
   Chop-Faelle (5.+) mit 0,577 die beste. Der Versatz "Modell 23-25 %, real
   40-42 %" ist in JEDER Teilmenge gleich gross.
3) Merkmale rekonstruiert (n=2275): mom3 live 0,149 vs Training 1,4523
   (-1,303), mom6 0,181 vs 1,6565 (-1,476). Daraus z-Versatz -1,05 ->
   aus 39,4 % Basisrate werden ~19 %, beobachtet 23 %. Erklaert praktisch
   die gesamte Fehlkalibrierung.

MECHANISMUS: Training fixiert das Level beim Entry (>=0,3xATR entfernt) und
wartet auf den ersten Touch -> mom3 ~1,45xATR = ein Schub. Live waehlt
_draw_levels das naechstgelegene Level jede Sekunde NEU -> der Kurs steht
oft neben einem Level, das gerade erst zum naechsten wurde, ohne Anlauf ->
mom3 ~0,15. Gleicher Code, andere Situation. Folge: das Modell liegt live
immer unter der Schwelle 0,55 -> das Gate war seit Inbetriebnahme faktisch
nie aktiv (91 % aller Beruehrungen geschlossen = der pauschale S/R-Close,
der 2x verworfen wurde).

Zwei Reparaturwege (beide ungemessen): (a) Ziel-Level beim Oeffnen fixieren
statt laufend neu waehlen, oder (b) Modell auf live-spiegelnder Stichprobe
neu trainieren.

TOUCH-ZAHL (User-Idee): im Backtest AUC 0,715 -> 0,725, Gewicht +0,176 fuer
die 2.-4. Beruehrung (Vorzeichen bestaetigt), aber R-Ertrag nicht robust
(H1 +131, H2 -43). Im Trainings-Datensatz 76 % Erst-Beruehrungen und KEINE
5+-Faelle - das live beobachtete umgekehrte U ist dort nicht abbildbar.
Kleine Verbesserung, nicht der Hebel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-07-31 08:45:53 +02:00
co-authored by Claude Opus 5
parent 92995b08d7
commit b137967a68
2 changed files with 322 additions and 0 deletions
+38
View File
@@ -1449,6 +1449,44 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
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 2325 %, real 4042 %" 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(levelentry)/atr`, `backtest_srclose_prob.py:86`), `mom6`/`mom3`
in beiden am Touch-Bar `jt`, `confirm`/`reject` dieselbe ±0,5×ATR-Definition, Timeout