Site icon Chainstack

How to build a HIP-4 trading bot on Hyperliquid

Hip 4 Bot 1 logo

Hyperliquid’s HIP-4 brings outcome markets natively on-chain. Learn how HIP-4 works, walk through the API, set up your environment, and run a passive market-making bot on binary outcome markets.

TL;DR

HIP-4 is Hyperliquid’s native outcome market protocol, live on mainnet

The API runs on the same engine as spot and perps

The bot quotes YES and NO tokens passively across multiple outcomes

Hyperliquid HIP-4 is live

🤔 Prefer diving straight into the code? Get the HIP-4 bot by Chainstack

HIP-4 is live on mainnet.

Hyperliquid integrated outcome markets directly into HyperCore that processes billions in daily spots and perpetuals volume. On day one, the initial BTC binary market generated 6.05 million contracts in the first 24 hours

and a cumulative volume of 25.1M in a week since May 2.

Source: https://www.artemis.ai/company/HYPE?tab=hip4

What is Hyperliquid HIP-4?

HIP-4 (Hyperliquid Improvement Proposal 4) introduces outcome markets: non-linear, expiry-based contracts designed as an alternative derivative primitive without leverage or liquidations.

For a deeper breakdown of all HIPs, read Hyperliquid HIPs Explained.

Outcomes are single tradable contract that settle into predefined results at expiry instead of behaving like continuously margined perpetual positions. For eg: “BTC closes above $80,922 at 06:00 UTC May 14”. Each outcome is identified by a positive integer index. On mainnet, the first BTC daily binary outcome has index 5915.

Recurring outcomes are markets automatically deployed and settled by the protocol on a fixed cadence, with the active market configuration stored in the description field of outcomeMeta.

Binary outcome markets settle into one of two states: YES or NO.

class:priceBinary|underlying:BTC|expiry:20260503-0600|targetPrice:78213|period:1d

Settlement uses linear interpolation between the mark price updates immediately before and after expiry. The market settles to YES if the interpolated price is greater than or equal to the target price.

Multi-price markets divide settlement into multiple price buckets.

class:priceBucket|underlying:BTC|expiry:20260507-0745|priceThresholds:81538,81783|period:15m

There are three price buckets depending on the target price at the time of settlement:

Exactly one of the 3 outcomes settles to 1, and the others settle to 0. One such example is already live on testnet and on mainnet. Hyperliquid has stated additional features will be rolled out gradually in later stages.

Outcome tokens are the tradable units representing each possible market result, and their prices are bounded between [0,1], where 0 represents an impossible outcome and 1 represents a guaranteed winning outcome at settlement.

USDH is the quote and settlement denomination used for outcome markets, allowing outcome token prices ( probabilities ) to trade as decimal probabilities such as 0.001, 0.25, or 0.9999 depending on the outcome. For eg: The price tick on the BTC binary is 0.0001

Settlement is automatic. There is no claimredeem, or settle call. At expiry, USDH credits land in the account in proportion to the side balances held, and the outcome is removed from the next outcomeMeta response.

Hyperliquid HIP-4 API

Before understanding how the bot works, it is important to understand how Hyperliquid exposes their APIs.

📖 All across Hyperliquid, whether spot, perps or outcomes, they run on the same engine accessible through the same HyperCore. It is recommended to read “Hyperliquid API for Developer” to understand the HIP-4 API smoothly.

Info

All on-chain state read from the chain flows through /info endpoint. The only thing that changes is the type field in the request.

Pass outcomeMeta in type

import requests

url = "<https://api.hyperliquid.xyz/info>"

payload = { "type": "outcomeMeta" }
headers = {"Content-Type": "application/json"}

response = requests.post(url, json=payload, headers=headers)

print(response.text)

Returns all outcome markets with their asset IDs, question text, expiry timestamp, oracle type, and settlement status.

To see it in action: list outcomes

📖 Find the full availability matrix for all hyperliquid methods and in detail explanation of each in API reference

Exchange

POST /exchange is the write layer. Every state-changing operation, placing orders, canceling, splitting, merging, flows here. All requests on /exchange endpoint requires an EIP-712 structured data signature from the wallet key for authentication and security purposes.

