skills/

hyperliquid

Tradingmultichain|#hyperliquid#perpetuals#futures#dex#trading
Target:

Install this skill:

$ npx cryptoskills install hyperliquid

Install all 95 skills:

$ npx cryptoskills install --all

Hyperliquid

Hyperliquid is a perpetual futures DEX running on its own L1 blockchain (HyperBFT consensus). It provides fully on-chain order book matching with sub-second finality, up to 50x leverage on perpetual contracts, and a spot DEX. The API uses EIP-712 wallet signing for authentication — there are no API keys. All trading actions are cryptographically signed by your wallet or an approved agent wallet.

Base URLs:

  • Mainnet: https://api.hyperliquid.xyz
  • Testnet: https://api.hyperliquid-testnet.xyz
  • WebSocket Mainnet: wss://api.hyperliquid.xyz/ws
  • WebSocket Testnet: wss://api.hyperliquid-testnet.xyz/ws

What You Probably Got Wrong

LLMs have stale training data. These are the most common mistakes.

  • "Hyperliquid uses API keys" → There are no API keys. Every exchange action is an EIP-712 signed message from your wallet. You can approve "agent wallets" for automated trading, but the auth mechanism is always wallet signatures — never bearer tokens or API secrets.
  • "Use chain ID 1 or the Arbitrum chain ID for signing" → L1 trading actions (orders, cancels, leverage changes) use chain ID 1337 with a "phantom agent" signing scheme. User-signed actions (agent approval, withdrawals) use chain ID 421614. Getting this wrong produces User or API Wallet does not exist errors with no further explanation.
  • "Prices and sizes are numbers" → All prices and sizes in the Exchange API are strings, not numbers. Sending "p": 50000 instead of "p": "50000" will fail silently or produce incorrect signatures.
  • "Asset IDs are coin symbols" → The Exchange API uses integer asset indices, not symbols. BTC might be 0, ETH might be 1. Query the meta endpoint to get the current universe mapping. Spot assets use 10000 + index.
  • "I can place a $1 order" → Minimum order notional is $10 for perpetuals. Orders below this are rejected with Order must have minimum value of $10.
  • "Rate limit is per-endpoint" → Rate limiting is per-address across all endpoints: 1200 requests/minute base. Trading volume increases your cap. Stale expiresAfter cancellations consume 5x the normal weight.
  • "The Python SDK uses async" → The official hyperliquid-python-sdk is synchronous. It uses requests under the hood. For async, use a community SDK or build your own with aiohttp.
  • "WebSocket subscriptions auto-reconnect" → They do not. You must implement reconnection logic. Missed data during disconnection is not replayed — use the Info REST API to backfill.

L1 Architecture

Hyperliquid runs its own L1 with HyperBFT consensus (modified HotStuff). Key properties:

  • Block time: ~400ms
  • Finality: Single-slot (sub-second)
  • Throughput: 100,000+ orders/second
  • Settlement: Fully on-chain order matching (not off-chain with on-chain settlement)
  • Bridge: Native bridge to Arbitrum for USDC deposits/withdrawals (~5 min, $1 fee)
  • HyperEVM: EVM-compatible execution layer for smart contracts alongside the native DEX

All positions, orders, and liquidations execute on the L1. The API is a gateway to this L1 — not a centralized matching engine.

Authentication: EIP-712 Signing

Hyperliquid uses two signing schemes:

L1 Actions (Trading)

Used for: orders, cancels, leverage changes, margin transfers.

  • Chain ID: 1337
  • Signing scheme: Phantom Agent — the action is hashed, then a phantom agent struct is signed
  • The wallet never signs the raw order; it signs a derived message

User-Signed Actions (Account)

Used for: agent wallet approval, withdrawals, USD transfers.

  • Chain ID: 421614 (Arbitrum Sepolia)
  • Signing scheme: Direct EIP-712 with the action payload

Agent Wallets

You can approve up to 4 agent wallets per account (1 unnamed + 3 named) for automated trading. Agent wallets sign L1 actions on behalf of the master account.

from hyperliquid.exchange import Exchange
from hyperliquid.utils import constants
from eth_account import Account
 
wallet = Account.from_key("0x...")
exchange = Exchange(wallet, constants.MAINNET_API_URL)
 
# Approve an agent wallet for automated trading
approve_result = exchange.approve_agent(agent_address="0xAGENT_ADDRESS", agent_name="my-bot")

