From 1c9bb34095ffed3c843f5c7d8bb1d639d8bde892 Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Wed, 5 Aug 2026 08:25:48 +0200 Subject: [PATCH] Auto-Squeeze-Nachtsperre von 0-7 auf 3-6 Uhr verkuerzt (User-Vorgabe) Gemessen (backtest_cost_gate.py, Teil 2): es gibt KEINE toten Stunden - alle 24 Stunden sind in beiden Halbjahren positiv, keine ist beidhaelftig negativ. Std 0 ist sogar die staerkste des Tages (OeR +1,48/+1,51) und lag im alten Gate. Jedes Fenster kostet also Ertrag; die Sperre kauft ausschliesslich Beaufsichtigungs-Ruhe. Gewaehlt wurde deshalb nach PREIS, nicht nach Edge: 3-6 ist das guenstigste Fenster mit noch >=4 h Schutz (SigmaR -12,5/-62,9 gegen -42,4/-242,6 bei 0-7 = ~27 %). Freigegeben: 0, 1, 2, 7 Uhr. Die 4-h-Untergrenze ist gesetzt, nicht gemessen. Umsetzung: neues [trading] auto_squeeze_night_hours (Default 3,4,5,6, leer = aus), genutzt von _check_auto_squeeze und _check_auto_signal. _SQUEEZE_NIGHT bleibt als STATISTISCHE Nacht-Definition fuer Entry-Checkliste und B4-Monitor - dort gilt die Kostenfalle weiter und der Altdaten-Vergleich bleibt stabil. 7 Parse-Faelle getestet (Default, fehlender Schluessel, leer, Muell, Rueckwaerts-Kompatibilitaet). Live verifiziert am NEUEN Snapshot-Feld squeeze_night_hours = 3,4,5,6 und am Startup-Log; genau eine Instanz auf 8000. Offen und als Reminder hinterlegt: der Backtest modelliert keine Slippage, und die freigegebenen Stunden liegen in der duennsten Liquiditaet. Nach 15 Squeeze-Trades aus 0/1/2/7 Uhr gegen die Erwartung pruefen. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 53 ++++++++++++++++++++++--- backtest_cost_gate.py | 86 +++++++++++++++++++++++++++++++++++++++++ core/engine.py | 52 ++++++++++++++++++++----- measurement_reminder.py | 21 ++++++++++ 4 files changed, 197 insertions(+), 15 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ada8318..eb56cf2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1418,9 +1418,26 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche: ENTRY` + Telegram + Fehlschlag-Push. Snapshot `auto_squeeze`/`squeeze_entry_count`; Toggle `POST /api/autosqueeze` (`set_auto_squeeze`) = Dashboard-Button **🚀 BRK** (blau=AN, ersetzt den alten Dry-Run-AUTO-Button). **Dead-Hour-Guard = AN - (User-Vorgabe 2026-07-24, `[trading] auto_squeeze_skip_night=true`):** der Bot - eröffnet **nachts 0–7 Uhr KEINE** Squeeze-Trades (`_SQUEEZE_NIGHT=(0..7)`, - `_squeeze_skip_night` → `_check_auto_squeeze` return; kein Entry UND kein Reverse). + (User-Vorgabe 2026-07-24, `[trading] auto_squeeze_skip_night=true`) — **Fenster + seit 2026-08-05 auf `3,4,5,6` VERKÜRZT** (`[trading] auto_squeeze_night_hours`, + User-Vorgabe „verkürze auf die wirklich toten Stunden"; war fest 0–7):** der Bot + eröffnet in diesen Stunden **KEINE** Squeeze-Trades (`_squeeze_night_hours` → + `_check_auto_squeeze` return; kein Entry UND kein Reverse; leere Liste = aus). + ⚠ **`_SQUEEZE_NIGHT=(0..7)` bleibt bestehen, ist aber NICHT mehr das Gate** — es + ist jetzt nur noch die STATISTISCHE Nacht-Definition für die Entry-Checkliste + (dort gilt die Kostenfalle weiterhin, s. u.) und den Tag/Nacht-Split im + B4-Monitor (damit der Vergleich mit den Altdaten stabil bleibt). Der + Auto-Signal-Pfad nutzt dasselbe neue Fenster (ist ohnehin aus). + ⚠⚠ **Die Verkürzung ist NICHT ertrags-begründet, sondern preis-begründet** — + gemessen ist **keine einzige Nachtstunde in beiden Halbjahren negativ**, jede + geblockte Stunde kostet Ertrag (Details beim Fallbeispiel unten). 3–6 ist + schlicht das **günstigste Fenster mit noch ≥4 h Schutz**: Preis ΣR + **−12,5/−62,9 R** gegen **−42,4/−242,6** bei 0–7 = **~27 %** der bisherigen + Kosten. Freigegeben sind **0, 1, 2 und 7 Uhr**. ⚠ Std 0 ist dabei die **beste + Stunde des ganzen Tages** für den Squeeze (ØR +1,48/+1,51) — sie zu blocken war + der teuerste Einzelposten. Live verifiziert am neuen Snapshot-Feld + `squeeze_night_hours` + Startup-Log „Auto-Squeeze-Nachtsperre: 3:00, 4:00, 5:00, + 6:00". Zurück auf den alten Stand: `auto_squeeze_night_hours = 0,1,2,3,4,5,6,7`. Doppelt begründet: (1) **gemessen** unrentabel (`backtest_realcosts.py`, Spread÷Nacht- ATR 0,32–0,50×ATR, beide Halbjahre negativ = Kostenfalle) und (2) **kein Trade soll unbeaufsichtigt nachts feuern, während der User schläft** — Auslöser: „müsste @@ -1503,8 +1520,34 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche: (P(break) nachtrainiert, 15-Min-Regel aus, Trail 1,5→1,0). (3) Der **Beaufsichtigungs-Grund** („kein Trade soll feuern, während ich schlafe", User-Vorgabe 2026-07-24) ist eine Präferenz und wird von keiner Zahl berührt. - ✅ **Deshalb KEINE Änderung vorgenommen** — `auto_squeeze_skip_night` bleibt `true`. - Die Messung liegt vor, die Entscheidung ist eine Nutzungs-, keine Messfrage. + ✅ **Konsequenz (User-Vorgabe 2026-08-05 „verkürze auf die wirklich toten + Stunden"): Fenster von 0–7 auf 3–6 verkürzt** — Details oben beim Guard. + ⚠⚠ **Die Prämisse der Vorgabe hält der Messung nicht stand: es gibt KEINE toten + Stunden.** Je Stunde einzeln, sequentiell, beide Halbjahre — **alle 24 Stunden + sind in beiden Hälften positiv**, keine einzige ist beidhälftig negativ. Die + ruhigsten Nachtstunden (n über beide Hälften): 5 (41 Ausbrüche) · 4 (56) · 3 (61) + · 6 (62); die aktivsten 7 (75) · 0/2 (72) · 1 (71). ⚠ **Std 0 ist die stärkste + Stunde des gesamten Tages** (ØR +1,483/+1,510, ΣR +22,2/+86,1) — ausgerechnet sie + lag im alten Gate. + Damit ist die Auswahl **keine Ertrags-Optimierung, sondern eine Preis-Minimierung + für eine Präferenz** — und deshalb auch nicht overfitting-gefährdet: es wird keine + prädiktive Behauptung aufgestellt. Preisliste (ΣR gegen „kein Gate"): + | Fenster | Std | Preis H1 | Preis H2 | + |---|---|---|---| + | 4–5 | 2 | −3,9 | −18,1 | + | 4–6 | 3 | −6,8 | −39,9 | + | **3–6 (gewählt)** | **4** | **−12,5** | **−62,9** | + | 2–5 | 4 | −12,8 | −77,2 | + | 0–7 (alt) | 8 | −42,4 | −242,6 | + Die 4-Stunden-Untergrenze ist **gesetzt, nicht gemessen** — darunter wäre der + Schutzzweck hinfällig. Wer weniger Schutz will, nimmt 4–5 (fast gratis); wer den + alten Stand will, trägt `0,1,2,3,4,5,6,7` ein. + ⚠ **Was die Verkürzung NICHT ausräumt:** die drei Vorbehalte oben gelten + unverändert — vor allem die **nicht modellierte Slippage** (dünnes Nacht-Buch, + Tail 0,75×ATR über den Stop). Die freigegebenen Stunden 0/1/2/7 liegen mitten in + der dünnsten Zeit. **B4-Pflicht: die Squeeze-Trades aus 0/1/2/7 Uhr getrennt + mitzählen** — bleiben sie hinter der Erwartung (ØR ~+0,5 R), war die Slippage + der Grund und das Fenster gehört zurück auf 0–7. **Stop-&-Reverse = AUS (Default, `[trading] auto_squeeze_reverse=false`, `_squeeze_reverse`; User-Vorgabe 2026-07-17):** War kurz scharf, dann abgeschaltet — nur der **flat-Entry ist gemessen-validiert**, der Reverse ist unbelegt, verdoppelt diff --git a/backtest_cost_gate.py b/backtest_cost_gate.py index 2e38ca1..a5c157f 100644 --- a/backtest_cost_gate.py +++ b/backtest_cost_gate.py @@ -346,6 +346,92 @@ def main(): print(" waehrend ich schlafe\") ist eine Praeferenz und wird hier NICHT beruehrt.") print() + # ── TEIL 2: welche Nachtstunden sind WIRKLICH tot? ───────────────────── + print("=" * 100) + print(" TEIL 2 — WELCHE Nachtstunden tragen den Block? (je Stunde, sequentiell)") + print("=" * 100) + print(f" {'Std':>3} {'Kosten':>7} | {'n H1':>5} {'OeR H1':>8} {'SigmaR H1':>10}" + f" | {'n H2':>5} {'OeR H2':>8} {'SigmaR H2':>10} Urteil") + print(" " + "-" * 92) + beide_neg = [] + for s in range(24): + k1 = kennzahlen(sequentiell(K, H, L, C, lambda e, s=s: e["stunde"] == s, 0, mid)) + k2 = kennzahlen(sequentiell(K, H, L, C, lambda e, s=s: e["stunde"] == s, mid, N)) + ks_ = [e["kosten"] for e in E if e["stunde"] == s] + kmed = sorted(ks_)[len(ks_) // 2] if ks_ else float("nan") + if not k1 or not k2: + continue + neg = k1["sr"] < 0 and k2["sr"] < 0 + if neg: + beide_neg.append(s) + print(f" {s:>3} {kmed:>7.3f} | {k1['n']:>5} {k1['oer']:>+8.3f} {k1['sr']:>+10.1f}" + f" | {k2['n']:>5} {k2['oer']:>+8.3f} {k2['sr']:>+10.1f} " + f"{'beide NEGATIV' if neg else ''}") + print() + print(f" In BEIDEN Haelften negativ: {beide_neg or 'keine'}") + print(" ⚠ Diese Liste NICHT direkt als Gate verwenden — 24 Stunden = 24 Tests,") + print(" das waere in-sample-zirkulaer (dokumentierter Fehler, backtest_realcosts).") + print(" Deshalb unten nur ZUSAMMENHAENGENDE Fenster: ein Fenster hat 1 Freiheits-") + print(" grad statt 24 und laesst sich nicht auf das Rauschen zurechtlegen.") + print() + + # ── Fenster-Sweep, Regel vorab fixiert ──────────────────────────────── + print(" ZUSAMMENHAENGENDE NACHT-FENSTER (sequentiell, EIN Slot)") + print(" REGEL (vor dem Lauf fixiert): das Fenster muss das heutige 0-7 im SigmaR") + print(" in BEIDEN Haelften schlagen. Bei mehreren Treffern gewinnt das LAENGSTE") + print(" — der Beaufsichtigungs-Grund hat Wert, der in keiner Zahl steht.") + print(f" {'Fenster':<18}{'H1 n WR OeR PF SigmaR':>32}" + f" |{'H2 n WR OeR PF SigmaR':>32}") + print(" " + "-" * 96) + fenster = [("kein Gate", ())] + for a in range(0, 8): + for b in range(a, 8): + fenster.append((f"{a}-{b} Uhr", tuple(range(a, b + 1)))) + b1, b2 = ergebnis["ohne Gate (Basis)"] + zeilen = [] + for lbl, hh in fenster: + if not hh: + continue + k1 = kennzahlen(sequentiell(K, H, L, C, + lambda e, hh=hh: e["stunde"] not in hh, 0, mid)) + k2 = kennzahlen(sequentiell(K, H, L, C, + lambda e, hh=hh: e["stunde"] not in hh, mid, N)) + if not k1 or not k2: + continue + p1 = k1["sr"] - b1["sr"]; p2 = k2["sr"] - b2["sr"] # Preis (negativ) + nblk = sum(1 for e in E if e["stunde"] in hh) + zeilen.append({"lbl": lbl, "hh": hh, "h": len(hh), "p1": p1, "p2": p2, + "n": nblk, "je": (p1 + p2) / len(hh)}) + + print(" PREIS DER BEAUFSICHTIGUNG — was kostet ein Fenster gegenueber KEINEM Gate?") + print(" ⚠ Keine Stunde ist in beiden Haelften negativ. Es gibt also keine") + print(" 'toten Stunden' zu streichen — jedes Fenster KOSTET. Die Frage ist") + print(" nur, wieviel Schlaf-Schutz man wie teuer einkauft. Das ist eine") + print(" PREIS-Minimierung fuer eine Praeferenz, KEINE Edge-Behauptung —") + print(" deshalb ist die Auswahl hier auch nicht overfitting-gefaehrdet.") + print(f" {'Fenster':<12}{'Std':>4}{'geblockt':>10}{'Preis H1':>10}{'Preis H2':>10}" + f"{'SigmaR/Std':>12}") + print(" " + "-" * 60) + for z in sorted(zeilen, key=lambda z: -z["je"])[:12]: + print(f" {z['lbl']:<12}{z['h']:>4}{z['n']:>10}{z['p1']:>+10.1f}" + f"{z['p2']:>+10.1f}{z['je']:>+12.1f}") + heute = next(z for z in zeilen if z["lbl"] == "0-7 Uhr") + print(f" {'... heute:':<12}{heute['h']:>4}{heute['n']:>10}{heute['p1']:>+10.1f}" + f"{heute['p2']:>+10.1f}{heute['je']:>+12.1f} <- 0-7 Uhr") + print() + # Auswahl: guenstigstes Fenster, das noch >=4 Stunden Schlaf-Schutz gibt. + # 4 h ist gesetzt, nicht gemessen — darunter waere der Schutzzweck hinfaellig. + kand = [z for z in zeilen if z["h"] >= 4] + best = max(kand, key=lambda z: z["je"]) + print(f" => EMPFEHLUNG (>=4 h Schutz, guenstigstes Fenster): **{best['lbl']}**") + print(f" Preis H1 {best['p1']:+.1f} / H2 {best['p2']:+.1f} R " + f"gegen heute {heute['p1']:+.1f} / {heute['p2']:+.1f}") + print(f" = {100*abs(best['p1'])/abs(heute['p1']):.0f} % / " + f"{100*abs(best['p2'])/abs(heute['p2']):.0f} % der heutigen Kosten," + f" bei {best['h']} statt 8 Stunden") + print(f" freigegeben: {[s for s in range(8) if s not in best['hh']]} Uhr") + print() + if __name__ == "__main__": main() diff --git a/core/engine.py b/core/engine.py index b4f7c3b..7d5a88b 100644 --- a/core/engine.py +++ b/core/engine.py @@ -77,7 +77,13 @@ _PAT_W = 0.25 # Verdict-Gewicht der Chartmuster-Stimme (bestaetigt; `form # letzten 4.000 `verdict_votes`: Median der uebrigen Gewichte 4,50 # → 0,25/(4,50+0,25) = 5,3 % Anteil an der Bias-Nadel (vorher 1,0 # = 18,2 %). Begruendung + Beleglage bei der Stimme in `_verdict`. -_SQUEEZE_NIGHT = (0, 1, 2, 3, 4, 5, 6, 7) # Auto-Squeeze-Nacht-Sperre (Kostenfalle) +_SQUEEZE_NIGHT = (0, 1, 2, 3, 4, 5, 6, 7) # STATISTISCHE Nacht-Definition +# ⚠ `_SQUEEZE_NIGHT` ist NICHT mehr das Entry-Gate — es bleibt die Definition von +# „Nacht" für die Entry-Checkliste (dort gilt die Kostenfalle: über ALLE Nacht-Bars +# 0,333×ATR) und für den Tag/Nacht-Split im B4-Monitor (damit der Vergleich mit den +# Altdaten stabil bleibt). Welche Stunden der Auto-Squeeze wirklich AUSSETZT, steht +# in `[trading] auto_squeeze_night_hours` (s. `_squeeze_night_hours`). +_SQUEEZE_NIGHT_DEFAULT = "3,4,5,6" # gemessen günstigstes ≥4-h-Fenster, s. u. _SQUEEZE_SR_COOLDOWN_S = 180.0 # nach S/R-Close kein Auto-Squeeze in GLEICHER Richtung # (sonst Close-„Level hält" vs. Entry-„Level bricht"- # Roundtrip in 1 s; 2026-07-17) @@ -310,6 +316,16 @@ class TradingEngine: # auto_squeeze=true` aktiviert. Dedup über (dir, level) des Ausbruchs. self._auto_squeeze = self.cfg["trading"].get("auto_squeeze", "false").lower() == "true" self._squeeze_skip_night = self.cfg["trading"].get("auto_squeeze_skip_night", "true").lower() == "true" + # ── WELCHE Stunden ausgesetzt werden (2026-08-05, gemessen `backtest_cost_gate.py`). + # War fest 0–7. Die Messung zeigt: KEINE Stunde ist in beiden Halbjahren negativ — + # es gibt keine „toten Stunden". Jedes Fenster kostet also Ertrag; die Sperre kauft + # ausschließlich Beaufsichtigungs-Ruhe. 3–6 Uhr ist das günstigste Fenster mit noch + # ≥4 h Schutz: Preis ΣR −12,5/−62,9 R gegen −42,4/−242,6 bei 0–7 (≈ 27 %). + # Leere Liste = wirkt wie `auto_squeeze_skip_night=false`. + self._squeeze_night_hours = tuple( + int(x) for x in str(self.cfg["trading"].get( + "auto_squeeze_night_hours", _SQUEEZE_NIGHT_DEFAULT)).split(",") + if x.strip().isdigit() and 0 <= int(x) <= 23) # Stop-&-Reverse nur, wenn explizit an (Default aus) — der Reverse ist unbelegt; # der flat-Entry bleibt davon unberührt (der validierte Teil). self._squeeze_reverse = self.cfg["trading"].get("auto_squeeze_reverse", "false").lower() == "true" @@ -564,6 +580,9 @@ class TradingEngine: self._threads.append(t) log.info(f"Engine gestartet · Symbol {self.data.symbol} · TF {saved_tf}") + log.info("Auto-Squeeze-Nachtsperre: " + ( + ", ".join(f"{h}:00" for h in self._squeeze_night_hours) + if (self._squeeze_skip_night and self._squeeze_night_hours) else "AUS")) return True def stop(self): @@ -1047,12 +1066,15 @@ class TradingEngine: if d not in ("LONG", "SHORT") or lvl is None: return sig = (d, round(float(lvl), 2)) - # ── Dead-Hour-Guard (0–7 Uhr Berlin, 2026-07-17): nachts KOMPLETT inaktiv — - # kein Entry UND kein Reverse (der Squeeze ist in dünner Nacht-Liquidität in - # BEIDE Richtungen unzuverlässig; die 3 Nacht-Squeezes waren die ganze - # Verlustquelle). Setzt keine Dedup-Marker → feuert nach 08:00, falls noch - # aktiv. `auto_squeeze_skip_night=false` schaltet aus. - if self._squeeze_skip_night and datetime.now(_BERLIN).hour in _SQUEEZE_NIGHT: + # ── Nacht-Guard (Fenster aus `auto_squeeze_night_hours`, Default 3–6 Uhr + # Berlin; war bis 2026-08-05 fest 0–7): kein Entry UND kein Reverse. Setzt + # keine Dedup-Marker → feuert nach dem Fenster, falls noch aktiv. + # ⚠ Der Guard ist NICHT ertrags-begründet: gemessen ist keine Nachtstunde in + # beiden Halbjahren negativ, jede geblockte Stunde KOSTET (`backtest_cost_ + # gate.py`). Er kauft Beaufsichtigungs-Ruhe — 3–6 ist die günstigste Variante + # davon. `auto_squeeze_skip_night=false` (oder leere Liste) schaltet ihn aus. + if (self._squeeze_skip_night + and datetime.now(_BERLIN).hour in self._squeeze_night_hours): return # ── News-Blackout (Handbuch-Regel, NICHT gemessen): 15 min um ein # High-Impact-Event (FOMC/NFP/CPI/EIA/OPEC) kein autonomer Entry. Setzt @@ -1179,9 +1201,14 @@ class TradingEngine: conf = sig_snap.get("conf_pct") or 0 if conf < self._auto_signal_min_conf: return # zu schwach (Dedup NICHT setzen) - # Nacht-Sperre (gemessene Kostenfalle 0–7 Uhr Berlin) — kein Dedup-Marker, - # damit das Signal nach 08:00 noch feuern kann, falls es dann noch steht. - if self._auto_signal_skip_night and datetime.now(_BERLIN).hour in _SQUEEZE_NIGHT: + # Nacht-Sperre — dasselbe Fenster wie beim Auto-Squeeze + # (`auto_squeeze_night_hours`, Default 3–6). Kein Dedup-Marker, damit das + # Signal danach noch feuern kann, falls es dann noch steht. + # ⚠ Anders als beim Squeeze ist die Kostenfalle für die WELLEN-Population + # gemessen einschlägig (alle Nacht-Bars 0,333×ATR) — hier ist der Guard also + # auch ertrags-begründet. Der Pfad ist ohnehin aus (`auto_signal=false`). + if (self._auto_signal_skip_night + and datetime.now(_BERLIN).hour in self._squeeze_night_hours): return # News-Blackout (s. `_check_auto_squeeze`) — kein Dedup-Marker. if self.calendar.blackout(): @@ -3330,6 +3357,11 @@ class TradingEngine: "sr_close_pbreak": self._sr_close_pbreak, "sr_close_count": self._sr_close_count, "auto_squeeze": self._auto_squeeze, + # Nacht-Fenster mitliefern: nach `restart_server.bat` ist ein NEUES + # Snapshot-Feld der einzige verlässliche Beleg, dass wirklich der neue + # Python-Code läuft (der Batch hat schon 2× still nicht neu gestartet). + "squeeze_night_hours": list(self._squeeze_night_hours) + if self._squeeze_skip_night else [], "squeeze_entry_count": self._squeeze_entry_count, "auto_signal": self._auto_signal, "auto_signal_min_conf": self._auto_signal_min_conf, diff --git a/measurement_reminder.py b/measurement_reminder.py index 54e94e2..2d447b8 100644 --- a/measurement_reminder.py +++ b/measurement_reminder.py @@ -145,6 +145,27 @@ CHECKS = [ "→ `auto_squeeze=false` (B5-Rückbau, kein Ego). Sonst weiterlaufen " "lassen. Zahlen: /api/squeeze_monitor bzw. Statistik-Tab.", }, + { + "key": "night_window_0127", + "title": "Nacht-Fenster 3–6: tragen die freigegebenen Stunden 0/1/2/7?", + "why": "Am 05.08. wurde die Auto-Squeeze-Nachtsperre von 0–7 auf 3–6 verkürzt " + "(`backtest_cost_gate.py`: keine Nachtstunde ist beidhälftig negativ, " + "das alte Gate kostete ΣR −42/−243 R). Der Backtest modelliert aber " + "KEINE Slippage — und genau die freigegebenen Stunden liegen in der " + "dünnsten Liquidität (gemessener Tail: 0,75×ATR über den Stop hinaus). " + "Das ist die einzige offene Bedrohung des Befunds, und sie lässt sich " + "nur live prüfen.", + "have": lambda: _q("SELECT COUNT(*) FROM trades WHERE setup LIKE 'SQUEEZE%' " + "AND pnl IS NOT NULL AND entry_time >= " + "strftime('%s','2026-08-05') AND " + "CAST(strftime('%H', entry_time, 'unixepoch', 'localtime') " + "AS INTEGER) IN (0,1,2,7)"), + "need": 15, "unit": "Squeeze-Trades aus 0/1/2/7 Uhr seit 05.08.", + "cmd": "Erwartung aus dem Backtest: diese Stunden gehören zu den besseren " + "(Std 0 ist die STÄRKSTE des Tages, ØR +1,48/+1,51). Liegen sie live " + "deutlich darunter oder im Minus, war die Slippage der Grund → " + "`auto_squeeze_night_hours = 0,1,2,3,4,5,6,7` zurücksetzen.", + }, # (erledigt 2026-08-04: „M30-Level-Umstellung live gegenprüfen" — Ertrag JE LOT # +17,53 → +36,92 €, Ø-Move je Trade 0,202 → 0,427 $, also verdoppelt wie im # Backtest erwartet. ⚠ Normierung war entscheidend: unnormiert sah HEUTE wie der