"""RSI ZUM VIERTEN MAL gemessen — diesmal auf der Ausfuehrungs-Achse (2026-08-19)

User: "messe ihn nochmal".

WARUM DAS KEIN BLOSSES WIEDERHOLEN IST. RSI ist im Projekt dreimal
durchgefallen (`backtest_confluence.py` als Konfluenz-Filter,
`backtest_buch_luis.py` als Divergenz, dazu der Pullback-Befund). ALLE drei
stiegen am **Bar-Close zum Marktpreis** ein. Genau diese Annahme hat am
05./06.08. Squeeze und Auto-Signal von negativ auf positiv gedreht - der
Einstiegspreis war dort +0,32 bzw. +0,61 R wert, mehr als alle Signal-Ideen
zusammen. Fuer RSI war diese Achse nie geprueft.

⚠⚠ ABER die Vorhersage ist ungünstig, und sie steht VOR dem Lauf hier: RSI ist
ein REVERSIONS-Einstieg (ueberkauft -> short). Und fuer Reversion ist die
ruhende Order gemessen SCHLECHTER, nicht besser - `backtest_candle_fade2.py`
(05.08.): "ruhende Order hilft bei Momentum-Entries, schadet bei
Reversion-Entries", weil eine Limit-Order am Extrem nur fuellt, wenn sich die
Bewegung weiter ausdehnt - also genau dann, wenn man falsch liegt. Wenn dieser
Lauf dasselbe zeigt, ist das eine Bestaetigung des Mechanismus, keine
Ueberraschung.

GEMESSEN WERDEN BEIDE LESARTEN, weil der Projekt-Befund sagt, dass die
Lehrbuch-Lesart hier invertiert ist ("RSI *gegen* die Richtung = doppelter
Edge"):
    REVERSION   RSI > hi -> SHORT,  RSI < lo -> LONG   (Lehrbuch)
    MOMENTUM    RSI > hi -> LONG,   RSI < lo -> SHORT  (die Umkehrung)
je mit drei Schwellenpaaren (70/30, 75/25, 80/20) = Nachbar-Robustheit, und je
mit zwei Ausfuehrungen (Markt am Bar-Close / ruhende Order am Extrem der
Ausloese-Bar, Fill bei BERUEHRUNG).

⚠ KEIN NACHBAU des RSI: die Serie wird inkrementell gerechnet UND an 200
Stichproben gegen die echte `core.analysis.calc_rsi` verifiziert. Weicht sie ab,
bricht das Skript ab - ein selbst gebauter Indikator waere genau die
Divergenz-Falle, die im Projekt dreimal zugeschlagen hat.

REGEL VORAB FIXIERT: tragfaehig nur, wenn eine Zelle in BEIDEN Halbjahren
OeR > 0 UND PF > 1 liefert, das 95-%-KI die Null ausschliesst UND die beiden
Nachbar-Schwellen dasselbe Vorzeichen haben.

Aufruf:  python backtest_rsi_v2.py [bars]
"""
import random
import sys

import MetaTrader5 as mt5

from backtest_cost_gate import kennzahlen
from core.analysis import calc_rsi
from core.exit_model import LIVE, simulate
from core.squeeze_scan import atr_series

PERIOD = 14
SCHWELLEN = [(70, 30), (75, 25), (80, 20)]
COOLDOWN = 12          # Bars, damit dieselbe Ueberdehnung nicht dutzendfach zaehlt


def rsi_serie(C, period=PERIOD):
    """Wilder-RSI je Bar. ⚠ Gegen `calc_rsi` verifiziert (s. `pruefe`)."""
    n = len(C)
    out = [50.0] * n
    if n < period + 1:
        return out
    g = l = 0.0
    for i in range(1, period + 1):
        d = C[i] - C[i - 1]
        g += max(d, 0.0); l += max(-d, 0.0)
    ag, al = g / period, l / period
    out[period] = 100.0 if al == 0 else 100.0 - 100.0 / (1.0 + ag / al)
    for i in range(period + 1, n):
        d = C[i] - C[i - 1]
        ag = (ag * (period - 1) + max(d, 0.0)) / period
        al = (al * (period - 1) + max(-d, 0.0)) / period
        out[i] = 100.0 if al == 0 else 100.0 - 100.0 / (1.0 + ag / al)
    return out


def pruefe(C, R, proben=200):
    """Die Serie MUSS der echten Funktion entsprechen — sonst Abbruch."""
    rnd = random.Random(42)
    schlimmster = 0.0
    for _ in range(proben):
        i = rnd.randrange(PERIOD + 5, len(C))
        schlimmster = max(schlimmster, abs(calc_rsi(C[:i + 1], PERIOD) - R[i]))
    return schlimmster


