#!/usr/bin/env python3
"""User-Frage (2026-07-24): „Passieren die meisten Durchbrüche schon ab P>40 %?"
Recall/Precision der P(break)-Schwelle an ECHTEN Level-Touches (Ziel-Seite = die
Population, auf die der S/R-Auto-Close wirkt), 2 Halbjahre, Modell = live
`core.engine._p_break`, Break/Bounce-Definition trainingsgleich (_BRK_ATR=0,5/
_BRK_W=12 M5-Bars).

Antwortet direkt:
  (A) Verteilung: von ALLEN echten Durchbrüchen — wie viel % hatten P≥X? (Recall)
  (B) Precision: von den Touches mit P≥X — wie viel % brachen wirklich durch?
  (C) Anteil der Touches, die bei Schwelle X als „läuft durch" gelten.
Schwellen X = 30/35/40/45/50/55/60 %. Bezug: sr_close_pbreak steuert genau diesen
Schnitt (close wenn P<X, laufen lassen wenn P≥X).
"""
import sys
import MetaTrader5 as mt5
from core.engine import _p_break
from core.wave_rec import (_EMA_FAST, _EMA_SLOW, _N_BARS, _ANGLE_LR, _ANGLE_DEAD,
                           _REVERSAL_STRETCH, _STRETCH_MAX)
from core.analysis import calc_trend_angle

_MAXH = 200; _ATRMIN = 0.12; _PIV_K = 3; _LOOKBACK = 300; _BRK_W = 12; _BRK_ATR = 0.5
_THRS = [0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60]


def _ema(v, p):
    k = 2.0/(p+1); o = []; e = v[0]
    for i, x in enumerate(v): e = x if i == 0 else x*k+e*(1-k); o.append(e)
    return o
def _atr_series(H, L, C, p=14):
    t = [0.0]
    for i in range(1, len(C)): t.append(max(H[i]-L[i], abs(H[i]-C[i-1]), abs(L[i]-C[i-1])))
    return [(sum(t[max(1, i-p+1):i+1])/max(1, len(t[max(1, i-p+1):i+1]))) if i else None
            for i in range(len(C))]


def collect(a, b, H, L, C, EF, ES, AT):
    """Ziel-Seiten-Touches (Wave-Signal-Richtung → nächstes Gegenlevel), je Touch
    (p_break, brk 1/0). Break/Bounce nach _BRK_ATR/_BRK_W, Timeout = Bounce."""
    out = []
    for i in range(max(a, _N_BARS, _LOOKBACK), min(b, len(C)-_MAXH-1)):
        atr = AT[i]
        if not atr or atr <= 0: continue
        atr = max(atr, _ATRMIN); es = ES[i]; ef = EF[i]
        stretch = (C[i]-es)/atr
        ang = calc_trend_angle(C[i-_ANGLE_LR-2:i], _ANGLE_LR); ad = ang-90.0
        d = 0
        if stretch <= -_REVERSAL_STRETCH and ad >= _ANGLE_DEAD: d = 1
        elif stretch >= _REVERSAL_STRETCH and ad <= -_ANGLE_DEAD: d = -1
        elif abs(stretch) < _STRETCH_MAX: d = 1 if ef > es else -1 if ef < es else 0
        if not d: continue
        entry = C[i]
        phis = [H[j] for j in range(i-_LOOKBACK+_PIV_K, i-_PIV_K) if H[j] == max(H[j-_PIV_K:j+_PIV_K+1])]
        plos = [L[j] for j in range(i-_LOOKBACK+_PIV_K, i-_PIV_K) if L[j] == min(L[j-_PIV_K:j+_PIV_K+1])]
        if d > 0:
            cands = [p for p in phis if p > entry+0.3*atr]; level = min(cands) if cands else None
        else:
            cands = [p for p in plos if p < entry-0.3*atr]; level = max(cands) if cands else None
        if level is None: continue
        jt = None
        for j in range(i+1, min(i+_MAXH, len(C)-_BRK_W)):
            if (H[j] >= level) if d > 0 else (L[j] <= level): jt = j; break
            if ((entry-L[j]) if d > 0 else (H[j]-entry)) >= 2.0*atr: break
        if jt is None: continue
        up = level+d*_BRK_ATR*atr; dn = level-d*_BRK_ATR*atr; brk = None
        for j in range(jt, min(jt+_BRK_W, len(H))):
            if (H[j] >= up) if d > 0 else (L[j] <= up): brk = 1; break
            if (L[j] <= dn) if d > 0 else (H[j] >= dn): brk = 0; break
        if brk is None: brk = 0
        a_j = max(AT[jt] or atr, _ATRMIN)
        mom6 = (C[jt]-C[max(0, jt-6)])*d/a_j; mom3 = (C[jt]-C[max(0, jt-3)])*d/a_j
        wt = 1.0 if (EF[jt]-ES[jt])*d > 0 else 0.0
        p = _p_break(mom6, mom3, wt, abs(level-entry)/a_j)
        out.append((p, brk))
    return out


def report(lbl, ev):
    n = len(ev); breaks = [p for p, b in ev if b == 1]; nb = len(breaks)
    base = 100*nb/n if n else 0
    print(f"\n{lbl}:  {n} Level-Touches · echte Durchbrüche {nb} ({base:.0f}% Basisrate)")
    print(f"  {'Schwelle':>8} | {'Recall (% der Brüche mit P≥X)':>30} | "
          f"{'Precision (Bruch-Rate bei P≥X)':>30} | {'% Touches „läuft durch\"':>22}")
    for X in _THRS:
        run = [(p, b) for p, b in ev if p >= X]
        recall = 100*sum(1 for p in breaks if p >= X)/nb if nb else 0
        prec = 100*sum(1 for p, b in run if b == 1)/len(run) if run else 0
        share = 100*len(run)/n if n else 0
        mark = "  ← 0,55 live" if abs(X-0.55) < 1e-9 else ("  ← 0,40 (Frage)" if abs(X-0.40) < 1e-9 else "")
        print(f"  {X:>7.0%} | {recall:>28.0f} % | {prec:>28.0f} % | {share:>20.0f} %{mark}")


def main():
    n = 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 = None
    for req in (n, 80000, 60000, 40000):
        bars = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_M5, 0, req)
        if bars is not None and len(bars) > 2000: break
    mt5.shutdown()
    H = [float(b["high"]) for b in bars]; L = [float(b["low"]) for b in bars]
    C = [float(b["close"]) for b in bars]
    EF = _ema(C, _EMA_FAST); ES = _ema(C, _EMA_SLOW); AT = _atr_series(H, L, C)
    N = len(C); mid = N//2
    print("="*100)
    print(f"  P(break)-Schwelle — Recall/Precision an echten Level-Touches — {sym} M5 ({N} Bars)")
    print(f"  Frage: passieren die meisten Durchbrüche schon ab P>40 %? (Recall bei X=0,40)")
    print("="*100)
    for lbl, lo, hi in (("H1 (alt)", _N_BARS, mid), ("H2 (neu)", mid, N)):
        report(lbl, collect(lo, hi, H, L, C, EF, ES, AT))
    print("\n  Lesart: Recall = 'so viel % ALLER echten Durchbrüche liegen ≥ Schwelle'")
    print("  (hoch = kaum Runner verpasst). Precision = 'so viel % der ≥-Schwelle-Touches")
    print("  brechen wirklich durch'. sr_close_pbreak = genau dieser Schnitt.")


if __name__ == "__main__":
    main()
