coinmetrics daily panel ingest
Browse files- ingest2.py +94 -0
ingest2.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Ingest daily price panel for 6-8 major crypto assets from coinmetrics.
|
| 2 |
+
|
| 3 |
+
Source: coinmetrics-community CSVs on GitHub (PriceUSD = volume-weighted
|
| 4 |
+
index across exchanges). Long daily history (btc 2010+, ltc/xrp 2013+,
|
| 5 |
+
eth 2015+) — this is the statistical-power lever for the DSR gate.
|
| 6 |
+
|
| 7 |
+
Output (uploaded to Avi-L/crypto-quant-eval-data):
|
| 8 |
+
panel_daily_close.parquet wide close-price panel, index=date, cols=symbols
|
| 9 |
+
panel_daily_meta.json per-asset start dates, obs counts, ADV assumptions
|
| 10 |
+
manifest2.json sha256 outputs + sources
|
| 11 |
+
"""
|
| 12 |
+
import hashlib
|
| 13 |
+
import io
|
| 14 |
+
import json
|
| 15 |
+
import tempfile
|
| 16 |
+
import urllib.request
|
| 17 |
+
from pathlib import Path
|
| 18 |
+
|
| 19 |
+
import pandas as pd
|
| 20 |
+
from huggingface_hub import HfApi
|
| 21 |
+
|
| 22 |
+
DEST = "Avi-L/crypto-quant-eval-data"
|
| 23 |
+
BASE = "https://raw.githubusercontent.com/coinmetrics/data/master/csv/{}.csv"
|
| 24 |
+
# majors with long daily history; ADV = realistic combined spot+perp
|
| 25 |
+
# notional/day used by the impact model (conservative, 2025-26 orderbook)
|
| 26 |
+
ASSETS = {
|
| 27 |
+
"BTC": 20e9, "ETH": 10e9, "LTC": 1.0e9, "XRP": 1.5e9,
|
| 28 |
+
"ADA": 0.5e9, "DOGE": 1.0e9, "ETC": 0.3e9, "DASH": 0.1e9,
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
MANIFEST = {"sources": {}, "outputs": {}, "assets": {}}
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def sha256_file(p: Path) -> str:
|
| 35 |
+
h = hashlib.sha256()
|
| 36 |
+
with open(p, "rb") as f:
|
| 37 |
+
for chunk in iter(lambda: f.read(1 << 20), b""):
|
| 38 |
+
h.update(chunk)
|
| 39 |
+
return h.hexdigest()
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def load_asset(sym: str) -> pd.Series:
|
| 43 |
+
url = BASE.format(sym.lower())
|
| 44 |
+
print(f"[ingest] fetching {url}", flush=True)
|
| 45 |
+
with urllib.request.urlopen(url, timeout=120) as r:
|
| 46 |
+
df = pd.read_csv(io.BytesIO(r.read()))
|
| 47 |
+
if "time" not in df or "PriceUSD" not in df:
|
| 48 |
+
raise ValueError(f"{sym}: missing time/PriceUSD columns")
|
| 49 |
+
s = pd.Series(df["PriceUSD"].values,
|
| 50 |
+
index=pd.to_datetime(df["time"], utc=True),
|
| 51 |
+
name=sym).dropna()
|
| 52 |
+
return s[s.index >= "2013-01-01"]
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def main() -> None:
|
| 56 |
+
with tempfile.TemporaryDirectory() as tmp:
|
| 57 |
+
out = Path(tmp)
|
| 58 |
+
panel = {}
|
| 59 |
+
for sym, adv in ASSETS.items():
|
| 60 |
+
try:
|
| 61 |
+
s = load_asset(sym)
|
| 62 |
+
except Exception as e:
|
| 63 |
+
print(f"[ingest] {sym} FAILED: {e}", flush=True)
|
| 64 |
+
continue
|
| 65 |
+
panel[sym] = s
|
| 66 |
+
MANIFEST["assets"][sym] = {
|
| 67 |
+
"start": str(s.index[0].date()), "obs": int(len(s)), "adv_usd": adv,
|
| 68 |
+
}
|
| 69 |
+
print(f"[ingest] {sym}: {len(s):,} days from {s.index[0].date()}",
|
| 70 |
+
flush=True)
|
| 71 |
+
if "BTC" not in panel:
|
| 72 |
+
raise RuntimeError("no BTC data — aborting")
|
| 73 |
+
close = pd.DataFrame(panel).sort_index()
|
| 74 |
+
close.index.name = "date"
|
| 75 |
+
p = out / "panel_daily_close.parquet"
|
| 76 |
+
close.to_parquet(p)
|
| 77 |
+
MANIFEST["outputs"][p.name] = {"sha256": sha256_file(p),
|
| 78 |
+
"rows": len(close), "cols": close.shape[1]}
|
| 79 |
+
meta = out / "panel_daily_meta.json"
|
| 80 |
+
meta.write_text(json.dumps(MANIFEST, indent=2))
|
| 81 |
+
MANIFEST["sources"]["coinmetrics-community CSVs"] = BASE.format("btc")
|
| 82 |
+
meta.write_text(json.dumps(MANIFEST, indent=2))
|
| 83 |
+
|
| 84 |
+
api = HfApi()
|
| 85 |
+
for f in (p, meta):
|
| 86 |
+
api.upload_file(path_or_fileobj=str(f), path_in_repo=f.name,
|
| 87 |
+
repo_id=DEST, repo_type="dataset",
|
| 88 |
+
commit_message="daily multi-asset price panel")
|
| 89 |
+
print(f"[ingest] panel: {close.shape[0]:,} days x "
|
| 90 |
+
f"{close.shape[1]} assets, uploaded", flush=True)
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
if __name__ == "__main__":
|
| 94 |
+
main()
|