Skip to main content

How to Trade HIP-4 Prediction Markets on Hyperliquid

Updated on
Jun 15, 2026

19 min read

Overview

HIP-4 launched on Hyperliquid mainnet on May 2, 2026, adding fully collateralized binary outcome markets that settle to 0 or 1 in USDC. These markets live in HyperCore alongside perpetuals and spot, sharing the same CLOB infrastructure, the same WebSocket streams, and the same data endpoints you use for every other Hyperliquid asset. This guide walks you through the data model first, then takes you step by step through discovering markets, reading and streaming the orderbook, and placing trades using Hyperliquid API, Quicknode's zero-custody REST builder API for Hyperliquid.


TL;DR
  • HIP-4 markets are binary YES/NO contracts that settle to 0 or 1 in USDC and trade on the HyperCore CLOB with zero open fee
  • Each side appears as a #N coin (for example, #20 for YES and #21 for NO) in allMids, datasets, and all streaming APIs
  • Quicknode builds and maintains SDKs for TypeScript, Python, Go, and Rust, plus Hyperliquid API (a zero-custody REST builder API) and gRPC streaming endpoints
  • Full source code for this guide is in qn-guide-examples
  • SQL Explorer lets you query historical HIP-4 trades and orders with plain SQL

What You Will Do

  • Understand the HIP-4 data model: #N coin notation, outcomeMeta, allMids, and the trades/orders/book-updates datasets
  • Set up your project and connect to the hyperliquidapi.com REST API
  • Approve the builder fee (one-time per wallet)
  • Discover active prediction markets and inspect YES/NO side shapes
  • Read and stream the orderbook for both sides in real time
  • Fund your Hyperliquid spot account with USDC
  • Place limit and market orders, cancel resting orders, and handle common errors

What You Will Need

  • Node.js 20+ and a package manager (npm, pnpm, or yarn)
  • A Hyperliquid wallet private key and at least some USDC in your spot balance for trading
  • Familiarity with TypeScript and basic prediction market concepts

What Are HIP-4 Outcome Markets?

HIP-4 introduces binary outcome markets to HyperCore. Each market poses a yes/no question (for example, "Will BTC be above 79,980 USD at expiry?"). Traders buy YES or NO positions using USDC as collateral. At expiry, the winning side settles to 1 USDC per unit and the losing side settles to 0. The price of a YES token at any moment represents the market's implied probability of the event occurring.

HIP-4 markets run:

  • Onchain and permissionless. Settled directly on Hyperliquid L1 with no bridge or custodian.
  • With zero open fee. Unlike perps and spot, HIP-4 charges no fee to open a position (the builder fee still applies).
  • On the same CLOB as everything else. The same L2 book endpoints, WebSocket streams, and fill datasets cover HIP-4 alongside all other assets. If you already query Hyperliquid data, you are most of the way there.

The Quicknode Hyperliquid Developer Toolkit

Before writing any code, it is worth knowing what Quicknode provides for Hyperliquid so you can pick the right tool for what you want to build.

ToolWhat it gives youLink
Hyperliquid APIZero-custody REST builder API for Hyperliquid, built by Quicknodehyperliquidapi.com
Hyperliquid SDKs (TypeScript, Python, Go, Rust)Full trading and data SDK: orders, streams, account stateHyperliquid SDKs
DatasetsData streams that contain different types of blockchain and exchange events such as trades, orders, and book-updates via gRPC and JSON-RPC/WebSocket APIsdocs
WebSocket / JSON-RPC APIsReal-time subscriptions (bookUpdates, allMids, trades) and state queriesdocs
gRPC StreamingLowest-latency path to block-level orderbook and trade data, ideal for high-frequency strategiesdocs
Info EndpointsSnapshot queries: outcomeMeta, allMids, l2Book, clearinghouseStatedocs
SQL ExplorerHistorical queries over indexed tables: hyperliquid_trades, hyperliquid_orders, and 35+ otherssql-explorer

