Resolved Markets logoResolved Markets logo
Abstract blue gradient background with a soft light streakAbstract blue gradient background with a soft light streak
Data

Perp book × spot reference, one clock

Every Polymarket snapshot carries a spot price stamp; the Hyperliquid feed carries the perp L2 book. Joined on one UTC clock, you get two price views of the same underlying, here is the data structure of that join.

8 min read · Updated Jun 22, 2026

  • Hyperliquid L2Perp feed
  • crypto_priceSpot stamp
  • Nearest PRIORJoin rule
  • Scale tier + Access

The same BTC trades in two places at once: as a perpetual future on Hyperliquid and as a spot reference stamped onto every Polymarket snapshot. Both feeds are recorded in UTC at millisecond precision, so they share a clock. This post is about the shape of the join, which fields line up, how the cadences differ, and the one rule that keeps the merge honest.

This is a data-structure piece, not a strategy. The goal is to describe exactly what sits on each side of the join and how to bring them together without leaking the future into the past. The signal you build on top is yours; the plumbing is what we are documenting here.

Two feeds carry a view of the same underlying. The Hyperliquid exchange feed is the perpetual futures L2 book for BTC, ETH, SOL and XRP, sampled to storage at roughly one record per second. The Polymarket snapshot carries a crypto_price field, a spot reference (BTC from Binance, for example), alongside a crypto_price_age_ms that tells you how fresh that stamp was when the snapshot was taken.

The two sides of the join

Hyperliquid perp book

The perpetual L2 book, captured event-driven and sampled to storage at about one record per second per asset.

  • Perp bid/ask depth
  • ~1/sec to storage
  • BTC · ETH · SOL · XRP

Spot reference stamp

crypto_price rides on every Polymarket snapshot, a spot mark for the same coin, with an age field for freshness.

  • crypto_price
  • crypto_price_age_ms
  • Spot, not perp

One shared clock

Both feeds are UTC at millisecond precision, so timestamps from one can be matched directly against the other.

  • UTC throughout
  • Millisecond precision
  • No timezone math

The cadence mismatch is the whole problem

The two feeds do not tick together. Crypto snapshots arrive at roughly 20 Hz; the Hyperliquid perp record lands about once a second. You cannot expect a perp row for every snapshot, so the join is asymmetric: for each snapshot you reach back to the most recent perp record that already existed, and attach it.

The one rule

Merge on the nearest PRIOR record

For any snapshot at time T, attach the latest perp record whose timestamp is ≤ T, never one stamped after T. Matching to a future perp print would leak information that did not exist yet. Nearest-prior is what keeps the joined panel backtest-safe.

How to assemble the panel

  1. 1Pull the Polymarket snapshots for your market and window, each row already carries crypto_price, crypto_price_age_ms, and the touch and depth fields.
  2. 2Pull the Hyperliquid perp records for the same coin and window from the exchange snapshots feed, one stream of ~1/sec L2 books.
  3. 3Sort both by timestamp, then for each snapshot bind the most recent perp record at or before its timestamp (an as-of, backward join).
  4. 4Carry crypto_price_age_ms through so you can later drop or down-weight rows where the spot stamp was stale when captured.

The fields that line up

  • timestampUTC ms, the join key on both feeds
  • crypto_priceSpot reference on every snapshot
  • crypto_price_age_msStaleness of that spot stamp
  • best_bidbest_askPerp touch from the exchange L2 book

Once joined, a single row holds three price views of the same coin: the Polymarket implied probability, the spot reference it was stamped with, and the perpetual book’s touch. That is the structure that lets you compare how the prediction market, spot, and perp move relative to one another, without ever re-fetching or re-aligning by hand.

Honest limits of the join

  • The perp feed is sampled to ~1/sec for storage, so between samples you are holding the last known book, fine for a reference, not a tick-by-tick perp replay.
  • crypto_price is a spot mark, not the perp mid; treating them as interchangeable hides the basis you may actually want to study.
  • crypto_price_age_ms is there for a reason, a snapshot can carry a spot stamp that was already a little stale, and a strict pipeline filters on it.
  • Exchange data is a Scale-tier-and-up capability; the spot reference on snapshots is part of the crypto dataset itself.
Two feeds, one clock. The discipline is not in matching them, it is in never matching a snapshot to a record that had not happened yet.

Build the joined panel

The Hyperliquid data page shows the perp coverage; the docs show the exchange snapshots endpoint and the snapshot fields you join on.

Frequently asked questions