#!/usr/bin/env python3
"""Trailing breiter (Richtung M15) — haelt der M5-Befund auf M1-Aufloesung?

ANLASS (2026-08-11, User: „sollten wir das Trailing auf M15 setzen?"). Auslöser
war ein Trade, der nach ~1×ATR Ruecklauf auf Einstand ausgestoppt wurde — bei
Trail 1,0×ATR_M5 ist das zwangslaeufig.

⚠⚠ WAS SCHON GEMESSEN IST (und die Frage NICHT beantwortet):
  · `backtest_exit_atr_tf.py` (08.08.) hat den GESAMTEN Exit auf ATR_M15 gestellt
    → schlechter in beiden Haelften, Tail-Risiko verdreifacht. ⚠ Aber `simulate()`
    nimmt EINEN atr fuer SL, Trail, TP und Breakeven — dort wurde also alles
    mitverbreitert. Das ist NICHT „nur das Trailing".
  · `backtest_trailmult.py` (31.07.) hat `mult` bei FIXEM SL 2,0 gesweept
    → enger ist monoton besser (1,0: +80/+1360 · 1,5: −220/+1305 · 2,0: −394/+1198).
    Das trifft die Frage — aber es lief auf **M5-Bars**.
⚠⚠ UND GENAU DA SITZT DER ZWEIFEL: `analyze_trail_resolution.py` (05.08.) hat
gezeigt, dass eine Bar-Simulation **enge Trails ueberschaetzt und weite
unterschaetzt**; das Vorzeichen kippt bei ~0,3×ATR. Bei 1,5×ATR meldete M5
−0,038, M1 dagegen **+0,028** — ein Vorzeichenwechsel. Der Sweep, der „enger ist
besser" sagt, ist also mit einer Verzerrung GEGEN weite Trails gemessen.

DIESE MESSUNG kontrolliert genau das:
  · **dieselben Einstiege**, derselbe ATR (aus M5) → der Trail-Abstand ist in
    DOLLAR identisch, nur die Aufloesung des Exits unterscheidet sich.
  · Zeitgrenzen in WALL-CLOCK gleich gehalten: `max_hold` 200 M5-Bars = 1000
    M1-Bars, `timestop` 24 = 120. ⚠ Der Altbestand liess 200 stehen und kappte
    M1-Trades damit nach 200 statt 1000 Minuten.
  · zwei Populationen (Squeeze + Welle), zwei Haelften, Echtkosten.
  · `mult = 1.86` ist die „Trailing auf M15"-Variante: Median ATR_M15/ATR_M5.

⚠ ENTSCHEIDUNGSREGEL, VORAB: ein breiterer Trail wird nur empfohlen, wenn er auf
**M1-Aufloesung** in BEIDEN Haelften UND auf BEIDEN Populationen besser ist als
1,0 — und die Nachbarwerte nicht kippen.

Aufruf:  python backtest_trail_tf.py [n_M1]        (Default 80000)
"""
from __future__ import annotations

import bisect
import sys

import MetaTrader5 as mt5

from core.exit_model import LIVE, simulate
from core.squeeze_scan import atr_series, scan

_MULTS = (0.5, 0.8, 1.0, 1.3, 1.5, 1.86, 2.0, 2.5)
_M5_IN_M1 = 5


def hole(tf, n):
    b = mt5.copy_rates_from_pos(SYM, tf, 0, n)
    if b is None:
        raise SystemExit(f"keine Bars fuer {tf}")
    return ([int(x["time"]) for x in b], [float(x["high"]) for x in b],
            [float(x["low"]) for x in b], [float(x["close"]) for x in b],
            [float(x["spread"]) * POINT for x in b])


def kennz(Rs):
    n = len(Rs)
    if not n:
        return "  —"
    w = sum(1 for x in Rs if x > 0)
    g = sum(x for x in Rs if x > 0); v = -sum(x for x in Rs if x < 0)
    return (f"{n:>5}{100*w/n:>7.0f} %{sum(Rs)/n:>+9.3f}"
            f"{(g/v if v else 9.99):>7.2f}{sum(Rs):>+9.1f}")


