From 81c4c195da66dbf1456a70df19982aa7dd995682 Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Tue, 4 Aug 2026 11:49:10 +0200 Subject: [PATCH] =?UTF-8?q?Auto-Signal=20nach=20dem=20Reversal-Abschalten?= =?UTF-8?q?=20gemessen=20=E2=80=94=20immer=20noch=20nicht=20tragfaehig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Frage: liefert der Auto-Signal-Entry mit den heutigen Aenderungen positive Ergebnisse? Antwort: nein, aber deutlich naeher dran. LIVE-Konfiguration (Reversal aus, Winkel modelliert), conf>=75: H1 OR -0,047 PF 0,94 n=678 H2 OR +0,120 PF 1,17 n=710 H1 bleibt negativ -> 2-Stichproben-Regel weiterhin NICHT erfuellt. Kein Konfidenz-Schwellwert rettet das (55: -0,103 - 65: -0,111 - 75: -0,047). Das Reversal-Abschalten wirkt aber konsistent. Delta OR: 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 Haelften um ~+0,09; H1 geht von -0,145 auf -0,047, PF von 0,82 auf 0,94. Erste Bestaetigung der gestrigen Entscheidung auf dem GESAMTEN Signal statt nur dem isolierten Trigger. DABEI GEFUNDEN - die Messung vom 30.07. war strukturell unvollstaendig: backtest_auto_signal.py ruft _build OHNE angle= auf. Der Default 90,0 macht 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. Der Winkelterm allein hebt H1 bei conf 75 von -0,123 auf -0,047. Deployment-Drift Fall 5. Die Originaldatei bleibt unveraendert (ihre Zahlen sind dokumentiert); die live-treue Variante steht als backtest_auto_signal_norev.py daneben und sollte kuenftig die Referenz sein. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 25 ++++++ backtest_auto_signal_norev.py | 159 ++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 backtest_auto_signal_norev.py diff --git a/CLAUDE.md b/CLAUDE.md index 340a779..2460f4c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1106,6 +1106,30 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche: ü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. - **⚠⚠ 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 @@ -2552,6 +2576,7 @@ Bedingungen BETRIEBEN als VALIDIERT. An EINEM Tag wurden drei Fälle gefunden: | 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** | +| 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** | Keiner wäre durch MEHR Backtesting gefunden worden — die Backtests waren korrekt. **Ursachen (strukturell):** (a) Backtests sind NACHBAUTEN, keine Nutzer des Live-Codes diff --git a/backtest_auto_signal_norev.py b/backtest_auto_signal_norev.py new file mode 100644 index 0000000..c868a8c --- /dev/null +++ b/backtest_auto_signal_norev.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +"""Auto-Signal-Entry: trägt er OHNE das Reversal-Setup? (2026-08-04) + +Anlass: Am 2026-08-04 wurde das antizyklische Reversal-Setup abgeschaltet +(`reversal_enabled=false`, gemessen beidhälftig negativ). Frage des Users: +liefert der Auto-Signal-Entry mit dieser Änderung positive Ergebnisse? + +Die letzte Messung (`backtest_auto_signal.py`, 2026-07-30) fand ALLE 8 Varianten +durchgefallen — H1 durchgehend negativ (ØR −0,076…−0,182), H2 positiv +(+0,112…+0,167) = Regime-Kippen. Damals war der Reversal-Zweig aktiv, denn +`_build` enthielt ihn bedingungslos. + +Dieses Skript importiert `scan`/`_rep` aus `backtest_auto_signal` — also exakt +dieselbe Maschinerie (echte `_build`-Logik, HTF aus M5 aggregiert, sequentielle +1-Positions-Sim, Live-Exit, Echtkosten) — und variiert NUR +`WaveRecommender._reversal_enabled`. Das isoliert den Effekt der Abschaltung. + +⚠ Das bestehende Skript bleibt unverändert: sein Default (Reversal AN) reproduziert +weiterhin die Messung vom 30.07. + +Geprüft werden die LIVE-Parameter (`auto_signal_min_conf=75`, +`auto_signal_skip_night=true`, `adverse_15min_atr=0` → keine 15-Min-Regel) plus +die Konfidenz-Nachbarn 55/65, damit ein Treffer nicht an einer Schwelle klebt. + +Aufruf: python backtest_auto_signal_norev.py [bars] +""" +import sys + +import MetaTrader5 as mt5 + +from datetime import datetime, timezone + +from backtest_auto_signal import (_ATRMIN, _MAXH, _NIGHT, _TIMESTOP, _NeutralTU, + _atr_series, _ema_series, _htf_signs, _HTF_N, + _sim, scan) +from core.analysis import calc_trend_angle +from core.wave_rec import _ANGLE_LR, _EMA_FAST, _EMA_SLOW, WaveRecommender + + +def scan_angle(bars, H, L, C, SP, A, HTF, H1, EF, ES, AD, w, + lo_i, hi_i, min_conf, skip_night): + """Wie `scan`, aber MIT dem Regressions-Winkel. + + ⚠⚠ Der Grund für diese Kopie: `backtest_auto_signal.scan` ruft `_build` OHNE + `angle=` auf → der Default 90,0 macht `ad = 0`. Damit kann dort weder der + Reversal-Zweig feuern (braucht |ad| > _ANGLE_DEAD) noch der Winkel-Konfidenz- + term (±5/−10) greifen. Das Skript behauptet in seiner Docstring, die „echte + `_build`-Logik inkl. Reversal-Zweig" zu testen — strukturell tut es das nicht. + Vierter Fall derselben Klasse: gemessen unter „kein Winkel", betrieben unter + „Winkel steuert einen Signalzweig UND ±15 Konfidenzpunkte". + Die Original-Datei bleibt unangetastet (ihre Zahlen sind dokumentiert); hier + steht die live-treue Variante daneben. + """ + Rs = [] + i = max(lo_i, _EMA_SLOW + 20) + hi_i = min(hi_i, len(C) - _MAXH - 2) + while i < hi_i: + atr = A[i] + if not atr or atr < _ATRMIN: + i += 1; continue + if skip_night: + hb = (datetime.fromtimestamp(int(bars[i]["time"]), + timezone.utc).hour - 1) % 24 + if hb in _NIGHT: + i += 1; continue + rec, _snap = w._build(EF[i], ES[i], C[i], atr, "M5", 5, + htf_trend=HTF[i], h1_trend=H1[i], angle=AD[i]) + sig = rec.get("signal") + if sig not in ("LONG", "SHORT") or (rec.get("conf_pct") or 0) < min_conf: + i += 1; continue + d = 1 if sig == "LONG" else -1 + cost = (SP[i] if SP[i] > 0 else 0.0225) / atr + Rs.append(_sim(C[i], d, atr, H, L, C, i + 1, None) - cost) + i += _TIMESTOP # identischer Vorlauf wie im Original (kein Overlap) + return Rs + + +def stats(Rs): + if not Rs: + return None + n = len(Rs); w = sum(1 for x in Rs if x > 0); s = sum(Rs) + up = sum(x for x in Rs if x > 0); dn = -sum(x for x in Rs if x < 0) + return dict(n=n, wr=100 * w / n, oR=s / n, + pf=(up / dn if dn > 0 else 9.99), sum=s) + + +def fmt(s): + if not s: + return "— (keine Trades)" + return (f"n={s['n']:>4} WR={s['wr']:>3.0f}% ØR={s['oR']:+.3f} " + f"PF={s['pf']:>4.2f} ΣR={s['sum']:>+6.0f}") + + +def main(): + n = int(sys.argv[1]) if len(sys.argv) > 1 else 80000 + if not mt5.initialize(): + print(f"MT5-Init fehlgeschlagen: {mt5.last_error()}"); sys.exit(1) + sym = next((c for c in ("SpotCrude", "USOIL", "WTI", "XTIUSD") + if mt5.symbol_info(c)), None) + bars = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_M5, 0, n + _MAXH + 60) + point = mt5.symbol_info(sym).point + mt5.shutdown() + if bars is None or len(bars) < 5000: + print("Zu wenige Bars."); sys.exit(1) + + H = [float(b["high"]) for b in bars] + L = [float(b["low"]) for b in bars] + C = [float(b["close"]) for b in bars] + SP = [float(b["spread"]) * point for b in bars] + A = _atr_series(H, L, C) + HTF = _htf_signs(H, L, C, _HTF_N) + H1 = _htf_signs(H, L, C, _HTF_N * 2) + EF = _ema_series(C, _EMA_FAST) + ES = _ema_series(C, _EMA_SLOW) + N = len(C); mid = N // 2 + + w_on = WaveRecommender(_NeutralTU(), mt5.TIMEFRAME_M5) + w_on.set_reversal_enabled(True) # Stand bis 03.08. (= Messung 30.07.) + w_off = WaveRecommender(_NeutralTU(), mt5.TIMEFRAME_M5) + w_off.set_reversal_enabled(False) # Live-Stand seit 04.08. + + print("=" * 96) + print(f" AUTO-SIGNAL-ENTRY mit/ohne Reversal — {sym} M5 ({N} Bars)") + print(" Identische Maschinerie aus backtest_auto_signal.py; variiert wird NUR") + print(" `_reversal_enabled`. Nacht-Sperre AN, keine 15-Min-Regel (live = 0).") + print(" Referenz (validierter Auto-Entry Squeeze): ØR +0,14…+0,23 · PF>1") + print("=" * 96) + + # Winkel je Bar — genau wie im Live-Pfad aufgerufen + AD = [90.0] * len(C) + for i in range(_ANGLE_LR + 2, len(C)): + AD[i] = calc_trend_angle(C[i - (_ANGLE_LR + 1):i + 1], _ANGLE_LR) + + for mc in (55, 65, 75): + live = " ← LIVE-Schwelle" if mc == 75 else "" + print(f"\n Konfidenz ≥ {mc}{live}") + for lbl, lo, hi in (("H1 (alt)", 0, mid), ("H2 (neu)", mid, N)): + r_alt = stats(scan(bars, H, L, C, SP, A, HTF, H1, EF, ES, + w_on, lo, hi, mc, True, None)) + r_rev = stats(scan_angle(bars, H, L, C, SP, A, HTF, H1, EF, ES, AD, + w_on, lo, hi, mc, True)) + r_live = stats(scan_angle(bars, H, L, C, SP, A, HTF, H1, EF, ES, AD, + w_off, lo, hi, mc, True)) + print(f" {lbl}") + print(f" ALT-Messung (ohne Winkel, Rev. wirkungslos) : {fmt(r_alt)}") + print(f" mit Winkel + Reversal AN (Stand bis 03.08.): {fmt(r_rev)}") + print(f" mit Winkel + Reversal AUS (LIVE seit 04.08.): {fmt(r_live)}") + if r_rev and r_live: + print(f" Δ durch das Abschalten: ØR " + f"{r_live['oR'] - r_rev['oR']:+.3f} ΣR " + f"{r_live['sum'] - r_rev['sum']:+.0f}") + print() + print(" Urteil: Einbau nur, wenn ØR>0 UND PF>1 in BEIDEN Hälften — und die") + print(" Konfidenz-Nachbarn mithalten (sonst klebt der Treffer an einer Schwelle).") + print() + + +if __name__ == "__main__": + main()