"""SH01 EXIT POLICY 08 — DISASTER-CAP LARGO, close-confirm (minimal intervention).

Ipotesi: SH01 (exit a orizzonte puro, niente TP/SL) si fa massacrare dalle code
rare (crash ETH 2026-06-05 −15.6% in un trade, ETH 2020). Uno stop LARGO a
k·ATR14[i] (k grande) dovrebbe toccare SOLO quei pochi trade-disastro, lasciando
intatto il resto della distribuzione — e quindi l'edge asimmetrico dei winner.

    sl = entry - d * k * ATR14[i]      (fissato all'ingresso, dati <= i)
    mode = "close"                     (stop solo se il CLOSE sfonda, stile EXIT-16)

Griglia LARGA: k in {3.0, 4.0, 5.0, 6.0}. E' il complemento "wide-only" della
policy 02 (che spazzava anche stop stretti): qui l'intento e' la NON-interferenza.

Strumentazione extra (richiesta dal mandato): per ogni k riporto
  - stop_rate (quanti trade vengono stoppati),
  - la DISTRIBUZIONE dei trade tagliati: erano tutti loser? quanti winner uccisi?
    Per ogni trade stoppato confronto il suo ret (post-stop, ⇒ negativo) con il
    ret che AVREBBE avuto a orizzonte puro (baseline, senza stop) → conto quanti
    sarebbero finiti winner (stop "dannoso") vs loser (stop "utile").

ANTI-LOOK-AHEAD: sl usa SOLO atr14[i] e c[i] (dati <= i); mode="close" decide sul
close del bar j (dati <= j). Nessun indicatore valutato al bar j.

    cd /opt/docker/PythagorasGoal && uv run python scripts/analysis/sh01_exit_policies/08_disaster_wide_close.py
"""
from __future__ import annotations

import sys

sys.path.insert(0, "/opt/docker/PythagorasGoal")

import numpy as np  # noqa: E402

from scripts.analysis.sh01_exit_lab import (  # noqa: E402
    ExitPolicy, load_sleeves, simulate, OOS_START_MS, FEE_RT, LEV, POS,
)


class DisasterWideClose(ExitPolicy):
    def __init__(self, k: float):
        self.k = float(k)
        self.name = f"disaster_wide_close k={k:g}"

    def open_trade(self, ctx: dict, i: int, d: int) -> dict:
        atr = ctx["atr14"][i]
        entry = ctx["close"][i]
        sl = entry - d * self.k * atr
        return {"sl": float(sl)}

    def levels(self, ctx: dict, i: int, d: int, j: int, st: dict):
        return st["sl"], "close"


GRID = [3.0, 4.0, 5.0, 6.0]


def _fmt(m):
    return (f"ret={m['ret']:>+7.0f}% dd={m['dd']:>4.0f}% shrp={m['sharpe']:>5.2f} "
            f"worst={m['worst']:>+5.1f}% stop={m['stop_rate']:>4.1f}% n={m['trades']}")


def _baseline_ret_by_entry(ctx, t_lo=None, t_hi=None):
    """Mappa entry-i -> ret a orizzonte puro (baseline, nessuno stop), stesso
    engine, stesso slice. Serve a classificare i trade stoppati."""
    base = simulate(ctx, ExitPolicy(), t_lo=t_lo, t_hi=t_hi)
    return {r["i"]: r["ret"] for r in base["_trades"]}


def _stop_breakdown(ctx, policy, t_lo=None, t_hi=None):
    """Esegue la policy e analizza SOLO i trade con reason=='stop'.
    Ritorna (n_stop, n_winner_killed, n_loser_cut, dettaglio_list)."""
    res = simulate(ctx, policy, t_lo=t_lo, t_hi=t_hi)
    base_ret = _baseline_ret_by_entry(ctx, t_lo=t_lo, t_hi=t_hi)
    killed = cut = 0
    detail = []
    for r in res["_trades"]:
        if r["reason"] != "stop":
            continue
        br = base_ret.get(r["i"])
        would_win = (br is not None and br > 0)
        killed += would_win
        cut += (not would_win)
        detail.append((r["i"], r["d"], r["ret"], br, would_win))
    return res, len(detail), killed, cut, detail


