> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dune.com/llms.txt
> Use this file to discover all available pages before exploring further.

# hyperliquid.perp_market_metrics_hourly

> Hourly per-market candles, volume, fees, open interest and funding for every Hyperliquid perpetual market.

export const PremiumDatasetAccessCard = ({href = "https://dune.com/enterprise#contact-form", note = null}) => <Card title="Gated dataset" icon="lock" href={href}>
    Querying this dataset requires an entitlement on your workspace. See <a href="/data-catalog/overview#access-tiers-public-vs-gated-datasets">access tiers</a>, or contact the Dune team to enable access.
    {note && <><br /><br />{note}</>}
  </Card>;

`hyperliquid.perp_market_metrics_hourly` is the venue-wide hourly market table for Hyperliquid perpetual futures. Grain: one row per `(coin, block_hour)`, covering both first-party markets (`coin` = `BTC`) and HIP-3 builder-deployed markets (`coin` = `xyz:TSLA`); spot and HIP-4 outcome markets are out of scope. `coin` is the join key to [`perp_market_details`](/data-catalog/curated/perpetuals/hyperliquid/perp-market-details); `market_symbol` repeats across dexes, so never join on it.

<PremiumDatasetAccessCard />

This table, not `perp_trades`, is the volume figure to quote: [`hyperliquid.perp_trades`](/data-catalog/curated/perpetuals/hyperliquid/perp-trades) holds both legs of every match, so its raw `SUM(notional_usd)` is exactly twice the volume reported here. The candle, volume and count columns here are measured off the taker leg, so each match is counted once. The participant counts and `fee_usd` deliberately span both legs — `unique_traders` is distinct accounts on either side, and `fee_usd` nets the legs, because a rebated maker fee is negative.

## Table schema

| Column                    | Type                          | Description                                                                                                              |
| ------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `block_month`             | `DATE`                        | Partition key, and the only column that prunes — filter it on every read                                                 |
| `block_date`              | `DATE`                        | Date of `block_hour`                                                                                                     |
| `block_hour`              | `TIMESTAMP(3) WITH TIME ZONE` | Start of the hour, UTC. The grain, with `coin`                                                                           |
| `perp_dex`                | `VARCHAR`                     | `hyperliquid` for first-party markets, otherwise the builder DEX code (`xyz`, `flx`, `para`, ...)                        |
| `coin`                    | `VARCHAR`                     | Venue-native market id. The grain, with `block_hour`, and the join key to `perp_market_details`                          |
| `market_symbol`           | `VARCHAR`                     | `coin` with the `dex:` prefix stripped. Not unique across dexes                                                          |
| `asset_id`                | `BIGINT`                      | Numeric asset id used by the raw action tables. `NULL` when the market traded before the registry recorded it            |
| `open_price`              | `DOUBLE`                      | First trade price in the hour. `NULL` on an hour with no trades                                                          |
| `high_price`              | `DOUBLE`                      | Highest trade price in the hour                                                                                          |
| `low_price`               | `DOUBLE`                      | Lowest trade price in the hour                                                                                           |
| `close_price`             | `DOUBLE`                      | Last trade price in the hour. Never carried forward                                                                      |
| `vwap`                    | `DOUBLE`                      | `volume_usd / volume_units`                                                                                              |
| `volume_usd`              | `DOUBLE`                      | Traded notional, one count per match. Includes administrative fills                                                      |
| `volume_units`            | `DOUBLE`                      | Traded volume in base units                                                                                              |
| `buy_volume_usd`          | `DOUBLE`                      | Aggressor buy notional — not a directional net, and it includes engine-forced closes                                     |
| `sell_volume_usd`         | `DOUBLE`                      | Aggressor sell notional. Same forced-flow caveat                                                                         |
| `trades_count`            | `BIGINT`                      | Matches in the hour                                                                                                      |
| `unique_traders`          | `BIGINT`                      | Distinct accounts on either leg                                                                                          |
| `unique_takers`           | `BIGINT`                      | Distinct aggressors                                                                                                      |
| `unique_makers`           | `BIGINT`                      | Distinct passive accounts                                                                                                |
| `fee_usd`                 | `DOUBLE`                      | Protocol fee revenue for the market, both legs netted. Denominated in the market's quote token, which is not always USDC |
| `taker_fee_usd`           | `DOUBLE`                      | Taker-leg fees                                                                                                           |
| `maker_fee_usd`           | `DOUBLE`                      | Maker-leg fees, negative where rebated                                                                                   |
| `builder_fee_usd`         | `DOUBLE`                      | Front-end / builder-code fee share. A component of `fee_usd`, not an addend                                              |
| `deployer_fee_usd`        | `DOUBLE`                      | HIP-3 deployer fee share. A component of `fee_usd`, not an addend                                                        |
| `forced_count`            | `BIGINT`                      | Matches the engine forced: liquidations and ADL, counted once per match                                                  |
| `forced_volume_usd`       | `DOUBLE`                      | Notional of those matches, directly comparable to `volume_usd`                                                           |
| `open_interest_units`     | `DOUBLE`                      | Open interest at the hour's funding round, in base units. `NULL` when no round covered the market                        |
| `open_interest_usd`       | `DOUBLE`                      | `open_interest_units` priced at the last trade at or before the hour                                                     |
| `open_positions`          | `BIGINT`                      | Distinct accounts holding an open position at the funding round                                                          |
| `funding_rate`            | `DOUBLE`                      | The hour's funding rate as settled, signed                                                                               |
| `funding_rate_annualized` | `DOUBLE`                      | `funding_rate * 24 * 365`                                                                                                |
| `funding_paid_usd`        | `DOUBLE`                      | Funding that changed hands in the hour, as a positive magnitude                                                          |
| `_updated_at`             | `TIMESTAMP(3) WITH TIME ZONE` | Build timestamp                                                                                                          |

