"""EXIT-17 — wide_sl_trail: RESPIRA POI PROTEGGI.

Idea: l'SL fisso all'entrata (sl0) puo' essere troppo stretto -> stoppa fade che
poi sarebbero rientrate alla media. Qui ALLARGHIAMO l'SL iniziale a
    sl_w = entry - w*(entry - sl0)     (long, w>1 -> stop piu' lontano = piu' respiro)
    sl_w = entry + w*(sl0 - entry)     (short)
ma attiviamo SUBITO un trailing chandelier a k*ATR dall'estremo favorevole. Lo stop
attivo nel bar j e' il PIU' PROTETTIVO fra l'SL allargato e il chandelier:
    long : sl = max(sl_w, max(high[i..j-1]) - k*ATR[j-1])
    short: sl = min(sl_w, min(low[i..j-1])  + k*ATR[j-1])
TP fisso tp0 invariato; horizon = max_bars invariato.

Logica: finche' il prezzo NON corre a favore, il rischio e' l'SL allargato (respiro:
si tollera un ritracciamento iniziale piu' ampio nella speranza che la fade rientri).
Appena il prezzo corre a favore, il chandelier sale sopra sl_w e protegge il
profitto come un trail normale. Differenza da EXIT-02 (che usa max(sl0, chand)):
qui il floor iniziale e' PIU' LARGO di sl0, quindi early il rischio per-trade e'
maggiore (peggiora i loser veloci) ma si salvano fade che con sl0 sarebbero state
stoppate giusto prima del rientro.

ANTI-LOOK-AHEAD: levels(j) usa SOLO dati <= j-1 (estremo favorevole su [i..j-1]
mantenuto incrementalmente fino a j-1; atr14[j-1]). after_bar non usato.

GRID: w in {1.5, 2.0} x k in {2.0, 3.0}  (4 celle).
"""
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from exit_lab import ExitPolicy, evaluate  # noqa: E402


class WideSLTrail(ExitPolicy):
    name = "wide_sl_trail"

    def __init__(self, ctx, i, d, entry, tp0, sl0, mb, w=1.5, k=2.0, **params):
        super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
        self.w = float(w)
        self.k = float(k)
        self.high = ctx["high"]
        self.low = ctx["low"]
        self.atr = ctx["atr14"]
        # SL iniziale ALLARGATO: piu' lontano dall'entrata (w>1)
        if d == 1:
            self.sl_w = entry - self.w * (entry - sl0)
        else:
            self.sl_w = entry + self.w * (sl0 - entry)
        # estremo favorevole running (solo barre <= j-1); init = barra d'entrata i
        self.run_hi = self.high[i]
        self.run_lo = self.low[i]
        self.last_seen = i

    def _update_running(self, upto: int) -> None:
        """Incorpora le barre (last_seen, upto] nell'estremo favorevole. upto = j-1
        -> NON tocca il bar j (anti-look-ahead)."""
        while self.last_seen < upto:
            self.last_seen += 1
            if self.high[self.last_seen] > self.run_hi:
                self.run_hi = self.high[self.last_seen]
            if self.low[self.last_seen] < self.run_lo:
                self.run_lo = self.low[self.last_seen]

    def levels(self, j: int):
        self._update_running(j - 1)            # solo dati <= j-1
        a = self.atr[j - 1]
        if a is None or a != a:                # NaN nei primi 14 bar -> usa l'SL allargato
            return self.tp0, self.sl_w, 1.0
        if self.d == 1:
            chand = self.run_hi - self.k * a
            sl = max(self.sl_w, chand)         # il piu' protettivo (stop piu' alto)
        else:
            chand = self.run_lo + self.k * a
            sl = min(self.sl_w, chand)         # il piu' protettivo (stop piu' basso)
        return self.tp0, sl, 1.0


GRID = [
    {"w": w, "k": k}
    for w in (1.5, 2.0)
    for k in (2.0, 3.0)
]


if __name__ == "__main__":
    evaluate(WideSLTrail, GRID)
