#!/usr/bin/env python3 """Inter-Market-Kontext als Verdict-Kandidat (2026-07-19, User-Frage „weitere Indikatoren?"): die EINZIGE noch ungetestete Indikator-Klasse — Information von AUSSERHALB des WTI-Charts. Getestet werden zwei Hypothesen: (A) Brent-Bestätigung: Brent-Trend (EMA12/50 M5) bestätigt das WTI-Signal → besserer Edge; Brent DAGEGEN → schlechter. (Brent-WTI laufen ~parallel, Divergenz = möglicher Fehlausbruch.) (B) DXY-Gegenwind: USDX-Trend gegen die Trade-Richtung (Öl in USD: Dollar rauf = Öl-Gegenwind für LONG, Dollar runter = Gegenwind für SHORT). Methodik wie die 8 verworfenen Signal-Filter: ereignisbasierte EMA-Signale auf WTI M5 (frisches Kreuzen, Totband 0,15×ATR), sequentielle 1-Positions-Sim mit Live-Exit (SL 2,0×ATR + Trailing 1,5 + BE 1,3), Echtkosten = Bar-Spread/ATR. Jeder Trade wird nach dem Inter-Market-Zustand AM ENTRY gebucketed. 2 Halbjahre. Verdict-Regel (vorab festgelegt, gegen Parameter-Fishing): ein Gate/Konfidenz- Einbau kommt NUR in Frage, wenn der „dagegen"-Bucket in BEIDEN Hälften klar schlechter ist als „dafür" UND das über beide Feature-Varianten (EMA-Stand und Momentum N=12/36) robust ist. Maßstab der 6×-Lektion: Signal-Filter tragen kaum. """ import sys import MetaTrader5 as mt5 _MAXH = 288; _ATRMIN = 0.12; _COOL = 6; _DEAD = 0.15 def _ema_series(C, p): k = 2.0 / (p + 1); e = C[0]; out = [e] for x in C[1:]: e = e + k * (x - e); out.append(e) return out 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 _load(sym, n): mt5.symbol_select(sym, True) bars = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_M5, 0, n) if bars is None or len(bars) < 1000: print(f"FEHLER: keine Daten für {sym}"); sys.exit(1) point = mt5.symbol_info(sym).point T = [int(b["time"]) for b in bars] H = [float(b["high"]) for b in bars]; L = [float(b["low"]) for b in bars] C = [float(b["close"]) for b in bars] SP = [float(b["spread"])*point for b in bars] return T, H, L, C, SP def _dir_series(H, L, C): """EMA12/50-Richtung mit Totband: +1/-1/0 je Bar.""" e12 = _ema_series(C, 12); e50 = _ema_series(C, 50); A = _atr_series(H, L, C) out = [] for i in range(len(C)): a = A[i] or 0 if a <= 0: out.append(0); continue d = e12[i] - e50[i] out.append(1 if d > _DEAD*a else (-1 if d < -_DEAD*a else 0)) return out, A def st(Rs): if not Rs: return None n = len(Rs); w = sum(1 for x in Rs if x > 0); s = sum(Rs) up = sum(x for x in Rs if x > 0); dn = -sum(x for x in Rs if x < 0) return dict(n=n, wr=100*w/n, oR=s/n, pf=(up/dn if dn > 0 else 9.99), sum=s) def line(lbl, s): if not s: return f" {lbl:<26} —" return (f" {lbl:<26} n={s['n']:>4} WR={s['wr']:>3.0f}% ØR={s['oR']:+.3f} " f"PF={s['pf']:>4.2f} ΣR={s['sum']:>+6.0f}") def sim_trades(T, H, L, C, A, SP): """Sequentielle Sim: frisches EMA-Signal → Trade mit Live-Exit. Liefert Liste (entry_index, dir, netto_R).""" def cost(i, atr): return (SP[i] if SP[i] > 0 else 0.0225)/atr sig, _ = _dir_series(H, L, C) out = []; i = 60 while i < len(C) - 2: if sig[i] == 0 or sig[i] == sig[i-1]: i += 1; continue d = sig[i] atr = max(A[i] or 0, _ATRMIN) entry = C[i]; eff = entry - d*2.0*atr; hw = entry end = min(i+_MAXH, len(C)-1); exit_px = C[end]; exit_j = end for j in range(i+1, end+1): hj, lj = H[j], L[j] if (lj <= eff) if d > 0 else (hj >= eff): exit_px = eff; exit_j = j; break hw = max(hw, hj) if d > 0 else min(hw, lj) prof = (C[j]-entry)*d if prof >= 0.3*atr: cand = hw - d*1.5*atr if prof >= 1.3*atr: cand = max(cand, entry) if d > 0 else min(cand, entry) eff = max(eff, cand) if d > 0 else min(eff, cand) out.append((i, d, (exit_px-entry)*d/atr - cost(i, atr))) i = exit_j + _COOL return out def main(): n = int(sys.argv[1]) if len(sys.argv) > 1 else 80000 mt5.initialize() Tw, Hw, Lw, Cw, SPw = _load("SpotCrude", n+_MAXH+60) Tb, Hb, Lb, Cb, _ = _load("SpotBrent", n+_MAXH+60) Tx, Hx, Lx, Cx, _ = _load("USDX", n+_MAXH+60) mt5.shutdown() dir_b, Ab = _dir_series(Hb, Lb, Cb) dir_x, Ax = _dir_series(Hx, Lx, Cx) idx_b = {t: i for i, t in enumerate(Tb)} idx_x = {t: i for i, t in enumerate(Tx)} Aw = _atr_series(Hw, Lw, Cw) def look(idx, t): """Index zum WTI-Zeitstempel (bis 2 Gitterschritte zurück — Feed-Lücken).""" for dt_ in (0, 300, 600): j = idx.get(t - dt_) if j is not None: return j return None def mom_dir(C_, A_, j, N, thr=0.3, floor=None): if j is None or j < N: return None a = A_[j] or 0 a = max(a, floor) if floor else (a if a > 0 else None) if not a: return None m = (C_[j] - C_[j-N]) / a return 1 if m > thr else (-1 if m < -thr else 0) trades = sim_trades(Tw, Hw, Lw, Cw, Aw, SPw) print("="*88) print(f" Inter-Market-Kontext — WTI M5 ({len(trades)} Trades, seq. Sim, Live-Exit, Echtkosten)") print(f" Buckets nach Zustand AM ENTRY. Verdict nur bei Robustheit in BEIDEN Hälften.") print("="*88) mid_t = Tw[len(Tw)//2] feats = [] # (half, dir, R, brent_ema, dxy_ema, brent_m12, brent_m36, dxy_m12, dxy_m36) miss_b = miss_x = 0 for i, d, r in trades: t = Tw[i] jb = look(idx_b, t); jx = look(idx_x, t) if jb is None: miss_b += 1 if jx is None: miss_x += 1 h = 1 if t < mid_t else 2 feats.append((h, d, r, dir_b[jb] if jb is not None else None, dir_x[jx] if jx is not None else None, mom_dir(Cb, Ab, jb, 12, floor=0.12), mom_dir(Cb, Ab, jb, 36, floor=0.12), mom_dir(Cx, Ax, jx, 12), mom_dir(Cx, Ax, jx, 36))) if miss_b or miss_x: print(f" (ohne Inter-Market-Match: Brent {miss_b} · USDX {miss_x} — übersprungen)") def bucket3(val, d): """dafür / dagegen / neutral relativ zur Trade-Richtung.""" if val is None: return None if val == 0: return "neutral" return "dafür" if val == d else "dagegen" def bucket_dxy(val, d): """DXY: Dollar MIT der Trade-Richtung = Gegenwind (Öl invers zum Dollar).""" if val is None: return None if val == 0: return "neutral" return "Gegenwind" if val == d else "Rückenwind" sections = [ ("(A) Brent-Trend (EMA12/50)", 3, bucket3), ("(A2) Brent-Momentum 1h (N=12)", 5, bucket3), ("(A3) Brent-Momentum 3h (N=36)", 6, bucket3), ("(B) DXY-Trend (EMA12/50)", 4, bucket_dxy), ("(B2) DXY-Momentum 1h (N=12)", 7, bucket_dxy), ("(B3) DXY-Momentum 3h (N=36)", 8, bucket_dxy), ] order = ("dafür", "dagegen", "neutral", "Rückenwind", "Gegenwind") for title, col, bfn in sections: print(f"\n {title}:") for h in (1, 2): print(f" H{h}:") groups = {} for row in feats: if row[0] != h: continue b = bfn(row[col], row[1]) if b is None: continue groups.setdefault(b, []).append(row[2]) for name in order: if name in groups: print(line(name, st(groups[name]))) print("\n Verdict-Regel: Einbau NUR wenn 'dagegen'/'Gegenwind' in BEIDEN Hälften") print(" klar schlechter als 'dafür'/'Rückenwind' UND über die Varianten robust.") if __name__ == "__main__": main()