#!/usr/bin/env python3
"""Kursdrift nach Tageszeit — steigt WTI nachmittags und fällt morgens? (2026-08-04)

User-Beobachtung: „der Kurs sinkt morgens eher und steigt nachmittags."

⚠ Das ist eine ANDERE Frage als `backtest_hourly_split.py`. Dort wurde gemessen,
wie gut das SIGNAL je Stunde abschneidet (Edge nach Kosten). Hier geht es um die
rohe Richtungsdrift des Kurses selbst — unabhängig von jedem Signal.

Methodik und ihre Fallstricke, alle bewusst adressiert:
 1. **Gesamt-Drift herausrechnen.** Fiel der Markt im Zeitraum, sehen ALLE Stunden
    negativ aus. Deshalb wird zusätzlich der Überschuss gegen den Tagesmittelwert
    ausgewiesen — nur der ist eine Tageszeit-Aussage.
 2. **Zwei Stichproben.** 24 Stunden = 24 Tests; bei 5 % Fehlalarmquote sieht rund
    eine Stunde zufällig „signifikant" aus. Nur was in BEIDEN Hälften dasselbe
    Vorzeichen hat, zählt.
 3. **KEIN ATR-Filter, KEINE ATR-Normierung** — der erste Entwurf hatte beides und
    erzeugte damit genau den Befund, den er messen sollte (s. `auswerten`).
 4. **Echte Zeitzone.** Broker-Zeit ist UTC+3 (ganzjährig), Berlin wechselt
    zwischen UTC+1 und +2 — über eine lange Historie ist ein fester Versatz von
    1 h falsch. Umrechnung daher über `zoneinfo`.

Aufruf:  python analyze_tageszeit.py [bars]
"""
import sys
from collections import defaultdict
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo

import MetaTrader5 as mt5

_BERLIN = ZoneInfo("Europe/Berlin")
_BROKER = timezone(timedelta(hours=3))      # Pepperstone: UTC+3 ganzjährig
_UTC = timezone.utc


def _berlin(ts):
    """Bar-Zeitstempel → echte Berliner Zeit (DST-korrekt).

    ⚠⚠ FEHLERQUELLE, hier einmal falsch gemacht (Fix 2026-08-05): MT5 liefert
    `rates["time"]` so, dass `fromtimestamp(ts, UTC)` bereits die BROKER-Wallclock
    zeigt — die Zahl ist KEIN echter UTC-Epoch. Ein `fromtimestamp(ts, UTC+3)`
    behandelt sie fälschlich als echten Epoch und landet **3 Stunden zu spät**.
    Empirisch geprüft: letzte M5-Bar ergab so 11:00, während es real 08:00 war.
    Richtig ist: die Wallclock als UTC lesen, dann als UTC+3 ETIKETTIEREN und
    erst danach nach Berlin umrechnen.
    """
    return (datetime.fromtimestamp(int(ts), _UTC)
            .replace(tzinfo=_BROKER).astimezone(_BERLIN))


def _atr_series(H, L, C, p=14):
    t = [0.0]
    for i in range(1, len(C)):
        t.append(max(H[i] - L[i], abs(H[i] - C[i - 1]), abs(L[i] - C[i - 1])))
    return [(sum(t[max(1, i - p + 1):i + 1]) / max(1, len(t[max(1, i - p + 1):i + 1])))
            if i else None for i in range(len(C))]


