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,85 @@
|
||||
"""EXIT-03 — trail_pct: trailing percentuale dall'high-water-mark favorevole (sui CLOSE).
|
||||
|
||||
Idea: invece di un trail ATR (EXIT-01/02), lo stop segue l'estremo favorevole
|
||||
RAGGIUNTO dai CLOSE a una distanza percentuale fissa p. Lo stop puo' solo
|
||||
stringersi (mai allentarsi sotto sl0 lato rischio).
|
||||
|
||||
Long : hwm = max(close[i..j-1]); stop(j) = max(sl0, hwm*(1-p)) (sale, mai scende)
|
||||
Short: lwm = min(close[i..j-1]); stop(j) = min(sl0, lwm*(1+p)) (scende, mai sale)
|
||||
|
||||
Due varianti:
|
||||
keep_tp=True -> il TP fisso tp0 dall'entrata RESTA attivo (esci al primo dei due),
|
||||
horizon = max_bars (invariato). Il trail morde solo se il TP non
|
||||
viene raggiunto e il prezzo ha prima corso a favore e poi ritracciato.
|
||||
keep_tp=False -> TP RIMOSSO (cavalcata pura), horizon = 2*max_bars (cap HARD_CAP).
|
||||
Si esce solo sul trailing-stop o a orizzonte.
|
||||
|
||||
ANTI-LOOK-AHEAD: levels(j) usa SOLO dati <= j-1:
|
||||
- hwm/lwm sullo slice [i..j-1] dei CLOSE (mantenuto incrementalmente col bar j-1);
|
||||
- p e' una costante -> nessuna dipendenza da indicatori del bar j.
|
||||
L'hwm sui close (non sugli high) e' deliberato: il close e' il dato su cui il
|
||||
worker live decide al poll del tick; un hwm sugli high anticiperebbe un livello
|
||||
che il close non ha ancora confermato.
|
||||
|
||||
GRID: p in {0.005, 0.01, 0.02} x keep_tp in {True, False} (6 celle).
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from exit_lab import ExitPolicy, evaluate, HARD_CAP # noqa: E402
|
||||
|
||||
|
||||
class TrailPct(ExitPolicy):
|
||||
name = "trail_pct"
|
||||
|
||||
def __init__(self, ctx, i, d, entry, tp0, sl0, mb,
|
||||
p=0.01, keep_tp=True, **params):
|
||||
super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
|
||||
self.p = float(p)
|
||||
self.keep_tp = bool(keep_tp)
|
||||
if not self.keep_tp:
|
||||
self.horizon = min(2 * mb, HARD_CAP)
|
||||
self.close = ctx["close"]
|
||||
# high/low-water-mark sui CLOSE, solo barre <= j-1; init = close d'entrata i
|
||||
self.hwm = self.close[i]
|
||||
self.lwm = self.close[i]
|
||||
self.last_seen = i # ultimo indice gia' incorporato
|
||||
# stop trailing monotono: parte da sl0 e puo' solo stringersi
|
||||
self.cur_stop = sl0
|
||||
|
||||
def _update_wm(self, upto: int) -> None:
|
||||
"""Incorpora le barre (last_seen, upto] nell'estremo sui close. upto = j-1
|
||||
-> NON tocca il bar j (anti-look-ahead)."""
|
||||
while self.last_seen < upto:
|
||||
self.last_seen += 1
|
||||
cv = self.close[self.last_seen]
|
||||
if cv > self.hwm:
|
||||
self.hwm = cv
|
||||
if cv < self.lwm:
|
||||
self.lwm = cv
|
||||
|
||||
def levels(self, j: int):
|
||||
self._update_wm(j - 1) # solo dati <= j-1
|
||||
if self.d == 1:
|
||||
cand = self.hwm * (1.0 - self.p)
|
||||
if cand > self.cur_stop: # lo stop long puo' solo SALIRE
|
||||
self.cur_stop = cand
|
||||
else:
|
||||
cand = self.lwm * (1.0 + self.p)
|
||||
if cand < self.cur_stop: # lo stop short puo' solo SCENDERE
|
||||
self.cur_stop = cand
|
||||
tp = self.tp0 if self.keep_tp else None
|
||||
return tp, self.cur_stop, 1.0
|
||||
|
||||
|
||||
GRID = [
|
||||
{"p": p, "keep_tp": kt}
|
||||
for p in (0.005, 0.01, 0.02)
|
||||
for kt in (True, False)
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
evaluate(TrailPct, GRID)
|
||||
Reference in New Issue
Block a user