S/R-Level von M5 auf M30 umgestellt + P(break)-Modell nachtrainiert
User: "DIE S/R Linien liegen oft sehr nahe beieinander" -> Option 2 (beides umstellen, Modell nachtrainieren) gewaehlt. Erst gemessen: backtest_level_tf.py, 80k M5-Bars, 2 Halbjahre, Trades bleiben M5, nur die Level-Quelle variiert. Ergebnis (M30 gewinnt in BEIDEN Haelften): TF AUC(H2) Abstand <1xATR dR bei P<0.55 (H1/H2) M5 0.710 0.61xATR 71% +6698 +7749 (bisher live) M15 0.725 1.16xATR 46% +10138 +12071 M30 0.721 1.95xATR 28% +12424 +13047 <- gewaehlt Die Beschwerde ist damit quantifiziert: 71% der M5-Level lagen naeher als 1xATR. Umsetzung: - wave_rec: _m30_hl gespeichert, neues _pb_levels30 (k=3, letzte 50 M30-Bars = trainingsgleich zum 300-M5-Lookback), im Snapshot. - engine._draw_levels nutzt pb_levels30 (Fallback pb_levels beim Kaltstart); speist Chart, MQL5-CSV, Dashboard UND den P(break)-Auto-Close. - _PB_MU/_PB_SD/_PB_W auf M30 nachtrainiert: out-of-sample validiert (H1->H2), dann auf allen 80k gefittet (34771 Touches, Kalibrierung 23/25, 38/36, 58/59, 86/87). Merkmale bleiben M5. Alte M5-Werte als Kommentar (Rueckweg). - NICHT mitveraendert: Entry-Raum-Gate nutzt weiter M5-pb_levels (entry_room_atr=0.6 ist darauf kalibriert). Verifiziert live: R/S 84.617/83.722 = 2.9xATR Abstand (vorher 0.28xATR), M30-Pivots 4/7 statt M5 27/31, keine Fehler im Log. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
06b37a1474
commit
b77b3d60f5
+30
-7
@@ -86,9 +86,18 @@ _STARTUP_GRACE_S = 60.0 # nach Bot-Start schließt der Bot 60 s lang KE
|
||||
# auf H2 KALIBRIERT geprüft: AUC 0,71, predicted≈echt; `analyze_srwaves.py`).
|
||||
# Merkmale (in Positionsrichtung): [mom6, mom3, mit-Trend(0/1), Level-Distanz/ATR].
|
||||
# Standardisierung (mu/sd) + Gewichte (intercept + 4) aus `backtest_srclose_prob.py`.
|
||||
_PB_MU = (0.7482, 0.7036, 0.9112, 0.7072)
|
||||
_PB_SD = (1.1597, 0.9687, 0.2844, 0.6403)
|
||||
_PB_W = (-0.5853, 0.1192, 0.7820, 0.1204, -0.2994) # [b0, mom6, mom3, wt, dist]
|
||||
# P(break)-Modell — auf **M30-Level** trainiert (Umstellung 2026-07-30, gemessen
|
||||
# `backtest_level_tf.py`): M30-Pivots schlagen die alten M5-Pivots in BEIDEN Hälften
|
||||
# (AUC out-of-sample 0,721 vs 0,710 · R-Ertrag der Close-Regel ~2× · Level 1,95 statt
|
||||
# 0,61×ATR entfernt = die „S/R-Linien kleben aneinander"-Beobachtung des Users).
|
||||
# Ablauf: out-of-sample validiert (H1→H2), danach auf ALLEN 80k Bars nachtrainiert
|
||||
# (34.771 Touches, Basisrate 39,4 %, Kalibrierung trifft: 23/25 · 38/36 · 58/59 · 86/87).
|
||||
# ⚠ Die Merkmale bleiben M5-basiert (mom6/mom3 auf M5-Bars) — nur die LEVEL sind M30.
|
||||
# Alte M5-Werte (falls je zurück): MU (0.7482,0.7036,0.9112,0.7072) ·
|
||||
# SD (1.1597,0.9687,0.2844,0.6403) · W (-0.5853,0.1192,0.7820,0.1204,-0.2994).
|
||||
_PB_MU = (1.6565, 1.4523, 0.9465, 1.9133)
|
||||
_PB_SD = (2.5291, 2.4177, 0.2250, 2.1257)
|
||||
_PB_W = (-0.3264, 0.4719, 1.4330, 0.0943, -0.1885) # [b0, mom6, mom3, wt, dist]
|
||||
|
||||
def _p_break(mom6: float, mom3: float, wt: float, dist: float) -> float:
|
||||
"""P(Level-Durchbruch) ∈ (0,1) aus den 4 standardisierten Merkmalen."""
|
||||
@@ -1815,9 +1824,21 @@ class TradingEngine:
|
||||
}
|
||||
|
||||
def _draw_levels(self) -> dict:
|
||||
"""S/R-Linien fürs ZEICHNEN (PWA-Charts + MQL5-CSV): echte Kursstruktur =
|
||||
rohe M5-Pivots (`wave.pb_levels`, identisch zum P(break)-/Auto-Close-System),
|
||||
geclustert (~0,5×ATR). Zeichnet **nur die NÄCHSTE je Seite** (2 Linien total,
|
||||
"""S/R-Linien fürs ZEICHNEN (PWA-Charts + MQL5-CSV) UND für den
|
||||
P(break)-Auto-Close: echte Kursstruktur = rohe **M30**-Pivots
|
||||
(`wave.pb_levels30`), geclustert (~0,5×ATR).
|
||||
|
||||
⚠ **TF-Umstellung M5→M30 am 2026-07-30** (User: „die S/R-Linien liegen oft
|
||||
sehr nahe beieinander"), gemessen `backtest_level_tf.py` über 80k M5-Bars in
|
||||
2 Halbjahren — M30 gewinnt in BEIDEN: Level-Abstand **1,95 statt 0,61×ATR**
|
||||
(nur 28 % statt 71 % näher als 1×ATR), AUC out-of-sample 0,721 vs 0,710,
|
||||
R-Ertrag der Close-Regel **rund doppelt** (+12.424/+13.047 vs +6.698/+7.749).
|
||||
Das P(break)-Modell (`_PB_MU/_SD/_W`) wurde dafür auf M30-Leveln NACHTRAINIERT.
|
||||
Fallback auf die alten M5-Level (`pb_levels`), falls M30 noch nicht da ist.
|
||||
⚠ NICHT betroffen: das **Entry-Raum-Gate** nutzt weiter `pb_levels` (M5) —
|
||||
`entry_room_atr=0.6` ist darauf kalibriert.
|
||||
|
||||
Zeichnet **nur die NÄCHSTE je Seite** (2 Linien total,
|
||||
deckungsgleich mit den Dashboard-Werten `sr-res`/`sr-sup`) — sonst tauchten
|
||||
beim Kurslauf die weiter entfernten Linien laufend auf/ab (User-Feedback
|
||||
2026-07-14). Bugfix davor: Linien kamen aus `_sr_levels` (stale [zones]-Config
|
||||
@@ -1829,7 +1850,9 @@ class TradingEngine:
|
||||
return dc[1]
|
||||
try:
|
||||
ws = self.wave.snapshot() or {}
|
||||
lv = ws.get("pb_levels") or {}
|
||||
# M30-Level (gemessen besser); M5 nur als Fallback beim Kaltstart, bis
|
||||
# der erste M30-Turn-Fetch durch ist.
|
||||
lv = ws.get("pb_levels30") or ws.get("pb_levels") or {}
|
||||
atr = (ws.get("pb_feats") or {}).get("atr") or 0.2
|
||||
bid = (self.data.snapshot() or {}).get("bid")
|
||||
if not bid:
|
||||
|
||||
@@ -295,6 +295,14 @@ class WaveRecommender:
|
||||
turn_data[_lbl] = (_c, _atr(_h, _l, _c))
|
||||
if _lbl == "M5":
|
||||
self._m5_hl = (_h, _l)
|
||||
elif _lbl == "M30":
|
||||
# M30-H/L für die S/R-LEVEL (Chart + P(break)-Auto-Close).
|
||||
# Gemessen 2026-07-30 (`backtest_level_tf.py`): M30-Level
|
||||
# schlagen M5 in BEIDEN Hälften (R-Ertrag ~2×, Level 1,95
|
||||
# statt 0,61×ATR entfernt). Das M5-`_pb_levels` bleibt
|
||||
# daneben bestehen — es speist das Entry-Raum-Gate, das
|
||||
# auf M5 kalibriert ist (nicht ungemessen mitverändern!).
|
||||
self._m30_hl = (_h, _l)
|
||||
self._turn_due = time.time() + 15
|
||||
# ── Schneller M5-Squeeze-Fetch (Variante A): entkoppelt von der 15-s-
|
||||
# Ampel, kleiner 60-Bar-M5-Fetch ~alle 5 s → Ausbrüche werden ~3× schneller
|
||||
@@ -377,6 +385,19 @@ class WaveRecommender:
|
||||
self._pb_levels = {"ph": ph, "pl": pl}
|
||||
# (Squeeze läuft jetzt auf dem schnellen ~5-s-M5-Fetch oben,
|
||||
# nicht mehr hier auf dem 15-s-Ampel-Pfad — Variante A 2026-07-21.)
|
||||
# ── S/R-LEVEL auf M30 (gemessen 2026-07-30, `backtest_level_tf.py`) ──
|
||||
# Trainingsgleiches Fenster: der M5-Lookback von 300 Bars entspricht
|
||||
# 50 M30-Bars → nur die letzten 50+K betrachten, sonst kämen Level aus
|
||||
# einem viel längeren Zeitraum als im Training.
|
||||
_h30, _l30 = getattr(self, "_m30_hl", (None, None))
|
||||
if _h30 and len(_h30) >= 12:
|
||||
_K = 3; _WIN = 50 + _K
|
||||
_hs = _h30[-_WIN:]; _ls = _l30[-_WIN:]
|
||||
ph30 = [round(_hs[j], 3) for j in range(_K, len(_hs) - _K)
|
||||
if _hs[j] == max(_hs[j - _K:j + _K + 1])]
|
||||
pl30 = [round(_ls[j], 3) for j in range(_K, len(_ls) - _K)
|
||||
if _ls[j] == min(_ls[j - _K:j + _K + 1])]
|
||||
self._pb_levels30 = {"ph": ph30, "pl": pl30}
|
||||
# Multi-TF-Bounce: stärksten Status über M1/M5/M15/M30 wählen (H1 ist NUR
|
||||
# für die Ampel im turn_data, NICHT im gemessen-neutralen Bounce → skip).
|
||||
cands = []
|
||||
@@ -396,6 +417,9 @@ class WaveRecommender:
|
||||
snap["bounce"] = dict(self._last_bounce) # Multi-TF überschreibt Basis-TF
|
||||
snap["pb_feats"] = dict(self._pb_feats) if getattr(self, "_pb_feats", None) else None
|
||||
snap["pb_levels"] = dict(self._pb_levels) if getattr(self, "_pb_levels", None) else None
|
||||
# M30-Level = Quelle für Chart-Linien UND P(break)-Auto-Close (gemessen besser);
|
||||
# `pb_levels` (M5) bleibt für das Entry-Raum-Gate.
|
||||
snap["pb_levels30"] = dict(self._pb_levels30) if getattr(self, "_pb_levels30", None) else None
|
||||
snap["squeeze"] = dict(self._squeeze) if getattr(self, "_squeeze", None) else \
|
||||
{"state": None, "dir": None, "level": None, "box_atr": None}
|
||||
# ── Entry-Raum-Gate (gemessen `backtest_entryroom.py`, beide Hälften
|
||||
|
||||
Reference in New Issue
Block a user