Files
AH-Oil-Trader/backtest_auto_signal_norev.py
Axel HocksandClaude Opus 5 81c4c195da Auto-Signal nach dem Reversal-Abschalten gemessen — immer noch nicht tragfaehig
Frage: liefert der Auto-Signal-Entry mit den heutigen Aenderungen positive
Ergebnisse? Antwort: nein, aber deutlich naeher dran.

LIVE-Konfiguration (Reversal aus, Winkel modelliert), conf>=75:
  H1  OR -0,047  PF 0,94  n=678
  H2  OR +0,120  PF 1,17  n=710
H1 bleibt negativ -> 2-Stichproben-Regel weiterhin NICHT erfuellt. Kein
Konfidenz-Schwellwert rettet das (55: -0,103 - 65: -0,111 - 75: -0,047).

Das Reversal-Abschalten wirkt aber konsistent. Delta OR:
  conf 55  +0,015 / +0,099
  conf 65  +0,001 / +0,047
  conf 75  +0,099 / +0,087
An der Live-Schwelle verbessern sich BEIDE Haelften um ~+0,09; H1 geht von
-0,145 auf -0,047, PF von 0,82 auf 0,94. Erste Bestaetigung der gestrigen
Entscheidung auf dem GESAMTEN Signal statt nur dem isolierten Trigger.

DABEI GEFUNDEN - die Messung vom 30.07. war strukturell unvollstaendig:
backtest_auto_signal.py ruft _build OHNE angle= auf. Der Default 90,0 macht
ad = 0, damit konnte dort weder der Reversal-Zweig feuern noch der
Winkel-Konfidenzterm (+/-15 Punkte) greifen - obwohl die Docstring "echte
_build-Logik inkl. Reversal-Zweig" behauptet. Der Winkelterm allein hebt H1 bei
conf 75 von -0,123 auf -0,047. Deployment-Drift Fall 5.

Die Originaldatei bleibt unveraendert (ihre Zahlen sind dokumentiert); die
live-treue Variante steht als backtest_auto_signal_norev.py daneben und sollte
kuenftig die Referenz sein.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 11:49:10 +02:00

