research(carry): cash-and-carry CC01 — premio reale, Sharpe artefatto, non deployabile
Provato l'angolo basis/cash-and-carry (l'altro proposto; il funding cross-sectional FC01 era gia' SCARTATO 2026-06-22). Delta-neutral long-spot/short-perp -> r=funding, zero esposizione prezzo. Premio di funding REALE (~+8-14%/anno aggregato, positivo ogni anno in-sample, ortogonale a TP01). MA Sharpe modellato 11-13 = ARTEFATTO: rischi di coda fuori dal dataset (manca il 2022; procyclico +23% 2024 -> +1.7% 2026; liquidazione/slippage non modellati). Il mark-to-market della base (premium col) sgonfia solo 13->11 -> il basis-from-data non e' il rischio vero. NON eseguibile a $600 (spot+perp, funding HL non Deribit) -> STAT-MODE. LEAD da rivedere a scala, non uno sleeve. Sottoprodotto: CC01 passa ogni gate del marginal scorer -> punto cieco (manca un gate 'Sharpe implausibile -> rischio nascosto'). - scripts/research/cash_carry_hl.py (CC-static/gated, BTC-ETH + 19-major, basis MtM, reality-check) - tests/test_cash_carry.py (lock del fatto economico, non dello Sharpe) - docs/diary/2026-06-26-cash-carry-hl.md - CLAUDE.md: riga nella sintesi di ricerca Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -107,6 +107,21 @@ Prima ondata di ricerca onesta su BTC/ETH certificati (5 track, harness condivis
|
||||
rehedge lento = premio, veloce = fee → **nessuna frequenza vince.** Regola gemella del VRP:
|
||||
*niente long-vol scalp da modello in deploy*. Il VRP01 (lato short, gated) resta l'unico edge
|
||||
opzioni — funziona perché sta sul lato *giusto* dello stesso premio. Diario `2026-06-26-gamma-scalp-options.md`.
|
||||
- **CASH-AND-CARRY (basis trade) "CC01" — premio REALE, Sharpe ARTEFATTO, NON deployabile (2026-06-26)** —
|
||||
`scripts/research/cash_carry_hl.py`, test `tests/test_cash_carry.py`. Diverso da FC01 (funding
|
||||
cross-sectional, già scartato): qui delta-neutral long-spot/short-perp sullo *stesso* asset →
|
||||
ritorno ≈ **+funding** (zero esposizione prezzo). Il premio di funding è **reale** (~+8-14%/anno
|
||||
aggregato, positivo ogni anno in-sample, ortogonale a TP01 corr ~0.05). MA lo **Sharpe modellato
|
||||
11-13 (DD 0.3%) è un ARTEFATTO**: il modello cattura solo il cashflow liscio del funding e i
|
||||
rischi di coda sono **strutturalmente fuori dal dataset** — (1) storico funding dal 2023-05 →
|
||||
**manca il 2022** (deleveraging, funding −, basis blow-out); (2) **procyclico** (carry +23% toro
|
||||
2024 → +1.7% bear 2026, si comprime quando servirebbe); (3) liquidazione short/slippage non
|
||||
modellati. Il mark-to-market della base (`premium` col → r=funding−Δpremium) sgonfia lo Sharpe
|
||||
solo 13→11 → il basis-from-data NON è il rischio vero. Sharpe reale di un basis-trade ~1-3 con
|
||||
code brusche. **NON eseguibile a $600** (spot+perp = 4-38 gambe, funding HL non Deribit) → STAT-MODE.
|
||||
**LEAD da rivedere a scala (~20k+ e venue con funding eseguibile), non uno sleeve.** Sottoprodotto:
|
||||
CC01 passa OGNI gate del marginal scorer → **punto cieco** (manca un gate "Sharpe implausibile →
|
||||
rischio nascosto"; prossima indurita raccomandata). Diario `2026-06-26-cash-carry-hl.md`.
|
||||
- **Soffitto strutturale BTC/ETH-direzionale ~1.3** superato SOLO espandendo a un meccanismo diverso:
|
||||
cross-sectional su universo Hyperliquid certificato (XS01) → portafoglio Sharpe ~1.55.
|
||||
- **Sweep "strategie alternative" (2026-06-20) — 104 ipotesi / 153 agenti / NIENTE di nuovo regge.**
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# 2026-06-26 — Cash-and-carry (basis trade) delta-neutral su HL (CC01): premio REALE, Sharpe ARTEFATTO, NON deployabile
|
||||
|
||||
**Goal utente:** dopo lo scarto del gamma scalping, "provare" gli altri due angoli proposti —
|
||||
funding-carry cross-sectional e basis/cash-and-carry. Il **funding cross-sectional (FC01) è già
|
||||
SCARTATO** (diario 2026-06-22: DILUTES, sign-flip su un nome). L'angolo non testato è il
|
||||
**cash-and-carry delta-neutral (CC01)**. Branch `research/gamma-scalp-options`,
|
||||
script `scripts/research/cash_carry_hl.py`.
|
||||
|
||||
## Meccanismo (diverso da FC01)
|
||||
|
||||
FC01 pickava cross-section (short alto-funding / long basso-funding) → scommessa relativa, fragile.
|
||||
CC01 **non picka**: harvesta il premio di funding AGGREGATO restando delta-neutral sullo *stesso*
|
||||
asset. Long spot + short perp (stessa size): le gambe lineari nel prezzo si cancellano → ritorno
|
||||
≈ **+funding** (lo short incassa il funding quando f>0), zero esposizione direzionale. Dati: funding
|
||||
orario reale HL dal 2023-05 (`hlfund_*_1h.parquet`), gli stessi 19 major di XS01.
|
||||
|
||||
## Risultato grezzo — e perché NON va creduto
|
||||
|
||||
| config | Sharpe | CAGR | maxDD | per-anno |
|
||||
|---|---|---|---|---|
|
||||
| BTC/ETH CC-static | **+12.9** | +13% | 0.8% | 2023:+10 2024:+23 2025:+8 2026:+1% |
|
||||
| BTC/ETH CC-gated | +13.9 | +14% | 0.3% | tutti positivi |
|
||||
| 19-major CC-gated | +11.6 | +16% | 0.4% | tutti positivi |
|
||||
|
||||
**Uno Sharpe di 13 con DD 0.3% è un ALLARME, non un edge.** È la firma di un modello che cattura
|
||||
solo un cashflow liscio (il funding) e azzera la volatilità reale. Il marginal scorer dà **ADDS /
|
||||
robust_oos / has_insample_edge=True / multicut_persistent** su OGNI config — ma questo **espone un
|
||||
punto cieco dello scorer**: si fida della vol riportata dal candidato e non ha un gate "Sharpe
|
||||
implausibile → rischio nascosto". Un Sharpe di 13 dovrebbe squalificare-e-indagare, non passare.
|
||||
|
||||
## Il rischio è NASCOSTO, non assente
|
||||
|
||||
- **Mark-to-market della base** (`premium` col → `r = funding − Δpremium`): sgonfia lo Sharpe solo
|
||||
13→11. La colonna premium di HL è già lisciata/clampata → a risoluzione daily il basis-vol è
|
||||
minimo. **Quindi il basis-from-data NON è il rischio vero.**
|
||||
- **I rischi che giustificherebbero uno Sharpe realistico (~1-3) sono ASSENTI dal dataset:**
|
||||
1. **2022 mancante.** Lo storico funding parte 2023-05 → niente deleveraging LUNA/3AC/FTX, il
|
||||
regime di funding profondamente negativo + basis blow-out che farebbe il vero drawdown.
|
||||
2. **Procyclicità.** Funding aggregato BTC/ETH: +23% nel toro 2024 → **+1.7% nel bear 2026**
|
||||
(e 19-major CC-static 2026 = **−1%**). Solo 10% di giorni a funding negativo *in-sample* — ma
|
||||
in un vero bear quella frazione esplode. Il carry **non è all-weather**: è un risk-on premium.
|
||||
3. **Liquidazione dello short** in uno squeeze e **slippage** su spot+perp: non modellati.
|
||||
- **Sharpe reale di un crypto basis-trade ≈ 1-3 con code brusche** (Mar-2020, Mag-2021, FTX), non 13.
|
||||
|
||||
## Eseguibilità a $600 — muro indipendente
|
||||
|
||||
Serve spot **+** perp per gamba: BTC/ETH = 4 posizioni, 19-major = 38. Su Deribit (dove operiamo) lo
|
||||
storico funding è **bloccato** (404) e non tradiamo spot HL. **STAT-MODE**, come XS01/VRP01 ma peggio
|
||||
(doppia gamba per asset). Non deployabile a questo capitale.
|
||||
|
||||
## Verdetto
|
||||
|
||||
**CC01 = premio di funding REALE (~+8-14%/anno aggregato, positivo ogni anno in-sample) e
|
||||
genuinamente ortogonale a TP01 (corr ~0.05, NON direzionale).** È la "fonte di ritorno diversa" più
|
||||
concreta trovata finora. MA:
|
||||
- lo **Sharpe headline è fiction** (rischi di coda strutturalmente fuori dal dataset);
|
||||
- è **procyclico** (si comprime/inverte nel bear — proprio quando servirebbe);
|
||||
- **non eseguibile** a $600 / sul nostro venue.
|
||||
|
||||
→ **LEAD da rivedere a scala** (capitale ~20k+ e un venue con funding eseguibile), **non uno sleeve**.
|
||||
Nessun impatto sul book live (branch separato).
|
||||
|
||||
## Sottoprodotto metodologico (raccomandazione)
|
||||
|
||||
CC01 ha passato ogni gate del marginal scorer pur essendo un'illusione di rischio. **Prossima
|
||||
indurita dello scorer:** un gate di *plausibilità dello Sharpe* — uno standalone Sharpe ≫ 4 deve
|
||||
declassare a "SUSPECT/INVESTIGATE" invece di alimentare ADDS, perché segnala vol sottostimata
|
||||
(mark-to-cashflow, dati che escludono il regime di stress, leva nascosta). Documentato qui, non
|
||||
implementato in questa passata per non toccare lo scorer condiviso su `main`.
|
||||
@@ -0,0 +1,173 @@
|
||||
"""CC01 — CASH-AND-CARRY (basis trade) delta-neutral su Hyperliquid. Backtest onesto, STAT-MODE.
|
||||
|
||||
DIVERSO da FC01 (funding cross-sectional, gia' SCARTATO 2026-06-22). Qui NON si pickano
|
||||
vincitori/perdenti cross-section: si HARVESTA il premio di funding AGGREGATO restando
|
||||
delta-neutral sullo STESSO asset.
|
||||
|
||||
MECCANISMO. Long spot + short perp (stesso asset, stessa size):
|
||||
long spot -> +price_ret
|
||||
short perp -> -price_ret + funding (lo short INCASSA il funding quando f>0)
|
||||
netto -> +funding (il prezzo si cancella -> zero esposizione direzionale)
|
||||
Il ritorno della gamba delta-neutral = il funding realizzato, meno fee. Entrambe le gambe sono
|
||||
lineari nel prezzo => restano matchate in nozionale senza ribilanciare (delta ~neutro da solo);
|
||||
i costi reali sono entry/exit + spread, modellati come drag annuo fisso.
|
||||
|
||||
VARIANTI:
|
||||
CC-static -> sempre long-spot/short-perp (basis trade classico): r = funding. Perde se f<0.
|
||||
CC-gated -> harvest solo quando il funding trailing CAUSALE > 0 (esci dai regimi a funding
|
||||
negativo invece di pagarli). r = funding se trail>0 else 0.
|
||||
UNIVERSI: BTC/ETH (sottoinsieme rilevante per l'esecuzione) e i 19 major (basket pieno).
|
||||
|
||||
GIUDIZIO: standalone (Sharpe/DD/anni) + marginal_vs_tp01. CAVEAT ONESTI (pre-risultato):
|
||||
- NON eseguibile a $600: serve spot+perp per gamba (BTC/ETH = 4 posizioni; 19 = 38). Su Deribit
|
||||
lo storico funding e' bloccato e non operiamo spot HL -> STAT-MODE.
|
||||
- Il modello "r=funding" IGNORA il rischio di base (perp != spot), la liquidazione dello short in
|
||||
uno squeeze, e l'inversione brusca del funding. La vol modellata SOTTOSTIMA la coda.
|
||||
- Lo storico funding parte 2023-05 -> NON contiene il deleveraging 2022 (il regime peggiore per il
|
||||
carry). Edge potenzialmente sovrastimato.
|
||||
|
||||
uv run python scripts/research/cash_carry_hl.py
|
||||
"""
|
||||
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))
|
||||
sys.path.insert(0, str(ROOT / "scripts" / "research" / "alt"))
|
||||
|
||||
from src.portfolio.sleeves import XS_UNIVERSE, _HL_DIR
|
||||
from altlib import marginal_vs_tp01 # type: ignore
|
||||
|
||||
SQ365 = np.sqrt(365.25)
|
||||
ANNUAL_COST = 0.02 # drag annuo fisso: entry/exit + spread + borrow (generoso per un hold continuo)
|
||||
|
||||
|
||||
def load_funding_panel(universe):
|
||||
"""FUND, PREM [date x asset]: funding giornaliero (somma oraria) e premium/basis (ultimo del
|
||||
giorno) per gli asset con dati. PREM serve a iniettare il mark-to-market della BASE (perp!=spot)."""
|
||||
fund, prem = {}, {}
|
||||
for sym in universe:
|
||||
fp = _HL_DIR / f"hlfund_{sym.lower()}_1h.parquet"
|
||||
if not fp.exists():
|
||||
continue
|
||||
df = pd.read_parquet(fp)
|
||||
fund[sym] = df["funding"].resample("1D").sum()
|
||||
prem[sym] = df["premium"].resample("1D").last()
|
||||
FUND = pd.concat(fund, axis=1).sort_index()
|
||||
PREM = pd.concat(prem, axis=1).sort_index().reindex(FUND.index)
|
||||
if FUND.index.tz is None:
|
||||
FUND.index = FUND.index.tz_localize("UTC"); PREM.index = PREM.index.tz_localize("UTC")
|
||||
return FUND, PREM
|
||||
|
||||
|
||||
def cc_returns(universe, mode="static", trail=14, cost=ANNUAL_COST, with_basis=False) -> pd.Series:
|
||||
"""Ritorno giornaliero del basket cash-and-carry equal-weight, netto drag annuo.
|
||||
with_basis=True inietta il mark-to-market reale della base: r = funding - Δpremium (lo short
|
||||
perp marca contro l'allargamento del basis). E' il rischio che 'r=funding' nasconde."""
|
||||
FUND, PREM = load_funding_panel(universe)
|
||||
daily_cost = cost / 365.25
|
||||
leg_raw = FUND - PREM.diff() if with_basis else FUND # per-asset daily mark
|
||||
if mode == "gated":
|
||||
sig = FUND.rolling(trail, min_periods=trail // 2).mean().shift(1) # funding trailing causale
|
||||
active = (sig > 0).astype(float)
|
||||
n_active = active.sum(axis=1).replace(0, np.nan)
|
||||
gross = ((leg_raw * active).sum(axis=1) / n_active).fillna(0.0)
|
||||
drag = (active.sum(axis=1) > 0).astype(float) * daily_cost
|
||||
else:
|
||||
gross = leg_raw.mean(axis=1)
|
||||
drag = daily_cost
|
||||
return (gross - drag).dropna()
|
||||
|
||||
|
||||
def metrics(daily: pd.Series) -> dict:
|
||||
r = daily.values
|
||||
sh = float(np.mean(r) / np.std(r) * SQ365) if np.std(r) > 0 else 0.0
|
||||
eq = np.cumprod(1.0 + np.clip(r, -0.99, None))
|
||||
pk = np.maximum.accumulate(eq)
|
||||
dd = float(np.max((pk - eq) / pk)) if len(eq) else 0.0
|
||||
yrs = (daily.index[-1] - daily.index[0]).days / 365.25 if len(daily) > 1 else 1.0
|
||||
cagr = eq[-1] ** (1 / yrs) - 1 if yrs > 0 and len(eq) and eq[-1] > 0 else -1.0
|
||||
s = pd.Series(eq, index=daily.index)
|
||||
yearly = {}
|
||||
for y, g in s.groupby(s.index.year):
|
||||
if len(g) > 1:
|
||||
v = g.values; p = np.maximum.accumulate(v)
|
||||
yearly[int(y)] = (float(g.iloc[-1] / g.iloc[0] - 1), float(np.max((p - v) / p)))
|
||||
return dict(sharpe=sh, dd=dd, cagr=cagr, yearly=yearly, gross_ann=float(np.mean(r) * 365.25))
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 92)
|
||||
print(" CC01 — CASH-AND-CARRY (basis trade) delta-neutral su Hyperliquid")
|
||||
print(" long spot + short perp -> r = funding (zero esposizione prezzo). Netto drag 2%/anno.")
|
||||
print("=" * 92)
|
||||
|
||||
configs = [
|
||||
("BTC/ETH CC-static", ["BTC", "ETH"], "static"),
|
||||
("BTC/ETH CC-gated", ["BTC", "ETH"], "gated"),
|
||||
("19-major CC-static", XS_UNIVERSE, "static"),
|
||||
("19-major CC-gated", XS_UNIVERSE, "gated"),
|
||||
]
|
||||
print("\n [A] MODELLO INGENUO (r = funding) — IGNORA il rischio di base:")
|
||||
series = {}
|
||||
for label, uni, mode in configs:
|
||||
r = cc_returns(uni, mode=mode)
|
||||
series[label] = r
|
||||
m = metrics(r)
|
||||
ys = " ".join(f"{y}:{p*100:+.0f}%" for y, (p, d) in sorted(m['yearly'].items()))
|
||||
print(f"\n --- {label} --- ({r.index[0].date()} -> {r.index[-1].date()}, {len(r)}g)")
|
||||
print(f" Sharpe {m['sharpe']:+.2f} CAGR {m['cagr']*100:+.1f}% maxDD {m['dd']*100:.1f}% "
|
||||
f"carry lordo {m['gross_ann']*100:+.1f}%/anno | per-anno: {ys}")
|
||||
|
||||
print("\n [B] CON MARK-TO-MARKET DELLA BASE (r = funding - Δpremium) — il rischio nascosto:")
|
||||
series_b = {}
|
||||
for label, uni, mode in configs:
|
||||
r = cc_returns(uni, mode=mode, with_basis=True)
|
||||
series_b[label] = r
|
||||
m = metrics(r)
|
||||
ys = " ".join(f"{y}:{p*100:+.0f}%" for y, (p, d) in sorted(m['yearly'].items()))
|
||||
print(f"\n --- {label} (basis) ---")
|
||||
print(f" Sharpe {m['sharpe']:+.2f} CAGR {m['cagr']*100:+.1f}% maxDD {m['dd']*100:.1f}% "
|
||||
f"| per-anno: {ys}")
|
||||
|
||||
print("\n" + "=" * 92)
|
||||
print(" REALITY CHECK — perche' uno Sharpe 11-13 e' un ARTEFATTO, non un edge")
|
||||
print("=" * 92)
|
||||
FUND, _ = load_funding_panel(["BTC", "ETH"])
|
||||
agg = FUND.mean(axis=1)
|
||||
neg = float((agg < 0).mean())
|
||||
by_year = agg.groupby(agg.index.year).apply(lambda x: float(x.sum()))
|
||||
print(f" funding aggregato BTC/ETH: giorni a funding NEGATIVO {neg*100:.0f}% | "
|
||||
f"per-anno (somma): " + " ".join(f"{y}:{v*100:+.1f}%" for y, v in by_year.items()))
|
||||
print(" -> il carry e' PROCYCLICO: +23% nel toro 2024, ~+1% nel 2026 (si comprime nel bear).")
|
||||
print(" -> lo storico funding parte 2023-05: ASSENTE il deleveraging 2022 (funding -, basis blow-out),")
|
||||
print(" il regime che farebbe il vero drawdown. + assenti: liquidazione short in squeeze, slippage")
|
||||
print(" su spot+perp a $600. Sharpe reale di un basis-trade ~1-3 con code brusche, NON 13.")
|
||||
|
||||
print("\n" + "=" * 92)
|
||||
print(" SCORING MARGINALE vs TP01 (sul modello ONESTO con rischio di base [B])")
|
||||
print(" NB: ADDS/robust_oos qui ESPONGONO un punto cieco dello scorer — si fida della vol")
|
||||
print(" riportata e non ha un gate 'Sharpe implausibile -> rischio nascosto'.")
|
||||
print("=" * 92)
|
||||
for label, r in series_b.items():
|
||||
if r.std() == 0:
|
||||
print(f"\n[{label}] flat — skip"); continue
|
||||
m = marginal_vs_tp01(r)
|
||||
b = m.get("blends", {}).get("w25", {})
|
||||
print(f"\n[{label}]")
|
||||
print(f" verdict={m.get('marginal_verdict')} corr->TP01 full={m.get('corr_full')} "
|
||||
f"hold={m.get('corr_hold')} is_hedge={m.get('is_hedge')} "
|
||||
f"has_insample_edge={m.get('has_insample_edge')} (cand IS Sharpe {m.get('cand_insample_sharpe')})")
|
||||
print(f" cand Sharpe full={m.get('cand_full_sharpe')} hold={m.get('cand_hold_sharpe')} | "
|
||||
f"blend25 full {b.get('full')} (upl {b.get('uplift_full')}) "
|
||||
f"hold {b.get('hold')} (upl {b.get('uplift_hold')}) DD {b.get('dd')}")
|
||||
print(f" multicut persistente={m.get('multicut_persistent')} robust_oos={m.get('robust_oos')} "
|
||||
f"hedge-check up/down {m.get('uplift_tp01_up')}/{m.get('uplift_tp01_down')}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,36 @@
|
||||
"""Lock della conclusione 2026-06-26 (CC01 cash-and-carry): il premio di funding e' REALE e
|
||||
positivo, ma PROCYCLICO (si comprime nel bear) — NON un edge all-weather. Lo Sharpe headline ~13 e'
|
||||
un artefatto (rischi di coda assenti dal dataset) -> non testiamo lo Sharpe, testiamo il FATTO
|
||||
economico e la procyclicita'. Diario docs/diary/2026-06-26-cash-carry-hl.md."""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
||||
|
||||
from cash_carry_hl import cc_returns, load_funding_panel, metrics # type: ignore
|
||||
|
||||
|
||||
def test_funding_premium_is_real_and_positive():
|
||||
"""Il carry esiste: funding aggregato BTC/ETH positivo, CC-static con CAGR > 0."""
|
||||
r = cc_returns(["BTC", "ETH"], mode="static")
|
||||
m = metrics(r)
|
||||
assert m["gross_ann"] > 0.0 # premio di funding netto positivo
|
||||
assert m["cagr"] > 0.0
|
||||
|
||||
|
||||
def test_basis_markup_does_not_explain_high_sharpe():
|
||||
"""Aggiungere il mark-to-market della base (Δpremium) NON sgonfia lo Sharpe in modo materiale:
|
||||
prova che il rischio vero e' FUORI dal dataset (2022/liquidazione), non nel basis dei dati."""
|
||||
naive = metrics(cc_returns(["BTC", "ETH"], mode="static"))["sharpe"]
|
||||
basis = metrics(cc_returns(["BTC", "ETH"], mode="static", with_basis=True))["sharpe"]
|
||||
assert basis > 0.7 * naive # cala poco -> il basis-from-data non e' il rischio nascosto
|
||||
|
||||
|
||||
def test_carry_is_procyclical_fades_in_bear():
|
||||
"""Il carry e' procyclico: il funding aggregato del toro 2024 supera quello del bear 2026."""
|
||||
FUND, _ = load_funding_panel(["BTC", "ETH"])
|
||||
agg = FUND.mean(axis=1)
|
||||
by_year = agg.groupby(agg.index.year).sum()
|
||||
assert by_year.get(2024, 0.0) > by_year.get(2026, 0.0)
|
||||
Reference in New Issue
Block a user