This guide uses Hyperliquid API for all trading operations and the Hyperliquid public info API for market data queries. The result is a dependency-light setup: just viem for local signing and ws for WebSocket streaming.

The Data Model

Before writing any code, it is important to understand the HIP-4 data model. HIP-4 data flows through the same APIs and datasets as every other Hyperliquid asset. The only distinguishing element is the #N coin notation. This section covers the three things you need to know before writing any code.

Market Identity and Discovery

Every HIP-4 outcome side is identified by a coin string prefixed with #, for example #20 (YES) and #21 (NO). This distinguishes outcome market coins from perpetuals (no prefix) and spot tokens (@N or xyz: prefix). The integer N is derived from the asset's numeric ID using the encoding N = 10 * outcomeIndex + sideIndex. The Hyperliquid Asset IDs specification covers the full mapping.

Two endpoints give you the complete picture of any active market:

outcomeMeta returns the market definition:

outcomeMeta response
{
"outcomes": [
{
"outcome": 3,
"name": "Recurring",
"description": "class:priceBinary|underlying:BTC|expiry:20260506-0600|targetPrice:80930|period:1d",
"sideSpecs": [
{ "name": "Yes" },
{ "name": "No" }
]
}
],
"questions": []
}
FieldDescription
outcomes[].outcomeIncrementing integer that identifies the market instance
outcomes[].nameMarket type label (for example, "Recurring")
outcomes[].descriptionPipe-delimited key:value pairs: class, underlying, expiry (format YYYYMMDD-HHMM), targetPrice, period
outcomes[].sideSpecsArray with one entry per side. sideSpecs[0] is YES, sideSpecs[1] is NO
questionsReserved for non-binary market types, empty for price binary markets

allMids returns live mid prices for every asset on the exchange. HIP-4 markets appear as #N keys alongside normal perp and spot tickers:

allMids response (excerpt)
{
"BTC": "96423.0",
"ETH": "1823.5",
"#20": "0.47",
"#21": "0.53"
}

Filter for keys starting with # to isolate outcome market mids. A native WebSocket subscription to allMids gives you the same data as a continuous real-time stream (covered in Stream Real-Time Orderbook Updates).

What Activity Looks Like in Data

When orders are placed and trades are matched, the Quicknode Hyperliquid datasets capture the activity with #N identifiers throughout:

trades: Each trade row uses coin: "#N". The feeToken field uses the +N notation (for example, +20), and fee is always "0.0" because HIP-4 open orders carry no fee.

orders: Order records use the same coin: "#N" notation. Both sides of a matched outcome trade appear as Buy orders (side: "B").

book-updates: Each event includes a raw_book_diff field: {"new": {"sz": "..."}} (new order placed), "remove" (order cancelled or filled), or {"update": {"origSz": "...", "newSz": "..."}} (in-place size modification, HIP-4 only). Use these to maintain a local orderbook replica.

l2-book and l4-book: Aggregated and raw book snapshots queryable by coin. Pass "#N" as the coin filter to retrieve data for a specific outcome side.

Reading Prices: Implied Probabilities

Because YES and NO are complementary outcomes, their mid prices sum to approximately 1.0:

YES mid + NO mid ≈ 1.0

A YES mid of 0.47 means the market implies a 47% probability of the event occurring. A NO mid of 0.53 implies 53%. Minor deviations from 1.0 reflect the spread and any temporary arbitrage opportunity between the two sides.

The #N coin notation is the only thing that distinguishes outcome market data in raw API responses. Once you understand that mapping, reading GET /markets from hyperliquidapi.com and filtering allMids for # keys is all you need. With that foundation in place, the rest of this guide is hands-on.

Trade HIP-4 Markets with Quicknode

This section walks through everything you need to go from zero to live trades on a HIP-4 market: approving the builder fee, verifying your setup, discovering active markets, reading and streaming the orderbook, and placing your first order.

