Files
PythagorasGoal/scripts/analysis/regime_fetcher.py
T
Adriano Dal Pastro 7169614506 feat(research): substrato ricerca frattali x regime ARGO
- 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>
2026-06-02 00:05:14 +00:00

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()