Message Types
Every server-to-client frame carries a type field that uniquely identifies its shape. Use this as the discriminator in your message handler. JSON Schemas for each type are available in the repository under schemas/.
Universal conventions:
- All
timefields are epoch milliseconds (Date.now()integers). - All
addressfields are lowercase EVM addresses (0x+ 40 hex). - All
chainvalues are lowercase:arbitrum|base|ethereum|unichain|hyperevm. - Numeric fields use float64 unless explicitly typed as decimal string (
amount0,amount1in LP events use strings to preserve uint256 precision). - Prices and sizes are in human units (e.g. WETH/USDC price = USDC per WETH, size = WETH count) unless noted otherwise.
Discriminator table
type | Description | Applicable venues |
|---|---|---|
quote | Top-of-book snapshot | All |
print | Single trade execution | All |
funding | Perpetual funding rate | CLOB perps, oracle perps |
depth | AMM tick-liquidity snapshot + market-impact estimates | AMM spot |
liquidity | LP Mint or Burn event | AMM spot |
rate_market | Yield-market snapshot (APY, PT price, TVL, expiry) | pendle, spectra |
rate_depth | Depth-at-size APY quote | pendle only |
ammbook | Consolidated NBBO across all four Arbitrum AMMs | ammbook virtual |
ammliquidity_snapshot | One-time LP-event backfill on subscribe | ammliquidity virtual |
arbflag | Cross-venue arb-gap edge signal | arbflag virtual (super_admin) |
spread | Same-venue cross-pool spread signal | uni, univ4 |
feed_stale / feed_live | Upstream feed staleness transitions | Broadcast to all |
server_closing | Planned restart notice | Broadcast to all |
authed | API key auth success | Connection-level |
error | Action rejected | Connection-level |
quote — top-of-book snapshot
Emitted on every book update. Shape varies slightly by venue class:
- CLOB venues (
hl,dydx): full depth array.bids[0]= best bid,asks[0]= best ask. - AMM venues: typically one level per fee tier.
OrderLevel.feeis the pool fee in ppm (500 = 0.05%). - Oracle perps (
gmx,vertex,ostium): single-level synthetic mid + spread model.
| Field | Type | Notes |
|---|---|---|
exchange | string | Venue id |
symbol | string | Canonical symbol |
bids | OrderLevel[] | Descending by price |
asks | OrderLevel[] | Ascending by price |
bids[].fee / asks[].fee | integer (ppm) | AMM venues only. 500 = 0.05%. |
bids[].lastSwapMs | integer | AMM venues only. Age of the pool’s last on-chain swap in ms. |
time | integer | Server broadcast timestamp (epoch ms) |
{ "type": "quote", "exchange": "hl", "symbol": "ETH", "bids": [{"price": 3499.5, "size": 12.4}, {"price": 3499.0, "size": 31.1}], "asks": [{"price": 3500.5, "size": 8.7}], "time": 1748275200000 }AMM example with fee tier:
{ "type": "quote", "exchange": "uni", "symbol": "WETH/USDC", "bids": [{"price": 3499.12, "size": 5.2, "fee": 500, "lastSwapMs": 1100}], "asks": [{"price": 3500.88, "size": 5.2, "fee": 500, "lastSwapMs": 1100}], "time": 1748275200000 }print — trade execution
One trade. Emitted on every on-chain swap or HL matching event.
| Field | Type | Notes |
|---|---|---|
exchange | string | Venue id |
symbol | string | |
price | number | Execution price in human units |
size | number | Trade size in base-token units |
side | integer | 0 = bid hit (seller initiated); 1 = ask lift (buyer initiated); 2 = unknown |
time | integer | Epoch ms |
tick | integer | AMM/yield venues only. Pool tick at time of swap. |
blockNumber | integer | On-chain venues only. |
txIndex | integer | On-chain venues only. Use with blockNumber for deduplication. |
logSender | string | AMM venues only. Router/aggregator address that submitted the swap. |
d_* | number | HL and dYdX only. Stat-engine fields — see table below. |
For Pendle/Spectra, side applies to the PT leg: 1 = trade bought PT; 0 = trade sold PT.
Stat-engine fields (d_* — HL and dYdX prints)
These fields are emitted on HL and dYdX V4 prints with the same shape and semantics. They are absent on all other venues.
| Field | Units | Range | Description |
|---|---|---|---|
d_tickrate | trades/sec | [0, ∞) | Trade arrival rate over the last 1 s window |
d_volumerate | contracts/sec | [0, ∞) | Volume velocity over the last 1 s window |
d_quoterate | L2 updates/sec | [0, ∞) | Quote-update velocity over the last 1 s window |
d_hawkes | events/sec | [0, ~1000+] | Hawkes self-exciting intensity (all sides). Treat as elevated when > 5× 30-day mean — not a probability. |
d_hawkesB | events/sec | same | Hawkes intensity, bid-side prints |
d_hawkesA | events/sec | same | Hawkes intensity, ask-side prints |
d_lobimb | dimensionless | [−1, +1] | LOB imbalance from top 10 L2 levels. −1 = all on ask; +1 = all on bid; 0 = balanced |
d_volumeimb | contracts | (−∞, +∞) | Signed volume over last 1 s: buyVol − sellVol. Positive = net buying. |
d_bidqty | contracts | [0, ∞) | Total bid-side quantity in the L2 snapshot |
d_askqty | contracts | [0, ∞) | Total ask-side quantity in the L2 snapshot |
Hawkes parameters: µ=0.1, α=2.0, β=0.0005/ms. The absolute level is venue/symbol-specific; compare against a rolling baseline rather than a fixed threshold.
funding — perpetual funding rate
Emitted approximately every 60 seconds per venue per symbol.
| Field | Type | Notes |
|---|---|---|
ratePct | number | Annualized percentage, signed. +10.95 = longs pay shorts at 10.95%/yr. |
intervalHrs | number | Venue’s native payment cadence. HL/dYdX = 8h; Ostium = 24h; GMX/Vertex = 1h. |
time | integer | Epoch ms |
ratePct is always annualized regardless of intervalHrs, so cross-venue comparisons are direct.
Conversions:
- Per-hour rate (decimal):
ratePct / 100 / (365 * 24) - Per-payment-interval rate:
ratePct / 100 / (365 * 24 / intervalHrs)
{ "type": "funding", "exchange": "hl", "symbol": "ETH", "ratePct": 10.95, "intervalHrs": 8, "time": 1748275200000 }depth — AMM concentrated-liquidity snapshot
AMM venues only (uni, univ4, univ4chain, sushi, pancake). Emitted on every Mint/Burn event, throttled to 200 ms per symbol.
| Field | Type | Notes |
|---|---|---|
currentTick | integer | Active pool tick at snapshot time |
ticks | object[] | Tick boundaries near currentTick. Each has price, liquidityDelta (signed), cumLiquidity. |
impacts | object[] | Pre-computed market-impact estimates at $1k / $10k / $100k / $1M notional |
impacts[].usd | number | Trade notional in USD |
impacts[].buyPrice | number | Effective price when buying at this size |
impacts[].sellPrice | number | Effective price when selling at this size |
impacts[].buyPct | number | % premium paid over mid — always ≥ 0 |
impacts[].sellPct | number | % discount received vs. mid — always ≥ 0 |
{ "type": "depth", "exchange": "uni", "symbol": "WETH/USDC", "currentTick": 196234, "time": 1748275200000, "ticks": [...], "impacts": [ {"usd": 1000, "buyPrice": 3500.35, "sellPrice": 3499.65, "buyPct": 0.020, "sellPct": 0.020}, {"usd": 10000, "buyPrice": 3501.20, "sellPrice": 3498.80, "buyPct": 0.060, "sellPct": 0.060}, {"usd": 100000, "buyPrice": 3505.00, "sellPrice": 3495.00, "buyPct": 0.180, "sellPct": 0.180}, {"usd": 1000000, "buyPrice": 3540.00, "sellPrice": 3460.00, "buyPct": 1.130, "sellPct": 1.130} ]}liquidity — AMM LP Mint/Burn event
One Mint or Burn event on an AMM pool.
| Field | Type | Notes |
|---|---|---|
action | string | "mint" = LP adds; "burn" = LP removes |
feeTier | integer | Pool fee in ppm. 500 = 0.05%. |
tickLower / tickUpper | integer | int24 price range for this position |
amount | string | Raw uint128 liquidity delta (string to preserve precision) |
amount0 / amount1 | string | Raw token amounts in smallest units (wei / 6-decimal USDC). Divide by 10^decimals for human units. |
amountUsd | number | Platform-estimated USD notional. May be 0 if price feed was unavailable. |
owner | string | LP position owner address |
blockNumber / txIndex | integer | On-chain location; use for ordering and deduplication |
{ "type": "liquidity", "exchange": "uni", "symbol": "WETH/USDC", "feeTier": 500, "action": "mint", "tickLower": 195000, "tickUpper": 197000, "amountUsd": 25000, "time": 1748275200000, "amount": "12500000000000000000", "amount0": "3570000000000000000", "amount1": "12500000000", "owner": "0xabc...", "blockNumber": 290845001, "txIndex": 17 }rate_market — yield-market snapshot
Per-market snapshot for Pendle and Spectra. Emitted ~5 s (Pendle) / ~30 s (Spectra).
| Field | Type | Notes |
|---|---|---|
address | string | Unique market key (per chain). Use with GET /v1/history/rates/:address. |
chain | string | arbitrum | base | ethereum |
symbol | string | Human-readable PT symbol (e.g. PT-weETH-25JUN2026) |
impliedApy | number | Implied APY as decimal fraction. 0.131 = 13.1%/yr. |
underlyingApy | number | Underlying asset APY |
lpApy | number | LP fee APY. Always 0 for Spectra v1. |
ptPrice | number | Pendle: USD. Spectra: fraction of IBT (1.0 = at-parity). |
ytPrice | number | YT price in USD (Pendle) |
expiry | integer | PT maturity as epoch ms |
daysToExpiry | number | Calendar days remaining |
tvl | number | Pendle: USD. Spectra v1: underlying asset units (no USD oracle path). |
volume24h | number | 24h swap volume. Always 0 for Spectra v1. |
{ "type": "rate_market", "exchange": "pendle", "chain": "ethereum", "address": "0xabc...", "symbol": "PT-weETH-25JUN2026", "underlyingApy": 0.031, "impliedApy": 0.058, "lpApy": 0.012, "ptPrice": 0.985, "ytPrice": 0.015, "expiry": 1751155200000, "daysToExpiry": 28.5, "tvl": 12500000, "volume24h": 450000, "time": 1748275200000 }rate_depth — yield-market depth-at-size
Pendle only. Emitted per poll cycle for individually-subscribed rate markets. Replaces the traditional price-level DOM for rate venues — “price” is an implied APY and cost of execution is size-conditional.
| Field | Type | Notes |
|---|---|---|
midRate | number | Current mid APY (decimal fraction) |
levels | object[] | Sorted ascending by sizeUsd. Each level has sizeUsd, buyRate, sellRate, spreadBps. |
levels[].spreadBps | number | Total bid-ask spread in APY basis points at that size |
Short-dated markets exhibit wider APY spreads for the same price-impact because a given bps of price impact maps to more APY bps as daysToExpiry shrinks.
ammbook — consolidated AMM NBBO
Cross-venue best-bid / best-ask across the four Arbitrum AMMs (uni, univ4, sushi, pancake). One bid and one ask per contributing venue; bids sorted descending, asks sorted ascending.
arbGap is present only when the highest bid (across venues) exceeds the lowest ask on a different venue — a cross-venue arb gap exists.
{ "type": "ammbook", "symbol": "WETH/USDC", "bids": [ {"exchange": "univ4", "price": 3500.18, "size": 4.1, "feeBps": 3, "time": 1748275200001}, {"exchange": "uni", "price": 3500.15, "size": 3.9, "feeBps": 5, "time": 1748275200001} ], "asks": [ {"exchange": "sushi", "price": 3500.22, "size": 3.5, "feeBps": 5, "time": 1748275200000}, {"exchange": "uni", "price": 3500.30, "size": 4.0, "feeBps": 5, "time": 1748275200001} ], "midPrice": 3500.20, "spreadBps": 0.11, "time": 1748275200001 }ammliquidity_snapshot — LP-event backfill
Sent once on ammliquidity:<symbol> subscribe. Contains up to 1,000 recent liquidity events across all four Arbitrum AMM venues, sorted oldest-first. After this snapshot, subsequent Mint/Burn events arrive as individual liquidity frames.
arbflag — cross-venue arb edge signal
Edge-triggered: one frame on gap open (status: "open"), one on gap close (status: "closed"). Not a continuous stream.
Gated: requires role super_admin.
spread — cross-pool spread (same venue)
Fired when, within a single AMM venue, the best bid in one fee tier exceeds the best ask in another fee tier. Distinct from arbflag (which is cross-venue).
Available on uni and univ4 only.
feed_stale / feed_live
Broadcast to all connected clients on upstream feed staleness transitions. See WebSocket — Heartbeat for thresholds.
{ "type": "feed_stale", "exchange": "hl", "staleSec": 67.4 }{ "type": "feed_live", "exchange": "hl" }server_closing
Sent before a planned restart. See WebSocket — Reconnect.
{ "type": "server_closing", "reconnectIn": 5000 }authed
Reply to a successful {"action":"auth", "key":"mk_live_..."} message.
{ "type": "authed", "tier": "api", "symbolLimit": 100 }symbolLimit is null for super_admin (unlimited).
error
Sent when the server rejects a client action. The connection remains open after most errors; auth_failed may be followed by a close frame.
{ "type": "error", "code": "symbol_limit_reached", "message": "Tier 'none' allows 3 symbols; you have 3." }See Errors for the full code reference and retry guidance.