The guide examples repository contains six standalone scripts, each covering a specific aspect of working with HIP-4. You can follow along with the key excerpts below to understand each concept, or clone the full repo and run the scripts one by one to see live output from your own wallet.

Get the Example Code

Clone and install
git clone https://github.com/quiknode-labs/qn-guide-examples.git
cd qn-guide-examples/hyperliquid/hip4-prediction-markets
npm install
cp .env.example .env

Fill in .env with your private key and wallet address (covered in Set Up Your Environment below), then build and run any script:

Build and run
npm run build
node --env-file=.env dist/api/0-approve.js

Each script is self-contained:

ScriptWhat it covers
api/0-approve.tsOne-time builder fee approval
api/1-setup.tsHealth check: API connection, active markets, account state
api/2-list-markets.tsDiscover active markets via GET /markets and live mid prices
api/3-orderbook-snapshot.tsFetch L2 orderbook for all active HIP-4 symbols, compute implied probabilities
api/4-stream-orderbook.tsSubscribe to l2Book and allMids via native WebSocket
api/5-trade.tsLimit and market orders, preflight check, cancel

Set Up Your Environment

Create a .env file in your project root with your wallet credentials:

.env
PRIVATE_KEY=your_wallet_private_key_hex
WALLET_ADDRESS=your_wallet_address

caution

Never commit .env to version control. Add it to .gitignore. The PRIVATE_KEY field gives full control over your wallet.

Install Dependencies and Set Up the API Client

Trading via Hyperliquid API requires no SDK. The only runtime dependency is viem for local signing. ws is needed for WebSocket streaming in Node.js:

Install
npm install viem ws
npm install --save-dev @types/ws

Using a Quicknode SDK instead?

This guide uses the hyperliquidapi.com REST API directly. If you prefer a higher-level interface, Quicknode also maintains SDKs for TypeScript, Python, Go, and Rust that wrap the same underlying protocol.

All trading actions follow the same three-step build-sign-send pattern, implemented once in a shared client.ts helper:

  1. Build: POST the action to /exchange without a signature. The API returns a hash and nonce.
  2. Sign: Sign the hash locally with your private key using viem. Your key never leaves your machine.
  3. Send: POST again with the action, nonce, and signature to execute.
client.ts (key helpers)
import { privateKeyToAccount } from "viem/accounts";
import { parseSignature } from "viem";

export const API_BASE = "https://send.hyperliquidapi.com";
export const HL_INFO = "https://api.hyperliquid.xyz/info";
export const HL_WS = "wss://api.hyperliquid.xyz/ws";

/** GET from hyperliquidapi.com (health, markets, approval status) */
export async function apiGet(path: string) {
const res = await fetch(`${API_BASE}${path}`);
return res.json();
}

/** POST to Hyperliquid public info endpoint (read-only market data) */
export async function hlInfo(type: string, extra?: Record<string, unknown>) {
const res = await fetch(HL_INFO, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ type, ...extra }),
});
return res.json();
}

/**
* Build-sign-send: the pattern for every trading action.
* 1. POST action (no sig) → API returns hash + nonce
* 2. Sign hash with viem (local, no key transmission)
* 3. POST with signature → executes on-chain
*/
export async function buildSignSend(action: Record<string, unknown>) {
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const built = await exchangePost({ action }) as any;
const rawSig = await account.sign({ hash: built.hash as `0x${string}` });
const { r, s, v } = parseSignature(rawSig);
return exchangePost({
action: built.action ?? action,
nonce: built.nonce,
signature: { r, s, v: Number(v) },
});
}

There are three destinations: apiGet reads non-trading state from hyperliquidapi.com (/health, /markets, /approval). hlInfo reads market data from the public Hyperliquid info API. buildSignSend executes any trading action via hyperliquidapi.com using the three-step signing flow. WebSocket streaming uses wss://api.hyperliquid.xyz/ws directly.

Full source: client.ts

