hyperliquid.daily_metrics
Documentation for eth_defi.hyperliquid.daily_metrics Python module.
Hyperliquid daily vault metrics with DuckDB storage.
This module provides a daily pipeline for scanning Hyperliquid native vault
metrics and storing them in a DuckDB database. It computes ERC-4626-style
share prices using the time-weighted return approach, reusing the share
price calculation from eth_defi.hyperliquid.combined_analysis.
The pipeline:
Bulk-fetches all vaults from the stats-data API
Filters by TVL and open status
Fetches per-vault portfolio history via
vaultDetailsComputes share prices from portfolio history
Stores daily prices and metadata in DuckDB
Example:
from eth_defi.hyperliquid.session import create_hyperliquid_session
from eth_defi.hyperliquid.daily_metrics import run_daily_scan, HyperliquidDailyMetricsDatabase
session = create_hyperliquid_session(requests_per_second=2.75)
db = run_daily_scan(session, min_tvl=5_000, max_vaults=500)
print(f"Stored metrics for {db.get_vault_count()} vaults")
db.close()
Functions
|
Fetch a single vault's details and store metrics in the database. |
Convert vaultDetails portfolio history into a combined DataFrame with share prices. |
|
|
Run the daily Hyperliquid vault metrics scan. |
Classes
DuckDB database for storing Hyperliquid vault daily metrics. |
|
A single Hyperliquid daily price row ready for DuckDB upsert. |
- class HyperliquidDailyMetricsDatabase
Bases:
eth_defi.hyperliquid.vault_metrics_db.HyperliquidMetricsDatabaseBaseDuckDB database for storing Hyperliquid vault daily metrics.
Inherits shared metadata and lifecycle methods from
HyperliquidMetricsDatabaseBase.Stores daily share price time series and vault metadata. The share prices are computed using time-weighted returns from the Hyperliquid API portfolio history.
Example:
from pathlib import Path from eth_defi.hyperliquid.daily_metrics import HyperliquidDailyMetricsDatabase db = HyperliquidDailyMetricsDatabase(Path("/tmp/metrics.duckdb")) # Query vault data df = db.get_all_daily_prices() print(df) db.close()- __init__(path)
- Parameters
path (pathlib.Path) –
- close()
Close the database connection.
- delete_vault_daily_prices(vault_address)
Delete all daily price records for a vault.
Used by the healing script to clear corrupted share prices before re-computing from fresh API data.
- Parameters
vault_address (eth_typing.evm.HexAddress) – Vault address to delete.
- Returns
Number of rows deleted.
- Return type
- detect_broken_vaults()
Detect vaults with anomalous share price data.
Checks for four classes of breakage:
Extreme daily returns (> 10,000%): typically epoch reset artefacts where share price jumped to/from 1.0
Share price at cap (>= 9,999): overflow from zero total_supply
Share price stuck at 1.0: share price identical for 5+ rows, suggesting failed epoch reset logic
Missing epoch_reset column: rows computed with old code that lacked the
epoch_resetcolumn (all NULL)
- Returns
DataFrame with columns:
vault_address,name,issue_type,affected_rows,example_value- Return type
- get_all_daily_prices()
Get all daily price data across all vaults.
- Returns
DataFrame with all daily price records, ordered by vault then date.
- Return type
- get_all_tracked_addresses()
Return all vault addresses that have any price data.
- get_all_vault_metadata()
Get metadata for all vaults.
- Returns
DataFrame with one row per vault.
- Return type
- get_existing_dates(vault_address)
Get all dates with existing data for a vault.
- Parameters
vault_address (eth_typing.evm.HexAddress) – Vault address to query.
- Returns
Set of dates that already have data in the database.
- Return type
- get_latest_leader_fractions()
Get the latest leader_fraction for each vault.
Queries the most recent row per vault that has a non-NULL
leader_fractionvalue.
- get_leader_fraction_history(vault_address)
Get the recorded leader_fraction snapshots for a vault.
Returns only rows where
leader_fractionwas recorded (non-NULL). Each row represents a day when the scanner ran and observed the value. Over time, daily scans build up a sparse history of leader capital ownership that can be used to detect threshold crossings.- Parameters
vault_address (str) – Vault address to query (will be lowercased).
- Returns
DataFrame with columns
dateandleader_fraction, ordered by date ascending. Empty if no snapshots recorded.- Return type
- get_recently_tracked_addresses(within_days=4)
Return vault addresses with price data within the last within_days days.
Uses a pure date cutoff so that whole-day semantics are preserved for the daily table (DATE column) and behave identically for the HF table (TIMESTAMP column — DuckDB casts DATE to midnight).
- get_vault_daily_price_count(vault_address)
Get the number of daily price records for a vault.
- Parameters
vault_address (eth_typing.evm.HexAddress) – Vault address to query.
- Returns
Number of daily price records.
- Return type
- get_vault_daily_prices(vault_address)
Get daily price data for a specific vault.
- Parameters
vault_address (eth_typing.evm.HexAddress) – Vault address to query.
- Returns
DataFrame with price records for this vault, ordered by date.
- Return type
- get_vault_last_date(vault_address)
Get the last date with price data for a vault.
- Parameters
vault_address (eth_typing.evm.HexAddress) – Vault address to query.
- Returns
The latest date, or None if no data.
- Return type
- mark_vaults_disappeared(known_addresses)
Set TVL to zero for vaults that disappeared from the API.
Also writes tombstone price rows so downstream consumers see a fresh row reflecting removal.
Recompute share prices for all vaults in the database.
Iterates over every vault and calls
recompute_vault_share_prices()for each.- Returns
Summary dict:
{"total_vaults": int, "vaults_with_changes": int, "vaults_with_epoch_resets": int, "total_changed_rows": int, "per_vault": dict[str, dict]}- Return type
Recompute share prices for a vault from stored tvl/pnl data.
Uses the stored
tvl(total_assets),daily_pnl(pnl_update), andcumulative_pnlcolumns to reconstructnetflow_update, then feeds the data through_calculate_share_price()with the current chain-linked epoch reset logic.This allows healing of share price data without re-fetching from the Hyperliquid API.
- Parameters
vault_address (eth_typing.evm.HexAddress) – Vault address to recompute.
- Returns
Dict with before/after statistics:
{"rows": int, "old_sp_min": float, "old_sp_max": float, "new_sp_min": float, "new_sp_max": float, "epoch_resets": int, "changed_rows": int}- Return type
- save()
Force a checkpoint to ensure data is written to disk.
- tombstone_stale_vaults(known_api_addresses, wind_down_days=4)
Write tombstone rows for vaults whose wind-down window has expired.
A vault is eligible for tombstoning when:
It has existing price data in the database
It is NOT present in the current bulk API listing
Its most recent price row is older than
wind_down_daysIt does not already have a tombstone row
The tombstone carries forward the last known share_price and cumulative_pnl so return calculations are not distorted.
- update_vault_tvl_bulk(updates)
Bulk-update TVL, is_closed, and APR for existing vaults.
Only updates rows that already exist in
vault_metadata.
- upsert_daily_prices(rows, cutoff_date=None)
Bulk upsert daily price rows for a vault.
- Parameters
rows (list[eth_defi.hyperliquid.daily_metrics.HyperliquidDailyPriceRow]) –
List of
HyperliquidDailyPriceRowitems.The
is_closed,allow_deposits,leader_fraction, andleader_commissionfields should beNonefor historical rows and only set for the latest (today’s) row, so that we track how these values evolve over time.The flow columns (
daily_deposit_countetc.) should beNonefor dates outside the backfill window and0for dates with no activity within the backfill window.COALESCEpreserves existing values whenNoneis passed.cutoff_date (Optional[datetime.date]) – If provided, only store rows up to this date (inclusive). Used for incremental scanning / testing.
- upsert_vault_metadata(vault_address, name, leader, description, is_closed, relationship_type, create_time, commission_rate, follower_count, tvl, apr, allow_deposits=True, flow_data_earliest_date=None)
Insert or update a vault’s metadata.
- Parameters
vault_address (eth_typing.evm.HexAddress) – Vault address (will be lowercased).
flow_data_earliest_date (Optional[datetime.date]) – Earliest date for which daily deposit/withdrawal flow data has been backfilled.
Nonemeans no flow data yet.name (str) –
leader (eth_typing.evm.HexAddress) –
is_closed (bool) –
relationship_type (str) –
create_time (Optional[datetime.datetime]) –
allow_deposits (bool) –
- class HyperliquidDailyPriceRow
Bases:
objectA single Hyperliquid daily price row ready for DuckDB upsert.
- __init__(vault_address, date, share_price, tvl, cumulative_pnl, cumulative_volume=None, daily_pnl=0.0, daily_return=0.0, follower_count=None, apr=None, is_closed=None, allow_deposits=None, leader_fraction=None, leader_commission=None, daily_deposit_count=None, daily_withdrawal_count=None, daily_deposit_usd=None, daily_withdrawal_usd=None, epoch_reset=None, data_source='api', written_at=None)
- Parameters
vault_address (eth_typing.evm.HexAddress) –
date (datetime.date) –
share_price (float) –
tvl (float) –
cumulative_pnl (float) –
daily_pnl (float) –
daily_return (float) –
data_source (str) –
written_at (Optional[datetime.datetime]) –
- Return type
None
- fetch_and_store_vault(session, db, summary, cutoff_date=None, timeout=30.0, flow_backfill_days=7)
Fetch a single vault’s details and store metrics in the database.
- Parameters
session (eth_defi.hyperliquid.session.HyperliquidSession) – HTTP session with rate limiting.
db (eth_defi.hyperliquid.daily_metrics.HyperliquidDailyMetricsDatabase) – The metrics database to write into.
summary (eth_defi.hyperliquid.vault.VaultSummary) – Vault summary from the bulk listing.
cutoff_date (Optional[datetime.date]) – If provided, only store price data up to this date.
timeout (float) – HTTP request timeout.
flow_backfill_days (int) – Number of complete days to backfill deposit/withdrawal flow data. Only complete days are fetched (up to yesterday). Set to
0to disable flow fetching.
- Returns
True if the vault was successfully processed.
- Return type
- portfolio_to_combined_dataframe(portfolio_all_time=None, *, portfolio=None)
Convert vaultDetails portfolio history into a combined DataFrame with share prices.
Derives
pnl_updateandnetflow_updatefrom the portfolio history, then feeds them through_calculate_share_price()to compute ERC-4626-style share prices.When
portfolio(the full period dict) is provided, all available periods (day,week,month,allTime) are merged to produce a higher-resolution time series. This yields more data points for recent history compared to usingallTimealone.The derivation:
pnl_update[i] = pnl_history[i] - pnl_history[i-1]netflow_update[i] = (account_value[i] - account_value[i-1]) - pnl_update[i]cumulative_account_value[i] = account_value[i]
- Parameters
portfolio_all_time (Optional[eth_defi.hyperliquid.vault.PortfolioHistory]) – The
allTimePortfolioHistoryfromvaultDetailsendpoint. Kept for backward compatibility.portfolio (Optional[dict[str, eth_defi.hyperliquid.vault.PortfolioHistory]]) – Full portfolio period dict from
VaultInfo.portfolio. When provided, all periods are merged for higher resolution. Takes precedence overportfolio_all_time.
- Returns
DataFrame with timestamp index and columns:
pnl_update,netflow_update,cumulative_pnl,cumulative_netflow,cumulative_account_value,total_assets,total_supply,share_price,epoch_reset- Return type
- run_daily_scan(session, db_path=PosixPath('/home/runner/.tradingstrategy/vaults/hyperliquid-vaults.duckdb'), min_tvl=5000, max_vaults=20000, max_workers=16, cutoff_date=None, timeout=30.0, vault_addresses=None, flow_backfill_days=7, full_scan=False)
Run the daily Hyperliquid vault metrics scan.
Bulk-fetches all vaults from stats-data API
Filters by TVL and vault limit (or by explicit address list)
Fetches per-vault details and computes share prices
Fetches deposit/withdrawal events for flow metrics
Stores everything in DuckDB
- Parameters
session (eth_defi.hyperliquid.session.HyperliquidSession) – HTTP session with rate limiting. Use
create_hyperliquid_session().db_path (pathlib.Path) – Path to the DuckDB database file.
min_tvl (float) – Minimum TVL in USD to include a vault. Ignored when
vault_addressesis provided.max_vaults (int) – Maximum number of vaults to process (sorted by TVL descending). Ignored when
vault_addressesis provided.max_workers (int) – Number of parallel workers for fetching vault details.
cutoff_date (Optional[datetime.date]) – If provided, only store price data up to this date. Used for incremental scanning / testing.
timeout (float) – HTTP request timeout.
vault_addresses (Optional[list[str]]) – If provided, only scan these specific vault addresses. Overrides
min_tvlandmax_vaultsfilters.flow_backfill_days (int) – Number of complete days to backfill deposit/withdrawal flow data. Only complete days are fetched (up to yesterday). Set to
0to disable flow fetching. Use a large value (e.g.365) for initial deep backfill.full_scan (bool) – When
True, bypass themin_tvlfilter and process all vaults that exist in the database and are still present in the bulk API listing, regardless of their current TVL. This is intended to be run periodically (e.g. once a week) to refresh data for vaults that have dropped below the daily TVL threshold but still have historical data worth keeping current. Themax_vaultslimit still applies to cap total work. Ignored whenvault_addressesis provided.
- Returns
The metrics database instance.
- Return type
eth_defi.hyperliquid.daily_metrics.HyperliquidDailyMetricsDatabase