c7c07f4c35
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
88 lines
4.2 KiB
Python
88 lines
4.2 KiB
Python
"""SKH_P_RR — fine-sweep reward:risk on the ptn_n=55 V1 base.
|
|
|
|
V1 base: SkyhookParams(ptn_n=55, sl_atr=2.5, tp_atr=6.0, vola_lo=35, vola_hi=95, vol_lo=0.0)
|
|
-> minFull +0.69, HOLD +0.64 (BTC 0.64 / ETH 0.64), DD ~40-49% (HIGH).
|
|
|
|
Sweep: sl_atr in {2.0,2.25,2.5,2.75,3.0,3.5} x tp_atr in {5,6,7,8,9,10}.
|
|
Objective: maximize min-asset HOLD-OUT subject to minFull>=0.5, cut DD. Report best + plateau.
|
|
"""
|
|
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
|
|
|
|
BASE = dict(ptn_n=55, vola_lo=35.0, vola_hi=95.0, vol_lo=0.0)
|
|
|
|
SL_GRID = [2.0, 2.25, 2.5, 2.75, 3.0, 3.5]
|
|
TP_GRID = [5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
|
|
|
|
def cell(sl, tp):
|
|
p = SkyhookParams(sl_atr=sl, tp_atr=tp, **BASE)
|
|
out = {}
|
|
for a in ("BTC", "ETH"):
|
|
r = sk.run_asset(a, p, fee_rt=sk.FEE_RT)
|
|
out[a] = r
|
|
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)
|
|
return dict(sl=sl, tp=tp, min_full=min_full, min_hold=min_hold,
|
|
min_tr=min_tr, max_dd=max_dd,
|
|
btc_full=out["BTC"]["full"]["sharpe"], eth_full=out["ETH"]["full"]["sharpe"],
|
|
btc_hold=out["BTC"]["holdout"]["sharpe"], eth_hold=out["ETH"]["holdout"]["sharpe"],
|
|
btc_dd=out["BTC"]["full"]["maxdd"], eth_dd=out["ETH"]["full"]["maxdd"])
|
|
|
|
print("=== SKH_P_RR sweep (ptn_n=55 base) — fee=0.10%RT ===")
|
|
print(f"{'sl':>5} {'tp':>5} | {'minFull':>7} {'minHold':>7} {'minTr':>5} {'maxDD':>6} | "
|
|
f"{'btcF':>5} {'ethF':>5} {'btcH':>5} {'ethH':>5} {'btcDD':>5} {'ethDD':>5}")
|
|
results = []
|
|
for sl in SL_GRID:
|
|
for tp in TP_GRID:
|
|
if tp <= sl: # tp must exceed sl for a sensible R:R; skip degenerate
|
|
continue
|
|
c = cell(sl, tp)
|
|
results.append(c)
|
|
flag = ""
|
|
if c["min_full"] >= 0.5 and c["min_tr"] >= 20:
|
|
flag = " *" # eligible
|
|
print(f"{sl:>5} {tp:>5} | {c['min_full']:>+7.2f} {c['min_hold']:>+7.2f} "
|
|
f"{c['min_tr']:>5} {c['max_dd']*100:>5.0f}% | "
|
|
f"{c['btc_full']:>+5.2f} {c['eth_full']:>+5.2f} "
|
|
f"{c['btc_hold']:>+5.2f} {c['eth_hold']:>+5.2f} "
|
|
f"{c['btc_dd']*100:>4.0f}% {c['eth_dd']*100:>4.0f}%{flag}")
|
|
|
|
# Eligible = minFull>=0.5, minTrades>=20. Rank by min_hold, tie-break lower maxDD.
|
|
elig = [c for c in results if c["min_full"] >= 0.5 and c["min_tr"] >= 20]
|
|
print(f"\nEligible cells (minFull>=0.5, minTr>=20): {len(elig)}")
|
|
if elig:
|
|
elig_sorted = sorted(elig, key=lambda c: (-round(c["min_hold"], 3), c["max_dd"]))
|
|
print("Top by minHold (tie-break lower maxDD):")
|
|
for c in elig_sorted[:6]:
|
|
print(f" sl={c['sl']} tp={c['tp']}: minHold={c['min_hold']:+.2f} "
|
|
f"minFull={c['min_full']:+.2f} maxDD={c['max_dd']*100:.0f}% minTr={c['min_tr']}")
|
|
best = elig_sorted[0]
|
|
# DD-cutting candidate: best minHold among cells with maxDD < V1-ish (lower DD priority)
|
|
dd_cands = sorted(elig, key=lambda c: (c["max_dd"], -round(c["min_hold"], 3)))
|
|
print("\nTop by lowest maxDD (DD-cut objective):")
|
|
for c in dd_cands[:6]:
|
|
print(f" sl={c['sl']} tp={c['tp']}: maxDD={c['max_dd']*100:.0f}% "
|
|
f"minHold={c['min_hold']:+.2f} minFull={c['min_full']:+.2f} minTr={c['min_tr']}")
|
|
|
|
print("\n=== STUDY on best-by-minHold ===")
|
|
pbest = SkyhookParams(sl_atr=best["sl"], tp_atr=best["tp"], **BASE)
|
|
rep = sk.study(f"P_RR_sl{best['sl']}_tp{best['tp']}", pbest)
|
|
print(sk.fmt(rep))
|
|
print("causality:", sk.causality(pbest))
|
|
print("marginal:", {k: v for k, v in sk.marginal(pbest).items()
|
|
if k in ("corr_full","marginal_verdict","has_insample_edge","is_hedge","robust_oos")})
|
|
try:
|
|
mg = sk.marginal(pbest)
|
|
print("marginal-full-keys:", list(mg.keys()))
|
|
print("blend w25 uplift_hold:", mg.get("blends",{}).get("w25",{}).get("uplift_hold"))
|
|
except Exception as e:
|
|
print("marginal err:", e)
|
|
print("\nAS_JSON_STUDY:", sk.as_json(rep))
|
|
else:
|
|
print("No eligible cell — V1 base may already be at the frontier.")
|