Files
PythagorasGoal/scripts/research/xsec/runs/XS01b.py
T
Adriano Dal Pastro 9612560479 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>
2026-06-20 21:36:57 +00:00

59 lines
2.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""XS01b — Double-sort Momentum × Low-Vol
Score = xs_zscore(past_return(close, 60)) + xs_zscore(-roll_std(ret, 30))
Combines cross-sectional momentum with low-vol preference (lower realized vol = higher score).
Grid: universe x H x k variations, <=5 total backtests.
"""
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/xsec")
import xslib as xs
import numpy as np
# --- score factory ---
def score_mom_lowvol(mom_L=60, vol_win=30):
"""Double-sort: momentum z + low-vol z. Both causal (data <= close[i])."""
def _score(P):
mom = xs.xs_zscore(xs.past_return(P.close, mom_L))
# low vol = higher score -> negate std
lowvol = xs.xs_zscore(-xs.roll_std(P.ret, vol_win))
return mom + lowvol
return _score
# Grid (<=5 calls total):
# 1. Baseline: majors H10 k5 LS (19 assets, closest to XS01 universe)
# 2. All universe H10 k5 LS
# 3. All universe H5 k5 LS (faster rebalance)
# 4. Majors H10 k5 LS with longer mom window (90d) to differ from XS01
# 5. All universe H10 k7 LS (wider book)
configs = [
dict(name="XS01b-MAJ-H10-k5", universe="majors", H=10, k=5, long_short=True, fn=score_mom_lowvol(60,30)),
dict(name="XS01b-ALL-H10-k5", universe="all", H=10, k=5, long_short=True, fn=score_mom_lowvol(60,30)),
dict(name="XS01b-ALL-H5-k5", universe="all", H=5, k=5, long_short=True, fn=score_mom_lowvol(60,30)),
dict(name="XS01b-MAJ-H10-MOM90", universe="majors", H=10, k=5, long_short=True, fn=score_mom_lowvol(90,30)),
dict(name="XS01b-ALL-H10-k7", universe="all", H=10, k=7, long_short=True, fn=score_mom_lowvol(60,30)),
]
results = []
for cfg in configs:
print(f"\nRunning {cfg['name']} ...")
fn = cfg.pop("fn")
rep = xs.study_xs(score_fn=fn, **cfg)
results.append(rep)
print(xs.fmt(rep))
print()
# --- pick best: prefer earns_slot, then hold-out sharpe, then corr_xs01 < 0.6
def score_result(r):
earns = 1 if r["earns_slot"] else 0
hold_sh = r["holdout"].get("sharpe", -99)
full_sh = r["full"]["sharpe"]
distinct = 1 if (r["corr_xs01"] or 1.0) < 0.6 else 0
return (earns, hold_sh, full_sh, distinct)
best = max(results, key=score_result)
print("\n" + "="*60)
print("BEST CONFIG:")
print(xs.fmt(best))
print("JSON:", xs.as_json(best))