"""SH01 EXIT policy 01 — atr_fixed_intrabar.

SL fisso ad ATR, INTRABAR. In open_trade fissiamo il livello una volta sola:
    sl = entry - d * k * ATR14[i]   (entry = close[i], ATR14[i] noto a close[i])
levels() restituisce (sl, "intrabar") costante per tutta la vita del trade.
Il fill è gap-aware (worse(sl, open[j])) nell'engine — realistico sui crash a
gap (es. 2026-06-05: feed flat 2h -> gap ETH 1655->1600).

ANTI-LOOK-AHEAD: il livello usa SOLO dati <= i (ATR14[i], close[i]); levels usa
quel valore congelato (nessun dato del bar j). OK.

PROTOCOLLO: grid su k SOLO sul train (t_hi=OOS_START_MS). Plateau >=3 celle
adiacenti migliorative. Poi OOS una volta sulla config scelta + 2 vicine.

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

import sys

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

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


class AtrFixedIntrabar(ExitPolicy):
    def __init__(self, k: float):
        self.k = float(k)
        self.name = f"atr_fixed_intrabar k={k:.1f}"

    def open_trade(self, ctx, i, d):
        atr = ctx["atr14"][i]
        entry = ctx["close"][i]
        # se atr nan/0 (early bars) -> nessuno stop attivo
        sl = entry - d * self.k * atr if atr == atr and atr > 0 else None
        return {"sl": sl}

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

    def after_bar(self, ctx, i, d, j, st):
        return False


# baseline numbers (exit a orizzonte puro) — dal prompt/harness
BASELINE = {
    "BTC": {"train": dict(ret=127, dd=23, sharpe=2.09, worst=-5.5),
            "oos":   dict(ret=41, dd=8, sharpe=2.18, worst=-3.1)},
    "ETH": {"train": dict(ret=-26, dd=61, sharpe=-0.16, worst=-14.9),
            "oos":   dict(ret=143, dd=7, sharpe=3.60, worst=-4.6)},
}


def _row(tag, a, r):
    print(f"    {tag:<10s} {a}: ret={r['ret']:>+7.0f}% dd={r['dd']:>4.0f}% "
          f"shrp={r['sharpe']:>5.2f} worst={r['worst']:>+5.1f}% "
          f"stop={r['stop_rate']:>4.1f}% trades={r['trades']}")


def main():
    sleeves = load_sleeves()
    KS = [1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0]

    print("=" * 78)
    print("TRAIN GRID (selezione SOLO sul train, t_hi=OOS_START)")
    print("=" * 78)
    # baseline train
    print("  baseline (orizzonte puro):")
    base = evaluate(ExitPolicy(), sleeves=sleeves)
    print()

    train = {}  # k -> {asset: result}
    for k in KS:
        pol = AtrFixedIntrabar(k)
        row = {}
        for a in ("BTC", "ETH"):
            ctx = sleeves[a]
            row[a] = simulate(ctx, pol, t_hi=OOS_START_MS)
        train[k] = row
        print(f"  k={k:.1f}")
        _row("TRAIN", "BTC", row["BTC"])
        _row("TRAIN", "ETH", row["ETH"])

    print()
    print("=" * 78)
    print("PLATEAU CHECK (train): per ogni k, ETH sharpe up & dd down & worst up,")
    print("                       BTC sharpe>=95% & ret>=80% baseline")
    print("=" * 78)
    b_btc, b_eth = BASELINE["BTC"]["train"], BASELINE["ETH"]["train"]
    improving = []
    for k in KS:
        bt, et = train[k]["BTC"], train[k]["ETH"]
        eth_ok = (et["sharpe"] > b_eth["sharpe"] and et["dd"] < b_eth["dd"]
                  and et["worst"] > b_eth["worst"])
        btc_ok = (bt["sharpe"] >= 0.95 * b_btc["sharpe"]
                  and bt["ret"] >= 0.80 * b_btc["ret"])
        ok = eth_ok and btc_ok
        if ok:
            improving.append(k)
        print(f"  k={k:.1f}  ETH_ok={eth_ok}  BTC_ok={btc_ok}  -> "
              f"{'IMPROVING' if ok else '-'}")
    print(f"  improving cells (train): {improving}")

    # plateau = >=3 k adiacenti improving
    plateau = []
    for idx in range(len(KS)):
        run = []
        for j in range(idx, len(KS)):
            if KS[j] in improving:
                run.append(KS[j])
            else:
                break
        if len(run) >= 3 and len(run) > len(plateau):
            plateau = run
    print(f"  longest adjacent improving run: {plateau} "
          f"(plateau={'YES' if len(plateau) >= 3 else 'NO'})")

    # scelgo centro del plateau (o miglior ETH sharpe fra gli improving)
    chosen = None
    if len(plateau) >= 3:
        chosen = plateau[len(plateau) // 2]
    elif improving:
        chosen = max(improving, key=lambda k: train[k]["ETH"]["sharpe"])

    print()
    print("=" * 78)
    if chosen is None:
        print("NESSUNA cella migliorativa sul train -> verdetto NO (niente OOS).")
        print("=" * 78)
        return {"chosen": None, "plateau": plateau, "improving": improving,
                "train": train, "oos": None}

    print(f"CHOSEN k={chosen:.1f} -> OOS (config + 2 vicine), guardato UNA volta")
    print("=" * 78)
    ci = KS.index(chosen)
    neigh = [KS[x] for x in (ci - 1, ci, ci + 1) if 0 <= x < len(KS)]
    oos = {}
    for k in neigh:
        pol = AtrFixedIntrabar(k)
        row = {}
        for a in ("BTC", "ETH"):
            ctx = sleeves[a]
            row[a] = {
                "train": train[k][a],
                "oos": simulate(ctx, pol, t_lo=OOS_START_MS),
            }
        oos[k] = row
        print(f"  k={k:.1f}")
        _row("TRAIN", "BTC", row["BTC"]["train"])
        _row("OOS", "BTC", row["BTC"]["oos"])
        _row("TRAIN", "ETH", row["ETH"]["train"])
        _row("OOS", "ETH", row["ETH"]["oos"])

    # gate finale sulla config scelta
    print()
    print("=" * 78)
    print(f"GATE finale (k={chosen:.1f}):")
    bt_tr, et_tr = oos[chosen]["BTC"]["train"], oos[chosen]["ETH"]["train"]
    bt_oo, et_oo = oos[chosen]["BTC"]["oos"], oos[chosen]["ETH"]["oos"]
    Bb_o, Be_o = BASELINE["BTC"]["oos"], BASELINE["ETH"]["oos"]

    # a) ETH: sharpe up & dd down & worst up, train E oos
    a_train = (et_tr["sharpe"] > b_eth["sharpe"] and et_tr["dd"] < b_eth["dd"]
               and et_tr["worst"] > b_eth["worst"])
    a_oos = (et_oo["sharpe"] > Be_o["sharpe"] and et_oo["dd"] < Be_o["dd"]
             and et_oo["worst"] > Be_o["worst"])
    cond_a = a_train and a_oos
    # b) BTC sharpe>=95% & ret>=80% baseline (train e oos)
    b_tr = (bt_tr["sharpe"] >= 0.95 * b_btc["sharpe"]
            and bt_tr["ret"] >= 0.80 * b_btc["ret"])
    b_oo = (bt_oo["sharpe"] >= 0.95 * Bb_o["sharpe"]
            and bt_oo["ret"] >= 0.80 * Bb_o["ret"])
    cond_b = b_tr and b_oo
    # c) ret ETH oos >= 80% baseline
    cond_c = et_oo["ret"] >= 0.80 * Be_o["ret"]
    # d) plateau
    cond_d = len(plateau) >= 3

    print(f"  a) ETH sharpe up & dd down & worst up (train&oos): {cond_a}")
    print(f"     train: shrp {et_tr['sharpe']:.2f} vs {b_eth['sharpe']:.2f} | "
          f"dd {et_tr['dd']:.0f} vs {b_eth['dd']:.0f} | "
          f"worst {et_tr['worst']:.1f} vs {b_eth['worst']:.1f}")
    print(f"     oos:   shrp {et_oo['sharpe']:.2f} vs {Be_o['sharpe']:.2f} | "
          f"dd {et_oo['dd']:.0f} vs {Be_o['dd']:.0f} | "
          f"worst {et_oo['worst']:.1f} vs {Be_o['worst']:.1f}")
    print(f"  b) BTC sharpe>=95% & ret>=80% (train&oos): {cond_b}")
    print(f"     train: shrp {bt_tr['sharpe']:.2f} (>={0.95*b_btc['sharpe']:.2f}) | "
          f"ret {bt_tr['ret']:.0f} (>={0.80*b_btc['ret']:.0f})")
    print(f"     oos:   shrp {bt_oo['sharpe']:.2f} (>={0.95*Bb_o['sharpe']:.2f}) | "
          f"ret {bt_oo['ret']:.0f} (>={0.80*Bb_o['ret']:.0f})")
    print(f"  c) ETH oos ret>=80% baseline ({0.80*Be_o['ret']:.0f}): {cond_c} "
          f"(ret={et_oo['ret']:.0f})")
    print(f"  d) plateau: {cond_d} ({plateau})")
    passes = cond_a and cond_b and cond_c and cond_d
    print(f"  PASSES GATE: {passes}")
    print("=" * 78)

    return {"chosen": chosen, "plateau": plateau, "improving": improving,
            "passes": passes, "oos": oos, "conds": (cond_a, cond_b, cond_c, cond_d)}


if __name__ == "__main__":
    main()
