Hyperliquid (HyperCore) Data
Bitquery provides Hyperliquid Core data dumps in Parquet format, designed for backtesting, market microstructure research, and data lake integrations. These datasets can be hosted directly in your own cloud storage (for example, AWS S3) and queried using engines like Snowflake, BigQuery, Athena, Spark, etc.
This dataset covers HyperCore, the L1 order-book exchange, not HyperEVM state. It is an event stream, not a state chain: there are no blocks, transactions or transfers tables. Every row is an exchange event (a fill, an order status change, a book delta, an oracle price, a funding payment), and every row carries Block_Number and Block_Time, so tables join on block without a separate blocks table.
Coverage & Freshness
- History: a rolling window of the last 30 days, across all seven tables. Older blocks are trimmed by whole UTC day.
- Same window as the API: the Hyperliquid GraphQL API and streams also keep the last 30 days. The difference is shape and delivery, not depth.
- Freshness: the archive is written from the same pipeline as our live streams, so the cloud export can follow new blocks as they are produced. A Data Store purchase is a one-time set of files covering the 30 days up to the day before we prepare them.
- Markets: plain perpetuals, HIP-3 builder-deployed perps, spot pairs, and outcome/prediction tokens. See Markets for how they share one column.
How Much History Is Included?
The last 30 days, on every table. The window rolls forward each UTC day, so at any moment you can replay roughly a month of live HyperCore, including any HIP-3 market that launched inside that window. Blocks older than 30 days are not in the dataset.
The Hyperliquid cube on the streaming API has the same 30-day window, and a query for anything earlier returns an empty result. If you need to keep more than 30 days, land the Parquet files in your own bucket as they arrive and they stay there for as long as you want.
How Is This Different From Hyperliquid's Free S3 Archive?
Hyperliquid's own archive (and free mirrors of it) publishes L2 snapshots and asset contexts. This dataset is a different shape:
| Free archives (HL S3, Reservoir) | Bitquery cloud dataset | |
|---|---|---|
| Order book | L2 snapshots (price levels, fixed cadence) | L4 per-order updates with owner address and order id, every change |
| Orders | Not included | Full lifecycle: opens, cancels, every reject reason, TP/SL children |
| Fills | Not included | Both sides of every match, realized PnL, fees, builder and deployer fees |
| Liquidations | Not included | Both sides, with the liquidated account, mark price and method |
| Funding | Not included | Every account's payment at each hourly settlement |
| Oracle | Not included | Mark, oracle and external perp prices per coin, plus the inputs behind them |
| History | Varies by bucket, no completeness guarantee | Rolling last 30 days (keep your own copies to build a longer archive) |
| Freshness | Roughly monthly uploads | Follows the chain |
Queue position, order lifetime, maker behaviour, spoofing patterns, and why a liquidation fired are only recoverable from this shape of data.
Hyperliquid Data for Tax Reporting
Tax reports need every trading event a wallet produced. The dataset carries the last 30 days of fills, liquidations, TWAP fills and funding, so you, your accountant or your tax software can compute gains, losses and income for any wallet inside that window. To cover a full tax year, keep the files as they land in your bucket and the archive builds itself.
| Tax input | Bitquery table |
|---|---|
| Realized PnL on every closed position | trades and perp_liquidations: *_Position_RealizedPnl |
| Trading fees, including builder and HIP-3 deployer fees | trades and perp_liquidations: *_Fees_Fee, _FeeToken, _BuilderFee, _DeployerFee |
| Funding paid and received | perp_fundings: Funding_Amount |
| Liquidations | perp_liquidations |
| TWAP executions, fill by fill | trades where Trade_IsTwap = 1, with twaps for the parent order |
| Spot buys and sells for cost basis | trades where Trade_Market_Kind = 'spot' |
Deposits, withdrawals and transfers between accounts are not in the export today.
Why teams use it for tax work:
- Nothing dropped from trading. Funding, TWAP child fills and liquidations are included alongside regular trades, so PnL reconciles.
- Exact amounts. Prices, sizes, PnL and fees are stored as decimal strings, so no precision is lost to floating point.
- Works with your stack. Delivered to S3, GCS, Snowflake or BigQuery. Query one wallet or thousands with SQL, then export to CSV for tax software.
Built for tax platforms, accountants and funds reconciling Hyperliquid activity, as well as individual traders with high trade counts. To get the full dataset, buy it from the Bitquery Data Store, fill the API form or email sales@bitquery.io.
Get Full Access
The full dataset is delivered into your own cloud storage (S3, GCS) or warehouse share (Snowflake, BigQuery). To buy or trial it, go to the Bitquery Data Store, fill the API form or contact sales@bitquery.io.
Datasets you can buy on the Data Store, as one-time files covering the last 30 days:
- Hyperliquid: all seven tables
- Hyperliquid Trades:
trades,perp_liquidationsandtwaps - Hyperliquid Order Book:
order_updates,book_updatesandprice_updates - Hyperliquid Funding:
perp_fundings
Available Hyperliquid Tables
| Table | Grain | What it holds |
|---|---|---|
trades | one row per side of each match, liquidations excepted | Price, size, direction, taker flag, fees, signed position before the fill, realized PnL, TWAP link |
perp_liquidations | one row per side of each liquidation match | The same fields as trades, plus the liquidated account, mark price and method |
twaps | one row per TWAP status change | Total size, executed size and notional, duration, status and error text |
order_updates | one row per order status change | Full lifecycle: open, canceled, filled, triggered and every *Rejected state, with TIF, triggers and TP/SL children |
book_updates | one row per L4 change of a single order | new, update or remove, each with the owning address, order id, price and size |
price_updates | one row per coin and price kind in each oracle push | Resulting mark, oracle and external perp prices, and the inputs behind them |
perp_fundings | one row per account and market at each hourly settlement | Funding rate, signed position size, USDC paid or received |
Pick trades for executed volume and realized PnL, order_updates for intent and rejection analysis, and book_updates when you need to rebuild the book at an arbitrary block.
Why L4 Matters
An L2 snapshot tells you the book was 12 deep at a price; an L4 stream tells you which order moved and whose it was. Likewise, order_updates includes orders that never traded. Rejects and cancels are invisible in any trades-only dataset, and in the sample below they are most of the order events.
Sample Hyperliquid Cloud Dataset
You can explore schemas and validate your tooling using the public Hyperliquid sample datasets:
GitHub reference (schemas & examples) https://github.com/bitquery/blockchain-cloud-data-dump-sample/tree/main/hyperliquid
Sample Parquet downloads (public S3)
Every sample is written by the same exporter that produces a delivery, with every column.
| Table | Rows | Source | Download |
|---|---|---|---|
trades | 100 | first rows of pack 1165214000–1165214999 | 1165214001_1165214024.parquet |
twaps | 7 | every TWAP status in the same pack | 1165214012_1165214910.parquet |
perp_fundings | 100 | the settlement in block 1165214467 | 1165214467_1165214467.parquet |
order_updates | 100 | first rows from block 1078000000 | 1078000000_1078000000.parquet |
book_updates | 100 | first rows from block 1078000000 | 1078000000_1078000001.parquet |
price_updates | 100 | first rows from block 1078000000 | 1078000012_1078000012.parquet |
perp_liquidations has no public sample; liquidations are rare, and its columns match trades plus Liquidation_MarkPx, Liquidation_Method and Liquidation_LiquidatedUser.
The figures on this page come from the full export of two 1,000-block packs, not only the small samples: pack 1165214000–1165214999 (29 September 2026) held 11,082 trade rows, 7 TWAP statuses and one funding settlement of 411,880 rows across 327 markets, and pack 1078000000–1078000999 (19 July 2026) held 321,282 order updates, 193,903 book updates and 22,696 price updates.
Hyperliquid Dataset Directory Structure
bitquery-blockchain-dataset/
└── hyperliquid/
├── trades/
├── perp_liquidations/
├── twaps/
├── order_updates/
├── book_updates/
├── price_updates/
└── perp_fundings/
├── <start_block>_<end_block>.parquet
└── ...
Each file name gives the first and last block it holds. HyperCore produces roughly 14 blocks per second. File sizes track activity rather than block count, so size a download from the delivery manifest rather than from the block range.
A range with no events produces no file. Sparse tables such as twaps, perp_liquidations and perp_fundings (one settlement an hour) skip ranges, so discover files under a prefix rather than generating names from a stride.
Common Columns
Every table starts with the same block columns and ends with the same bookkeeping columns:
| Column | Type | Description |
|---|---|---|
ChainId | string | Always hyperliquid-core |
Block_Number | uint64 | HyperCore block height; the join key across tables |
Block_Time | int64 | Block timestamp, epoch nanoseconds |
Indexing_Time | int64 | When our pipeline indexed the block, epoch nanoseconds |
Indexing_OnTree, Indexing_OnTrunk | uint8 | 1 when the block was on our block tree and finalized chain |
Recording_Time | uint32 | When the row was written, epoch seconds |
Recording_Hash | uint64 | Row hash used to drop duplicates |
Block_Date | date | UTC date of the block, handy for partitioning |
Each table then prefixes its own fields with its entity name: Trade_*, Liquidation_*, Twap_*, Order_*, BookUpdate_*, PriceUpdate_*, Funding_*. Nested structures are flattened with _, for example Trade_Execution_Price and Order_Market_CoinRaw. The trading tables share these groups:
| Group | Columns |
|---|---|
_Trader_* | Address, Signer, Vault, Broadcaster, SignedAt |
_Market_* | Symbol, Kind, Protocol, CoinRaw, IsPerp, MaxLeverage |
_Position_* | Side, Size, SizeBefore, EntryPrice, RealizedPnl, Funding, Leverage, IsCross, Closed |
Some of these are never filled in a given table (for example Trade_Position_EntryPrice or Trade_MarkPx), and signer, vault and leverage fields are filled only when the source data carries them. The listing on the Data Store says, per column, when it can be empty.
Correctness Notes
Five things about this dataset will silently produce wrong numbers if you treat it like an EVM chain export.
1. Money Is Strings
Prices, sizes, PnL, fees and funding amounts are stored as decimal strings, not numerics. This is deliberate: exchange decimals must not pass through a float. Block numbers, ids and timestamps are integers. Use Decimal rather than float for money:
from decimal import Decimal
df["px"] = df.Trade_Execution_Price.map(Decimal)
Summing Trade_Execution_Price as float across a large export will drift; Decimal will not.
2. Every Match Appears Twice
trades carries one row per side, so a single match produces two rows, one taker and one maker. In the 29 September pack, 11,082 trade rows are 5,548 with Trade_Execution_IsAggressor = 1 and 5,534 with 0, resolving to 5,555 unique (Block_Number, Trade_Market_CoinRaw, Trade_Execution_Tid) keys.
Summing Trade_Execution_Size across the raw table double-counts volume. Filter to one side:
SELECT SUM(CAST(Trade_Execution_Size AS DECIMAL(38,8)))
FROM hyperliquid_trades
WHERE Trade_Execution_IsAggressor = 1 -- taker side only
Liquidation fills are not in trades; they are only in perp_liquidations. Include both tables for complete volume and PnL.
3. Trade_Execution_Hash Is Not a Join Key
One L1 transaction spans many matches, and TWAP fills carry 32 zero bytes. In the same pack, 11,082 rows share only 2,938 distinct hashes, and 1,296 rows (the TWAP fills, Trade_IsTwap = 1) have an all-zero hash.
Key matches on (Block_Number, Trade_Market_CoinRaw, Trade_Execution_Tid) instead.
4. Rejected Orders Dominate order_updates
order_updates is a record of intent, and most intent never trades. The 19 July pack's 321,282 order events break down as:
| Status | Records |
|---|---|
badAloPxRejected | 108,952 |
open | 95,735 |
canceled | 91,148 |
perpMarginRejected | 15,373 |
reduceOnlyCanceled | 3,875 |
iocCancelRejected | 3,505 |
insufficientSpotBalanceRejected | 1,299 |
filled | 1,118 |
minTradeNtlRejected | 121 |
reduceOnlyRejected | 101 |
selfTradeCanceled | 48 |
tooManyOpenOrdersRejected | 6 |
triggered | 1 |
Only 1,118 of 321,282 events, about 0.35%, are filled. badAloPxRejected alone is a third of the file: post-only (Alo) orders priced through the book, rejected rather than crossed. Alo is 313,186 of the 321,282 Order_Tif values, so this table is dominated by market makers quoting and requoting. Order_IsReject = 1 flags every *Rejected state.
Filter on Order_Status for whatever you are measuring; never treat row count as order flow.
5. Time Units Differ by Field
Two different epochs are in play, and mixing them is a factor-of-a-million error:
| Field | Unit |
|---|---|
Block_Time, Order_EventTime, Twap_Interval_EventTime | nanoseconds |
Trade_Execution_EventTime, Liquidation_Execution_EventTime | milliseconds |
Order_PlacedTime, Twap_Interval_StartTime | milliseconds |
Recording_Time | seconds |
In the sample, the same fill carries Block_Time = 1790672366376122193 and Trade_Execution_EventTime = 1790672366376.
Markets
*_Market_CoinRaw covers four market types in one column, and *_Market_Kind names the type so you do not have to parse the prefix:
CoinRaw | Kind | Meaning | Trade rows (29 Sep pack) | Order events (19 Jul pack) |
|---|---|---|---|---|
HYPE, BTC | perp | Plain perpetual | 6,406 | 258,980 |
xyz:DRAM | hip3 | HIP-3 builder-deployed perp | 4,044 | 50,963 |
@107 | spot | Spot pair, by index | 524 | 10,603 |
#1890 | outcome | Outcome / prediction token | 108 | 736 |
*_Market_Protocol is hypercore for plain perps, spot and outcome tokens, and the deploying dex (such as xyz) for HIP-3 markets. HIP-3 perps were 36% of trade rows in the 29 September pack, so leaving them in a "Hyperliquid perps" aggregate will not give you the numbers you expect.
Table Notes
The full column list for every table, with a description of each column, is on the Data Store listings. The points below are the ones that matter when you query.
trades
Trade_Execution_SideisBuyorSellfor this side;Trade_Execution_Directionis the node's label:Open Long,Close Short,Long > Short,Buy,Selland so on.Trade_Position_SizeBeforeis the signed position before the fill (negative is short), not money.Trade_Position_RealizedPnlis the realized PnL of the fill in USDC. It was non-zero on 5,277 of the 11,082 rows in the 29 September pack.Trade_Fees_Feeis negative when the maker received a rebate.Trade_Fees_FeeTokenwasUSDCon 10,743 of those rows.Trade_TwapIdjoins toTwap_TwapId.
perp_liquidations
- Each liquidation match has two rows, one for the liquidated account and one for the counterparty.
Liquidation_LiquidatedUsernames the liquidated account on both. Liquidation_MarkPxandLiquidation_Method(for examplemarket) come from the node.Liquidation_Liquidatoris always empty.
twaps
Twap_State_Statusisactivated,finished,terminatedorerror, with the error text inTwap_State_StatusError.Twap_Order_Sizeis the total to execute;Twap_State_ExecutedSizeandTwap_State_ExecutedNotionalare the running totals.
order_updates
Order_Oidjoins toTrade_Execution_OidandBookUpdate_Oid.Order_ParentOidandOrder_Leglink take-profit and stop orders to the order they are attached to.
book_updates
BookUpdate_Kindisnew(95,060 rows in the 19 July pack),remove(95,456) orupdate(3,387).BookUpdate_Sizeis the order's size onnewand the size after the change onupdate;BookUpdate_SizeBeforeis the size before anupdate.- Replay in
Block_NumberthenBookUpdate_Indexorder to reconstruct the book at any block.
price_updates
PriceUpdate_Kindsays which price the row is:mark,oracleorextPerpfor the resulting prices,markInput,spotInputorextPerpInputfor the inputs.- Oracle prices drive funding and liquidation, so join these to
perp_liquidationsonBlock_Numberwhen reconstructing why a liquidation fired.
perp_fundings
Funding_Amountis the USDC the account received, negative when it paid.Funding_Sizeis the signed position, negative for a short. With a positiveFunding_Rate, longs pay shorts.
Joining Tables
Every table carries Block_Number, and the order id threads the trading tables together:
-- taker fills with the order that produced them
SELECT
t.Trade_Market_CoinRaw,
t.Trade_Execution_Price,
t.Trade_Execution_Size,
t.Trade_Position_RealizedPnl,
o.Order_Tif,
o.Order_ReduceOnly
FROM hyperliquid_trades t
JOIN hyperliquid_order_updates o
ON t.Trade_Execution_Oid = o.Order_Oid
AND o.Order_Status = 'filled'
WHERE t.Trade_Execution_IsAggressor = 1
Because one order produces many status events and many fills, join on the order id plus a status filter, or aggregate one side first; otherwise you get a cross product.
Reading Files with DuckDB
No key, no client library. Point DuckDB at the public sample directly:
SELECT Trade_Market_CoinRaw,
SUM(CAST(Trade_Execution_Size AS DECIMAL(38,8)) * CAST(Trade_Execution_Price AS DECIMAL(38,8))) AS notional
FROM read_parquet('https://bitquery-blockchain-dataset.s3.us-east-1.amazonaws.com/hyperliquid/trades/1165214001_1165214024.parquet')
WHERE Trade_Execution_IsAggressor = 1 -- taker side only, see Correctness Notes
GROUP BY Trade_Market_CoinRaw
ORDER BY notional DESC
LIMIT 10;
Reading Files in Python
import pandas as pd
from decimal import Decimal
BASE = "https://bitquery-blockchain-dataset.s3.us-east-1.amazonaws.com/hyperliquid/"
trades = pd.read_parquet(f"{BASE}trades/1165214001_1165214024.parquet")
orders = pd.read_parquet(f"{BASE}order_updates/1078000000_1078000000.parquet")
# taker side only: every match is in the file twice
taker = trades[trades.Trade_Execution_IsAggressor == 1]
taker = taker.assign(
px=taker.Trade_Execution_Price.map(Decimal),
sz=taker.Trade_Execution_Size.map(Decimal),
)
print("matches:", len(taker), "of", len(trades), "trade rows")
print("notional:", (taker.px * taker.sz).sum())
# realized PnL net of fees, by wallet
net = trades.assign(
pnl=trades.Trade_Position_RealizedPnl.map(Decimal) - trades.Trade_Fees_Fee.map(Decimal)
).groupby("Trade_Trader_Address").pnl.sum()
print(net.sort_values().tail())
# order flow is mostly rejects, not trades
print(orders.Order_Status.value_counts().head())
Real-Time vs Batch Data Access
Cloud data dumps are optimized for batch analytics and historical workloads.
If you require low-latency or streaming Hyperliquid data, Bitquery also provides:
The Parquet exports and the live streams share the same field definitions, so a backtest reading this archive and a production consumer reading the stream decode with the same code.
Buy the Full Dataset
The public samples above are a few hundred rows. The full product is the rolling 30-day history, delivered into your own S3/GCS bucket or as a Snowflake/BigQuery share. A Data Store purchase is a one-time set of files covering the last 30 days.
Ready to run this in production?
Get an API key and run these queries in minutes, or talk to us about plans and enterprise delivery.