The four outcome-specific exchange actions:

ActionEffect
splitOutcomeBurns 1 USDH, mints 1 YES + 1 NO for a question
mergeOutcomeBurns 1 YES + 1 NO, returns 1 USDH
negateOutcomeConverts YES to NO (or NO to YES) for the same question
mergeQuestionSettles a categorical question at expiry

The Hyperliquid Python SDK handles signing and payload encoding. The hl4/ library in the repo wraps the SDK specifically for outcome asset IDs and action types.

WebSocket

Hyperliquid’s WebSocket endpoint streams l2Book and trades updates for any asset including outcomes at:

See an easy implementation of subscribing to l2Book + trades for one side of an outcome over WebSocket

Updates arrive as JSON frames with bid/ask depth changes. The bot uses this feed to track mid-price in real time without polling the REST endpoint on every tick.

Use this reference to check all subscription types: WebSocket subscriptions docs

Build the bot

We will use:

hyperliquid-hip-4/
├── hl4/              ← core client library (API calls, signing, types)
├── examples/         ← 13 standalone scripts, one per primitive operation
├── bot/              ← passive market-maker
├── .env.example      ← environment variable template

All scripts target testnet by default. Mainnet requires changing the base URL in the config.

Create Chainstack Account

Head to Console and create a free account

This is where you can manage all your nodes across 70+ protocols

Click on “Add node”

Choose Hyperliquid and then select “Hyperliquid Testnet” in the network section below

Click “Next”

In the next step, select the type of node. For this project, choose “Global Node”. Give your node a name, review all the settings in the summary, and click “Deploy Node”

Once the node is deployed, click “View Node Details”. Scroll down to the “Access and credentials” section. Here you will find the endpoint URL, copy it and keep it safe. It will be needed in the next step.

Do not expose your URL. Store it securely in a .env file and add it to your .gitignore before pushing the code to GitHub. Consider using password-protected endpoints to enhance security.

Environment Setup

git clone <https://github.com/chainstacklabs/hyperliquid-hip-4>
cd hyperliquid-hip-4

uv sync
cp .env.example .env

Fill in .env:

HYPERLIQUID_TESTNET_PRIVATE_KEY=0x...
TESTNET_WALLET_ADDRESS=0x...

For your RPC endpoint, use Chainstack, deploy a Hyperliquid node and drop the HTTPS URL into your config. Chainstack’s Hyperliquid nodes expose both HyperEVM /evm and a subset of HyperCore /info queries on a single endpoint, which simplifies auth and latency tuning when running bots.

Explore examples (optional)

# list all outcome IDs with metadata
uv run python examples/01_list_outcomes.py

# list categorical questions (multi-leg markets)
uv run python examples/02_list_questions.py

For learning purposes, explore and run scripts like 01_list_outcomes.py to call outcomeMeta on /info endpoint. It returns each outcome’s ID, question text, expiry timestamp, and settlement oracle type. On testnet, you will see the BTC daily binary, a HYPE 15-minute binary, and several validator-deployed test markets.

Move to next step to setup and run the bot.

Get USDH (critical)

HIP-4 settles in USDH. To get USDH, run script 00_get_usdh.pyThe standard testnet faucet pays USDC; handles the USDC → USDH swap.

uv run python examples/00_get_usdh.py --amount 50

Then, check your USDC, USDH, and outcome token balances

uv run python examples/03_account_state.py

Run the bot

The bot/ directory contains market_maker.py, a passive market-making loop designed for binary outcome markets. Here is what it does and how each parameter controls behavior. To run the bot:

# market-make BTC daily and HYPE 15-min binaries simultaneously
uv run python -m bot.market_maker --outcomes 5915,5969 \\
    --quote-notional 12 --half-spread 0.04 --max-inventory 30 --tick 5
ParameterDescription
--outcomesComma-separated outcome IDs to make markets on simultaneously
--quote-notionalUSDH size of each resting order (bid and ask)
--half-spreadOne-sided spread from mid-price, in USDH. A half-spread of 0.04 places bids at mid - 0.04 and asks at mid + 0.04
--max-inventoryMaximum net position in USDH before the bot stops quoting the direction that would increase exposure
--tickLoop interval in seconds

