hyperliquid.perp_trades is the fill table for Hyperliquid perpetual futures, venue-wide: first-party markets (coin = BTC) and HIP-3 builder-deployed markets (coin = xyz:TSLA) alike. Grain: one row per fill leg — both sides of every match — keyed on (block_date, coin, trader, oid, tid), with tid shared by the two legs of a match. Spot and HIP-4 outcome markets are out of scope.
Fills are decoded from the full node fills stream rather than from taker order actions (fills_raw), which misses every engine-generated fill — TWAP child slices and liquidation executions — worth 5-30% of volume. History starts 2025-07-27 and runs at roughly 8.75M legs per day across the 417 coins that have traded (as of 2026-08-21).
Table schema
Bound block_date on every query. It is the partition key and the only column that prunes: block_month is a plain column, so filtering it alone reads every day in the month, and nothing prunes within a day — a coin filter still scans the full partition. An unbounded scan reads the whole multi-billion-row history.
Both legs are here, so filter is_taker for volume
SUM(notional_usd) over this table is ~2x exchange-reported volume, because every match contributes a taker leg and a maker leg. Filtering is_taker counts each match once. For pre-aggregated volume, use perp_market_metrics_hourly or perp_market_metrics_daily, which are the volume source of truth.
Do not filter is_taker when the question is per-account: at account grain each leg is one account’s own participation, which is also why perp_accounts_daily aggregates both legs. The same applies to closed_pnl_usd — maker-side closes carry realised PnL too, and a taker-only filter drops roughly 40% of it.
fill_type
fill_type is the leg’s role, normalised from dir, liquidation_method, is_taker and twap_id.
settlement and vault_netting are administrative and belong in no volume or activity measure. liquidation and adl are genuine risk transfers at market prices, but they are forced, so include them only when the question covers forced flow.
TWAP legs inflate fill counts far more than volume — one parent order becomes many slices. Count DISTINCT twap_id for order-level figures.
Liquidation columns mark the match, not the role
liquidation_method and liquidated_trader are stamped on both legs of a liquidation match. Counting rows where liquidation_method IS NOT NULL therefore doubles the answer: measured 2026-08-12, 17,044 legs carried a method while only 8,522 accounts were liquidated. Use fill_type = 'liquidation' for the forced leg.
The roles depend on the method. On market (ordinary) liquidations the liquidated account is the taker and its counterparty is a plain order leg. On backstop (auto-deleveraging) the roles invert: the bankrupt account is the maker leg with fill_type = 'liquidation', and the profitable account force-closed against it is the taker, with fill_type = 'adl'.
side is the order direction, dir is the position effect
The two legs of a match always carry opposite side values. dir is not symmetric that way: it describes what the fill did to this leg’s own position — Open Long, Close Short, Long > Short — so a Close Long fill has side = 'sell'. Use side for order flow, dir or start_position for position intent. Perp scope carries exactly 13 dir values, verified across full fill history; an unrecognised value lands in fill_type = 'order' until the model is taught it.
Fees
fee_usd is negative on maker rebates, so summing it across all legs yields net protocol revenue; add is_taker for gross taker fees. builder_fee_usd is populated together with builder on the ~6% of legs routed through a front-end, and deployer_fee_usd only on HIP-3 markets.
All three are denominated in fee_token, which is not always USDC — a HIP-3 DEX picks its own quote token, and USDC, USDT0, USDH and USDE all appear across history. They are USD-pegged, which is what keeps the _usd column names honest, but a fee on a USDH market is paid in USDH.
Not in this table
Curated classification — market_category, asset_class, asset_type, underlying_ticker, builder_name — is deliberately kept off this table so a reclassification does not rewrite billions of rows. Join perp_market_details on coin for it, along with the market-level max_leverage, margin_tiers and margin_mode.
Per-fill leverage and margin mode are absent by design: both are a per-(user, asset) account setting rather than a property of a fill, and the venue-wide models do not reconstruct them.