Files
AH-Oil-Trader/backtest_flipclose_htf.py
T
Axel HocksandClaude Opus 5 cef6646a17 Verlustbegrenzung: Notfall-Stop und HTF-Flip-Close gemessen - beide durchgefallen
User-Frage nach automatischem Notfall-Close (30 %?) und alternativ einem
Flip-Close bei entgegengerichtetem Trend.

1) NOTFALL-STOP. 30 % waere ohnehin wirkungslos: Einsatz 670 EUR/Lot,
Median-Verlust 20 EUR/Lot = 3 % der Margin. 30 % erreichen 1 % aller Verluste.
Kontrafaktisch ueber 1022 echte Trades (MAE aus M5-Bars, 87 EUR/$/Lot aus den
Trades selbst geschaetzt):
  ohne Stop  +358 EUR
  3 %  -293 | 5 %  -639 | 8 %  -14 | 15 %  -416 | 30 %  -331
Keine Schwelle schlaegt "gar kein Stop". Nach Einstiegsart: bei "ohne Signal"
sieht 8-10 % mit +298/+290 gut aus, aber beide Nachbarn kippen (-292 / -106)
-> Rauschen.

2) FLIP-CLOSE AUF DEN HTF-TREND (backtest_flipclose_htf.py) - war nie gemessen.
Alle sechs Varianten in BEIDEN Haelften schlechter:
  BASIS                        H1 -548   H2 -154
  M30 ab +0,0R                    -629      -247
  M30 ab +0,5R                    -574      -234
  M30 ab +1,0R                    -549      -183
  M30 auch im Minus               -616      -327
  M30+H1 ab +0,5R                 -570      -222
  M30+H1 auch im Minus            -616      -290
Die mildeste Variante ist die, die kaum feuert - exakt das Muster der
15-Minuten-Regel.

Kontrolle: die Basis (-548/-154) reproduziert backtest_flipclose2.py
(-537/-140), die Pipeline ist in Ordnung.

Methodik: feste, geteilte Entry-Liste ueber alle Varianten.
backtest_flipclose2.py laeuft nach jedem Exit bei xb+1 weiter, wodurch jede
Variante eine andere Trade-Folge bekommt (der am 31.07. bei
backtest_exit_combo.py korrigierte Fehler). Hier von vornherein vermieden.

Mechanismus in allen Faellen derselbe: Trefferquote steigt (38->44 %, 42->51 %),
Ertrag faellt = Gewinner-Kappen. Fuenfte unabhaengige Messung dieser Klasse,
alle negativ.

Nichts gebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 14:41:13 +02:00