Approve the Builder Fee

The builder fee must be approved before your wallet can trade. This is a one-time onchain action per wallet. Running it explicitly shows you the before/after state and confirms everything is ready.

api/0-approve.ts (key excerpt)
import { buildSignSend, apiGet, getAccount } from "./client.js";

const account = getAccount();

// 1. Check current status
const before = await apiGet(`/approval?user=${account.address}`) as any;

// 2. Build + sign + send the approval action
const result = await buildSignSend({ type: "approveBuilderFee", maxFeeRate: "1%" });

// 3. Verify
const after = await apiGet(`/approval?user=${account.address}`) as any;
console.log("canTradePerps:", after.canTradePerps);
console.log("canTradeSpot :", after.canTradeSpot);

Full source: api/0-approve.ts

Expected output (first run):

api/0-approve.ts output
============================================================
BUILDER FEE APPROVAL — hyperliquidapi.com
============================================================
Wallet: 0xYourWalletAddress

── Current Status ───────────────────────────────────────
approved : true
maxFeeRate : 0%
canTradePerps: false
canTradeSpot : false
status msg : Your approval is below the minimum fee. Re-approve with a higher maxFeeRate.

Approving builder fee (maxFeeRate: 1%)...

── Approval Result ──────────────────────────────────────
{
"status": "ok",
"response": { "type": "default" }
}

── Status After Approval ────────────────────────────────
approved : true
maxFeeRate : 1%
canTradePerps: true
canTradeSpot : true
status msg : You can trade both perps and spot.

Approval successful. Ready to trade perps, spot, and HIP-4.

Verify Your Connection and Active Markets

Before placing any orders, confirm the API is reachable, HIP-4 markets are live, and builder fee approval is in place.

api/1-setup.ts (key excerpt)
import { apiGet, hlInfo, getAccount } from "./client.js";

const account = getAccount();

// API health
const health = await apiGet("/health") as any;
console.log("Health:", JSON.stringify(health));

// Builder fee approval
const approval = await apiGet(`/approval?user=${account.address}`) as any;
console.log("canTradePerps:", approval.canTradePerps);
console.log("canTradeSpot :", approval.canTradeSpot);

// Active HIP-4 markets
const markets = await apiGet("/markets") as any;
console.log("HIP-4 markets:", markets.hip4?.length ?? 0);

// Live mid prices: filter allMids for # symbols
const allMids = await hlInfo("allMids") as any;
const hip4Mids = Object.fromEntries(
Object.entries(allMids).filter(([k]) => k.startsWith("#"))
);
console.log("HIP-4 mids:", JSON.stringify(hip4Mids));

Full source: api/1-setup.ts

Expected output (trimmed):

api/1-setup.ts output
── Health ───────────────────────────────────────────────
{ "status": "ok" }

── Builder Fee Approval ─────────────────────────────────
canTradePerps: true
canTradeSpot : true
✓ Ready to trade.

── Markets Summary ──────────────────────────────────────
hip4 : 1 market(s)

── HIP-4 Mid Prices (from allMids) ──────────────────────
#20 : 0.47
#21 : 0.53

Browse Active Prediction Markets

GET /markets from hyperliquidapi.com returns all active markets grouped by type (perps, spot, hip4). Cross-reference the hip4 array with allMids to get live prices, and use recentTrades to see recent activity for each symbol.

api/2-list-markets.ts (key excerpt)
import { apiGet, hlInfo } from "./client.js";

// Active markets from hyperliquidapi.com
const markets = await apiGet("/markets") as any;
const hip4 = markets.hip4 ?? [];
console.log(`Found ${hip4.length} HIP-4 market(s)`);

// Live mid prices: filter allMids for # symbols
const allMids = await hlInfo("allMids") as any;
const hip4Mids = Object.fromEntries(
Object.entries(allMids).filter(([k]) => k.startsWith("#"))
);

