Files
PythagorasGoal/scripts/research/skyhook/runs/SKH_P_LOCAL2.py
T
Adriano Dal Pastro de72e3ce1f 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>
2026-06-23 16:10:38 +00:00

66 lines
3.3 KiB
Python

"""SKH_P_LOCAL2 — refine: combine the two winning axes (ptn_n DD-cut + sl/tp hold-lift)
and CHECK MARGINAL clean-year(2025) uplift on the top few, since that is the true gate
(robust_oos requires clean_year_uplift>0.02 AND multicut_persistent)."""
from __future__ import annotations
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/skyhook")
import skyhooklib as sk
from src.strategies.skyhook import SkyhookParams
V1 = dict(ptn_n=55, sl_atr=2.5, tp_atr=6.0, vola_lo=35.0, vola_hi=95.0, vol_lo=0.0)
def evalp(**over):
p = SkyhookParams(**{**V1, **over})
out = {a: sk.run_asset(a, p, fee_rt=sk.FEE_RT) for a in ("BTC","ETH")}
return dict(p=p, over=over,
min_full=min(out[a]["full"]["sharpe"] for a in out),
min_hold=min(out[a]["holdout"]["sharpe"] for a in out),
min_tr=min(out[a]["full"]["n_trades"] for a in out),
max_dd=max(out[a]["full"]["maxdd"] for a in out),
btc_dd=out["BTC"]["full"]["maxdd"], eth_dd=out["ETH"]["full"]["maxdd"])
def row(tag, r):
elig = (r["min_full"]>=0.5 and r["min_tr"]>=20 and r["min_hold"]>=0.5)
print(f"{tag:<26} minFull={r['min_full']:+.2f} minHold={r['min_hold']:+.2f} "
f"maxDD={r['max_dd']*100:>3.0f}% (b{r['btc_dd']*100:.0f}/e{r['eth_dd']*100:.0f}) "
f"minTr={r['min_tr']:>3} {'OK' if elig else 'x'}")
return r
print("=== SKH_P_LOCAL2 combine ptn_n x sl/tp (fee 0.10%RT) ===")
cands=[]
for ptn in (40, 45, 48):
for sl,tp in ((2.0,6.0),(2.0,7.0),(2.25,6.0),(2.25,7.0),(2.5,7.0)):
over=dict(ptn_n=ptn, sl_atr=sl, tp_atr=tp)
cands.append((f"ptn{ptn}_sl{sl}_tp{tp}", row(f"ptn{ptn}_sl{sl}_tp{tp}", evalp(**over))))
# also ptn45 with exit-bar asymmetry that lifted hold
for ul,us in ((30,18),(24,18)):
over=dict(ptn_n=45, uscitalong=ul, uscitashort=us)
cands.append((f"ptn45_ex{ul}/{us}", row(f"ptn45_ex{ul}/{us}", evalp(**over))))
elig=[(t,r) for (t,r) in cands if r["min_full"]>=0.5 and r["min_tr"]>=20 and r["min_hold"]>=0.5]
elig.sort(key=lambda tr:(tr[1]["max_dd"], -round(tr[1]["min_hold"],3)))
print(f"\nEligible: {len(elig)} (sorted by lowest DD)")
for t,r in elig:
print(f" {t:<24} maxDD={r['max_dd']*100:.0f}% minHold={r['min_hold']:+.2f} minFull={r['min_full']:+.2f} over={r['over']}")
# Check MARGINAL clean-year uplift on the lowest-DD eligible + the best-hold eligible.
def marg_check(tag, over):
p = SkyhookParams(**{**V1, **over})
mg = sk.marginal(p)
print(f"\n--- MARGINAL {tag} over={over} ---")
print(f" verdict={mg.get('marginal_verdict')} corr_full={mg.get('corr_full')} "
f"robust_oos={mg.get('robust_oos')} multicut_persistent={mg.get('multicut_persistent')}")
print(f" clean_year_uplift={mg.get('clean_year_uplift')} jackknife_min={mg.get('jackknife_min_uplift')} "
f"has_insample_edge={mg.get('has_insample_edge')} is_hedge={mg.get('is_hedge')}")
print(f" blend w25 uplift_hold={mg.get('blends',{}).get('w25',{}).get('uplift_hold')} "
f"uplift_full={mg.get('blends',{}).get('w25',{}).get('uplift_full')}")
return mg
# pick up to 4 distinct configs to marginal-check
seen=set(); checked=0
for t,r in elig:
key=tuple(sorted(r["over"].items()))
if key in seen: continue
seen.add(key); marg_check(t, r["over"]); checked+=1
if checked>=5: break