Skip to main content

Module hyperliquid

Module hyperliquid 

Source
Expand description

Hyperliquid ExecutionClient implementations for perpetual futures and spot trading.

Uses the official hyperliquid_rust_sdk crate for REST and WebSocket API access. Gated behind the “hyperliquid” feature flag.

§Modules

§Authentication

Hyperliquid uses EVM-based authentication (Ethereum private key + EIP-712 signatures) instead of traditional API key/secret. The SDK handles all signing internally.

§Architecture

  • REST (InfoClient): account_snapshot, fetch_balances, fetch_open_orders, fetch_trades. fetch_trades posts userFillsByTime to /info through the SDK’s HTTP client but parses the response into common::UserFill rather than the SDK’s type, which drops tid and feeToken.
  • REST (ExchangeClient): open_order, cancel_order
  • WebSocket (InfoClient with with_reconnect): account_stream via UserFills + OrderUpdates + UserFundings subscriptions
  • REST (InfoClient), after each reconnect and through OrderStatusClient: how the orders held as live ended, by openOrders and orderStatus

§SDK Delegation Model

This client delegates connection management to the official hyperliquid_rust_sdk:

ResponsibilityImplementation
ReconnectionInfoClient::with_reconnect() handles WebSocket reconnection automatically, every 1 s with no limit on attempts (see below)
HeartbeatSDK manages WebSocket ping/pong internally
DeduplicationThis client’s own, on the fills and funding payments. See below and Funding.
Fill recoveryOnly the recent fills the venue’s userFills snapshot redelivers on each resubscription — use ExecutionClient::fetch_trades for older ones
Ended-order recoveryThis client’s own, after each reconnect: see HyperliquidClient’s account_stream and OrderStatusClient

The SDK’s reconnection has no limit, so a lost connection never ends the account stream: the SDK (hyperliquid_rust_sdk 0.6) retries it once a second until it succeeds, and this client sets no budget of its own. The stream ends, with StreamTerminated carrying StreamTerminationReason::Error, only if the SDK closes a subscription’s channel. Giving up on a venue that stays unreachable is the caller’s decision.

Caller responsibilities:

  • If fill recovery is critical, monitor for reconnection events and call fetch_trades()
  • REST clients (InfoClient::new(), ExchangeClient::new()) do NOT auto-reconnect; only WebSocket streams via with_reconnect() do

§Trade identity and duplicate fills

A Trade’s id is the venue’s tid, which identifies the fill. It is deliberately not hash, which identifies the transaction: one aggressive order sweeping several resting orders produces several fills under a single hash, and keying on the hash makes them indistinguishable. Hyperliquid documents tid as unique per fill but qualified by coin rather than globally, so callers reconciling across instruments should qualify it the same way.

The fills subscription opens with a snapshot of recent fills and the SDK resubscribes on every reconnect, so the same fill is redelivered each time the socket comes back. A trade is a delta the consumer accumulates, so this client deduplicates the stream itself — the cache is scoped to one ExecutionClient::account_stream call and survives reconnects within it.

Order updates are deliberately not deduplicated. They assert absolute state rather than a delta, so a replayed one is idempotent while a dropped one could strand a consumer on stale state.

ExecutionClient::fetch_trades does not deduplicate: it answers the window it was asked for, and its results reach the caller rather than the stream. A caller feeding both into one consumer should expect overlap and reconcile on the trade id.

§Perpetual and spot coins

Hyperliquid reports every market a wallet trades through the same account streams and open-order and fill endpoints, each order and fill under its coin. Each client keeps only its own, judged by CoinKind::of:

