"""EXIT-14 — protezione GIVEBACK dal picco (trailing sul PROFITTO, non sul prezzo). Idea: una fade puo' correre a favore quasi fino al TP (alla media) e poi ritracciare prima di toccarlo, restituendo gran parte del guadagno gia' maturato. Questa policy traccia l'high-water-mark (hwm) del PROFITTO non realizzato sul CLOSE; quando il profitto corrente scende sotto (1-g)*hwm_profit -- cioe' si e' restituita una frazione g del picco -- e il picco era gia' "significativo" (hwm_profit > soglia * dist(entry, tp0)), esce al close del bar (after_bar). Il TP fisso tp0 e lo SL fisso sl0 RESTANO invariati e prioritari intrabar. Razionale: protegge il profitto maturato senza toccare i livelli; non e' un ladder (ieri scartato perche' al TP/media il movimento e' esaurito) ma un trailing sul drawdown del trade quando ha gia' fatto buona parte del lavoro. ANTI-LOOK-AHEAD: - levels(j) NON e' toccato (TP/SL fissi): nessun dato futuro. - after_bar(j) decide sul CLOSE del bar j (eseguibile al poll). Aggiorna l'hwm con il profitto a close[j] e poi confronta: tutto con dati <= j. Il bar j e' lecito in after_bar per contratto. L'hwm e' un cliquet (non scende mai). GRID: g in {0.4, 0.6} x arm_frac in {0.3, 0.5} della distanza al TP (4 celle). - g = frazione del picco di profitto restituita che fa scattare l'uscita. - arm_frac = il giveback e' armato solo quando hwm_profit ha superato arm_frac * dist(entry, tp0): finche' il trade non ha "fatto strada" (frazione del cammino verso la media) non interviene -> non taglia i trade che partono storti, solo quelli che hanno gia' guadagnato e poi mollano. """ 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 Giveback(ExitPolicy): name = "giveback" def __init__(self, ctx, i, d, entry, tp0, sl0, mb, **params): super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params) self.g = float(params.get("g", 0.4)) self.arm_frac = float(params.get("arm_frac", 0.3)) self.close = ctx["close"] # distanza (in prezzo, sempre positiva) dall'entrata al TP iniziale self.dist_tp = abs(self.tp0 - entry) if self.tp0 is not None else 0.0 self.arm_level = self.arm_frac * self.dist_tp self.hwm_profit = 0.0 # high-water-mark del profitto a favore (cliquet) def after_bar(self, j: int) -> bool: if self.dist_tp <= 0.0: return False # profitto NON realizzato a favore, valutato sul close del bar j (lecito qui) profit = (self.close[j] - self.entry) * self.d if profit > self.hwm_profit: self.hwm_profit = profit # aggiorna il picco (non scende mai) # arma solo se il picco ha superato la soglia (trade gia' "in cammino") if self.hwm_profit < self.arm_level: return False # esci se il profitto corrente ha restituito >= g del picco if profit < (1.0 - self.g) * self.hwm_profit: return True return False GRID = [ {"g": 0.4, "arm_frac": 0.3}, {"g": 0.4, "arm_frac": 0.5}, {"g": 0.6, "arm_frac": 0.3}, {"g": 0.6, "arm_frac": 0.5}, ] if __name__ == "__main__": evaluate(Giveback, GRID)