Files
AH-Oil-Trader/backtest_reversal.py
T
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

72 lines
3.0 KiB
Python

#!/usr/bin/env python3
"""Misst einen Rebound/Reversal-Einstieg für ÜBERDEHNTE Lagen (jetzt = WARTEN):
Kurs weit von der EMA weg (|stretch| >= _STRETCH_MAX) UND Regressions-Winkel
hat in die Gegenrichtung gedreht → antizyklischer Einstieg in Winkel-Richtung.
überdehnt UNTER EMA + Winkel auf → LONG
überdehnt ÜBER EMA + Winkel ab → SHORT
Kontrolle: gleiche Überdehnung OHNE Winkel-Bestätigung (reines Fade).
"""
from __future__ import annotations
import sys
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,
_STRETCH_MAX)
_LR = 14
def _rep(name, r):
if not r: print(f" {name:<28} -"); return
n=len(r); w=sum(1 for x in r if x>0)
print(f" {name:<28} n={n:>4} Treffer={100*w/n:>3.0f}% Oe-Edge={sum(r)/n:+.4f} Summe={sum(r):+.1f}")
def main():
n=int(sys.argv[1]) if len(sys.argv)>1 else 8000
dead=2.0
if not mt5.initialize(): print("init",mt5.last_error()); sys.exit(1)
sym=None
for c in ("SpotCrude","USOIL","WTI","XTIUSD"):
if mt5.symbol_info(c): sym=c; break
sym=sym or "SpotCrude"
KS=[5,10,20]
bars=mt5.copy_rates_from_pos(sym,mt5.TIMEFRAME_M5,0,n+_N_BARS+max(KS)+5)
mt5.shutdown()
if bars is None: print("Bars fehlen"); sys.exit(1)
H=[float(b["high"]) for b in bars]; L=[float(b["low"]) for b in bars]; C=[float(b["close"]) for b in bars]
print("="*72)
print(f" Reversal-Test (überdehnt + Winkel gedreht) — {sym} M5 _STRETCH_MAX={_STRETCH_MAX}")
print("="*72)
for K in KS:
rev=[]; rev_long=[]; rev_short=[]; fade_noang=[]
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)
stretch=(C[i-1]-es)/atr
if abs(stretch) < _STRETCH_MAX: # nur ÜBERDEHNTE Lagen (sonst normal)
continue
ang=calc_trend_angle(C[i-_LR-2:i], _LR)
down_over = stretch <= -_STRETCH_MAX # weit unter EMA
up_over = stretch >= _STRETCH_MAX # weit über EMA
d=0
if down_over and ang >= 90+dead: d=1 # Rebound long
elif up_over and ang <= 90-dead: d=-1 # Rebound short
if d!=0:
r=(C[i+K]-C[i])*d
rev.append(r); (rev_long if d>0 else rev_short).append(r)
else:
# Überdehnt, aber Winkel NICHT gedreht → reines Fade gegen Überdehnung
df = 1 if down_over else -1
fade_noang.append((C[i+K]-C[i])*df)
print(f"\nVorlauf {K} Bars:")
_rep("Reversal (Winkel best.)", rev)
_rep(" davon LONG", rev_long); _rep(" davon SHORT", rev_short)
_rep("Fade OHNE Winkel (Kontrolle)", fade_noang)
print("\nHinweis: überdehnte Lagen sind selten → kleine n, mit Vorsicht lesen.")
if __name__=="__main__":
main()