for (const [sym, mid] of Object.entries(hip4Mids)) {
console.log(` ${sym.padEnd(6)}: ${mid}`);
}

// Recent trades for each active symbol
for (const symbol of Object.keys(hip4Mids)) {
const trades = await hlInfo("recentTrades", { coin: symbol }) as any[];
console.log(`\nRecent trades for ${symbol}:`);
for (const t of (trades ?? []).slice(0, 3)) {
console.log(` [${new Date(t.time).toISOString()}] ${t.side} px=${t.px} sz=${t.sz}`);
}
}

Full source: api/2-list-markets.ts

Expected output (trimmed):

api/2-list-markets.ts output
Found 1 HIP-4 market(s) via GET /markets

── HIP-4 Mid Prices (from allMids) ──────────────────────
#20 : 0.47
#21 : 0.53

── Recent Trades: #20 ────────────────────────────────────
[2026-05-09T11:00:00.000Z] B px=0.47 sz=25
[2026-05-09T10:58:00.000Z] A px=0.48 sz=30
[2026-05-09T10:55:00.000Z] B px=0.46 sz=15

The # prefix is how every HIP-4 symbol appears across all Hyperliquid data: in allMids, recentTrades, dataset records, and WebSocket messages. Use Object.keys(allMids).filter(k => k.startsWith("#")) as your source of truth for active symbols.

Read the Live Orderbook

Pass a #N symbol to hlInfo("l2Book", { coin: sym }) to get the full aggregated orderbook. Each level has px (price), sz (total size), and n (number of resting orders). Fetching books for all active symbols in parallel minimizes latency.

api/3-orderbook-snapshot.ts (key excerpt)
import { hlInfo } from "./client.js";

const allMids = await hlInfo("allMids") as any;
const hip4Symbols = Object.keys(allMids).filter(k => k.startsWith("#"));

// Fetch all books in parallel
const books = await Promise.all(
hip4Symbols.map(sym => hlInfo("l2Book", { coin: sym }))
) as any[];

for (let i = 0; i < hip4Symbols.length; i++) {
const sym = hip4Symbols[i];
const book = books[i];
const bids = book.levels?.[0] ?? []; // sorted best-to-worst
const asks = book.levels?.[1] ?? [];

const bestBid = parseFloat(bids[0]?.px ?? "0");
const bestAsk = parseFloat(asks[0]?.px ?? "0");
const spread = bestAsk - bestBid;

console.log(`${sym} best bid: ${bestBid} best ask: ${bestAsk} spread: ${spread.toFixed(5)}`);
}

// Implied probabilities: YES mid + NO mid ≈ 1.0
const yesMid = parseFloat(allMids["#20"] ?? "0");
const noMid = parseFloat(allMids["#21"] ?? "0");
console.log(`YES: ${(yesMid * 100).toFixed(2)}% NO: ${(noMid * 100).toFixed(2)}% Sum: ${((yesMid + noMid) * 100).toFixed(2)}%`);

Full source: api/3-orderbook-snapshot.ts

Expected output (trimmed):

api/3-orderbook-snapshot.ts output
============================================================
HIP-4 ORDERBOOK SNAPSHOT
============================================================
Active HIP-4 symbols: #20, #21
Time: 2026-05-09T11:30:00.000Z

── #1040 ─────────────────────────────────────────────
Best bid : 0.00871
Best ask : 0.01621
Spread : 0.00750 (60.193%)
Mid : 0.01246
Bid depth : 262911 units / 17 levels
Ask depth : 106189 units / 20 levels

PRICE SIZE ORDERS
────────────────────────────────────
0.05 605.0 2 ASK
0.04123 1.0 1 ASK
0.03689 16.0 1 ASK
0.035 11.0 1 ASK
0.032 200.0 1 ASK
0.024 1560.0 1 ASK
0.0213 13650.0 1 ASK
0.02 700.0 1 ASK
── spread 0.00750 ────────────────────────
0.00871 1500.0 1 BID
0.0087 1106.0 1 BID
0.00868 1219.0 1 BID
0.00812 756.0 1 BID
0.0081 2500.0 2 BID
0.0079 1984.0 1 BID
0.0075 2685.0 1 BID
0.0071 5000.0 1 BID