160 lines
7.0 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
"""Auto-Signal-Entry: trägt er OHNE das Reversal-Setup? (2026-08-04)
Anlass: Am 2026-08-04 wurde das antizyklische Reversal-Setup abgeschaltet
(`reversal_enabled=false`, gemessen beidhälftig negativ). Frage des Users:
liefert der Auto-Signal-Entry mit dieser Änderung positive Ergebnisse?
Die letzte Messung (`backtest_auto_signal.py`, 2026-07-30) fand ALLE 8 Varianten
durchgefallen — H1 durchgehend negativ (ØR 0,076…−0,182), H2 positiv
(+0,112…+0,167) = Regime-Kippen. Damals war der Reversal-Zweig aktiv, denn
`_build` enthielt ihn bedingungslos.
Dieses Skript importiert `scan`/`_rep` aus `backtest_auto_signal` — also exakt
dieselbe Maschinerie (echte `_build`-Logik, HTF aus M5 aggregiert, sequentielle
1-Positions-Sim, Live-Exit, Echtkosten) — und variiert NUR
`WaveRecommender._reversal_enabled`. Das isoliert den Effekt der Abschaltung.
⚠ Das bestehende Skript bleibt unverändert: sein Default (Reversal AN) reproduziert
weiterhin die Messung vom 30.07.
Geprüft werden die LIVE-Parameter (`auto_signal_min_conf=75`,
`auto_signal_skip_night=true`, `adverse_15min_atr=0` → keine 15-Min-Regel) plus
die Konfidenz-Nachbarn 55/65, damit ein Treffer nicht an einer Schwelle klebt.
Aufruf: python backtest_auto_signal_norev.py [bars]
"""
import sys
import MetaTrader5 as mt5
from datetime import datetime, timezone
from backtest_auto_signal import (_ATRMIN, _MAXH, _NIGHT, _TIMESTOP, _NeutralTU,
_atr_series, _ema_series, _htf_signs, _HTF_N,
_sim, scan)
from core.analysis import calc_trend_angle
from core.wave_rec import _ANGLE_LR, _EMA_FAST, _EMA_SLOW, WaveRecommender
def scan_angle(bars, H, L, C, SP, A, HTF, H1, EF, ES, AD, w,
lo_i, hi_i, min_conf, skip_night):
"""Wie `scan`, aber MIT dem Regressions-Winkel.
⚠⚠ Der Grund für diese Kopie: `backtest_auto_signal.scan` ruft `_build` OHNE
`angle=` auf → der Default 90,0 macht `ad = 0`. Damit kann dort weder der
Reversal-Zweig feuern (braucht |ad| > _ANGLE_DEAD) noch der Winkel-Konfidenz-
term (±5/10) greifen. Das Skript behauptet in seiner Docstring, die „echte
`_build`-Logik inkl. Reversal-Zweig" zu testen — strukturell tut es das nicht.
Vierter Fall derselben Klasse: gemessen unter „kein Winkel", betrieben unter
„Winkel steuert einen Signalzweig UND ±15 Konfidenzpunkte".
Die Original-Datei bleibt unangetastet (ihre Zahlen sind dokumentiert); hier
steht die live-treue Variante daneben.
"""
Rs = []
i = max(lo_i, _EMA_SLOW + 20)
hi_i = min(hi_i, len(C) - _MAXH - 2)
while i < hi_i:
atr = A[i]
if not atr or atr < _ATRMIN:
i += 1; continue
if skip_night:
hb = (datetime.fromtimestamp(int(bars[i]["time"]),
timezone.utc).hour - 1) % 24
if hb in _NIGHT:
i += 1; continue
rec, _snap = w._build(EF[i], ES[i], C[i], atr, "M5", 5,
htf_trend=HTF[i], h1_trend=H1[i], angle=AD[i])
sig = rec.get("signal")
if sig not in ("LONG", "SHORT") or (rec.get("conf_pct") or 0) < min_conf:
i += 1; continue
d = 1 if sig == "LONG" else -1
cost = (SP[i] if SP[i] > 0 else 0.0225) / atr
Rs.append(_sim(C[i], d, atr, H, L, C, i + 1, None) - cost)
i += _TIMESTOP # identischer Vorlauf wie im Original (kein Overlap)
return Rs
def stats(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 fmt(s):
if not s:
return "— (keine Trades)"
return (f"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 main():
n = int(sys.argv[1]) if len(sys.argv) > 1 else 80000
if not mt5.initialize():
print(f"MT5-Init fehlgeschlagen: {mt5.last_error()}"); sys.exit(1)
sym = next((c for c in ("SpotCrude", "USOIL", "WTI", "XTIUSD")
if mt5.symbol_info(c)), None)
bars = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_M5, 0, n + _MAXH + 60)
point = mt5.symbol_info(sym).point
mt5.shutdown()
if bars is None or len(bars) < 5000:
print("Zu wenige Bars."); 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]
SP = [float(b["spread"]) * point for b in bars]
A = _atr_series(H, L, C)
HTF = _htf_signs(H, L, C, _HTF_N)
H1 = _htf_signs(H, L, C, _HTF_N * 2)
EF = _ema_series(C, _EMA_FAST)
ES = _ema_series(C, _EMA_SLOW)
N = len(C); mid = N // 2
w_on = WaveRecommender(_NeutralTU(), mt5.TIMEFRAME_M5)
w_on.set_reversal_enabled(True) # Stand bis 03.08. (= Messung 30.07.)
w_off = WaveRecommender(_NeutralTU(), mt5.TIMEFRAME_M5)
w_off.set_reversal_enabled(False) # Live-Stand seit 04.08.
print("=" * 96)
print(f" AUTO-SIGNAL-ENTRY mit/ohne Reversal — {sym} M5 ({N} Bars)")
print(" Identische Maschinerie aus backtest_auto_signal.py; variiert wird NUR")
print(" `_reversal_enabled`. Nacht-Sperre AN, keine 15-Min-Regel (live = 0).")
print(" Referenz (validierter Auto-Entry Squeeze): ØR +0,14…+0,23 · PF>1")
print("=" * 96)
# Winkel je Bar — genau wie im Live-Pfad aufgerufen
AD = [90.0] * len(C)
for i in range(_ANGLE_LR + 2, len(C)):
AD[i] = calc_trend_angle(C[i - (_ANGLE_LR + 1):i + 1], _ANGLE_LR)
for mc in (55, 65, 75):
live = " ← LIVE-Schwelle" if mc == 75 else ""
print(f"\n Konfidenz ≥ {mc}{live}")
for lbl, lo, hi in (("H1 (alt)", 0, mid), ("H2 (neu)", mid, N)):
r_alt = stats(scan(bars, H, L, C, SP, A, HTF, H1, EF, ES,
w_on, lo, hi, mc, True, None))
r_rev = stats(scan_angle(bars, H, L, C, SP, A, HTF, H1, EF, ES, AD,
w_on, lo, hi, mc, True))
r_live = stats(scan_angle(bars, H, L, C, SP, A, HTF, H1, EF, ES, AD,
w_off, lo, hi, mc, True))
print(f" {lbl}")
print(f" ALT-Messung (ohne Winkel, Rev. wirkungslos) : {fmt(r_alt)}")
print(f" mit Winkel + Reversal AN (Stand bis 03.08.): {fmt(r_rev)}")
print(f" mit Winkel + Reversal AUS (LIVE seit 04.08.): {fmt(r_live)}")
if r_rev and r_live:
print(f" Δ durch das Abschalten: ØR "
f"{r_live['oR'] - r_rev['oR']:+.3f} ΣR "
f"{r_live['sum'] - r_rev['sum']:+.0f}")
print()
print(" Urteil: Einbau nur, wenn ØR>0 UND PF>1 in BEIDEN Hälften — und die")
print(" Konfidenz-Nachbarn mithalten (sonst klebt der Treffer an einer Schwelle).")
print()
if __name__ == "__main__":
main()