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:
Adriano Dal Pastro
2026-06-05 17:56:16 +00:00
parent 6f86c644bf
commit bd6232dc00
16 changed files with 2413 additions and 3 deletions
@@ -0,0 +1,203 @@
"""SH01 exit policy 03 — pct_fixed.
SL fisso in PERCENTUALE del prezzo d'ingresso: sl = entry * (1 - d*p).
Griglia p in {0.01, 0.015, 0.02, 0.03, 0.04, 0.05}, modalita' {intrabar, close}
-> 12 celle. Il livello e' FISSO (deciso a open_trade su close[i]) -> nessun
look-ahead nei bar successivi (i livelli usano solo dati <= i).
Protocollo: grid SOLO sul train; plateau (>=3 celle adiacenti migliorative);
poi OOS una volta per la config scelta + le 2 vicine.
cd /opt/docker/PythagorasGoal && uv run python scripts/analysis/sh01_exit_policies/03_pct_fixed.py
"""
from __future__ import annotations
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal")
from scripts.analysis.sh01_exit_lab import ( # noqa: E402
ASSETS, OOS_START_MS, ExitPolicy, load_sleeves, simulate,
)
class PctFixed(ExitPolicy):
"""SL fisso a una frazione p del prezzo d'ingresso."""
def __init__(self, p: float, mode: str = "intrabar"):
self.p = p
self.mode = mode
self.name = f"pct_fixed p={p:.3f} {mode}"
def open_trade(self, ctx, i, d):
entry = ctx["close"][i]
sl = entry * (1.0 - d * self.p) # long: sotto; short: sopra
return {"sl": sl}
def levels(self, ctx, i, d, j, st):
return st["sl"], self.mode
# ----------------------------------------------------------------------------- grid
P_GRID = [0.01, 0.015, 0.02, 0.03, 0.04, 0.05]
MODES = ["intrabar", "close"]
def _row(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}%")
def main():
sleeves = load_sleeves()
# baseline (no stop)
print("=" * 110)
print("BASELINE (orizzonte puro, no SL) — TRAIN:")
base = {}
for a in ASSETS:
m = simulate(sleeves[a], ExitPolicy(), t_hi=OOS_START_MS)
base[a] = m
print(f" {a}: {_row(m)}")
print()
# ---------------- grid TRAIN only
print("=" * 110)
print("GRID — TRAIN ONLY (selezione qui):")
train = {}
for mode in MODES:
print(f"\n mode={mode}")
for p in P_GRID:
pol = PctFixed(p, mode)
row = {}
for a in ASSETS:
m = simulate(sleeves[a], pol, t_hi=OOS_START_MS)
row[a] = m
train[(mode, p)] = row
print(f" p={p:.3f} | BTC {_row(row['BTC'])}")
print(f" | ETH {_row(row['ETH'])}")
# improvement flags vs baseline on TRAIN: ETH gate (sharpe up, dd down, worst less neg)
# + BTC not degraded (sharpe>=0.95x, ret>=0.80x)
print("\n" + "=" * 110)
print("TRAIN improvement check (cell = migliorativa se ETH sharpe^ dd v worst^ AND BTC sharpe>=95% ret>=80%):")
bE, bB = base["ETH"], base["BTC"]
improved = {}
for mode in MODES:
flags = []
for p in P_GRID:
r = train[(mode, p)]
eth, btc = r["ETH"], r["BTC"]
eth_ok = (eth["sharpe"] > bE["sharpe"] and eth["dd"] < bE["dd"]
and eth["worst"] > bE["worst"])
btc_ok = (btc["sharpe"] >= 0.95 * bB["sharpe"]
and btc["ret"] >= 0.80 * bB["ret"])
cell = eth_ok and btc_ok
improved[(mode, p)] = cell
flags.append("Y" if cell else (".|E" if not eth_ok else ".|B"))
print(f" mode={mode:<9s} " + " ".join(f"p={p:.3f}:{f}" for p, f in zip(P_GRID, flags)))
# plateau detection: >=3 adjacent p's (same mode) all improved
print("\nPLATEAU (>=3 p adiacenti migliorativi nella stessa modalita'):")
plateau_cells = []
for mode in MODES:
run = []
runs = []
for p in P_GRID:
if improved[(mode, p)]:
run.append(p)
else:
if len(run) >= 1:
runs.append(run)
run = []
if run:
runs.append(run)
for run in runs:
mark = " <-- PLATEAU" if len(run) >= 3 else ""
print(f" mode={mode}: run {run} (len {len(run)}){mark}")
if len(run) >= 3:
plateau_cells.extend((mode, p) for p in run)
if not plateau_cells:
print("\nNESSUN PLATEAU sul train -> famiglia NON passa. OOS solo informativo.")
else:
print(f"\nplateau cells: {plateau_cells}")
# ---------------- pick best cell on TRAIN within plateau (or best overall if no plateau)
def score(cell):
r = train[cell]
# ETH train e' il banco di prova (baseline negativo) -> max ETH sharpe,
# tie-break ETH dd minore, poi BTC sharpe.
return (r["ETH"]["sharpe"], -r["ETH"]["dd"], r["BTC"]["sharpe"])
pool = plateau_cells if plateau_cells else list(train.keys())
best = max(pool, key=score)
print(f"\nCHOSEN (train): mode={best[0]} p={best[1]:.3f}")
# neighbors (same mode, adjacent p)
mode_b, p_b = best
idx = P_GRID.index(p_b)
neigh = [(mode_b, P_GRID[k]) for k in (idx - 1, idx, idx + 1) if 0 <= k < len(P_GRID)]
# ---------------- OOS verdict (chosen + 2 neighbors) — looked at ONCE
print("\n" + "=" * 110)
print("OOS VERDICT (config scelta + 2 vicine) — guardato UNA volta:")
print("\nBaseline OOS:")
base_oos = {}
for a in ASSETS:
m = simulate(sleeves[a], ExitPolicy(), t_lo=OOS_START_MS)
base_oos[a] = m
print(f" {a}: {_row(m)}")
chosen_oos = None
for cell in neigh:
pol = PctFixed(cell[1], cell[0])
tag = " <== CHOSEN" if cell == best else ""
print(f"\n mode={cell[0]} p={cell[1]:.3f}{tag}")
res = {}
for a in ASSETS:
tr = simulate(sleeves[a], pol, t_hi=OOS_START_MS)
oo = simulate(sleeves[a], pol, t_lo=OOS_START_MS)
res[a] = {"train": tr, "oos": oo}
print(f" {a} TRAIN {_row(tr)}")
print(f" {a} OOS {_row(oo)}")
if cell == best:
chosen_oos = res
# ---------------- gate evaluation on chosen
print("\n" + "=" * 110)
print("GATE (tutte e 4, train E oos):")
r = chosen_oos
bE_o, bB_o = base_oos["ETH"], base_oos["BTC"]
def g(label, cond):
print(f" [{'PASS' if cond else 'FAIL'}] {label}")
return cond
# a) ETH: sharpe^ dd v worst^ su train E oos
a_tr = (r["ETH"]["train"]["sharpe"] > bE["sharpe"]
and r["ETH"]["train"]["dd"] < bE["dd"]
and r["ETH"]["train"]["worst"] > bE["worst"])
a_oo = (r["ETH"]["oos"]["sharpe"] > bE_o["sharpe"]
and r["ETH"]["oos"]["dd"] < bE_o["dd"]
and r["ETH"]["oos"]["worst"] > bE_o["worst"])
A = g("a) ETH sharpe^ dd v worst^ (train E oos)", a_tr and a_oo)
# b) BTC sharpe>=95% ret>=80% baseline (train E oos)
b_tr = (r["BTC"]["train"]["sharpe"] >= 0.95 * bB["sharpe"]
and r["BTC"]["train"]["ret"] >= 0.80 * bB["ret"])
b_oo = (r["BTC"]["oos"]["sharpe"] >= 0.95 * bB_o["sharpe"]
and r["BTC"]["oos"]["ret"] >= 0.80 * bB_o["ret"])
B = g("b) BTC sharpe>=95% ret>=80% (train E oos)", b_tr and b_oo)
# c) ret ETH oos >= 80% baseline
C = g("c) ret ETH oos >= 80% baseline", r["ETH"]["oos"]["ret"] >= 0.80 * bE_o["ret"])
# d) plateau
D = g("d) plateau confermato", bool(plateau_cells) and best in plateau_cells)
passes = A and B and C and D
print(f"\n ==> GATE {'PASS' if passes else 'FAIL'}")
return passes
if __name__ == "__main__":
main()