"""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)