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=True routes to api.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 aggressive Ioc limits. Stop and stop-limit map to trigger orders with sl tpsl.
  • 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_orders also produces PartiallyFilled from origSz vs. sz.
  • Funding auto-settles. The 1-hour funding cycle debits or credits USDC directly in clearinghouseState; no extra ledger wiring.

Quickstart: testnet

python
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

TypeOrderAction.order_typeNotes
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_priceTrigger + isMarket.
Stop-limit"stop_limit" with stop_priceTrigger + 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

python
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

CapabilityStatus
submit / cancel / amend / cancel_allShipped
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 USDCHandled by Hyperliquid
Deposits / withdrawalsOut of scope

Related