A coin of a kind neither recognises, such as an outcome coin (#12), is left out by both, and logged with warn! by the perpetuals client.

§HIP-3 perpetuals

Besides its default perpetuals DEX, Hyperliquid hosts builder-deployed (HIP-3) DEXs, each with its own perpetuals, named {dex}:{asset} (xyz:TSLA), its own margin, and its own collateral token, which need not be USDC. HyperliquidClient trades the default DEX and the HIP-3 DEXs named in HyperliquidConfig::dexes, which it reads when it connects.

  • Names. Every perpetual is named after the token its DEX settles in: {coin}-{collateral}-PERP, such as BTC-USDC-PERP, xyz:TSLA-USDC-PERP and flx:TSLA-USDH-PERP. An order, cancel or filtered read on any other name, a perpetual on an unconfigured DEX included, is refused with ApiError::InstrumentInvalid.
  • Unconfigured DEXs. Their fills and orders still arrive on the account stream and in the venue’s listings, but without the DEX’s metadata they cannot be named, so they are left out, logged at warn once per DEX.
  • New listings. The SDK addresses an order by the asset ids read at connect, so a perpetual listed after that, on any DEX, cannot be ordered until the client connects again.
  • Balances. Where collateral is held depends on the account’s abstraction mode; see account_snapshot.
  • Cost. Connecting reads perpDexs, spotMeta and each configured DEX’s meta (weight 20 each) when any DEX is configured. Each read of open orders or positions makes one request per DEX, the default one included: Hyperliquid lists a HIP-3 DEX’s only when asked for it. Fills need no such request.

§Client order ids

Every order must be placed under a client id in ClientOrderId::uuid() form: a UUID, lowercase and hyphenated. Any other id is refused at placement with OrderError::Rejected, before anything is sent.

Hyperliquid tracks an order by its venue oid and by an optional 16-byte cloid, and reports the cloid back on every order record, REST and WebSocket alike. This client sends the client id as the cloid and turns the one reported back into the same id, so every account snapshot, open-order listing and order update names an order by the id it was placed with. Only that form of UUID comes back unchanged, which is why nothing else is accepted.

An order placed some other way, such as the web app, has no cloid and is reported under its oid. It cannot be one of this client’s orders, which is what lets an account snapshot declare its order list complete (see InstrumentAccountSnapshot::orders_complete).

§Conditional Orders (Stop, TakeProfit)

Hyperliquid supports conditional (trigger) orders via the ClientOrder::Trigger variant.

Supported order kinds:

  • Stop → market order triggered at stop price (tpsl: "sl", is_market: true)
  • StopLimit → limit order triggered at stop price (tpsl: "sl", is_market: false)
  • TakeProfit → market order triggered at take-profit price (tpsl: "tp", is_market: true)
  • TakeProfitLimit → limit order triggered at take-profit price (tpsl: "tp", is_market: false)

Unsupported: TrailingStop, TrailingStopLimit, TrailingStopFixedLimit (Hyperliquid does not support trailing stops)

SDK limitations (hyperliquid_rust_sdk 0.6.x): neither the open orders this client reads nor OrderUpdate carry an order’s trigger fields or time in force, so orders from both are reported as OrderKind::Limit, GoodUntilCancelled. Track OrderKind from the placement request.

§Funding

Hyperliquid settles perpetual funding every hour, as a payment into or out of the collateral that margins the position. The perpetuals account stream reports each payment, from the userFundings subscription, as an AccountEventKind::CashFlow of CashFlowKind::Funding:

  • amount is Hyperliquid’s usdc, signed positive when the account received it. Its asset is the collateral of the perpetual’s DEX. Hyperliquid names the field usdc on every DEX, and that it is in the collateral on a HIP-3 DEX settling in another token is this client’s reading, not documented. rate is fundingRate and position_quantity is the signed szi.
  • Hyperliquid gives a payment no id, so id is None. The instrument and time_exchange identify it.
  • The subscription opens with a snapshot of recent payments, so a stream’s first events can predate it: filter on time_exchange to take only new ones. The SDK resubscribes on every reconnect and the snapshot comes again, so this client sends each payment once per ExecutionClient::account_stream call, as it does fills. Hyperliquid does not document how far back the snapshot reaches, so payments made during a long outage can be missing; Hyperliquid’s userFunding history has them.
  • The spot client’s stream carries none.

Under the standard account mode a DEX’s balance is its accountValue, which includes the funding paid and received, and the positions’ unrealised PnL too, so adding payments to a balance read after them counts them twice. Under a unified account or portfolio margin a balance is the collateral token’s spot balance; whether Hyperliquid books funding there at once has not been checked. The stream carries no balance: balances are read only by account_snapshot and fetch_balances.

Positions report funding apart from their PnL:

  • A position’s unrealized_pnl is Hyperliquid’s unrealizedPnl, which comes from price alone.
  • Its carry is the funding since the position opened, from Hyperliquid’s cumFunding.sinceOpen with the sign flipped: Hyperliquid counts funding paid as positive, and carry counts it as negative, as a payment’s amount does.
  • Fills carry no funding, so PnL computed from them excludes it too.

§Limitations

  • Order precision: Hyperliquid limits a size to the asset’s szDecimals decimal places, and a price to 5 significant figures and 6 - szDecimals decimal places (integers always allowed). An order whose quantity, price or trigger price breaks these is refused with OrderError::InvalidPrecision before anything is sent; the client never rounds a value, since that would place an order other than the one requested. Read the rules with HyperliquidClient::order_precision and round first. See precision.

Re-exports§

pub use config::HyperliquidConfig;
pub use config::HyperliquidConfigError;
pub use error::HyperliquidConnectError;
pub use precision::OrderPrecision;

Modules§

common
Shared utilities for Hyperliquid execution clients (perps and spot).
config
Configuration for the Hyperliquid execution client.
error
Error mapping for Hyperliquid SDK errors to rustrade-execution error types.
precision
Hyperliquid’s precision rules for order sizes and prices, and the check an order passes before it is sent.
spot
Hyperliquid spot trading ExecutionClient implementation.

Structs§

HyperliquidClient
Hyperliquid perpetual futures execution client.

Enums§

Network
A Hyperliquid network. Serialized in lower case ("mainnet", "testnet").