research(exit-lab): 34 agenti su exit dinamiche → EXIT-16 close-confirm SL PROMOSSO a livello PORT06

23 famiglie esplorate (harness condiviso exit_lab, train/OOS embargo nov-2023,
tutto lo storico 1h 2018-2026) + 10 verifiche avversariali + test PORT06.
'Cavalcare il prezzo' non esiste (4a conferma: oltre il TP=media non c'e' coda).
Scoperta: lo SL intrabar fisso e' il distruttore di valore n.1 delle fade
(stop da wick = falsi negativi). Forma robusta: SL solo su CHIUSURA oltre
sl0±0.5·ATR14 — PORT06 FULL Sharpe 6.47→7.84 DD 4.10→2.60, OOS 8.82→10.06.
Collaterali: bias gap-through dell'engine sugli stop stretti; ramo -2% del
worker morto con sl=0. Diario: docs/diary/2026-06-04-exit-lab.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-06-04 21:16:58 +00:00
parent 3accc91f84
commit ad65a0b344
36 changed files with 5116 additions and 0 deletions
@@ -0,0 +1,73 @@
"""EXIT-07 — TP dinamico che DECADE col tempo (verso breakeven).
PRIOR (vincolante, ieri SCARTATO un ladder 80/20): il TP delle fade sta alla
MEDIA, e li' il movimento e' esaurito -> il prezzo spesso NON arriva al TP e il
trade muore a max_bars (close) o sullo SL. Idea di questa famiglia: invece di
tenere il TP fisso al target ambizioso (la media), farlo DECADERE col passare
delle barre verso un target piu' modesto (breakeven che copre le fee, o meta'
strada). Cosi' un trade che s'e' avvicinato ma non ha toccato il TP pieno puo'
essere chiuso in profitto/pareggio prima di scadere a max_bars potenzialmente
in perdita. SL FISSO (sl0) invariato. horizon = max_bars invariato.
TP attivo nel bar j:
frac(j) = clamp( ((j - i) / mb) * speed , 0, 1 )
tp(j) = tp0 + frac(j) * (target_fin - tp0)
- frac dipende SOLO da j (indice di tempo) e da costanti note all'entrata
(i, mb, speed) -> NESSUN dato > j-1 -> anti-look-ahead OK per costruzione.
- long (d=+1): tp0 > entry; target_fin <= tp0 -> il TP SCENDE verso entry.
- short (d=-1): tp0 < entry; target_fin >= tp0 -> il TP SALE verso entry.
- speed>1: il TP raggiunge il target finale prima della fine (clamp a 1).
target_fin (param "target"):
- "breakeven": entry*(1 + d*0.003) -> copre 0.3% (~ fee 0.10%RT + margine).
Per long sta sopra entry, per short sotto: chiudere li' NON e' in perdita.
- "halfway" : (entry + tp0)/2 -> meta' strada fra entry e il TP pieno.
GRID: speed in {0.5, 1.0, 1.5} x target in {breakeven, halfway} (6 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 TpDecay(ExitPolicy):
name = "tp_decay"
def __init__(self, ctx, i, d, entry, tp0, sl0, mb, **params):
super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
self.speed = float(params.get("speed", 1.0))
target = str(params.get("target", "breakeven"))
if target == "breakeven":
self.target_fin = entry * (1.0 + d * 0.003)
elif target == "halfway":
self.target_fin = 0.5 * (entry + tp0)
else:
raise ValueError(f"target sconosciuto: {target}")
# mb puo' essere 0 in teoria; protezione
self.mb_eff = max(int(mb), 1)
def levels(self, j: int):
# frac dipende SOLO dal tempo (j - i), mb, speed: costanti note all'entrata
frac = ((j - self.i) / self.mb_eff) * self.speed
if frac < 0.0:
frac = 0.0
elif frac > 1.0:
frac = 1.0
tp = self.tp0 + frac * (self.target_fin - self.tp0)
return tp, self.sl0, 1.0
GRID = [
{"speed": 0.5, "target": "breakeven"},
{"speed": 1.0, "target": "breakeven"},
{"speed": 1.5, "target": "breakeven"},
{"speed": 0.5, "target": "halfway"},
{"speed": 1.0, "target": "halfway"},
{"speed": 1.5, "target": "halfway"},
]
if __name__ == "__main__":
evaluate(TpDecay, GRID)