#!/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())