143 lines
6.0 KiB
Python
Raw 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
"""Flip-Close auf dem HÖHEREN TREND (M30 / H1) — trägt er als EXIT?
ANLASS (2026-08-07, User: „oder doch lieber ein Flip-Close bei entgegen-
gerichtetem Trend?"). Zu unterscheiden sind ZWEI Dinge:
· Flip auf das EMPFEHLUNGS-Signal — gemessen (`backtest_flipclose2.py`), in
JEDER Variante und BEIDEN Hälften schlechter, am 05.08. abgeschaltet.
Mechanik: die nachlaufende EMA dreht oft mitten im Pullback → Gewinner-Kappen.
· Flip auf den HTF-TREND (M30/H1) — **war nie gemessen**. Der HTF ist träger,
dreht viel seltener, und als FILTER ist er im Projekt belegt (Edge ×2). Ob er
auch als EXIT trägt, ist damit keineswegs entschieden.
⚠⚠ ENTSCHEIDUNGSREGEL, VORAB FESTGELEGT:
Eine Variante gilt nur dann als besser, wenn sie
(1) den ΣR in BEIDEN Halbjahren erhöht, UND
(2) den Worst-Case nicht verschlechtert, UND
(3) bei den Nachbar-Einstellungen (minR) nicht das Vorzeichen kippt.
Sonst: kein Einbau. Punkt (3), weil in diesem Projekt schon mehrfach eine
einzelne Zelle gut aussah, deren Nachbarn kippten (ORB, SMC-Sweeps, und heute
der Notfall-Stop bei 8 % gegen 292/106 bei 5 %/15 %).
⚠ FESTE, GETEILTE ENTRY-LISTE. `backtest_flipclose2.py` lässt die sequentielle
Sim nach jedem Exit bei `xb+1` weiterlaufen — dadurch bekommt JEDE Variante eine
andere Trade-Folge (steigt eine früher aus, verschiebt sich alles Nachfolgende)
und die Zahlen sind nicht vergleichbar. Genau dieser Fehler wurde am 31.07. bei
`backtest_exit_combo.py` schon einmal korrigiert; hier wird er von vornherein
vermieden: alle Varianten laufen auf DENSELBEN Einstiegen.
Exit-Kern = `core/exit_model.simulate()` mit LIVE-Parametern (geteilte Quelle,
kein Nachbau) + `stop_when`-Hook für den Flip. Kosten = echter Bar-Spread/ATR.
Aufruf: python backtest_flipclose_htf.py [n_bars] (Default 80000)
"""
from __future__ import annotations
import sys
import MetaTrader5 as mt5
from backtest_auto_signal import _atr_series, _ema_series, _htf_signs
# ⚠ `_ATRMIN` aus `backtest_flipclose2` uebernehmen, NICHT neu setzen — der
# Vergleich mit dem dortigen Befund gilt nur bei identischem ATR-Floor.
from backtest_flipclose2 import _ATRMIN, signal_series
from core.exit_model import LIVE, simulate
from core.wave_rec import _EMA_FAST, _EMA_SLOW, _N_BARS
_M30_STEP = 6 # 6 M5-Bars = M30
_H1_STEP = 12 # 12 M5-Bars = H1
def entries(sig, lo, hi, H, L, C, AT, SP):
"""EINE feste Einstiegsliste, sequentiell mit dem BASIS-Exit ermittelt.
⚠ Bewusst mit der Basis (ohne Flip) bestimmt: nur so handeln alle Varianten
dieselben Trades und der Vergleich misst den EXIT, nicht eine verschobene
Trade-Folge."""
out = []
i = lo
while i < hi:
d = sig[i]
if d == 0:
i += 1
continue
atr = max(AT[i] or 0, _ATRMIN)
kosten = (SP[i] if SP[i] > 0 else 0.0225) / atr
_, xb = simulate(C[i], d, atr, H, L, C, i + 1, LIVE, ret_bar=True)
out.append((i, d, atr, kosten))
i = xb + 1
return out
def lauf(ein, H, L, C, htf, h1, *, flip=None, min_r=0.0, auch_im_minus=False):
"""`flip`: None = Basis · "m30" · "m30+h1" (beide müssen dagegen stehen)."""
Rs = []
for i, d, atr, kosten in ein:
haken = None
if flip:
def haken(j, prof_atr, _d=d):
gegen = htf[j] == -_d
if flip == "m30+h1":
gegen = gegen and h1[j] == -_d
if not gegen:
return False
return auch_im_minus or prof_atr > min_r
R = simulate(C[i], d, atr, H, L, C, i + 1, LIVE, stop_when=haken)
Rs.append(R - kosten)
return Rs
def kz(Rs):
if not Rs:
return "—"
n = len(Rs); w = sum(1 for x in Rs if x > 0); s = sum(Rs)
g = sum(x for x in Rs if x > 0); v = -sum(x for x in Rs if x < 0)
return (f"n={n:>4} WR={100*w/n:>3.0f}% ØR={s/n:+.3f} "
f"PF={(g/v if v > 0 else 9.99):>4.2f} ΣR={s:>+6.0f} Worst={min(Rs):+.2f}")
def main():
n = int(sys.argv[1]) if len(sys.argv) > 1 else 80000
mt5.initialize()
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)
si = mt5.symbol_info(sym); point = si.point
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]
SP = [float(b["spread"]) * point for b in bars]
EF = _ema_series(C, _EMA_FAST); ES = _ema_series(C, _EMA_SLOW)
AT = _atr_series(H, L, C)
sig = signal_series(H, L, C, EF, ES, AT)
htf = _htf_signs(H, L, C, _M30_STEP)
h1 = _htf_signs(H, L, C, _H1_STEP)
mid = len(C) // 2
print("=" * 96)
print(f" FLIP-CLOSE auf dem HTF-TREND — {sym} M5, {len(C)} Bars")
print(" Regel vorab: ΣR in BEIDEN Hälften besser · Worst nicht schlechter · "
"Nachbarn kippen nicht")
print("=" * 96)
for name, (lo, hi) in (("H1", (_N_BARS, mid)), ("H2", (mid, len(C) - LIVE.max_hold - 2))):
ein = entries(sig, lo, hi, H, L, C, AT, SP)
print(f"\n ── {name} ── {len(ein)} feste Einstiege, alle Varianten identisch")
basis = lauf(ein, H, L, C, htf, h1)
b_sum = sum(basis)
print(f" {'BASIS (kein Flip-Close)':<34}{kz(basis)}")
for etikett, kw in (
("M30 dagegen, ab +0,0R", dict(flip="m30", min_r=0.0)),
("M30 dagegen, ab +0,5R", dict(flip="m30", min_r=0.5)),
("M30 dagegen, ab +1,0R", dict(flip="m30", min_r=1.0)),
("M30 dagegen, AUCH im Minus", dict(flip="m30", auch_im_minus=True)),
("M30+H1 dagegen, ab +0,5R", dict(flip="m30+h1", min_r=0.5)),
("M30+H1, AUCH im Minus", dict(flip="m30+h1", auch_im_minus=True))):
Rs = lauf(ein, H, L, C, htf, h1, **kw)
d = sum(Rs) - b_sum
print(f" {etikett:<34}{kz(Rs)} Δ{d:>+6.0f}")
if __name__ == "__main__":
main()