"""EXIT-06 — sar_trail: Parabolic SAR semplificato come trailing stop, TP rimosso.

Idea: come EXIT-01 (cavalcata pura senza TP), ma lo stop trailing non e' un
chandelier a k*ATR fisso bensi' un Parabolic SAR semplificato che ACCELERA: il
fattore af parte a 0.02 e cresce di af_step ad ogni NUOVO estremo favorevole
(cap af_max). Lo stop si avvicina sempre piu' in fretta al prezzo man mano che il
trade corre nella direzione giusta -> protegge i runner stringendo aggressivamente.

  sar(j) = sar(j-1) + af * (ep - sar(j-1))     con ep = estremo favorevole (high long / low short)
  stop attivo nel bar j = max(sl0, sar)   long   (min(sl0, sar) short)
  af: 0.02 -> +af_step ad ogni nuovo ep -> cap af_max

TP RIMOSSO, horizon = 4*mb (cap HARD_CAP=240).

ANTI-LOOK-AHEAD: levels(j) usa SOLO dati <= j-1. L'estremo favorevole `ep` e lo
stato SAR vengono aggiornati incorporando i bar fino a j-1 (gia' chiusi al poll
del bar j). sar(j-1) -> sar(j) usa ep aggiornato a j-1. Nessun dato del bar j.
Nessun TP -> nessun fill parziale; after_bar non usato (uscita solo a SL/orizzonte).

GRID: af_max in {0.1, 0.2} x af_step in {0.02, 0.04}  (4 celle).
"""
import sys
from pathlib import Path

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

from exit_lab import ExitPolicy, evaluate, HARD_CAP


class SarTrail(ExitPolicy):
    name = "sar_trail"

    def __init__(self, ctx, i, d, entry, tp0, sl0, mb, af_max=0.2, af_step=0.02, **params):
        super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
        self.af_max = float(af_max)
        self.af_step = float(af_step)
        self.horizon = min(4 * mb, HARD_CAP)
        # estremo favorevole (ep) sullo slice [i..j-1]; inizializzato al bar di entrata
        self.ep = ctx["high"][i] if d == 1 else ctx["low"][i]
        self.af = 0.02
        # SAR iniziale: lo stop di partenza (sl0). Il SAR converge verso ep dall'sl0.
        self.sar = sl0
        self._last_seen = i           # ultimo indice gia' incorporato nello stato SAR
        self.cur_stop = sl0           # stop monotono (solo si stringe)

    def _step(self, idx):
        """Incorpora il bar `idx` (causale, <= j-1) aggiornando ep, af e sar."""
        h = self.ctx["high"][idx]
        l = self.ctx["low"][idx]
        # avanza il SAR di un passo verso l'estremo favorevole corrente
        self.sar = self.sar + self.af * (self.ep - self.sar)
        # nuovo estremo favorevole? -> accelera
        if self.d == 1:
            if h > self.ep:
                self.ep = h
                self.af = min(self.af + self.af_step, self.af_max)
        else:
            if l < self.ep:
                self.ep = l
                self.af = min(self.af + self.af_step, self.af_max)

    def levels(self, j):
        # incorpora i bar fino a j-1 (gia' chiusi al poll del bar j)
        while self._last_seen < j - 1:
            self._last_seen += 1
            self._step(self._last_seen)
        if self.d == 1:
            if self.sar > self.cur_stop:   # lo stop long puo' solo SALIRE (stringersi)
                self.cur_stop = self.sar
        else:
            if self.sar < self.cur_stop:   # lo stop short puo' solo SCENDERE
                self.cur_stop = self.sar
        return None, self.cur_stop, 1.0    # TP rimosso


GRID = [
    {"af_max": am, "af_step": st}
    for am in (0.1, 0.2)
    for st in (0.02, 0.04)
]


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