#!/usr/bin/env python3
"""Ist die Richtung der NAECHSTEN Kerze vorhersagbar? (User-Frage 2026-08-17)

Der Pfeil an der aktuellen Kerze zeigt heute die ABPRALL-Wahrscheinlichkeit des
naechsten S/R-Levels (`AR;L;…`) — so ist er seit v1.32 (31.07.) entworfen. Der
User moechte stattdessen die Richtung der naechsten Kerze.

⚠⚠ WARUM DAS NICHT EINFACH UMGEBAUT WIRD. Die freie Richtungsfrage ist im
Projekt mehrfach als Muenzwurf gemessen (`analyze_reversal.py`: AUC 0,499-0,509
out-of-sample und **in-sample nur 0,52** — die Merkmale enthalten keine
Richtungsinformation; Live-Richtung 48-51 % ueber 1.071 Episoden). Genau deshalb
zeigt der Pfeil eine BEDINGTE Groesse (haelt DIESES Level?), die out-of-sample
kalibriert ist (AUC 0,65).

⚠ ABER: gemessen wurden bisher Horizonte von 60-360 Minuten. Die **naechste
M5-Kerze** ist ein anderer, kuerzerer Horizont — auf Ein-Bar-Ebene gibt es
Mikrostruktur-Effekte (Mean Reversion), die dort anders wirken koennen. Das ist
offen und wird hier gemessen, statt aus Analogie abgelehnt.

VORAB FIXIERTE REGEL: ein Praediktor taugt, wenn er
  (1) in BEIDEN Haelften eine Trefferquote > 50 % hat,
  (2) deren 95-%-Intervall die 50 % ausschliesst, und
  (3) die mittlere Bewegung in Vorhersagerichtung den SPREAD schlaegt.
Punkt (3) ist die haerteste Huerde: eine M5-Kerze bewegt sich im Median ~0,10 $,
der Spread ist ~0,0225 $ — eine Trefferquote knapp ueber 50 % zahlt ihn nicht.

Aufruf:  python backtest_naechste_kerze.py [bars]
"""
from __future__ import annotations

import random
import sys

import MetaTrader5 as mt5

from core.squeeze_scan import atr_series


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


def anteil_ci(tr, n, seed=5, w=2000):
    if n < 30:
        return None, None
    r = random.Random(seed); p = tr / n
    b = sorted(sum(1 if r.random() < p else 0 for _ in range(n)) / n for _ in range(w))
    return 100 * b[int(.025 * w)], 100 * b[int(.975 * w)]


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)
    point = mt5.symbol_info(sym).point
    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]; O = [float(b["open"]) for b in bars]
    SP = [float(b["spread"]) * point for b in bars]
    A = atr_series(H, L, C)
    ef, es = ema(C, 12), ema(C, 50)

    beweg = sorted(abs(C[i + 1] - C[i]) for i in range(len(C) - 1))
    med_bew = beweg[len(beweg) // 2]
    med_sp = sorted(SP)[len(SP) // 2]
    print(f"\n  {sym} · {len(C)} M5-Bars")
    print(f"  Median-Bewegung einer M5-Kerze: {med_bew:.4f} $ · Median-Spread "
          f"{med_sp:.4f} $ = {100*med_sp/med_bew:.0f} % davon")
    print(f"  ⚠ Eine Trefferquote muss diesen Anteil SCHLAGEN, nicht nur > 50 % sein.\n")

    # Praediktoren: jeder gibt +1 / -1 / 0 fuer die Richtung der NAECHSTEN Kerze
    def p_ema(i):        # Trendfolge (das Wellen-Signal)
        d = ef[i] - es[i]
        return 0 if not A[i] or abs(d) < 0.15 * A[i] else (1 if d > 0 else -1)

    def p_mom3(i):       # Momentum-Fortsetzung
        m = C[i] - C[i - 3]
        return 0 if not A[i] or abs(m) < 0.1 * A[i] else (1 if m > 0 else -1)

    def p_kerze(i):      # letzte Kerze setzt sich fort
        b = C[i] - O[i]
        return 0 if b == 0 else (1 if b > 0 else -1)

    def p_kerze_geg(i):  # letzte Kerze kehrt um (Mean Reversion auf 1 Bar)
        return -p_kerze(i)

    def p_docht(i):      # langer Docht oben -> abwaerts (Lehrbuch)
        sp = H[i] - L[i]
        if sp <= 0 or not A[i] or sp < 0.5 * A[i]:
            return 0
        ob = H[i] - max(O[i], C[i]); un = min(O[i], C[i]) - L[i]
        if ob > 2 * un and ob > 0.3 * sp:
            return -1
        if un > 2 * ob and un > 0.3 * sp:
            return 1
        return 0

    praed = (("EMA-Trend (Wellen-Signal)", p_ema),
             ("Momentum 3 Bars", p_mom3),
             ("letzte Kerze setzt fort", p_kerze),
             ("letzte Kerze kehrt um", p_kerze_geg),
             ("langer Docht (Lehrbuch)", p_docht))

    mid = len(C) // 2
    print(f"  {'Praediktor':<28}{'Haelfte':>8}{'n':>8}{'Treffer':>9}"
          f"{'95-%-KI':>16}{'Ø Bew. in Richtung':>20}")
    for name, fn in praed:
        for lbl, lo, hi in (("H1", 60, mid), ("H2", mid, len(C) - 2)):
            tr = ges = unentschieden = 0; summe = 0.0
            for i in range(lo, hi):
                if not A[i]:
                    continue
                d = fn(i)
                if d == 0:
                    continue
                bew = (C[i + 1] - C[i]) * d
                # ⚠⚠ UNVERAENDERTE Schlusskurse AUSSCHLIESSEN. Der erste Entwurf
                #   zaehlte sie als Fehltreffer — dadurch lagen ein Praediktor UND
                #   sein eigenes Gegenteil beide unter 50 % (43,4 + 45,8 = 89,2),
                #   was arithmetisch unmoeglich ist und den Fehler verriet.
                #   Real sind ~11 % der M5-Kerzen unveraendert.
                if bew == 0:
                    unentschieden += 1
                    continue
                ges += 1; summe += bew
                if bew > 0:
                    tr += 1
            if ges < 100:
                continue
            u, o = anteil_ci(tr, ges)
            q = 100 * tr / ges
            flag = "" if u is None or (u <= 50 <= o) else ("  ✅" if u > 50 else "  ❌")
            print(f"  {name:<28}{lbl:>8}{ges:>8}{q:>8.1f}%"
                  f"  [{u:>4.1f} … {o:>4.1f}]{summe/ges:>+17.4f} $" + flag)
        print()

    print("  ── LESEHILFE ──────────────────────────────────────────────────")
    print(f"    Die Spalte ganz rechts ist die Ø-Bewegung in Vorhersagerichtung.")
    print(f"    Sie muss den halben Spread ({med_sp/2:.4f} $) schlagen, damit ein")
    print(f"    Trade ueberhaupt kostendeckend waere — und den ganzen ({med_sp:.4f} $)")
    print(f"    fuer Ein- UND Ausstieg.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
