Skip to main content
rwa_hyperliquid.perp_positions_hourly is the per-account position surface for RWA perpetuals. Grain: one row per (trader, coin, block_hour) — every account position open in a Hyperliquid HIP-3 RWA market at the single global funding round that clears every market and account each hour. Market classification is already joined on. It is the RWA-scoped subset of hyperliquid.perp_positions_hourly. perp_trades tells you what an account did; this table tells you what it held.

Table schema

A snapshot series, not a position history

Rows exist only for positions open at the top of the hour. A close is the absence of a row in the next hour, never a zero-size row, and a position opened and closed inside the same hour never appears at all. To detect a close, compare the set of (trader, coin) keys between two hours rather than looking for a terminating row. This also means positions are not reconstructible from fills: perp_trades publishes both legs of every match and so is per-account complete, but it reports fills, not what an account held at an hour boundary. RWA history starts 2025-10-13, later than the venue-wide funding feed floor, because RWA HIP-3 markets listed after it.
The newest hour is missing rather than empty until its funding round is ingested. Filter block_hour < date_trunc('hour', now()) for complete data, and never read a zero count at the current hour as zero open interest.

Single-account reads must filter trader_prefix

The physical table this view reads is partitioned on (block_month, trader_prefix). Trino derives no partition value from trader = 0x… on its own, so a trader-only predicate reads every one of the 256 buckets in range instead of one. The bucket is a readable hex prefix rather than a hash precisely so you can state it yourself — it is the address’s first two hex characters, lowercase:
Where the address is not a literal, lower(substr(to_hex(trader), 1, 2)) gives the same value. to_hex returns uppercase and the partition values are lowercase, so an uppercase prefix matches nothing. coin does not prune here. Market-grain questions belong in perp_metrics_hourly, which already carries open interest and funding per market-hour.

Rolling up reproduces open interest

Both tables value a position at the same carried-forward trade price, so aggregating to (coin, block_hour) reproduces perp_metrics_hourly.open_interest_usd and open_positions exactly:
That sum counts both sides — every long is matched by a short — which is the convention the Hyperliquid UI uses. Classical one-sided open interest is half of it. Use perp_metrics_hourly when the market-level figure is all you need; this table is for when you need to decompose it by account. Because valuation_price is a trade price rather than a mark price, a position’s notional differs from the Hyperliquid UI’s mark-priced value by the trade-to-mark basis.

Funding

funding_amount_usd is the payment the venue actually settled on that position in that round, not a rate applied to a notional you compute yourself — use it directly for per-account funding cost. A long pays (negative) when funding_rate is positive and receives when it is negative; a short is the mirror. funding_amount_usd = 0 means the payment rounded to zero on a live position, not a disabled market. funding_rate repeats on every row of a (coin, block_hour), so average it per market-hour rather than per position. It is the rate as settled, not the forward-looking rate shown beside the Hyperliquid funding countdown.

Classification is joined live

asset_class, asset_type, and underlying_ticker come from rwa_hyperliquid.markets on every read, so reclassifying a market lands immediately with nothing to backfill. RWA scope is that classification itself, and it is default-deny: a market appears only once its underlying is classified as real-world. Crypto, crypto-dominance indices, GPU-compute indices, and any market that has listed but not been classified are all absent — a market missing here is unclassified, not pending. fx is in scope.
Entry price, unrealized PnL, and an account’s leverage and margin mode are not here — perp_trades carries leverage and margin mode per fill, and market-level max_leverage and margin_mode are in rwa_hyperliquid.markets. Vaults are ordinary accounts here and are not flagged.