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,74 @@
|
||||
"""EXIT-13 — hurst_exit: TP condizionato al REGIME (rolling-Hurst causale).
|
||||
|
||||
IDEA. Le fade puntano alla MEDIA: in regime ANTI-PERSISTENTE (Hurst basso) la
|
||||
reversione tende a completarsi -> ha senso tenere il TP pieno tp0. In regime
|
||||
PERSISTENTE/trending (Hurst alto) la reversione spesso NON arriva fino in fondo
|
||||
(il movimento continua, lo stop-loss si concentra li': vedi loss-guard Hurst) ->
|
||||
conviene "prendere quel che c'e'": TP anticipato a meta' strada (entry+tp0)/2.
|
||||
|
||||
- H[j-1] < h_lo (anti-persistente): tp(j) = tp0 (reversione completa)
|
||||
- H[j-1] >= h_lo (persistente): tp(j) = (entry+tp0)/2 (TP a meta')
|
||||
|
||||
SL FISSO (sl0) e horizon (max_bars) invariati.
|
||||
|
||||
ANTI-LOOK-AHEAD. prepare() precalcola H = rolling_hurst(close, window=100, step=6)
|
||||
una volta sullo sleeve. rolling_hurst e' causale: H[k] usa returns[k-window:k] e
|
||||
returns[m]=diff(log(close))[m] dipende da close[m+1], quindi H[k] dipende solo da
|
||||
close <= k. In levels(j) leggo H[j-1] -> solo dati <= j-1. SL/horizon invariati.
|
||||
La decisione del regime e' fissata col bar precedente, il bar j tocca i livelli.
|
||||
|
||||
NB sul TP a meta'. Lo "step=6" significa che H e' costante a tratti di 6 barre; il
|
||||
regime e' ri-letto ogni bar (j-1) ma cambia valore ogni 6. Il TP a meta' strada
|
||||
e' SEMPRE >= breakeven per costruzione (e' fra entry e tp0, e tp0 e' gia' oltre il
|
||||
margine fee per come la fade lo fissa), quindi non maschera uscite in perdita.
|
||||
|
||||
GRID: h_lo in {0.45, 0.50, 0.55} (3 celle).
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
from exit_lab import ExitPolicy, evaluate # noqa: E402
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
|
||||
from src.fractal.indicators import rolling_hurst # noqa: E402
|
||||
|
||||
|
||||
class HurstExit(ExitPolicy):
|
||||
name = "hurst_exit"
|
||||
|
||||
@classmethod
|
||||
def prepare(cls, ctx, **params):
|
||||
if "hurst100" not in ctx:
|
||||
c = ctx["close"]
|
||||
ctx["hurst100"] = rolling_hurst(c, window=100, step=6)
|
||||
|
||||
def __init__(self, ctx, i, d, entry, tp0, sl0, mb, **params):
|
||||
super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
|
||||
self.h = ctx["hurst100"]
|
||||
self.h_lo = float(params.get("h_lo", 0.50))
|
||||
self.tp_half = (entry + tp0) / 2.0 # TP a meta' strada (persistente)
|
||||
|
||||
def levels(self, j: int):
|
||||
# SOLO dati <= j-1
|
||||
hv = self.h[j - 1]
|
||||
if not np.isfinite(hv):
|
||||
return self.tp0, self.sl0, 1.0
|
||||
if hv < self.h_lo:
|
||||
tp = self.tp0 # anti-persistente: reversione completa
|
||||
else:
|
||||
tp = self.tp_half # persistente: prendi la meta'
|
||||
return tp, self.sl0, 1.0
|
||||
|
||||
|
||||
GRID = [
|
||||
{"h_lo": 0.45},
|
||||
{"h_lo": 0.50},
|
||||
{"h_lo": 0.55},
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
evaluate(HurstExit, GRID)
|
||||
Reference in New Issue
Block a user