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:
@@ -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).
|
||||
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user