research(xsec): sweep cross-sectional su Hyperliquid (43 script/257 config) + verifica avversariale

Nuova harness condivisa xslib.py (panel HL certificato, score per-asset causale, book
long-k/short-k vol-targeted leak-free) + 43 script in runs/ su 11 famiglie (MOM/REV/VOL/
DIST/LIQ/VAL/STRUCT/UNIV). Scoring = earns_slot (full>0 AND hold-out>0 AND marginal ADDS
al portafoglio live AND corr XS01<0.6, con jackknife drop-one-month).

Find: 42/257 config earns_slot=True, ma TUTTE con corr TP01 -0.2..-0.4 e PnL ~solo 2025.
Verify (verify_survivors.py, 3 scettici deterministici):
 - S1 redundancy: cluster low-vol = UNA scommessa (XV01=XU02=1.00, XV02/XV03 r 0.44-0.67);
   XM09/XL02/XS06b/XR02 distinti (corr media off-diag +0.20).
 - S2 short-beta: cluster low-vol carica 0.44-0.70 su short-market -> NON market-neutral,
   e' un tilt short-alt-beta di regime. XM09(0.08)/XR02(-0.21) NON short-beta.
 - S3 per-anno: cluster low-vol decade (XV01/XU02 2026 -0.09); XL02 morto (2025 -0.14,
   2026 -0.43); XM09 (0.82/0.50/0.74) e XR02 (0.84/0.40/2.68) positivi in tutti e 3 gli anni.

Esito: nessuna sleeve nuova. Cluster low-vol RIGETTATO (regime-bet), XL02 RIGETTATO (overfit).
2 LEAD genuini (XM09 trend-gated x-sec momentum, XR02 reversal vol-gated) -> forward-monitor,
non deployabili (panel 2.5y regime unico + STAT-MODE esecuzione). Portafoglio live invariato.

Incluso anche options_vrp_managed.py (A/B VRP01 hold-to-expiry vs gestione attiva del doc
credit-spread): la gestione attiva DISTRUGGE l'edge (combo FULL managed Sh -1.29 vs HtE +0.96,
il delta-exit taglia i vincenti) -> scartata, VRP01 resta hold-to-expiry.

Diari: 2026-06-20-xsec-strategies-sweep.md, 2026-06-20-vrp-active-management.md.
gitignore: data/paper_portfolio/ (stato runtime paper) + scripts/research/xsec/runs/out/ (output rigenerabile).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-06-20 21:36:57 +00:00
parent 5ac4e16af8
commit 9612560479
50 changed files with 5457 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
"""XM05 — Momentum Acceleration
MECHANISM: Score = past_return(close, L_short) - past_return(close, L_long)
i.e. is momentum ACCELERATING? The idea: assets that are outperforming
recently vs. their longer-run momentum are gaining momentum -> rank them
high. Assets that were strong long-term but are slowing down -> rank low.
L_short=20, L_long=60 (canonical config).
Grid: vary universe (all/majors), H (5/10), and L_short param
to find the best config within <=5 backtests.
Distinctness target: if score is correlated to raw momentum (XS01), it's just XS01.
If acceleration captures something different (regime change, reversal of leaders), it
could be distinct and add to portfolio.
"""
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/xsec")
import xslib as xs
import numpy as np
print("XM05 — Momentum Acceleration (L_short - L_long)")
print("=" * 60)
def mom_accel(close, L_short, L_long):
"""Score = short-term return minus long-term return (causal). Higher = accelerating."""
r_short = xs.past_return(close, L_short)
r_long = xs.past_return(close, L_long)
return r_short - r_long
# --- 5 targeted backtests ---
# 1) Canonical config: all universe, L_short=20, L_long=60, H=10, LS
rep1 = xs.study_xs(
"XM05_ALL_20_60",
lambda P: mom_accel(P.close, 20, 60),
universe="all", H=10, k=5, long_short=True
)
print(xs.fmt(rep1))
print("JSON:", xs.as_json(rep1))
print()
# 2) Majors universe (19 XS01 assets), same canonical L_short=20, L_long=60
rep2 = xs.study_xs(
"XM05_MAJ_20_60",
lambda P: mom_accel(P.close, 20, 60),
universe="majors", H=10, k=5, long_short=True
)
print(xs.fmt(rep2))
print("JSON:", xs.as_json(rep2))
print()
# 3) All universe, shorter window: L_short=10, L_long=30 (faster acceleration signal)
rep3 = xs.study_xs(
"XM05_ALL_10_30",
lambda P: mom_accel(P.close, 10, 30),
universe="all", H=10, k=5, long_short=True
)
print(xs.fmt(rep3))
print("JSON:", xs.as_json(rep3))
print()
# 4) All universe, L_short=20, L_long=60, longer holding period H=20
rep4 = xs.study_xs(
"XM05_ALL_20_60_H20",
lambda P: mom_accel(P.close, 20, 60),
universe="all", H=20, k=5, long_short=True
)
print(xs.fmt(rep4))
print("JSON:", xs.as_json(rep4))
print()
# 5) All universe, longer windows: L_short=30, L_long=90 (medium-term acceleration)
rep5 = xs.study_xs(
"XM05_ALL_30_90",
lambda P: mom_accel(P.close, 30, 90),
universe="all", H=10, k=5, long_short=True
)
print(xs.fmt(rep5))
print("JSON:", xs.as_json(rep5))
print()
# Pick best: prefer earns_slot, then hold-out sharpe, then distinctness from XS01
all_reps = [rep1, rep2, rep3, rep4, rep5]
def score_rep(r):
earns = int(r["earns_slot"])
hold_sh = r["holdout"].get("sharpe", -9)
full_sh = r["full"]["sharpe"]
corr_xs01 = r.get("corr_xs01") or 1.0
distinctness = 1 - abs(corr_xs01) # higher = more distinct
return (earns, hold_sh, full_sh, distinctness)
best = max(all_reps, key=score_rep)
print("=" * 60)
print(f"BEST CONFIG: {best['name']}")
print(xs.fmt(best))
print("JSON:", xs.as_json(best))