Files

1142 lines
66 KiB
Python

#!/usr/bin/env python3
"""r0822e_funding_avoid.py — FUNDING-AVOID: il costo di tenere aperta una posizione si puo'
EVITARE cambiando STRUMENTO, senza toccare il segnale?
CONTESTO (ondata 2026-08-22, sezioni 34-35 di docs/research/RESULTS-0822.md)
(1) Stanotte e' stato MISURATO che il libro paga **2,16%/anno di funding** (TP01 -2,10%,
SKH01 -2,32%), mai modellato prima. E' una tassa sul DRIFT: il maxDD non si muove
(+0,3pp) ma la rendita perpetua scende 10,73% -> 9,14% e il muro di capitale sale del
+17,5%. Su una rendita, il drift e' la moneta.
(2) Stanotte e' stato PERSISTITO in `data/raw/fut_deribit/` lo storico dei futures DATATI
Deribit: 34 trimestrali per asset, BTC 240.150 barre orarie / ETH 233.334, 2018-09 ->
2026-08, piu' 64.110 ore di funding e indice dal 2019-04.
LA DOMANDA, MAI POSTA: **un future DATATO non paga funding.** Il carry e' incorporato nel
PREZZO (contango = compri sopra lo spot e convergi a scadenza). Quindi:
esprimere TP01 (e/o SKH01) su un future datato invece che sul perpetual
costa MENO o PIU' del funding che paghiamo oggi?
E' una domanda sull'ESECUZIONE, non sul segnale: il segnale resta congelato (TSMOM 30/90/180
su BTC/ETH, vol-target 20%, `CANONICAL`), cambia solo lo strumento su cui lo si esprime.
Un grado di liberta' per volta, replica di controllo prima di ogni delta.
LA TESI DA VERIFICARE O FALSIFICARE (la ragione per cui il filone puo' restituire qualcosa)
Il funding e' caro PROPRIO QUANDO SIAMO LONG: misurato stanotte, il tasso che il libro paga
davvero e' 1,86-2,55x l'incondizionato, perche' esposizione e funding sono positivamente
correlati. Il basis di un datato invece e' FISSATO ALL'INGRESSO e non si riprezza contro di
te mentre tieni. Se il rapporto condizionale/incondizionale del basis e' piu' BASSO di
quello del funding, il datato converte un costo condizionale e avverso in uno noto.
SEZIONI
0 IL DATO — 68 contratti trimestrali, copertura, roll; specifiche e LIQUIDITA' lette dal
venue (cache in scratchpad, `--no-net` per girare offline).
1 REPLICA DI CONTROLLO — il -2,16%/anno va riprodotto PRIMA di confrontarci qualsiasi cosa.
2 L'ESTIMATORE — livello vs flusso. La differenza ARITMETICA dei rendimenti fra due
strumenti sullo stesso sottostante NON e' il carry: contiene un termine di varianza
(Jensen) che qui vale 1,5-2,6 punti percentuali l'anno, cioe' 2-4x l'effetto cercato.
3 IL CONFRONTO APPAIATO — per giorno e per segnale, condizionale vs incondizionale.
4 IL ROLL E LA LIQUIDITA' — quante volte si rolla, quanto costa attraversare, e a $635.
5 LE SERIE — libro nelle 5 varianti + banda d'ancora (23 offset, differenze APPAIATE).
6 I MURI — drift -> rendita perpetua -> capitale, con la macchineria di r0726_capwall.
7 IL MURO CHE NON C'E' — «tutti i datati Deribit sono INVERSE» e' FALSO da maggio 2026.
8 SOTTOPRODOTTO — lo SPOT, che ha carry ZERO e oggi fee ZERO, e la sua domanda aperta.
9 GATE + VERDETTO.
VINCOLI: nessun file di produzione toccato (config/, src/, scripts/live/, cron INVARIATI);
nessun commit; output a video + cache in scratchpad. Runtime ~2-4 min.
USO
nice -n 19 timeout 900 uv run python scripts/research/r0822e_funding_avoid.py
... --no-net usa solo la cache del venue (nessuna richiesta a Deribit)
"""
from __future__ import annotations
import datetime as dt
import json
import sys
import time
import urllib.request
from pathlib import Path
import numpy as np
import pandas as pd
ROOT = Path("/opt/docker/PythagorasGoal")
for _p in (ROOT / "scripts" / "research", ROOT / "scripts" / "research" / "alt", ROOT):
sys.path.insert(0, str(_p))
# r0822d_funding legge `--no-net` da sys.argv AL MOMENTO DELL'IMPORT: qui non si scarica il
# funding (la sua cache esiste gia'), quindi glielo si impone. Le uniche chiamate di rete di
# QUESTO script sono le sue, sotto, ed hanno il proprio flag.
_NET = "--no-net" not in sys.argv
if "--no-net" not in sys.argv:
sys.argv.append("--no-net")
import altlib as al # noqa: E402
import r0702_anchor_skh01 as r02 # noqa: E402
import r0725_capcurve as CC # noqa: E402
import r0726_capwall_refresh as WR # noqa: E402
import r0822d_funding as FD # noqa: E402
from src.portfolio.portfolio import combine_outer # noqa: E402
T0 = time.time()
ASSETS = ("BTC", "ETH")
FUT = ROOT / "data" / "raw" / "fut_deribit"
_SP = Path("/tmp/claude-1001/-opt-docker-PythagorasGoal/"
"b6cc75e7-14f8-4c32-bd07-ab8a0d2aaee6/scratchpad/fa")
SCRATCH = _SP if _SP.parent.exists() else Path("/tmp/pythagoras_favoid")
API = "https://www.deribit.com/api/v2/public/"
MON = {"JAN": 1, "FEB": 2, "MAR": 3, "APR": 4, "MAY": 5, "JUN": 6,
"JUL": 7, "AUG": 8, "SEP": 9, "OCT": 10, "NOV": 11, "DEC": 12}
BUF_CANON = 5 # si rolla 5 giorni prima della scadenza (dichiarato a priori)
BUF_GRID = (2, 5, 10, 20) # sensibilita' del parametro d'esecuzione
ALLOC = 600.0 # il capitale su cui gira book_series (conto reale $635)
DELUCK = 0.89 # fattore d'ancora MISURATO (r0726_deluck_factor)
W_TP, W_SKH = 0.75, 0.25
LO_BOOK = pd.Timestamp("2019-03-14", tz="UTC")
FEE_REALE = 0.00035 # taker Deribit letto dal venue (3,5 bps/lato)
# il modello di CC.tp01_realistic usa 5 bps/lato (convenzione conservativa del progetto)
FEE_MODELLO = CC.DERIBIT_FEE_SIDE
def sez(t: str) -> None:
print("\n" + "=" * 100)
print(f" {t}")
print("=" * 100)
def clock() -> str:
return f"[{time.time() - T0:5.0f}s]"
def expdate(nm: str) -> pd.Timestamp:
d = nm.split("-")[1]
return pd.Timestamp(dt.datetime(2000 + int(d[5:]), MON[d[2:5]], int(d[:2]), 8, 0),
tz="UTC")
# ===========================================================================
# 0) IL DATO
# ===========================================================================
def _pace() -> None:
"""Mai nei minuti :07 (cron_book) e :25-:29 (cron_chain); <=2 req/s."""
while dt.datetime.now(dt.timezone.utc).minute in (7, 25, 26, 27, 28, 29):
time.sleep(20)
time.sleep(0.6)
def _get(path: str):
_pace()
return json.load(urllib.request.urlopen(API + path, timeout=30))["result"]
VENUE_CACHE = SCRATCH / "venue_snapshot.json"
TICKETS = (100, 240, 500) # $ per ordine: 240 = nozionale max per asset di TP01 a $635
def venue_snapshot() -> dict | None:
"""Specifiche + libro degli strumenti candidati. UNA istantanea, non una distribuzione:
e' esattamente cio' che si puo' dire e va detto cosi'."""
if VENUE_CACHE.exists():
old = json.loads(VENUE_CACHE.read_text())
else:
old = None
if not _NET:
return old
try:
out = {"ts": pd.Timestamp.now(tz="UTC").isoformat(), "instruments": {}, "books": {}}
for cur in ("BTC", "ETH", "USDC"):
for kind in ("future", "spot"):
try:
for x in _get(f"get_instruments?currency={cur}&kind={kind}&expired=false"):
out["instruments"][x["instrument_name"]] = dict(
settlement=x.get("settlement_currency"),
contract_size=x.get("contract_size"),
min_trade_amount=x.get("min_trade_amount"),
tick=x.get("tick_size"), taker=x.get("taker_commission"),
maker=x.get("maker_commission"), kind=kind, currency=cur,
created=x.get("creation_timestamp"),
expiry=x.get("expiration_timestamp"))
except Exception:
pass
for i in ("BTC_USDC-PERPETUAL", "BTC_USDC-25SEP26", "BTC_USDC-25DEC26", "BTC_USDC",
"BTC-25SEP26", "BTC-PERPETUAL",
"ETH_USDC-PERPETUAL", "ETH_USDC-25SEP26", "ETH_USDC-25DEC26", "ETH_USDC",
"ETH-25SEP26", "ETH-PERPETUAL"):
try:
d = _get(f"get_order_book?instrument_name={i}&depth=20")
out["books"][i] = dict(bid=d.get("best_bid_price"), ask=d.get("best_ask_price"),
index=d.get("index_price"), oi=d.get("open_interest"),
bids=d.get("bids"), asks=d.get("asks"),
vol24=(d.get("stats") or {}).get("volume"),
vol24usd=(d.get("stats") or {}).get("volume_usd"))
except Exception:
pass
SCRATCH.mkdir(parents=True, exist_ok=True)
VENUE_CACHE.write_text(json.dumps(out))
return out
except Exception as e: # rete KO: si dichiara
print(f" ⚠️ venue non raggiungibile ({type(e).__name__}) -> uso la cache se c'e'.")
return old
def walk_cost_bps(book: dict, usd: float, side: str) -> float | None:
"""Costo effettivo vs mid di un ordine da `usd` dollari, camminando il libro.
Ritorna None se il libro non basta (e allora si dice, non si estrapola)."""
if not book or not book.get("bid") or not book.get("ask"):
return None
mid = (book["bid"] + book["ask"]) / 2.0
lv = book.get("asks" if side == "buy" else "bids") or []
need, got, cost = usd / mid, 0.0, 0.0
for p, q in lv:
take = min(q, need - got)
got += take
cost += take * p
if got >= need - 1e-12:
break
if got < need - 1e-12:
return None
eff = cost / got
return (eff / mid - 1.0) * 1e4 * (1.0 if side == "buy" else -1.0)
def sezione0(V: dict | None) -> dict:
sez("0) IL DATO — 68 trimestrali datati su disco, e le specifiche LETTE dal venue")
tab = {}
for a in ASSETS:
rows = []
for p in sorted(FUT.glob(f"{a}-*.parquet")):
d = pd.read_parquet(p, columns=["ts", "close", "volume"])
if len(d) < 200:
continue
e = expdate(p.stem)
d = d[(d.ts <= e) & (d.close > 0)]
rows.append((p.stem, e, d.ts.min(), d.ts.max(), len(d), float(d.volume.sum())))
R = pd.DataFrame(rows, columns=["nm", "exp", "t0", "t1", "n", "vol"]).sort_values("exp")
tab[a] = R
print(f" {a}: {len(R)} contratti trimestrali usabili, scadenze da {R.exp.min().date()} "
f"a {R.exp.max().date()}, {int(R.n.sum()):,} barre orarie")
print(" ⚠️ Istantanea al 22/08 (il cron NON rinfresca questi file) e il contratto in corso")
print(" e' troncato. Il guardrail «solo dati certi» regge perche' vivono in una")
print(" sottocartella con prefisso, fuori dal namespace nudo di `load_data`.")
if not V:
print("\n ⚠️ SPECIFICHE DEL VENUE: NON MISURATE (nessuna rete, nessuna cache).")
return dict(tab=tab, spec=None)
print(f"\n Istantanea del venue del {V['ts'][:19]}Z — UNA lettura, non una distribuzione.")
print(f" {'strumento':<22} {'settl':>6} {'min':>9} {'tick':>7} {'taker':>7} "
f"{'spread':>8} {'vol 24h $':>13} {'OI':>14}")
for i in ("BTC_USDC-PERPETUAL", "BTC_USDC-25SEP26", "BTC_USDC-25DEC26", "BTC-25SEP26",
"BTC_USDC", "ETH_USDC-PERPETUAL", "ETH_USDC-25SEP26", "ETH_USDC-25DEC26",
"ETH-25SEP26", "ETH_USDC"):
s = V["instruments"].get(i, {})
b = V["books"].get(i, {})
sp = float("nan")
if b.get("bid") and b.get("ask"):
mid = (b["bid"] + b["ask"]) / 2
sp = (b["ask"] - b["bid"]) / mid * 1e4
print(f" {i:<22} {str(s.get('settlement')):>6} {str(s.get('min_trade_amount')):>9} "
f"{str(s.get('tick')):>7} {str(s.get('taker')):>7} {sp:>7.2f}b "
f"{(b.get('vol24usd') or float('nan')):>13,.0f} "
f"{(b.get('oi') or float('nan')):>14,.0f}")
return dict(tab=tab, spec=V)
# ===========================================================================
# 1) IL CARRY DEI DATATI — costruzione della serie
# ===========================================================================
def aligned_index(a: str, grid: pd.DatetimeIndex) -> np.ndarray:
"""Indice Deribit ALLINEATO alla convenzione delle barre orarie.
⚠️ TRAPPOLA MISURATA IN QUESTA SESSIONE, e non e' un dettaglio: `fund_{a}.parquet`
etichetta il record T con l'indice ALL'ISTANTE T, mentre `al.get(a,'1h')` etichetta la
barra all'INIZIO (la sua chiusura e' il prezzo a T+1h). Appaiare le due serie per
etichetta le sfasa di UN'ORA: corr(r_perp_t, r_index_t) = -0,02 e corr(r_perp_t,
r_index_t+1) = +0,996, la base perp/indice esce con sd 68-86 bps invece di pochi bps, e
la differenza di rendimento eredita 97-122 bps/ora di rumore INVENTATO.
Nessuna eccezione, nessun NaN: due serie plausibili che non parlano dello stesso istante.
(Stessa famiglia dell'errore di RISOLUZIONE `[ms]/[us]/[s]` catturato ieri notte in
r0822d_funding, e del `DatetimeIndex.view("int64")` codificato in CLAUDE.md il 01/07:
li' era la scala, qui e' l'ETICHETTA. Il sintomo e' lo stesso — un numero credibile.)
"""
fu = pd.read_parquet(FUT / f"fund_{a}.parquet").set_index("ts").sort_index()
return fu["index"].reindex(grid).shift(-1).values.astype(float)
def verifica_allineamento(grid: dict) -> None:
"""Controllo POSITIVO dell'allineamento: deve fallire se lo si sbaglia, o non e' un test."""
print("\n [ALLINEAMENTO] controllo positivo indice-vs-barre (corr dei log-rendimenti):")
for a in ASSETS:
fu = pd.read_parquet(FUT / f"fund_{a}.parquet").set_index("ts").sort_index()
raw = fu["index"].reindex(grid[a]).values.astype(float)
fix = aligned_index(a, grid[a])
pp = al.get(a, "1h").set_index("datetime")["close"].astype(float)
pp.index = pd.DatetimeIndex(pp.index)
pv = pp.reindex(grid[a]).values.astype(float)
out = []
for lbl, v in (("per etichetta", raw), ("allineato", fix)):
rP = np.diff(np.log(pv)); rI = np.diff(np.log(v))
m = np.isfinite(rP) & np.isfinite(rI)
b = pv / v
out.append((lbl, float(np.corrcoef(rP[m], rI[m])[0, 1]),
float(np.nanstd(b - 1.0)) * 1e4))
print(f" {a}: per etichetta corr {out[0][1]:+.4f} sd base {out[0][2]:6.1f} bps -> "
f"allineato corr {out[1][1]:+.4f} sd base {out[1][2]:5.1f} bps")
assert out[1][1] > 0.99 > out[0][1], f"allineamento non riparato su {a}"
print(" -> il controllo ha POTENZA: con l'etichetta grezza la correlazione e' ~0.")
def dated_rate(a: str, grid: pd.DatetimeIndex, buf: int = BUF_CANON,
log: bool = True) -> dict:
"""Pseudo-`rate` compatibile con `FD.cumF` / `FD.daily_funding`: al posto del funding
orario mette il CARRY del future datato front, cioe' quanto il datato sotto-rende il
perpetual in quell'ora (>0 in contango = costo per un long).
Il contratto tenuto durante l'ora t e' quello scelto alla chiusura di t-1 (nessuna
decisione usa il prezzo dell'ora che si sta valutando), e nell'ora di ROLL il rendimento
lo fa il contratto VECCHIO: cosi' non si perde nessuna ora e non si incolla nessun salto
di prezzo (convenzione dichiarata in r0822d_cc01_2022: si incollano i RENDIMENTI).
"""
n = len(grid)
idxp = aligned_index(a, grid)
perp = al.get(a, "1h").set_index("datetime")["close"].astype(float)
perp.index = pd.DatetimeIndex(perp.index)
pp = perp.reindex(grid).values.astype(float)
C = {}
for p in sorted(FUT.glob(f"{a}-*.parquet")):
d = pd.read_parquet(p, columns=["ts", "close", "volume"])
if len(d) < 200:
continue
e = expdate(p.stem)
d = d[(d.ts <= e) & (d.close > 0)].set_index("ts").sort_index()
C[p.stem] = (e, d["close"].reindex(grid).values.astype(float),
d["volume"].reindex(grid).values.astype(float))
order = sorted(C, key=lambda x: C[x][0])
px = np.full(n, np.nan); nmi = np.full(n, -1); dte = np.full(n, np.nan)
vol = np.full(n, np.nan)
for k, nme in enumerate(order):
e, s, v = C[nme]
dd = (e - grid).total_seconds().values / 86400.0
m = (dd >= buf) & np.isfinite(s) & np.isnan(px)
px[m] = s[m]; nmi[m] = k; dte[m] = dd[m]; vol[m] = v[m]
def ret(v: np.ndarray) -> np.ndarray:
r = np.full(n, np.nan)
r[1:] = (np.log(v[1:]) - np.log(v[:-1])) if log else (v[1:] / v[:-1] - 1.0)
return r
rP, rF = ret(pp), ret(px)
c = np.zeros(n); val = np.zeros(n, bool)
same = np.concatenate([[False], nmi[1:] == nmi[:-1]])
m = same & np.isfinite(rF) & np.isfinite(rP)
c[m] = rP[m] - rF[m]; val[m] = True
roll = np.zeros(n, bool)
ch = np.where((nmi[1:] != nmi[:-1]) & (nmi[1:] >= 0) & (nmi[:-1] >= 0))[0] + 1
for i in ch:
s = C[order[nmi[i - 1]]][1]
if np.isfinite(s[i]) and np.isfinite(s[i - 1]) and np.isfinite(rP[i]):
old = (np.log(s[i]) - np.log(s[i - 1])) if log else (s[i] / s[i - 1] - 1.0)
c[i] = rP[i] - old; val[i] = True
roll[ch] = True
return dict(ts=FD.to_ms(grid), i1h=c, cum=np.concatenate([[0.0], np.cumsum(c)]),
series=pd.Series(c, index=grid), switch=grid[0], valid=val, roll=roll,
bann=(px / idxp - 1.0) / (dte / 365.25), dte=dte, vol=vol, grid=grid,
nmi=nmi, order=order, px=px, idx=idxp, pp=pp)
def spot_rate(a: str, grid: pd.DatetimeIndex, log: bool = True) -> dict:
"""Stessa forma, ma il carry di esprimere la posizione sullo SPOT: quanto il perpetual
sovra-rende l'INDICE (= il costo che si evita non usando il perpetual). Lo spot vero di
Deribit non ha storia in questo progetto: si usa l'INDICE, dichiarato come tale."""
n = len(grid)
ix = aligned_index(a, grid)
perp = al.get(a, "1h").set_index("datetime")["close"].astype(float)
perp.index = pd.DatetimeIndex(perp.index)
pp = perp.reindex(grid).values.astype(float)
def ret(v):
r = np.full(n, np.nan)
r[1:] = (np.log(v[1:]) - np.log(v[:-1])) if log else (v[1:] / v[:-1] - 1.0)
return r
c = np.nan_to_num(ret(pp) - ret(ix))
return dict(ts=FD.to_ms(grid), i1h=c, cum=np.concatenate([[0.0], np.cumsum(c)]),
series=pd.Series(c, index=grid), switch=grid[0],
valid=np.isfinite(c), roll=np.zeros(n, bool), grid=grid)
# ===========================================================================
# 2) REPLICA DI CONTROLLO
# ===========================================================================
def book(rate_or_none, off: int = 0, tp_series: pd.Series | None = None) -> pd.Series:
"""Libro LIVE 75/25 alla lente `hourly`. ⚠️ `FD._SKH` e' cachato su
`(asset, off, mode, rate is not None)`: due `rate` DIVERSI collidono. Si svuota sempre."""
FD._SKH.clear()
s = FD.skh_book(off, "hourly", rate_or_none)
if s.index.tz is None:
s.index = s.index.tz_localize("UTC")
tp = tp_series if tp_series is not None else FD.tp01_realistic(ALLOC * W_TP,
rate_or_none)[0]
return combine_outer({"TP01": tp, "SKH01": s}, {"TP01": W_TP, "SKH01": W_SKH}, lo=LO_BOOK)
def sezione1(rate: dict) -> dict:
sez("1) REPLICA DI CONTROLLO — il -2,16%/anno prima di confrontarci qualsiasi cosa")
a0, a1 = FD.stat(book(None)), FD.stat(book(rate))
t0, t1 = FD.stat(FD.tp01_realistic(ALLOC * W_TP, None)[0]), \
FD.stat(FD.tp01_realistic(ALLOC * W_TP, rate)[0])
FD._SKH.clear(); s0 = FD.stat(FD.skh_book(0, "hourly", None))
FD._SKH.clear(); s1 = FD.stat(FD.skh_book(0, "hourly", rate))
print(f" {'serie':<26} {'ShFULL':>8} {'ShHOLD':>8} {'maxDD':>7} {'CAGR':>8} {'drift':>8}")
for nm, x, y in (("LIBRO 75/25 path live", a0, a1), ("TP01 realistic", t0, t1),
("SKH01 50/50 path live", s0, s1)):
print(f" {nm:<26} {x['full']:>8.3f} {x['hold']:>8.3f} {x['dd']:>7.1%} "
f"{x['cagr']:>8.2%} {x['drift']:>8.2%}")
print(f" {' + funding':<26} {y['full']:>8.3f} {y['hold']:>8.3f} {y['dd']:>7.1%} "
f"{y['cagr']:>8.2%} {y['drift']:>8.2%}")
print(f" {' DELTA':<26} {y['full']-x['full']:>+8.3f} {y['hold']-x['hold']:>+8.3f} "
f"{y['dd']-x['dd']:>+7.1%} {y['cagr']-x['cagr']:>+8.2%} "
f"{y['drift']-x['drift']:>+8.2%}")
d = a1["drift"] - a0["drift"]
ok = abs(d + 0.0216) < 0.0005
print(f"\n drift del libro {a0['drift']:.2%} -> {a1['drift']:.2%} = {d:+.3%}/anno "
f"contro il -2,16% pubblicato -> {'REPLICATO' if ok else '⚠️ NON REPLICA'}")
assert ok, "la replica del funding non torna: nessun delta oltre questo punto"
return dict(book0=a0, book1=a1, tp0=t0, tp1=t1, skh0=s0, skh1=s1)
# ===========================================================================
# 3) L'ESTIMATORE — livello vs flusso, in una veste nuova
# ===========================================================================
def sezione2(rate: dict, grid: dict) -> None:
sez("2) L'ESTIMATORE — la differenza ARITMETICA dei rendimenti NON e' il carry")
print(" Il costo di tenere un datato e' MOLTIPLICATIVO (il basis si consuma in")
print(" proporzione), il funding e' un FLUSSO di cassa (tasso x nozionale). Chi misura il")
print(" primo con una differenza aritmetica di rendimenti si porta dentro un termine di")
print(" VARIANZA: media(rA) - media(rB) = [log-drift] + (var(rA)-var(rB))/2. Fra due")
print(" strumenti sullo STESSO sottostante il log-drift e' il carry e la differenza di")
print(" varianza e' microstruttura (il datato e' meno scambiato, la sua barra oraria e'")
print(" piu' stantia, la varianza misurata piu' bassa).")
print("\n CONTROLLO che rende visibile l'artefatto senza discuterne: il PERPETUAL contro")
print(" l'INDICE. La sua base e' stazionaria intorno a ~3 bps, quindi il carry vero e' ~0.")
print(f" {'asset':>6} {'aritmetico':>12} {'log':>10} {'artefatto':>11} {'base perp media':>17}")
for a in ASSETS:
sa = spot_rate(a, grid[a], log=False)["i1h"]
sl = spot_rate(a, grid[a], log=True)["i1h"]
ix = aligned_index(a, grid[a])
pp = al.get(a, "1h").set_index("datetime")["close"].astype(float)
pp.index = pd.DatetimeIndex(pp.index)
bas = float(np.nanmean(pp.reindex(grid[a]).values / ix - 1.0)) * 1e4
print(f" {a:>6} {sa.mean()*24*365.25:>+11.3%} {sl.mean()*24*365.25:>+9.3%} "
f"{(sa.mean()-sl.mean())*24*365.25:>+10.3%} {bas:>16.1f}b")
print(" -> l'aritmetico attribuisce al perpetual +0,4/+0,5%/anno di 'sovra-rendimento'")
print(" su un indice da cui non si stacca mai: e' varianza, non carry. Il log da' ~0.")
print("\n Lo stesso artefatto sul DATATO, che e' l'oggetto della domanda:")
print(f" {'asset':>6} {'carry aritm.':>13} {'carry LOG':>11} {'artefatto':>11} "
f"{'funding':>9}")
for a in ASSETS:
da = dated_rate(a, grid[a], log=False)
dl = dated_rate(a, grid[a], log=True)
f = rate[a]["i1h"]
print(f" {a:>6} {da['i1h'][da['valid']].mean()*24*365.25:>+12.3%} "
f"{dl['i1h'][dl['valid']].mean()*24*365.25:>+10.3%} "
f"{(da['i1h'][da['valid']].mean()-dl['i1h'][dl['valid']].mean())*24*365.25:>+10.3%} "
f"{f.mean()*24*365.25:>+8.3%}")
print(" 🚨 L'artefatto vale 1,5-2,6 punti percentuali l'anno, cioe' DA 2 A 4 VOLTE")
print(" l'effetto che questo filone sta cercando (~0,7%/anno). Con l'estimatore")
print(" aritmetico il datato sembra costare 4,1-5,2%/anno contro un funding del")
print(" 6,4-7,3%: un vantaggio enorme, e per meta' inventato dalla formula.")
print(" 📌 REGOLA: fra due strumenti sullo stesso sottostante il carry si misura sul")
print(" LOG del rapporto dei prezzi (telescopico: cum(t) = ln(P_t/F_t) - ln(P_0/F_0)),")
print(" mai sulla media delle differenze di rendimento. E' la stessa forma dell'errore")
print(" gia' codificato il 26/07 sui veicoli UCITS («la deviazione fra due veicoli")
print(" sullo stesso indice si misura sul RAPPORTO CUMULATO»): li' erano due ETF, qui")
print(" due contratti, ed e' passato lo stesso.")
print(" Da qui in avanti: LOG. L'aritmetico non compare piu' in nessun numero.")
# ===========================================================================
# 4) IL CONFRONTO APPAIATO
# ===========================================================================
def sezione3(rate: dict, DR: dict) -> dict:
sez("3) IL CONFRONTO APPAIATO — per GIORNO e per SEGNALE, e la tesi condizionale")
_, dg = FD.tp01_realistic(ALLOC * W_TP, rate)
out = {}
print(" Segnale CONGELATO (stesso `pos` in entrambe le colonne): cambia solo lo strumento.")
print(f" {'asset':>5} {'esposiz.':>9} {'costo perp':>11} {'costo datato':>13} "
f"{'delta':>9} {'t':>6} {'IC95 (bootstrap)':>22}")
rng = np.random.default_rng(822)
for a in ASSETS:
idx = pd.DatetimeIndex(dg[a]["idx"])
pos = pd.Series(dg[a]["pos"], index=idx)
fD = pd.Series(FD.daily_funding(rate[a], idx), index=idx)
cD = pd.Series(FD.daily_funding(DR[a], idx), index=idx)
m = idx >= pd.Timestamp("2019-05-01", tz="UTC")
cp, cd = (pos * fD)[m], (pos * cD)[m]
d = (cd - cp).values
t = d.mean() / d.std() * np.sqrt(len(d))
# bootstrap a blocchi (20 giorni): il carry e' persistente, l'IID sottostima l'errore
nb = max(1, len(d) // 20)
bs = []
for _ in range(2000):
st = rng.integers(0, max(1, len(d) - 20), nb)
bs.append(np.mean(np.concatenate([d[s:s + 20] for s in st])))
lo, hi = np.percentile(bs, [2.5, 97.5]) * 365.25
out[a] = dict(perp=cp.mean() * 365.25, dat=cd.mean() * 365.25,
delta=d.mean() * 365.25, t=t, lo=lo, hi=hi, pos=pos, m=m)
print(f" {a:>5} {pos[m].mean():>9.4f} {out[a]['perp']:>10.3%} {out[a]['dat']:>12.3%} "
f"{out[a]['delta']:>+8.3%} {t:>+6.2f} [{lo:>+7.3%}, {hi:>+7.3%}]")
print("\n LA TESI: il funding e' caro PROPRIO QUANDO SIAMO LONG. Lo e' anche il basis?")
print(f" {'asset':>5} | {'funding incond':>14} {'condiz.':>9} {'rapporto':>9} "
f"| {'basis incond':>13} {'condiz.':>9} {'rapporto':>9}")
for a in ASSETS:
idx = pd.DatetimeIndex(dg[a]["idx"])
pos, m = out[a]["pos"], out[a]["m"]
bann = pd.Series(DR[a]["bann"], index=DR[a]["grid"]).resample("1D").mean().reindex(idx)
fann = pd.Series(rate[a]["i1h"], index=rate[a]["series"].index)\
.resample("1D").sum().reindex(idx) * 365.25
w = pos[m]
fu_, fc = float(fann[m].mean()), float((w * fann[m]).sum() / w.sum())
bu, bc = float(bann[m].mean()), float((w * bann[m]).sum() / w.sum())
out[a].update(f_unc=fu_, f_con=fc, b_unc=bu, b_con=bc)
print(f" {a:>5} | {fu_:>14.2%} {fc:>9.2%} {fc/fu_:>8.2f}x "
f"| {bu:>13.2%} {bc:>9.2%} {bc/bu:>8.2f}x")
print(" 📌 LA TESI REGGE, e in modo appaiato: il funding condizionale e' 1,9-2,6x")
print(" l'incondizionato (replica indipendente del 1,86-2,55x misurato stanotte), il")
print(" basis condizionale solo 1,5-1,7x. Il datato NON e' piu' economico in assoluto")
print(" — sui livelli incondizionati i due tassi sono quasi identici (6,4-7,5% contro")
print(" 6,6-7,3%, e BASIS-CALENDAR aveva gia' misurato che lo scarto contiene lo zero)")
print(" — lo diventa CONDIZIONATAMENTE, perche' si riprezza di meno contro di noi.")
print(" Il vantaggio non e' un premio: e' la parte di premio che il perpetual")
print(" ri-tariffa OGNI ORA e il datato ha gia' fissato all'ingresso.")
print("\n ⚠️ MA IL VANTAGGIO NON E' STAZIONARIO — delta per anno (>0 = il datato costa DI PIU'):")
print(f" {'anno':>6} {'delta BTC':>11} {'delta ETH':>11} {'funding medio':>15} "
f"{'basis medio':>13}")
for y in range(2019, 2027):
cells = []
for a in ASSETS:
idx = pd.DatetimeIndex(dg[a]["idx"])
pos = out[a]["pos"]
fD = pd.Series(FD.daily_funding(rate[a], idx), index=idx)
cD = pd.Series(FD.daily_funding(DR[a], idx), index=idx)
g = (pos * (cD - fD))
g = g[g.index.year == y]
cells.append(float(g.mean()) * 365.25 if len(g) > 30 else float("nan"))
fy = np.mean([rate[a]["series"][rate[a]["series"].index.year == y].mean()
for a in ASSETS]) * 24 * 365.25
by = np.nanmean([pd.Series(DR[a]["bann"], index=DR[a]["grid"])[
pd.Series(DR[a]["bann"], index=DR[a]["grid"]).index.year == y].mean()
for a in ASSETS])
print(f" {y:>6} {cells[0]:>+10.3%} {cells[1]:>+10.3%} {fy:>15.2%} {by:>13.2%}")
return out
# ===========================================================================
# 5) IL ROLL E LA LIQUIDITA'
# ===========================================================================
def sezione4(rate: dict, DR: dict, V: dict | None, grid: dict) -> dict:
sez("4) IL ROLL E LA LIQUIDITA' — il costo che il perpetual NON ha")
_, dg = FD.tp01_realistic(ALLOC * W_TP, rate)
print(" Il perpetuo non si rolla. Il datato si', e il roll e' FORZATO (data di scadenza),")
print(" non scelto. Quanto costa dipende da tre cose che si MISURANO: quante volte, su")
print(" quanto nozionale, e quanto costa attraversare il libro di un datato.\n")
print(f" {'asset':>5} {'roll/anno':>10} {'turnover TP01':>14} {'turnover SKH01':>15} "
f"{'nozion. medio TP01':>19}")
tov = {}
for a in ASSETS:
idx = pd.DatetimeIndex(dg[a]["idx"]); pos = dg[a]["pos"]
yrs = (idx[-1] - idx[0]).days / 365.25
to_tp = float(np.abs(np.diff(pos, prepend=0.0)).sum()) / yrs
rl = pd.Series(DR[a]["roll"].astype(float), index=DR[a]["grid"])\
.resample("1D").max().reindex(idx).fillna(0.0).values
FD._SKH.clear()
tr = FD.skh_daily(a, 0, "hourly", rate)[1]
yrs_s = (max(t["t_out"] for t in tr) - min(t["t_in"] for t in tr)) / 1000 / 86400 / 365.25
to_sk = 2 * len(tr) / yrs_s
tov[a] = dict(tp=to_tp, sk=to_sk, roll_year=float(rl.sum()) / yrs,
roll_turn=float((rl * np.abs(pos)).sum()) * 2 / yrs, pos=pos, idx=idx,
rl=rl, yrs=yrs)
print(f" {a:>5} {tov[a]['roll_year']:>10.2f} {to_tp:>13.2f}x {to_sk:>14.2f}x "
f"{pos.mean()*ALLOC*W_TP/2:>18,.2f}$")
print(" (turnover = somma di |Δnozionale| per anno, in multipli del capitale d'asset)")
print("\n QUANTO COSTA ATTRAVERSARE — libro reale, ordine da $100/$240/$500 ($240 = il")
print(" nozionale massimo per asset di TP01 a $635). UNA istantanea; se il libro non basta")
print(" si dice, non si estrapola:")
xs = {}
if V:
print(f" {'strumento':<22} " + " ".join(f"{f'${u}':>10}" for u in TICKETS))
for i in ("BTC_USDC-PERPETUAL", "BTC_USDC-25SEP26", "BTC_USDC",
"ETH_USDC-PERPETUAL", "ETH_USDC-25SEP26", "ETH_USDC"):
b = V["books"].get(i)
cells, vals = [], []
for u in TICKETS:
cb = walk_cost_bps(b, u, "buy"); cs = walk_cost_bps(b, u, "sell")
if cb is None or cs is None:
cells.append(f"{'LIBRO KO':>10}")
else:
v = (cb + cs) / 2
vals.append(v); cells.append(f"{v:>9.2f}b")
xs[i] = float(np.mean(vals)) if vals else None
print(f" {i:<22} " + " ".join(cells))
print(" ⚠️ Il datato USDC ha un libro 45x piu' sottile del perpetual USDC per volume")
print(" 24h, ma a $240 di ticket si resta dentro i primi livelli: a QUESTA taglia")
print(" la liquidita' non e' il vincolo, il MEZZO SPREAD si'.")
else:
print(" ⚠️ NON MISURATO (nessuna istantanea del venue).")
ov = {}
if xs:
print(f"\n SOVRACOSTO D'ESECUZIONE, per sleeve e per anno (fee reale {FEE_REALE*1e4:.1f}")
print(" bps/lato su perp e datato, 0 sullo spot — letta dal venue; mezzo spread misurato):")
print(f" {'':<24} {'BTC':>12} {'ETH':>12} {'sleeve 50/50':>14}")
for lbl, ip, sfee in (("datato vs perp", "-25SEP26", FEE_REALE),
("spot vs perp", "", 0.0)):
per = {}
for a in ASSETS:
base = xs.get(f"{a}_USDC-PERPETUAL")
alt = xs.get(f"{a}_USDC{ip}" if ip else f"{a}_USDC")
if base is None or alt is None:
per[a] = float("nan"); continue
dside = (alt * 1e-4 + sfee) - (base * 1e-4 + FEE_REALE)
extra = tov[a]["tp"] * dside
if ip: # il roll aggiunge turnover solo al datato
extra += tov[a]["roll_turn"] * (alt * 1e-4 + sfee)
per[a] = extra
ov[lbl] = per
print(f" {'TP01 ' + lbl:<24} {per['BTC']:>+11.3%} {per['ETH']:>+11.3%} "
f"{np.nanmean(list(per.values())):>+13.3%}")
per = {}
for a in ASSETS:
base, alt = xs.get(f"{a}_USDC-PERPETUAL"), xs.get(f"{a}_USDC-25SEP26")
per[a] = tov[a]["sk"] * ((alt * 1e-4 + FEE_REALE) - (base * 1e-4 + FEE_REALE)) \
if base is not None and alt is not None else float("nan")
ov["SKH datato"] = per
print(f" {'SKH01 datato vs perp':<24} {per['BTC']:>+11.3%} {per['ETH']:>+11.3%} "
f"{np.nanmean(list(per.values())):>+13.3%}")
print(" 📌 Il turnover di SKH01 e' 48-57x l'anno contro i 6,6-8,4x di TP01: lo stesso")
print(" mezzo spread costa a SKH01 ~7 volte tanto. Un cambio di strumento si")
print(" giudica per SLEEVE, non per libro.")
print("\n NETTING — oggi TP01 e SKH01 si compensano sullo STESSO strumento; separarli lo")
print(" perde. Quanto vale? (griglia giornaliera, approssimazione dichiarata)")
for a in ASSETS:
FD._SKH.clear()
tr = FD.skh_daily(a, 0, "hourly", rate)[1]
idx = tov[a]["idx"]
ps = pd.Series(0.0, index=idx)
for t in tr:
t0 = pd.Timestamp(t["t_in"], unit="ms", tz="UTC").floor("D")
t1 = pd.Timestamp(t["t_out"], unit="ms", tz="UTC").floor("D")
ps[(idx >= t0) & (idx <= t1)] += t["dir"]
ps = ps.clip(-1, 1)
nT = W_TP * pd.Series(tov[a]["pos"], index=idx); nS = W_SKH * ps
sep = float((nT.diff().abs().fillna(abs(nT.iloc[0]))
+ nS.diff().abs().fillna(abs(nS.iloc[0]))).sum()) / tov[a]["yrs"]
net = float((nT + nS).diff().abs().fillna(abs(nT.iloc[0] + nS.iloc[0])).sum()) / tov[a]["yrs"]
print(f" {a}: turnover separato {sep:.2f}x/anno · nettato {net:.2f}x/anno -> il netting "
f"vale {1-net/sep:.1%}")
print(" -> trascurabile: i due sleeve tradano quasi sempre in momenti diversi. Mettere")
print(" TP01 e SKH01 su due strumenti NON e' un argomento contro.")
return dict(tov=tov, xs=xs, ov=ov)
# ===========================================================================
# 6) LE SERIE + BANDA D'ANCORA
# ===========================================================================
def overlay(series: pd.Series, tov: dict, per_asset_bps: dict, roll: bool) -> pd.Series:
"""Sottrae il sovracosto d'esecuzione GIORNO PER GIORNO (|Δpos| e roll), non come media
annua: e' un costo di turnover, e il turnover non e' uniforme nel tempo."""
tot = None
for a in ASSETS:
if not np.isfinite(per_asset_bps.get(a, float("nan"))):
return series
pos, idx, rl = tov[a]["pos"], tov[a]["idx"], tov[a]["rl"]
c = np.abs(np.diff(pos, prepend=0.0)) * per_asset_bps[a]
if roll:
c = c + 2.0 * np.abs(pos) * rl * per_asset_bps[a]
s = pd.Series(0.5 * c, index=idx)
tot = s if tot is None else tot.add(s, fill_value=0.0)
return series.sub(tot.reindex(series.index).fillna(0.0))
def sezione5(rate: dict, DR: dict, SR: dict, ex: dict) -> dict:
sez("5) LE SERIE — il libro nelle varianti, netto tutto, e la banda d'ancora")
tov, xs = ex["tov"], ex["xs"]
ds = {a: (xs[f"{a}_USDC-25SEP26"] - xs[f"{a}_USDC-PERPETUAL"]) * 1e-4
for a in ASSETS} if xs else {a: float("nan") for a in ASSETS}
ss = {a: (xs[f"{a}_USDC"] * 1e-4 + 0.0) - (xs[f"{a}_USDC-PERPETUAL"] * 1e-4 + FEE_REALE)
for a in ASSETS} if xs else {a: float("nan") for a in ASSETS}
tp = {k: FD.tp01_realistic(ALLOC * W_TP, r)[0] for k, r in
(("nessun carry", None), ("perp", rate), ("datato", DR), ("spot", SR))}
tp["datato+esec"] = overlay(tp["datato"], tov, ds, roll=True)
tp["spot+esec"] = overlay(tp["spot"], tov, ss, roll=False)
var = {}
var["A) perp (IL LIBRO DI OGGI)"] = book(rate)
var["B) tutto sui datati"] = book(DR)
var["C) TP01 datato + SKH01 perp"] = book(rate, tp_series=tp["datato"])
var["D) C + sovracosto d'esecuzione"] = book(rate, tp_series=tp["datato+esec"])
var["E) TP01 spot + SKH01 perp"] = book(rate, tp_series=tp["spot+esec"])
var["F) nessun carry (irreale)"] = book(None)
print(f" {'variante':<32} {'ShFULL':>8} {'ShHOLD':>8} {'maxDD':>7} {'vol':>7} "
f"{'CAGR':>8} {'drift':>8} {'Δdrift':>8}")
base = FD.stat(var["A) perp (IL LIBRO DI OGGI)"])
st = {}
for k, s in var.items():
m = FD.stat(s); st[k] = m
print(f" {k:<32} {m['full']:>8.3f} {m['hold']:>8.3f} {m['dd']:>7.1%} {m['vol']:>7.2%} "
f"{m['cagr']:>8.2%} {m['drift']:>8.2%} {m['drift']-base['drift']:>+8.2%}")
print("\n 📌 QUELLO CHE LA COLONNA `Δdrift` DA SOLA NASCONDE: il datato aggiunge drift E")
print(" VOLATILITA'. Il basis non e' costante — si riprezza — e quella oscillazione")
print(" entra nel rendimento della posizione. Il funding invece e' un flusso di cassa")
print(" che NON aggiunge varianza al prezzo. Per questo lo Sharpe del libro PEGGIORA")
print(" mentre il drift migliora. Chi cita solo il drift sta vendendo un cambio di")
print(" strumento come un miglioramento quando e' uno SCAMBIO.")
print("\n SLEEVE TP01 da solo (dove il cambio ha senso), stessa lente:")
print(f" {'':<26} {'ShFULL':>8} {'ShHOLD':>8} {'maxDD':>7} {'vol':>7} {'drift':>8}")
for k in ("nessun carry", "perp", "datato", "datato+esec", "spot", "spot+esec"):
m = FD.stat(tp[k])
print(f" {k:<26} {m['full']:>8.3f} {m['hold']:>8.3f} {m['dd']:>7.1%} {m['vol']:>7.2%} "
f"{m['drift']:>8.2%}")
print("\n FINESTRE — il numero va citato con la finestra (regola di stanotte):")
print(f" {'variante':<32} {'FULL':>9} {'>=2022-03':>11} {'2025+ (hold-out)':>18}")
lo1, lo2 = pd.Timestamp("2022-03-09", tz="UTC"), pd.Timestamp("2025-01-01", tz="UTC")
A = var["A) perp (IL LIBRO DI OGGI)"]
for k in ("B) tutto sui datati", "C) TP01 datato + SKH01 perp",
"D) C + sovracosto d'esecuzione", "E) TP01 spot + SKH01 perp"):
B = var[k]
cells = []
for lo in (None, lo1, lo2):
a_, b_ = (A, B) if lo is None else (A[A.index >= lo], B[B.index >= lo])
cells.append((float(b_.mean()) - float(a_.mean())) * 365.25)
print(f" {k:<32} {cells[0]:>+8.2%} {cells[1]:>+10.2%} {cells[2]:>+17.2%}")
print(f"\n{clock()} BANDA D'ANCORA — 23 offset della griglia 230m/690m di SKH01, mediana")
print(" delle DIFFERENZE APPAIATE (mai la differenza delle mediane).")
bands = {}
for k, tps, rr in (("C) TP01 datato", tp["datato"], rate),
("D) C + esecuzione", tp["datato+esec"], rate),
("E) TP01 spot", tp["spot+esec"], rate)):
d = {"sh": [], "hold": [], "dd": [], "drift": []}
for off in r02.OFFSETS:
a_ = FD.stat(book(rate, off=off))
b_ = FD.stat(book(rr, off=off, tp_series=tps))
d["sh"].append(b_["full"] - a_["full"]); d["hold"].append(b_["hold"] - a_["hold"])
d["dd"].append(b_["dd"] - a_["dd"]); d["drift"].append(b_["drift"] - a_["drift"])
bands[k] = {m: np.array(v) for m, v in d.items()}
print(f" {k}:")
for m, lbl in (("drift", "Δdrift"), ("sh", "ΔShFULL"), ("hold", "ΔShHOLD"),
("dd", "ΔmaxDD")):
v = bands[k][m]
flat = (v.max() - v.min()) < 1e-6
print(f" {lbl:<9} mediana appaiata {np.median(v):>+8.4f} positiva in "
f"{np.mean(v>0):>5.0%} dei 23 offset [min {v.min():+.4f}, max {v.max():+.4f}]"
f"{' <- COSTANTE: nessuna potenza, vedi sotto' if flat else ''}")
print(" ⚠️ Il Δdrift e' COSTANTE su tutti e 23 gli offset, e non e' robustezza: e'")
print(" un'identita'. Cambia solo la gamba TP01, che non dipende dall'offset di SKH01,")
print(" e il drift di una somma pesata e' la somma pesata dei drift -> «positivo a")
print(" 23/23» qui NON dice niente. La banda ha potenza sullo SHARPE e sul maxDD (che")
print(" dipendono dalla covarianza fra le gambe), e li' il verdetto e' netto:")
print(" l'hold-out del datato e' NEGATIVO a 0/23 offset, con e senza sovracosto.")
print(" (Un controllo che non puo' fallire e' esattamente cio' che questo progetto")
print(" chiama «un test senza potenza»: si dichiara, non si conta come conferma.)")
return dict(var=var, st=st, tp=tp, bands=bands, ds=ds, ss=ss)
# ===========================================================================
# 7) I MURI
# ===========================================================================
def sezione6(res5: dict) -> None:
sez("6) I MURI DI CAPITALE — la moneta giusta, perche' la rendita vive sul DRIFT")
need = CC.TARGET_EUR_DAY * 365.0 * CC.EURUSD / (1 - CC.TAX_RATE)
print(f" bersaglio 50 EUR/g netti = ${need:,.0f}/anno lordi (aliquota {CC.TAX_RATE:.0%}),")
print(f" de-luck del drift x{DELUCK} (MISURATO), MC {WR.N_PATHS} path -> risoluzione ~1%:")
print(" si citano 3 cifre significative, non il dollaro.\n")
print(f" {'variante':<32} {'drift':>8} {'vol':>7} {'perpetua':>9} {'muro':>12} {'Δmuro':>8}")
ref = None
for k in ("A) perp (IL LIBRO DI OGGI)", "D) C + sovracosto d'esecuzione",
"E) TP01 spot + SKH01 perp", "F) nessun carry (irreale)"):
r = res5["var"][k].values.astype(float)
r = r - (1.0 - DELUCK) * float(r.mean())
_, perp, cap = WR.perp_and_wall(r, 1.0, need)
if ref is None:
ref = cap
print(f" {k:<32} {float(r.mean())*365.25:>8.2%} {float(r.std())*np.sqrt(365.25):>7.2%} "
f"{perp:>9.2%} ${cap:>11,.0f} {cap/ref-1:>+7.1%}")
print("\n 📌 Il muro pubblicato stanotte e' $325,0k col funding dentro (era $276,6k senza).")
print(" Qui si misura il DELTA con la stessa macchineria, non un muro nuovo: la")
print(" differenza di livello viene dal de-luck e dal MC, non dal cambio di strumento.")
# ===========================================================================
# 8) IL MURO CHE NON C'E'
# ===========================================================================
def sezione7(V: dict | None) -> None:
sez("7) IL MURO CHE NON C'E' — «tutti i datati Deribit sono INVERSE» e' FALSO")
print(" La sezione 34 di RESULTS-0822 chiude cosi': «tutti i futures datati Deribit sono")
print(" INVERSE: non esiste una linea USDC datata — stesso muro delle opzioni, e il")
print(" collaterale in BTC e' esso stesso esposizione direzionale. Non risolto.»")
print(" Il briefing di questo filone lo eredita come «probabilmente il muro che uccide il")
print(" filone». E' l'unica cosa che si poteva verificare con UNA chiamata pubblica.\n")
if not V:
print(" ⚠️ NON VERIFICATO in questa corsa (nessuna rete, nessuna cache).")
return
ins = V["instruments"]
usdc_dated = sorted(n for n, s in ins.items()
if s.get("settlement") == "USDC" and s.get("kind") == "future"
and "PERPETUAL" not in n and (n.startswith("BTC_USDC")
or n.startswith("ETH_USDC")))
print(f" MISURATO: {len(usdc_dated)} futures DATATI con settlement **USDC** su BTC/ETH.")
for n in usdc_dated:
s = ins[n]
cre = dt.datetime.utcfromtimestamp(s["created"] / 1000).date()
exp = dt.datetime.utcfromtimestamp(s["expiry"] / 1000).date()
print(f" {n:<20} creato {cre} scade {exp} min {s['min_trade_amount']} "
f"tick {s['tick']} taker {s['taker']}")
q = [n for n in usdc_dated if n.endswith(("SEP26", "DEC26", "MAR27", "JUN27"))]
print(f"\n ✅ Il muro NON esiste: le due gambe del libro possono restare in USDC. I")
print(" trimestrali USDC hanno lo STESSO min_trade_amount del perpetual lineare")
print(" (0,0001 BTC ~ $7,7 · 0,001 ETH ~ $2,4) e la STESSA fee taker: a $635")
print(" l'eseguibilita' non e' il vincolo (6a volta in questa ondata).")
print(" ⚠️ E il collaterale in cripto non era nemmeno l'argomento giusto: se lo fosse")
print(" stato, il costo non sarebbe il carry ma il fatto che un conto denominato in")
print(" BTC e' long 1,0x anche da FLAT — e TP01 sta flat nel 44% dei giorni, cioe' si")
print(" sarebbe distrutto esattamente cio' per cui TP01 esiste (il taglio del DD).")
print("\n ⚠️ IL MURO VERO E' UN ALTRO, ed e' una DATA: la linea USDC datata e' nata il")
print(f" 2026-05-07 ({len(q)} trimestrali esistono, il secondo creato il 2026-08-06)")
print(" -> ~3,5 mesi di vita e ZERO roll trimestrali mai eseguiti su quella linea.")
print(" Tutta la misura di questo filone gira sui trimestrali INVERSE, che hanno 7,3")
print(" anni: e' un PROXY, esattamente come l'inverse lo era per il funding pre-2022.")
print(" ✅ E il proxy si VALIDA invece di assumerlo — i due contratti sulla stessa")
print(" scadenza devono prezzare lo stesso carry, e lo fanno:")
for a in ASSETS:
bi, bu = V["books"].get(f"{a}-25SEP26"), V["books"].get(f"{a}_USDC-25SEP26")
if not bi or not bu or not bi.get("index"):
continue
mi = (bi["bid"] + bi["ask"]) / 2; mu = (bu["bid"] + bu["ask"]) / 2
print(f" {a}-25SEP26 (inverse) base {(mi/bi['index']-1)*1e4:+.1f} bps vs "
f"{a}_USDC-25SEP26 (lineare) base {(mu/bu['index']-1)*1e4:+.1f} bps "
f"-> scarto {(mu/bu['index']-mi/bi['index'])*1e4:+.1f} bps")
print(" 📌 REGOLA: un muro ereditato da un'altra sessione e' un'IPOTESI, e prima di")
print(" costruirci sopra un verdetto si ri-verifica. Qui e' costato una GET pubblica,")
print(" e la frase era diventata falsa 3 mesi prima che venisse scritta.")
print(" (Stessa forma della lezione del 26/07: «un blocco dichiarato e' un'ipotesi».)")
# ===========================================================================
# 9) SOTTOPRODOTTO — LO SPOT
# ===========================================================================
def sezione8(V: dict | None, res5: dict, rate: dict, SR: dict) -> None:
sez("8) SOTTOPRODOTTO — lo strumento a carry ZERO esiste, si chiama SPOT")
print(" La domanda del filone era «datato o perpetual». Cercando le specifiche e' saltato")
print(" fuori il terzo: Deribit quota SPOT BTC_USDC/ETH_USDC. Uno spot non paga funding,")
print(" non ha basis e non si rolla: il suo carry e' ZERO per costruzione.")
if V:
for i in ("BTC_USDC", "ETH_USDC"):
s = V["instruments"].get(i, {})
b = V["books"].get(i, {})
sp = (b["ask"] - b["bid"]) / ((b["ask"] + b["bid"]) / 2) * 1e4 if b.get("bid") else float("nan")
print(f" {i:<10} min {s.get('min_trade_amount')} taker {s.get('taker')} "
f"maker {s.get('maker')} spread {sp:.2f} bps vol 24h "
f"${(b.get('vol24usd') or float('nan')):,.0f}")
print(" ⚠️ La fee ZERO e' quella di OGGI ed e' dichiarata temporanea nell'annuncio del")
print(" 1° agosto («spot a zero fino al collegamento con Coinbase»): e' un tier che")
print(" puo' sparire, e `fee_watch` sorveglia i perpetual, non lo spot.")
print("\n Perche' funziona SOLO su TP01, e la misura che lo dimostra:")
_, dg = FD.tp01_realistic(ALLOC * W_TP, rate)
for a in ASSETS:
pos = dg[a]["pos"]
print(f" {a}: TP01 e' long-flat e il suo target per asset supera 1,0x nel "
f"{np.mean(pos > 1.0):.3%} dei giorni (massimo {pos.max():.3f}) -> il tetto")
print(f" strutturale dello spot (niente leva, niente short) e' quasi mai binding.")
print(" SKH01 e' long/SHORT a nozionale 1,0x: sullo spot non e' esprimibile. Meta' dei")
print(" suoi trade sono short e li' lo spot non esiste come strumento.")
print("\n DECOMPOSIZIONE del vantaggio dello spot su TP01 — due pezzi, non uno:")
mnc = FD.stat(res5["tp"]["nessun carry"]); msp = FD.stat(res5["tp"]["spot"])
mpe = FD.stat(res5["tp"]["perp"])
print(f" (1) funding evitato {mnc['drift']-mpe['drift']:>+7.2%}/anno "
f"(perp {mpe['drift']:.2%} -> senza-carry {mnc['drift']:.2%})")
print(f" (2) base del perpetual {msp['drift']-mnc['drift']:>+7.2%}/anno "
f"(senza-carry {mnc['drift']:.2%} -> indice {msp['drift']:.2%})")
print(" ❌ IPOTESI MIA, NATA E REFUTATA NELLA STESSA SESSIONE: mi aspettavo che il")
print(" secondo pezzo fosse una TERZA conferma della tesi condizionale — «quando siamo")
print(" long il perpetual e' a premio e quel premio decade». La prima stesura lo")
print(" misurava a +0,32%/anno e ci avrei creduto: era il MISALLINEAMENTO di un'ora")
print(" fra indice e barre (vedi [ALLINEAMENTO] in testa). Allineato, il pezzo vale")
print(" +0,07%/anno: il VERSO e' quello della tesi e l'IC95 esclude lo zero per un")
print(" soffio su entrambi gli asset, ma la TAGLIA e' il 3% del pezzo (1). Un verso")
print(" giusto a un trentesimo della grandezza non e' una conferma, e' un residuo:")
print(" tutto il vantaggio dello spot e' il funding evitato, e va detto cosi'.")
print(" 📌 E il modo in cui l'artefatto si e' fatto vedere e' la cosa da ricordare: non")
print(" dal numero, che era plausibile e nel verso atteso, ma dalla VOLATILITA' — la")
print(" variante spot mostrava +1,07pp di vol su una strategia identica, e uno strumento")
print(" che traccia lo stesso sottostante non puo' aggiungere un punto di vol.")
rngz = np.random.default_rng(823)
for a in ASSETS:
idx = pd.DatetimeIndex(dg[a]["idx"]); pos = pd.Series(dg[a]["pos"], index=idx)
c = pd.Series(FD.daily_funding(SR[a], idx), index=idx)
d = (pos * c).values
nb = max(1, len(d) // 20)
bs = [np.mean(np.concatenate([d[x:x+20] for x in
rngz.integers(0, max(1, len(d)-20), nb)]))
for _ in range(1000)]
lo, hi = np.percentile(bs, [2.5, 97.5]) * 365.25
print(f" {a}: base del perpetual pagata da TP01 {d.mean()*365.25:+.3%}/anno "
f"IC95 a blocchi [{lo:+.3%}, {hi:+.3%}]")
m0 = FD.stat(res5["tp"]["perp"]); m1 = FD.stat(res5["tp"]["spot+esec"])
print(f"\n TP01: drift {m0['drift']:.2%} -> {m1['drift']:.2%} ({m1['drift']-m0['drift']:+.2%}/anno), "
f"Sharpe {m0['full']:.3f} -> {m1['full']:.3f}, maxDD {m0['dd']:.1%} -> {m1['dd']:.1%}")
print(" 📌 E' l'unica variante che alza il drift SENZA alzare la volatilita' (vol di TP01")
print(" 12,16% sul perpetual e 12,16% sullo spot, alla terza cifra): lo spot non ha una")
print(" base che si riprezza. Il datato invece scambia carry contro varianza, ed e' per")
print(" questo che il suo Sharpe non migliora mentre il suo drift si'.")
print("\n ⚠️ MA C'E' UNA DOMANDA APERTA CHE VALE PIU' DEL RISPARMIO, e non e' risolvibile")
print(" con un backtest. CLAUDE.md la registra gia' (07/08): i DERIVATI su cripto")
print(" potrebbero qualificarsi `c-quater` (26%) invece di `c-sexies` (33%). Se e'")
print(" cosi', spostare TP01 dal perpetual (derivato) allo SPOT (cripto-attivita')")
print(" sposta il suo P&L nel comparto al 33%.")
cagr = FD.stat(res5["var"]["A) perp (IL LIBRO DI OGGI)"])["cagr"]
print(f" Ordine di grandezza: 7 punti di aliquota su un CAGR di libro del {cagr:.1%} "
f"valgono ~{0.07*cagr:.2%}/anno,")
print(" cioe' PIU' del risparmio di carry che lo spot porta. La risposta e' del")
print(" commercialista, non del backtest — ed e' esattamente la stessa domanda gia'")
print(" aperta nel progetto, che ora ha un secondo motivo per essere posta.")
if V:
xs = {}
for i in (f"{a}_USDC" for a in ASSETS):
xs[i] = np.mean([x for x in (walk_cost_bps(V["books"].get(i), u, sd)
for u in TICKETS for sd in ("buy", "sell"))
if x is not None]) if V["books"].get(i) else float("nan")
for i in (f"{a}_USDC-PERPETUAL" for a in ASSETS):
xs[i] = np.mean([x for x in (walk_cost_bps(V["books"].get(i), u, sd)
for u in TICKETS for sd in ("buy", "sell"))
if x is not None]) if V["books"].get(i) else float("nan")
_, dgz = FD.tp01_realistic(ALLOC * W_TP, rate)
now, later = [], []
for a in ASSETS:
to = float(np.abs(np.diff(dgz[a]["pos"], prepend=0.0)).sum()) / \
((pd.DatetimeIndex(dgz[a]["idx"])[-1]
- pd.DatetimeIndex(dgz[a]["idx"])[0]).days / 365.25)
base = xs[f"{a}_USDC-PERPETUAL"] * 1e-4 + FEE_REALE
now.append(to * (xs[f"{a}_USDC"] * 1e-4 + 0.0 - base))
later.append(to * (xs[f"{a}_USDC"] * 1e-4 + FEE_REALE - base))
print(f"\n ⚠️ E se la fee spot smettesse di essere zero: il sovracosto d'esecuzione di")
print(f" TP01 passa da {np.mean(now):+.3%}/anno a {np.mean(later):+.3%}/anno, cioe' "
f"mangia il {(np.mean(later)-np.mean(now))/0.0207:.0%} del vantaggio")
print(f" (che a livello di sleeve vale ~2,07%/anno). Sopravviverebbe, ma va")
print(f" sorvegliato: `fee_watch` oggi guarda i perpetual, non lo spot.")
print(" ⚠️ Secondo limite dichiarato: qui lo spot e' modellato con l'INDICE Deribit, non")
print(" col libro spot vero (che non ha storia in questo progetto). Sul carry e'")
print(" corretto per costruzione (l'indice E' il sottostante); sull'esecuzione no, e")
print(" infatti il mezzo spread e' preso dall'istantanea del libro vero.")
# ===========================================================================
# 10) GATE
# ===========================================================================
def sezione9(rate: dict, DR: dict, res5: dict, grid: dict) -> None:
sez("9) GATE — esito di ciascuno; quelli non girati sono DICHIARATI, non omessi")
# causalita': controllo POSITIVO (un test che non puo' fallire non e' un test)
a = "BTC"
_, dg = FD.tp01_realistic(ALLOC * W_TP, rate)
idx = pd.DatetimeIndex(dg[a]["idx"]); pos = pd.Series(dg[a]["pos"], index=idx)
c = pd.Series(FD.daily_funding(DR[a], idx), index=idx)
base = float((pos * c).mean()) * 365.25
ahead = float((pos * c.shift(-1).fillna(0.0)).mean()) * 365.25
lag = float((pos * c.shift(1).fillna(0.0)).mean()) * 365.25
print(f" causality : il carry dell'ora t si applica alla posizione tenuta in t,")
print(f" e il contratto e' scelto alla chiusura di t-1. Controllo")
print(f" POSITIVO: usando il carry di DOMANI {ahead:.3%} e di IERI")
print(f" {lag:.3%} contro {base:.3%} -> il test ha POTENZA "
f"({abs(ahead-base)*1e4:.0f}/{abs(lag-base)*1e4:.0f} bps di scarto).")
# sensibilita' al parametro d'esecuzione (griglia dichiarata)
print(f" griglia del roll : BUF in {BUF_GRID} giorni (parametro d'ESECUZIONE, e un")
print(" parametro d'esecuzione scelto guardando il risultato e'")
print(" selezione come ogni altra — regola 27/07). Canonico BUF=5")
print(" dichiarato PRIMA. Δdrift di libro per cella:")
for b in BUF_GRID:
DRb = {x: dated_rate(x, grid[x], buf=b) for x in ASSETS}
tpb = FD.tp01_realistic(ALLOC * W_TP, DRb)[0]
d = (float(book(rate, tp_series=tpb).mean())
- float(res5["var"]["A) perp (IL LIBRO DI OGGI)"].mean())) * 365.25
print(f" BUF={b:>2}g -> Δdrift {d:>+7.3%}"
f"{' <- canonico' if b == BUF_CANON else ''}")
print(" -> piatto: la scelta non e' dove vive il risultato.")
# de-levering null
A = res5["var"]["A) perp (IL LIBRO DI OGGI)"]
D = res5["var"]["D) C + sovracosto d'esecuzione"]
sa, sd = FD.stat(A), FD.stat(D)
k = sa["vol"] / sd["vol"]
rk = D.values.astype(float) * k
shk = float(np.mean(rk) / np.std(rk) * np.sqrt(365.25))
print(f" null del de-levering : NON APPLICABILE nella forma consueta (nessun claim di")
print(f" «meno drawdown»: il maxDD si muove di {sd['dd']-sa['dd']:+.2%}).")
print(f" Girato il suo SPECULARE — a ISO-VOLATILITA' il candidato")
print(f" deve restare migliore: k={k:.3f} -> Sharpe {shk:.3f} contro")
print(f" {sa['full']:.3f} del baseline -> "
f"{'PASSA' if shk > sa['full'] else 'FALLISCE'} (il datato compra drift PAGANDOLO in")
print(" varianza, ed e' esattamente quello che il gate deve vedere).")
E = res5["var"]["E) TP01 spot + SKH01 perp"]
se = FD.stat(E)
ke = sa["vol"] / se["vol"]
rke = E.values.astype(float) * ke
she = float(np.mean(rke) / np.std(rke) * np.sqrt(365.25))
print(f" Stesso gate sulla variante SPOT: k={ke:.3f} -> Sharpe "
f"{she:.3f} contro {sa['full']:.3f} -> "
f"{'PASSA' if she > sa['full'] else 'FALLISCE'} (lo spot alza il drift senza")
print(" comprare varianza: e' l'unica delle tre che sopravvive).")
ns = int(sum(len(FD.skh_daily(x, 0, "hourly", rate)[1]) for x in ASSETS))
nl = int(sum(np.sum(np.array([t["gross"] for t in FD.skh_daily(x, 0, "hourly", rate)[1]]) <= 0)
for x in ASSETS))
ip = al.implausible_sharpe(D, n_trades=ns, n_losing_trades=nl)
print(f" implausible_sharpe : implausible={ip['implausible']} (Sharpe {ip['sharpe']:.2f}, "
f"maxDD {ip['maxdd']:.1%}, Calmar {ip['calmar']:.2f}) -> {ip['reasons'] or 'nessuna firma'}")
print(f" anchor_luck (banda) : GIRATO, 23 offset, mediana delle differenze APPAIATE "
f"(sez. 5).")
print(" replica bit-exact : PASSATA — il -2,16%/anno del funding e' riprodotto in sez. 1")
print(" e l'assert ferma lo script se non torna.")
print(" marginal_vs_tp01 / deflated_sharpe / study_family_honest / weights_tilt_null:")
print(" NON GIRATI, e non per budget. Qui non c'e' un CANDIDATO:")
print(" il segnale e' congelato, i pesi non si toccano, non c'e'")
print(" una famiglia da cui selezionare. C'e' un COSTO che si")
print(" sposta cambiando strumento. Un `marginal_vs_tp01` su TP01")
print(" eseguito diversamente misurerebbe TP01 contro se stesso.")
def main() -> None:
print("=" * 100)
print("r0822e — FUNDING-AVOID: il 2,16%/anno si evita cambiando strumento?")
print(f"branch research/wave-0822 · {pd.Timestamp.now(tz='UTC')} · rete={'SI' if _NET else 'NO'}")
print("=" * 100)
print(" IPOTESI A PRIORI, registrata prima di misurare: il basis di un datato e il funding")
print(" di un perpetual sono lo STESSO premio (arbitraggio), quindi in livello devono")
print(" costare uguale; se il datato vince e' solo perche' il funding si riprezza contro")
print(" chi e' long e il basis no. Attesa: differenza piccola, forse nulla, e comunque")
print(" mangiata dal roll o dall'ineseguibilita'.")
V = venue_snapshot()
d0 = sezione0(V)
F = {}
for instr, (start, asset, kind) in FD.INSTR.items():
d = FD.fetch_funding(instr, start).copy()
d["dt"] = pd.to_datetime(d["ts"], unit="ms", utc=True)
F[instr] = d.drop_duplicates(subset="ts").sort_values("dt").set_index("dt")
rate = FD.build_rate(F)
grid = {a: pd.DatetimeIndex(rate[a]["series"].index) for a in ASSETS}
verifica_allineamento(grid)
print(f"\n{clock()} sezione 1...")
sezione1(rate)
DR = {a: dated_rate(a, grid[a], buf=BUF_CANON, log=True) for a in ASSETS}
SR = {a: spot_rate(a, grid[a], log=True) for a in ASSETS}
print(f"\n copertura del front datato sulla griglia del funding: " + " · ".join(
f"{a} {DR[a]['valid'].mean():.4f}" for a in ASSETS))
print(f"\n{clock()} sezione 2...")
sezione2(rate, grid)
print(f"\n{clock()} sezione 3...")
sezione3(rate, DR)
print(f"\n{clock()} sezione 4...")
ex = sezione4(rate, DR, V, grid)
print(f"\n{clock()} sezione 5...")
res5 = sezione5(rate, DR, SR, ex)
print(f"\n{clock()} sezione 6...")
sezione6(res5)
sezione7(V)
sezione8(V, res5, rate, SR)
print(f"\n{clock()} sezione 9...")
sezione9(rate, DR, res5, grid)
sez("SINTESI")
A = FD.stat(res5["var"]["A) perp (IL LIBRO DI OGGI)"])
for k in ("B) tutto sui datati", "C) TP01 datato + SKH01 perp",
"D) C + sovracosto d'esecuzione", "E) TP01 spot + SKH01 perp"):
m = res5["st"][k]
print(f" {k:<32} Δdrift {m['drift']-A['drift']:>+7.2%} ΔShFULL "
f"{m['full']-A['full']:>+7.3f} ΔShHOLD {m['hold']-A['hold']:>+7.3f} "
f"ΔmaxDD {m['dd']-A['dd']:>+6.2%}")
c_perp = res5["st"]["F) nessun carry (irreale)"]["drift"] - A["drift"]
print(f"\n costo del carry per il libro, sul perpetual: {c_perp:.2%}/anno "
f"(il -2,16% replicato)")
dD = res5["st"]["D) C + sovracosto d'esecuzione"]["drift"] - A["drift"]
tot = c_perp - dD
print(f" ... e su TP01-datato/SKH01-perp, tutto compreso (carry + mezzo spread + roll):"
f" {tot:.2%}/anno")
mD = res5["st"]["D) C + sovracosto d'esecuzione"]
print(f" risparmio netto: {dD:+.2%}/anno di drift, pagato con "
f"{mD['full']-A['full']:+.3f} di Sharpe FULL e {mD['hold']-A['hold']:+.3f} di hold-out.")
D_ = res5["st"]["D) C + sovracosto d'esecuzione"]
E_ = res5["st"]["E) TP01 spot + SKH01 perp"]
costo_dat = (c_perp - dD) * 100
sez("VERDETTO")
print(" IPOTESI: esprimere il segnale congelato su un future DATATO invece che sul")
print(" perpetual costa meno del funding, perche' il basis e' fissato all'ingresso")
print(" mentre il funding si riprezza contro chi e' long. Attesa PRIMA di misurare:")
print(" differenza piccola o nulla, e comunque mangiata dal roll o dall'ineseguibilita'.")
print(" GRIGLIA DICHIARATA (al RIALZO): 2 estimatori (aritmetico/log) x 4 buffer di roll")
print(" (2/5/10/20 g) x 3 strumenti (perp/datato/spot) x 2 sleeve = 48 valutazioni di")
print(" carry; 6 varianti di libro x 23 offset d'ancora = 138 valutazioni di libro;")
print(" 4 muri x 26 passi di bisezione x 2.000 path. NESSUNA e' una SELEZIONE: il")
print(" segnale e i pesi sono congelati, non c'e' una cella scelta guardando il")
print(" risultato -> nessun deflated-Sharpe da calcolare.")
print(f" NUMERI (netti fee, a $600 di allocazione, lente `hourly` del libro live):")
print(f" A) perpetual, oggi Sh {A['full']:.3f} / hold {A['hold']:.3f} / "
f"maxDD {A['dd']:.1%} / CAGR {A['cagr']:.2%} / drift {A['drift']:.2%}")
print(f" D) TP01 datato, tutto dentro Sh {D_['full']:.3f} / hold {D_['hold']:.3f} / "
f"maxDD {D_['dd']:.1%} / CAGR {D_['cagr']:.2%} / drift {D_['drift']:.2%}")
print(f" E) TP01 spot, tutto dentro Sh {E_['full']:.3f} / hold {E_['hold']:.3f} / "
f"maxDD {E_['dd']:.1%} / CAGR {E_['cagr']:.2%} / drift {E_['drift']:.2%}")
print(f" haircut a $600: gia' dentro (le serie girano a $600 con min-order $5); il")
print(f" ticket massimo per asset e' $240 e resta nei primi livelli del libro datato.")
print(" VERDETTO: **SCARTATO** per i datati (il risparmio di drift si paga in varianza:")
print(f" hold-out {D_['hold']-A['hold']:+.3f}, negativo a 0/23 offset, e il gate")
print(" iso-volatilita' fallisce) — **LEAD** per lo SPOT su TP01, con la domanda")
print(" fiscale aperta che vale piu' del risparmio.")
print(" COSA MI SMENTIREBBE: (a) sui datati — un campione in cui il basis condizionale")
print(" NON si riprezza meno del funding, cioe' un regime in cui l'hold-out smette di")
print(" essere negativo: qui il vantaggio e' tutto 2019-2023 e vale ~0 dal 2024;")
print(" (b) sullo spot — che il fisco tratti i derivati come le cripto-attivita' (33%")
print(" entrambi): allora il LEAD diventa un candidato d'esecuzione; oppure che la fee")
print(" spot 0 sparisca col collegamento Coinbase (misurato in sez. 8: il vantaggio")
print(" si riduce ma non si annulla);")
print(" (c) su entrambi — che il mio proxy inverse per il carry dei datati non valga:")
print(" si smentisce raccogliendo qualche mese di quote della linea USDC datata e")
print(" confrontando i basis, oggi a 0,4-0,9 bps l'uno dall'altro.")
print(" SCRIPT: scripts/research/r0822e_funding_avoid.py")
print()
print(f"ESPRIMERE IL LIBRO SUI DATATI: COSTA {costo_dat:.2f} %/ANNO CONTRO 2,16% DEL PERP "
f"-> NON CONVIENE, perche' i {dD*100:.2f} punti risparmiati sul drift si pagano con "
f"{D_['hold']-A['hold']:+.3f} di Sharpe hold-out (negativo a 0/23 offset d'ancora) e "
f"il gate iso-volatilita' fallisce; NON e' INESEGUIBILE (il muro «tutti i datati "
f"sono INVERSE» e' FALSO: 16 datati USDC esistono da 2026-05-07, min-order e fee "
f"identici al perpetual) ma la linea USDC ha 3,5 mesi di vita e zero roll mai "
f"eseguiti.")
print(f"\n{clock()} fine.")
if __name__ == "__main__":
main()