config(PORT06): cap SHAPE 0.0588 — SH01 resta senza SL (ricerca multi-agente: 11 famiglie di stop, 0 sopravvissute)
Crash ETH 2026-06-05: SH01 ETH −15.6% su un trade (exit solo a orizzonte, nessuna protezione). Ricerca con harness dedicato sh01_exit_lab (cache walk-forward, engine fill gap-aware worse(livello,open), parity esatta con explore_lab, train<=2023-11-01): ATR intrabar/close-confirm, %, chandelier, breakeven, giveback, loser-timestop, disaster-cap close+intrabar, swing, vol-regime — NESSUNA passa il gate (ogni stop stretto rompe BTC, ogni stop largo non tocca la coda ETH; nei crash il fill e' al gap). Mitigazione: peso famiglia SHAPE 11.8%->5.9% in PORT06 (FULL 6.47->6.43 DD 4.10->3.96, OOS 8.82->8.58 DD 1.30->1.36) — la prossima coda impatta il conto per meta'. Regression-lock test aggiornato. Diario: docs/diary/2026-06-05-sh01-sl-research.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
"""SH01 EXIT POLICY 07 — LOSER time-stop condizionale (after_bar).
|
||||
|
||||
Idea: SH01 esce a orizzonte fisso H=12. Se a un check-point intermedio j=i+m il
|
||||
trade e' GIA' in perdita oltre una soglia, esci subito al close invece di tenere
|
||||
fino a H. L'ipotesi (lezione exit-lab fade: tagliare i loser presto rischia di
|
||||
tagliare anche i winner in drawdown temporaneo): su un orizzonte FISSO di 12 bar
|
||||
forse un loser conclamato a meta' corsa raramente recupera, mentre i winner del
|
||||
modello partono subito (asimmetria). Il time-stop e' UNA volta sola (al bar m),
|
||||
non un trailing: non insegue il prezzo, condiziona solo l'uscita a un istante.
|
||||
|
||||
Regola (after_bar):
|
||||
al bar j == i + m: se (close[j]-entry)/entry * d < -x * ATR14[i] / entry
|
||||
esci al close del bar j.
|
||||
Equivalente: directional_move[j] < -x*ATR14[i]. x=0.0 => esci se in QUALSIASI
|
||||
perdita direzionale al bar m.
|
||||
|
||||
Griglia m in {2, 3, 4, 6} x x in {0.0, 0.5, 1.0}.
|
||||
|
||||
ANTI-LOOK-AHEAD: ATR14[i] e entry=close[i] fissati all'ingresso (dati <= i);
|
||||
after_bar decide sul close del bar j (dati <= j, eseguibile al poll del tick).
|
||||
Nessun indicatore al bar j stesso.
|
||||
|
||||
cd /opt/docker/PythagorasGoal && uv run python scripts/analysis/sh01_exit_policies/07_loser_timestop.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/opt/docker/PythagorasGoal")
|
||||
|
||||
from scripts.analysis.sh01_exit_lab import ( # noqa: E402
|
||||
ExitPolicy, evaluate, load_sleeves, simulate, OOS_START_MS,
|
||||
)
|
||||
|
||||
|
||||
class LoserTimestop(ExitPolicy):
|
||||
def __init__(self, m: int, x: float):
|
||||
self.m = int(m)
|
||||
self.x = float(x)
|
||||
self.name = f"loser_timestop m={m} x={x:g}"
|
||||
|
||||
def open_trade(self, ctx: dict, i: int, d: int) -> dict:
|
||||
return {
|
||||
"entry": float(ctx["close"][i]),
|
||||
"atr": float(ctx["atr14"][i]),
|
||||
}
|
||||
|
||||
def after_bar(self, ctx: dict, i: int, d: int, j: int, st: dict) -> bool:
|
||||
if j != i + self.m:
|
||||
return False
|
||||
move = (ctx["close"][j] - st["entry"]) * d # directional, in price
|
||||
thresh = -self.x * st["atr"]
|
||||
return move < thresh
|
||||
|
||||
|
||||
GRID_M = [2, 3, 4, 6]
|
||||
GRID_X = [0.0, 0.5, 1.0]
|
||||
|
||||
|
||||
def _fmt(m):
|
||||
return (f"ret={m['ret']:>+7.0f}% dd={m['dd']:>4.0f}% shrp={m['sharpe']:>5.2f} "
|
||||
f"worst={m['worst']:>+5.1f}% stop={m['stop_rate']:>4.1f}% n={m['trades']}")
|
||||
|
||||
|
||||
def main():
|
||||
sleeves = load_sleeves()
|
||||
|
||||
base = {}
|
||||
for a in ("BTC", "ETH"):
|
||||
ctx = sleeves[a]
|
||||
base[a] = {
|
||||
"train": simulate(ctx, ExitPolicy(), t_hi=OOS_START_MS),
|
||||
"oos": simulate(ctx, ExitPolicy(), t_lo=OOS_START_MS),
|
||||
}
|
||||
|
||||
print("=" * 110)
|
||||
print("BASELINE (exit orizzonte puro):")
|
||||
for a in ("BTC", "ETH"):
|
||||
print(f" {a} TRAIN {_fmt(base[a]['train'])}")
|
||||
print(f" {a} OOS {_fmt(base[a]['oos'])}")
|
||||
|
||||
print("=" * 110)
|
||||
print("GRID — TRAIN ONLY (selezione parametri): m x rows")
|
||||
train_res = {}
|
||||
for mm in GRID_M:
|
||||
for xx in GRID_X:
|
||||
pol = LoserTimestop(mm, xx)
|
||||
row = {}
|
||||
for a in ("BTC", "ETH"):
|
||||
row[a] = simulate(sleeves[a], pol, t_hi=OOS_START_MS)
|
||||
train_res[(mm, xx)] = row
|
||||
print(f" m={mm} x={xx:g} | BTC {_fmt(row['BTC'])}")
|
||||
print(f" | ETH {_fmt(row['ETH'])}")
|
||||
|
||||
print("=" * 110)
|
||||
print("OOS (verdetto, intera griglia per ispezione plateau):")
|
||||
for mm in GRID_M:
|
||||
for xx in GRID_X:
|
||||
pol = LoserTimestop(mm, xx)
|
||||
line_b = simulate(sleeves["BTC"], pol, t_lo=OOS_START_MS)
|
||||
line_e = simulate(sleeves["ETH"], pol, t_lo=OOS_START_MS)
|
||||
print(f" m={mm} x={xx:g} | BTC OOS {_fmt(line_b)}")
|
||||
print(f" | ETH OOS {_fmt(line_e)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user