"""SH01 EXIT POLICY 02 — ATR-fixed stop, CLOSE-CONFIRM (stile EXIT-16 delle fade).

Stesso livello di stop fisso della policy 01 (intrabar):
    sl = entry - d * k * ATR14[i]      (fissato all'ingresso, dati <= i)
ma `levels` ritorna mode="close" → lo stop scatta SOLO se il CLOSE del bar j
sfonda il livello, con uscita al close (immune ai wick). E' il trasferimento a
SH01 della lezione EXIT-16 sulle fade: l'overshoot che buca lo stop e rientra e'
un falso negativo; aspettare la conferma del CLOSE evita di farsi stoppare dai
wick di un crash che poi rimbalza dentro l'orizzonte.

Griglia k in {1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0}.

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

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

import sys

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

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


class AtrFixedCloseConfirm(ExitPolicy):
    def __init__(self, k: float):
        self.k = float(k)
        self.name = f"atr_fixed_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 = [1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.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 main():
    sleeves = load_sleeves()

    # baseline (orizzonte puro) per riferimento
    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("=" * 110)
    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("=" * 110)
    print("GRID — TRAIN ONLY (selezione parametri):")
    train_res = {}
    for k in GRID:
        pol = AtrFixedCloseConfirm(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'])}")

    # verdetto OOS sulla config scelta + vicine (guardato una volta sola)
    print("=" * 110)
    print("OOS (verdetto, config scelta + vicine):")
    for k in GRID:
        pol = AtrFixedCloseConfirm(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)}")


if __name__ == "__main__":
    main()
