Hyperliquid adapter
Crypto perps + spot on the Hyperliquid L1. Non-custodial, EIP-712 signed.
horizon.venues.hyperliquid.Hyperliquid wraps the official hyperliquid-python-sdk. Hyperliquid is non-custodial: orders sign in-process with an Ethereum private key (EIP-712). There is no hosted session to maintain.
Tier 1 to 3 code does not change. Construct Hyperliquid(...) only when you intend to place orders.
What you get
- Testnet or mainnet.
testnet=Trueroutes toapi.hyperliquid-testnet.xyz. - Perps and spot.
market_id="BTC"is perp.market_id="SPOT:PURR"is spot. The venue strips the prefix before handing to the SDK. - All Hyperliquid order types. Limit with
Gtc,Ioc,Alo(post-only). Market orders map to aggressiveIoclimits. Stop and stop-limit map totriggerorders withsltpsl. - Non-custodial + gasless. Signing happens locally. Orders cost no gas.
- Agent wallets. Pass
account_address=<main>when signing with an API key (private_key) that is different from the main account. - Status map. Submit response status
resting-> Accepted,filled-> Filled,error-> Rejected.open_ordersalso producesPartiallyFilledfromorigSzvs.sz. - Funding auto-settles. The 1-hour funding cycle debits or credits USDC directly in
clearinghouseState; no extra ledger wiring.
Quickstart: testnet
from horizon.venues.hyperliquid import Hyperliquid
hl = Hyperliquid(testnet=True) # reads hyperliquid.private_key via Secrets
hl.connect()
print(hl.balance()) # accountValue, withdrawable, margin
Credentials: Hyperliquid reads hyperliquid.private_key (hex, with or without 0x). Pass private_key= directly for tests. For agent-wallet setups, also pass account_address=<main_account> or set hyperliquid.account_address in Secrets.
Market ids
BTC perp
ETH perp
SPOT:PURR spot
SPOT:@107 spot (numeric index for HIP-1/HIP-2 deployments)
Order types
| Type | OrderAction.order_type | Notes |
|---|---|---|
| Limit Gtc | "limit" | Default TIF. |
| Limit Ioc | "limit" with tif=TimeInForce.IOC | |
| Limit Alo (post-only) | "limit" with post_only=True | |
| Market | "market" | Requires price (aggressive crossing limit). No native market type on Hyperliquid. |
| Stop | "stop" with stop_price | Trigger + isMarket. |
| Stop-limit | "stop_limit" with stop_price | Trigger + limit. |
reduce_only=True is passed through. client_order_id maps to Hyperliquid’s cloid (16-byte hex); the adapter derives a valid cloid from the trailing 32 hex chars of the supplied id.
Amend
Hyperliquid supports modify_order. The adapter exposes this as amend(order_id, new_quantity=..., new_price=...). Both fields are required (cancel-and-resubmit is simpler for single-field changes).
WebSocket feed
from horizon.data.live import HyperliquidLiveFeed, SubscriptionKind
feed = HyperliquidLiveFeed(
testnet=True,
account_address=hl._address, # or pass Secrets
fill_hook=hl.push_fill,
)
feed.subscribe(["BTC", "ETH"], SubscriptionKind.Trades)
feed.subscribe(["BTC"], SubscriptionKind.OrderBook)
Channels: trades, l2Book, candle, userFills. userFills is auto-subscribed when fill_hook is set.
Rate limits
Per-IP: 1200 weight/minute REST. Per-address: scaled by lifetime trading volume (+10k buffer). WebSocket: 10 connections, 1000 subs, 2000 msgs/min. The venue’s HTTP client already rate-limits per host; the SDK handles its own WS throttling.
Deposits and withdrawals
Out of scope for the adapter. USDC on HyperCore bridges from Arbitrum via the Hyperliquid bridge contract. Deposit gas is ~$0.10 on Arbitrum; withdraw charges a flat 1 USDC fee on the Hyperliquid side.
Status by phase
| Capability | Status |
|---|---|
| submit / cancel / amend / cancel_all | Shipped |
| positions (incl. leverage, liquidation price metadata) | Shipped |
| balance (margin summary, withdrawable) | Shipped |
| drain_fills (REST via user_fills + WS via fill_hook) | Shipped |
| orderbook (l2 snapshot) | Shipped |
| Funding accrual auto-settled into USDC | Handled by Hyperliquid |
| Deposits / withdrawals | Out of scope |