def main():
    global SYM, POINT
    nM1 = 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)
    POINT = mt5.symbol_info(SYM).point
    T5, H5, L5, C5, SP5 = hole(mt5.TIMEFRAME_M5, 20000)
    T1, H1, L1, C1, _ = hole(mt5.TIMEFRAME_M1, nM1)
    mt5.shutdown()
    A5 = atr_series(H5, L5, C5)

    # gemeinsames Fenster — nur wo BEIDE Zeitebenen Daten haben
    t_von, t_bis = max(T5[0], T1[0]), min(T5[-1], T1[-1])
    lo = next(i for i, t in enumerate(T5) if t >= t_von)
    hi = max(i for i, t in enumerate(T5) if t <= t_bis)

    # ── Einstiege sammeln: Squeeze (Auto-Pfad) ────────────────────────────
    sq_ein: list = []
    scan(H5, L5, C5, A5, SP5, POINT, lo, hi, 2.5,
         on_entry=lambda j, d, lvl, atr, komp: (
             sq_ein.append((j, d, lvl, atr)) if komp else None))
    # ── und Welle (EMA-Kreuz, die manuelle Population) ────────────────────
    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
    ef, es = ema(C5, 12), ema(C5, 50)
    wv_ein = []
    vor = 0
    for i in range(lo, hi):
        a = A5[i]
        if not a or a < 0.06:
            continue
        s = 1 if ef[i] > es[i] else -1
        if s != vor and vor != 0:
            wv_ein.append((i, s, C5[i], a))
        vor = s

    print("=" * 104)
    print(f"  TRAIL-BREITE × AUFLOESUNG — {SYM}")
    print(f"  gemeinsames Fenster: {len(T1)} M1-Bars, {hi-lo} M5-Bars")
    print(f"  Squeeze-Einstiege {len(sq_ein)} · Wellen-Einstiege {len(wv_ein)}")
    print("  ⚠ Gleiche Einstiege, gleicher ATR (M5) → Trail-Abstand in DOLLAR")
    print("    identisch; NUR die Aufloesung des Exits unterscheidet sich.")
    print("    Zeitgrenzen wall-clock gleich (200 M5-Bars = 1000 M1-Bars).")
    print("=" * 104)

    for name, ein in (("SQUEEZE", sq_ein), ("WELLE", wv_ein)):
        mid = len(ein) // 2
        print(f"\n  ══ {name} ══")
        print(f"  {'mult':<7}{'':>3}{'Haelfte':<9}{'n':>5}{'Treffer':>9}{'OeR':>9}"
              f"{'PF':>7}{'SumR':>9}   ||{'n':>5}{'Treffer':>9}{'OeR':>9}{'PF':>7}{'SumR':>9}")
        print(f"  {'':<19}{'--- Exit auf M5 ---':^39}   {'--- Exit auf M1 ---':^39}")
        for m in _MULTS:
            p5 = LIVE.with_(mult=m)
            p1 = LIVE.with_(mult=m,
                            timestop_bars=LIVE.timestop_bars * _M5_IN_M1,
                            max_hold=LIVE.max_hold * _M5_IN_M1)
            for lbl, teil in (("H1", ein[:mid]), ("H2", ein[mid:])):
                r5, r1 = [], []
                for j, d, px, atr in teil:
                    kost = (SP5[j] if SP5[j] > 0 else 0.0225) / atr
                    if j + 1 >= len(C5) - LIVE.max_hold - 2:
                        continue
                    r5.append(simulate(px, d, atr, H5, L5, C5, j + 1, p5) - kost)
                    k = bisect.bisect_left(T1, T5[j])
                    if not (0 < k < len(C1) - p1.max_hold - 2):
                        continue
                    r1.append(simulate(px, d, atr, H1, L1, C1, k + 1, p1) - kost)
                print(f"  {m:<7.2f}{'':>3}{lbl:<9}{kennz(r5)}   ||{kennz(r1)}")

    print("\n  " + "-" * 100)
    print("  ⚠ REGEL (vorab): breiter nur, wenn auf M1-Aufloesung in BEIDEN Haelften")
    print("    UND auf BEIDEN Populationen besser als 1,0 — Nachbarn duerfen nicht kippen.")
    print("  ⚠ M1-Historie ist beim Broker hart gedeckelt → kuerzeres Fenster als 80k M5.")
    return 0


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