#!/usr/bin/env python3 """Flatter-Analyse der Durchbruchs-Empfehlung (letzte 2 Tage, M1-Auflösung): Rekonstruiert P(break) minütlich WIE DIE ENGINE (M5-Serie inkl. LAUFENDER Kerze, deren Close = aktueller Kurs — genau das lässt mom3/mom6 im Bar springen) und misst in Level-Nähe (≤0,6×ATR): - P-Spanne innerhalb einzelner M5-Kerzen (wie stark springt P intra-Bar) - Schwellen-Kreuzungen (close↔laufen bei 60 %) je Annäherungs-Episode - Zerlegung: Sprünge durch mom3 (laufende Kerze) vs. Level-Wechsel """ import sys, math import MetaTrader5 as mt5 from core.engine import _p_break from core.wave_rec import _EMA_FAST, _EMA_SLOW _PIV_K=3; _LOOKBACK=300; _THR=0.60 def ema_last(vals,p): k=2.0/(p+1); e=vals[0] for x in vals[1:]: e=x*k+e*(1-k) return e def atr14(H,L,C): tr=[max(H[i]-L[i],abs(H[i]-C[i-1]),abs(L[i]-C[i-1])) for i in range(1,len(C))] return sum(tr[-14:])/14 def main(): days=int(sys.argv[1]) if len(sys.argv)>1 else 2 mt5.initialize(); sym='SpotCrude' m1=mt5.copy_rates_from_pos(sym,mt5.TIMEFRAME_M1,0,days*1440+50) m5=mt5.copy_rates_from_pos(sym,mt5.TIMEFRAME_M5,0,days*288+_LOOKBACK+60) mt5.shutdown() T5=[int(b["time"]) for b in m5] O5=[float(b["open"]) for b in m5]; H5=[float(b["high"]) for b in m5] L5=[float(b["low"]) for b in m5]; C5=[float(b["close"]) for b in m5] T1=[int(b["time"]) for b in m1]; C1=[float(b["close"]) for b in m1] import bisect ep=[] # aktive Annäherungs-Episode: Liste von (P, level, mom3) episodes=[] # abgeschlossene Episoden per_bar={} # (m5-index) -> [P...] für Intra-Bar-Spanne n_eval=0 for i1 in range(30, len(m1)): t=T1[i1]; px=C1[i1] j=bisect.bisect_right(T5,t)-1 # laufende/letzte M5-Kerze if j < _LOOKBACK+10: continue # M5-Serie wie die Engine sie sieht: abgeschlossene Bars + laufende Kerze # mit Close = aktuellem M1-Kurs closes=C5[j-_LOOKBACK:j]+[px] highs =H5[j-_LOOKBACK:j]+[max(px,O5[j])] lows =L5[j-_LOOKBACK:j]+[min(px,O5[j])] atr=max(atr14(highs,lows,closes),0.12) mom6=(closes[-1]-closes[-7])/atr mom3=(closes[-1]-closes[-4])/atr ediff=(ema_last(closes,_EMA_FAST)-ema_last(closes,_EMA_SLOW))/atr # Pivot-Level aus abgeschlossenen Bars ph=[H5[k] for k in range(j-_LOOKBACK+_PIV_K, j-_PIV_K) if H5[k]==max(H5[k-_PIV_K:k+_PIV_K+1])] # LONG-Sicht: nächster Widerstand über Kurs (analog gilt Short symmetrisch) cands=[p for p in ph if p>px] level=min(cands) if cands else None if level is None or (level-px) > 0.6*atr: if ep: episodes.append(ep); ep=[] continue wt=1.0 if ediff>0 else 0.0 dist=abs(level-px)/atr P=_p_break(mom6,mom3,wt,dist) ep.append((P,level,mom3)); n_eval+=1 per_bar.setdefault(j,[]).append(P) if ep: episodes.append(ep) print("="*84) print(f" P(break)-Flattern — letzte {days} Tage, {n_eval} Minuten in Level-Nähe (≤0,6×ATR)") print("="*84) # Intra-Bar-Spanne spans=[max(v)-min(v) for v in per_bar.values() if len(v)>=3] if spans: spans.sort() print(f"\nP-Spanne INNERHALB einer M5-Kerze (n={len(spans)} Kerzen):") print(f" Median {100*spans[len(spans)//2]:.0f} Pp · P90 {100*spans[int(len(spans)*0.9)]:.0f} Pp · Max {100*max(spans):.0f} Pp") # Episoden: Schwellen-Flips flips=[]; levsw=[] for e in episodes: if len(e)<3: continue f=sum(1 for k in range(1,len(e)) if (e[k][0]<_THR)!=(e[k-1][0]<_THR)) sw=sum(1 for k in range(1,len(e)) if abs(e[k][1]-e[k-1][1])>1e-9) flips.append(f); levsw.append(sw) if flips: flips.sort() print(f"\nAnnäherungs-Episoden: {len(flips)} · Ø-Dauer " f"{sum(len(e) for e in episodes if len(e)>=3)/len(flips):.0f} min") print(f" close↔laufen-Flips (60%-Schwelle) je Episode: " f"Median {flips[len(flips)//2]} · P90 {flips[int(len(flips)*0.9)]} · Max {max(flips)}") print(f" Episoden mit ≥2 Flips: {100*sum(1 for f in flips if f>=2)/len(flips):.0f}% " f"· Level-Wechsel je Episode Ø {sum(levsw)/len(levsw):.1f}") # mom3-Beitrag: P-Änderung je Minute vs mom3-Änderung dP=[]; dM=[] for e in episodes: for k in range(1,len(e)): if abs(e[k][1]-e[k-1][1])<1e-9: # gleiches Level → reiner Feature-Effekt dP.append(abs(e[k][0]-e[k-1][0])); dM.append(abs(e[k][2]-e[k-1][2])) if dP: print(f"\nMinuten-Sprünge bei GLEICHEM Level (n={len(dP)}):") print(f" Ø |ΔP|/min = {100*sum(dP)/len(dP):.1f} Pp · Ø |Δmom3|/min = {sum(dM)/len(dM):.2f}×ATR") big=[p for p,m in zip(dP,dM) if m>0.3] print(f" bei |Δmom3|>0,3: Ø |ΔP| = {100*sum(big)/max(1,len(big)):.1f} Pp " f"({100*len(big)/len(dP):.0f}% der Minuten) → mom3 (laufende Kerze) ist der Treiber") if __name__=="__main__": main()