tip

A YES mid below 0.50 means the market currently considers the event unlikely. A YES mid above 0.50 means it is likely. Track both sides: the NO mid is not simply 1 - YES mid in a live orderbook because each side has its own spread.

Stream Real-Time Orderbook Updates

For live data, open a native WebSocket to wss://api.hyperliquid.xyz/ws and subscribe to l2Book for each active HIP-4 symbol. Subscribe to allMids in parallel to track mid prices. The hyperliquidapi.com REST API is for trading only; streaming always uses the native Hyperliquid WebSocket.

api/4-stream-orderbook.ts (key excerpt)
import WebSocket from "ws";
import { hlInfo, HL_WS } from "./client.js";

const allMids = await hlInfo("allMids") as any;
const hip4Symbols = Object.keys(allMids).filter(k => k.startsWith("#"));

const ws = new WebSocket(HL_WS);

ws.on("open", () => {
// Subscribe to allMids for mid price ticks
ws.send(JSON.stringify({ method: "subscribe", subscription: { type: "allMids" } }));

// Subscribe to l2Book for each active HIP-4 symbol
for (const sym of hip4Symbols) {
ws.send(JSON.stringify({ method: "subscribe", subscription: { type: "l2Book", coin: sym } }));
}
});

ws.on("message", (raw) => {
const msg = JSON.parse(raw.toString()) as any;

if (msg.channel === "allMids") {
const mids = msg.data?.mids ?? msg.data ?? {};
const hip4 = Object.fromEntries(Object.entries(mids).filter(([k]) => k.startsWith("#")));
console.log("[allMids] HIP-4:", JSON.stringify(hip4));
return;
}

if (msg.channel === "l2Book") {
const data = msg.data;
const bids = data.levels?.[0] ?? [];
const asks = data.levels?.[1] ?? [];
console.log(`[l2Book ${data.coin}] ${bids.length} bids / ${asks.length} asks best bid=${bids[0]?.px} best ask=${asks[0]?.px}`);
}
});

// Stream for 30 seconds then exit
await new Promise((resolve) => setTimeout(resolve, 30_000));
ws.close();

Full source: api/4-stream-orderbook.ts

Expected output (trimmed):

api/4-stream-orderbook.ts output
============================================================
HIP-4 ORDERBOOK STREAM — native WebSocket
============================================================
Symbols : #20, #21
WS URL : wss://api.hyperliquid.xyz/ws
Streaming for 30 seconds...

[ws] Connected ✓
[ws] subscriptionResponse: {"method":"subscribe","subscription":{"type":"l2Book","coin":"#1040","nSigFigs":null,"mantissa":null}}

[l2Book #1] #1040 bestBid=0.00871 bestAsk=0.01621 t=1781287285958
asks: 0.01621x3500.0 0.01622x10000.0 0.01625x1731.0
bids: 0.00871x1500.0 0.0087x1106.0 0.00868x1219.0
[ws] subscriptionResponse: {"method":"subscribe","subscription":{"type":"l2Book","coin":"#1041","nSigFigs":null,"mantissa":null}}

[l2Book #1] #1041 bestBid=0.98379 bestAsk=0.99129 t=1781287285958
asks: 0.99129x1500.0 0.9913x1106.0 0.99132x1219.0
bids: 0.98379x3500.0 0.98378x10000.0 0.98375x1731.0

// ...

Place Your First HIP-4 Trade

All orders use buildSignSend with a type: "order" action. Pass the #N symbol as asset, specify side ("buy" or "sell"), price, size, and tif (time-in-force). For market orders, set tif: "market" and omit price (the API auto-computes a mid-price-plus-slippage fill).


HIP-4 size constraint

HIP-4 assets have szDecimals = 0, meaning size must be a whole integer. The minimum order notional is 10 USDC. Find the smallest valid size with Math.ceil(10 / price). The examples below add a buffer of 5 units on top of the minimum.

Preflight Check

Use the /preflight endpoint to validate an order without signing. It catches errors (insufficient balance, size too small, invalid asset) before you commit:

api/5-trade.ts (preflight)
const preflight = await fetch("https://send.hyperliquidapi.com/preflight", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
action: {
type: "order",
orders: [{ asset: yesSymbol, side: "buy", price: String(buyPrice), size: limitSize, tif: "gtc" }],
},
}),
}).then(r => r.json());
console.log(JSON.stringify(preflight, null, 2));

