research: strato trend multi-asset (52 alt) RIDONDANTE col trend TP01 -> non aggiunto
TSMOM CANONICAL applicato a ogni alt dei 52, equal-weight. Standalone FULL 0.66 ma HOLD-OUT -1.03 (long negli alt nel calo 2025-26), corr a TP01 +0.74 (stessa beta direzionale). Contributo al portafoglio NEGATIVO (HOLD -0.16/-0.27). Broadenizzare il TREND non diversifica: e' la stessa direzionalita' su asset piu' rumorosi. Solo il market-neutral (XS01) diversifica davvero. Chiude il filone espansione-universo (XS-52, top-liquidita' dinamico, trend-52: tutti peggiori). Configurazione validata invariata: TP01 70% + XS01 (19 major) 30%, FULL Sh 1.41 / HOLD 1.15. I margini reali sono in un MECCANISMO diverso (opzioni VRP), non nell'universo crypto-direzionale. Diario 2026-06-19-trend-multiasset.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
# 2026-06-19 — Strato trend multi-asset sui 52 alt: RIDONDANTE col trend di TP01
|
||||||
|
|
||||||
|
Tentativo: aggiungere un terzo sleeve = TSMOM (stessa logica TP01 CANONICAL, long-flat vol-target)
|
||||||
|
applicato a OGNI alt dei 52 Hyperliquid certificati, equal-weight ragged. Idea: trend più
|
||||||
|
diversificato che diversifichi TP01 (BTC/ETH). `scripts/portfolio/trend_multiasset.py`.
|
||||||
|
|
||||||
|
## Esito: ridondante e peggiore
|
||||||
|
- **TREND-52 standalone**: FULL Sh 0.66, **HOLD-OUT −1.03** (negativo), anni+ 33%. Gli alt sono
|
||||||
|
stati long nel calo 2025-26 e hanno sanguinato — a differenza di TP01 (BTC/ETH) che il
|
||||||
|
vol-target+trend portò in cash. I trend degli alt sono più rumorosi/whippy.
|
||||||
|
- **corr a TP01 = +0.74** (stessa beta direzionale, come previsto) | corr a XS01 −0.05.
|
||||||
|
- **Contributo al portafoglio (TP01 70 + XS 30):** +TREND-52 w20% → FULL −0.01, **HOLD −0.16**;
|
||||||
|
w30% → FULL −0.02, **HOLD −0.27**. PEGGIORA.
|
||||||
|
|
||||||
|
## Lezione
|
||||||
|
Broadenizzare il TREND su molti alt NON diversifica: è la **stessa direzionalità** (corr 0.74 con
|
||||||
|
TP01) su asset più rumorosi → aggiunge perdita/rumore, non edge. La dimensione trend è già catturata
|
||||||
|
in modo pulito da TP01 (BTC/ETH, vol-targeted). L'unica espansione che diversifica davvero resta
|
||||||
|
quella **market-neutral** (XS01 cross-sectional), perché è ortogonale alla beta direzionale.
|
||||||
|
|
||||||
|
## Conclusione (chiusura del filone "espansione universo")
|
||||||
|
Esplorate tutte le vie di espansione sui certificati Hyperliquid:
|
||||||
|
1. XS su 52-all → diluito (memecoin), negativo.
|
||||||
|
2. XS top-liquidità dinamico → peggiore del fisso-19 (liquidità ≠ qualità).
|
||||||
|
3. Trend multi-asset su 52 → ridondante (corr 0.74) + hold-out negativo.
|
||||||
|
Nessuna migliora il portafoglio. **Configurazione validata e invariata: TP01 70% + XS01 (19 major)
|
||||||
|
30% — FULL Sh 1.41 / HOLD 1.15.** I margini reali per crescere NON sono nell'universo crypto-
|
||||||
|
direzionale (saturo), ma in un MECCANISMO diverso (opzioni VRP, in attesa di dati di stress reali).
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
"""STRATO TREND MULTI-ASSET sui 52 alt Hyperliquid certificati (diversificazione del trend).
|
||||||
|
|
||||||
|
TP01 e' TSMOM vol-target long-flat su BTC+ETH (2 asset). Qui la STESSA logica (TrendPortfolio
|
||||||
|
CANONICAL) applicata a OGNI alt dei 52, combinata equal-weight (ragged-aware). Idea: un trend
|
||||||
|
piu' diversificato. Test onesto: e' correlato a TP01 (entrambi trend)? aggiunge al portafoglio
|
||||||
|
TP01+XS01 nel hold-out? Causale, netto fee.
|
||||||
|
|
||||||
|
uv run python scripts/portfolio/trend_multiasset.py
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
import sys, glob
|
||||||
|
from pathlib import Path
|
||||||
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
sys.path.insert(0, str(PROJECT_ROOT))
|
||||||
|
import numpy as np, pandas as pd
|
||||||
|
from src.strategies.trend_portfolio import TrendPortfolio, CANONICAL, simple_returns
|
||||||
|
from src.portfolio.portfolio import to_daily, metrics, HOLDOUT, Sleeve, StrategyPortfolio
|
||||||
|
from src.portfolio.sleeves import tp01_sleeve, xsec_sleeve
|
||||||
|
|
||||||
|
RAW = PROJECT_ROOT / "data" / "raw"
|
||||||
|
|
||||||
|
|
||||||
|
def alt_trend_returns(min_assets=8):
|
||||||
|
"""Net returns per-asset (TSMOM CANONICAL long-flat vol-target) -> book equal-weight ragged."""
|
||||||
|
eng = TrendPortfolio(**CANONICAL)
|
||||||
|
series = {}
|
||||||
|
for p in sorted(glob.glob(str(RAW / "hl_*_1d.parquet"))):
|
||||||
|
sym = Path(p).stem.replace("hl_", "").replace("_1d", "").upper()
|
||||||
|
d = pd.read_parquet(p)
|
||||||
|
d = d.copy(); d["datetime"] = pd.to_datetime(d["timestamp"], unit="ms", utc=True)
|
||||||
|
c = d["close"].values.astype(float)
|
||||||
|
r = simple_returns(c); tgt = eng.target_series(d)
|
||||||
|
held = np.zeros(len(tgt)); held[1:] = tgt[:-1]
|
||||||
|
net = held * r - eng.fee_side * np.abs(np.diff(held, prepend=0.0)); net[0] = 0.0
|
||||||
|
series[sym] = pd.Series(np.clip(net, -0.99, None), index=d["datetime"])
|
||||||
|
M = pd.concat(series, axis=1, join="outer").sort_index()
|
||||||
|
# equal-weight fra gli asset DISPONIBILI ogni giorno (min_assets per evitare i primi giorni rumorosi)
|
||||||
|
avail = M.notna().sum(axis=1)
|
||||||
|
book = M.mean(axis=1, skipna=True).where(avail >= min_assets)
|
||||||
|
return book.dropna(), M
|
||||||
|
|
||||||
|
|
||||||
|
def ev(d, label):
|
||||||
|
f = metrics(d); h = metrics(d[d.index >= HOLDOUT])
|
||||||
|
yr = [float((1 + g).prod() - 1) for _, g in d.groupby(d.index.year)]
|
||||||
|
pct = sum(v > 0 for v in yr) / len(yr) if yr else 0
|
||||||
|
print(f" {label:<28} FULL Sh {f['sharpe']:>5.2f} ret {f['ret']*100:>+6.0f}% DD {f['maxdd']*100:>4.0f}% | "
|
||||||
|
f"HOLD Sh {h['sharpe']:>5.2f} | anni+ {pct*100:.0f}%")
|
||||||
|
return f, h
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("=" * 96)
|
||||||
|
print(" STRATO TREND MULTI-ASSET (52 alt Hyperliquid, TSMOM CANONICAL long-flat vol-target)")
|
||||||
|
print("=" * 96)
|
||||||
|
book, M = alt_trend_returns()
|
||||||
|
bd = to_daily(book)
|
||||||
|
print(f" universo {M.shape[1]} alt, book [{bd.index[0].date()} -> {bd.index[-1].date()}]\n")
|
||||||
|
ev(bd, "TREND-52alt standalone")
|
||||||
|
|
||||||
|
tp = tp01_sleeve().daily(); xs = xsec_sleeve().daily()
|
||||||
|
def corr(a, b):
|
||||||
|
J = pd.concat({"a": a, "b": b}, axis=1, join="inner").dropna()
|
||||||
|
return float(J["a"].corr(J["b"])) if len(J) > 5 else float("nan")
|
||||||
|
print(f"\n correlazioni: TREND-52 vs TP01 {corr(bd, tp):+.2f} | vs XS01 {corr(bd, xs):+.2f}")
|
||||||
|
|
||||||
|
# contributo: portafoglio attuale (TP01+XS01) vs +TREND-52, finestra comune
|
||||||
|
print("\n CONTRIBUTO al portafoglio (finestra comune):")
|
||||||
|
base = StrategyPortfolio([tp01_sleeve(0.70), xsec_sleeve(0.30)]).backtest()
|
||||||
|
J = pd.concat({"tp": tp, "xs": xs, "tr": bd}, axis=1, join="inner").dropna()
|
||||||
|
print(f" [comune {J.index[0].date()} -> {J.index[-1].date()}]")
|
||||||
|
# baseline sulla finestra comune (TP01 0.7 + XS 0.3 rinormalizzato)
|
||||||
|
base_c = 0.7 * J["tp"] + 0.3 * J["xs"]
|
||||||
|
bf, bh = metrics(base_c), metrics(base_c[base_c.index >= HOLDOUT])
|
||||||
|
print(f" TP01 70 + XS 30 (attuale) FULL Sh {bf['sharpe']:.2f} DD {bf['maxdd']*100:.0f}% | HOLD Sh {bh['sharpe']:.2f}")
|
||||||
|
for wtr in (0.2, 0.3):
|
||||||
|
wt, wx = 0.7 * (1 - wtr), 0.3 * (1 - wtr)
|
||||||
|
comb = wt * J["tp"] + wx * J["xs"] + wtr * J["tr"]
|
||||||
|
cf, ch = metrics(comb), metrics(comb[comb.index >= HOLDOUT])
|
||||||
|
print(f" +TREND-52 w{wtr:.0%} FULL Sh {cf['sharpe']:.2f} ({cf['sharpe']-bf['sharpe']:+.2f}) DD {cf['maxdd']*100:.0f}% | HOLD Sh {ch['sharpe']:.2f} ({ch['sharpe']-bh['sharpe']:+.2f})")
|
||||||
|
|
||||||
|
print("\n -> aggiungere se: scorrelato a TP01/XS01 e migliora FULL E HOLD. Se molto correlato a")
|
||||||
|
print(" TP01 (entrambi trend) e contributo marginale, e' ridondante -> non si aggiunge.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user