"""SKH_R_PCTL final: verify top configs with sk.study + marginal, refine for minFull/DD.""" from __future__ import annotations import sys sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/skyhook") import numpy as np, pandas as pd import skyhooklib as sk from src.backtest.harness import backtest_signals from src.strategies import skyhook as S from src.strategies.skyhook import SkyhookParams, HTF_MIN # import the structural builder from the sweep script import importlib.util spec = importlib.util.spec_from_file_location( "skr", "/opt/docker/PythagorasGoal/scripts/research/skyhook/runs/SKH_R_PCTL.py") skr = importlib.util.module_from_spec(spec) # avoid running its __main__ import builtins _orig_name = "__main__" spec.loader.exec_module(skr) # defines functions; __main__ guard prevents the sweep HOLDOUT = sk.HOLDOUT FEE = sk.FEE_RT def study_struct(name, cfg, p): """sk.study-equivalent for our structural variant: FULL+HOLD+fee-sweep+per-year BOTH assets.""" per_asset = {} fee_ok_all = True for a in ("BTC", "ETH"): ltf, htf = sk.frames(a) ent = skr.pctl_entries(ltf, htf, p, **cfg) m = backtest_signals(ltf, ent, fee_rt=FEE, leverage=1.0, asset=a, tf="230m") eq = m.equity idx = pd.DatetimeIndex(pd.to_datetime(m.eq_index, utc=True)) hmask = np.asarray(idx >= HOLDOUT) full = dict(sharpe=round(m.sharpe, 3), ret=round(m.net_return, 4), maxdd=round(m.max_dd, 4), n_trades=int(m.n_trades), win_rate=round(m.win_rate, 1)) hold = skr._split(eq, idx, hmask) sweep = {} for f in (0.0, 0.001, 0.002, 0.003): mf = backtest_signals(ltf, ent, fee_rt=f, leverage=1.0, asset=a, tf="230m") sweep[f"{f*100:.2f}%"] = round(mf.sharpe, 3) fee_ok_all = fee_ok_all and (sweep["0.30%"] > 0) per_asset[a] = dict(full=full, hold=hold, yearly={int(y): round(v, 4) for y, v in m.yearly.items()}, fee_sweep=sweep) mf = min(per_asset[a]["full"]["sharpe"] for a in per_asset) mh = min(per_asset[a]["hold"]["sharpe"] for a in per_asset) mt = min(per_asset[a]["full"]["n_trades"] for a in per_asset) mdd = max(per_asset[a]["full"]["maxdd"] for a in per_asset) grade = "PASS" if (mt >= 20 and mf >= 0.5 and mh >= 0.2 and fee_ok_all) else \ ("WEAK" if (mt >= 20 and mf >= 0.3 and mh >= 0.0) else "FAIL") print(f"\n=== {name} -> {grade} (minFull={mf:+.2f} minHold={mh:+.2f} minTr={mt} maxDD={mdd*100:.0f}% feeOK={fee_ok_all})") for a in per_asset: pa = per_asset[a] yr = " ".join(f"{y}:{r*100:+.0f}%" for y, r in pa["yearly"].items()) print(f" {a}: FULL Sh={pa['full']['sharpe']:+.2f} ret={pa['full']['ret']*100:+.0f}% DD={pa['full']['maxdd']*100:.0f}%" f" n={pa['full']['n_trades']} wr={pa['full']['win_rate']:.0f}% | HOLD Sh={pa['hold']['sharpe']:+.2f} ret={pa['hold']['ret']*100:+.0f}%") print(f" fee: " + " ".join(f"{k}={v:+.2f}" for k, v in pa["fee_sweep"].items())) print(f" yr: {yr}") return dict(grade=grade, minFull=mf, minHold=mh, minTr=mt, maxDD=mdd, fee_ok=fee_ok_all, per_asset=per_asset) def marginal_struct(cfg, p): import altlib as al def daily(a): ltf, htf = sk.frames(a) ent = skr.pctl_entries(ltf, htf, p, **cfg) m = backtest_signals(ltf, ent, fee_rt=FEE, leverage=1.0, asset=a, tf="230m") s = pd.Series(m.equity, index=pd.DatetimeIndex(pd.to_datetime(m.eq_index, utc=True))) return s.resample("1D").last().ffill().pct_change().dropna() sb, se = daily("BTC"), daily("ETH") J = pd.concat({"BTC": sb, "ETH": se}, axis=1, join="inner").fillna(0.0) cand = 0.5 * J["BTC"] + 0.5 * J["ETH"] return al.marginal_vs_tp01(cand) if __name__ == "__main__": p = skr.v1_like_params() # Candidate A: best minHold (exp_volaHi_volHi) -- minFull 0.53 cfgA = dict(vola_win=None, vol_win=None, vola_lo=0.35, vola_hi=0.95, vol_lo=0.40, vol_hi=1.0) # Candidate B: best minFull + lower DD (exp_volaLo_vol0) -- minFull 0.70, DD 39% cfgB = dict(vola_win=None, vol_win=None, vola_lo=0.0, vola_hi=0.70, vol_lo=0.0, vol_hi=1.0) # Refinements to lift minFull on A while keeping hold-out: tighten vola band / add small vol floor cfgC = dict(vola_win=None, vol_win=None, vola_lo=0.10, vola_hi=0.80, vol_lo=0.30, vol_hi=1.0) # B + modest vol floor to keep DD low but lift hold cfgD = dict(vola_win=None, vol_win=None, vola_lo=0.0, vola_hi=0.70, vol_lo=0.30, vol_hi=1.0) rA = study_struct("PCTL-A exp_volaHi_volHi", cfgA, p) rB = study_struct("PCTL-B exp_volaLo_vol0", cfgB, p) rC = study_struct("PCTL-C exp_volaLoMid_volFloor", cfgC, p) rD = study_struct("PCTL-D exp_volaLo_volFloor", cfgD, p) print("\n\n##### MARGINAL vs TP01 #####") for tag, cfg, r in [("A", cfgA, rA), ("B", cfgB, rB), ("C", cfgC, rC), ("D", cfgD, rD)]: mg = marginal_struct(cfg, p) print(f"[{tag}] grade={r['grade']} minFull={r['minFull']:+.2f} minHold={r['minHold']:+.2f} DD={r['maxDD']*100:.0f}%" f" | corr_full={mg.get('corr_full')} upliftHold={mg.get('blends',{}).get('w25',{}).get('uplift_hold')}" f" verdict={mg.get('marginal_verdict')} robust_oos={mg.get('robust_oos')}" f" insample_edge={mg.get('has_insample_edge')} hedge={mg.get('is_hedge')}" f" cleanYr={mg.get('clean_year_uplift')}")