8f9ce89039
fetch_dvol.py: storia DVOL (IV Deribit) BTC/ETH 2021-2026 -> data/raw/dvol_*. options_vrp_lab.py: backtest CSP settimanale, premio BS su DVOL reale + calibrazione f (skew/spread), payoff sul path realizzato, causale; gauntlet (VRP, sweep f/delta, per-anno, worst-weeks, corr+contributo vs TP01). Esiti (book 50/50 put delta-0.28): VRP reale (BTC IV>RV 78% del tempo). Sharpe DIPENDE da f: 0.71 conservativo (IV-ATM) -> 1.70 a f=1.29 (skew reale calm). CODA severa (DD 30-33%, settimane -15..-26% su LUNA/FTX/crash; 2022 -9%, 2026-YTD -14%). Scorrelato a TP01 (+0.07) -> migliora il portafoglio anche a premio conservativo (TP01 70%+OPT 30%: Sh settimanale 0.71->0.97). VERDETTO: lead reale e diversificante, MA premio modellato (non catena reale) + calibrazione ottimistica + coda short-vol non catturata nello stress. Regola: mai short-vol da modello in deploy. NON aggiunto. Portafoglio invariato TP01 70% + XS01 30%. Prossimo: accumulo quote reali multi-regime + stress crash + daily-MTM + paper testnet. Diario 2026-06-19-options-vrp-lab.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
59 lines
2.1 KiB
Python
59 lines
2.1 KiB
Python
"""FETCH storia DVOL (Deribit Volatility Index) — input IV per lo sleeve opzioni VRP.
|
|
|
|
DVOL = vol implicita 30d annualizzata di Deribit (l'IV "ATM" del mercato). Public API, no auth.
|
|
Limite 1000 punti/richiesta -> paginazione all'indietro. Salva data/raw/dvol_<asset>.parquet
|
|
(colonne: timestamp ms, close = DVOL%). Usato come IV per prezzare BS le opzioni nel backtest VRP;
|
|
la RV viene dai nostri prezzi certificati. VRP = IV - RV.
|
|
|
|
uv run python scripts/research/fetch_dvol.py
|
|
"""
|
|
from __future__ import annotations
|
|
import sys, time
|
|
from pathlib import Path
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
import requests, pandas as pd
|
|
|
|
URL = "https://www.deribit.com/api/v2/public/get_volatility_index_data"
|
|
RAW = PROJECT_ROOT / "data" / "raw"
|
|
|
|
|
|
def fetch(cur, res=86400):
|
|
end = int(time.time() * 1000)
|
|
floor = int(pd.Timestamp("2020-06-01", tz="UTC").timestamp() * 1000)
|
|
rows = {}
|
|
guard = 0
|
|
while end > floor and guard < 60:
|
|
guard += 1
|
|
r = requests.get(URL, params={"currency": cur, "start_timestamp": floor,
|
|
"end_timestamp": end, "resolution": res}, timeout=40)
|
|
data = r.json().get("result", {}).get("data", [])
|
|
if not data:
|
|
break
|
|
for ts, o, h, l, c in data:
|
|
rows[int(ts)] = float(c)
|
|
earliest = min(int(x[0]) for x in data)
|
|
if earliest >= end:
|
|
break
|
|
end = earliest - 1
|
|
if not rows:
|
|
return pd.DataFrame()
|
|
df = pd.DataFrame(sorted(rows.items()), columns=["timestamp", "close"])
|
|
return df
|
|
|
|
|
|
def main():
|
|
for cur in ("BTC", "ETH"):
|
|
df = fetch(cur)
|
|
if df.empty:
|
|
print(f"{cur}: VUOTO"); continue
|
|
ts = pd.to_datetime(df["timestamp"], unit="ms", utc=True)
|
|
df.to_parquet(RAW / f"dvol_{cur.lower()}.parquet", index=False)
|
|
print(f"{cur}: {len(df)} giorni [{ts.iloc[0].date()} -> {ts.iloc[-1].date()}] "
|
|
f"DVOL media {df['close'].mean():.1f} range [{df['close'].min():.1f}, {df['close'].max():.1f}] "
|
|
f"-> data/raw/dvol_{cur.lower()}.parquet")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|