research(equities): EQ-GTAA01 trend multi-asset + COMBO cross-mercato (diversifica il crypto)
(1) GTAA: trend difensivo long-flat su SPY/QQQ/IWM/TLT/GLD/HYG (EW sugli asset disponibili). GTAA lf vt12%: Sharpe 0.64 (OOS 0.89), maxDD 15% (8% sui 6-asset 2016+), corr SPY 0.64. Migliore sleeve equity: rischio-aggiustato > mono-SPY, DD bassissimo, diversificatore migliore. Difensiva (CAGR basso). Bear DD: GFC 14% vs 55%, COVID 10% vs 34%. (2) COMBO cross-mercato: crypto (TP01+XS01+VRP01) x equity (GTAA vt12), finestra 2019-2026. corr crypto<->equity = +0.17 (bassissima). blend 50/50 Sharpe 1.81 > crypto solo 1.60 > equity 1.12; maxDD dimezzato 14%->7%. DIVERSIFICA: primo miglioramento STRUTTURALE del rischio-aggiustato complessivo della ricerca post-reset (diversificazione vera, non alpha). CAVEAT: finestra crypto corta/favorevole (Sharpe assoluti ottimistici), cross-venue Deribit+IB, XS01/VRP01 STAT-MODE -> il combo deployable reale e' ~TP01+GTAA. Non risolve EUR50/g (capitale). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
# 2026-06-22 — EQ-GTAA01 (trend multi-asset) + COMBO cross-mercato equity×crypto
|
||||
|
||||
## (1) EQ-GTAA01 — trend difensivo multi-asset (GTAA)
|
||||
|
||||
EQ-TREND01 (trend su SPY) taglia il DD. Diversificare le SORGENTI di trend (azioni US/tech/small +
|
||||
bond + oro + high-yield) migliora il rischio-aggiustato. `eq_gtaa_trend.py`: ogni asset gestito col
|
||||
proprio trend long-flat (TSMOM multi-orizzonte), equal-weight tra gli asset disponibili (outer-join,
|
||||
cash dove off/assente). Universo SPY/QQQ/IWM/TLT/GLD/HYG. Causale, netto fee, OOS 2015+.
|
||||
|
||||
| strategia | CAGR | Sharpe (pre15/OOS) | maxDD | corr SPY |
|
||||
|---|---|---|---|---|
|
||||
| SPY buy&hold | 9.7% | 0.58 (0.45/0.82) | 55% | — |
|
||||
| EW statico (no trend) | 9.4% | 0.59 | 62% | 0.89 |
|
||||
| SPY-trend mono | 5.5% | 0.56 (/0.78) | 30% | 0.72 |
|
||||
| **GTAA lf vt12%** | 3.8% | **0.64** (0.53/**0.89**) | **15%** | **0.64** |
|
||||
| GTAA vt12 (6-asset, 2016+) | 5.6% | **1.08** | **8%** | 0.60 |
|
||||
|
||||
DD nei bear (GTAA vs SPY): dot-com 32%/49% · GFC **14%/55%** · COVID **10%/34%** · 2022 11%/24%.
|
||||
Marginale vs SPY: corr 0.64; 50/50 uplift +0.041 FULL / **+0.086 OOS** (meglio del mono-SPY). Plateau
|
||||
stabile (Sh 0.55-0.61, DD 25-35%). **Migliore sleeve equity**: Sharpe più alto, maxDD bassissimo
|
||||
(8-15%), corr SPY più bassa (0.64) = diversificatore migliore. Tradeoff: CAGR molto più basso
|
||||
(fortemente difensiva). Caveat: la finestra 6-asset (Sh 1.08) è tutta OOS ma un solo regime (toro).
|
||||
|
||||
## (2) COMBO cross-mercato — equity-trend × crypto
|
||||
|
||||
La via che alza il Sharpe COMPLESSIVO senza nuovo alpha: combinare due book scorrelati.
|
||||
`eq_crypto_combo.py`: crypto = portafoglio attivo TP01+XS01+VRP01 (`StrategyPortfolio.combined_daily`,
|
||||
rinormalizzato); equity = GTAA lf vt12%. Crypto compoundato sul calendario giorni-di-borsa (cattura
|
||||
i weekend). Finestra comune = era crypto (2019-03 .. 2026-06, 1827 giorni di borsa).
|
||||
|
||||
| | Sharpe | CAGR | volAnn | maxDD |
|
||||
|---|---|---|---|---|
|
||||
| crypto TP01+XS01+VRP01 | 1.60 | 18.7% | 11.1% | 14% |
|
||||
| equity GTAA vt12 | 1.12 | 6.0% | 5.3% | 8% |
|
||||
| **blend 50/50** | **1.81** | 12.4% | 6.6% | **7%** |
|
||||
| risk-parity (32c/68e) | 1.78 | 10.1% | 5.5% | 8% |
|
||||
|
||||
**Correlazione crypto↔equity = +0.167** (bassissima). Il blend 50/50 fa **Sharpe 1.81 > di ciascuno**
|
||||
(crypto 1.60, equity 1.12), **maxDD dimezzato 14%→7%**. VERDETTO: DIVERSIFICA (blend > miglior solo
|
||||
di +0.21 Sharpe). È il guadagno STRUTTURALE: due fonti di rischio scorrelate alzano il Sharpe
|
||||
complessivo senza cercare un nuovo edge.
|
||||
|
||||
### Caveat onesti
|
||||
- **Finestra crypto corta (~7y) e favorevole**: il crypto Sharpe 1.60 e' alto (regime toro + XS01/VRP01
|
||||
STAT-MODE a storia corta). Gli SHARPE ASSOLUTI sono ottimistici. Ma il PUNTO della diversificazione
|
||||
(corr 0.17, blend > solo, DD dimezzato) è robusto al livello assoluto.
|
||||
- **Cross-venue**: crypto su Deribit, equity su IB → due conti, due percorsi d'esecuzione. A $0.5-2k
|
||||
totali, ogni sleeve è minuscola. La parte equity (GTAA) e la TP01 sono entrambe eseguibili a basso
|
||||
capitale; XS01/VRP01 restano STAT-MODE (il blend "reale" deployable è ~TP01 + GTAA).
|
||||
|
||||
## Lettura strategica
|
||||
Il fronte equity da' due cose: (a) una sleeve difensiva robusta (GTAA, maxDD ~10%), (b) — piu'
|
||||
importante — un DIVERSIFICATORE quasi-scorrelato al crypto che alza il Sharpe del portafoglio
|
||||
complessivo da ~1.6 a ~1.8. Non risolve €50/g (resta capitale), ma e' il primo miglioramento
|
||||
STRUTTURALE del rischio-aggiustato complessivo trovato in tutta la ricerca post-reset, ed e' del tipo
|
||||
giusto (diversificazione vera, non alpha fittizio). Prossimo: validare il combo deployable TP01+GTAA
|
||||
(solo le due gambe eseguibili), e valutare l'operativita' cross-venue.
|
||||
@@ -0,0 +1,84 @@
|
||||
"""CROSS-MARKET COMBO — la sleeve equity-trend DIVERSIFICA il portafoglio crypto?
|
||||
|
||||
(2) dopo EQ-GTAA01. La via che alza il Sharpe COMPLESSIVO senza cercare nuovo alpha: combinare due
|
||||
book scorrelati su mercati diversi. crypto = portafoglio attivo TP01+XS01+VRP01 (src.portfolio);
|
||||
equity = GTAA lf vt12% (la migliore sleeve equity, corr SPY 0.64, maxDD ~15%). Se la correlazione
|
||||
crypto<->equity e' bassa, il blend ha Sharpe > di ciascuno.
|
||||
|
||||
ALLINEAMENTO ONESTO: crypto e' calendario-giornaliero (7gg), equity giorni di borsa. Compoundo i
|
||||
rendimenti crypto sul grid dei giorni di borsa (cattura i weekend) prima di combinare. Finestra =
|
||||
era crypto (TP01 dal 2019). Esecuzione split Deribit+IB (lo noto: e' un portafoglio cross-venue).
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import numpy as np, pandas as pd
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT)); sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
||||
from eq_sector_momentum import _sh, _cagr, _dd
|
||||
from eq_gtaa_trend import gtaa
|
||||
from eq_spy_trend import tsmom_exposure, backtest, _series
|
||||
from src.portfolio.sleeves import active_sleeves
|
||||
from src.portfolio.portfolio import StrategyPortfolio
|
||||
|
||||
ANN = np.sqrt(252.0)
|
||||
|
||||
|
||||
def _ann_vol(r):
|
||||
return float(np.std(np.asarray(r, float)) * ANN)
|
||||
|
||||
|
||||
def compound_to_grid(daily: pd.Series, grid: pd.DatetimeIndex) -> pd.Series:
|
||||
"""Compounda una serie di rendimenti (calendario) sul grid dato (giorni di borsa): per ogni data
|
||||
del grid, somma-composta i rendimenti dal punto precedente."""
|
||||
cum = (1.0 + daily).cumprod()
|
||||
cum = cum.reindex(cum.index.union(grid)).ffill().reindex(grid)
|
||||
return (cum / cum.shift(1) - 1.0).dropna()
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 96)
|
||||
print(" CROSS-MARKET COMBO — equity-trend (GTAA) x crypto (TP01+XS01+VRP01)")
|
||||
print("=" * 96)
|
||||
# crypto blend (rinormalizzato, date diverse)
|
||||
crypto = StrategyPortfolio(active_sleeves()).combined_daily()
|
||||
if crypto.index.tz is None:
|
||||
crypto.index = crypto.index.tz_localize("UTC")
|
||||
# equity sleeve (giorni di borsa)
|
||||
eq = gtaa(target_vol=0.12).dropna()
|
||||
# allinea: compounda crypto sui giorni di borsa dell'equity
|
||||
grid = eq.index[eq.index >= crypto.index[0]]
|
||||
cr = compound_to_grid(crypto, grid)
|
||||
J = pd.concat({"crypto": cr, "equity": eq.reindex(cr.index)}, axis=1).dropna()
|
||||
print(f" finestra comune {J.index[0].date()}..{J.index[-1].date()} ({len(J)} giorni di borsa)\n")
|
||||
|
||||
c, e = J["crypto"], J["equity"]
|
||||
print(" --- STANDALONE (sulla finestra comune) ---")
|
||||
for nm, r in (("crypto TP01+XS01+VRP01", c), ("equity GTAA vt12", e)):
|
||||
print(f" {nm:24} Sh {_sh(r):>5.2f} CAGR {_cagr(r.values,r.index)*100:>5.1f}% volAnn {_ann_vol(r)*100:>4.1f}% maxDD {_dd(r.values)*100:>4.0f}%")
|
||||
print(f"\n --- CORRELAZIONE crypto <-> equity = {c.corr(e):+.3f} (bassa = diversifica) ---")
|
||||
|
||||
print("\n --- BLEND (capitale) ---")
|
||||
print(f" {'mix':18} {'Sharpe':>7} {'CAGR%':>6} {'volAnn%':>7} {'maxDD%':>6}")
|
||||
for wc in (1.0, 0.75, 0.5, 0.25, 0.0):
|
||||
b = wc * c + (1 - wc) * e
|
||||
print(f" crypto {int(wc*100):>3}/{int((1-wc)*100):<3} eq {_sh(b):>7.2f} {_cagr(b.values,b.index)*100:>6.1f} {_ann_vol(b)*100:>7.1f} {_dd(b.values)*100:>6.0f}")
|
||||
|
||||
# risk-parity (peso inverso alla vol) — il blend "giusto" quando le vol differiscono
|
||||
vc, ve = _ann_vol(c), _ann_vol(e)
|
||||
wc_rp = (1/vc) / (1/vc + 1/ve)
|
||||
b_rp = wc_rp * c + (1 - wc_rp) * e
|
||||
print(f"\n --- RISK-PARITY (inv-vol: crypto {wc_rp*100:.0f}% / eq {(1-wc_rp)*100:.0f}%) ---")
|
||||
print(f" Sharpe {_sh(b_rp):.2f} CAGR {_cagr(b_rp.values,b_rp.index)*100:.1f}% volAnn {_ann_vol(b_rp)*100:.1f}% maxDD {_dd(b_rp.values)*100:.0f}%")
|
||||
|
||||
# verdetto: il blender batte il migliore dei due?
|
||||
best_solo = max(_sh(c), _sh(e))
|
||||
best_blend = max(_sh(0.5*c+0.5*e), _sh(b_rp))
|
||||
print(f"\n --- VERDETTO ---")
|
||||
print(f" miglior standalone Sharpe = {best_solo:.2f} | miglior blend Sharpe = {best_blend:.2f} "
|
||||
f"-> {'DIVERSIFICA (blend > solo)' if best_blend > best_solo + 0.03 else 'nessun guadagno netto'}")
|
||||
print(f" (nota: portafoglio cross-venue Deribit+IB; finestra crypto corta ~{len(J)//252}y)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,109 @@
|
||||
"""EQ-GTAA01 — Trend difensivo MULTI-ASSET (GTAA) sull'universo ETF.
|
||||
|
||||
EQ-TREND01 ha mostrato che il trend long-flat su SPY taglia il DD (analogo TP01). La diversificazione
|
||||
delle SORGENTI di trend (azioni US/tech/small + bond + oro + high-yield) di solito migliora il
|
||||
rischio-aggiustato del trend mono-asset. Qui: ogni asset gestito col proprio trend long-flat
|
||||
(TSMOM multi-orizzonte), equal-weight tra gli asset DISPONIBILI (la quota "off" o assente -> cash).
|
||||
|
||||
DATI: cache eqlib (ADJUSTED, nessun IB). Start diversi -> outer-join con peso rinormalizzato sugli
|
||||
asset esistenti (come gli sleeve crypto). Finestra lunga: SPY/QQQ/IWM da ~2000; TLT(2016)/GLD(2004)/
|
||||
HYG(2007) entrano dopo. Riporto anche la finestra 6-asset comune (2016+).
|
||||
|
||||
GIUDIZIO: vs SPY buy&hold, vs EW statico (isola il valore del TIMING di trend), vs SPY-trend mono;
|
||||
Sharpe full/pre15/OOS + maxDD + plateau. Causale, netto fee.
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import numpy as np, pandas as pd
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
||||
import eqlib
|
||||
from eqlib import load_eq
|
||||
from eq_sector_momentum import _sh, _cagr, _dd, EQ_HOLDOUT, spy_bh
|
||||
from eq_spy_trend import tsmom_exposure, backtest, _series
|
||||
|
||||
ASSETS = ["SPY", "QQQ", "IWM", "TLT", "GLD", "HYG"]
|
||||
|
||||
|
||||
def gated_returns(sym, horizons=(21, 63, 126, 252), fee_side=0.0002, target_vol=None, lev_cap=1.0):
|
||||
"""Rendimenti netti daily di UN asset gestito col proprio trend long-flat (cash quando off)."""
|
||||
px = _series(sym)
|
||||
ex = tsmom_exposure(px, horizons=horizons, target_vol=target_vol, lev_cap=lev_cap)
|
||||
return backtest(px, ex, fee_side=fee_side)
|
||||
|
||||
|
||||
def gtaa(assets=ASSETS, horizons=(21, 63, 126, 252), fee_side=0.0002, target_vol=None):
|
||||
"""Portafoglio GTAA: media (equal-weight) dei rendimenti trend-gated sugli asset disponibili
|
||||
ogni giorno (outer-join). La quota di asset assenti/in-cash resta in cash."""
|
||||
cols = {a: gated_returns(a, horizons, fee_side, target_vol) for a in assets}
|
||||
R = pd.concat(cols, axis=1).sort_index()
|
||||
return R.mean(axis=1, skipna=True) # EW sugli asset esistenti quel giorno
|
||||
|
||||
|
||||
def ew_buyhold(assets=ASSETS):
|
||||
cols = {a: _series(a).pct_change() for a in assets}
|
||||
return pd.concat(cols, axis=1).sort_index().mean(axis=1, skipna=True)
|
||||
|
||||
|
||||
def _row(name, r, common=None, bench=None):
|
||||
r = r.dropna() if common is None else r.reindex(common).fillna(0.0)
|
||||
h = r[r.index >= EQ_HOLDOUT]; ii = r[r.index < EQ_HOLDOUT]
|
||||
tim = float((r != 0).mean()) * 100
|
||||
extra = ""
|
||||
if bench is not None:
|
||||
J = pd.concat({"r": r, "b": bench}, axis=1, join="inner").dropna()
|
||||
extra = f" corrSPY {J['r'].corr(J['b']):+.2f}"
|
||||
print(f" {name:26} CAGR {_cagr(r.values, r.index)*100:>5.1f}% Sh {_sh(r):>5.2f} "
|
||||
f"(pre15 {_sh(ii):>5.2f}|OOS {_sh(h):>5.2f}) maxDD {_dd(r.values)*100:>4.0f}% inMkt {tim:>3.0f}%{extra}")
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 100)
|
||||
print(" EQ-GTAA01 — Trend difensivo MULTI-ASSET (GTAA)")
|
||||
print("=" * 100)
|
||||
spy = spy_bh()
|
||||
g = gtaa() # outer-join, finestra lunga
|
||||
gl = g.dropna()
|
||||
print(f" finestra lunga (outer-join) {gl.index[0].date()}..{gl.index[-1].date()} ({len(gl)}g) OOS {EQ_HOLDOUT.date()}+\n")
|
||||
|
||||
print(" --- BASELINE & confronti (finestra lunga) ---")
|
||||
cl = gl.index
|
||||
_row("SPY buy&hold", spy.reindex(cl).fillna(0))
|
||||
_row("EW statico (no trend)", ew_buyhold().reindex(cl).fillna(0), bench=spy.reindex(cl).fillna(0))
|
||||
_row("SPY-trend mono (TREND01)", backtest(_series("SPY"), tsmom_exposure(_series("SPY"))).reindex(cl).fillna(0), bench=spy.reindex(cl).fillna(0))
|
||||
print("\n --- GTAA (multi-asset trend) ---")
|
||||
_row("GTAA lf", gl, bench=spy.reindex(cl).fillna(0))
|
||||
_row("GTAA lf vt12%", gtaa(target_vol=0.12).reindex(cl).fillna(0), bench=spy.reindex(cl).fillna(0))
|
||||
|
||||
# finestra 6-asset comune (tutti gli ETF esistono): 2016+
|
||||
tlt0 = _series("TLT").index[0]
|
||||
c6 = gl.index[gl.index >= tlt0]
|
||||
print(f"\n --- finestra 6-asset comune ({c6[0].date()}+) ---")
|
||||
_row("SPY buy&hold (6a win)", spy.reindex(c6).fillna(0))
|
||||
_row("GTAA lf (6a win)", g.reindex(c6).fillna(0), bench=spy.reindex(c6).fillna(0))
|
||||
_row("GTAA lf vt12 (6a win)", gtaa(target_vol=0.12).reindex(c6).fillna(0), bench=spy.reindex(c6).fillna(0))
|
||||
|
||||
# MARGINALE vs SPY
|
||||
print("\n --- MARGINALE vs SPY (GTAA lf, finestra lunga) ---")
|
||||
J = pd.concat({"spy": spy, "c": gl}, axis=1, join="inner").dropna(); JH = J[J.index >= EQ_HOLDOUT]
|
||||
print(f" corr full {J['spy'].corr(J['c']):+.3f} | OOS {JH['spy'].corr(JH['c']):+.3f}")
|
||||
for wt in (0.5, 1.0):
|
||||
bf = _sh((1-wt)*J['spy']+wt*J['c'])-_sh(J['spy']); bh = _sh((1-wt)*JH['spy']+wt*JH['c'])-_sh(JH['spy'])
|
||||
lbl = "100% GTAA" if wt == 1.0 else "50/50 SPY/GTAA"
|
||||
print(f" {lbl:16}: uplift Sharpe FULL {bf:+.3f} OOS {bh:+.3f}")
|
||||
print(" DD nei bear (GTAA vs SPY):")
|
||||
for lo, hi, lbl in [("2000-03-01","2002-12-31","dot-com"),("2007-10-01","2009-06-30","GFC"),
|
||||
("2020-02-01","2020-04-30","COVID"),("2022-01-01","2022-12-31","2022")]:
|
||||
seg=lambda s: _dd(s.reindex(cl).fillna(0)[(cl>=pd.Timestamp(lo,tz='UTC'))&(cl<=pd.Timestamp(hi,tz='UTC'))].values)*100
|
||||
print(f" {lbl:8} GTAA {seg(gl):.0f}% | SPY {seg(spy):.0f}%")
|
||||
|
||||
print("\n --- PLATEAU (Sharpe FULL/pre15/OOS, DD, CAGR) GTAA lf, finestra lunga ---")
|
||||
print(f" {'horizons':22} {'FULL':>6} {'pre15':>6} {'OOS':>6} {'DD%':>5} {'CAGR%':>6}")
|
||||
for hz in [(63,126,252),(21,63,126,252),(126,252),(252,)]:
|
||||
r = gtaa(horizons=hz).reindex(cl).fillna(0); h=r[r.index>=EQ_HOLDOUT]; ii=r[r.index<EQ_HOLDOUT]
|
||||
print(f" {'x'.join(map(str,hz)):22} {_sh(r):>6.2f} {_sh(ii):>6.2f} {_sh(h):>6.2f} {_dd(r.values)*100:>5.0f} {_cagr(r.values,r.index)*100:>6.1f}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user