fix(fetch_hyperliquid): taglia il backfill sintetico di cerbero MCP (vol=0) — universo 52->51
Cerbero MCP padda il periodo pre-quotazione su HL con barre SINTETICHE (volume 0, prezzi copiati da Binance -> matchano cross-venue e non sono flat): asset listati dopo lo START (es. AXS 83%, ALGO/SAND 37%) passavano i gate flat+cross-venue ed erano certificati PULITO pur non essendo negoziabili. E' il caso v2.0.0 (edge su un book che non c'era). Fix: il VOLUME e' il rivelatore del backfill -> (1) taglio del run iniziale a volume 0 (serie nativa), (2) gate storia nativa >=365g reali (AXS scartato), (3) gate vol=0 interno, (4) cross-venue/flat ricalcolati solo sulle barre reali, (5) parquet scartati rimossi. Verificato direttamente su cerbero MCP mainnet. I 19 major di XS01 hanno 0 backfill -> strategia live invariata. Diario 2026-06-20-cerbero-backfill-fix.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,11 +2,22 @@
|
||||
|
||||
Hyperliquid (via cerbero-mcp mainnet) offre ~230 perp liquidi, ma storia nativa REALE solo dal
|
||||
2024 (pre-2024 = backfill, volume 0). Qui scarico un set liquido a 1d (2024+), e CERTIFICO ogni
|
||||
asset come BTC/ETH: cross-venue vs Binance (realismo) + flat-bar (liquidita'). Scrivo SOLO i puliti
|
||||
in data/raw/hl_<sym>_1d.parquet (namespace dedicato, NON mischiato col Deribit BTC/ETH).
|
||||
asset come BTC/ETH: cross-venue vs Binance (realismo) + flat-bar + VOLUME (liquidita'). Scrivo SOLO
|
||||
i puliti in data/raw/hl_<sym>_1d.parquet (namespace dedicato, NON mischiato col Deribit BTC/ETH).
|
||||
|
||||
Disciplina: Cerbero ci ha gia' bruciato (testnet) -> niente fiducia, solo certificazione.
|
||||
|
||||
CORREZIONE estrazione (2026-06-20, "analisi fatte"): il floor START=2024-01-01 NON basta. Cerbero
|
||||
restituisce BACKFILL SINTETICO (volume==0, ma prezzi copiati da un venue di riferimento -> matchano
|
||||
Binance e NON sono flat) per il periodo PRIMA che l'asset quotasse davvero su Hyperliquid. Cosi'
|
||||
asset listati a meta'/fine 2024+ passavano cross-venue+flat ed erano certificati PULITO pur essendo
|
||||
in gran parte sintetici (es. AXS 83% backfill: trading reale solo da 2026-01; ALGO/SAND 37%). E' lo
|
||||
stesso errore v2.0.0 (edge su un book che non c'era). Fix: (1) il VOLUME e' il rivelatore di backfill
|
||||
-> si TAGLIA il run iniziale di barre a volume 0 e si tiene solo la serie NATIVA; (2) gate su storia
|
||||
nativa minima (>= MIN_NATIVE_DAYS reali) -> scarta chi e' troppo corto dopo il taglio; (3) gate su
|
||||
volume-0 INTERNO (gap di liquidita') oltre il taglio iniziale; (4) cross-venue/flat ricalcolati SOLO
|
||||
sulle barre reali; (5) i parquet degli asset scartati vengono RIMOSSI (disco == set certificato).
|
||||
|
||||
uv run python scripts/analysis/fetch_hyperliquid.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
@@ -17,7 +28,9 @@ sys.path.insert(0, str(PROJECT_ROOT))
|
||||
import numpy as np, pandas as pd, requests, ccxt
|
||||
|
||||
RAW = PROJECT_ROOT / "data" / "raw"
|
||||
START = "2024-01-01"; END = pd.Timestamp.utcnow().strftime("%Y-%m-%d") # dinamico (refresh giornaliero)
|
||||
START = "2024-01-01"; END = pd.Timestamp.now("UTC").strftime("%Y-%m-%d") # dinamico (refresh giornaliero)
|
||||
MIN_NATIVE_DAYS = 365 # storia NATIVA reale minima (post-taglio backfill) per entrare nell'universo
|
||||
INTERIOR_VOL0_MAX = 5.0 # % max di barre a volume 0 DOPO il taglio iniziale (gap di liquidita' interni)
|
||||
# UNIVERSO ESTESO: alt liquidi noti su Hyperliquid (mappa Binance auto = SYM/USDT). Il gate di
|
||||
# certificazione (cross-venue + liquidita' + flat) scarta i non-conformi. k-prefissi esclusi
|
||||
# (scaling 1000x complica il cross-venue). MATIC morto escluso.
|
||||
@@ -62,17 +75,33 @@ def binance_daily(sym_b, start_ms, end_ms):
|
||||
return pd.Series(out)
|
||||
|
||||
|
||||
def trim_backfill(df):
|
||||
"""Taglia il run INIZIALE di barre a volume 0 (= backfill sintetico pre-quotazione su HL).
|
||||
Ritorna (serie_nativa, n_barre_tagliate). Il volume e' il rivelatore: il backfill copia i
|
||||
prezzi da un venue di riferimento (non flat, matcha Binance) ma ha volume 0."""
|
||||
vol = df["volume"].to_numpy()
|
||||
lead = int(np.argmax(vol > 0)) if (vol > 0).any() else len(df)
|
||||
return df.iloc[lead:].reset_index(drop=True), lead
|
||||
|
||||
|
||||
def main():
|
||||
H=_h()
|
||||
print("="*92); print(" FETCH + CERTIFY Hyperliquid 1d (Cerbero mainnet) — cross-venue vs Binance + liquidita'"); print("="*92)
|
||||
print(f" {'sym':<6}{'barre':>7}{'start':>12}{'flat%':>7}{'med_bps':>9}{'>1%':>7}{'verdetto':>12}")
|
||||
print("="*100); print(" FETCH + CERTIFY Hyperliquid 1d (Cerbero mainnet) — cross-venue + flat + VOLUME (no backfill)"); print("="*100)
|
||||
print(f" {'sym':<6}{'reali':>6}{'bfill':>6}{'start_reale':>13}{'flat%':>7}{'vol0%':>7}{'med_bps':>9}{'>1%':>7}{'verdetto':>14}")
|
||||
certified=[]
|
||||
for s in SYMS:
|
||||
df=fetch_hl(s,H)
|
||||
if df.empty: print(f" {s:<6} vuoto"); continue
|
||||
path = RAW/f"hl_{s.lower()}_1d.parquet"
|
||||
raw=fetch_hl(s,H)
|
||||
if raw.empty:
|
||||
print(f" {s:<6} vuoto"); path.unlink(missing_ok=True); continue
|
||||
# --- CORREZIONE: taglia il backfill sintetico (volume 0 iniziale), tieni la serie nativa ---
|
||||
df, n_bfill = trim_backfill(raw)
|
||||
if df.empty:
|
||||
print(f" {s:<6} tutto backfill (vol0) -> scarta"); path.unlink(missing_ok=True); continue
|
||||
ts=pd.to_datetime(df["timestamp"],unit="ms",utc=True)
|
||||
flat=((df.open==df.high)&(df.high==df.low)&(df.low==df.close)).mean()*100
|
||||
# cross-venue vs Binance USDT (daily close)
|
||||
vol0=(df["volume"].to_numpy()==0).mean()*100 # gap di liquidita' INTERNI (post-taglio)
|
||||
# cross-venue vs Binance USDT (daily close) — SOLO sulle barre reali
|
||||
ref=binance_daily(BINANCE[s], int(df["timestamp"].iloc[0]), int(df["timestamp"].iloc[-1]))
|
||||
a=df.set_index("timestamp")["close"]
|
||||
m=pd.concat([a.rename("a"),ref.rename("b")],axis=1,join="inner").dropna()
|
||||
@@ -83,13 +112,20 @@ def main():
|
||||
# gate "delistato/migrato": l'ultima barra dev'essere recente (entro ~21g da END),
|
||||
# altrimenti l'asset tronca l'universo cross-sectional (es. MKR fermo a 2025-09, FXS 2026-01).
|
||||
recent = (pd.Timestamp(END, tz="UTC") - ts.iloc[-1]) <= pd.Timedelta("21D")
|
||||
clean = (not np.isnan(med)) and med<60 and g1<3 and flat<5 and recent
|
||||
v = "PULITO" if clean else "scarta"
|
||||
print(f" {s:<6}{len(df):>7}{str(ts.iloc[0].date()):>12}{flat:>6.1f}%{med:>9.1f}{g1:>6.1f}%{v:>12}")
|
||||
# gate storia NATIVA: dopo il taglio dev'esserci abbastanza vita reale (es. AXS quotato 2026-01 -> scarta)
|
||||
native_days = (ts.iloc[-1] - ts.iloc[0]).days
|
||||
enough = native_days >= MIN_NATIVE_DAYS
|
||||
clean = (not np.isnan(med)) and med<60 and g1<3 and flat<5 and vol0<INTERIOR_VOL0_MAX and recent and enough
|
||||
if clean: v="PULITO"
|
||||
elif not enough: v=f"corto<{MIN_NATIVE_DAYS}g"
|
||||
else: v="scarta"
|
||||
print(f" {s:<6}{len(df):>6}{n_bfill:>6}{str(ts.iloc[0].date()):>13}{flat:>6.1f}%{vol0:>6.1f}%{med:>9.1f}{g1:>6.1f}%{v:>14}")
|
||||
if clean:
|
||||
df.to_parquet(RAW/f"hl_{s.lower()}_1d.parquet", index=False); certified.append(s)
|
||||
df.to_parquet(path, index=False); certified.append(s)
|
||||
else:
|
||||
path.unlink(missing_ok=True) # disco == set certificato (niente parquet contaminati a riposo)
|
||||
print(f"\n CERTIFICATI ({len(certified)}): {certified}")
|
||||
print(" Scritti in data/raw/hl_<sym>_1d.parquet (namespace dedicato). Universo per cross-sectional.")
|
||||
print(" Scritti in data/raw/hl_<sym>_1d.parquet (namespace dedicato, SERIE NATIVA senza backfill).")
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
|
||||
Reference in New Issue
Block a user