Install the hyperliquid-rust-sdk
masterTo add the Hyperliquid Rust SDK to your project, use cargo add to include the crate in your Cargo.toml dependencies.
cargo add hyperliquid_rust_sdkrepository·master·Indexed 19 days ago
https://github.com/hyperliquid-dex/hyperliquid-rust-sdkA Rust client for interacting with the Hyperliquid decentralized exchange (version 0.6.0). The SDK provides the ExchangeClient for executing trades, managing orders, updating leverage, claiming rewards, and performing bridge withdrawals, as well as the InfoClient for querying market data, user states, and historical information. It also includes a MarketMaker abstraction for implementing automated liquidity strategies.
To add the Hyperliquid Rust SDK to your project, use cargo add to include the crate in your Cargo.toml dependencies.
cargo add hyperliquid_rust_sdkThe repository contains several usage examples located in the src/bin directory. You can execute any specific example using cargo run --bin followed by the name of the example binary.
cargo run --bin [EXAMPLE]The SDK uses several metadata structures to represent the market universe and asset-specific context. These are typically deserialized from API responses to provide information about available assets, their leverage, decimals, and market statistics like funding or open interest.
Meta: Contains a universe of AssetMeta objects.SpotMeta: Contains a universe of SpotAssetMeta and a list of TokenInfo.AssetMeta: Defines properties for a single asset, including name, sz_decimals, max_leverage, and an optional only_isolated flag.SpotAssetMeta: Defines a spot trading pair, including the tokens involved (as indices), the pair name, its index, and whether it is is_canonical.TokenInfo: Provides details for individual tokens, such as name, sz_decimals, wei_decimals, index, token_id, and is_canonical status.These structures provide real-time or historical market context for assets:
AssetContext: Used for perpetual/standard assets. Includes day_ntl_vlm (net volume), funding, impact_pxs, mark_px, mid_px, open_interest, oracle_px, prev_day_px, and optional premium.SpotAssetContext: Used for spot assets. Includes day_ntl_vlm, mark_px, mid_px, prev_day_px, circulating_supply, and coin.Note: All context fields use camelCase during deserialization.
InfoClient::subscribe method with the Subscription::L2Book variant. You must provide a coin identifier (e.g., `You can subscribe to real-time active asset data using the InfoClient::subscribe method. This requires a Subscription::ActiveAssetData variant, which takes a user address and a coin symbol. The method returns a subscription_id used for unsubscribing and requires an mpsc::UnboundedSender to stream incoming Message::ActiveAssetData updates.
To stop receiving updates, call info_client.unsubscribe(subscription_id) using the ID returned during subscription.
use alloy::primitives::address;
use hyperliquid_rust_sdk::{BaseUrl, InfoClient, Message, Subscription};
use tokio::sync::mpsc::unbounded_channel;
#[tokio::main]
async fn main() {
let mut info_client = InfoClient::new(None, Some(BaseUrl::Testnet)).await.unwrap();
let user = address!("0xc64cc00b46101bd40aa1c3121195e85c0b0918d8");
let coin = "BTC".to_string();
let (sender, mut receiver) = unbounded_channel();
// Start subscription
let subscription_id = info_client
.subscribe(Subscription::ActiveAssetData { user, coin }, sender)
.await
.unwrap();
// Handle incoming messages
while let Some(Message::ActiveAssetData(active_asset_data)) = receiver.recv().await {
println!("Received active asset data: {:?}", active_asset_data);
}
}You can transfer assets between different classes (e.g., from Spot to Perps) using the class_transfer method on an ExchangeClient instance.
Parameters:
amount: The amount of the asset to transfer (e.g., 1.0).to_perp: A boolean flag. Set to true to transfer to the Perps class, or false to transfer to the Spot class.metadata: An optional parameter (passed as None in the example) for additional transfer context if required by the API.This method is asynchronous and returns a result that indicates the success or failure of the transfer.
use alloy::signers::local::PrivateKeySigner;
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient};
use log::info;
#[tokio::main]
async fn main() {
env_logger::init();
// Initialize the signer with a private key
let wallet: PrivateKeySigner = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e"
.parse()
.unwrap();
// Initialize the ExchangeClient
let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None)
.await
.unwrap();
let usdc = 1.0; // Amount to transfer
let to_perp = false; // false = Spot, true = Perps
let res = exchange_client
.class_transfer(usdc, to_perp, None)
.await
.unwrap();
info!("Class transfer result: {res:?}");
}To cancel an active order, use the cancel method on an ExchangeClient. You must provide a ClientCancelRequest which specifies the asset and the order ID (oid) obtained from the original order response.
oid (Order ID) can be extracted from the ExchangeDataStatus returned after placing an order (e.g., from Filled or Resting statuses).let cancel = ClientCancelRequest {
asset: "ETH".to_string(),
oid,
};
// This response will return an error if order was filled,
// otherwise it will cancel the order
let response = exchange_client.cancel(cancel, None).await.unwrap();To interact with the Hyperliquid exchange for spot trading, use the ExchangeClient. You can place limit orders by constructing a ClientOrderRequest and calling .order(). After placing an order, you can cancel it using a ClientCancelRequest which requires the order ID (oid) obtained from the initial order response.
Note that canceling an order that has already been filled will result in an error.
use alloy::signers::local::PrivateKeySigner;
use hyperliquid_rust_sdk::{BaseUrl, ClientCancelRequest, ClientLimit, ClientOrder, ClientOrderRequest, ExchangeClient, ExchangeDataStatus, ExchangeResponseStatus};
#[tokio::main]
async fn main() {
let wallet: PrivateKeySigner = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e".parse().unwrap();
// Initialize the client (using Testnet in this example)
let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None).await.unwrap();
// 1. Define a Limit Order
let order = ClientOrderRequest {
asset: "XYZTWO/USDC".to_string(),
is_buy: true,
reduce_only: false,
limit_px: 0.00002378,
sz: 1000000.0,
cloid: None,
order_type: ClientOrder::Limit(ClientLimit {
tif: "Gtc".to_string(),
}),
};
// 2. Place the order
let response = exchange_client.order(order, None).await.unwrap();
// Handle response to extract the Order ID (oid)
let response_data = match response {
ExchangeResponseStatus::Ok(exchange_response) => exchange_response,
ExchangeResponseStatus::Err(e) => panic!("error with exchange response: {e}"),
};
let oid = match response_data.data.unwrap().statuses[0].clone() {
ExchangeDataStatus::Filled(order) => order.oid,
ExchangeDataStatus::Resting(order) => order.oid,
_ => panic!("Unexpected status"),
};
// 3. Cancel the order
let cancel = ClientCancelRequest {
asset: "XYZTWO/USDC".to_string(),
oid,
};
let cancel_response = exchange_client.cancel(cancel, None).await.unwrap();
println!("Order potentially cancelled: {cancel_response:?}");
}You can subscribe to real-time candle (OHLCV) data using the InfoClient::subscribe method. This requires providing a Subscription::Candle variant which specifies the coin (e.g., "ETH") and the interval (e.g., "1m").
To receive updates, you must pass an unbounded_channel sender to the subscribe method. The method returns a subscription_id which can be used later with unsubscribe(subscription_id) to stop receiving data for that specific subscription.
use hyperliquid_rust_sdk::{BaseUrl, InfoClient, Message, Subscription};
use tokio::sync::mpsc::unbounded_channel;
#[tokio::main]
async fn main() {
let mut info_client = InfoClient::new(None, Some(BaseUrl::Mainnet)).await.unwrap();
let (sender, mut receiver) = unbounded_channel();
// Subscribe to 1m ETH candles
let subscription_id = info_client
.subscribe(
Subscription::Candle {
coin: "ETH".to_string(),
interval: "1m".to_string(),
},
sender,
)
.await
.unwrap();
// Handle incoming messages
while let Some(Message::Candle(candle)) = receiver.recv().await {
println!("Received candle data: {:?}", candle);
}
}To close an existing position using a market order, use the market_close method on an ExchangeClient with MarketCloseParams.
asset: The asset symbol.sz: The size to close. If set to None, the entire position for that asset will be closed.px: Price (set to None for market orders).slippage: Optional slippage tolerance.cloid: Client Order ID (optional).wallet: Wallet address (optional).Similar to opening orders, check ExchangeResponseStatus::Ok and then inspect the ExchangeDataStatus within the response to see if the close order was Filled or is Resting.
let market_close_params = MarketCloseParams {
asset: "ETH".to_string(),
sz: None, // Passing None closes the entire position
px: None,
slippage: Some(0.01),
cloid: None,
wallet: None,
};
let response = exchange_client
.market_close(market_close_params)
.await
.unwrap();You can subscribe to real-time updates for a specific asset's context (such as funding rates, open interest, etc.) using the InfoClient::subscribe method.
InfoClient with a BaseUrl (e.g., BaseUrl::Testnet or BaseUrl::Mainnet).tokio::sync::mpsc::unbounded_channel) to receive messages..subscribe() passing a Subscription::ActiveAssetCtx { coin } variant and the channel's sender. This returns a subscription_id.Message::ActiveAssetCtx variant..unsubscribe(subscription_id) using the ID returned during subscription.use hyperliquid_rust_sdk::{BaseUrl, InfoClient, Message, Subscription};
use tokio::sync::mpsc::unbounded_channel;
#[tokio::main]
async fn main() {
let mut info_client = InfoClient::new(None, Some(BaseUrl::Testnet)).await.unwrap();
let coin = "BTC".to_string();
let (sender, mut receiver) = unbounded_channel();
// Subscribe to the active asset context for a specific coin
let subscription_id = info_client
.subscribe(Subscription::ActiveAssetCtx { coin }, sender)
.await
.unwrap();
// Example: Unsubscribe after 30 seconds
tokio::spawn(async move {
tokio::time::sleep(tokio::time::Duration::from_secs(30)).await;
info_client.unsubscribe(subscription_id).await.unwrap()
});
// Process incoming messages
while let Some(Message::ActiveAssetCtx(active_asset_ctx)) = receiver.recv().await {
println!("Received active asset ctx: {:?}", active_asset_ctx);
}
}To receive real-time order updates for a specific user, use the InfoClient::subscribe method with the Subscription::OrderUpdates variant. You must provide the user's wallet address and an unbounded_channel sender to receive the messages.
To stop receiving updates, call info_client.unsubscribe(subscription_id) using the ID returned by the initial subscription call. The receiver loop will terminate once the subscription is closed.
use alloy::primitives::address;
use hyperliquid_rust_sdk::{BaseUrl, InfoClient, Message, Subscription};
use tokio::sync::mpsc::unbounded_channel;
#[tokio::main]
async fn main() {
let mut info_client = InfoClient::new(None, Some(BaseUrl::Testnet)).await.unwrap();
let user = address!("0xc64cc00b46101bd40aa1c3121195e85c0b0918d8");
let (sender, mut receiver) = unbounded_channel();
// Subscribe to order updates
let subscription_id = info_client
.subscribe(Subscription::OrderUpdates { user }, sender)
.await
.unwrap();
// Handle incoming messages
while let Some(Message::OrderUpdates(order_updates)) = receiver.recv().await {
println!("Received order update data: {order_updates:?}");
}
}