feat(skyhook): SKH01-V2-DD — asymmetric %-exits cut standalone DD <30% (2-wave agent research)
Second agent wave (skyhook-improve-v2, 14 DD-reduction families, each adversarially verified by 2 skeptics) beats the prior winner on the only unmet goal (DD<30%). Winner = ASYM_LS -> promoted to engine as SKH01_V2_DD: same signal (ptn_n=45, vola[35,95], vol_lo=0, exit-bars 24/16) but exits switched from ATR to FIXED-PCT ASYMMETRIC — long sl4%/tp10%, short sl2%(tighter)/tp8%. The tight short %-SL caps the per-trade loss that forms the maxDD in vol spikes. Verified (sk.study, independent re-run): standalone maxDD BTC 21.4% / ETH 27.4% (<30%), minFull +0.99, minHold +1.26, causality 0/400 both assets, fee-surviving to 0.40%RT, marginal vs TP01 ADDS (corr 0.09, in-sample edge, robust_oos, multicut, clean-year +0.57), blend 0.75*TP01+0.25*SKH uplift_hold +0.87; blend 50/50 full 1.84/hold 1.59/DD 10.7%. Plateau (not knife-edge); both skeptics holds_up=high, killer=null. Engine: per-direction short exit overrides (exit_mode_short/sl_*_short/tp_*_short), backward-compatible (None -> symmetric, V1/intermediate-winner unchanged). +3 tests (8/8 pass). Lessons: DD is cut by changing the exit MECHANISM (%-SL, L/S asymmetry, ensembles), NOT by entry-only kill-switch / vol-target / cadence. PATTERN_CONF killed as overfit (knife-edge). PCTL_DD unverified (rate-limit) and ENS_PARAM/TPSL_DD recency/hedge-loaded -> forward-monitor. NOT yet wired to live sleeves: re-verify blend@0.25 + causality on execution code before deploy. Includes both waves' research scripts (runs/SKH_* wave 1, runs/SKH2_* wave 2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -135,13 +135,21 @@ class SkyhookParams:
|
||||
# uscite — time-based asimmetrico (barre LTF)
|
||||
uscitalong: int = 24
|
||||
uscitashort: int = 18
|
||||
# uscite — hard stop/profit
|
||||
# uscite — hard stop/profit (LONG, e SHORT se gli override sotto sono None)
|
||||
exit_mode: str = "atr" # 'atr' = multipli di ATR LTF ; 'pct' = percentuale fissa
|
||||
sl_atr: float = 2.0
|
||||
tp_atr: float = 5.0
|
||||
sl_pct: float = 0.03
|
||||
tp_pct: float = 0.075
|
||||
ltf_atr_win: int = 14
|
||||
# uscite — OVERRIDE asimmetrico SHORT (None = usa i valori simmetrici sopra).
|
||||
# In crypto lo short si fa steamrollare da uno spike vola: stop short piu' stretti
|
||||
# tagliano il draw-down standalone senza toccare il segnale (vedi SKH01-V2-DD, diario).
|
||||
exit_mode_short: str | None = None
|
||||
sl_atr_short: float | None = None
|
||||
tp_atr_short: float | None = None
|
||||
sl_pct_short: float | None = None
|
||||
tp_pct_short: float | None = None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -204,14 +212,21 @@ def skyhook_entries(ltf: pd.DataFrame, htf: pd.DataFrame, p: SkyhookParams | Non
|
||||
continue
|
||||
if comp_long[i]:
|
||||
direction, mb = 1, p.uscitalong
|
||||
mode, sl_a, tp_a, sl_p, tp_p = p.exit_mode, p.sl_atr, p.tp_atr, p.sl_pct, p.tp_pct
|
||||
elif comp_short[i]:
|
||||
direction, mb = -1, p.uscitashort
|
||||
# SHORT: usa l'override asimmetrico dove presente, altrimenti i valori simmetrici.
|
||||
mode = p.exit_mode_short if p.exit_mode_short is not None else p.exit_mode
|
||||
sl_a = p.sl_atr_short if p.sl_atr_short is not None else p.sl_atr
|
||||
tp_a = p.tp_atr_short if p.tp_atr_short is not None else p.tp_atr
|
||||
sl_p = p.sl_pct_short if p.sl_pct_short is not None else p.sl_pct
|
||||
tp_p = p.tp_pct_short if p.tp_pct_short is not None else p.tp_pct
|
||||
else:
|
||||
continue
|
||||
if p.exit_mode == "atr":
|
||||
sl_off, tp_off = p.sl_atr * a[i], p.tp_atr * a[i]
|
||||
if mode == "atr":
|
||||
sl_off, tp_off = sl_a * a[i], tp_a * a[i]
|
||||
else:
|
||||
sl_off, tp_off = p.sl_pct * c[i], p.tp_pct * c[i]
|
||||
sl_off, tp_off = sl_p * c[i], tp_p * c[i]
|
||||
if direction == 1:
|
||||
sl, tp = c[i] - sl_off, c[i] + tp_off
|
||||
else:
|
||||
@@ -221,6 +236,24 @@ def skyhook_entries(ltf: pd.DataFrame, htf: pd.DataFrame, p: SkyhookParams | Non
|
||||
return entries
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Config canoniche (vedi docs/diary/2026-06-23-skyhook.md)
|
||||
# ---------------------------------------------------------------------------
|
||||
# SKH01-V1: vincente del primo lever-scout/grid (regime gate + breakout lento + stop larghi).
|
||||
SKH01_V1 = SkyhookParams(ptn_n=55, sl_atr=2.5, tp_atr=6.0, vola_lo=35.0, vola_hi=95.0, vol_lo=0.0)
|
||||
|
||||
# SKH01-V2-DD: vincente dell'onda DD-reduction (famiglia ASYM_LS). Stesso SEGNALE del winner
|
||||
# intermedio (ptn_n=45, banda vola larga) ma EXIT a percentuale fissa ASIMMETRICA: short con SL
|
||||
# piu' stretto (2% vs 4% long) -> taglia il draw-down standalone (maxDD BTC 21% / ETH 27% <30%)
|
||||
# alzando hold-out e uplift di portafoglio. Verificato leak-free + 2 scettici avversariali.
|
||||
SKH01_V2_DD = SkyhookParams(
|
||||
ptn_n=45, vola_lo=35.0, vola_hi=95.0, vol_lo=0.0,
|
||||
uscitalong=24, uscitashort=16,
|
||||
exit_mode="pct", sl_pct=0.04, tp_pct=0.10, # LONG
|
||||
exit_mode_short="pct", sl_pct_short=0.02, tp_pct_short=0.08, # SHORT (SL piu' stretto)
|
||||
)
|
||||
|
||||
|
||||
def signal_counts(ltf: pd.DataFrame, htf: pd.DataFrame, p: SkyhookParams | None = None) -> dict:
|
||||
"""Diagnostica: quante barre passano regime/pattern/composer (prima del cap giornaliero)."""
|
||||
p = p or SkyhookParams()
|
||||
|
||||
Reference in New Issue
Block a user