A C++20 SDK for the Hyperliquid DEX API — REST market data, authenticated trading, and real-time WebSocket subscriptions, with native EIP-712 signing.
Hyperliquid is a high-performance on-chain perpetuals and spot DEX. This SDK provides typed C++ wrappers for the full Hyperliquid API surface, mirroring the structure of the official Python SDK:
Info— read-only REST queries for market data and account stateExchange— authenticated order management, leverage, transfers, and sub-accountsWebsocketManager— real-time subscriptions (used internally byInfo::subscribe)
All authenticated actions are signed locally using EIP-712 / secp256k1; your private key never leaves the process.
- Read-only market data (
Info): universe metadata, mid prices, order books, candles, funding history, account state - Authenticated trading (
Exchange): limit/market/trigger orders, bulk ops, cancel, modify, leverage, margin, USD/spot transfers, bridge withdrawal - Real-time WebSocket subscriptions via
Info::subscribewith multi-callback fan-out per channel - Native EIP-712 signing for both L1 actions (orders, leverage) and user-signed actions (transfers) using OpenSSL secp256k1
nlohmann::ordered_json+ msgpack action hashing — byte-for-byte compatible with the Python SDK signing output- Embedded Keccak-256 (Ethereum's pre-FIPS variant — distinct from OpenSSL's SHA3-256)
- C++20; single umbrella include
<hyperliquid/hyperliquid.hpp>; links as a static library
- C++20 compiler (MSVC 2022, GCC 13+, Clang 16+)
- CMake ≥ 3.20
- vcpkg with the required packages installed (see below)
git clone https://github.com/microsoft/vcpkg.git
cd vcpkg
# Linux / macOS
./bootstrap-vcpkg.sh
# Windows (PowerShell)
.\bootstrap-vcpkg.batvcpkg install nlohmann-json openssl gtestslick-net is the HTTP/WebSocket library used by this SDK. Install it through its own distribution and register it in the same vcpkg instance:
vcpkg install slick-netNote: This project uses
find_packagevia the CMake toolchain — novcpkg.jsonis needed. Pass-DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmakeat configure time, or set it permanently in aCMakePresets.json.
# Configure (replace the toolchain path with your vcpkg location)
cmake -S . -B build \
-DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake
# Build all targets
cmake --build build --config DebugTargets produced:
| Target | Output |
|---|---|
hyperliquid |
Static library |
basic_order |
Example executable |
hyperliquid_tests |
Offline unit tests |
hyperliquid_integration_tests |
Live testnet integration tests |
find_package(hyperliquid CONFIG REQUIRED) # or add_subdirectory
target_link_libraries(my_app PRIVATE hyperliquid)#include <hyperliquid/hyperliquid.hpp> // umbrella headerauto info = std::make_shared<hyperliquid::Info>(hyperliquid::TESTNET_API_URL);
// All mid prices
auto mids = info->all_mids();
std::cout << "ETH mid: " << mids["ETH"] << "\n";
// L2 order book
auto book = info->l2_snapshot("BTC");
// book["levels"][0] = bids, book["levels"][1] = asks
// 1-hour candles for the last 24 hours
using namespace std::chrono;
int64_t now_ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
auto candles = info->candle_snapshot("ETH", "1h", now_ms - 86400000LL);auto info = std::make_shared<hyperliquid::Info>(
hyperliquid::TESTNET_API_URL, /*skip_ws=*/true);
hyperliquid::Exchange exchange(
"0xYOUR_PRIVATE_KEY_HEX", // with or without "0x" prefix
hyperliquid::TESTNET_API_URL,
info);
std::cout << "Wallet: " << exchange.wallet_address() << "\n";
// Resting limit buy — 0.01 ETH at $1 100 (GTC)
auto result = exchange.order(
"ETH", /*is_buy=*/true, /*sz=*/0.01, /*limit_px=*/1100.0,
hyperliquid::LimitOrderType{hyperliquid::Tif::Gtc});
// Cancel if resting
if (result.value("status", "") == "ok") {
const auto& statuses = result["response"]["data"]["statuses"];
if (!statuses.empty() && statuses[0].contains("resting")) {
int64_t oid = statuses[0]["resting"]["oid"].get<int64_t>();
exchange.cancel("ETH", oid);
}
}
// Market buy with 2% slippage
auto fill = exchange.market_open("ETH", /*is_buy=*/true, /*sz=*/0.01, /*slippage=*/0.02);
// Close entire ETH position at default 5% slippage
exchange.market_close("ETH");// skip_ws defaults to false — WebSocket connects automatically
auto info = std::make_shared<hyperliquid::Info>(hyperliquid::TESTNET_API_URL);
// Subscribe to ETH order book updates
int sid = info->subscribe(
{{"type", "l2Book"}, {"coin", "ETH"}},
[](const nlohmann::json& msg) {
// msg = {"channel": "l2Book", "data": {"coin": "ETH", "levels": [...]}}
std::cout << msg["data"]["levels"][0].size() << " bids\n";
});
// Multiple callbacks on the same channel are supported
int sid2 = info->subscribe(
{{"type", "l2Book"}, {"coin", "ETH"}},
[](const nlohmann::json& msg) { /* another handler */ });
// Unsubscribe by ID
info->unsubscribe({{"type", "l2Book"}, {"coin", "ETH"}}, sid);
info->unsubscribe({{"type", "l2Book"}, {"coin", "ETH"}}, sid2);Construct with:
hyperliquid::Info info(base_url, skip_ws = false);Setting skip_ws = true disables the WebSocket connection (REST-only mode, lower overhead).
| Method | Description |
|---|---|
meta() |
Perpetuals universe metadata (names, size decimals, max leverage, …) |
spot_meta() |
Spot tokens and spot universe |
all_mids() |
All perpetuals mid prices as {"BTC": "105000.0", "ETH": "2500.5", …} |
l2_snapshot(coin) |
Full L2 order book snapshot; levels[0] = bids, levels[1] = asks |
candle_snapshot(coin, interval, start_ms) |
OHLCV candles from start_ms to now |
candle_snapshot(coin, interval, start_ms, end_ms) |
OHLCV candles in a time window |
perp_asset_ctxs() |
[meta, assetCtxs] — funding rate, OI, mark price for every perp |
spot_asset_ctxs() |
[meta, assetCtxs] for spot markets |
funding_history(coin, start_ms) |
Historical funding rates from start_ms |
funding_history(coin, start_ms, end_ms) |
Historical funding rates in a window |
Candle interval values: "1m" "5m" "15m" "30m" "1h" "4h" "8h" "12h" "1d" "3d" "1w"
| Method | Description |
|---|---|
user_state(address) |
Full account state: margin summary, cross/isolated positions |
open_orders(address) |
All open orders |
user_fills(address) |
Complete fill history |
user_fills_by_time(address, start_ms) |
Fills since start_ms |
user_fills_by_time(address, start_ms, end_ms) |
Fills in a window |
query_order_by_oid(address, oid) |
Single order status by order ID |
query_order_by_cloid(address, cloid) |
Single order status by client order ID |
sub_accounts(address) |
Sub-account list |
| Method | Description |
|---|---|
subscribe(sub_json, callback) |
Subscribe to a channel; returns subscription_id (positive int) |
unsubscribe(sub_json, subscription_id) |
Remove one callback; double-unsubscribe is a no-op |
load_meta() |
Populate coin_to_asset (called automatically by Exchange) |
coin_to_asset is a public std::unordered_map<std::string, int> mapping coin names to 0-based asset indices.
Construct with:
hyperliquid::Exchange exchange(
private_key_hex, // "0x..." or bare 64 hex chars
base_url, // MAINNET_API_URL or TESTNET_API_URL
info, // shared_ptr<Info>
vault_address); // optional sub-account addressThe constructor derives wallet_address() from the private key and calls info->load_meta().
All methods return nlohmann::json with the raw Hyperliquid API response.
| Method | Description |
|---|---|
order(coin, is_buy, sz, limit_px, order_type, reduce_only, cloid, builder, grouping) |
Place a single order |
bulk_orders(orders, builder, grouping) |
Place multiple orders atomically |
market_open(coin, is_buy, sz, slippage=0.05, cloid) |
IoC limit at mid ± slippage |
market_close(coin, sz={}, slippage=0.05, cloid) |
Close position (size from user_state if omitted) |
| Method | Description |
|---|---|
cancel(coin, oid) |
Cancel a single order by order ID |
bulk_cancel(cancels) |
Cancel multiple orders |
cancel_by_cloid(coin, cloid) |
Cancel by client order ID |
bulk_cancel_by_cloid(cancels) |
Cancel multiple orders by client order ID |
schedule_cancel(time_ms) |
Dead-man's switch — cancels all orders at time_ms (omit for immediate) |
| Method | Description |
|---|---|
modify_order(oid, new_order) |
Replace a resting order |
bulk_modify_orders(mods) |
Replace multiple resting orders |
| Method | Description |
|---|---|
update_leverage(coin, is_cross, leverage) |
Set cross or isolated leverage |
update_isolated_margin(coin, is_buy, ntl) |
Add/remove USD from an isolated position |
| Method | Description |
|---|---|
usd_class_transfer(amount, to_perp) |
Move USDC between spot and perpetuals vault |
usd_transfer(amount, destination) |
Send USDC to another address |
spot_transfer(amount, destination, token) |
Send spot tokens to another address |
withdraw_from_bridge(amount, destination) |
Initiate L1 bridge withdrawal |
| Method | Description |
|---|---|
approve_agent(agent_address, agent_name="") |
Authorize an agent key |
approve_builder_fee(builder, max_fee_rate) |
Approve builder fee (rate as "0.001%" string) |
| Method | Description |
|---|---|
create_sub_account(name) |
Create a named sub-account |
sub_account_transfer(usd, to_sub, sub_account_user) |
Transfer USDC between main and sub |
hyperliquid::MAINNET_API_URL // "https://api.hyperliquid.xyz"
hyperliquid::TESTNET_API_URL // "https://api.hyperliquid-testnet.xyz"
hyperliquid::LOCAL_API_URL // "http://localhost:3001"hyperliquid::Tif::Gtc // Good till cancel
hyperliquid::Tif::Ioc // Immediate or cancel
hyperliquid::Tif::Alo // Add liquidity only (post-only)// Standard limit order
hyperliquid::LimitOrderType{hyperliquid::Tif::Gtc}
// Stop / TP-SL trigger
hyperliquid::TriggerOrderType{
.trigger_px = 2000.0,
.is_market = true, // true = market fill on trigger, false = limit
.tpsl = "sl", // "tp" or "sl"
}OrderType is std::variant<LimitOrderType, TriggerOrderType>.
auto cloid = hyperliquid::Cloid::from_int(42);
auto cloid = hyperliquid::Cloid::from_str("0x0000000000000000000000000000002a");hyperliquid::BuilderInfo{
.b = "0xBUILDER_ADDRESS",
.f = 10, // tenths of basis points (10 = 1 bps = 0.01%)
}Subscription objects follow the Python SDK's JSON format:
| Subscription | Channel identifier | Description |
|---|---|---|
{"type":"allMids"} |
allMids |
All mid prices — fires on every top-of-book change |
{"type":"l2Book","coin":"ETH"} |
l2Book:ETH |
Full order book updates |
{"type":"bbo","coin":"ETH"} |
bbo:ETH |
Best bid/offer only |
{"type":"trades","coin":"ETH"} |
trades:ETH |
Public trade feed |
{"type":"candle","coin":"BTC","interval":"1m"} |
candle:BTC,1m |
Candle updates |
{"type":"activeAssetCtx","coin":"ETH"} |
activeAssetCtx:ETH |
Funding / mark price updates |
{"type":"userEvents","user":"0x..."} |
userEvents:0x... |
Fills, liquidations, funding |
{"type":"userFills","user":"0x..."} |
userFills:0x... |
Fill stream |
{"type":"orderUpdates","user":"0x..."} |
orderUpdates:0x... |
Order status changes |
{"type":"userFundings","user":"0x..."} |
userFundings:0x... |
Funding payments |
{"type":"userNonFundingLedgerUpdates","user":"0x..."} |
userNonFundingLedgerUpdates:0x... |
Non-funding ledger events |
{"type":"webData2","user":"0x..."} |
webData2:0x... |
Aggregated UI data |
{"type":"activeAssetData","coin":"ETH","user":"0x..."} |
activeAssetData:ETH,0x... |
Per-asset user context |
Note: BBO is a WebSocket-only channel. The REST
Info::bbo()method is not supported by the testnet endpoint.
Each callback receives the full message object:
{
"channel": "l2Book",
"data": {
"coin": "ETH",
"levels": [ ["...bids..."], ["...asks..."] ]
}
}# Using the hardhat test key (no real funds)
./build/Debug/basic_order
# Using your own key
./build/Debug/basic_order 0xYOUR_PRIVATE_KEY_HEXThe example places a resting limit buy on testnet and cancels it if it is resting.
cmake --build build --config Debug --target hyperliquid_tests
ctest --test-dir build -C Debug -R hyperliquid_tests -VCovers: Keccak-256 vectors, EIP-712 signing round-trips, type serialisation (float_to_wire, Cloid, Tif), WebSocket URL conversion, and channel identifier generation.
cmake --build build --config Debug --target hyperliquid_integration_tests
ctest --test-dir build -C Debug -R hyperliquid_integration_tests -VCovers:
- REST (
InfoIntegration, ~36 tests):meta,spot_meta,all_mids,l2_snapshot,candle_snapshot,perp_asset_ctxs,spot_asset_ctxs,funding_history,user_state,open_orders,user_fills,sub_accounts,coin_to_assetconsistency - WebSocket (
WsIntegration, ~18 tests):allMidsandl2Bookmessage delivery, channel field validation, subscription ID uniqueness, multi-callback fan-out, simultaneous channels, unsubscribe delivery stop, subscribe/unsubscribe lifecycle
MIT — Copyright (c) 2026 Slick Quant