## Why hourly, and when a row exists

Funding is one global round per hour that covers every market and every account at a single block, so an hour is one clean position snapshot — there is no finer cadence at which open interest is well defined.

A row exists if the market had **either** a trade **or** an open position in the hour. That is why an illiquid market with zero trades still reports open interest: the trade columns are zero-filled (`volume_usd = 0`, `trades_count = 0`) while the candles and `vwap` stay `NULL`, because there was no price.

The newest hour is partial by construction. Trades are still accumulating, and `open_interest_*`, `open_positions` and `funding_*` stay `NULL` until the top-of-hour round clears ingestion, observed at 30–75+ minutes. Filter `block_hour < date_trunc('hour', now())` for complete hours only.

## Flows and stocks have different history floors

Flow columns are aggregated from `perp_trades`, which starts 2025-07-27. Stock columns — `open_interest_units`, `open_interest_usd`, `open_positions`, `funding_rate`, `funding_rate_annualized`, `funding_paid_usd` — come from the venue's hourly funding events, which start 2025-09-27 (both floors measured 2026-08-21). Rows in between carry trade columns only, with every stock column `NULL`.

<Warning>
  Open interest, open position counts and funding rates cannot be reconstructed from fills under any circumstances — a fill records a change in a position, never the standing position of every account. There is no backfill for the two-month gap.
</Warning>

## Open interest is published both-sides

`open_interest_units` is longs plus shorts, matching what the Hyperliquid UI and API publish. Classical one-sided open interest is exactly half of it.

`open_interest_usd` prices those units at the market's last trade at or before the hour, carried forward on hours the market did not trade. The Hyperliquid UI prices open interest at mark instead, so the two differ by the trade-to-mark basis.

## Funding direction and magnitude

`funding_rate` is the rate as it settled for that hour, not the forward-looking rate shown beside the funding countdown in the UI, which prices the round about to close. A long pays at a positive rate.

`funding_paid_usd` is the magnitude that changed hands, taken from the paying side. The receiving side is the same figure — open interest is balanced, so funding nets to zero within a market — which means the column is a gross flow, not a net, and direction has to come from the sign of `funding_rate`.

## Classification and administrative fills

Curated metadata (`market_category`, `asset_class`, `asset_type`, `underlying_ticker`, `builder_name`) is deliberately not stored here, so that a reclassification never rewrites this table — join `perp_market_details` on `coin` for it.

```sql theme={null}
SELECT
  d.asset_class,
  m.coin,
  SUM(m.volume_usd) AS volume_usd,
  SUM(m.funding_paid_usd) AS funding_paid_usd,
  MAX_BY(m.open_interest_usd, m.block_hour) AS latest_open_interest_usd
FROM hyperliquid.perp_market_metrics_hourly AS m
JOIN hyperliquid.perp_market_details AS d
  ON d.coin = m.coin
WHERE m.block_month >= DATE_TRUNC('month', NOW() - INTERVAL '7' DAY)
  AND m.block_hour >= DATE_TRUNC('hour', NOW()) - INTERVAL '7' DAY
  AND m.block_hour < DATE_TRUNC('hour', NOW())
GROUP BY 1, 2
ORDER BY 3 DESC
LIMIT 50
```

`volume_usd` includes administrative fills — the engine's force-close of every position when a builder DEX delists a market, and vault position netting. Neither is trading, and neither can be excluded at this grain: for a tape without them, aggregate `perp_trades` with `is_taker AND fill_type NOT IN ('settlement', 'vault_netting')`.

<Note>
  For the reporting grain use [`perp_market_metrics_daily`](/data-catalog/curated/perpetuals/hyperliquid/perp-market-metrics-daily), which re-aggregates from fills rather than summing these rows — daily distinct account counts cannot be summed out of hourly ones.
</Note>
