diff --git a/analyze_pbreak_threshold.py b/analyze_pbreak_threshold.py new file mode 100644 index 0000000..11e54bf --- /dev/null +++ b/analyze_pbreak_threshold.py @@ -0,0 +1,118 @@ +#!/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= _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()