Setting Position Limits and Exposure Caps on Hyperliquid
Uncapped exposure is how HFT bots blow up accounts overnight. Learn how to enforce per-asset position limits and total portfolio exposure caps using Hyperliquid's API and on-chain margin accounting.
Uncapped exposure is the most common way automated trading systems blow up accounts — not bad alpha, not execution slippage, but a risk layer that was never built. Hyperliquid's on-chain perpetuals architecture gives you the raw materials to enforce hard limits at both the per-asset and portfolio level, but the exchange won't do it for you. Here is exactly how to wire it in.
Understanding What Hyperliquid Exposes
Hyperliquid runs a fully on-chain order book on its own L1. Every position and margin balance is readable from the info endpoint in real time — no need to reconcile with a CEX's private ledger or trust delayed WebSocket snapshots.
The two endpoints you care about most:
/infowithtype: "clearinghouseState"— returns your cross-margin account summary, includingmarginSummary.accountValue,marginSummary.totalNtlPos, andcrossMaintenanceMarginUsed./infowithtype: "openOrders"— returns working orders with size and side, letting you calculate your pending notional exposure before fills land.
totalNtlPos is the sum of absolute notional values across all open positions. That single field is your gross exposure number. If you are running a delta-neutral strategy you will want to track totalNtlPos and net delta separately — they tell different stories.
Defining Your Limit Hierarchy
Before writing a single line of enforcement code, write down three numbers per asset and two numbers at the portfolio level:
Per-asset:
max_position_notional— the hard cap on your absolute exposure in one market (e.g., $50,000 notional on BTC-PERP)max_order_size— the largest single order you will send (prevents fat-finger fills from clearing your headroom instantly)max_open_orders— a secondary check that bounds how many resting orders can stack up on one side
Portfolio:
max_gross_exposure— the ceiling ontotalNtlPosacross all markets (e.g., $200,000)max_margin_utilization— e.g., 60% ofaccountValueused ascrossMaintenanceMarginUsedbefore you stop opening new positions
These are not suggestions; they go into a config file that your bot reads at startup and reloads on SIGHUP without a restart.
Enforcing Limits Before Order Submission
The right place to enforce limits is in a pre-flight check that gates every order before it touches the API. Structure it as a synchronous function that returns either approved or a reason string — never a boolean, because you will want the reason in your logs.
def preflight(order, state, config):
asset = order["coin"]
side_sign = 1 if order["is_buy"] else -1
new_notional = abs(order["sz"] * order["limit_px"])
# Per-asset position check
current = state["positions"].get(asset, 0.0) # signed notional
projected = current + side_sign * new_notional
if abs(projected) > config["limits"][asset]["max_position_notional"]:
return f"REJECT: {asset} projected notional {projected:.0f} exceeds cap"
# Portfolio gross exposure check
pending_gross = state["total_ntl_pos"] + new_notional
if pending_gross > config["max_gross_exposure"]:
return f"REJECT: gross exposure {pending_gross:.0f} exceeds portfolio cap"
# Margin utilization check
if state["margin_used"] / state["account_value"] > config["max_margin_utilization"]:
return "REJECT: margin utilization ceiling hit"
return "approved"
The state object here is populated from a polling loop hitting clearinghouseState every 500ms — fast enough for most strategies, with a mutex protecting reads during order submission. For strategies running sub-100ms tick frequencies you will want the WebSocket fills feed instead, maintaining a local position shadow and reconciling against the REST snapshot every few seconds.
Handling the Async Gap
Hyperliquid's order acknowledgement is fast, but there is a window between when you send an order and when it appears in clearinghouseState. During that window a second order can pass preflight and you can end up with 2x your intended position.
The fix is a pending-orders register: a thread-safe dict keyed by client order ID (cloid) that tracks submitted-but-not-yet-confirmed notional. Your preflight check includes pending notional in its calculations. When a fill or cancel confirmation arrives on the WebSocket, you remove the entry. If an order is not confirmed within a configurable timeout (typically 2-3 seconds on Hyperliquid), you treat it as failed and drop it from the register — then reconcile against REST before placing anything new.
This is the part that most open-source bot frameworks skip. It is also the part that causes the 3am incident.
Hard Stops vs. Soft Limits
Soft limits halt new order submission. Hard stops go further: they cancel all open orders and optionally flatten positions. You need both.
Trigger hard stops on:
account_valuedropping below a floor (e.g., 80% of session-start value)- Any single position moving more than N% against you in a defined window
- The
clearinghouseStatepolling loop failing to return a valid response for more than 5 consecutive seconds
Use Hyperliquid's cancel all action (cancelAllOrders) rather than iterating individual cancels — it is a single signed transaction and clears the book atomically from your account's perspective. For flattening, market orders at slippage: 0.05 (5% worst-case) are acceptable in a stop scenario; you are prioritizing speed over price.
Tie the hard-stop logic to a separate process, not the main trading loop. If the trading process hangs, the stop process still fires. This is standard practice for any production trading bot and not optional.
Margin Mode Considerations
Hyperliquid supports both cross-margin and isolated-margin per position. Cross-margin makes portfolio-level exposure tracking straightforward — one accountValue, one totalNtlPos. Isolated margin lets you cap loss on a single position structurally, but it complicates portfolio-level accounting because you have to sum isolated margin allocations manually.
In practice: use cross-margin for multi-asset strategies where you actively manage gross exposure, use isolated margin only when you want a position that is explicitly ring-fenced with a fixed loss budget and no dynamic rebalancing. Mixing modes in the same account without careful accounting is where silent risk accumulates.
If you want a production risk layer built to these specifications — integrated with your existing Hyperliquid strategy — talk to us.
Need a bot like this built?
We design, build and run trading bots on Solana, Hyperliquid and Polymarket.
Start a projectMore from the blog
Public vs Paid RPC: How to Run a Fair Benchmark
A fair comparison of public and paid RPC endpoints must account for quotas, workload, freshness and support guarantees.
Read articleRPC Benchmark Percentiles Explained: p50, p95 and p99
Why averages hide the latency spikes that break trading bots, wallets and production blockchain applications.
Read articleHyperliquid REST vs WebSocket Benchmark: What to Measure
A benchmark plan for Hyperliquid market data that separates request latency from streaming freshness and recovery.
Read article