Hyperliquid¶
kind = "hyperliquid" (alias hl) trades perpetuals on the main dex and on builder-deployed (HIP-3) dexes, with orders over the WebSocket; the example configuration is configs/hyperliquid.toml.
| Markets | perpetuals on the main dex (BTC) and HIP-3 dexes (xyz:NVDA), one-way positions |
| Order entry | WebSocket post actions, REST fallback; orders batched per action |
| Environments | mainnet (testnet = false), testnet (testnet = true) |
| Keys | api_key the account address, api_secret the signing key |
| Replace | batchModify, carried out by the venue as a new order |
| Dead man's switch | scheduleCancel, off by default (dead_mans_switch_s) |
| Example config | configs/hyperliquid.toml |
Keys for the section are in Configuration: hyperliquid.
Connections¶
| Channel | Endpoint | Purpose |
|---|---|---|
| md | ws_url (wss://api.hyperliquid.xyz/ws) |
l2Book (20 levels a side, each push a whole book), bbo, trades per coin |
| private | the same URL, a second connection | orderUpdates, userFills, userFundings of the account address; public, no auth |
| trade | the same URL, a third connection | {"method":"post"} actions order, cancel, cancelByCloid, batchModify; REST fallback |
| rest | rest_url (https://api.hyperliquid.xyz) |
POST /info reads, POST /exchange for the fallback, scheduleCancel and cancel-all |
The POST /info requests are perpDexs, metaAndAssetCtxs, extraAgents, userRateLimit, frontendOpenOrders, clearinghouseState, spotClearinghouseState and userFillsByTime.
Keys and signing¶
| Key | Value |
|---|---|
api_key |
the account's address |
api_secret |
the signing key: an agent wallet the account approved, or the account's own key |
vault_address |
trade for a vault or sub-account of the account |
At start-up load_reference_data() reads extraAgents and refuses a key the account has not approved; fastmm-live exits 3.
An action is signed as Hyperliquid's SDK does: msgpack of the action, the nonce and the vault flag, hashed with keccak256 into an EIP-712 Agent message. The signature is secp256k1 ECDSA with deterministic nonces. Nonces are wall-clock milliseconds, strictly increasing per signer.
Instruments¶
| Field | Value |
|---|---|
| Symbol | the coin with its dex prefix (xyz:NVDA), or without it when dex names the dex |
| Asset id | the index in the main dex's meta; on a HIP-3 dex 100000 + dex index × 10000 + index in its meta |
| Lot | 10^-szDecimals |
| Tick | the step at the current mark: 5 significant figures, at most 6 - szDecimals decimals; an integer price is always valid |
| Minimum notional | 10 USDC |
| Quote | USDC |
The encoder puts every price on the tick grid, a bid rounded down and an ask up, so a price crossing a power of ten stays valid.
Market data¶
Public trades carry side as the taker's (B buys) and tid as the trade id. users is [buyer, seller]: both wallet addresses go into the trade as its parties (buyer and seller in Python). A trade whose users is not two addresses names nobody.
Orders¶
| Engine order | Hyperliquid |
|---|---|
| post-only | Alo |
| IOC, market | Ioc; a market order needs a price |
| any other | Gtc |
| FOK | refused |
The cloid is 0x666d000000000000 followed by the client order id. One engine drain goes out as one action per kind, at most 40 orders each (Batch order entry). The reply answers the orders by position.
A cancel names the order by its oid once a reply or the stream gave it, else by cloid. With supports_replace a replace is a batchModify: its ack has no kAmendedInPlace, and the old order's canceled is not forwarded.
Order events¶
The action reply and orderUpdates both report an order; the connector forwards whichever comes first, once.
| Venue status | Engine event |
|---|---|
resting, filled in the reply |
ack |
| error text in the reply | reject |
success in a cancel reply |
cancel accepted |
open |
ack |
canceled |
cancel ack, origSz - sz filled |
other *Canceled, scheduledCancel |
OrderExpired |
*Rejected |
reject; badAloPxRejected is post-only would cross |
filled |
nothing; fills come on userFills |
A userFills row is a fill: exec id tid, crossed the taker, fee in USDC with a rebate negative. Cumulative and remaining quantity come from the order shadow. The snapshots userFills and userFundings send on subscribing are skipped; the fill replay books what the stream missed.
Reconciliation and fills¶
A reconciliation runs the fill replay, then reads frontendOpenOrders and clearinghouseState of each dex the instruments use (Reconciliation). The replay is userFillsByTime from the watermark: oldest first, 2000 rows a reply, 7 days back (Execution replay).
Balances come from spotClearinghouseState, one row per token: free is total - hold, locked is hold. A unified account keeps every dex's collateral in its spot USDC, so the dexes' clearinghouseState show accountValue 0.
Funding¶
Each userFundings payment becomes a Funding event under <time>:<coin>.
Rate limits¶
| Limit | Scope | Connector |
|---|---|---|
| 1 request per USDC traded since the address's inception, plus 10000 | each new order and modify, per address; growth-mode volume counts 10 %; cancels have a larger limit | reads userRateLimit at start and every rate_limit_refresh_s, counts what it sends, refuses at address_reserve |
| 2000 WebSocket messages a minute | per IP | posts_per_minute (default 1800) |
| 100 posts in flight | per IP | |
| new orders and modifies | orders_per_second (default 20) |
The address budget's cap includes nRequestsSurplus, the requests reserved with reserveRequestWeight. A refusal is VenueRateLimit "local rate limit: address budget", published as the day window of the order budget. A rate-limit error cools down 10 s: an address over its budget may send one request every 10 s.
Dead man's switch¶
scheduleCancel at now + dead_mans_switch_s, over REST, refreshed at a third of the window. Each refresh spends one request of the address budget, and the venue allows 10 triggers a day. disconnect() removes it.
The venue refuses the switch until the address has traded enough; the connector logs the reason once and runs without it. The kill switch reads frontendOpenOrders of each dex and cancels by oid, in signed batches of 40.
Connection health¶
stale_ms defaults to 5000 ms. dead_ms is at least twice ping_interval_ms plus 5000 ms (default 60000 ms); the venue closes a connection that sent nothing for 60 s.
Every latency_log_s the log gives the post reply's round trip, each order's open lag and the address budget. The open lag is the block time of the order's open minus its send time, so it mixes the venue's clock with the host's.
Not handled¶
Spot, TWAP and trigger orders, expiresAfter, the priority fee, leverage and isolated margin changes, and user-signed actions such as transfers and withdrawals.
Sources¶
Read 2026-10-12: Hyperliquid API docs and hyperliquid-python-sdk 0.24.0 for the signing.