feat(portfolio): XS01 cross-sectional (Hyperliquid) BATTE il portafoglio -> TP01 70% + XS01 30%
Espansione universo (su input utente "storico da cerbero"): il Cerbero MCP col token MAINNET serve Hyperliquid (230 perp REALI, storia nativa dal 2024). fetch_hyperliquid.py certifica 19 alt liquidi a 1d (flat 0%, cross-venue 4-9 bps vs Binance) -> data/raw/hl_*_1d.parquet. Abilita le strategie CROSS-SECTIONAL (impossibili a 2 asset). XS01 = cross-sectional momentum market-neutral (long 5 forti / short 5 deboli su ret 30g, ogni 10g, vol-target 20%). Validato onesto: plateau (config/k/subset), fee-robusto (0.3% RT), scorrelato a TP01 (-0.06), positivo OGNI anno 2024-26, meccanismo complementare (lavora nella dispersione quando TP01 e' in cash). Diverso dal regime-luck RV bocciato (19 asset, plateau, ogni anno+). Contributo al portafoglio (outer-join + pesi rinormalizzati per sleeve a date diverse): TP01-solo FULL 1.30 / HOLD 0.31 -> TP01 70% + XS01 30%: FULL 1.41 / HOLD 1.15, DD giu', ~ogni anno+. -> XS01 BATTE il portafoglio esistente: inserito in active_sleeves. Caveat (documentati): storia XS ~2.5 anni; STAT-MODE (book 19 gambe non eseguibile a 2k -> ~20k), sleeve diagnostico/forward-monitor. portfolio.combine ora outer-join+renorm. 12 test passano. Diario 2026-06-19-hyperliquid-xsec.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,8 +17,8 @@ from pathlib import Path
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
import numpy as np
|
||||
from src.data.downloader import load_data
|
||||
from scripts.analysis.research_lab import backtest, buy_hold, mc_pvalue, VAL_START, HOLDOUT_START
|
||||
import pandas as pd
|
||||
from scripts.analysis.research_lab import backtest, buy_hold, mc_pvalue, load_tf, ts, _net_series, VAL_START, HOLDOUT_START
|
||||
|
||||
|
||||
def load_signal(path):
|
||||
@@ -58,7 +58,7 @@ def main():
|
||||
res = {"asset": asset, "tf": tf, "sigfile": sigfile}
|
||||
try:
|
||||
signal = load_signal(sigfile)
|
||||
df = load_data(asset, tf)
|
||||
df = load_tf(asset, tf)
|
||||
pos = np.asarray(signal(df, asset, tf), float)
|
||||
res["n"] = int(len(df))
|
||||
res["len_ok"] = bool(len(pos) == len(df))
|
||||
@@ -81,6 +81,19 @@ def main():
|
||||
null_p=round(p, 4),
|
||||
beats_bh=bool(full.sharpe > bh.sharpe and oos.sharpe > 0),
|
||||
)
|
||||
# breadth per-anno (pre-hold-out): % anni positivi, anni rossi consecutivi
|
||||
net, _, _, _ = _net_series(df, pos)
|
||||
s = pd.Series(net, index=ts(df))
|
||||
s = s[s.index < pd.Timestamp(HOLDOUT_START, tz="UTC")]
|
||||
yr = {int(y): float((1 + g).prod() - 1) for y, g in s.groupby(s.index.year)}
|
||||
vals = list(yr.values())
|
||||
max_consec_red = 0; cur = 0
|
||||
for v in vals:
|
||||
cur = cur + 1 if v < 0 else 0
|
||||
max_consec_red = max(max_consec_red, cur)
|
||||
res["per_year_preho"] = {y: round(v, 3) for y, v in yr.items()}
|
||||
res["pct_years_pos"] = round(sum(v > 0 for v in vals) / len(vals), 2) if vals else 0.0
|
||||
res["max_consec_red_years"] = int(max_consec_red)
|
||||
if holdout:
|
||||
ho = backtest(df, pos, tf, lo=HOLDOUT_START)
|
||||
res["holdout_sharpe"] = round(ho.sharpe, 3)
|
||||
|
||||
Reference in New Issue
Block a user