#!/usr/bin/env python3
"""Wie weit über dem Ausbruchs-Level steigt der Auto-Squeeze LIVE wirklich ein?

WOZU — das ist die Erfolgskontrolle für den Umbau vom 2026-08-05 (Market-Order →
ruhende Stop-Order, `squeeze_pending_entry`). Der gemessene Zusammenhang
(`backtest_squeeze_entry.py`): der Squeeze hat seinen gesamten Edge im
EINSTIEGSPREIS — am Level ØR **+0,456/+0,611**, hinterhergelaufen **−0,158/−0,006**.
Vor dem Umbau lag der Live-Abstand im **Median +0,306×ATR** (Mittel +0,741, Max
+4,339). **Er muss jetzt gegen 0 gehen.** Tut er das nicht, greift die Pending-Order
nicht wie gedacht — dann ist der Umbau wirkungslos, ohne dass es sonst auffiele.

QUELLE (bewusst zwei unabhängige): das Ausbruchs-Level steht NUR im Log
(„AUTO-SQUEEZE-ENTRY: <dir> @ Ausbruch <level>"), der tatsächliche Fill NUR in der
`trades`-Tabelle. Zusammengeführt über die Zeit (±90 s).
⚠ Seit dem Umbau füllt eine Pending-Order OHNE diese Log-Zeile (sie entsteht im
Market-Fallback-Pfad). Deshalb werden Squeeze-Trades ohne Log-Treffer separat
ausgewiesen — **genau die sind der Erfolg**, nicht ein Fehler.

Aufruf:  python analyze_squeeze_entry_gap.py [tage]
"""
import glob
import os
import re
import sqlite3
import sys
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo

import MetaTrader5 as mt5

_HIER = os.path.dirname(os.path.abspath(__file__))
_BERLIN = ZoneInfo("Europe/Berlin")
_BROKER = timezone(timedelta(hours=3))
_UTC = timezone.utc
_UMBAU = datetime(2026, 8, 5, 8, 55, tzinfo=_BERLIN)   # Pending-Entry ging live
# ⚠ BEIDE Market-Pfade erfassen (2026-08-05): seit dem Pending-Umbau laeuft auch
# der Signal-Entry ueber eine ruhende Stop-Order. Nur der FALLBACK schreibt noch
# eine Log-Zeile mit Level — die faengt dieses Muster ebenfalls ein.
_RX = re.compile(r"^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d).*"
                 r"(?:AUTO-SQUEEZE-ENTRY: (\w+) @ Ausbruch ([\d.]+)"
                 r"|AUTO-SIGNAL-ENTRY: (\w+) )")
# Der Signal-Log nennt kein Level (es steht nur im Snapshot) — dort wird der
# Abstand deshalb nicht berechnet, der Trade zaehlt aber als Market-Fallback.
_RX_SIG = re.compile(r"^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d).*AUTO-SIGNAL-ENTRY: (\w+) ")
_RX_SQ = re.compile(r"^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d).*AUTO-SQUEEZE-ENTRY: (\w+) "
                    r"@ Ausbruch ([\d.]+)")


def _berlin(ts):
    """MT5-Bar-Zeitstempel → echte Berliner Zeit (s. CLAUDE.md, Fix 2026-08-05)."""
    return (datetime.fromtimestamp(int(ts), _UTC)
            .replace(tzinfo=_BROKER).astimezone(_BERLIN))


def _stat(v, lbl):
    if not v:
        print(f"    {lbl:<22} —")
        return
    s = sorted(v)
    print(f"    {lbl:<22} n={len(s):<4} Median {s[len(s)//2]:+.3f} · "
          f"Ø {sum(s)/len(s):+.3f} · 75 % {s[3*len(s)//4]:+.3f} · Max {s[-1]:+.3f}")