def auswerten(bars, H, L, C, A, lo, hi):
    """→ {stunde: [Rendite je Bar in DOLLAR]}

    ⚠⚠ KEIN ATR-Filter und KEINE ATR-Normierung mehr (Korrektur 2026-08-04).
    Der erste Entwurf übersprang Bars mit `atr < 0,06` und teilte durch den
    rollierenden M5-ATR. Beides klang vernünftig, erzeugte aber eine massive
    **Selektionsverzerrung**: in den ruhigen Nacht-/Morgenstunden der älteren
    Periode liegt der ATR unter dem Floor, also blieben dort nur die
    ungewöhnlich VOLATILEN Tage übrig — von 1.740 vorhandenen Bars je Stunde
    kamen nur 138–391 in die Auswertung. Genau die Stunden, um die es geht,
    waren damit eine verzerrte Teilstichprobe.
    Jetzt: rohe Dollar-Rendite je Bar, ALLE Bars. Die Regime-Unterschiede
    zwischen den Hälften fängt der Abzug des jeweiligen Tagesmittels ab.
    """
    out = defaultdict(list)
    for i in range(max(lo, 1), hi):
        # Broker-Epoch → echte Berliner Stunde (DST-korrekt)
        t = _berlin(bars[i]["time"])
        out[t.hour].append(C[i] - C[i - 1])
    return out


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)
    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]
    A = _atr_series(H, L, C)
    N = len(C); mid = N // 2
    t0 = _berlin(bars[0]["time"])
    t1 = _berlin(bars[-1]["time"])

    h1 = auswerten(bars, H, L, C, A, 0, mid)
    h2 = auswerten(bars, H, L, C, A, mid, N)

    def mittel(d, s):
        v = d.get(s) or []
        return (sum(v) / len(v), len(v)) if v else (0.0, 0)

    # Tagesmittel je Hälfte — dagegen wird der Überschuss gerechnet
    alle1 = [x for v in h1.values() for x in v]
    alle2 = [x for v in h2.values() for x in v]
    m1 = sum(alle1) / len(alle1) if alle1 else 0.0
    m2 = sum(alle2) / len(alle2) if alle2 else 0.0

    print("=" * 86)
    print(f"  KURSDRIFT NACH TAGESZEIT — {sym} M5, {N} Bars")
    print(f"  {t0:%d.%m.%Y} bis {t1:%d.%m.%Y} · Berliner Zeit (DST-korrekt) · 2 Hälften")
    print(f"  Werte = Ø Rendite je 5-Min-Bar in US-Dollar (×1000 = Zehntel-Cent)")
    print(f"  Gesamt-Drift je Bar: H1 {m1*1000:+.3f} · H2 {m2*1000:+.3f} (wird abgezogen)")
    print("=" * 86)
    print(f"  {'Std':>3} {'n H1':>6} {'H1 roh':>9} {'H1 Übersch.':>12} "
          f"{'n H2':>6} {'H2 roh':>9} {'H2 Übersch.':>12}   Urteil")
    print("-" * 86)
    robust_auf, robust_ab = [], []
    for s in range(24):
        a1, n1 = mittel(h1, s); a2, n2 = mittel(h2, s)
        if n1 < 50 or n2 < 50:
            continue
        u1 = (a1 - m1) * 1000; u2 = (a2 - m2) * 1000
        if u1 > 0 and u2 > 0:
            urteil = "beide AUF"; robust_auf.append((s, u1, u2))
        elif u1 < 0 and u2 < 0:
            urteil = "beide AB"; robust_ab.append((s, u1, u2))
        else:
            urteil = "kippt"
        print(f"  {s:>3} {n1:>6} {a1*1000:>+9.3f} {u1:>+12.3f} "
              f"{n2:>6} {a2*1000:>+9.3f} {u2:>+12.3f}   {urteil}")

    print("\n" + "=" * 86)
    print("  ROBUST (gleiches Vorzeichen des Überschusses in BEIDEN Hälften)")
    print("=" * 86)
    print("  aufwärts: " + (", ".join(f"{s}:00 ({u1:+.2f}/{u2:+.2f})"
                                      for s, u1, u2 in robust_auf) or "keine"))
    print("  abwärts : " + (", ".join(f"{s}:00 ({u1:+.2f}/{u2:+.2f})"
                                      for s, u1, u2 in robust_ab) or "keine"))
    # Direkter Test der Behauptung: Vormittag gegen Nachmittag
    def block(d, m, von, bis):
        v = [x for s in range(von, bis) for x in (d.get(s) or [])]
        return ((sum(v) / len(v) - m) * 1000, len(v)) if v else (0.0, 0)
    print("\n  Die Behauptung direkt geprüft (Überschuss gegen den Tagesschnitt):")
    for lbl, von, bis in (("Vormittag 08–12", 8, 12), ("Nachmittag 14–18", 14, 18),
                          ("Abend 19–23", 19, 23), ("Nacht 00–07", 0, 7)):
        b1, c1 = block(h1, m1, von, bis); b2, c2 = block(h2, m2, von, bis)
        ok = "robust" if (b1 > 0) == (b2 > 0) else "KIPPT"
        print(f"     {lbl:<18} H1 {b1:>+7.3f} (n={c1:>6})   "
              f"H2 {b2:>+7.3f} (n={c2:>6})   {ok}")
    print("\n  ⚠ 24 Stunden = 24 Tests: bei 5 % Fehlalarmquote sieht rund EINE Stunde")
    print("    zufällig auffällig aus. Nur beidhälftig gleiche Vorzeichen zählen —")
    print("    und auch die sind ohne Kosten gerechnet (Spread ~0,265×ATR!).")
    print()


if __name__ == "__main__":
    main()
