Verworfene Setups auf der Ausfuehrungs-Achse — und der Squeeze steht mit auf null
User: "backteste alle verworfenen Signale". Bewusst kein Komplettlauf (der lief am 04.08., 14 Klassen, alle blieben verworfen) — neu ist genau eine Achse, der Einstiegspreis, angewandt mit der heutigen Korrektur (Fill bei BERUEHRUNG statt bei Schluss-Bruch) und nur dort, wo sie greifen kann: stehendes Level + Ausbruchsrichtung. Ergebnis (Variante C, 80k M5, 2 Halbjahre): KONTROLLE Squeeze +0,133/+0,293 (reproduziert touchfill — Pipeline ok) ORB (4 Varianten) faellt durch (bestaetigt 2026-07-17) PDH/PDL +0,246/+0,464 Asian Range +0,142/+0,134 Equal H/L +0,218/+0,170 Alle drei nachbar-robust (11 Zellen, keine kippt). ⚠ Das sind keine drei neuen Signale: PDH/PDL, Asian-Range-Extrem, Equal H/L und die Squeeze-Box sind dasselbe — waagerechtes Level aus frueheren Extremen, gebrochen, ruhende Order, kanonisches Trailing. Deckt sich mit der Kontrolle "beliebige Box" aus dem Multi-Instrument-Lauf. ⚠⚠ Mit der gemessenen Exit-Slippage bleibt fast nichts uebrig. Beim guenstigsten Wert (0,0124 $, Tagfenster): PDH/PDL +0,099/+0,386 haelt, Equal H/L duenn, Asian Range faellt — und der LIVE laufende Auto-Squeeze steht in H1 bei -0,002. Die Edges dieser Familie liegen in derselben Groessenordnung wie die Ausstiegskosten, und die Slippage-Zahl ist eine Untergrenze. Konsequenz: kein neues Setup bauen. Der naechste Hebel ist die Ausfuehrungsqualitaet am EXIT, nicht ein weiteres Einstiegs-Signal. Offen und benannt: Volume-Profile-LVN und die Chartmuster-Nackenlinie qualifizieren sich, wurden aber nicht gemessen (Laufzeit bzw. Detektor). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
45bfc87153
commit
7e03e481ca
@@ -319,6 +319,61 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
|
||||
~15 s→5 s schneller erkannt, dann ~1 s Handeln (`_pos_loop`). Gleiche validierte M5-Regel,
|
||||
reine Infra (kein Backtest nötig). **Variante B (Squeeze auf M1) = OFFEN/UNBELEGT** — erst
|
||||
backtesten, wenn der M1-Logger (`candles_m1`, seit 2026-07-21) ab ~2027-01 genug History hat.
|
||||
- **⚠⚠⚠ VERWORFENE SETUPS AUF DER AUSFÜHRUNGS-ACHSE NEU GEMESSEN — und das
|
||||
Ergebnis stellt den LIVE-Squeeze mit in Frage (`backtest_verworfene_v2.py`,
|
||||
2026-08-06, User: „backteste alle verworfenen Signale").**
|
||||
⚠ Bewusst **kein** erneuter Komplettlauf — der lief am 04.08. (14 Klassen, alle
|
||||
blieben verworfen). Neu ist genau **eine** Achse, der Einstiegspreis, und sie
|
||||
wurde mit der heutigen Korrektur angewandt: **Fill bei BERÜHRUNG**, nicht bei
|
||||
Schluss-Bruch. Geprüft wurde nur, wo die Achse überhaupt greifen kann —
|
||||
**stehendes Level + Ausbruchsrichtung** (Momentum scheitert, weil sein Level
|
||||
wandert; Reversions-Einstiege werden von einer ruhenden Order sogar schlechter,
|
||||
`backtest_candle_fade2.py`).
|
||||
| Setup | A) Markt | B) Close-Bruch | **C) Berührung (real)** | n |
|
||||
|---|---|---|---|---|
|
||||
| *KONTROLLE Squeeze* | −0,180/−0,017 | +0,560/+0,755 | **+0,133/+0,293** | 1094 |
|
||||
| ORB EU 15/30 · US 15/30 | negativ | +0,45…+0,73 | **alle 4 ❌** | ~200 |
|
||||
| PDH / PDL Bruch | −0,280/+0,044 | +0,906/+1,368 | **+0,246/+0,464** | 147 |
|
||||
| Asian Range Bruch | +0,021/−0,116 | +0,928/+0,718 | **+0,142/+0,134** | 218 |
|
||||
| Equal H/L Bruch | −0,279/+0,228 | +0,574/+0,905 | **+0,218/+0,170** | 168 |
|
||||
✅ Die Kontrolle reproduziert `backtest_squeeze_touchfill.py` (+0,133/+0,293
|
||||
gegen +0,124/+0,292) — Pipeline in Ordnung. ✅ **ORB bleibt verworfen** in allen
|
||||
vier Varianten (bestätigt 2026-07-17). ✅ Die drei Überlebenden sind
|
||||
**nachbar-robust** (PDH/PDL über die Gültigkeitsdauer, Asian Range über das
|
||||
Fenster, Equal H/L über Toleranz und Gültigkeit — 11 Zellen, keine kippt).
|
||||
⚠⚠ **Das sind KEINE drei neuen Signale.** PDH/PDL, Asian-Range-Extrem, Equal
|
||||
Highs/Lows und die Squeeze-Box sind **dasselbe**: ein waagerechtes Level aus
|
||||
früheren Kursextremen, gebrochen, mit ruhender Stop-Order betreten und mit dem
|
||||
kanonischen Trailing verlassen. Deckt sich mit der Multi-Instrument-Kontrolle
|
||||
(„beliebige Box" ist überall positiv, die Kompression trägt +0,009…+0,155).
|
||||
**Der Edge liegt in der Kombination Ausführung + Exit, nicht in der Regel, die
|
||||
das Level auswählt.**
|
||||
⚠⚠⚠ **UND JETZT DER TEIL, DER AUCH DAS LAUFENDE SYSTEM BETRIFFT — mit der
|
||||
gemessenen Exit-Slippage (`analyze_slippage_holdtime.py`) bleibt fast nichts
|
||||
übrig:**
|
||||
| Setup | 0 $ | **0,0124 $** (Tagfenster) | 0,0370 $ (alle Std) |
|
||||
|---|---|---|---|
|
||||
| **Squeeze (LIVE!)** | +0,133/+0,293 | **−0,002/+0,221** | −0,270/+0,080 |
|
||||
| PDH / PDL | +0,246/+0,464 | **+0,099/+0,386** | −0,192/+0,231 |
|
||||
| Equal H/L | +0,218/+0,170 | +0,077/+0,092 | −0,202/−0,062 |
|
||||
| Asian Range | +0,142/+0,134 | −0,011/+0,065 | −0,316/−0,071 |
|
||||
**Die Edges dieser ganzen Familie liegen in derselben Größenordnung wie die
|
||||
Exit-Slippage.** Beim günstigsten gemessenen Wert hält nur **PDH/PDL** beide
|
||||
Hälften klar; Equal H/L ist dünn, Asian Range fällt — und der **live laufende
|
||||
Auto-Squeeze steht in H1 bei −0,002, also auf null**.
|
||||
⚠ Die Slippage ist dabei eine **Untergrenze** (s. Herleitung dort) — der wahre
|
||||
Abzug ist größer, nicht kleiner.
|
||||
✅ **Konsequenz: KEIN neues Setup bauen.** Drei weitere Module derselben Klasse
|
||||
addieren Frequenz zu einem Edge, der von den Ausstiegskosten aufgezehrt wird.
|
||||
Der nächste Hebel ist **die Ausführungsqualität am EXIT** (dort ist der Posten,
|
||||
der alles frisst), nicht ein weiteres Einstiegs-Signal.
|
||||
⚠ **Nicht mitgemessen, ehrlich benannt:** (a) **Volume-Profile-LVN** — stehendes
|
||||
Level, aber das Profil müsste je Bar neu gebinnt werden (Laufzeit); (b)
|
||||
**Chartmuster-/Doppeltop-Nackenlinie** — stehendes Level und Ausbruchsrichtung,
|
||||
qualifiziert sich also, braucht aber den Muster-Detektor kausal getrieben.
|
||||
**Beide bleiben offen.** Alle übrigen Ablehnungen (Inter-Market, Chop-Gate,
|
||||
Meta-Labeling, RSI/MACD/ADX, HTF-Winkel, H1-Stärke, Konfluenz-Strafe) haben gar
|
||||
kein Level — die Achse kann dort nichts ändern.
|
||||
- **⚠⚠ DER SQUEEZE TRÄGT AUCH AUF ANDEREN INSTRUMENTEN — 7 von 9 (gemessen
|
||||
`backtest_squeeze_multi.py`, 2026-08-06).** Anlass: `analyze_signal_live.py` hat
|
||||
gezeigt, dass die Richtung ein Münzwurf ist; das Grundproblem ist also nicht die
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
"""Nachbar-Pruefung der drei Setups, die den vollen Lauf bestanden haben.
|
||||
|
||||
Kippt ein Nachbar-Parameter das Vorzeichen, ist der Treffer Rauschen — genau das
|
||||
Kriterium, an dem ORB (2026-07-17) und die SMC-Sweeps gescheitert sind.
|
||||
"""
|
||||
import sys
|
||||
sys.path.insert(0, r"c:\Users\ah\Downloads\tradingrobot")
|
||||
from datetime import timedelta
|
||||
import MetaTrader5 as mt5
|
||||
from backtest_cost_gate import _atr_series, kennzahlen
|
||||
import backtest_verworfene_v2 as V
|
||||
|
||||
mt5.initialize()
|
||||
sym = "SpotCrude"
|
||||
b = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_M5, 0, 80000)
|
||||
point = mt5.symbol_info(sym).point
|
||||
mt5.shutdown()
|
||||
H = [float(x["high"]) for x in b]; L = [float(x["low"]) for x in b]
|
||||
C = [float(x["close"]) for x in b]; T = [int(x["time"]) for x in b]
|
||||
SP = [float(x["spread"]) * point for x in b]
|
||||
A = _atr_series(H, L, C); m = len(C) // 2
|
||||
|
||||
|
||||
def zeig(lbl, lv):
|
||||
k1 = kennzahlen(V.lauf(lv, H, L, C, SP, A, 0, m, "touch"))
|
||||
k2 = kennzahlen(V.lauf(lv, H, L, C, SP, A, m, len(C), "touch"))
|
||||
if not k1 or not k2:
|
||||
print(f" {lbl:<28} — zu wenige"); return False
|
||||
gut = k1["oer"] > 0 and k2["oer"] > 0 and k1["pf"] > 1 and k2["pf"] > 1
|
||||
print(f" {lbl:<28} H1 {k1['oer']:>+7.3f} PF {k1['pf']:.2f} n{k1['n']:<5} | "
|
||||
f"H2 {k2['oer']:>+7.3f} PF {k2['pf']:.2f} n{k2['n']:<5} {'OK' if gut else 'KIPPT'}")
|
||||
return gut
|
||||
|
||||
|
||||
print("=" * 96)
|
||||
print(" NACHBAR-PRUEFUNG (Variante C = Beruehrungs-Fill, 80k M5-Bars)")
|
||||
print("=" * 96)
|
||||
|
||||
print("\n PDH/PDL — Gueltigkeitsdauer variiert:")
|
||||
alle_pdh = True
|
||||
basis = V.tagesextrem(H, L, T)
|
||||
alle_pdh &= zeig("voller Folgetag (Basis)", basis)
|
||||
for kuerz in (0.5, 0.25):
|
||||
lv = [(a, a + int((bb - a) * kuerz), l, d) for a, bb, l, d in basis]
|
||||
alle_pdh &= zeig(f"nur erste {int(kuerz*100)} % des Tages", lv)
|
||||
|
||||
print("\n Asian Range — Fenster variiert:")
|
||||
alle_asia = True
|
||||
alle_asia &= zeig("00–08 Uhr (Basis)", V.asia(H, L, T))
|
||||
for h_ende in (7, 9):
|
||||
orig = V.asia.__code__
|
||||
lv = []
|
||||
tag, sam = None, []
|
||||
for i in range(len(H)):
|
||||
bz = V._berlin(T[i])
|
||||
if bz.date() != tag:
|
||||
tag, sam = bz.date(), []
|
||||
if bz.hour < h_ende:
|
||||
sam.append(i)
|
||||
elif sam and bz.hour == h_ende and bz.minute < 5:
|
||||
hi = max(H[j] for j in sam); lo = min(L[j] for j in sam)
|
||||
bis = i
|
||||
while bis < len(H) - 2 and V._berlin(T[bis]).date() == tag:
|
||||
bis += 1
|
||||
lv.append((i, bis, hi, +1)); lv.append((i, bis, lo, -1)); sam = []
|
||||
alle_asia &= zeig(f"00–{h_ende:02d} Uhr", lv)
|
||||
|
||||
print("\n Equal H/L — Toleranz und Gueltigkeit variiert:")
|
||||
alle_eq = True
|
||||
alle_eq &= zeig("tol 0.15 · 120 Bars (Basis)", V.equal_hl(H, L, A))
|
||||
for tol in (0.10, 0.25):
|
||||
alle_eq &= zeig(f"tol {tol} · 120 Bars", V.equal_hl(H, L, A, tol=tol))
|
||||
for g in (60, 240):
|
||||
alle_eq &= zeig(f"tol 0.15 · {g} Bars", V.equal_hl(H, L, A, gueltig=g))
|
||||
|
||||
print()
|
||||
print("=" * 96)
|
||||
for n, ok in (("PDH/PDL", alle_pdh), ("Asian Range", alle_asia), ("Equal H/L", alle_eq)):
|
||||
print(f" {n:<14} → {'ROBUST' if ok else 'KIPPT bei den Nachbarn — verworfen'}")
|
||||
print("=" * 96)
|
||||
@@ -0,0 +1,57 @@
|
||||
"""Halten die drei Ueberlebenden auch mit gemessener Exit-Slippage?
|
||||
|
||||
`backtest_verworfene_v2.lauf` rechnet nur den Spread. Aus
|
||||
`analyze_slippage_holdtime.py`: E[Slippage] >= 0,0370 $ ueber alle Stunden,
|
||||
0,0124 $ im Fenster 10-17. Bei ATR_M5 ~0,15 sind das 0,08-0,25 R je Trade —
|
||||
gegen Edges von +0,13..+0,25 ist das nicht vernachlaessigbar.
|
||||
"""
|
||||
import sys
|
||||
sys.path.insert(0, r"c:\Users\ah\Downloads\tradingrobot")
|
||||
import MetaTrader5 as mt5
|
||||
from backtest_cost_gate import _atr_series, kennzahlen
|
||||
from core.exit_model import LIVE, simulate
|
||||
import backtest_verworfene_v2 as V
|
||||
|
||||
mt5.initialize(); sym = "SpotCrude"
|
||||
b = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_M5, 0, 80000)
|
||||
point = mt5.symbol_info(sym).point
|
||||
mt5.shutdown()
|
||||
H=[float(x["high"]) for x in b]; L=[float(x["low"]) for x in b]
|
||||
C=[float(x["close"]) for x in b]; T=[int(x["time"]) for x in b]
|
||||
SP=[float(x["spread"])*point for x in b]
|
||||
A=_atr_series(H,L,C); m=len(C)//2
|
||||
|
||||
|
||||
def lauf_slip(levels, lo, hi, slip_usd):
|
||||
ev=[]
|
||||
for ab,bis,lvl,d in levels:
|
||||
for i in range(max(ab,1), min(bis+1, len(C)-2)):
|
||||
atr=A[i-1]
|
||||
if not atr or atr < V._ATRMIN: continue
|
||||
if not ((H[i]>=lvl) if d>0 else (L[i]<=lvl)): continue
|
||||
ev.append((i,d,lvl,atr,SP[i])); break
|
||||
ev.sort(); rs=[]; frei=lo
|
||||
for i,d,entry,atr,sp in ev:
|
||||
if not (lo<=i<hi) or i<frei: continue
|
||||
r,xb=simulate(entry,d,atr,H,L,C,i+1,LIVE,timestop=True,use_tp=True,ret_bar=True)
|
||||
rs.append(r-(sp if sp>0 else 0.0225)/atr - slip_usd/atr)
|
||||
frei=xb+1
|
||||
return rs
|
||||
|
||||
setups=[("KONTROLLE Squeeze", V.squeeze_kontrolle(H,L,C,A)),
|
||||
("PDH / PDL Bruch", V.tagesextrem(H,L,T)),
|
||||
("Asian Range Bruch", V.asia(H,L,T)),
|
||||
("Equal H/L Bruch", V.equal_hl(H,L,A))]
|
||||
print("="*92)
|
||||
print(" EXIT-SLIPPAGE angesetzt (gemessen: >=0,0124 $ im Tagfenster, >=0,0370 $ gesamt)")
|
||||
print("="*92)
|
||||
print(f" {'Setup':<22} {'0,000 $':>18} {'0,0124 $':>18} {'0,0370 $':>18}")
|
||||
for name,lv in setups:
|
||||
z=f" {name:<22}"
|
||||
for s in (0.0, 0.0124, 0.0370):
|
||||
k1=kennzahlen(lauf_slip(lv,0,m,s)); k2=kennzahlen(lauf_slip(lv,m,len(C),s))
|
||||
z += (f" {k1['oer']:>+7.3f}/{k2['oer']:>+7.3f}" if k1 and k2 else f" {'—':^16}")
|
||||
print(z)
|
||||
print()
|
||||
print(" ⚠ Die Edges liegen bei +0,13..+0,25 R — eine Slippage von 0,08..0,25 R")
|
||||
print(" je Trade ist dagegen keine Randgroesse, sondern der entscheidende Posten.")
|
||||
@@ -0,0 +1,282 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Die verworfenen Signale — neu gemessen auf der EINEN Achse, die heute neu ist.
|
||||
|
||||
ANLASS (2026-08-06, User: „backteste alle verworfenen Signale").
|
||||
|
||||
⚠ WAS DAS HIER NICHT IST: ein erneuter Durchlauf aller Ablehnungen. Der lief am
|
||||
2026-08-04 (`run_all_backtests.py`, 14 Klassen, alle blieben verworfen) und waere
|
||||
nur eine Wiederholung — mit dem Mehrfachvergleichs-Problem obendrauf.
|
||||
|
||||
NEU ist genau eine Achse: der **Einstiegspreis**. Sie hat am 05.08. Squeeze und
|
||||
Auto-Signal von negativ auf positiv gedreht. ALLE alten Ablehnungen wurden mit
|
||||
Markt-/Bar-Close-Einstieg gemessen — sie koennten also unter einer Annahme
|
||||
gefallen sein, die nicht mehr gilt.
|
||||
|
||||
⚠⚠ ABER MIT DER HEUTIGEN KORREKTUR. `backtest_momentum_v2.py` hat gezeigt, dass
|
||||
„Einstieg am Level" zweideutig ist:
|
||||
(B) Fill, WO der Bar-CLOSE das Level bricht → Look-ahead, ~+0,3…0,45 R zu gut
|
||||
(C) Fill bei der ERSTEN BERUEHRUNG → das, was live passiert
|
||||
Gemessen wird deshalb C. B laeuft nur als Diagnose mit, um den Aufschlag sichtbar
|
||||
zu machen.
|
||||
|
||||
⚠⚠ UND NUR DORT, WO DIE ACHSE UEBERHAUPT GREIFEN KANN. Der Vorteil entsteht an
|
||||
Levels, die **STILLSTEHEN** (Squeeze-Box: +0,124/+0,292) — nicht an mitwandernden
|
||||
(Momentum: −0,04…−0,09). Geprueft werden deshalb die verworfenen Setups mit einem
|
||||
stehenden Level und einer AUSBRUCHS-Richtung:
|
||||
ORB (EU/US) · PDH/PDL · Asian Range · Equal Highs/Lows
|
||||
⚠ Bewusst NICHT dabei, weil die Achse dort nichts aendern kann:
|
||||
· Momentum, Inter-Market, Chop-Gate, Meta-Labeling, RSI/MACD/ADX — kein Level.
|
||||
· Reversal/Bounce, Kerzen-Fade, POC-Reversion, Fibonacci, Kanalrand — REVERSIONS-
|
||||
Einstiege. Dort fuellt eine ruhende Order gerade dann, wenn man falsch liegt
|
||||
(gemessen `backtest_candle_fade2.py`: Limit am Extrem macht es SCHLECHTER).
|
||||
· Volume Profile — Level steht zwar, aber das Profil muesste je Bar neu gebinnt
|
||||
werden; Laufzeit unverhaeltnismaessig. Offen.
|
||||
|
||||
VORAB FIXIERTE ENTSCHEIDUNGSREGEL (vor dem ersten Lauf):
|
||||
(1) in Variante C: ØR > 0 UND PF > 1 in BEIDEN Haelften
|
||||
(2) mind. 40 Trades je Haelfte
|
||||
(3) muss die KONTROLLE (Squeeze, +0,124/+0,292) nicht schlagen, aber selbst
|
||||
beidhaelftig tragen
|
||||
(4) Mehrfachvergleich wird ausgewiesen: bei ~10 Tests sind ~0,5 Zufallstreffer
|
||||
zu erwarten — ein einzelnes ✅ ist damit schwach.
|
||||
|
||||
Aufruf: python backtest_verworfene_v2.py [bars]
|
||||
"""
|
||||
import sys
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
import MetaTrader5 as mt5
|
||||
|
||||
import core.wave_rec as wr
|
||||
from backtest_cost_gate import _atr_series, kennzahlen
|
||||
from core.exit_model import LIVE, simulate
|
||||
|
||||
_BERLIN = ZoneInfo("Europe/Berlin")
|
||||
_BROKER = timezone(timedelta(hours=3))
|
||||
_UTC = timezone.utc
|
||||
_ATRMIN = 0.06
|
||||
_MIN_N = 40
|
||||
|
||||
|
||||
def _berlin(ts):
|
||||
return (datetime.fromtimestamp(int(ts), _UTC)
|
||||
.replace(tzinfo=_BROKER).astimezone(_BERLIN))
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
# Setups: jedes liefert STEHENDE Level als (ab_bar, bis_bar, level, richtung)
|
||||
# richtung: +1 = Ausbruch nach oben ist LONG, −1 = nach unten ist SHORT
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
def orb(H, L, T, A, start_h, start_m, minuten):
|
||||
"""Opening Range Breakout: Box = erste `minuten` nach dem Open, gilt bis 22:00."""
|
||||
out, tag, box = [], None, None
|
||||
for i in range(len(H)):
|
||||
b = _berlin(T[i])
|
||||
d = b.date()
|
||||
if d != tag:
|
||||
tag, box = d, None
|
||||
beginn = b.replace(hour=start_h, minute=start_m, second=0, microsecond=0)
|
||||
ende = beginn + timedelta(minutes=minuten)
|
||||
if box is None and b >= ende:
|
||||
js = [j for j in range(max(0, i - 40), i)
|
||||
if beginn <= _berlin(T[j]) < ende]
|
||||
if js:
|
||||
hi, lo = max(H[j] for j in js), min(L[j] for j in js)
|
||||
bis = i
|
||||
while bis < len(H) - 2 and _berlin(T[bis]).date() == d \
|
||||
and _berlin(T[bis]).hour < 22:
|
||||
bis += 1
|
||||
out.append((i, bis, hi, +1))
|
||||
out.append((i, bis, lo, -1))
|
||||
box = True
|
||||
return out
|
||||
|
||||
|
||||
def tagesextrem(H, L, T):
|
||||
"""PDH/PDL — Hoch/Tief des Vortages, gilt den ganzen Folgetag."""
|
||||
tage = {}
|
||||
for i in range(len(H)):
|
||||
d = _berlin(T[i]).date()
|
||||
r = tage.setdefault(d, [i, i, H[i], L[i]])
|
||||
r[1] = i; r[2] = max(r[2], H[i]); r[3] = min(r[3], L[i])
|
||||
folge = sorted(tage)
|
||||
out = []
|
||||
for k in range(1, len(folge)):
|
||||
_, _, hi, lo = tage[folge[k - 1]]
|
||||
a, b, _, _ = tage[folge[k]]
|
||||
out.append((a, b, hi, +1))
|
||||
out.append((a, b, lo, -1))
|
||||
return out
|
||||
|
||||
|
||||
def asia(H, L, T):
|
||||
"""Asian Range 00:00–08:00 Berlin, gilt 08:00 bis Tagesende."""
|
||||
out, tag, gesammelt = [], None, []
|
||||
for i in range(len(H)):
|
||||
b = _berlin(T[i])
|
||||
if b.date() != tag:
|
||||
tag, gesammelt, gesetzt = b.date(), [], False
|
||||
if b.hour < 8:
|
||||
gesammelt.append(i)
|
||||
elif gesammelt and not globals().get("_dummy"):
|
||||
if b.hour == 8 and b.minute < 5:
|
||||
hi = max(H[j] for j in gesammelt); lo = min(L[j] for j in gesammelt)
|
||||
bis = i
|
||||
while bis < len(H) - 2 and _berlin(T[bis]).date() == tag:
|
||||
bis += 1
|
||||
out.append((i, bis, hi, +1))
|
||||
out.append((i, bis, lo, -1))
|
||||
gesammelt = []
|
||||
return out
|
||||
|
||||
|
||||
def equal_hl(H, L, A, tol=0.15, gueltig=120):
|
||||
"""Equal Highs/Lows aus M5-Pivots (k=3) — zwei Extrema innerhalb tol×ATR."""
|
||||
ph = [i for i in range(3, len(H) - 3)
|
||||
if H[i] == max(H[i - 3:i + 4])]
|
||||
pl = [i for i in range(3, len(L) - 3)
|
||||
if L[i] == min(L[i - 3:i + 4])]
|
||||
out = []
|
||||
for piv, arr, d in ((ph, H, +1), (pl, L, -1)):
|
||||
for a in range(1, len(piv)):
|
||||
i0, i1 = piv[a - 1], piv[a]
|
||||
if not (6 <= i1 - i0 <= 240):
|
||||
continue
|
||||
atr = A[i1]
|
||||
if not atr or atr < _ATRMIN:
|
||||
continue
|
||||
if abs(arr[i1] - arr[i0]) > tol * atr:
|
||||
continue
|
||||
lvl = (arr[i1] + arr[i0]) / 2.0
|
||||
out.append((i1 + 4, min(i1 + 4 + gueltig, len(H) - 2), lvl, d))
|
||||
return out
|
||||
|
||||
|
||||
def squeeze_kontrolle(H, L, C, A):
|
||||
"""Kontrolle: die Squeeze-Box als stehendes Level (muss +0,124/+0,292 treffen)."""
|
||||
out = []
|
||||
for i in range(wr._SQ_N + 2, len(C) - 2):
|
||||
atr = A[i - 1]
|
||||
if not atr or atr < _ATRMIN:
|
||||
continue
|
||||
hb = H[i - wr._SQ_N - 1:i - 1]; lb = L[i - wr._SQ_N - 1:i - 1]
|
||||
if not hb or not lb:
|
||||
continue
|
||||
hi, lo = max(hb), min(lb)
|
||||
if (hi - lo) / atr > wr._SQ_MULT:
|
||||
continue
|
||||
out.append((i, i, hi + wr._SQ_K * atr, +1))
|
||||
out.append((i, i, lo - wr._SQ_K * atr, -1))
|
||||
return out
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
def lauf(levels, H, L, C, SP, A, lo, hi, modus):
|
||||
"""modus: 'markt' = Bar-Close · 'close' = Level, aber nur bei Close-Bruch
|
||||
· 'touch' = Level, Fill bei erster Beruehrung (REALITAET)."""
|
||||
ereignisse = []
|
||||
for ab, bis, lvl, d in levels:
|
||||
for i in range(max(ab, 1), min(bis + 1, len(C) - 2)):
|
||||
atr = A[i - 1]
|
||||
if not atr or atr < _ATRMIN:
|
||||
continue
|
||||
beruehrt = (H[i] >= lvl) if d > 0 else (L[i] <= lvl)
|
||||
if not beruehrt:
|
||||
continue
|
||||
gebrochen = (C[i] > lvl) if d > 0 else (C[i] < lvl)
|
||||
if modus != "touch" and not gebrochen:
|
||||
break # Level verbraucht (Docht) — kein Signal
|
||||
entry = C[i] if modus == "markt" else lvl
|
||||
ereignisse.append((i, d, entry, atr, SP[i]))
|
||||
break # ein Fill je Level
|
||||
ereignisse.sort()
|
||||
rs, frei_ab = [], lo
|
||||
for i, d, entry, atr, sp in ereignisse:
|
||||
if not (lo <= i < hi) or i < frei_ab:
|
||||
continue
|
||||
r, xb = simulate(entry, d, atr, H, L, C, i + 1, LIVE,
|
||||
timestop=True, use_tp=True, ret_bar=True)
|
||||
rs.append(r - (sp if sp > 0 else 0.0225) / atr)
|
||||
frei_ab = xb + 1
|
||||
return rs
|
||||
|
||||
|
||||
def main():
|
||||
n = int(sys.argv[1]) if len(sys.argv) > 1 else 80000
|
||||
if not mt5.initialize():
|
||||
print("MT5 nicht erreichbar"); return 1
|
||||
sym = next((c for c in ("SpotCrude", "USOIL", "WTI", "XTIUSD")
|
||||
if mt5.symbol_info(c)), None)
|
||||
b = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_M5, 0, n)
|
||||
point = mt5.symbol_info(sym).point
|
||||
mt5.shutdown()
|
||||
H = [float(x["high"]) for x in b]; L = [float(x["low"]) for x in b]
|
||||
C = [float(x["close"]) for x in b]; T = [int(x["time"]) for x in b]
|
||||
SP = [float(x["spread"]) * point for x in b]
|
||||
A = _atr_series(H, L, C)
|
||||
m = len(C) // 2
|
||||
|
||||
setups = [
|
||||
("KONTROLLE Squeeze", squeeze_kontrolle(H, L, C, A)),
|
||||
("ORB EU 15 min", orb(H, L, T, A, 10, 0, 15)),
|
||||
("ORB EU 30 min", orb(H, L, T, A, 10, 0, 30)),
|
||||
("ORB US 15 min", orb(H, L, T, A, 16, 30, 15)),
|
||||
("ORB US 30 min", orb(H, L, T, A, 16, 30, 30)),
|
||||
("PDH / PDL Bruch", tagesextrem(H, L, T)),
|
||||
("Asian Range Bruch", asia(H, L, T)),
|
||||
("Equal H/L Bruch", equal_hl(H, L, A)),
|
||||
]
|
||||
|
||||
print("=" * 104)
|
||||
print(f" VERWORFENE SETUPS — neu auf der Ausfuehrungs-Achse ({sym}, {len(C)} M5-Bars)")
|
||||
print("=" * 104)
|
||||
print(" Geprueft wird NUR, was ein STEHENDES Level + Ausbruchsrichtung hat —")
|
||||
print(" dort kann die Achse ueberhaupt wirken (Squeeze ja, Momentum nein).")
|
||||
print(" Massgeblich ist Spalte C. B steht nur da, um den Look-ahead zu zeigen.")
|
||||
print()
|
||||
print(f" {'Setup':<22} {'A) Markt H1/H2':^22} {'B) Close-Bruch H1/H2':^24} "
|
||||
f"{'C) BERUEHRUNG H1/H2':^24} n Urteil")
|
||||
print(" " + "-" * 100)
|
||||
|
||||
treffer = []
|
||||
for name, lv in setups:
|
||||
z = f" {name:<22}"
|
||||
erg = {}
|
||||
for modus in ("markt", "close", "touch"):
|
||||
k1 = kennzahlen(lauf(lv, H, L, C, SP, A, 0, m, modus))
|
||||
k2 = kennzahlen(lauf(lv, H, L, C, SP, A, m, len(C), modus))
|
||||
erg[modus] = (k1, k2)
|
||||
z += (f" {k1['oer']:>+7.3f}/{k2['oer']:>+7.3f} " if k1 and k2
|
||||
else f" {'—':^16} ")
|
||||
c1, c2 = erg["touch"]
|
||||
if not c1 or not c2:
|
||||
print(z + " —"); continue
|
||||
ok = (c1["oer"] > 0 and c2["oer"] > 0 and c1["pf"] > 1 and c2["pf"] > 1
|
||||
and c1["n"] >= _MIN_N and c2["n"] >= _MIN_N)
|
||||
z += f" {c1['n']:>4} {'✅' if ok else '❌'}"
|
||||
if c1["n"] < _MIN_N or c2["n"] < _MIN_N:
|
||||
z += " (duenn)"
|
||||
print(z)
|
||||
if ok and not name.startswith("KONTROLLE"):
|
||||
treffer.append(name)
|
||||
|
||||
print()
|
||||
print("=" * 104)
|
||||
print(f" ERGEBNIS: {len(treffer)} von {len(setups)-1} verworfenen Setups tragen "
|
||||
f"auch mit realistischem Fill")
|
||||
if treffer:
|
||||
print(f" → {', '.join(treffer)}")
|
||||
print(" ⚠ Vor jeder Konsequenz: Nachbar-Parameter pruefen und gegen die")
|
||||
print(" Kontrolle halten. Ein einzelnes ✅ bei ~7 Tests ist erwartbar.")
|
||||
else:
|
||||
print(" → Die Ablehnungen bleiben bestehen. Die Ausfuehrungs-Achse rettet")
|
||||
print(" keines dieser Setups.")
|
||||
print()
|
||||
print(" ⚠ Spalte B gegen C gelesen = der Look-ahead-Aufschlag. Ist er gross,")
|
||||
print(" war das Setup nie so gut, wie ein Level-Backtest suggeriert.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user