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:
@@ -0,0 +1,77 @@
|
||||
"""EXIT-08 — SL che si STRINGE linearmente col tempo (time-based stop tighten).
|
||||
|
||||
Lo stop iniziale sl0 si avvicina linearmente a un livello d'arrivo `target`
|
||||
man mano che il trade invecchia. TP fisso (tp0) e horizon=max_bars invariati.
|
||||
|
||||
frac(j) = clamp( (j - i) / (mb * stretch), 0, 1 )
|
||||
sl(j) = sl0 + frac(j) * (target - sl0)
|
||||
|
||||
target ("arrivo"):
|
||||
- "entry" -> entry (a fine corsa lo stop e' a breakeven)
|
||||
- "midpoint" -> (entry + sl0)/2 (a fine corsa lo stop e' a meta' strada)
|
||||
|
||||
Direzioni: il segno e' gestito dalla geometria di sl0 vs entry.
|
||||
long (d=1): sl0 < entry -> target >= sl0 -> lo stop SALE verso entry
|
||||
short (d=-1): sl0 > entry -> target <= sl0 -> lo stop SCENDE verso entry
|
||||
In entrambi i casi lo stop si stringe (avvicina al prezzo d'ingresso) col tempo.
|
||||
|
||||
stretch:
|
||||
- 1.0 : lo stop raggiunge `target` a max_bars (fine horizon).
|
||||
- 2.0 : lo stop raggiunge `target` a 2*max_bars -> stringe a META' velocita',
|
||||
quindi a max_bars e' solo a meta' del cammino sl0->target (piu' lasco).
|
||||
|
||||
ANTI-LOOK-AHEAD: sl(j) dipende SOLO da {i, j, mb, stretch, entry, sl0}, tutti
|
||||
noti all'ENTRATA (close[i]). Nessun dato del bar j o successivi entra nel livello
|
||||
attivo nel bar j. Il TP resta tp0. -> contratto rispettato per costruzione.
|
||||
|
||||
MECCANISMO ATTESO: stringere lo stop col tempo taglia prima i trade che ristagnano
|
||||
(non vanno al TP ne' allo SL iniziale e scadrebbero a max_bars vicino al BE/in
|
||||
perdita). Rischio: stoppa fuori trade che sarebbero rientrati verso il TP (la fade
|
||||
e' mean-reversion: il movimento contrario e' spesso transitorio) -> puo' alzare lo
|
||||
stop-rate effettivo e tagliare winner ritardatari.
|
||||
|
||||
GRID: stretch in {1.0, 2.0} x target in {entry, midpoint} (4 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 SlTighten(ExitPolicy):
|
||||
name = "sl_tighten"
|
||||
|
||||
def __init__(self, ctx, i, d, entry, tp0, sl0, mb, **params):
|
||||
super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
|
||||
self.stretch = float(params.get("stretch", 1.0))
|
||||
self.target_kind = str(params.get("target", "entry"))
|
||||
if self.target_kind == "entry":
|
||||
self.target = entry
|
||||
elif self.target_kind == "midpoint":
|
||||
self.target = 0.5 * (entry + sl0)
|
||||
else:
|
||||
raise ValueError(f"target sconosciuto: {self.target_kind}")
|
||||
self.denom = max(self.mb * self.stretch, 1e-9)
|
||||
|
||||
def levels(self, j: int):
|
||||
# frac dipende solo da i, j, mb, stretch -> noti all'entrata. No look-ahead.
|
||||
frac = (j - self.i) / self.denom
|
||||
if frac < 0.0:
|
||||
frac = 0.0
|
||||
elif frac > 1.0:
|
||||
frac = 1.0
|
||||
sl = self.sl0 + frac * (self.target - self.sl0)
|
||||
return self.tp0, sl, 1.0
|
||||
|
||||
|
||||
GRID = [
|
||||
{"stretch": 1.0, "target": "entry"},
|
||||
{"stretch": 1.0, "target": "midpoint"},
|
||||
{"stretch": 2.0, "target": "entry"},
|
||||
{"stretch": 2.0, "target": "midpoint"},
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
evaluate(SlTighten, GRID)
|
||||
Reference in New Issue
Block a user