Files
PythagorasGoal/scripts/analysis/exit_policies/22_no_sl.py
T
Adriano Dal Pastro ad65a0b344 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>
2026-06-04 21:16:58 +00:00

75 lines
3.0 KiB
Python

"""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 sl0<entry, per short sl0>entry;
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)