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
@@ -228,3 +228,38 @@ confermato due cautele (fill intrabar, OOS rumoroso). **Il candidato più sensat
2018 (27.7%)** grazie al regime-gate. **Prima di QUALSIASI deploy**: (1) backtest che INCLUDA il
2018 nel gate (non solo IDX 2021+), (2) ledger shadow reale per i fill intrabar/maker, (3) half-size.
La pulizia del feed resta il guadagno trasversale più grande (irrobustisce ogni backtest del progetto).
---
## 8. I 3 PASSI PRE-DEPLOY + STUDIO SL/TP (ladder_sltp_study.py)
**[1] Valutazione 2018-INCLUSIVE** (standalone, tutta la storia; il gate IDX2021+ è cieco al 2018):
| Candidato (sl0.12/tp0.05) | full_sh | full_dd | oos_sh | DD 2018 | DD 2021 |
|---|---:|---:|---:|---:|---:|
| BTC 1h L6 **range1.5** | 3.18 | 27.7% | 4.95 | **27.7%** | 14.8% |
| BTC 1h L3 none | 2.93 | 60.2% | 5.51 | **50.3%** | 10.8% |
Il regime-gate già dimezza il tail vero (2018: 50%→−27.7%). Senza gate la griglia long-only è insostenibile (60%).
**Studio SL/TP** (sweep sl_buf × tp_buf sul candidato regime-gated, full 2018+):
- **Sweet spot: `sl_buf=0.10, tp_buf=0.03` → DD 2018 23.5%** (da 27.7%), oos_sh 5.06, full_sh 2.92. Migliora il tail SENZA intaccare l'edge.
- **Lezione (conferma il prior del progetto):** un SL **troppo stretto PEGGIORA** (sl 0.06 → 33/35%): stoppa e rideploya più in basso nel coltello che cade = la trappola del falso-negativo mean-reversion. Un SL **troppo largo** (0.20) idem (29/31%, oos più basso). Il bene sta in mezzo (0.10).
- **Il SL da solo (senza regime-gate) è ERRATICO** (tabella L3 none non-monotona: 20% → 52% → 43% al variare di pochi punti): è il **regime-gate** il controllo primario della coda; il SL moderato fine-tuna. Catastrophe-control = "non deployare nel bear" (regime) + "cappa l'episodio" (SL 0.10), NON uno stop stretto.
**[2] Fill maker vs taker** (Deribit: i livelli sono LIMIT → maker):
| fee | oos_sh | DD 2018 | gate½ |
|---|---:|---:|---|
| taker 0.10% RT (harness) | 5.06 | 23.5% | PROMOSSO (OOS 10.86→11.0, corr 0.195) |
| **maker 0%** (Deribit reale) | 5.18 | 22.7% | PROMOSSO (OOS→11.0, corr 0.194) |
Il maker è leggermente MIGLIORE → la harness taker è conservativa, nessun fee-cliff. (La parte live = shadow ledger reale resta il passo operativo finale.)
**[3] Half-size:** il gate "half" è già a metà size e PROMOSSO; la coda 2018 standalone 23.5% si dimezza sul book (~12% in un 2018-repeat a half-size) = reale ma gestibile.
### CONFIG FINALE raccomandata (con SL/TP aggiunti)
> **BTC 1h · regime=range trend_max 1.5 · rd 0.20 / ru 0.06 / 6 livelli · sl_buf 0.10 / tp_buf 0.03 · HALF-size.**
> - Tail 2018-inclusive: 23.5% standalone (~12% sul book a half) — reale ma controllato dal regime-gate + SL moderato.
> - Gate PORT06: PROMOSSO (OOS 10.86→11.0, corr 0.195); maker Deribit lo migliora; nessun fee-cliff.
> - **Unico passo residuo: shadow ledger reale** (fill intrabar/maker su Deribit) — operativo, non backtestabile.
Caveat onesti residui: l'OOS gain al peso deployabile è modesto (10.86→11.0); il tail 2018 non è zero (12% a half); l'edge dipende dai fill intrabar (per i LIMIT è legittimo, ma lo shadow lo deve confermare).
+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()