Files
AH-Oil-Trader/backtest_revlock.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

85 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
"""Misst den Edge von regulären Trendsignalen, die kurz NACH einem gegenläufigen
Reversal kommen (z.B. SHORT wenige Bars nach REV-LONG = in den Bounce shorten).
Sind die schwach/negativ → ein Reversal-Lockout (Gegenrichtung N Bars sperren)
würde genau diese Verlierer (wie der 26-€-Trade) verhindern.
"""
import sys
import MetaTrader5 as mt5
from core.analysis import calc_trend_angle
from core.wave_rec import (WaveRecommender, _atr, _ema_last, _EMA_FAST, _EMA_SLOW,
_N_BARS, _HTF_DEADBAND, _ANGLE_LR)
class _TU:
def snapshot(self): return {"intervals": {}}
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 _rep(name,r):
if not r: print(f" {name:<26} -"); return
n=len(r); w=sum(1 for x in r if x>0)
print(f" {name:<26} n={n:>4} Treffer={100*w/n:>3.0f}% Oe-Edge={sum(r)/n:+.4f}")
def main():
n=int(sys.argv[1]) if len(sys.argv)>1 else 12000
K=10
mt5.initialize()
sym=None
for c in ("SpotCrude","USOIL","WTI","XTIUSD"):
if mt5.symbol_info(c): sym=c; break
bars=mt5.copy_rates_from_pos(sym,mt5.TIMEFRAME_M5,0,n+_N_BARS+K+5)
m30=mt5.copy_rates_from_pos(sym,mt5.TIMEFRAME_M30,0,n//6+400)
mt5.shutdown()
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]
mT=[int(b["time"]) for b in m30]; mc=[float(b["close"]) for b in m30]
mh=[float(b["high"]) for b in m30]; ml=[float(b["low"]) for b in m30]
mEf=_ema_series(mc,_EMA_FAST); mEs=_ema_series(mc,_EMA_SLOW); mA=_atr_series(mh,ml,mc)
def m30s(ts):
lo,hi,idx=0,len(mT)-1,-1
while lo<=hi:
md=(lo+hi)//2
if mT[md]<=ts: idx=md; lo=md+1
else: hi=md-1
if idx<_EMA_SLOW or mA[idx] is None or mA[idx]<=0: return 0
d=mEf[idx]-mEs[idx]
return 0 if abs(d)<_HTF_DEADBAND*mA[idx] else (1 if d>0 else -1)
w=WaveRecommender(_TU(), mt5.TIMEFRAME_M5)
print("="*64); print(f" Reversal-Lockout-Test — {sym} M5+M30 Vorlauf={K}"); print("="*64)
for N in (6,12,24):
last_rev_bar=-999; last_rev_dir=0
counter=[]; normal=[]; rev=[]
for i in range(_N_BARS, len(C)-K):
wc=C[i-_N_BARS:i]; wh=H[i-_N_BARS:i]; wl=L[i-_N_BARS:i]
atr=_atr(wh,wl,wc)
if not atr or atr<=0: continue
ef=_ema_last(wc,_EMA_FAST); es=_ema_last(wc,_EMA_SLOW)
ang=calc_trend_angle(C[i-_ANGLE_LR-2:i],_ANGLE_LR)
rec,_=w._build(ef,es,C[i-1],atr,"M5",5,htf_trend=m30s(T[i]),angle=ang)
sig=rec["signal"]
if sig=="WARTEN": continue
d=1 if sig=="LONG" else -1
r=(C[i+K]-C[i])*d
is_rev="REV" in (rec.get("setup") or "")
if is_rev:
rev.append(r); last_rev_bar=i; last_rev_dir=d
else:
if (i-last_rev_bar)<=N and last_rev_dir!=0 and last_rev_dir!=d:
counter.append(r) # Gegenrichtung kurz nach Reversal
else:
normal.append(r)
print(f"\nLockout-Fenster N={N} Bars:")
_rep("Reversal selbst", rev)
_rep(f"Gegen-Signal <= {N} nach Rev", counter)
_rep("Normale Signale (Rest)", normal)
if __name__=="__main__":
main()