"""EXIT-22 — DIAGNOSTICA DEL VALORE DELLO SL sulle fade (MR01/MR02/MR07). Le fade live escono con TP/SL FISSI decisi all'entrata (al close[i]) + max_bars. Lo SL e' sl0 = entry -/+ k*ATR (per costruzione della strategia). Questa policy NON cambia TP ne' horizon: tocca SOLO lo SL per misurare quanto lo SL ATTUALE aggiunge/toglie a ret/DD/Sharpe per sleeve. Tre regimi: mode = "none" -> sl = None (SL RIMOSSO: restano TP + max_bars). Il trade puo' chiudere solo al TP o a scadenza horizon. Esposizione al rischio di coda massima (nessun taglio della perdita). mode = "wide2x" -> sl spostato a 2x la distanza entry->sl0 (stop piu' lasco): sl = entry + 2*(sl0 - entry). Stoppa meno spesso. mode = "tight05x" -> sl spostato a 0.5x la distanza (stop piu' stretto): sl = entry + 0.5*(sl0 - entry). Stoppa piu' spesso, ma perde di meno per stop. La geometria col segno e' automatica: per long sl0entry; scalare (sl0-entry) preserva il lato corretto in entrambi i casi. ANTI-LOOK-AHEAD: il livello sl(j) dipende SOLO da {entry, sl0, mode}, tutti noti all'ENTRATA (close[i]). Costante per tutto il trade, identico al baseline tranne il fattore di scala. Nessun dato del bar j o futuro entra. TP = tp0 invariato. -> contratto rispettato per costruzione (livello statico, come il baseline). INTERPRETAZIONE (diagnostica, non per forza deployabile): - se "none" >= base su ret E DD -> lo SL toglie valore (taglia winner che sarebbero rientrati: la fade e' mean-reversion, il movimento avverso e' spesso transitorio). - se "tight05x" peggiora molto -> lo SL e' gia' sul filo: stringerlo morde i rientri. se "wide2x" ~ "none" -> lo SL attuale e' quasi inerte (raramente toccato) e il rischio di coda e' contenuto da max_bars/TP. - DD e' la metrica chiave: lo SL serve a contenere la coda. Se "none" ha DD simile o piu' basso, lo SL non sta proteggendo nulla di utile. GRID: mode in {none, wide2x, tight05x} (3 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 _SCALE = {"none": None, "wide2x": 2.0, "tight05x": 0.5} class NoSl(ExitPolicy): name = "no_sl" def __init__(self, ctx, i, d, entry, tp0, sl0, mb, **params): super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params) self.mode = str(params.get("mode", "none")) if self.mode not in _SCALE: raise ValueError(f"mode sconosciuto: {self.mode}") scale = _SCALE[self.mode] if scale is None: self.sl = None else: # sl = entry + scale*(sl0 - entry): preserva il lato per long/short. self.sl = entry + scale * (sl0 - entry) def levels(self, j: int): return self.tp0, self.sl, 1.0 GRID = [ {"mode": "none"}, {"mode": "wide2x"}, {"mode": "tight05x"}, ] if __name__ == "__main__": evaluate(NoSl, GRID)