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:
Adriano Dal Pastro
2026-06-19 20:05:45 +00:00
parent 18f22160b2
commit a5a61ac7e3
10 changed files with 512 additions and 15 deletions
+14
View File
@@ -50,6 +50,20 @@ def test_metrics_basic():
assert m["ret"] > 0 and m["maxdd"] == 0.0 and m["n"] == 730
def test_outer_join_renormalizes_late_sleeve():
# sleeve con date d'inizio diverse: prima parte A da solo (peso rinormalizzato a 1),
# poi A+B (pesi 0.7/0.3). Il portafoglio NON si tronca alla finestra comune.
idxA = pd.date_range("2020-01-01", periods=120, freq="1D", tz="UTC")
idxB = pd.date_range("2020-02-15", periods=60, freq="1D", tz="UTC")
A = Sleeve("A", 0.7, lambda: pd.Series(0.001, index=idxA))
B = Sleeve("B", 0.3, lambda: pd.Series(0.003, index=idxB))
combo = StrategyPortfolio([A, B]).combined_daily()
assert abs(combo.iloc[0] - 0.001) < 1e-12 # solo A -> 100% A
both = combo[combo.index >= idxB[0]]
assert abs(both.iloc[0] - (0.7 * 0.001 + 0.3 * 0.003)) < 1e-12 # blend rinormalizzato
assert len(combo) == 120 # span completo di A, non tronca
def test_empty_portfolio_raises():
with pytest.raises(ValueError):
StrategyPortfolio([])