def main():
    tage = int(sys.argv[1]) if len(sys.argv) > 1 else 60
    seit = datetime.now(_BERLIN) - timedelta(days=tage)

    # ── 1) Log: Ausbruchs-Level je Market-Entry ──────────────────────────
    log_eintraege, sig_fallback = [], []
    for f in sorted(glob.glob(os.path.join(_HIER, "oil_widget.log*"))):
        try:
            with open(f, encoding="utf-8", errors="replace") as fh:
                for z in fh:
                    m = _RX_SQ.match(z)
                    if m:
                        ts = datetime.strptime(m.group(1), "%Y-%m-%d %H:%M:%S") \
                            .replace(tzinfo=_BERLIN)
                        if ts >= seit:
                            log_eintraege.append((ts, m.group(2), float(m.group(3))))
                        continue
                    m = _RX_SIG.match(z)
                    if m:
                        ts = datetime.strptime(m.group(1), "%Y-%m-%d %H:%M:%S") \
                            .replace(tzinfo=_BERLIN)
                        if ts >= seit:
                            sig_fallback.append((ts, m.group(2)))
        except OSError:
            continue
    log_eintraege.sort(); sig_fallback.sort()

    # ── 2) ATR-Reihe für die Normierung ──────────────────────────────────
    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, 30000)
    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]
    # Bar-Zeit → lokale Epoch, DST-korrekt (kein fester Versatz!)
    EP = [_berlin(b["time"]).timestamp() for b in bars]
    tr = [0.0] + [max(H[i] - L[i], abs(H[i] - C[i - 1]), abs(L[i] - C[i - 1]))
                  for i in range(1, len(C))]

    def atr_bei(ep):
        j = min(range(len(EP)), key=lambda i: abs(EP[i] - ep))
        seg = tr[max(1, j - 13):j + 1]
        return (sum(seg) / len(seg)) if seg else 0.0

    # ── 3) Trades dazu ───────────────────────────────────────────────────
    db = sqlite3.connect(os.path.join(_HIER, "oil_widget_history.db"))
    db.row_factory = sqlite3.Row
    trades = db.execute(
        "SELECT ticket, entry_time, entry_price, direction, pnl, closed_by, setup "
        "FROM trades WHERE entry_time >= ? ORDER BY entry_time",
        (seit.timestamp(),)).fetchall()

    vorher, nachher, ohne_log = [], [], []
    getroffen = set()
    for ts, richtung, lvl in log_eintraege:
        ep = ts.timestamp()
        kand = [t for t in trades if abs(t["entry_time"] - ep) <= 90
                and t["entry_price"]]
        if not kand:
            continue
        t = min(kand, key=lambda r: abs(r["entry_time"] - ep))
        getroffen.add(t["ticket"])
        a = atr_bei(ep)
        if a <= 0:
            continue
        d = 1 if richtung == "LONG" else -1
        weg = (float(t["entry_price"]) - lvl) * d / a
        (nachher if ts >= _UMBAU else vorher).append(weg)

    print("=" * 82)
    print(f"  AUTO-SQUEEZE — Abstand Einstieg ↔ Ausbruchs-Level (×ATR), {tage} Tage")
    print(f"  Umbau auf ruhende Stop-Orders: {_UMBAU:%d.%m.%Y %H:%M}")
    print("=" * 82)
    print("  Market-Entries (haben eine Log-Zeile mit dem Level):")
    _stat(vorher, "VOR dem Umbau")
    _stat(nachher, "NACH dem Umbau")
    print()
    print("  Erwartung: NACH dem Umbau sollten hier kaum noch Entries auftauchen —")
    print("  die Market-Order ist nur noch Fallback, und die Nachjagd-Bremse")
    print("  (squeeze_max_chase_atr) deckelt sie bei 0,20×ATR.")
    print()
    print("  ⚠ Die eigentliche Erfolgsmeldung: Bot-Trades OHNE Market-Log-Zeile —")
    print("    das sind die per Pending-Order gefüllten (Squeeze UND Signal).")
    sig_ts = {t.timestamp() for t, _ in sig_fallback}
    n_bot = 0
    for t in trades:
        if t["entry_time"] < _UMBAU.timestamp():
            continue
        # ⚠ NUR Bot-Trades. Bis zum Fix am 06.08. zaehlte hier JEDER Trade mit —
        # ein Pending-Fill wurde damals ohne `setup` gebucht (er laeuft nicht durch
        # `engine._open`), also war er von einem manuellen nicht zu unterscheiden.
        # Ergebnis: "31 Trades, davon 30 ohne Log-Zeile" — real waren es 5 Bot-Trades.
        # Seit `engine._check_pending_fill` traegt der Bot das Setup nach.
        su = (t["setup"] or "")
        if not (su.startswith("SQUEEZE") or su.startswith("AUTOSIG")):
            continue
        n_bot += 1
        # Market-Fallback erkannt, wenn eine der beiden Log-Zeilen zeitlich passt
        market = (t["ticket"] in getroffen
                  or any(abs(t["entry_time"] - s) <= 90 for s in sig_ts))
        if not market:
            ohne_log.append(t)
    print(f"    Bot-Trades seit dem Umbau: {n_bot} · davon per Pending gefüllt "
          f"(keine Market-Log-Zeile): {len(ohne_log)}")
    print(f"    (Market-Fallback-Zeilen im Log: Squeeze {len(log_eintraege)} · "
          f"Signal {len(sig_fallback)})")
    for t in ohne_log[-10:]:
        print(f"      {datetime.fromtimestamp(t['entry_time'], _BERLIN):%d.%m %H:%M}  "
              f"{(t['setup'] or '?'):<14} "
              f"{t['direction']:<5} @ {t['entry_price']:.3f}  "
              f"P&L {(t['pnl'] if t['pnl'] is not None else 0):+.2f}  {t['closed_by']}")
    print()
    print("  MASSSTAB (backtest_squeeze_entry.py): am Level ØR +0,456/+0,611 ·")
    print("  bei 0,20×ATR noch +0,244/+0,426 · hinterhergelaufen −0,158/−0,006.")
    print("  Vor dem Umbau war der Live-Median +0,306×ATR.")
    print()


if __name__ == "__main__":
    main()