Limit Orders (GTC)

Limit orders rest on the book until filled or cancelled. Use tif: "gtc" (Good Till Cancel). Do not set grouping to anything other than "na" on HIP-4 orders; priorityFee is not supported and will return an error.

api/5-trade.ts (limit orders)
const limitBuyResult = await buildSignSend({
type: "order",
orders: [{
asset: yesSymbol, // e.g. "#20"
side: "buy",
price: String(buyPrice), // 3% below mid
size: limitSize, // Math.ceil(10 / buyPrice) + 5
tif: "gtc",
}],
}) as any;

// Extract OID from response
const oid = limitBuyResult?.exchangeResponse?.data?.statuses?.[0]?.resting?.oid ?? null;
console.log("OID:", oid);

// Cancel when done
await buildSignSend({ type: "cancel", cancels: [{ a: yesSymbol, o: oid }] });

Market Orders (IOC)

Market orders fill immediately. Set tif: "market" and omit price; the API computes mid price plus 3% slippage automatically. Size must still satisfy the 10 USDC minimum at the worst-case fill price.

api/5-trade.ts (market orders)
const mktBuyResult = await buildSignSend({
type: "order",
orders: [{
asset: yesSymbol,
side: "buy",
size: marketSize,
tif: "market", // no price field — API auto-computes slippage
}],
});
console.log(JSON.stringify(mktBuyResult, null, 2));

Full source: api/5-trade.ts

Expected output (trimmed):

api/5-trade.ts output
── Limit BUY (GTC) ──────────────────────────────────────
Placing: BUY 27 #20 @ 0.4559
{
"exchangeResponse": {
"data": { "statuses": [{ "resting": { "oid": 4830012 } }] }
}
}
OID: 4830012

── Market BUY (IOC, auto-slippage) ──────────────────────
{
"exchangeResponse": {
"data": { "statuses": [{ "filled": { "oid": 4830014, "avgPx": "0.471", "totalSz": "27" } }] }
}
}

── Cancel Resting Limit Orders ──────────────────────────
{
"exchangeResponse": {
"data": { "statuses": [{ "success": "" }] }
}
}

Common Mistakes


Watch out for these

Insufficient USDC balance. HIP-4 orders will fail if your USDC spot balance is too low. Confirm your balance with hlInfo("spotClearinghouseState", { user: walletAddress }) before placing orders.

Order value below 10 USDC. Every HIP-4 order must satisfy size * price >= 10. If your price is 0.05, you need at least 200 units. Always compute size as Math.ceil(10 / price) + buffer rather than hardcoding a number.

priorityFee / unsupported grouping on HIP-4. The priorityFee parameter and non-default grouping values are not supported for outcome market orders. Omit both; the default grouping: "na" applies automatically.

Fractional size. szDecimals = 0 for all HIP-4 assets. Sizes like 10.5 are rejected. Always use Math.ceil() or Math.floor().

Builder fee not approved. If apiGet("/approval?user=address") shows canTradeSpot: false, no trades will go through. Run the approval step first.

Missing signature fields. The build-sign-send pattern requires a successful build step first. If hash or nonce is missing from the build response, the action was rejected before signing: check the raw response for the error message.

