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

92 lines
4.2 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
"""Bringt es Edge, den REVERSAL-Einstieg (antizyklischer Bounce: überdehnt + Winkel
gedreht) zu VERWERFEN, wenn der H1-Trend klar DAGEGEN steht?
Teilt alle Reversal-Signale nach H1-Ausrichtung (mit/gegen/flach zur Signalrichtung)
und misst je Bucket die echte Exit-R (SL 2×ATR + Trailing-TP). Wenn 'gegen' klar
schlechter/negativ ist → Veto lohnt.
"""
import sys, bisect
import MetaTrader5 as mt5
from core.analysis import calc_trend_angle
from core.wave_rec import (_atr, _ema_last, _EMA_FAST, _EMA_SLOW, _N_BARS,
_ANGLE_LR, _ANGLE_DEAD, _HTF_DEADBAND)
_MAXH=240; _TRAILON=0.3; _TPTRAIL=0.5; _ATRMIN=0.12; _SL_ATR=2.0
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 simulate(entry,d,atr,sl,H,L,C,j0):
eff=sl; hw=entry; trail=False
end=min(j0+_MAXH,len(C)-1); exit_px=C[end]
for j in range(j0,end+1):
hi,lo=H[j],L[j]
if (lo<=eff) if d>0 else (hi>=eff): return (eff-entry)*d/atr
hw=max(hw,hi) if d>0 else min(hw,lo)
if (C[j]-entry)*d>=_TRAILON*atr: trail=True
if trail:
lock=hw-d*_TPTRAIL*atr
eff=max(eff,lock) if d>0 else min(eff,lock)
return (exit_px-entry)*d/atr
def stats(Rs):
if not Rs: return " -"
n=len(Rs); w=sum(1 for r in Rs if r>0)
g=sum(r for r in Rs if r>0); ls=-sum(r for r in Rs if r<0)
pf=g/ls if ls>0 else 99.9
return f"n={n:>4} Treffer={100*w/n:>3.0f}% Ø-R={sum(Rs)/n:+.3f} PF={pf:>4.2f} Worst={min(Rs):+.2f} ΣR={sum(Rs):+.0f}"
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
bars=mt5.copy_rates_from_pos(sym,mt5.TIMEFRAME_M5,0,n+_N_BARS+_MAXH+5)
h1=mt5.copy_rates_from_pos(sym,mt5.TIMEFRAME_H1,0,20000)
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]
T=[int(b["time"]) for b in bars]
ES=_ema_series(C,_EMA_SLOW); AT=_atr_series(H,L,C)
# H1-Regime
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
dd=hEf[idx]-hEs[idx]
return 0 if abs(dd)<_HTF_DEADBAND*hA[idx] else (1 if dd>0 else -1)
print("="*92)
print(f" Reversal-Einstieg nach H1-Ausrichtung — {sym} M5 Exit: SL {_SL_ATR}×ATR + Trailing-TP")
print(" Frage: sind 'gegen H1' laufende Reversals schlechter (→ Veto lohnt)?")
print("="*92)
for TH in (3.0, 3.5):
B={"mit":[], "gegen":[], "flach":[], "alle":[]}
for i in range(_N_BARS, len(C)-_MAXH-1):
atr=AT[i]
if not atr or atr<=0: continue
atr=max(atr,_ATRMIN); es=ES[i]
stretch=(C[i]-es)/atr
ang=calc_trend_angle(C[i-_ANGLE_LR-2:i],_ANGLE_LR); ad=ang-90.0
for d,cs,ca in ((1, stretch<=-TH, ad>=_ANGLE_DEAD),
(-1, stretch>=TH, ad<=-_ANGLE_DEAD)):
if not (cs and ca): continue # echtes Reversal-Signal (überdehnt+Winkel)
R=simulate(C[i],d,atr,C[i]-d*_SL_ATR*atr,H,L,C,i+1)
reg=h1_sign(T[i])
cls="mit" if reg==d else "gegen" if reg==-d else "flach"
B[cls].append(R); B["alle"].append(R)
print(f"\nReversal-Schwelle TH={TH}×ATR:")
for cls in ("alle","mit","gegen","flach"):
print(f" {cls:<6} {stats(B[cls])}")
veto=[r for c in ("mit","flach") for r in B[c]]
print(f" → mit Veto (gegen verworfen): {stats(veto)} [ohne Veto = 'alle']")
print("\n 'gegen' = H1-Trend GEGEN das Reversal (Gegen-Trend-Fade) — Kandidat fürs Veto")
if __name__=="__main__":
main()