How does the bot work?

The bot/market_maker.py script runs a passive market-making strategy for HIP-4 binary outcome markets. Its job is to continuously place buy and sell quotes around the current market price while managing inventory risk automatically.

import argparse
import asyncio
import signal
import time
from dataclasses import dataclass, field

import httpx
from hyperliquid.exchange import Exchange
from hyperliquid.info import Info
from rich.console import Console

from hl4 import load_config
from hl4.client import make_clients
from hl4.outcomes import encode_balance_coin, encode_coin

console = Console()
PRICE_MIN=0.001
PRICE_MAX=0.999
PRICE_TICK=0.001

def round_tick(px: float) -> float:
    return max(PRICE_MIN, min(PRICE_MAX, round(px, 3)))

def fetch_book(base_url: str, coin: str) -> tuple[float | None, float | None]:
    r = httpx.post(f"{base_url}/info", json={"type": "l2Book", "coin": coin}, timeout=5.0)
    levels = r.json().get("levels", [[], []])
    bid = float(levels[0][0]["px"]) if levels[0] else None
    ask = float(levels[1][0]["px"]) if levels[1] else None
    return bid, ask

def fair_from_book(bid: float | None, ask: float | None) -> float | None:
    if bid is not None and ask is not None:
        return (bid + ask) / 2
    if bid is not None:
        return min(PRICE_MAX, bid + PRICE_TICK)
    if ask is not None:
        return max(PRICE_MIN, ask - PRICE_TICK)
    return 0.5

def position_for(base_url: str, address: str, balance_coin: str) -> float:
    """Look up an outcome side balance. Note: outcome balances appear under the
    `+<encoding>` coin form, not the `#<encoding>` form used for trading."""
    r = httpx.post(
        f"{base_url}/info",
        json={"type": "spotClearinghouseState", "user": address},
        timeout=5.0,
    )
    for b in r.json().get("balances", []):
        if b["coin"] == balance_coin:
            return float(b["total"])
    return 0.0

The bot maintains 4 quote channels per outcome:

@dataclass
classSideState:
coin:str
is_buy:bool
open_oid:int|None=None
open_px:float|None=None
open_sz:float|None=None

SideState tracks the currently resting order

classOutcomeMM:
def__post_init__(self):
forsidein (0,1):
coin=encode_coin(self.outcome_id,side)
self.sides[(side,True)]=SideState(coin,True)
self.sides[(side,False)]=SideState(coin,False)

This prebuilds the full two-sided quoting structure for each prediction-market outcome.

def__post_init__(self):
forsidein (0,1):
coin=encode_coin(self.outcome_id,side)

self.sides[(side,True)]=SideState(coin=coin,is_buy=True)
self.sides[(side,False)]=SideState(coin=coin,is_buy=False)

This builds full quoting structure:

This gives the bot continuous two-sided liquidity on both contracts simultaneously.

Once initialized, the bot enters a continuous quoting cycle:

bid_px=round_tick(mid-half_spread)
ask_px=round_tick(mid+half_spread)

This creates a passive spread designed to earn edge from market flow while staying inventory-neutral over time.

Troubleshooting

Common errors and their fixes when running the HIP-4 bot for the first time.

“Order must have minimum value of 10 USDH”

“Order has invalid size”

Orders fill but the position does not appear in account state

FormUsed in
#<encoding>/info l2Book, order placement, WebSocket
+<encoding>spotClearinghouseState balances

Use encode_coin(outcome_id, side) for order placement and encode_balance_coin(outcome_id, side) for balance lookups. Both helpers are exported from hl4.outcomes.

outcomeMeta returns empty or fails against the Chainstack endpoint

USDH balance is zero

uv run python examples/00_get_usdh.py --amount 50

Then confirm the balance before proceeding:

uv run python examples/03_account_state.py

The bot stops quoting one side

WebSocket feed goes silent mid-session

Settlement lands but the position lingers: waiting for a claim call

uv run python examples/12_wait_for_settlement.py

Price rejected near 0 or 1

Hyperliquid builder tools

The builder codes enable many perks and benefits for developers who choose to build on top of Hyperliquid. Outcome, a builder on HIP-4, generated 1M in trade volume:

