ad65a0b344
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>
74 lines
2.9 KiB
Python
74 lines
2.9 KiB
Python
"""EXIT-07 — TP dinamico che DECADE col tempo (verso breakeven).
|
|
|
|
PRIOR (vincolante, ieri SCARTATO un ladder 80/20): il TP delle fade sta alla
|
|
MEDIA, e li' il movimento e' esaurito -> il prezzo spesso NON arriva al TP e il
|
|
trade muore a max_bars (close) o sullo SL. Idea di questa famiglia: invece di
|
|
tenere il TP fisso al target ambizioso (la media), farlo DECADERE col passare
|
|
delle barre verso un target piu' modesto (breakeven che copre le fee, o meta'
|
|
strada). Cosi' un trade che s'e' avvicinato ma non ha toccato il TP pieno puo'
|
|
essere chiuso in profitto/pareggio prima di scadere a max_bars potenzialmente
|
|
in perdita. SL FISSO (sl0) invariato. horizon = max_bars invariato.
|
|
|
|
TP attivo nel bar j:
|
|
frac(j) = clamp( ((j - i) / mb) * speed , 0, 1 )
|
|
tp(j) = tp0 + frac(j) * (target_fin - tp0)
|
|
- frac dipende SOLO da j (indice di tempo) e da costanti note all'entrata
|
|
(i, mb, speed) -> NESSUN dato > j-1 -> anti-look-ahead OK per costruzione.
|
|
- long (d=+1): tp0 > entry; target_fin <= tp0 -> il TP SCENDE verso entry.
|
|
- short (d=-1): tp0 < entry; target_fin >= tp0 -> il TP SALE verso entry.
|
|
- speed>1: il TP raggiunge il target finale prima della fine (clamp a 1).
|
|
|
|
target_fin (param "target"):
|
|
- "breakeven": entry*(1 + d*0.003) -> copre 0.3% (~ fee 0.10%RT + margine).
|
|
Per long sta sopra entry, per short sotto: chiudere li' NON e' in perdita.
|
|
- "halfway" : (entry + tp0)/2 -> meta' strada fra entry e il TP pieno.
|
|
|
|
GRID: speed in {0.5, 1.0, 1.5} x target in {breakeven, halfway} (6 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 TpDecay(ExitPolicy):
|
|
name = "tp_decay"
|
|
|
|
def __init__(self, ctx, i, d, entry, tp0, sl0, mb, **params):
|
|
super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
|
|
self.speed = float(params.get("speed", 1.0))
|
|
target = str(params.get("target", "breakeven"))
|
|
if target == "breakeven":
|
|
self.target_fin = entry * (1.0 + d * 0.003)
|
|
elif target == "halfway":
|
|
self.target_fin = 0.5 * (entry + tp0)
|
|
else:
|
|
raise ValueError(f"target sconosciuto: {target}")
|
|
# mb puo' essere 0 in teoria; protezione
|
|
self.mb_eff = max(int(mb), 1)
|
|
|
|
def levels(self, j: int):
|
|
# frac dipende SOLO dal tempo (j - i), mb, speed: costanti note all'entrata
|
|
frac = ((j - self.i) / self.mb_eff) * self.speed
|
|
if frac < 0.0:
|
|
frac = 0.0
|
|
elif frac > 1.0:
|
|
frac = 1.0
|
|
tp = self.tp0 + frac * (self.target_fin - self.tp0)
|
|
return tp, self.sl0, 1.0
|
|
|
|
|
|
GRID = [
|
|
{"speed": 0.5, "target": "breakeven"},
|
|
{"speed": 1.0, "target": "breakeven"},
|
|
{"speed": 1.5, "target": "breakeven"},
|
|
{"speed": 0.5, "target": "halfway"},
|
|
{"speed": 1.0, "target": "halfway"},
|
|
{"speed": 1.5, "target": "halfway"},
|
|
]
|
|
|
|
|
|
if __name__ == "__main__":
|
|
evaluate(TpDecay, GRID)
|