"""Fetch dati REGIME backtestabili da Deribit MAINNET (public, no-auth) -> parquet. Abilita la ricerca strategie frattali x regime (ARGO-proxy). Salva in data/raw/: {btc,eth}_dvol.parquet : DVOL index 1h (IV 30d "VIX crypto"), storico ~2021->oggi {btc,eth}_funding.parquet : funding rate perp 1h, storico ~2019->oggi Solo componenti ARGO con STORICO GRATUITO (DVOL, funding) -> validabili OOS. Il GEX per-strike resta snapshot-only (vedi analisi 2026-06-01). Run: uv run python scripts/analysis/regime_fetcher.py """ from __future__ import annotations import time import urllib.request import urllib.parse import json from pathlib import Path import pandas as pd ROOT = Path(__file__).resolve().parents[2] RAW = ROOT / "data" / "regime" # NON data/raw (solo OHLCV) — evita pollution discovery asset BASE = "https://www.deribit.com/api/v2/public/" def _get(method: str, params: dict) -> dict: url = BASE + method + "?" + urllib.parse.urlencode(params) for _ in range(4): try: with urllib.request.urlopen(url, timeout=30) as r: return json.loads(r.read()) except Exception: time.sleep(1.0) return {} def fetch_dvol(currency: str, start_ms: int, end_ms: int, res: int = 3600) -> pd.DataFrame: """DVOL index (OHLC). Cap 1000 righe/chiamata -> chaining all'indietro.""" rows = [] cur_end = end_ms span = 1000 * res * 1000 while cur_end > start_ms: cur_start = max(start_ms, cur_end - span) d = _get("get_volatility_index_data", { "currency": currency, "start_timestamp": cur_start, "end_timestamp": cur_end, "resolution": res}) data = (d.get("result") or {}).get("data") or [] if not data: break rows.extend(data) oldest = min(x[0] for x in data) if oldest >= cur_end: break cur_end = oldest - 1 time.sleep(0.15) if not rows: return pd.DataFrame() df = pd.DataFrame(rows, columns=["timestamp", "open", "high", "low", "close"]) df = df.drop_duplicates("timestamp").sort_values("timestamp").reset_index(drop=True) df["dvol"] = df["close"] return df def fetch_funding(instrument: str, start_ms: int, end_ms: int) -> pd.DataFrame: """funding rate history perp (1h). Paginazione ~30g/chiamata.""" rows = [] cur_start = start_ms step = 30 * 24 * 3600 * 1000 while cur_start < end_ms: cur_end = min(end_ms, cur_start + step) d = _get("get_funding_rate_history", { "instrument_name": instrument, "start_timestamp": cur_start, "end_timestamp": cur_end}) data = d.get("result") or [] if data: rows.extend(data) cur_start = cur_end + 1 time.sleep(0.12) if not rows: return pd.DataFrame() df = pd.DataFrame(rows) ts_col = "timestamp" if "timestamp" in df.columns else df.columns[0] df = df.rename(columns={ts_col: "timestamp"}) keep = [c for c in ("timestamp", "interest_1h", "interest_8h", "index_price", "prev_index_price") if c in df.columns] df = df[keep].drop_duplicates("timestamp").sort_values("timestamp").reset_index(drop=True) return df def main(): RAW.mkdir(parents=True, exist_ok=True) now = _get("get_time", {}) end_ms = int(now.get("result", 0)) or int(time.time() * 1000) start_ms = end_ms - int(6.5 * 365 * 24 * 3600 * 1000) # ~6.5 anni for cur, inst in (("BTC", "BTC-PERPETUAL"), ("ETH", "ETH-PERPETUAL")): dv = fetch_dvol(cur, start_ms, end_ms) if not dv.empty: p = RAW / f"{cur.lower()}_dvol.parquet" dv.to_parquet(p) rng = (pd.to_datetime(dv['timestamp'].min(), unit='ms').date(), pd.to_datetime(dv['timestamp'].max(), unit='ms').date()) print(f" {cur} DVOL: {len(dv)} righe {rng[0]}->{rng[1]} (ora={dv['dvol'].iloc[-1]:.1f}) -> {p.name}") fr = fetch_funding(inst, start_ms, end_ms) if not fr.empty: p = RAW / f"{cur.lower()}_funding.parquet" fr.to_parquet(p) rng = (pd.to_datetime(fr['timestamp'].min(), unit='ms').date(), pd.to_datetime(fr['timestamp'].max(), unit='ms').date()) print(f" {cur} FUNDING: {len(fr)} righe {rng[0]}->{rng[1]} -> {p.name}") if __name__ == "__main__": main()