#!/usr/bin/env python3
"""Kosten des diskretionären Früh-Kappens — kontrafaktisch (2026-08-04).

Frage: Deine MANUELLEN Closes haben einen Median-Gewinn von +6,37 €, die
S/R-Auto-Closes des Bots +14,65 €. Ist das Früh-Kappen teuer — oder rettet es
Trades, die sonst gedreht wären?

Methode (wie `analyze_srclose_live.py`): Für jeden real geschlossenen Trade wird
ab dem ECHTEN Einstieg der **kanonische Live-Exit** (`core/exit_model.LIVE`)
über die M5-Bars simuliert. Verglichen wird das tatsächliche Ergebnis gegen das,
was der Bot-Exit erbracht hätte.

⚠ Grenzen, die das Ergebnis NICHT überschreiten darf:
 · Kontrafaktisch heißt: der Trade hätte den Slot länger belegt. Folge-Trades,
   die real stattfanden, wären dann evtl. nicht zustande gekommen. Der Vergleich
   ist also PRO TRADE gültig, nicht als Kontostand-Hochrechnung.
 · Der EUR-Faktor wird aus den echten Trades geschätzt (Median über Trades mit
   klarem Move und kurzer Laufzeit, damit der Swap nicht verzerrt) — `trader.pnl`
   enthält Swap, eine direkte Rückrechnung wäre verfälscht.
 · Slippage ist nicht modelliert (real bis 0,75×ATR über den Stop hinaus gemessen).

Aufruf:  python analyze_manual_close.py [tage]
"""
import bisect
import datetime as dt
import sqlite3
import statistics as st
import sys

import MetaTrader5 as mt5

from core.exit_model import LIVE, simulate

_BOFF = 3 * 3600
_ATRMIN = 0.06


def _atr_series(H, L, C, p=14):
    t = [0.0]
    for i in range(1, len(C)):
        t.append(max(H[i] - L[i], abs(H[i] - C[i - 1]), abs(L[i] - C[i - 1])))
    return [(sum(t[max(1, i - p + 1):i + 1]) / max(1, len(t[max(1, i - p + 1):i + 1])))
            if i else None for i in range(len(C))]


def main():
    tage = int(sys.argv[1]) if len(sys.argv) > 1 else 30
    mt5.initialize()
    bars = mt5.copy_rates_from_pos("SpotCrude", mt5.TIMEFRAME_M5, 0, 60000)
    mt5.shutdown()
    T = [int(b["time"]) - _BOFF for b in bars]          # → lokale Epoch
    H = [float(b["high"]) for b in bars]; L = [float(b["low"]) for b in bars]
    C = [float(b["close"]) for b in bars]
    A = _atr_series(H, L, C)

    c = sqlite3.connect("oil_widget_history.db"); c.row_factory = sqlite3.Row
    seit = (dt.datetime.now() - dt.timedelta(days=tage)).timestamp()
    rows = [r for r in c.execute(
        "SELECT * FROM trades WHERE exit_time>=? AND pnl IS NOT NULL "
        "AND lots>0 ORDER BY entry_time", (seit,))]

    # EUR je 1,00 $ und Lot robust schätzen: nur Trades mit klarem Move und
    # kurzer Laufzeit (Swap fällt kaum ins Gewicht).
    f = []
    for r in rows:
        mv = (r["exit_price"] - r["entry_price"]) * (1 if r["direction"] == "BUY" else -1)
        dur = (r["exit_time"] - r["entry_time"]) / 60
        if abs(mv) > 0.20 and dur < 120 and r["lots"]:
            f.append(r["pnl"] / (mv * r["lots"]))
    faktor = st.median(f) if f else 87.0
    print(f"EUR je 1,00 $ und Lot (geschätzt aus {len(f)} Trades): {faktor:.1f}\n")

    gruppen = {}
    for r in rows:
        i = bisect.bisect_left(T, r["entry_time"])
        if i <= 0 or i >= len(T) - LIVE.max_hold - 2 or abs(T[i] - r["entry_time"]) > 900:
            continue
        atr = A[i]
        if not atr or atr < _ATRMIN:
            continue
        d = 1 if r["direction"] == "BUY" else -1
        R, _xj = simulate(r["entry_price"], d, atr, H, L, C, i + 1, LIVE,
                          timestop=True, use_tp=True, ret_bar=True)
        soll = R * atr * r["lots"] * faktor          # was der Bot-Exit gebracht hätte
        ist = r["pnl"]
        key = (r["closed_by"] or "?", "Gewinn" if ist > 0 else "Verlust")
        gruppen.setdefault(key, []).append((ist, soll, r))

    print(f"{'Schließgrund':<16}{'Lage':<9}{'n':>5}{'IST Σ':>10}{'Bot-Exit Σ':>12}"
          f"{'Δ':>10}{'Δ je Trade':>12}")
    print("-" * 76)
    for key in sorted(gruppen, key=lambda k: -len(gruppen[k])):
        v = gruppen[key]
        ist = sum(x[0] for x in v); soll = sum(x[1] for x in v)
        print(f"{key[0]:<16}{key[1]:<9}{len(v):>5}{ist:>+10.0f}{soll:>+12.0f}"
              f"{soll-ist:>+10.0f}{(soll-ist)/len(v):>+12.1f}")

    man = [x for k, v in gruppen.items() if k[0] == "manual" for x in v]
    if man:
        besser = sum(1 for i_, s_, _ in man if s_ > i_)
        print(f"\n  MANUELLE Closes gesamt: n={len(man)}   "
              f"IST {sum(x[0] for x in man):+.0f} €   "
              f"Bot-Exit {sum(x[1] for x in man):+.0f} €   "
              f"Δ {sum(x[1]-x[0] for x in man):+.0f} €")
        print(f"  In {besser} von {len(man)} Fällen ({100*besser/len(man):.0f} %) hätte "
              f"der Bot-Exit mehr gebracht.")
        gew = [x for x in man if x[0] > 0]
        if gew:
            print(f"  Nur die manuellen GEWINN-Closes: n={len(gew)}  "
                  f"Δ {sum(x[1]-x[0] for x in gew):+.0f} €  "
                  f"({sum(x[1]-x[0] for x in gew)/len(gew):+.1f} € je Trade)")
        print("\n  Die 8 teuersten Früh-Kappungen:")
        for i_, s_, r in sorted(gew, key=lambda x: x[0] - x[1])[:8]:
            print(f"     {dt.datetime.fromtimestamp(r['exit_time']).strftime('%d.%m %H:%M')}  "
                  f"IST {i_:+7.2f}   Bot-Exit {s_:+7.2f}   Δ {s_-i_:+7.2f}   "
                  f"{r['direction']} {r['lots']}L")
    print("\n  ⚠ PRO TRADE gültig, nicht als Kontostand-Hochrechnung: ein länger")
    print("    laufender Trade hätte den Positions-Slot belegt und Folge-Trades verhindert.")
    print()


if __name__ == "__main__":
    main()