The HIP-4 ecosystem is moving fast. Outcome markets went live on May 2, 2026, and builders are already shipping on top of it. Here is everything you need to go further, from code and frontends to docs and data.

Code

Frontends and Builders

Analytics and Data

Core Documentation

Chainstack

Community

Conclusion

HIP-4 brings a new class of derivative to Hyperliquid, one that settles automatically, runs on the same engine as spot and perps, and requires no leverage or liquidations. For builders, that means a clean surface to build on. Outcome markets expose the same REST and WebSocket APIs, use the same signing flow, and sit inside the same liquidity environment that already processes billions in daily volume.

The bot in this guide is a starting point. It covers the core loop: fetching fair value from the order book, placing resting quotes on both YES and NO sides, managing inventory caps, and cleaning up on shutdown. From here, you can extend it with dynamic spread skewing based on inventory, multi-outcome correlation logic, or signal layers that adjust quotes around known settlement windows.

To get started, deploy a Hyperliquid node on Chainstack, clone the repo, and run the bot on testnet first. Chainstack’s Hyperliquid nodes give you a reliable, low-latency endpoint with access to both HyperCore and HyperEVM on a single URL, which keeps the setup straightforward as you move from exploration to production.

With Chainstack Hyperliquid nodes, you get a single low-latency endpoint for both HyperCore and HyperEVM, making it easier to manage market data, order execution, and on-chain interactions without stitching together multiple providers. Whether you’re experimenting with passive market making on testnet or deploying production trading systems, Chainstack gives you reliable RPC performance, predictable connectivity, and fast global access for latency-sensitive bots.

FAQ

What is HIP-4 on Hyperliquid?

HIP-4 (Hyperliquid Improvement Proposal 4) introduces outcome markets — expiry-based contracts that settle into predefined results like YES or NO. Unlike perpetuals, they have no leverage, no liquidations, and settle automatically in USDH at expiry.

How does settlement work in HIP-4?

Settlement is fully automatic. At expiry, USDH credits land in your account proportional to the side balances you hold. There is no claim, redeem, or settle transaction to submit. The settled outcome disappears from the next outcomeMeta response.

What is USDH and why does HIP-4 use it instead of USDC?

USDH is Hyperliquid’s native stablecoin used as the quote and settlement currency for outcome markets. The testnet faucet pays USDC — they are separate assets. Swap USDC to USDH via the @1338 spot pair before placing any outcome orders.

Does outcomeMeta work on a Chainstack Hyperliquid endpoint?

No. outcomeMeta is served only by the official Hyperliquid public API and is not part of the open-source node software. Point HIP-4 metadata and trading calls at https://api.hyperliquid.xyz (mainnet) or https://api.hyperliquid-testnet.xyz (testnet). Use your Chainstack endpoint for HyperEVM operations and supported /info methods.

What is the difference between splitOutcome, mergeOutcome, and negateOutcome?

splitOutcome burns 1 USDH and mints 1 YES + 1 NO token for a question. mergeOutcome does the reverse — burns 1 YES + 1 NO and returns 1 USDH. negateOutcome converts a YES token to NO (or NO to YES) for the same question without touching USDH.

How are outcome asset IDs derived?

Outcome asset IDs follow the schema 20000 + outcome_index. The first BTC daily binary on mainnet has index 5915, giving asset ID 25915. Use encode_coin(outcome_id, side) for order placement and encode_balance_coin(outcome_id, side) for balance lookups — the two forms are not interchangeable.

Can I run the bot on mainnet straight away?

The bot and all example scripts target testnet by default. To switch to mainnet, change the base URL in your config to https://api.hyperliquid.xyz and fund your wallet with real USDH. Run on testnet first — use examples/00_get_usdh.py to get testnet USDH from the faucet and verify your setup before moving to mainnet.

What are the minimum order requirements for HIP-4?

Each order must have a minimum notional of 10 USDH. Order sizes must be integers — fractional sizes are rejected. Prices are bounded to [0.001, 0.999] with a tick size of 0.0001 on the BTC binary. Always pass prices through round_tick() before submitting.

Related reading

Hyperliquid + HIPs

Infrastructure + RPC

Exit mobile version