Files
AH-Oil-Trader/backtest_bounce_reliability.py
Axel HocksandClaude Opus 4.8 75d28827e8 Initial commit: Oil Trading Bot (MT5, WTI)
Headless FastAPI-Backend (server.py + core/engine.py) mit Mobile-PWA (web/),
Strategie-/Backtest-Suite und Doku. Secrets, DB, Logs und Laufzeit-State sind
via .gitignore ausgeschlossen; Config-Vorlage: oil_widget_config.ini.example.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 08:29:23 +02:00

77 lines
3.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Verlässlichkeit der Bounce-Anzeige ('aktiv'): Wenn _bounce_one(TF)=active feuert
(überdehnt + Winkel gedreht), wie oft bewegt sich der Kurs in Bounce-Richtung?
Aufgeschlüsselt nach TF (M1/M5/M15/M30) und nach H1-Regime (mit/gegen/flach zur
Bounce-Richtung). Nur ONSET (Übergang nicht-aktiv→aktiv) = unabhängige Ereignisse.
"""
import sys, bisect
import MetaTrader5 as mt5
from core.wave_rec import WaveRecommender, _atr, _ema_last, _EMA_FAST, _EMA_SLOW, _HTF_DEADBAND
K = 10 # Vorlauf in Bars der jeweiligen TF
def _ema_series(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 main():
n=int(sys.argv[1]) if len(sys.argv)>1 else 40000
mt5.initialize()
sym=None
for c in ("SpotCrude","USOIL","WTI","XTIUSD"):
if mt5.symbol_info(c): sym=c; break
# H1-Regime vorbereiten
h1=mt5.copy_rates_from_pos(sym,mt5.TIMEFRAME_H1,0,20000)
hT=[int(b["time"]) for b in h1]; hc=[float(b["close"]) for b in h1]
hh=[float(b["high"]) for b in h1]; hl=[float(b["low"]) for b in h1]
hEf=_ema_series(hc,_EMA_FAST); hEs=_ema_series(hc,_EMA_SLOW); hA=_atr_series(hh,hl,hc)
def h1_sign(ts):
idx=bisect.bisect_right(hT,ts)-1
if idx<_EMA_SLOW or hA[idx] is None or hA[idx]<=0: return 0
d=hEf[idx]-hEs[idx]
return 0 if abs(d)<_HTF_DEADBAND*hA[idx] else (1 if d>0 else -1)
tfs=[("M1",mt5.TIMEFRAME_M1),("M5",mt5.TIMEFRAME_M5),
("M15",mt5.TIMEFRAME_M15),("M30",mt5.TIMEFRAME_M30)]
print("="*86)
print(f" Bounce-Anzeige 'aktiv' — Verlässlichkeit je TF & H1-Regime ({sym}, Vorlauf {K} Bars)")
print(" Treffer = Kurs bewegt sich in Bounce-Richtung · Ø = Ø-Bewegung in ATR")
print("="*86)
for lbl,tf in tfs:
r=mt5.copy_rates_from_pos(sym,tf,0,n)
if r is None or len(r)<200: print(f"{lbl}: zu wenig Bars"); continue
T=[int(b["time"]) for b in r]; H=[float(b["high"]) for b in r]
L=[float(b["low"]) for b in r]; C=[float(b["close"]) for b in r]
buckets={"mit":[], "gegen":[], "flach":[], "alle":[]}
prev=False
for i in range(120, len(C)-K):
wc=C[i-119:i+1]; wh=H[i-119:i+1]; wl=L[i-119:i+1]
atr=_atr(wh,wl,wc)
if not atr or atr<=0: prev=False; continue
b=WaveRecommender._bounce_one(wc,atr)
active = bool(b and b["state"]=="active")
if active and not prev:
d=1 if b["dir"]=="LONG" else -1
fwd=(C[i+K]-C[i])*d/atr # in ATR, Bounce-Richtung
reg=h1_sign(T[i])
cls = "mit" if reg==d else "gegen" if reg==-d else "flach"
buckets[cls].append(fwd); buckets["alle"].append(fwd)
prev=active
print(f"\n{lbl} ({len(buckets['alle'])} aktive Bounce-Signale)")
for cls in ("alle","mit","gegen","flach"):
v=buckets[cls]
if not v: print(f" {cls:<6} -"); continue
hit=100*sum(1 for x in v if x>0)/len(v)
print(f" {cls:<6} n={len(v):>4} Treffer={hit:>3.0f}% Ø={sum(v)/len(v):+.2f}×ATR")
mt5.shutdown()
print("\n 'mit' = H1-Trend in Bounce-Richtung (Trend-Fortsetzung)")
print(" 'gegen' = H1-Trend GEGEN den Bounce (Gegen-Trend-Fade)")
print(" 'flach' = H1 neutral (reine Range/Mean-Reversion)")
if __name__=="__main__":
main()