636be89b2b
- r0724_skh_live_weight.py: sweep w 0-0.50 sul path hourly x 23 offset; w*=0.25 (argmax mediana-IS di banda, plateau 0.20-0.30); w=0.30 passa weights_tilt_null solo a off0 (ancora fortunata), fallisce a offset mediano -> INVARIATO. Cadenza 230m = rumore (+0.01/+0.02 Sh med); il degrado live e' il fill-al-livello (~+0.35 Sh) che nessun cron recupera. Book e cron INVARIATI. - r0724_stable_snapshot.py: cattura giornaliera point-in-time supply stablecoin (DefiLlama, tokenless, idempotente) -> data/external/stable_snapshots/ (gitignored). Criterio di rivisita del lead STABLE: >=12 mesi di serie propria. - CLAUDE.md: bullet SKH01 aggiornato (follow-up chiuso). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
103 lines
3.9 KiB
Python
103 lines
3.9 KiB
Python
"""SNAPSHOT point-in-time supply stablecoin (DefiLlama) — sblocca il lead STABLE fra 12 mesi.
|
|
|
|
CONTESTO (2026-07-24, ondata on-chain). STABLE-supply-growth (gate risk-on su crescita 30g
|
|
della supply stablecoin totale, thr 10% ann.) e' stato l'unico candidato dell'ondata sopra
|
|
quasi tutti i gate (DSR 0.998, marginale ADDS, non-hedge) ma e' declassato a WATCH perche'
|
|
la storia DefiLlama e' RICOSTRUITA retroattivamente (chain/coin aggiunte nel tempo): il
|
|
"totale" visto oggi per il 2019-21 non era osservabile allora -> vintage-risk non sanabile.
|
|
|
|
Criterio di rivisita scritto a diario (2026-07-24-onchain-sentiment-wave.md): quando una
|
|
fonte POINT-IN-TIME della supply accumula >=12 mesi, ritestare il gate thr=10%. Questo
|
|
script E' quella fonte: cattura oggi cio' che e' osservabile oggi.
|
|
|
|
COSA FA (sola lettura, API pubblica tokenless, nessun ordine):
|
|
- GET https://stablecoins.llama.fi/stablecoins (lista coin + circolante corrente);
|
|
- 1 riga JSON per run APPESA a data/external/stable_snapshots/snapshots.jsonl:
|
|
totale USD-peg, totale all-peg, breakdown top-15 per simbolo, n_coins.
|
|
~1 KB/riga -> ~400 KB/anno. Idempotente per giorno: se esiste gia' una riga con la
|
|
stessa data UTC esce senza scrivere (safe da ri-run in cron).
|
|
|
|
CADENZA RACCOMANDATA (quando/se si decide di cablarla — NON e' in cron adesso):
|
|
1 run/giorno dopo mezzanotte UTC (es. 00:15), accanto agli altri snapshot.
|
|
|
|
USO:
|
|
uv run python scripts/research/r0724_stable_snapshot.py
|
|
|
|
NB ONESTO: questo script MISURA, non decide. Fra >=12 mesi la serie risponde a: il segnale
|
|
crescita-30g calcolato su dati point-in-time replica quello calcolato sulla storia
|
|
ricostruita? Se no, il lead STABLE era un artefatto di vintage e muore li'.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
import time
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
import requests
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
OUT_DIR = PROJECT_ROOT / "data" / "external" / "stable_snapshots"
|
|
OUT_FILE = OUT_DIR / "snapshots.jsonl"
|
|
API = "https://stablecoins.llama.fi/stablecoins?includePrices=true"
|
|
|
|
|
|
def fetch() -> dict:
|
|
last = None
|
|
for _ in range(4):
|
|
try:
|
|
r = requests.get(API, timeout=30)
|
|
j = r.json()
|
|
if "peggedAssets" in j:
|
|
return j
|
|
last = str(j)[:200]
|
|
except Exception as e:
|
|
last = str(e)
|
|
time.sleep(2.0)
|
|
raise RuntimeError(f"DefiLlama fail: {last}")
|
|
|
|
|
|
def circ_usd(asset: dict) -> float:
|
|
c = asset.get("circulating") or {}
|
|
return float(sum(v for v in c.values() if isinstance(v, (int, float))))
|
|
|
|
|
|
def main() -> None:
|
|
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
|
if OUT_FILE.exists():
|
|
for line in OUT_FILE.read_text().splitlines():
|
|
try:
|
|
if json.loads(line).get("date") == today:
|
|
print(f"SKIP: snapshot per {today} gia' presente")
|
|
return
|
|
except json.JSONDecodeError:
|
|
continue
|
|
|
|
data = fetch()
|
|
assets = data["peggedAssets"]
|
|
usd_peg = [a for a in assets if a.get("pegType") == "peggedUSD"]
|
|
tot_usd = sum(circ_usd(a) for a in usd_peg)
|
|
tot_all = sum(circ_usd(a) for a in assets)
|
|
top = sorted(usd_peg, key=circ_usd, reverse=True)[:15]
|
|
|
|
row = {
|
|
"date": today,
|
|
"snap_ts": int(time.time() * 1000),
|
|
"total_usd_peg": round(tot_usd, 2),
|
|
"total_all_peg": round(tot_all, 2),
|
|
"n_coins": len(assets),
|
|
"n_usd_peg": len(usd_peg),
|
|
"top15": {a.get("symbol", "?"): round(circ_usd(a), 2) for a in top},
|
|
"source": "stablecoins.llama.fi/stablecoins",
|
|
}
|
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
with OUT_FILE.open("a") as f:
|
|
f.write(json.dumps(row, separators=(",", ":")) + "\n")
|
|
print(f"OK {today}: totale USD-peg ${tot_usd/1e9:.2f}B ({len(usd_peg)} coin, "
|
|
f"{len(assets)} totali) -> {OUT_FILE}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|