Info API (Public Data — No Auth Required)

All Info requests are POST https://api.hyperliquid.xyz/info with a JSON body containing type.

Market Metadata

import requests
 
url = "https://api.hyperliquid.xyz/info"
 
# Universe: list of all perpetual markets with asset indices
meta = requests.post(url, json={"type": "meta"}).json()
for i, asset in enumerate(meta["universe"]):
    print(f"Asset {i}: {asset['name']} — maxLeverage: {asset['maxLeverage']}")

Mid Prices

mids = requests.post(url, json={"type": "allMids"}).json()
btc_mid = mids["BTC"]
eth_mid = mids["ETH"]

L2 Order Book

book = requests.post(url, json={
    "type": "l2Book",
    "coin": "BTC",
    "nSigFigs": 5
}).json()
 
for level in book["levels"][0][:5]:  # top 5 bids
    print(f"Bid: {level['px']} x {level['sz']}")
for level in book["levels"][1][:5]:  # top 5 asks
    print(f"Ask: {level['px']} x {level['sz']}")

Candle Data

import time
 
candles = requests.post(url, json={
    "type": "candleSnapshot",
    "req": {
        "coin": "ETH",
        "interval": "1h",
        "startTime": int(time.time() * 1000) - 86400000,
        "endTime": int(time.time() * 1000)
    }
}).json()
# Returns up to 5000 candles: T, o, h, l, c, v, n

User State (Positions + Margin)

state = requests.post(url, json={
    "type": "clearinghouseState",
    "user": "0xYOUR_ADDRESS"
}).json()
 
margin_summary = state["marginSummary"]
print(f"Account value: {margin_summary['accountValue']}")
print(f"Total margin used: {margin_summary['totalMarginUsed']}")
 
for pos in state["assetPositions"]:
    p = pos["position"]
    print(f"{p['coin']}: size={p['szi']} entry={p['entryPx']} unrealizedPnl={p['unrealizedPnl']}")

Open Orders

orders = requests.post(url, json={
    "type": "frontendOpenOrders",
    "user": "0xYOUR_ADDRESS"
}).json()
 
for o in orders:
    print(f"{o['coin']} {o['side']} {o['sz']}@{o['limitPx']} oid={o['oid']}")

Funding Rates

predicted = requests.post(url, json={"type": "predictedFundings"}).json()
 
history = requests.post(url, json={
    "type": "fundingHistory",
    "coin": "BTC",
    "startTime": int(time.time() * 1000) - 86400000
}).json()

User Rate Limit

rate = requests.post(url, json={
    "type": "userRateLimit",
    "user": "0xYOUR_ADDRESS"
}).json()
# nRequestsUsed, nRequestsCap, nRequestsSurplus, cumVlm

Exchange API (Orders — Requires Signing)

All Exchange requests go to POST https://api.hyperliquid.xyz/exchange with a signed payload.

Python SDK Setup

pip install hyperliquid-python-sdk
from hyperliquid.info import Info
from hyperliquid.exchange import Exchange
from hyperliquid.utils import constants
from eth_account import Account
 
wallet = Account.from_key("0xYOUR_PRIVATE_KEY")
info = Info(constants.MAINNET_API_URL, skip_ws=True)
exchange = Exchange(wallet, constants.MAINNET_API_URL)

Place a Limit Order

result = exchange.order(
    name="ETH",
    is_buy=True,
    sz=0.1,
    limit_px=3000.0,
    order_type={"limit": {"tif": "Gtc"}},
    reduce_only=False
)
print(result)
# {"status": "ok", "response": {"type": "order", "data": {"statuses": [{"resting": {"oid": 123456}}]}}}

Place a Market Order

# market_open uses IOC with slippage tolerance (default 5%)
result = exchange.market_open(
    name="BTC",
    is_buy=True,
    sz=0.01,
    slippage=0.03  # 3% slippage tolerance
)

Place a Trigger Order (Stop-Loss / Take-Profit)

# Stop-loss: sell if price drops to 2800
result = exchange.order(
    name="ETH",
    is_buy=False,
    sz=0.1,
    limit_px=2790.0,  # limit price after trigger
    order_type={"trigger": {
        "triggerPx": "2800",
        "isMarket": True,  # execute as market when triggered
        "tpsl": "sl"       # "sl" for stop-loss, "tp" for take-profit
    }},
    reduce_only=True
)

