7169614506
- regime_fetcher.py: fetch DVOL (2021+) + funding (2019+) BTC/ETH da Deribit mainnet public - regime_lab.py: API condivisa, allineamento regime<->prezzo CAUSALE no-look-ahead, feature regime (dvol_pct/vrp/funding_z/dvol_chg) + frattali (hurst/higuchi/vratio/williams), cache feature precalcolate, report()=netto-fee OOS via explore_lab - fractal_argo_workflow.js: workflow ~100 agenti (84 griglia + 8 wildcard + verifica + sintesi) Branch di ricerca: nessun impatto su main/live. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
113 lines
4.2 KiB
Python
113 lines
4.2 KiB
Python
"""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" / "raw"
|
|
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()
|