diff --git a/CLAUDE.md b/CLAUDE.md index 3ccf3e5..da019bd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -51,14 +51,22 @@ Prima ondata di ricerca onesta su BTC/ETH certificati (5 track, harness condivis monitor forward. NB il gate concentra XS nei regimi dispersi (2025-26 = hold-out alta-dispersione). Ricerca `scripts/portfolio/{xsec_research,xsec_blend,xsec_dispgate}.py`. Diari `2026-06-19-hyperliquid-xsec` / `-xsec-blend` / `-xsec-dispgate` / `-xsec-universe-expansion` / `-trend-multiasset`. -- **PORTAFOGLIO ATTIVO = TP01 (41.25%) + XS01 (18.75%) + VRP01 (15%) + SKH01 (25%)** (`src/portfolio/sleeves.active_sleeves`): - TP01+XS01 combinato **FULL Sharpe 1.55, HOLD-OUT 1.55, DD 4.4%**. Aggiunto **VRP01** (options - short-vol, sotto): TP01+VRP01 da solo fa FULL Sh 1.30→1.44 / HOLD 0.31→0.40 a peso 20% (3-way da - validare locale con dati HL). **Aggiunto SKH01-V2-DD @25% effettivo (2026-06-23, sotto):** i tre - preesistenti scalati nel restante 0.75 (rapporto 55:25:20). Il portafoglio a **4 sleeve** fa - **FULL Sharpe 1.68→2.13, HOLD-OUT 1.63→2.30, DD full 14.3%→7.8%** (Skyhook è quasi-ortogonale, - corr ~0.09). Report `scripts/portfolio/run_portfolio.py`. Sleeve a date d'inizio - diverse → outer-join con pesi rinormalizzati (TP01/SKH01 dal 2019, VRP dal 2021, XS dal 2024). +- **PORTAFOGLIO ATTIVO = TP01 (33%) + XS01 (15%) + VRP01 (12%) + SKH01 (20%) + GTAA01 (20%)** + (`src/portfolio/sleeves.active_sleeves`): TP01+XS01 combinato **FULL Sharpe 1.55, HOLD-OUT 1.55, + DD 4.4%**. Aggiunto **VRP01** (options short-vol, sotto): TP01+VRP01 da solo fa FULL Sh 1.30→1.44 / + HOLD 0.31→0.40 a peso 20%. **Aggiunto SKH01-V2-DD @25% effettivo (2026-06-23, sotto)**: 4-sleeve + **FULL Sharpe 1.68→2.13, HOLD-OUT 1.63→2.30, DD full 14.3%→7.8%** (Skyhook quasi-ortogonale, + corr ~0.09). **Aggiunto GTAA01 @20% effettivo (2026-07-01, i 4 preesistenti scalati ×0.80):** + trend difensivo equity 6-ETF su IB (`src/portfolio/gtaa.py`, ~30 anni storia, validato 2026-06-22 + su OOS equity 2015+ INDIPENDENTE dall'hold-out crypto, corr al book ~+0.10) → 5-sleeve + **FULL Sharpe 2.12→2.24, HOLD-OUT 2.21→2.46, DD full 7.8%→6.2%** (costo dichiarato: CAGR full + 23.3→18.8%; 2022 unico anno con dSh−). Uplift positivo in-sample E su tutte le finestre disgiunte + (vs EW-STR refutato lo stesso giorno). Convenzioni: weekend/festivi equity = 0.0 (capitale IB + fermo, non riciclato); attivazione nel book all'era crypto 2019-03; **il book live Deribit + (`deribit_book_sleeves` TP01+SKH01 75/25) NON lo include** (GTAA in paper_combo dal 2026-06-23). + Test `tests/test_gtaa_sleeve.py`; diario `2026-07-01-strategy-wave-6threads.md` (addendum GTAA). + Report `scripts/portfolio/run_portfolio.py`. Sleeve a date d'inizio diverse → outer-join con pesi + rinormalizzati (TP01/SKH01/GTAA dal 2019*, VRP dal 2021, XS dal 2024; *GTAA troncato all'era book). - **SKH01-V2-DD "Skyhook" — DIVERSIFICATORE quasi-ortogonale (research)** — `src/strategies/skyhook.SKH01_V2_DD`, sleeve `src/portfolio/sleeves._skyhook_returns`. Sistema dual-TF (segnale 690m / exec 230m) regime (BuzVola/BuzVolume tipo-Chande) AND pattern (Donchian breakout), NON trend-follower, L/S. Vincitrice @@ -159,8 +167,8 @@ Prima ondata di ricerca onesta su BTC/ETH certificati (5 track, harness condivis l'edge è *deployabile* (2 gambe BTC/ETH perp). Altri LEAD: IVOL (idio-vol XS, STAT-MODE), DVOLSPREAD (storia DVOL corta). Diario `2026-06-29-strategy-search-5threads.md`. Script `scripts/research/{xsec_v3_lowrisk,xsec_v3_momstruct,meta_allocation,orthogonal_signals,signal_inout_1leg}.py`. -- **Ondata 2026-07-01 (6 filoni multi-agente + scettico) — 0 nuovi sleeve, pesi CURRENT confermati, - 1 gate nuovo.** Filoni su angoli non coperti dalle ondate precedenti: (1) **funding time-series** +- **Ondata 2026-07-01 (6 filoni multi-agente + scettico) — 0 edge nuovi dai filoni, 1 gate nuovo, + e 1 sleeve promosso DALL'ARCHIVIO (GTAA01, sotto nel bullet portafoglio).** Filoni su angoli non coperti dalle ondate precedenti: (1) **funding time-series** BTC/ETH (posizionamento) = SCARTATO — FOLLOW è trend-beta ritardato, FADE shorta il toro, il gate è TP01 travestito (DSR 0.215); **il filone funding è chiuso su 3 lati** (FC01 carry, price-clock, TS). (2) **breadth/internals alt** (51 HL) = SCARTATO ma unico NON-ridondante col trend (corr→TP01 0.40); @@ -267,7 +275,7 @@ src/strategies/indicators.py → indicatori condivisi (ema, atr, keltner, ...) src/strategies/trend_portfolio.py → TP01: strategia DIFENSIVA robusta (PORT LF1d, >=12h), causale src/portfolio/ → PORTAFOGLIO DI STRATEGIE estensibile (Sleeve + StrategyPortfolio) portfolio.py → combina N sleeve per peso su griglia giornaliera; metriche FULL/hold-out/anno - sleeves.py → REGISTRY sleeve attivi: TP01 (55%) + XS01 (25%) + VRP01 (20%). Aggiungere = una riga + sleeves.py → REGISTRY sleeve attivi: TP01 33 / XS01 15 / VRP01 12 / SKH01 20 / GTAA01 20. Aggiungere = una riga src/fractal/ → indicatori frattali (patterns.py, indicators.py, similarity.py) src/backtest/engine.py → engine di backtesting riusabile src/backtest/harness.py → harness ONESTO (load BTC/ETH, backtest_signals no-leakage, OOS) diff --git a/docs/diary/2026-07-01-strategy-wave-6threads.md b/docs/diary/2026-07-01-strategy-wave-6threads.md index 82de00e..47bdbbd 100644 --- a/docs/diary/2026-07-01-strategy-wave-6threads.md +++ b/docs/diary/2026-07-01-strategy-wave-6threads.md @@ -72,3 +72,61 @@ reali di crescita restano (a) storia forward che promuova i lead (STATARB-RESID, anni), (b) capitale, (c) nuove fonti di dato (f di stress reale per VRP). **File dell'ondata:** `scripts/research/r0701_{funding_ts,breadth_internals,xs_residmom,portfolio_opt,portfolio_skeptic,vrp_refine,xs_seasonal}.py`; diari `2026-07-01-{funding-ts-signal,alt-breadth-internals,xs-residual-momentum,portfolio-weights-ddguard,vrp-refinement,xs-seasonality}.md`; gate in `src/portfolio/portfolio.py` + `tests/test_weights_tilt_null.py`. + +## ADDENDUM (stessa sera) — GTAA01 promosso a 5° SLEEVE: il miglioramento c'era, ma era in casa + +Il secondo passaggio sull'archivio (non sui dati!) ha trovato il candidato che l'ondata di *ricerca* +non poteva trovare: **EQ-GTAA01** (`src/portfolio/gtaa.py` — trend difensivo equity 6-ETF, vt12%, +~30 anni di storia, netto fee IB) era stato validato il 2026-06-22 come *"primo miglioramento +STRUTTURALE del rischio-aggiustato trovato in tutta la ricerca post-reset"*, messo in paper +cross-venue (`paper_combo`, nel cron dal 2026-06-23) e **mai valutato come 5° sleeve del +portafoglio attivo** (il giorno dopo arrivò SKH01 e il filone si fermò). + +### Valutazione onesta (`r0701_gtaa_5th_sleeve.py`) — perché GTAA passa dove EW-STR è morto + +| gate | EW-STR (refutato) | +GTAA01 (passa) | +|---|---|---| +| uplift IN-SAMPLE (pre-2025) | **−0.05** | **+0.09..+0.13** (2.134→2.23-2.26) | +| finestre OOS disgiunte | −0.12 / +0.06 / +0.14 | **+0.05 / +0.19 / +0.25** (tutte >0) | +| multi-cut (4 tagli) | solo annidate | **+0.21 / +0.21 / +0.22 / +0.25** | +| provenienza dell'edge | sleeve selezionati sull'hold-out crypto | validato su OOS equity 2015+, **indipendente** dall'hold-out crypto | +| corr al book | — | **+0.10** full, ~0.00 hold-out; is_hedge +0.30 (non-hedge) | +| plateau | gradiente generico anti-TP01 | **monotono su w 10-30%, ogni cella migliora IS/FULL/HOLD/DD** | + +Convenzione di realismo cross-venue (nuova, cablata nello sleeve): nei giorni senza barra equity +(weekend/festivi US) il rendimento è **0.0** — capitale IB fermo, NON rinormalizzato sul crypto +(due conti reali non si travasano nel weekend). Numeri quasi identici alla versione outer-join +(differenze al 3° decimale) ma il modello è quello giusto. Attivazione nel book all'era crypto +(2019-03-14): senza troncatura l'outer-join farebbe partire il book nel 1996 con 23 anni di +solo-GTAA rinormalizzato al 100%. + +### Decisione pesi +Cella IS-best = w=30% (bordo griglia), **ingresso @20%** per scelta strutturale dichiarata: +(a) cross-venue IB con paper operativo di soli 8 giorni — non diventa lo sleeve più grande del +book al day-one; (b) precedente: ogni sleeve nuovo è entrato ≤25% (SKH01 25%, VRP01 15%); +(c) costo CAGR (a 30%: 16.8% vs 18.8% a 20%) — e l'obiettivo €50/g vive di CAGR, non solo di +Sharpe. Tutte le celle 10-30% passano comunque ogni gate. Pesi nuovi: **TP01 33 / XS01 15 / +VRP01 12 / SKH01 20 / GTAA01 20** (i 4 preesistenti scalati ×0.80, rapporti conservati). + +### Portafoglio 5-sleeve (run_portfolio, capitale 2k) +| | FULL Sh | FULL DD | FULL CAGR | HOLD Sh | HOLD DD | HOLD CAGR | +|---|---|---|---|---|---|---| +| 4-sleeve (baseline) | 2.12 | 7.8% | +23.3% | 2.21 | 3.5% | +16.0% | +| **5-sleeve (+GTAA01 20%)** | **2.24** | **6.2%** | +18.8% | **2.46** | **2.2%** | +14.6% | + +Per-anno: tutti gli 8 anni positivi, 2022 +7.0% (era +11.6%: GTAA soffre il whipsaw 2022, unico +anno con dSh negativo −0.49 — dichiarato). Il trade-off è esplicito: −4.5pp di CAGR full per ++0.12/+0.25 di Sharpe e DD 7.8→6.2 / hold-out 3.5→2.2. A pesi research (XS/VRP/SKH modellati o +STAT-MODE) il CAGR era comunque di carta; lo Sharpe/DD del book è la metrica di verità. + +### Cosa NON cambia +Il **book live Deribit** (`deribit_book_sleeves` TP01+SKH01 75/25, `book_execute`) è INTATTO — +GTAA è su IB, già tracciato forward da `paper_combo`. `paper_portfolio` da oggi traccia la +composizione a 5 (regime-break dichiarato qui). Test: `tests/test_gtaa_sleeve.py` (registry, +weekend-zero, attivazione, book live invariato) — suite 168/168. + +### Lezione dell'addendum +Prima di una nuova ondata di *ricerca*, ri-leggere l'archivio dei **validati-ma-non-integrati**: +l'unico upgrade onesto del book in 3 ondate (104+5+6 filoni) era un edge già validato che +aspettava solo la valutazione da sleeve. La diversificazione strutturale (secondo mercato, +corr ~0.1) resta l'unica cosa che alza lo Sharpe del book senza nuovo alpha. diff --git a/scripts/research/r0701_gtaa_5th_sleeve.py b/scripts/research/r0701_gtaa_5th_sleeve.py new file mode 100644 index 0000000..02a551c --- /dev/null +++ b/scripts/research/r0701_gtaa_5th_sleeve.py @@ -0,0 +1,103 @@ +"""r0701_gtaa_5th_sleeve — EQ-GTAA01 come 5° sleeve del portafoglio attivo? Valutazione ONESTA. + +Contesto (diari 2026-06-22/23): GTAA (trend difensivo equity, 6 ETF, vt12%, ~30y di storia, netto +fee IB 2bps/lato) fu validato come diversificatore strutturale del book crypto (corr ~0.17-0.21, +combo Sharpe 1.60->1.81) ma lasciato in paper cross-venue (paper_combo) e MAI valutato come 5° +sleeve del portafoglio attivo (il giorno dopo arrivo' SKH01). Qui: gate onesti per lo slot. + +Metodo: + * pannello = 4 sleeve attivi + GTAA (to_daily), outer-join rinormalizzato (combine_outer); + * finestra di valutazione ancorata all'inizio del book crypto (TP01 start) — NO gonfiaggio + da 23 anni di solo-GTAA pre-2019; + * peso GTAA selezionato IN-SAMPLE (Sharpe pre-2025) su griglia {10..30%}, gli altri scalati + (1-w) — nessuna scelta sull'hold-out; + * verifica: hold-out 2025+, multi-cut (2023-01/2024-01/2024-07/2025-01), finestre DISGIUNTE, + per-anno, corr, is_hedge (corr tra Sharpe-anno del book e uplift-anno), plateau in w. +""" +from __future__ import annotations +import sys +from pathlib import Path + +import numpy as np +import pandas as pd + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT)) + +from src.portfolio.portfolio import HOLDOUT, combine_outer, metrics, to_daily +from src.portfolio.sleeves import active_sleeves +from src.portfolio.gtaa import gtaa_returns + +GRID_W = [0.10, 0.15, 0.20, 0.25, 0.30] +CUTS = [pd.Timestamp(c, tz="UTC") for c in ("2023-01-01", "2024-01-01", "2024-07-01", "2025-01-01")] +DISJOINT = [("2019-03-01", "2023-01-01"), ("2023-01-01", "2025-01-01"), ("2025-01-01", "2027-01-01")] + + +def sh(s: pd.Series, lo=None, hi=None) -> float: + v = s + if lo is not None: + v = v[v.index >= lo] + if hi is not None: + v = v[v.index < hi] + r = np.asarray(v.dropna().values, float) + return float(r.mean() / r.std() * np.sqrt(365.25)) if len(r) > 1 and r.std() > 0 else 0.0 + + +def main() -> None: + sl = active_sleeves() + cols = {s.name: s.daily() for s in sl} + w_cur = {s.name: s.weight for s in sl} + g = to_daily(gtaa_returns()) + lo_book = min(c.index.min() for c in cols.values()) # inizio era-crypto del book + cols5 = dict(cols, GTAA01=g) + + base = combine_outer(cols, w_cur, lo=lo_book) + print(f"book start {lo_book.date()} | GTAA storia {g.index.min().date()}->{g.index.max().date()}" + f" | GTAA standalone: FULL {sh(g):.2f}, pre-2025 {sh(g, hi=HOLDOUT):.2f}, " + f"era-crypto {sh(g[g.index >= lo_book]):.2f}, HOLD {sh(g, lo=HOLDOUT):.2f}") + print(f"corr(GTAA, book) era-crypto: {g.reindex(base.index).corr(base):+.3f} | " + f"hold-out: {g.reindex(base.index).loc[HOLDOUT:].corr(base.loc[HOLDOUT:]):+.3f}") + print(f"\nBASE 4-sleeve : FULL {sh(base):.3f} IS {sh(base, hi=HOLDOUT):.3f} " + f"HOLD {sh(base, lo=HOLDOUT):.3f} DD {metrics(base)['maxdd']:.1%} " + f"CAGR {metrics(base)['cagr']:+.1%}") + + # griglia peso GTAA — selezione IN-SAMPLE + rows = [] + for w in GRID_W: + w5 = {k: v * (1 - w) for k, v in w_cur.items()} | {"GTAA01": w} + c5 = combine_outer(cols5, w5, lo=lo_book) + rows.append(dict(w=w, IS=sh(c5, hi=HOLDOUT), FULL=sh(c5), HOLD=sh(c5, lo=HOLDOUT), + DD=metrics(c5)["maxdd"], CAGR=metrics(c5)["cagr"])) + print(f" +GTAA {w:.0%}: IS {rows[-1]['IS']:.3f} FULL {rows[-1]['FULL']:.3f} " + f"HOLD {rows[-1]['HOLD']:.3f} DD {rows[-1]['DD']:.1%} CAGR {rows[-1]['CAGR']:+.1%}") + best = max(rows, key=lambda r: r["IS"]) + w = best["w"] + print(f"\ncella IN-SAMPLE: w={w:.0%} (IS {best['IS']:.3f}); plateau IS: " + + ", ".join(f"{r['w']:.0%}:{r['IS']:.2f}" for r in rows)) + + w5 = {k: v * (1 - w) for k, v in w_cur.items()} | {"GTAA01": w} + c5 = combine_outer(cols5, w5, lo=lo_book) + + print("\nmulti-cut (Sharpe OOS dal taglio, book+GTAA vs book):") + for cut in CUTS: + print(f" {cut.date()}: {sh(c5, lo=cut):.3f} vs {sh(base, lo=cut):.3f} " + f"delta {sh(c5, lo=cut) - sh(base, lo=cut):+.3f}") + print("finestre DISGIUNTE:") + for a, b in DISJOINT: + la, lb = pd.Timestamp(a, tz="UTC"), pd.Timestamp(b, tz="UTC") + print(f" {a[:7]}..{b[:7]}: delta {sh(c5, lo=la, hi=lb) - sh(base, lo=la, hi=lb):+.3f}") + + print("per-anno (ret book -> ret book+GTAA):") + ups, shs = [], [] + for y in sorted(set(base.index.year)): + by, cy = base[base.index.year == y], c5[c5.index.year == y] + rb, rc = float((1 + by).prod() - 1), float((1 + cy).prod() - 1) + ups.append(sh(cy) - sh(by)); shs.append(sh(by)) + print(f" {y}: {rb:+7.1%} -> {rc:+7.1%} dSh {sh(cy) - sh(by):+.2f}") + ih = float(np.corrcoef(shs, ups)[0, 1]) if len(ups) > 2 else float("nan") + print(f"is_hedge check — corr(Sharpe-anno book, uplift-anno): {ih:+.2f} " + f"(molto negativa = hedge, non alpha)") + + +if __name__ == "__main__": + main() diff --git a/src/portfolio/sleeves.py b/src/portfolio/sleeves.py index bffa772..79d7b9a 100644 --- a/src/portfolio/sleeves.py +++ b/src/portfolio/sleeves.py @@ -277,6 +277,42 @@ def skyhook_sleeve(weight: float = 0.25) -> Sleeve: return Sleeve("SKH01_skyhook", weight, _skyhook_returns, pos_fn=_skyhook_positions) +# ----------------------------- GTAA01: trend difensivo equity multi-asset (IB) ----------------------------- +# EQ-GTAA01 (src/portfolio/gtaa.py): TSMOM long-flat multi-orizzonte [21/63/126/252g] per-ETF, +# vol-target 12%, EW su SPY/QQQ/IWM/TLT/GLD/HYG, netto fee IB 2bps/lato. ~30 anni di storia +# (1996+), validato su OOS 2015+ PRIMA e INDIPENDENTEMENTE dall'hold-out crypto (diari +# 2026-06-22-equities-gtaa-and-crypto-combo / -deployable-combo-tp01-gtaa). Diversificatore +# STRUTTURALE del book crypto: corr ~+0.10 (hold-out ~0.00), is_hedge no (+0.30). +# Promosso a 5o sleeve il 2026-07-01 (diario 2026-07-01-strategy-wave-6threads.md, addendum GTAA): +# uplift positivo IN-SAMPLE (+0.13) e su TUTTE le finestre disgiunte (2019-23 +0.05, 23-24 +0.19, +# 25+ +0.25), multi-cut +0.21..+0.25, plateau monotono su w 10-30%. A differenza di EW-STR +# (refutato lo stesso giorno) NON e' selezione-sull'hold-out: l'edge di GTAA e' pre-esistente. +# CONVENZIONE CROSS-VENUE ONESTA: nei giorni senza barra equity (weekend/festivi US) il rendimento +# e' 0.0 (capitale IB fermo), NON rinormalizzato sugli altri sleeve: due conti reali non si +# travasano nel weekend. ESEGUIBILE a basso capitale (ETF frazionali, switch mensile, gia' in +# paper_combo dal 2026-06-23). CAVEAT: cross-venue = due conti (Deribit+IB); il book live Deribit +# (deribit_book_sleeves) NON lo include. +from src.portfolio.gtaa import gtaa_returns, gtaa_weights + +# GTAA nel BOOK si attiva con l'era del book (inizio storia certificata BTC/ETH): senza troncatura +# l'outer-join farebbe partire il portafoglio nel 1996 con 23 anni di solo-GTAA rinormalizzato al +# 100%, distorcendo le metriche FULL. La storia 1996+ resta in gtaa.py per la ricerca. +GTAA_BOOK_ACTIVATION = pd.Timestamp("2019-03-14", tz="UTC") + + +def _gtaa_daily_returns() -> pd.Series: + r = gtaa_returns().dropna().sort_index() + if r.index.tz is None: + r.index = r.index.tz_localize("UTC") + days = pd.date_range(r.index.min().normalize(), r.index.max().normalize(), freq="1D", tz="UTC") + r = r.reindex(days).fillna(0.0) # weekend/festivi = capitale fermo (0), non riciclato + return r[r.index >= GTAA_BOOK_ACTIVATION] + + +def gtaa_sleeve(weight: float = 0.20) -> Sleeve: + return Sleeve("GTAA01_eq_trend", weight, _gtaa_daily_returns, pos_fn=gtaa_weights) + + _TRADE_FREQ_CACHE = None @@ -323,13 +359,17 @@ def active_sleeves() -> list[Sleeve]: """Sleeve ATTIVI nel portafoglio (pesi rinormalizzati; sleeve a date diverse si attivano quando parte la loro storia). Aggiungere qui SOLO strategie validate col gauntlet. - SKH01 cablato @0.25 effettivo: i tre sleeve preesistenti scalati nel restante 0.75 mantenendo - il loro rapporto 55:25:20 (-> 41.25/18.75/15), cosi' Skyhook pesa esattamente 25% del book.""" + SKH01 cablato @0.25 effettivo (2026-06-23): i tre preesistenti scalati nel restante 0.75 + (rapporto 55:25:20 -> 41.25/18.75/15). GTAA01 cablato @0.20 effettivo (2026-07-01): i quattro + preesistenti scalati ancora x0.80 (-> 33/15/12/20). NB: la cella IS-best della griglia era + w=30% ma l'ingresso e' @20% per scelta STRUTTURALE dichiarata (cross-venue IB con paper_combo + giovane; precedente ingresso-sleeve <=25%; costo CAGR) — tutte le celle 10-30% passano i gate.""" return [ - tp01_sleeve(weight=0.4125), # trend difensivo, BTC/ETH, dal 2019 (l'unico deployable pieno) - xsec_sleeve(weight=0.1875), # cross-sectional momentum Hyperliquid, dal 2024 (scorrelato, stat-mode) - vrp_sleeve(weight=0.15), # options short-vol (put credit spread + gate IV-rank), dal 2021 (lead modellato, scorrelato) - skyhook_sleeve(weight=0.25), # dual-TF regime+breakout BTC/ETH, dal 2019 (quasi-ortogonale, exit %-asimmetrici, research) + tp01_sleeve(weight=0.33), # trend difensivo, BTC/ETH, dal 2019 (l'unico deployable pieno) + xsec_sleeve(weight=0.15), # cross-sectional momentum Hyperliquid, dal 2024 (scorrelato, stat-mode) + vrp_sleeve(weight=0.12), # options short-vol (put credit spread + gate IV-rank), dal 2021 (lead modellato, scorrelato) + skyhook_sleeve(weight=0.20), # dual-TF regime+breakout BTC/ETH, dal 2019 (quasi-ortogonale, exit %-asimmetrici, research) + gtaa_sleeve(weight=0.20), # trend difensivo equity 6-ETF (IB), dal 1996 (diversificatore strutturale, cross-venue) ] diff --git a/tests/test_gtaa_sleeve.py b/tests/test_gtaa_sleeve.py new file mode 100644 index 0000000..58de15a --- /dev/null +++ b/tests/test_gtaa_sleeve.py @@ -0,0 +1,42 @@ +"""Lock del 5o sleeve GTAA01 (promosso 2026-07-01) e del registry a 5 sleeve. + +Pin degli invarianti decisi nel diario 2026-07-01-strategy-wave-6threads.md (addendum GTAA): + * registry = 5 sleeve, pesi 33/15/12/20/20 (somma 1), GTAA01 presente @0.20; + * convenzione cross-venue: weekend/festivi = 0.0 (capitale IB fermo, NON rinormalizzato); + * attivazione nel book all'era crypto (2019-03-14) — niente 1996-2019 di solo-GTAA; + * il book live Deribit (deribit_book_sleeves) NON include GTAA (resta TP01+SKH01 75/25). +""" +import sys +from pathlib import Path + +import numpy as np +import pandas as pd +import pytest + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) + +from src.portfolio.sleeves import GTAA_BOOK_ACTIVATION, _gtaa_daily_returns, active_sleeves, deribit_book_sleeves + + +def test_registry_5_sleeve_pesi(): + sl = active_sleeves() + w = {s.name: s.weight for s in sl} + assert len(sl) == 5 + assert w == {"TP01_trend_1d": 0.33, "XS01_xsec_hl": 0.15, "VRP01_shortvol": 0.12, + "SKH01_skyhook": 0.20, "GTAA01_eq_trend": 0.20} + assert sum(w.values()) == pytest.approx(1.0) + + +def test_gtaa_weekend_zero_e_attivazione(): + r = _gtaa_daily_returns() + assert r.index.min() == GTAA_BOOK_ACTIVATION # niente storia pre-book nel registry + assert r.index.freqstr in ("D", "1D") or (r.index[1:] - r.index[:-1] == pd.Timedelta("1D")).all() + wknd = r[r.index.dayofweek >= 5] # sab/dom: capitale equity fermo + assert len(wknd) > 100 and float(np.abs(wknd.values).max()) == 0.0 + wk = r[r.index.dayofweek < 5] + assert float(np.abs(wk.values).sum()) > 0 # nei feriali la strategia vive + + +def test_book_live_deribit_non_include_gtaa(): + names = [s.name for s in deribit_book_sleeves()] + assert names == ["TP01_trend_1d", "SKH01_skyhook"] # esecuzione live invariata