Trailing breiter (Richtung M15) gemessen: faellt durch
User-Frage nach einem Trade, der bei Trail 1,0xATR_M5 nach rund einer
ATR Ruecklauf auf Einstand ausgestoppt wurde.
Die Frage war nicht schon beantwortet: backtest_exit_atr_tf.py (08.08.)
stellte den GESAMTEN Exit auf ATR_M15 (simulate nimmt EINEN atr fuer
SL, Trail, TP und Breakeven - daher das verdreifachte Tail), und
backtest_trailmult.py (31.07.) traf die Frage, lief aber auf M5-Bars.
Genau davor warnt analyze_trail_resolution.py: eine Bar-Simulation
ueberschaetzt enge Trails und unterschaetzt weite - bei 1,5xATR kippte
dort sogar das Vorzeichen. Der Sweep war also mit einer Verzerrung
GEGEN weite Trails gemessen.
Diese Messung kontrolliert das: gleiche Einstiege, gleicher ATR (M5),
Trail-Abstand in Dollar identisch, nur die Exit-Aufloesung variiert;
Zeitgrenzen wall-clock gleich (200 M5-Bars = 1000 M1-Bars, Time-Stop
24 = 120 - der Altbestand kappte M1-Trades nach 200 statt 1000 min).
Ergebnis: 1,86 ("M15") faellt in H1 auf BEIDEN Populationen und BEIDEN
Aufloesungen klar durch (Treffer 25-30 %, PF 0,52-0,64).
Wichtiger Nebenertrag: der Aufloesungs-Einwand ist entkraeftet. Die
Differenz M1-M5 ist bei ENGEN Trails gross und verschwindet bei
weiten. Die feinere Aufloesung bestraft enge Trails, sie belohnt weite
nicht. Der Vorzeichenwechsel vom 05.08. kam aus willkuerlichen
Einstiegen und reproduziert mit echten nicht.
Grenzen: Squeeze n=46/47 je Haelfte, M1-Historie ~55 Tage - die zwei
Haelften sind zwei Haelften EINES Regimes. Keine Aenderung, Trail
bleibt 1,0xATR_M5.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4eb92bf192
commit
661b99cc88
@@ -5425,6 +5425,59 @@ nie feuert, belegt nichts). ⚠ Der gefährlichste Fall hat einen eigenen Test:
|
||||
`frei` MUSS auf dem **ungerundeten** Abstand entschieden werden — bei 0,5951 würde
|
||||
die gerundete 0,60 den Fall fälschlich durchlassen.
|
||||
|
||||
## ⚠⚠ NUR DAS TRAILING BREITER (Richtung M15)? = FÄLLT DURCH, und der
|
||||
## Auflösungs-Einwand trägt nicht (`backtest_trail_tf.py`, 2026-08-11)
|
||||
|
||||
**Anlass:** User „sollten wir das Trailing auf M15 setzen?" nach einem Trade, der
|
||||
bei Trail 1,0×ATR_M5 nach rund **1×ATR Rücklauf auf Einstand** ausgestoppt wurde
|
||||
(#49850541 — bei genau diesem Abstand ist das zwangsläufig).
|
||||
|
||||
⚠ **Warum die Frage NICHT schon beantwortet war.** `backtest_exit_atr_tf.py`
|
||||
(08.08.) stellte den **gesamten** Exit auf ATR_M15 — `simulate()` nimmt EINEN
|
||||
`atr` für SL, Trail, TP **und** Breakeven, dort wurde also alles mitverbreitert
|
||||
(daher das verdreifachte Tail). `backtest_trailmult.py` (31.07.) traf die Frage
|
||||
(fixer SL 2,0, nur `mult` gesweept), lief aber auf **M5-Bars** — und genau davor
|
||||
warnt `analyze_trail_resolution.py` (05.08.): eine Bar-Simulation **überschätzt
|
||||
enge Trails und unterschätzt weite**, bei 1,5×ATR kippte dort sogar das
|
||||
Vorzeichen (M5 −0,038 gegen M1 **+0,028**). Der Sweep, auf dem „enger ist besser"
|
||||
ruht, war also mit einer Verzerrung **GEGEN** weite Trails gemessen.
|
||||
|
||||
**Diese Messung kontrolliert das:** dieselben Einstiege, derselbe ATR (aus M5) →
|
||||
Trail-Abstand in **Dollar identisch**, nur die Exit-Auflösung unterscheidet sich;
|
||||
Zeitgrenzen **wall-clock gleich** (200 M5-Bars = 1000 M1-Bars, Time-Stop 24 = 120
|
||||
— der Altbestand liess 200 stehen und kappte M1-Trades nach 200 statt 1000 min).
|
||||
Zwei Populationen, Echtkosten.
|
||||
| mult | Squeeze M5 H1/H2 | **Squeeze M1** | Welle M5 | **Welle M1** |
|
||||
|---|---|---|---|---|
|
||||
| 0,80 | +0,123 / +0,557 | **+0,077 / +0,292** | −0,131 / +0,022 | −0,237 / −0,183 |
|
||||
| **1,00 (live)** | +0,019 / +0,495 | **−0,067 / +0,206** | −0,242 / +0,031 | −0,302 / −0,127 |
|
||||
| 1,50 | −0,155 / +0,624 | −0,095 / +0,446 | −0,275 / +0,063 | −0,405 / −0,112 |
|
||||
| **1,86 („M15")** | **−0,355 / +0,557** | **−0,327 / +0,379** | −0,297 / +0,053 | **−0,392 / −0,067** |
|
||||
**1,86 fällt in H1 auf BEIDEN Populationen und BEIDEN Auflösungen klar durch**
|
||||
(Trefferquote 25–30 %, PF 0,52–0,64). Regel nicht ansatzweise erfüllt.
|
||||
|
||||
✅✅ **DER EIGENTLICHE ERTRAG: der Auflösungs-Einwand ist entkräftet — er wirkt
|
||||
nur bei ENGEN Trails.** Δ (M1 − M5) auf der Welle: **−0,092/−0,236 bei 0,50**,
|
||||
−0,060/−0,158 bei 1,00, −0,095/−0,120 bei 1,86; auf dem Squeeze bei 2,00
|
||||
**+0,049/−0,016 = praktisch null**. Bei weiten Trails verschwindet der
|
||||
Unterschied — logisch, ein weiter Stop ist nicht auflösungsempfindlich.
|
||||
⚠ **Das PRÄZISIERT den Befund vom 05.08.**: die feinere Auflösung **bestraft enge
|
||||
Trails**, sie **belohnt weite nicht**. Der dortige Vorzeichenwechsel bei 1,5 kam
|
||||
aus n=597 **willkürlichen** Einstiegen (jeder 20. Bar, immer LONG) — mit echten
|
||||
Einstiegen reproduziert er nicht.
|
||||
|
||||
⚠ **Grenzen, ehrlich:** Squeeze nur **n=46/47** je Hälfte; die M1-Historie deckt
|
||||
~55 Tage, die „zwei Hälften" sind also **zwei Hälften EINES Regimes**, nicht zwei
|
||||
Regime. Ein ✅ wäre hier wenig wert — das ❌ bei 1,86 ist es, weil es in **jeder**
|
||||
Zelle auftritt. Die Wellen-Population ist in diesem Fenster durchgehend negativ
|
||||
und kann deshalb gar nicht diskriminieren.
|
||||
⚠ **Nebenbefund, ausdrücklich KEINE Empfehlung:** auf M1-Auflösung sieht **0,8
|
||||
besser aus als 1,0** (Squeeze +0,077/+0,292 gegen −0,067/+0,206) — also eher
|
||||
*enger*, die Gegenrichtung der Frage. Für eine Umstellung reicht das nicht: ein
|
||||
Regime, dünne Stichprobe, und die Sim modelliert **keine Exit-Slippage**, die
|
||||
enge Trails härter trifft (real bis 0,75×ATR über den Stop hinaus).
|
||||
**Keine Änderung — Trail bleibt 1,0×ATR_M5.**
|
||||
|
||||
## ⚠⚠ EXIT-ABSTAND AUS M15-ATR = FÄLLT DURCH (2026-08-08)
|
||||
|
||||
Zweiter Teil der M15-Idee: M5-Einstieg behalten, nur den EXIT gröber führen.
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Trailing breiter (Richtung M15) — haelt der M5-Befund auf M1-Aufloesung?
|
||||
|
||||
ANLASS (2026-08-11, User: „sollten wir das Trailing auf M15 setzen?"). Auslöser
|
||||
war ein Trade, der nach ~1×ATR Ruecklauf auf Einstand ausgestoppt wurde — bei
|
||||
Trail 1,0×ATR_M5 ist das zwangslaeufig.
|
||||
|
||||
⚠⚠ WAS SCHON GEMESSEN IST (und die Frage NICHT beantwortet):
|
||||
· `backtest_exit_atr_tf.py` (08.08.) hat den GESAMTEN Exit auf ATR_M15 gestellt
|
||||
→ schlechter in beiden Haelften, Tail-Risiko verdreifacht. ⚠ Aber `simulate()`
|
||||
nimmt EINEN atr fuer SL, Trail, TP und Breakeven — dort wurde also alles
|
||||
mitverbreitert. Das ist NICHT „nur das Trailing".
|
||||
· `backtest_trailmult.py` (31.07.) hat `mult` bei FIXEM SL 2,0 gesweept
|
||||
→ enger ist monoton besser (1,0: +80/+1360 · 1,5: −220/+1305 · 2,0: −394/+1198).
|
||||
Das trifft die Frage — aber es lief auf **M5-Bars**.
|
||||
⚠⚠ UND GENAU DA SITZT DER ZWEIFEL: `analyze_trail_resolution.py` (05.08.) hat
|
||||
gezeigt, dass eine Bar-Simulation **enge Trails ueberschaetzt und weite
|
||||
unterschaetzt**; das Vorzeichen kippt bei ~0,3×ATR. Bei 1,5×ATR meldete M5
|
||||
−0,038, M1 dagegen **+0,028** — ein Vorzeichenwechsel. Der Sweep, der „enger ist
|
||||
besser" sagt, ist also mit einer Verzerrung GEGEN weite Trails gemessen.
|
||||
|
||||
DIESE MESSUNG kontrolliert genau das:
|
||||
· **dieselben Einstiege**, derselbe ATR (aus M5) → der Trail-Abstand ist in
|
||||
DOLLAR identisch, nur die Aufloesung des Exits unterscheidet sich.
|
||||
· Zeitgrenzen in WALL-CLOCK gleich gehalten: `max_hold` 200 M5-Bars = 1000
|
||||
M1-Bars, `timestop` 24 = 120. ⚠ Der Altbestand liess 200 stehen und kappte
|
||||
M1-Trades damit nach 200 statt 1000 Minuten.
|
||||
· zwei Populationen (Squeeze + Welle), zwei Haelften, Echtkosten.
|
||||
· `mult = 1.86` ist die „Trailing auf M15"-Variante: Median ATR_M15/ATR_M5.
|
||||
|
||||
⚠ ENTSCHEIDUNGSREGEL, VORAB: ein breiterer Trail wird nur empfohlen, wenn er auf
|
||||
**M1-Aufloesung** in BEIDEN Haelften UND auf BEIDEN Populationen besser ist als
|
||||
1,0 — und die Nachbarwerte nicht kippen.
|
||||
|
||||
Aufruf: python backtest_trail_tf.py [n_M1] (Default 80000)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import bisect
|
||||
import sys
|
||||
|
||||
import MetaTrader5 as mt5
|
||||
|
||||
from core.exit_model import LIVE, simulate
|
||||
from core.squeeze_scan import atr_series, scan
|
||||
|
||||
_MULTS = (0.5, 0.8, 1.0, 1.3, 1.5, 1.86, 2.0, 2.5)
|
||||
_M5_IN_M1 = 5
|
||||
|
||||
|
||||
def hole(tf, n):
|
||||
b = mt5.copy_rates_from_pos(SYM, tf, 0, n)
|
||||
if b is None:
|
||||
raise SystemExit(f"keine Bars fuer {tf}")
|
||||
return ([int(x["time"]) for x in b], [float(x["high"]) for x in b],
|
||||
[float(x["low"]) for x in b], [float(x["close"]) for x in b],
|
||||
[float(x["spread"]) * POINT for x in b])
|
||||
|
||||
|
||||
def kennz(Rs):
|
||||
n = len(Rs)
|
||||
if not n:
|
||||
return " —"
|
||||
w = sum(1 for x in Rs if x > 0)
|
||||
g = sum(x for x in Rs if x > 0); v = -sum(x for x in Rs if x < 0)
|
||||
return (f"{n:>5}{100*w/n:>7.0f} %{sum(Rs)/n:>+9.3f}"
|
||||
f"{(g/v if v else 9.99):>7.2f}{sum(Rs):>+9.1f}")
|
||||
|
||||
|
||||
def main():
|
||||
global SYM, POINT
|
||||
nM1 = 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)
|
||||
POINT = mt5.symbol_info(SYM).point
|
||||
T5, H5, L5, C5, SP5 = hole(mt5.TIMEFRAME_M5, 20000)
|
||||
T1, H1, L1, C1, _ = hole(mt5.TIMEFRAME_M1, nM1)
|
||||
mt5.shutdown()
|
||||
A5 = atr_series(H5, L5, C5)
|
||||
|
||||
# gemeinsames Fenster — nur wo BEIDE Zeitebenen Daten haben
|
||||
t_von, t_bis = max(T5[0], T1[0]), min(T5[-1], T1[-1])
|
||||
lo = next(i for i, t in enumerate(T5) if t >= t_von)
|
||||
hi = max(i for i, t in enumerate(T5) if t <= t_bis)
|
||||
|
||||
# ── Einstiege sammeln: Squeeze (Auto-Pfad) ────────────────────────────
|
||||
sq_ein: list = []
|
||||
scan(H5, L5, C5, A5, SP5, POINT, lo, hi, 2.5,
|
||||
on_entry=lambda j, d, lvl, atr, komp: (
|
||||
sq_ein.append((j, d, lvl, atr)) if komp else None))
|
||||
# ── und Welle (EMA-Kreuz, die manuelle Population) ────────────────────
|
||||
def ema(v, p):
|
||||
k = 2 / (p + 1); o = [v[0]]
|
||||
for x in v[1:]:
|
||||
o.append(o[-1] + k * (x - o[-1]))
|
||||
return o
|
||||
ef, es = ema(C5, 12), ema(C5, 50)
|
||||
wv_ein = []
|
||||
vor = 0
|
||||
for i in range(lo, hi):
|
||||
a = A5[i]
|
||||
if not a or a < 0.06:
|
||||
continue
|
||||
s = 1 if ef[i] > es[i] else -1
|
||||
if s != vor and vor != 0:
|
||||
wv_ein.append((i, s, C5[i], a))
|
||||
vor = s
|
||||
|
||||
print("=" * 104)
|
||||
print(f" TRAIL-BREITE × AUFLOESUNG — {SYM}")
|
||||
print(f" gemeinsames Fenster: {len(T1)} M1-Bars, {hi-lo} M5-Bars")
|
||||
print(f" Squeeze-Einstiege {len(sq_ein)} · Wellen-Einstiege {len(wv_ein)}")
|
||||
print(" ⚠ Gleiche Einstiege, gleicher ATR (M5) → Trail-Abstand in DOLLAR")
|
||||
print(" identisch; NUR die Aufloesung des Exits unterscheidet sich.")
|
||||
print(" Zeitgrenzen wall-clock gleich (200 M5-Bars = 1000 M1-Bars).")
|
||||
print("=" * 104)
|
||||
|
||||
for name, ein in (("SQUEEZE", sq_ein), ("WELLE", wv_ein)):
|
||||
mid = len(ein) // 2
|
||||
print(f"\n ══ {name} ══")
|
||||
print(f" {'mult':<7}{'':>3}{'Haelfte':<9}{'n':>5}{'Treffer':>9}{'OeR':>9}"
|
||||
f"{'PF':>7}{'SumR':>9} ||{'n':>5}{'Treffer':>9}{'OeR':>9}{'PF':>7}{'SumR':>9}")
|
||||
print(f" {'':<19}{'--- Exit auf M5 ---':^39} {'--- Exit auf M1 ---':^39}")
|
||||
for m in _MULTS:
|
||||
p5 = LIVE.with_(mult=m)
|
||||
p1 = LIVE.with_(mult=m,
|
||||
timestop_bars=LIVE.timestop_bars * _M5_IN_M1,
|
||||
max_hold=LIVE.max_hold * _M5_IN_M1)
|
||||
for lbl, teil in (("H1", ein[:mid]), ("H2", ein[mid:])):
|
||||
r5, r1 = [], []
|
||||
for j, d, px, atr in teil:
|
||||
kost = (SP5[j] if SP5[j] > 0 else 0.0225) / atr
|
||||
if j + 1 >= len(C5) - LIVE.max_hold - 2:
|
||||
continue
|
||||
r5.append(simulate(px, d, atr, H5, L5, C5, j + 1, p5) - kost)
|
||||
k = bisect.bisect_left(T1, T5[j])
|
||||
if not (0 < k < len(C1) - p1.max_hold - 2):
|
||||
continue
|
||||
r1.append(simulate(px, d, atr, H1, L1, C1, k + 1, p1) - kost)
|
||||
print(f" {m:<7.2f}{'':>3}{lbl:<9}{kennz(r5)} ||{kennz(r1)}")
|
||||
|
||||
print("\n " + "-" * 100)
|
||||
print(" ⚠ REGEL (vorab): breiter nur, wenn auf M1-Aufloesung in BEIDEN Haelften")
|
||||
print(" UND auf BEIDEN Populationen besser als 1,0 — Nachbarn duerfen nicht kippen.")
|
||||
print(" ⚠ M1-Historie ist beim Broker hart gedeckelt → kuerzeres Fenster als 80k M5.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user