feat(analysis): ladder_sltp_study — 3 passi pre-deploy + SL/TP, config finale ladder

I 3 passi: [1] valutazione 2018-INCLUSIVE (il gate IDX2021+ e' cieco al 2018) -> il tail
vero del BTC ladder e' il 2018 (-27.7% regime-gated / -50% none); [2] fill maker 0% vs
taker 0.10% (Deribit = LIMIT = maker) -> maker leggermente MIGLIORE, harness conservativa,
nessun fee-cliff; [3] half-size (la coda 2018 si dimezza sul book). STUDIO SL/TP: sweet spot
sl_buf=0.10/tp_buf=0.03 cappa il 2018 a -23.5% (da -27.7) senza intaccare l'edge (oos 5.06).
Lezione (conferma prior progetto): SL troppo stretto PEGGIORA (redeploy nel coltello = falso
negativo MR), SL da solo senza regime-gate e' erratico -> il regime-gate e' il controllo
PRIMARIO della coda, il SL moderato fine-tuna. Config finale: BTC 1h range1.5 rd0.20 ru0.06
L6 sl0.10 tp0.03 half-size, PROMOSSO (OOS 10.86->11.0, corr 0.195). Unico passo residuo:
shadow ledger reale (operativo). Diario sez. 8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-06-18 14:48:39 +00:00
parent 8abeb7a83f
commit 587fbc0f61
2 changed files with 162 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
"""LADDER SL/TP STUDY (2026-06-18) — i 3 passi pre-deploy + studio di SL e TP da aggiungere.
Contesto: dopo clean_feed.py i Price Ladder BTC/ETH sono candidati VERI (PROMOSSO, DD gate
2021+ ~11-15%), MA il tail REALE e' il 2018 (-44/-52%) che il gate (IDX 2021+) NON vede. SL/TP
sono la leva per domarlo. Prior del progetto: gli stop su mean-reversion sono falsi negativi
(EXIT-16/SH01/pairs z-stop) -> ma un grid in un BEAR sostenuto (2018, niente rimbalzo) e' il
caso in cui un catastrophe-SL genuinamente aiuta. Questo studio distingue i due regimi.
Fa i 3 passi:
1. VALUTAZIONE 2018-INCLUSIVE: metriche standalone su TUTTA la storia (2018+) + DD per anno
(il gate del progetto e' cieco al 2018; qui no).
2. FILL maker vs taker: il grid e' LIMIT -> su Deribit fill MAKER ~0%; confronto 0% vs 0.10%
RT (la harness e' conservativa). E' la preparazione backtest dello shadow ledger (la parte
live = deploy operativo a parte).
3. HALF-SIZE: il candidato finale a meta' size (prudenza coda).
E studia SL (sl_buf, = catastrophe stop sotto il range) x TP (tp_buf) sul tail 2018 vs edge 2021+.
uv run python scripts/analysis/ladder_sltp_study.py
"""
from __future__ import annotations
import sys
from pathlib import Path
import numpy as np
import pandas as pd
PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))
from scripts.analysis.ladder_search import regime_mask, _gate
from scripts.analysis.grid_game_gate import grid_mtm
OOS_DATE = pd.Timestamp("2024-10-12", tz="UTC")
def fm(eqd: pd.Series) -> dict:
"""Metriche su TUTTA la storia (2018+), niente reindex a IDX 2021+."""
def sh(s):
r = s.pct_change().fillna(0.0)
return float(r.mean() / r.std() * np.sqrt(365)) if r.std() > 0 else 0.0
def dd(s):
c = s / s.iloc[0]
return float(((c - c.cummax()) / c.cummax()).min() * 100)
oos = eqd[eqd.index >= OOS_DATE]
peryear = {int(y): round(dd(g), 1) for y, g in eqd.groupby(eqd.index.year)}
return {"full_sh": round(sh(eqd), 2), "full_dd": round(dd(eqd), 1),
"oos_sh": round(sh(oos), 2) if len(oos) > 5 else 0.0,
"dd2018": peryear.get(2018, 0.0), "dd2022": peryear.get(2022, 0.0),
"peryear": peryear}
def run(asset, tf, rd, ru, levels, sl_buf, tp_buf, max_bars, regime, tmax, fee_side=0.0005):
mask = regime_mask(asset, tf, trend_max=tmax) if regime == "range" else None
eqd, st = grid_mtm(asset, tf=tf, range_down=rd, range_up=ru, levels=levels,
sl_buf=sl_buf, tp_buf=tp_buf, max_bars=max_bars,
deploy_mask=mask, fee_side=fee_side)
m = fm(eqd); m["trades"] = st["trades"]; m["eqd"] = eqd
return m
# candidati base (i migliori del re-gate pulito)
BASES = {
"BTC 1h L6 range1.5": dict(asset="BTC", tf="1h", rd=0.20, ru=0.06, levels=6,
max_bars=720, regime="range", tmax=1.5),
"BTC 1h L3 none": dict(asset="BTC", tf="1h", rd=0.08, ru=0.06, levels=3,
max_bars=720, regime="none", tmax=2.0),
}
def sltp_sweep(name, base):
print(f"\n{'='*104}\n SL/TP SWEEP — {name} (full=2018+, dd2018=tail vero, oos=2024-10+; fee 0.10% RT)\n{'='*104}")
print(f" {'sl_buf':>7}{'tp_buf':>7}{'trades':>8}{'full_sh':>9}{'full_dd':>9}{'dd2018':>8}{'dd2022':>8}{'oos_sh':>8}")
best = None
for slb in (0.06, 0.08, 0.10, 0.12, 0.15, 0.20):
for tpb in (0.03, 0.05, 0.08):
m = run(**base, sl_buf=slb, tp_buf=tpb)
star = ""
# criterio: tail 2018 contenuto (>-25%) E oos edge preservato (sh>3) E full edge ok
if m["dd2018"] > -25 and m["oos_sh"] > 3 and m["full_sh"] > 1.5:
star = " <-- tail-capped + edge"
if best is None or m["dd2018"] > best[1]["dd2018"]:
best = ((slb, tpb), m)
print(f" {slb:>7.2f}{tpb:>7.2f}{m['trades']:>8}{m['full_sh']:>9.2f}"
f"{m['full_dd']:>9.1f}{m['dd2018']:>8.1f}{m['dd2022']:>8.1f}{m['oos_sh']:>8.2f}{star}")
return best
def main():
print("LADDER SL/TP STUDY — 3 passi pre-deploy + SL/TP da aggiungere\n")
# passo 1: valutazione 2018-inclusive dei due base (sl/tp correnti)
print("[1] VALUTAZIONE 2018-INCLUSIVE (sl/tp correnti) — DD per anno (il gate IDX2021+ e' cieco al 2018):")
for name, base in BASES.items():
m = run(**base, sl_buf=0.12, tp_buf=0.05)
print(f" {name:<22} full_sh {m['full_sh']:>5.2f} full_dd {m['full_dd']:>6.1f} "
f"oos_sh {m['oos_sh']:>5.2f} | DD/anno {m['peryear']}")
# passo SL/TP: sweep su entrambi
winners = {}
for name, base in BASES.items():
winners[name] = sltp_sweep(name, base)
# passo 2+3: maker vs taker + half-size + gate 2021+, sul miglior (sl,tp) del candidato regime-gated
name = "BTC 1h L6 range1.5"
w = winners.get(name)
print(f"\n{'='*104}\n [2+3] FILL maker/taker + HALF-SIZE + GATE 2021+ — {name}\n{'='*104}")
if w is None:
print(" nessun (sl,tp) ha cappato il tail 2018 sotto -25% mantenendo l'edge: vedi sweep sopra.")
# usa comunque lo sl piu' stretto per il confronto fill
(slb, tpb) = (0.06, 0.05)
else:
(slb, tpb), _ = w
print(f" miglior (sl_buf,tp_buf) per tail 2018 + edge = ({slb}, {tpb})")
base = BASES[name]
for fee, lab in ((0.0005, "taker 0.10% RT"), (0.0, "maker 0% (Deribit limit)")):
m = run(**base, sl_buf=slb, tp_buf=tpb, fee_side=fee)
g = _gate(m["eqd"])
print(f" fee={lab:<26} oos_sh {m['oos_sh']:>5.2f} dd2018 {m['dd2018']:>6.1f} "
f"gate½ {g['verdict_half']} (OOS {g['base_oos_sh']}->{g['half_oos_sh']}, corr {g['max_corr_existing']})")
print("\n NB half-size: il gate 'half' E' gia' a meta' size (vedi grid_game_gate). La coda 2018")
print(" standalone va dimezzata sul book a half. Lo shadow ledger reale (fill intrabar/maker)")
print(" resta il passo OPERATIVO finale, non backtestabile qui.")
if __name__ == "__main__":
main()