Stufe 3: geteilter Exit-Kern (core/exit_model.py) + Korrektur der Stufe-1-Begruendung
core/exit_model.py (NEU): LIVE:ExitParams als EINZIGE Quelle der Exit-
Parameter; core/trailing.py leitet _TRAIL_START_ATR, _BREAKEVEN_ATR,
_PHASE4_* und _MULT_BY_TF jetzt davon ab statt eigene Zahlen zu halten.
Aendert jemand LIVE.mult, aendern sich Live-Verhalten UND Messung gemeinsam
-> Deployment-Drift-Fall 3 ist konstruktiv unmoeglich geworden. Dazu die
kanonische simulate() fuer 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:
A) Phasen ~ 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)
=> Dead-Hours, EIA-Blackout, Bounce, Chop-Gate, Stunden-Analyse und
breakout_k ruhen auf einem Exit, der dem Live-System nicht entspricht.
Bewusst NICHT stillschweigend umgestellt (wuerde historische Schluesse
rueckwirkend aendern); als LEGACY_SIMPLE / LEGACY_BE10 markiert.
(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.
(3) backtest_trailing.py koppelt den Initial-SL an mult (sl = entry -
d*mult*atr) statt fix 2,0.
KORREKTUR DER STUFE-1-BEGRUENDUNG: die "67 % groesserer Einzelverlust" war
ein Artefakt von (3) - dort war der Worst-Case per Konstruktion gleich dem
Multiplikator. Sauber nachgemessen mit fixem SL (backtest_trailmult.py, NEU,
80k Bars, 2 Halbjahre):
Trail 1,0 H1 -73 · H2 +1346 · Worst -2,00
Trail 1,5 H1 -325 · H2 +1294 · Worst -2,00 (live)
Trail 2,0 H1 -446 · H2 +1195 · Worst -2,00
Trail 2,5 H1 -1174 · H2 +1223 · Worst -2,00
Trail 3,0 H1 -1088 · H2 +1397 · Worst -2,00
Der Worst-Case ist bei JEDEM Multiplikator identisch -2,00. Die Entscheidung
bleibt richtig (1,5 schlaegt 2,0 und 2,5 in beiden Haelften), nur die
Tail-Begruendung war falsch.
NEU UND OFFEN: Trail 1,0 schlaegt 1,5 in BEIDEN Haelften - eigener
Vorschlag, bewusst nicht ungefragt umgesetzt.
Aequivalenz verifiziert (500 synthetische Kursreihen je Fall): _candle_fade
und _atrfloor sind bitgenau identisch zur neuen simulate().
Live verifiziert: "AKTIVIERT ATR=0.5796 (M30) mult=1.5x".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
97617cd81d
commit
4c1a68a7e1
@@ -1968,6 +1968,22 @@ 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.
|
||||
⚠ **NEU und offen:** **Trail 1,0 schlägt 1,5 in BEIDEN Hälften** (−73/+1346 vs
|
||||
−325/+1294) — eigener Vorschlag, bewusst NICHT ungefragt umgesetzt.
|
||||
- **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 ·
|
||||
@@ -1982,10 +1998,36 @@ konstant; (d) es gab **keine Telemetrie über die Entscheidung selbst**.
|
||||
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 — OFFEN (später, einzeln):** geteilter Kern. Den Exit-Simulator EINMAL in
|
||||
`core/exit_model.py` extrahieren und von `trailing.py` UND allen Backtests nutzen
|
||||
lassen — Fall 3 wäre damit unmöglich gewesen. Gegenprüfung: bestehende Backtests
|
||||
müssen danach dieselben Zahlen liefern.
|
||||
- **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.
|
||||
|
||||
⚠ **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`
|
||||
|
||||
Reference in New Issue
Block a user