#!/usr/bin/env python3 """Haelt der SIG-Pending-Pfad auch mit realistischem Fill? — die offene Zahl. ANLASS (2026-08-06): `signal_pending_entry` laeuft seit dem 05.08. mit ECHTEM Geld. Die Erwartung dahinter (ØR +0,39/+0,36) stammt aus `backtest_auto_signal_v3.py` und unterstellt: Einstieg zum Level-Preis, aber **nur dort, wo der Bar-CLOSE das Level gebrochen hat**. Eine liegende Stop-Order weiss das nicht — sie fuellt bei der ERSTEN BERUEHRUNG, auch bei jeder, die wieder zurueckfaellt. Wie teuer dieser Unterschied ist, wurde heute zweimal gemessen: Momentum +0,40 → −0,06 (0,45 R Aufschlag) — Level WANDERT Squeeze +0,46 → +0,13 (0,32 R Aufschlag) — Level STEHT Der Squeeze haelt, das Momentum nicht. **Und `_pend` gehoert zur wandernden Sorte:** der Anker wird bei jeder k×ATR-Gegenbewegung, nach Timeout und bei Richtungswechsel neu gesetzt. Es ist also mit dem schlechteren Fall zu rechnen. Benutzt dieselbe `lauf()` wie v3, nur mit `fill="touch"` — nichts nachgebaut, damit die Zahlen vergleichbar bleiben. Der Default `fill="close"` reproduziert v3 bitgenau (verifiziert). VORAB FIXIERTE ENTSCHEIDUNGSREGEL: `signal_pending_entry` bleibt nur gerechtfertigt, wenn der Pfad mit `fill="touch"` in BEIDEN Haelften ØR > 0 UND PF > 1 haelt UND besser ist als der Markt-Einstieg. Sonst gehoert er abgeschaltet. Aufruf: python backtest_signal_touchfill.py [bars] """ import sys import MetaTrader5 as mt5 import backtest_auto_signal_v3 as V from core.wave_rec import WaveRecommender, calc_trend_angle def main(): n_bars = int(sys.argv[1]) if len(sys.argv) > 1 else 80000 mt5.initialize() 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_bars) point = mt5.symbol_info(sym).point mt5.shutdown() # ⚠ Aufbau 1:1 wie in v3.main() — aber ueber die IMPORTIERTEN Helfer, nicht # ueber Kopien. Waere hier ein eigener EMA/ATR drin, waere der Vergleich # zwischen den beiden Fill-Annahmen nicht mehr sauber. H = [float(b["high"]) for b in bars]; L = [float(b["low"]) for b in bars] C = [float(b["close"]) for b in bars]; T = [int(b["time"]) for b in bars] SP = [float(b["spread"]) * point for b in bars] EF = V._ema_series(C, V._EMA_FAST); ES = V._ema_series(C, V._EMA_SLOW) A = V._atr_series(H, L, C) AD = [0.0] * len(C) for i in range(V._N_BARS, len(C)): AD[i] = calc_trend_angle(C[i - V._N_BARS:i + 1], V._ANGLE_LR) - 90.0 if abs(AD[i]) < V._ANGLE_DEAD: AD[i] = 0.0 HTF = [V._htf_sign(C, i, 6, A[i] or 0.1) for i in range(len(C))] H1S = [V._htf_sign(C, i, 12, A[i] or 0.1) for i in range(len(C))] PH = [j for j in range(V._PIV_K, len(C) - V._PIV_K) if H[j] == max(H[j - V._PIV_K:j + V._PIV_K + 1])] PL = [j for j in range(V._PIV_K, len(C) - V._PIV_K) if L[j] == min(L[j - V._PIV_K:j + V._PIV_K + 1])] w = WaveRecommender(lambda: None) w.set_reversal_enabled(False) w.set_entry_room(0.6) # ⚠ ohne das ist das Raum-Gate ein No-op w.set_dead_hours("") mid = len(C) // 2 print("=" * 104) print(f" SIG-PENDING — Fill bei BAR-CLOSE gegen Fill bei BERUEHRUNG " f"({sym}, {len(C)} M5-Bars)") print("=" * 104) print(f" {V._berlin(T[0]):%d.%m.%Y} bis {V._berlin(T[-1]):%d.%m.%Y} · " f"voller Live-Gate-Stack · kanonischer Exit · Echtkosten · EIN Slot") print() def paar(mc, am_level, fill): r1, _ = V.lauf(w, H, L, C, T, A, SP, EF, ES, AD, HTF, H1S, PH, PL, 0, mid, mc, V._NACHT_LIVE, am_level, 0.0, fill) r2, _ = V.lauf(w, H, L, C, T, A, SP, EF, ES, AD, HTF, H1S, PH, PL, mid, len(C), mc, V._NACHT_LIVE, am_level, 0.0, fill) return V.kenn(r1), V.kenn(r2) print(f" {'Variante':<38}{'H1 n WR OeR PF SigmaR':>33}" f" |{'H2 n WR OeR PF SigmaR':>33}") print(" " + "-" * 102) print(" A) MARKT am Bar-Close (der Ist-Zustand vor dem 05.08.)") for mc in (55, 65, 75): V.zeile(f" Konf>={mc}", *paar(mc, False, "close")) print(" " + "-" * 102) print(" B) am Level, Fill nur bei CLOSE-Bruch (die Zahl, auf der live steht)") for mc in (55, 65, 75): V.zeile(f" Konf>={mc}", *paar(mc, True, "close")) print(" " + "-" * 102) print(" C) am Level, Fill bei BERUEHRUNG (was live wirklich passiert)") haelt = [] for mc in (55, 65, 75): k1, k2 = paar(mc, True, "touch") if V.zeile(f" Konf>={mc}", k1, k2): haelt.append(mc) print() print("=" * 104) if haelt: print(f" → Variante C haelt bei Konf {haelt} — `signal_pending_entry` ist gedeckt.") else: print(" → Variante C haelt in KEINER Konfidenz-Stufe.") print(" Damit steht `signal_pending_entry=true` auf einer Zahl, die die") print(" realistische Fill-Annahme nicht ueberlebt → abschalten.") print(" ⚠ B gegen C gelesen = der Look-ahead-Aufschlag (Squeeze 0,32 R,") print(" Momentum 0,45 R).") return 0 if __name__ == "__main__": sys.exit(main())