crypto-forecasting-ai / scripts /fetch_dvol.py
chonthichar's picture
Claude Opus 5.5
Add Deribit DVOL implied-volatility features as a training option
69a7f91
Raw History Blame Contribute Delete
2.42 kB
"""Download Deribit's BTC implied-volatility index (DVOL), hourly, full history.
DVOL is the options market's own 30-day expectation of BTC volatility, in
annualised percent, published since March 2021. It is information the price
series does not contain directly: what option traders are paying for future
movement. Public endpoint, no key needed.
Rows are hourly OHLC of the index keyed by the hour's open time (UTC), the
same convention as the Binance candles, so the close of row t is known when
candle t closes and may be used for the forecast issued then.
Writes outputs/volatility/btc_dvol_1h.csv.
Usage:
python scripts/fetch_dvol.py
"""
from __future__ import annotations
import json
import time
import urllib.request
from pathlib import Path
import pandas as pd
ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "outputs" / "volatility" / "btc_dvol_1h.csv"
URL = ("https://www.deribit.com/api/v2/public/get_volatility_index_data"
"?currency=BTC&start_timestamp={start}&end_timestamp={end}&resolution=3600")
START_MS = 1609459200000 # 2021-01-01; the index begins in March 2021
def fetch() -> pd.DataFrame:
end, rows = int(time.time() * 1000), []
while end > START_MS:
with urllib.request.urlopen(URL.format(start=START_MS, end=end), timeout=30) as resp:
result = json.load(resp)["result"]
batch = result.get("data") or []
if not batch:
break
rows += batch
nxt = result.get("continuation")
if not nxt or nxt >= end:
break
end = nxt
time.sleep(0.2) # be polite to a public endpoint
df = pd.DataFrame(rows, columns=["ts", "open", "high", "low", "close"])
df["timestamp"] = pd.to_datetime(df["ts"], unit="ms", utc=True)
return (df.drop(columns="ts").drop_duplicates("timestamp")
.sort_values("timestamp").reset_index(drop=True))
def main() -> None:
df = fetch()
OUT.parent.mkdir(parents=True, exist_ok=True)
df.to_csv(OUT, index=False)
full = pd.date_range(df["timestamp"].min(), df["timestamp"].max(), freq="1h")
print(f"{len(df):,} hours, {df['timestamp'].min()} -> {df['timestamp'].max()}; "
f"{len(full) - len(df):,} missing hours; DVOL range {df['close'].min():.1f}-{df['close'].max():.1f}")
print(f"written {OUT.relative_to(ROOT)}")
if __name__ == "__main__":
main()