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,86 @@
|
||||
"""EXIT-17 — wide_sl_trail: RESPIRA POI PROTEGGI.
|
||||
|
||||
Idea: l'SL fisso all'entrata (sl0) puo' essere troppo stretto -> stoppa fade che
|
||||
poi sarebbero rientrate alla media. Qui ALLARGHIAMO l'SL iniziale a
|
||||
sl_w = entry - w*(entry - sl0) (long, w>1 -> stop piu' lontano = piu' respiro)
|
||||
sl_w = entry + w*(sl0 - entry) (short)
|
||||
ma attiviamo SUBITO un trailing chandelier a k*ATR dall'estremo favorevole. Lo stop
|
||||
attivo nel bar j e' il PIU' PROTETTIVO fra l'SL allargato e il chandelier:
|
||||
long : sl = max(sl_w, max(high[i..j-1]) - k*ATR[j-1])
|
||||
short: sl = min(sl_w, min(low[i..j-1]) + k*ATR[j-1])
|
||||
TP fisso tp0 invariato; horizon = max_bars invariato.
|
||||
|
||||
Logica: finche' il prezzo NON corre a favore, il rischio e' l'SL allargato (respiro:
|
||||
si tollera un ritracciamento iniziale piu' ampio nella speranza che la fade rientri).
|
||||
Appena il prezzo corre a favore, il chandelier sale sopra sl_w e protegge il
|
||||
profitto come un trail normale. Differenza da EXIT-02 (che usa max(sl0, chand)):
|
||||
qui il floor iniziale e' PIU' LARGO di sl0, quindi early il rischio per-trade e'
|
||||
maggiore (peggiora i loser veloci) ma si salvano fade che con sl0 sarebbero state
|
||||
stoppate giusto prima del rientro.
|
||||
|
||||
ANTI-LOOK-AHEAD: levels(j) usa SOLO dati <= j-1 (estremo favorevole su [i..j-1]
|
||||
mantenuto incrementalmente fino a j-1; atr14[j-1]). after_bar non usato.
|
||||
|
||||
GRID: w in {1.5, 2.0} x k in {2.0, 3.0} (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 WideSLTrail(ExitPolicy):
|
||||
name = "wide_sl_trail"
|
||||
|
||||
def __init__(self, ctx, i, d, entry, tp0, sl0, mb, w=1.5, k=2.0, **params):
|
||||
super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
|
||||
self.w = float(w)
|
||||
self.k = float(k)
|
||||
self.high = ctx["high"]
|
||||
self.low = ctx["low"]
|
||||
self.atr = ctx["atr14"]
|
||||
# SL iniziale ALLARGATO: piu' lontano dall'entrata (w>1)
|
||||
if d == 1:
|
||||
self.sl_w = entry - self.w * (entry - sl0)
|
||||
else:
|
||||
self.sl_w = entry + self.w * (sl0 - entry)
|
||||
# estremo favorevole running (solo barre <= j-1); init = barra d'entrata i
|
||||
self.run_hi = self.high[i]
|
||||
self.run_lo = self.low[i]
|
||||
self.last_seen = i
|
||||
|
||||
def _update_running(self, upto: int) -> None:
|
||||
"""Incorpora le barre (last_seen, upto] nell'estremo favorevole. upto = j-1
|
||||
-> NON tocca il bar j (anti-look-ahead)."""
|
||||
while self.last_seen < upto:
|
||||
self.last_seen += 1
|
||||
if self.high[self.last_seen] > self.run_hi:
|
||||
self.run_hi = self.high[self.last_seen]
|
||||
if self.low[self.last_seen] < self.run_lo:
|
||||
self.run_lo = self.low[self.last_seen]
|
||||
|
||||
def levels(self, j: int):
|
||||
self._update_running(j - 1) # solo dati <= j-1
|
||||
a = self.atr[j - 1]
|
||||
if a is None or a != a: # NaN nei primi 14 bar -> usa l'SL allargato
|
||||
return self.tp0, self.sl_w, 1.0
|
||||
if self.d == 1:
|
||||
chand = self.run_hi - self.k * a
|
||||
sl = max(self.sl_w, chand) # il piu' protettivo (stop piu' alto)
|
||||
else:
|
||||
chand = self.run_lo + self.k * a
|
||||
sl = min(self.sl_w, chand) # il piu' protettivo (stop piu' basso)
|
||||
return self.tp0, sl, 1.0
|
||||
|
||||
|
||||
GRID = [
|
||||
{"w": w, "k": k}
|
||||
for w in (1.5, 2.0)
|
||||
for k in (2.0, 3.0)
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
evaluate(WideSLTrail, GRID)
|
||||
Reference in New Issue
Block a user