Conclusion

You now have a complete path from raw data to live trades for HIP-4 prediction markets on Hyperliquid. The key insight is that HIP-4 is not a separate system: it uses the same CLOB, the same WebSocket streams, and the same fill datasets as every other Hyperliquid asset. The #N coin notation is the only thing that distinguishes outcome market data in raw API responses. With the build-sign-send pattern from hyperliquidapi.com, any trading action reduces to three HTTP calls and a local signature.

What to Build Next

Now that you have the fundamentals, here are some ideas for what to build on top:

Live probability alert bot. Subscribe to allMids via WebSocket and filter for #N keys. Stream mid prices in real time and fire a notification (Telegram, Discord, or Slack) when a market's implied probability crosses a threshold you define (useful for monitoring markets close to expiry or after a major news event).

Pre-expiry behavior analysis with SQL Explorer. Use SQL Explorer to query hyperliquid_trades WHERE coin LIKE '#%' and measure trade volume, fill frequency, and price action in the hours before settlement. Combine with hyperliquid_orders to track order placement patterns across multiple market cycles.

YES/NO spread arbitrage scanner. Subscribe to l2Book for all active YES/NO pairs via WebSocket. When YES mid + NO mid deviates meaningfully from 1.0, flag the spread as a potential arbitrage opportunity. Layer in depth data to estimate fill probability before acting.

High-frequency market data pipeline with gRPC. For latency-sensitive strategies, Quicknode's gRPC streaming API gives you the lowest-latency path to block-level orderbook and trade data. Parse #N events server-side and react to each block with minimal overhead compared to WebSocket polling.

Resources

Frequently Asked Questions

What collateral does HIP-4 use?

HIP-4 prediction markets use USDC as collateral. Deposit USDC into your Hyperliquid spot account and you can trade any active HIP-4 market directly. No token swap is required.

Why does YES mid + NO mid equal approximately 1.0?

YES and NO are complementary binary outcomes: exactly one settles to 1 and the other to 0. In an efficient market, their prices reflect the implied probabilities of each outcome, which must sum to 100%. Small deviations from 1.0 reflect the spread and temporary arbitrage opportunities between the two sides.

Can I trade HIP-4 markets without USDC?

No. USDC is the required collateral for all HIP-4 orders. Attempting to place an order without sufficient USDC spot balance will result in an error. Verify your balance with hlInfo("spotClearinghouseState", { user: walletAddress }) before trading.

Why must HIP-4 order sizes be whole integers?

HIP-4 assets have szDecimals = 0 on Hyperliquid, meaning the exchange only accepts integer quantities. If you pass a fractional size like 10.5, the order will be rejected. Always use Math.ceil() to compute the minimum valid size: Math.ceil(10 / price).

What does a coin like `#20` mean in API responses?

The #N notation identifies a HIP-4 outcome market side. The number N is derived from the asset ID using N = 10 * outcomeIndex + sideIndex, where outcomeIndex identifies the outcome and sideIndex the trading side. In practice, filter allMids for keys starting with # to discover all active HIP-4 symbols; you encounter raw #N values in allMids, trades, orders, and book-updates responses.

What is the build-sign-send pattern and why does it exist?

Build-sign-send is a zero-custody signing flow used by hyperliquidapi.com. You POST the action to the API without a signature to get a hash, sign the hash locally with your private key using viem, then POST again with the signature to execute. Your private key never leaves your machine. This is the same cryptographic model Hyperliquid uses natively; the REST API surfaces it as a three-step HTTP flow.

HIP-4 used to reference USDH and spot market @230. What changed?

Hyperliquid migrated HIP-4 collateral from USDH to USDC. Older guides and code that reference USDH or spot market @230 are out of date. With USDC as collateral, no intermediate swap step is needed: fund your spot account with USDC and trade directly.

We ❤️ Feedback!

Let us know if you have any feedback or requests for new topics. We'd love to hear from you.

Share this guide