Zeitzonen-Bug in analyze_tageszeit.py behoben + Squeeze-Nachtsperre 05.08. dokumentiert

Zeitzone: datetime.fromtimestamp(rates["time"], UTC+3) behandelt den MT5-
Bar-Zeitstempel als echten UTC-Epoch - MT5 liefert dort aber bereits die
Broker-Wallclock. Jede Stunde war 3 h zu spaet einsortiert. Empirisch gegen
die Wallclock geprueft (letzte Bar 11:00 statt real 08:00). Helfer _berlin()
mit korrekter Konvention; Rest des Projekts war nicht betroffen (geprueft).

Neu gerechnet: ALLE VIER Tagesbloecke kippen zwischen den Haelften - es gibt
gar kein Tageszeit-Muster. Der zuvor gemeldete "Nachmittag robust negativ"
war der um 3 h verschobene Abend. Plausibilitaetstest, der es entlarvt haette:
die duennen Stunden (n~650) muessen am Marktschluss liegen, nicht nachts.

Squeeze 05.08.: 5 active-Ausbrueche zwischen 02:00 und 07:30 Berlin, alle von
auto_squeeze_skip_night geblockt; Bot war ab 01:15 flat, also alleiniger
Blocker. Mit kanonischem Exit + Echtkosten Sigma +3,56 R (~+95 EUR), davon
92 % in zwei Trades - n=5 an einem Tag ist kein Gegenbeleg zum Guard.
Messbarer Ansatz notiert: kosten- statt stundenbasiertes Gate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-05 08:07:25 +02:00
co-authored by Claude Opus 5
parent d75789ac72
commit c9f1e8d039
2 changed files with 75 additions and 20 deletions
+56 -16
View File
@@ -357,25 +357,40 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
Berliner Stunde (nicht Signal-Edge wie `backtest_hourly_split.py`), 80k M5-Bars
(17.06.2504.08.26), DST-korrekt via `zoneinfo`, 2 Hälften, Überschuss gegen den
jeweiligen Tagesschnitt (sonst färbt das Regime alle Stunden ein: H1 fiel, H2 stieg).
**Beide Hälften der Behauptung fallen durch:** Vormittag 0812 **kippt**
(H1 +0,55 / H2 2,90 Zehntel-Cent je Bar), Nachmittag 1418 ist zwar
vorzeichen-robust, aber **NEGATIV in beiden** (0,23/2,10) — also das Gegenteil
der Vermutung. Robust auf: 0/2/5/8/15/23 Uhr · robust ab: 17/21/22 Uhr — kein
Vormittag/Nachmittag-Muster, sondern verstreute Einzelstunden aus 24 Tests.
**Größenordnung macht es ohnehin unhandelbar:** die stärkste robuste Stunde
(23:00, +2,2/+3,4) sammelt über eine volle Stunde ~0,030,04 $ — der Spread allein
**ALLE VIER Tagesblöcke kippen zwischen den Hälften** — es gibt überhaupt kein
Tageszeit-Muster (Zehntel-Cent je Bar):
| Block | H1 | H2 | |
|---|---|---|---|
| Vormittag 0812 | +0,06 | 0,43 | kippt |
| Nachmittag 1418 | 1,26 | +1,96 | kippt |
| Abend 1923 | 0,39 | +1,67 | kippt |
| Nacht 0007 | +0,42 | 1,30 | kippt |
Robust auf: 2/5/20/21/23 Uhr · robust ab: 14/18/19 Uhr — verstreute Einzelstunden
aus 24 Tests, kein Vormittag/Nachmittag-Muster.
**Größenordnung macht es ohnehin unhandelbar:** die stärkste belastbare Stunde
(20:00, +1,9/+3,3) sammelt über eine volle Stunde ~0,020,04 $ — der Spread allein
ist **0,0225 $**. Selbst wenn die Drift echt wäre, ist sie kleiner als die Kosten.
⚠ Std 2:00 sticht mit +33,0 in H2 hervor, hat dort aber nur n=648 (vs. ~1740 sonst)
= dünne Sonntag-Open-/Feiertagsstunde, Ausreißer-verdächtig.
⚠⚠ **Methodik-Lehre (eigener Fehler, dokumentiert):** der ERSTE Lauf meldete
„Vormittag 0812 robust negativ" — Artefakt eines **selbst eingebauten ATR-Filters**
(`atr < 0,06` übersprungen + ATR-Normierung). In den ruhigen Morgenstunden der
älteren Periode liegt der ATR unter dem Floor → von 1.740 Bars je Stunde blieben
**138391** übrig, und zwar ausgerechnet die ungewöhnlich volatilen Morgen.
Genau die Stunden, um die es ging, waren eine verzerrte Teilstichprobe. Gleiche
Fehlerklasse wie das ORB-Selektions-Artefakt 2026-07-17 (ATR-Skip statt Floor).
⚠ Std 23 sticht mit +33,0 in H2 hervor, hat aber nur n=648 (22 Uhr: 1062, sonst
~1750) — das ist der Marktschluss-Rand, kein Befund.
⚠⚠ **ZWEI eigene Messfehler auf dem Weg dorthin — beide dokumentiert, weil sie
Fehlerklassen sind, keine Ausrutscher:**
**(1) Selbst gebauter ATR-Filter (04.08.).** Der erste Lauf meldete „Vormittag
robust negativ" — Artefakt von `atr < 0,06` übersprungen + ATR-Normierung. In den
ruhigen Morgenstunden der älteren Periode liegt der ATR unter dem Floor → von
1.740 Bars je Stunde blieben **138391** übrig, und zwar die ungewöhnlich
volatilen Morgen. Gleiche Fehlerklasse wie das ORB-Selektions-Artefakt 2026-07-17.
**Regel: ein Filter, der Bars nach einer Eigenschaft verwirft, die mit der
untersuchten Achse korreliert, ERZEUGT den Befund.**
**(2) Zeitzone um 3 h verschoben (05.08., beim Squeeze-Nachgang aufgefallen).**
`datetime.fromtimestamp(rates["time"], UTC+3)` behandelt den Bar-Zeitstempel als
echten UTC-Epoch — MT5 liefert dort aber bereits die **Broker-Wallclock**. Ergebnis:
jede Stunde war **3 Stunden zu spät** einsortiert; der zweite Lauf meldete deshalb
„Nachmittag robust negativ", was in Wahrheit der Abend war. **Richtig:**
`fromtimestamp(ts, UTC).replace(tzinfo=UTC+3).astimezone(Berlin)` (Helfer `_berlin`
im Skript). ⚠ Der Rest des Projekts war NICHT betroffen (geprüft) — `core/gaps.py`
& Co. nutzen die richtige Konvention; der Fehler steckte nur in diesem neuen Skript.
**Plausibilitätstest, der ihn entlarvt hätte: die dünnen Stunden (n≈650/1060) müssen
am Marktschluss liegen (22/23 Uhr), nicht mitten in der Nacht.**
- **Tageszeit-Gate = AUS geschaltet (User-Vorgabe 2026-07-22, `[trading] dead_hours=`
leer — bewusst GEGEN die Messung).** Jetzt **config-steuerbar** (`wave.set_dead_hours`,
Komma-Liste von Stunden; leer=aus). Gemessener Default im Code = `0,1,2,3,4,5,6,7,12,16`
@@ -1417,6 +1432,31 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
aus (dann auch nachts, unbeaufsichtigt). Einen engen Schutz-%-Stop NUR für Squeeze
wurde bewusst NICHT gebaut (enger %-Stop unter Margin-Sizing wird vom Squeeze-
Rauschen ausgelöst, 48,94-€-Tag — der Squeeze braucht Raum).
⚠⚠ **FALLBEISPIEL 05.08.2026 — der Guard hat 5 Ausbrüche geblockt, darunter den
Einstieg direkt vor einem 1,8-$-Rutsch (User-Frage „warum ist der Squeeze nicht
eingestiegen?").** Nachgerechnet mit der ECHTEN `_squeeze_one` auf den M5-Bars
(nicht aus dem Log geschlossen — der Squeeze-ZUSTAND wird nirgends geloggt, nur der
Entry): zwischen 02:00 und 07:30 Berlin feuerte 5× `active`, **alle in 07 Uhr**.
Der Bot war ab 01:15 flat, die Nachtsperre war also der ALLEINIGE Blocker (kein
Cooldown, keine offene Position). Kursverlauf: Hoch **76,373** um 03:30 → Tief
**74,548** um 04:40 = **1,825 $ in 70 min**; der Ausbruch **04:00 SHORT @ 76,11**
lag praktisch auf dem Hoch.
**Mit dem kanonischen Exit** (`exit_model.LIVE`, Echtkosten aus dem Bar-Spread —
bewusst NICHT „maximale Auslenkung", s. die Korrektur bei der 15-Minuten-Regel):
02:00 SHORT **+3,28 R** · 03:00 LONG 0,24 · 04:00 SHORT **+1,36 R** · 06:15 LONG
0,60 · 07:30 SHORT 0,24 → **Σ +3,56 R ≈ +95 € bei 2,66 Lots**.
**Trotzdem kein Argument gegen den Guard:** n=5 an EINEM Tag, und **92 % davon
stecken in zwei Trades** — ohne den 02:00er bleiben +0,29 R ≈ +12 €. Ein Einzeltag
taugt nicht als Beleg, auch nicht als widerlegender (dieselbe Lehre wie am 01.08.).
Die Guard-Begründung ruht auf 2 Halbjahren; dazu kommt der nicht-ökonomische Grund
(keine unbeaufsichtigten Nacht-Trades), der von Zahlen gar nicht berührt wird.
**Ein messbarer Ansatz ergibt sich aber:** die Nacht-Begründung ist eine
**Kosten**-Aussage (Spread÷ATR 0,320,50). In dieser Nacht lagen die realen Kosten
bei **0,110,22×ATR**, also klar UNTER der Falle — weil der ATR hoch war. Ein
**kosten-basiertes statt stunden-basiertes Gate** („blocke, wenn Spread/ATR > X")
würde genau solche Nächte durchlassen und ruhige weiter sperren. **Ungemessen**
vor dem Bau 2-Stichproben-Test gegen das Stunden-Gate; und es hebelt den
Beaufsichtigungs-Grund aus, ist also zusätzlich eine User-Entscheidung.
**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