def handeln(i, d, H, L, C, SP, A, ende, modus):
    """modus 'markt' = Fill am Close von i · 'level' = ruhende Order am Extrem."""
    atr = A[i]
    if not atr or atr <= 0 or i + 1 >= ende:
        return None
    if modus == "markt":
        f, preis = i + 1, C[i]
    else:
        # Ruhende Order am Extrem der Ausloese-Bar, Fill bei BERUEHRUNG.
        preis = H[i] if d > 0 else L[i]
        f = None
        for x in range(i + 1, min(i + 13, ende)):
            if (H[x] >= preis) if d > 0 else (L[x] <= preis):
                f = x
                break
        if f is None or f + 1 >= ende:
            return None
        f += 1
    r = simulate(preis, d, atr, H, L, C, f, LIVE, timestop=True, use_tp=True)
    return r - ((SP[i] if SP[i] > 0 else 0.0225) / atr)


def lauf(H, L, C, SP, A, R, lo_i, hi_i, ende, hi, lo, lesart, modus):
    rs, letzte = [], -10**9
    for i in range(max(lo_i, PERIOD + 1), hi_i):
        if i - letzte < COOLDOWN:
            continue
        if R[i] >= hi:
            d = -1 if lesart == "reversion" else 1
        elif R[i] <= lo:
            d = 1 if lesart == "reversion" else -1
        else:
            continue
        letzte = i
        r = handeln(i, d, H, L, C, SP, A, ende, modus)
        if r is not None:
            rs.append(r)
    return rs


def zeig(lbl, k1, k2):
    def f(k):
        if not k:
            return "  --"
        ci = k.get("ci"); m = ""
        if ci:
            m = (" [{:+.3f} .. {:+.3f}]".format(ci[0], ci[1])
                 + ("" if ci[0] * ci[1] > 0 else "  ~0"))
        return "n={:>4} WR={:>3.0f}% OeR={:+.3f} PF={:.2f}{}".format(
            k["n"], k["wr"], k["oer"], k["pf"], m)
    ok = (k1 and k2 and k1["oer"] > 0 and k2["oer"] > 0 and k1["pf"] > 1
          and k2["pf"] > 1 and (k1.get("ci") or (0, 0))[0] > 0
          and (k2.get("ci") or (0, 0))[0] > 0)
    print("    {:<26} H1 {}".format(lbl, f(k1)))
    print("    {:<26} H2 {}   {}".format("", f(k2), "✅" if ok else "❌"))
    return ok


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]
    SP = [float(b["spread"]) * point for b in bars]
    A = atr_series(H, L, C)
    R = rsi_serie(C)
    N = len(C); mid = N // 2; ende = N - LIVE.max_hold - 1

    fehler = pruefe(C, R)
    print("=" * 96)
    print("  RSI({}) auf {} M5 ({} Bars) · kanonischer Exit · Echtkosten · 95-%-KI"
          .format(PERIOD, sym, N))
    print("  Serie gegen die echte `calc_rsi` geprueft: groesste Abweichung {:.2e}"
          .format(fehler))
    assert fehler < 1e-6, "RSI-Serie weicht von calc_rsi ab — Abbruch"
    print("=" * 96)

    treffer = []
    for modus, titel in (("markt", "A) MARKT am Bar-Close (= die drei alten Laeufe)"),
                         ("level", "B) ruhende Order am Extrem, Fill bei BERUEHRUNG")):
        print("\n  " + titel)
        print("  " + "-" * 92)
        for lesart in ("reversion", "momentum"):
            for hi, lo in SCHWELLEN:
                k1 = kennzahlen(lauf(H, L, C, SP, A, R, 0, mid, ende, hi, lo, lesart, modus))
                k2 = kennzahlen(lauf(H, L, C, SP, A, R, mid, ende, ende, hi, lo, lesart, modus))
                if zeig("{} {}/{}".format(lesart.upper()[:3], hi, lo), k1, k2):
                    treffer.append((modus, lesart, hi, lo))
            print("")

    print("  " + "=" * 92)
    print("    Zellen, die die vorab fixierte Regel erfuellen: {}".format(
        len(treffer) or "KEINE"))
    for t in treffer:
        print("      {}".format(t))
    print("    ⚠ Nachbar-Robustheit zusaetzlich pruefen: eine einzelne Zelle unter")
    print("      12 getesteten ist bei 5 % Irrtum die ERWARTETE Zahl an Fehlalarmen.")
    print("  " + "=" * 92)


if __name__ == "__main__":
    main()