def main():
    sleeves = load_sleeves()

    # baseline orizzonte puro
    base = {}
    for a in ("BTC", "ETH"):
        ctx = sleeves[a]
        base[a] = {
            "train": simulate(ctx, ExitPolicy(), t_hi=OOS_START_MS),
            "oos": simulate(ctx, ExitPolicy(), t_lo=OOS_START_MS),
        }

    print("=" * 118)
    print("BASELINE (exit orizzonte puro):")
    for a in ("BTC", "ETH"):
        print(f"  {a} TRAIN {_fmt(base[a]['train'])}")
        print(f"  {a} OOS   {_fmt(base[a]['oos'])}")

    print("=" * 118)
    print("GRID — TRAIN ONLY (selezione parametri):")
    train_res = {}
    for k in GRID:
        pol = DisasterWideClose(k)
        row = {}
        for a in ("BTC", "ETH"):
            row[a] = simulate(sleeves[a], pol, t_hi=OOS_START_MS)
        train_res[k] = row
        print(f"  k={k:>3g} | BTC {_fmt(row['BTC'])}")
        print(f"          | ETH {_fmt(row['ETH'])}")

    # ---- breakdown dei trade stoppati (TRAIN), per la domanda "minimal intervention"
    print("=" * 118)
    print("STOP BREAKDOWN — TRAIN (chi viene tagliato? winner uccisi vs loser tagliati):")
    for k in GRID:
        pol = DisasterWideClose(k)
        for a in ("BTC", "ETH"):
            ctx = sleeves[a]
            res, ns, killed, cut, detail = _stop_breakdown(ctx, pol, t_hi=OOS_START_MS)
            print(f"  k={k:>3g} {a} TRAIN: stop n={ns:>2d}/{res['trades']} "
                  f"({res['stop_rate']:.1f}%) -> loser_tagliati={cut}  winner_UCCISI={killed}")
            for (i, d, ret, br, ww) in detail:
                tag = "WINNER-KILLED" if ww else "loser-cut"
                brs = f"{br*100:>+6.1f}%" if br is not None else "  n/a "
                print(f"        i={i:>6d} d={d:>+d} stop_ret={ret*100:>+6.1f}% "
                      f"baseline_ret={brs}  [{tag}]")

    # ---- verdetto OOS (config scelta + vicine, guardato una volta)
    print("=" * 118)
    print("OOS (verdetto):")
    for k in GRID:
        pol = DisasterWideClose(k)
        print(f"  k={k:>3g} | BTC TRAIN {_fmt(train_res[k]['BTC'])}")
        for a in ("BTC", "ETH"):
            oo = simulate(sleeves[a], pol, t_lo=OOS_START_MS)
            print(f"          | {a} OOS   {_fmt(oo)}")
    print("=" * 118)
    print("STOP BREAKDOWN — OOS:")
    for k in GRID:
        pol = DisasterWideClose(k)
        for a in ("BTC", "ETH"):
            ctx = sleeves[a]
            res, ns, killed, cut, detail = _stop_breakdown(ctx, pol, t_lo=OOS_START_MS)
            print(f"  k={k:>3g} {a} OOS  : stop n={ns:>2d}/{res['trades']} "
                  f"({res['stop_rate']:.1f}%) -> loser_tagliati={cut}  winner_UCCISI={killed}")
            for (i, d, ret, br, ww) in detail:
                tag = "WINNER-KILLED" if ww else "loser-cut"
                brs = f"{br*100:>+6.1f}%" if br is not None else "  n/a "
                print(f"        i={i:>6d} d={d:>+d} stop_ret={ret*100:>+6.1f}% "
                      f"baseline_ret={brs}  [{tag}]")


if __name__ == "__main__":
    main()
