"""analyze_srclose_live.py — War der S/R-Auto-Close live dienlich oder hinderlich?

User-Frage 2026-07-31 („ist der Auto-Close beim S/R eher behindernd als dienlich?").

⚠ WARUM DIE ROHE STATISTIK NICHT REICHT: `closed_by='sr_close'` hat live 99 % WR
und Ø +13,71 € — das ist ein **Konstruktions-Artefakt**, der Close feuert nur im
Plus. Die Frage ist die KONTRAFAKTISCHE: was wäre passiert, wenn der Trade
weitergelaufen wäre?

METHODE: für jeden sr_close-Trade die nächsten 60 min aus `candles_m1` holen und
messen, wie weit der Kurs danach noch IN Trade-Richtung lief (= entgangener Gewinn)
bzw. GEGEN sie (= vermiedener Rückgang). Umgerechnet in EUR über die tatsächliche
Lot-Größe des Trades.

⚠ Grenzen (bewusst benannt):
 * Der Vergleich „hätte weitergelaufen" ist eine OBERGRENZE für das Halten — er
   unterstellt einen perfekten Ausstieg am besten Punkt. Real hätte das Trailing
   einen Teil davon wieder abgegeben. Die MAE-Spalte zeigt die Gegenseite.
 * 60-min-Fenster = dasselbe wie beim P(break)-Modelltraining (_BRK_W=12 M5-Bars).
 * `candles_m1.time` ist ROHE Broker-Zeit (UTC+3), `trades.exit_time` lokale Epoch
   — der Offset wird unten korrigiert.
"""
import sqlite3
import sys
import time
from datetime import datetime

DB = "oil_widget_history.db"
WINDOW_MIN = 60          # wie beim Modelltraining (_BRK_W=12 M5-Bars)


def broker_offset_s(c) -> int:
    """Offset zwischen candles_m1.time (Broker, UTC+3) und lokaler Epoch.

    Robust bestimmt: die letzte M1-Kerze ist ~jetzt, die Differenz zu time.time()
    auf volle Stunden gerundet.
    """
    c.execute("SELECT MAX(time) FROM candles_m1")
    last = c.fetchone()[0]
    if not last:
        return 0
    return round((last - time.time()) / 3600.0) * 3600


def main():
    db = sqlite3.connect(DB)
    c = db.cursor()
    off = broker_offset_s(c)
    print(f"Broker-Offset erkannt: {off/3600:+.0f} h\n")

    c.execute("""SELECT ticket, direction, lots, exit_time, exit_price, pnl, entry_price
                 FROM trades
                 WHERE closed_by='sr_close' AND exit_time IS NOT NULL
                   AND exit_price IS NOT NULL AND lots > 0
                 ORDER BY exit_time""")
    rows = c.fetchall()
    if not rows:
        print("Keine sr_close-Trades gefunden.")
        return

    # EUR je 1,00 Kursbewegung und Lot — aus den Trades selbst kalibriert, damit
    # keine Kontraktgroesse geraten werden muss.
    cal = []
    for tk, d, lots, xt, xp, pnl, ep in rows:
        if ep and xp and lots and pnl is not None and abs(xp - ep) > 1e-9:
            sgn = 1 if (d or "").upper().startswith("B") or d == "LONG" else -1
            move = (xp - ep) * sgn
            if abs(move) > 0.01:
                cal.append(abs(pnl) / (abs(move) * lots))
    cal.sort()
    eur_per_point_lot = cal[len(cal)//2] if cal else 100.0
    print(f"kalibriert: {eur_per_point_lot:.1f} EUR je 1.00 Kurs und Lot "
          f"(Median aus {len(cal)} Trades)\n")

    tot_missed = tot_saved = 0.0
    n_missed = n_saved = 0
    detail = []

    for tk, d, lots, xt, xp, pnl, ep in rows:
        long = (d == "LONG") or (d or "").upper().startswith("B")
        sgn = 1 if long else -1
        t0 = xt + off
        t1 = t0 + WINDOW_MIN * 60
        c.execute("SELECT MAX(h), MIN(l) FROM candles_m1 WHERE time > ? AND time <= ?",
                  (t0, t1))
        hi, lo = c.fetchone()
        if hi is None:
            continue
        # weiter IN Richtung (entgangen) / GEGEN Richtung (vermieden), in Kurspunkten
        fav = (hi - xp) if long else (xp - lo)
        adv = (xp - lo) if long else (hi - xp)
        fav = max(0.0, fav)
        adv = max(0.0, adv)
        eur_fav = fav * lots * eur_per_point_lot
        eur_adv = adv * lots * eur_per_point_lot
        tot_missed += eur_fav
        tot_saved += eur_adv
        if eur_fav > 1:
            n_missed += 1
        if eur_adv > 1:
            n_saved += 1
        detail.append((xt, tk, pnl, eur_fav, eur_adv))

    n = len(detail)
    print(f"=== {n} S/R-Auto-Closes, Fenster {WINDOW_MIN} min danach ===\n")
    realized = sum(x[2] for x in detail)
    print(f"realisiert (tatsaechlich kassiert) : {realized:>10.2f} EUR")
    print(f"entgangen  (Kurs lief noch weiter) : {tot_missed:>10.2f} EUR  "
          f"(in {n_missed}/{n} Faellen)")
    print(f"vermieden  (Kurs drehte zurueck)   : {tot_saved:>10.2f} EUR  "
          f"(in {n_saved}/{n} Faellen)")
    print()
    print(f"Ø entgangen je Trade : {tot_missed/n:>8.2f} EUR")
    print(f"Ø vermieden je Trade : {tot_saved/n:>8.2f} EUR")
    print(f"Netto (vermieden - entgangen): {(tot_saved-tot_missed)/n:+8.2f} EUR/Trade "
          f"= {tot_saved-tot_missed:+.2f} EUR gesamt")
    print()
    print("⚠ 'entgangen' ist eine OBERGRENZE fuer das Halten (perfekter Ausstieg am")
    print("  Hoch). 'vermieden' ist die Gegenseite: so weit waere der Trade")
    print("  zurueckgefallen. Die Wahrheit liegt dazwischen — real haette das")
    print("  Trailing einen Teil des Laufs mitgenommen und einen Teil abgegeben.")

    # Verteilung: wie oft war der Close klar richtig / klar zu frueh?
    clear_right = sum(1 for x in detail if x[4] > 2 * x[3])
    clear_early = sum(1 for x in detail if x[3] > 2 * x[4])
    print()
    print(f"klar RICHTIG geschlossen (Rueckfall > 2x weiterer Lauf): {clear_right}/{n} "
          f"= {100*clear_right/n:.0f}%")
    print(f"klar ZU FRUEH  (weiterer Lauf > 2x Rueckfall)          : {clear_early}/{n} "
          f"= {100*clear_early/n:.0f}%")

    if "-v" in sys.argv:
        print("\n--- Einzelfaelle ---")
        for xt, tk, pnl, f, a in detail:
            print(f"{datetime.fromtimestamp(xt):%d.%m %H:%M} #{tk} "
                  f"kassiert {pnl:+7.2f} | entgangen {f:6.2f} | vermieden {a:6.2f}")
    db.close()


if __name__ == "__main__":
    main()