Batch Orders

orders = [
    {
        "name": "BTC",
        "is_buy": True,
        "sz": 0.01,
        "limit_px": 95000.0,
        "order_type": {"limit": {"tif": "Gtc"}},
        "reduce_only": False
    },
    {
        "name": "ETH",
        "is_buy": True,
        "sz": 0.1,
        "limit_px": 3000.0,
        "order_type": {"limit": {"tif": "Gtc"}},
        "reduce_only": False
    }
]
result = exchange.bulk_orders(orders)

TWAP Orders

# TWAP: execute large order over time to minimize impact
result = exchange.twap_order(
    name="BTC",
    is_buy=True,
    sz=1.0,
    reduce_only=False,
    minutes=30,      # execute over 30 minutes
    randomize=True   # randomize slice timing
)
 
# Cancel a running TWAP
exchange.twap_cancel(twap_id=12345)

Cancel Orders

# Cancel by order ID
exchange.cancel(name="ETH", oid=123456)
 
# Cancel by client order ID
exchange.cancel_by_cloid(name="ETH", cloid="0x00000000000000000000000000000001")

Schedule Cancel (Dead Man's Switch)

Cancels all open orders if no heartbeat received within the timeout. Minimum 5-second delay, maximum 10 triggers per day.

import time
 
result = exchange.schedule_cancel(time=int(time.time() * 1000) + 30000)  # 30s from now

Position Management

Set Leverage

# Cross leverage
exchange.update_leverage(name="BTC", leverage=10, is_cross=True)
 
# Isolated leverage
exchange.update_leverage(name="ETH", leverage=20, is_cross=False)

Adjust Isolated Margin

# Add margin to isolated position (positive = add, negative = remove)
exchange.update_isolated_margin(name="ETH", amount=100.0)

Close Position

# Market close entire position
exchange.market_close(name="BTC")
 
# Or close with a limit order
state = info.user_state("0xYOUR_ADDRESS")
for pos in state["assetPositions"]:
    p = pos["position"]
    if p["coin"] == "ETH":
        size = abs(float(p["szi"]))
        is_long = float(p["szi"]) > 0
        exchange.order(
            name="ETH",
            is_buy=not is_long,
            sz=size,
            limit_px=float(p["entryPx"]) * (1.01 if is_long else 0.99),
            order_type={"limit": {"tif": "Gtc"}},
            reduce_only=True
        )

WebSocket Streaming

Connect to wss://api.hyperliquid.xyz/ws and send JSON subscription messages.

Available Channels

Channel Params Description
allMids dex (optional) All mid prices, real-time
l2Book coin L2 orderbook updates
trades coin Trade prints
candle coin, interval Candle updates
bbo coin Best bid/offer
orderUpdates user Order status changes
userEvents user All user events
userFills user Fill notifications
userFundings user Funding payments
clearinghouseState user, dex Position updates
openOrders user, dex Open order changes
activeAssetCtx coin Market context (funding, OI)
twapStates user, dex TWAP order progress

Subscribe/Unsubscribe Pattern

{"method": "subscribe", "subscription": {"type": "trades", "coin": "BTC"}}
{"method": "subscribe", "subscription": {"type": "l2Book", "coin": "ETH"}}
{"method": "subscribe", "subscription": {"type": "userFills", "user": "0xYOUR_ADDRESS"}}
{"method": "unsubscribe", "subscription": {"type": "trades", "coin": "BTC"}}

Python WebSocket Example

import json
import websocket
import threading
 
def on_message(ws, message):
    data = json.loads(message)
    channel = data.get("channel")
    if channel == "trades":
        for trade in data["data"]:
            print(f"{trade['coin']} {trade['side']} {trade['sz']}@{trade['px']}")
    elif channel == "l2Book":
        book = data["data"]
        best_bid = book["levels"][0][0] if book["levels"][0] else None
        best_ask = book["levels"][1][0] if book["levels"][1] else None
        if best_bid and best_ask:
            print(f"BBO: {best_bid['px']} / {best_ask['px']}")
 
def on_open(ws):
    ws.send(json.dumps({
        "method": "subscribe",
        "subscription": {"type": "trades", "coin": "BTC"}
    }))
    ws.send(json.dumps({
        "method": "subscribe",
        "subscription": {"type": "l2Book", "coin": "BTC"}
    }))
 
def on_error(ws, error):
    print(f"WebSocket error: {error}")
 
def on_close(ws, close_status_code, close_msg):
    print("WebSocket closed, reconnecting...")
    threading.Timer(5.0, connect).start()
 
def connect():
    ws = websocket.WebSocketApp(
        "wss://api.hyperliquid.xyz/ws",
        on_open=on_open,
        on_message=on_message,
        on_error=on_error,
        on_close=on_close
    )
    ws.run_forever()
 
connect()

Rate Limits

Metric Limit
Base request cap 1200/minute per address
Weight per request 1 (standard)
Stale expiresAfter cancel 5x weight
Volume bonus Higher trading volume increases cap
Burst No documented burst limit — sustained rate

Check your current usage:

rate = requests.post("https://api.hyperliquid.xyz/info", json={
    "type": "userRateLimit",
    "user": "0xYOUR_ADDRESS"
}).json()
print(f"Used: {rate['nRequestsUsed']}/{rate['nRequestsCap']}")

You can reserve additional rate limit capacity at 0.0005 USDC per request via the requestWeightReservation exchange action.

Vault Strategies

Vaults are on-chain managed accounts. A vault leader trades with depositors' funds and takes a profit share.

# Deposit into a vault
exchange.vault_transfer(
    vault_address="0xVAULT_ADDRESS",
    is_deposit=True,
    usd=1000.0
)
 
# Query vault details
vault = requests.post("https://api.hyperliquid.xyz/info", json={
    "type": "vaultDetails",
    "vaultAddress": "0xVAULT_ADDRESS"
}).json()
print(f"TVL: {vault['summary']['tvl']}")
print(f"APR: {vault['summary']['apr']}")

Vault leaders trade by passing vault_address to exchange methods — the master account signs on behalf of the vault.

Subaccounts

Subaccounts are separate trading accounts under a master wallet. They have their own positions and margin but no private key — the master account signs for them.

# Create a subaccount
exchange.create_sub_account(name="arb-bot")
 
# Transfer USDC to subaccount
exchange.sub_account_transfer(
    sub_account_user="0xSUB_ADDRESS",
    is_deposit=True,
    usd=5000.0
)
 
# Query subaccounts
subs = requests.post("https://api.hyperliquid.xyz/info", json={
    "type": "subAccounts",
    "user": "0xMASTER_ADDRESS"
}).json()

Liquidation Mechanics

  • Maintenance margin: Varies by asset tier. Positions are liquidated when account margin drops below maintenance requirement.
  • Margin tiers: Larger positions require more margin. A 10x BTC position has lower maintenance margin than a 50x one.
  • Liquidation price: Visible via clearinghouseState — check liquidationPx on each position.
  • Insurance fund: Backstops liquidations that cannot be filled at the bankruptcy price.
  • ADL (Auto-Deleveraging): Last resort when insurance fund is depleted. Profitable positions are reduced proportionally.
state = info.user_state("0xYOUR_ADDRESS")
for pos in state["assetPositions"]:
    p = pos["position"]
    if p.get("liquidationPx"):
        print(f"{p['coin']}: liquidation at {p['liquidationPx']}")

TypeScript Signing Pattern

For TypeScript integrations, use ethers or viem for EIP-712 signing. The SDK @nktkas/hyperliquid provides a typed client.

import { privateKeyToAccount } from "viem/accounts";
import { type Hex, hashTypedData, signTypedData } from "viem";
 
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
 
const EXCHANGE_URL = "https://api.hyperliquid.xyz/exchange";
const INFO_URL = "https://api.hyperliquid.xyz/info";
 
async function queryInfo(body: Record<string, unknown>): Promise<unknown> {
  const res = await fetch(INFO_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  return res.json();
}
 
// Info endpoints require no authentication
const meta = await queryInfo({ type: "meta" }) as { universe: Array<{ name: string }> };
const assetIndex = meta.universe.findIndex((a) => a.name === "ETH");

Key Differences from CEX APIs

Feature CEX (Binance/Bybit) Hyperliquid
Auth API key + secret EIP-712 wallet signature
Order ID Server-assigned OID + optional client order ID (CLOID)
Asset reference Symbol string Integer index from meta
Price/size format Number String
Rate limit Per-endpoint Per-address, all endpoints
Settlement Database updates On-chain L1 transactions
Withdrawals Centralized Bridge to Arbitrum (~5 min)

Official Resources