Setting Per-Asset Position Limits on Hyperliquid for HFT Bots
Walks through Hyperliquid's API leverage and notional caps, shows how to enforce a hard notional ceiling client-side before order submission, and explains why relying solely on exchange limits can still leave you over-exposed during rapid fills.
Setting per-asset position limits on Hyperliquid is one of the first things you wire up before going live — not after your first runaway fill. The exchange gives you server-side levers, but in a fast market they lag behind your order stream, and the gap is where bots get hurt. This post walks through what Hyperliquid actually exposes, where those controls are insufficient for HFT, and how to layer a client-side notional ceiling that holds even during burst activity.
What Hyperliquid Gives You at the Exchange Level
Hyperliquid exposes two server-side controls relevant to position sizing:
Leverage per asset. You set this via the updateLeverage endpoint, signed with EIP-712. The call takes a coin string, an isCross boolean and an leverage integer. Cross-margin shares collateral across positions; isolated margin caps loss to the margin allocated to that specific position. For an HFT bot, isolated is almost always the right default — it prevents one blow-up asset from draining margin elsewhere.
Max notional per order. The exchange enforces per-order notional limits that vary by asset and by your account tier. As of mid-2025 these range from roughly $100k–$2M per order for liquid perps like BTC and ETH, lower for the long tail. These are ceiling checks on individual orders, not on your running position.
What the exchange does not give you out of the box: a user-configurable notional cap on the aggregate open position for an asset. You can be filled to multiples of the per-order limit through rapid sequential orders, and nothing on the exchange side stops that automatically.
Why Exchange-Level Limits Are Not Enough for HFT
In an HFT context, your bot can submit dozens of orders per second. The exchange's risk checks are stateless per-order — they do not sum your in-flight orders against your current position before accepting a new one. The sequence is:
- Bot submits order N.
- Exchange validates: margin available, per-order notional OK, leverage within allowed range.
- Exchange fills order N partially or fully.
- Position state updates — but there is a brief propagation lag before your websocket subscription reflects the new position.
- Bot submits order N+1, using position data that may not yet include the fill from N.
During a fast one-directional move, this lag compounds. You can end up holding 3–4x your intended position ceiling because every order passed the exchange's per-order check, and your local position state was stale when you sized each one. This is not a theoretical edge case — it is the normal behaviour during momentum bursts, which is exactly when you least want runaway exposure.
Building a Client-Side Notional Ceiling
The correct pattern is a pre-submission gate: before any order is signed and sent, the bot computes the projected notional and rejects the order if it would breach the ceiling. Here is what that looks like in practice:
interface PositionState {
coin: string;
szi: number; // signed size in base units from /info clearinghouse state
entryPx: number;
pendingNotional: number; // sum of in-flight order sizes * last price
}
function canSubmitOrder(
coin: string,
orderSzUnits: number,
markPx: number,
state: PositionState,
ceilNotional: number,
): boolean {
const currentNotional = Math.abs(state.szi) * markPx;
const pendingNotional = state.pendingNotional;
const addedNotional = Math.abs(orderSzUnits) * markPx;
return currentNotional + pendingNotional + addedNotional <= ceilNotional;
}
Key points on the implementation:
- Track pending separately. Maintain a local map of
orderId → notionalfor every order you have submitted but not yet confirmed filled or cancelled. Add topendingNotionalon submission, subtract on fill or cancel. This is your bridge across the exchange propagation lag. - Use mark price, not order price. Limit orders placed away from the market will move toward mark; sizing them at their limit price understates true exposure if they fill during a spike.
- Treat both sides independently if running a market-maker. A long ceiling and a short ceiling are separate limits. Net exposure can look small while gross exposure is large and your P&L is getting tossed around by funding.
Syncing State with the Hyperliquid API
Your local position tracker needs to reconcile against the exchange on two cadences:
Continuous (websocket). Subscribe to userFills and userEvents on the websocket to catch fills in near-real-time. On each userFills event, update szi and remove the corresponding entries from your pending map.
Periodic (REST reconcile). Every 5–30 seconds, pull the full clearinghouse state from /info with request type clearinghouseState and user set to your address. This catches any edge cases where a websocket event was dropped. Diff the reconciled szi against your local state and log any discrepancy above a small tolerance — those discrepancies are your blind spots.
The reconcile cycle also serves as a health check. If your pending map and the exchange state diverge by more than a configurable threshold, halt new order submission until they converge. This is a cheap guard that has saved positions in real deployments when a websocket reconnect caused events to be missed.
Per-Asset Ceiling Configuration
Store your ceilings in a simple config, keyed by coin:
const positionCeilings: Record<string, number> = {
"BTC": 50_000, // USD notional
"ETH": 25_000,
"SOL": 10_000,
"WIF": 3_000,
// ...
};
Size these based on:
- Available margin. A ceiling at 2–3x your margin for an asset at max leverage is already a margin-call position. Keep ceilings comfortably inside what your margin can sustain.
- ADV fraction. For any illiquid asset, a notional ceiling above ~0.5% of 24h ADV will start moving the book against you on the way in and out.
- Correlated assets. If you are running strategies on both ETH and stETH perps (when listed), gross notional across correlated assets needs its own aggregate ceiling — not just per-coin limits.
For a Hyperliquid perps bot running multiple concurrent strategies on the same asset, run one shared PositionState per coin across all strategies so the ceilings are global, not per-strategy. Strategy-level limits are additive inside the per-coin global limit.
Kill-Switch Integration
Position ceilings are a pre-entry control. You also need a post-entry control: if the position breaches the ceiling because of a reconcile discovering a fill that was missed, or an edge case in your pending accounting, the bot must immediately switch to reduce-only mode for that asset.
Reduce-only on Hyperliquid is the reduceOnly flag on the order object. When your kill-switch fires:
- Cancel all open orders on the asset.
- Set a flag that forces
reduceOnly: trueon all new orders for that coin. - Alert immediately (Telegram, PagerDuty, whatever you have).
- Do not automatically resume — require a human acknowledgement before normal operation restarts.
The cross-venue arbitrage work we've shipped follows the same pattern: separate pre-submission gates, position reconciliation loops, and manual-resume kill-switches. The specific numbers differ per venue but the architecture is the same.
A Note on Cross-Margin vs. Isolated
One decision that interacts with position ceilings: if you run cross-margin, a large move on one asset draws down margin available to others, which can cause positions on other assets to get liquidated even if those positions are individually small and within their notional ceilings. Isolated margin is cleaner for multi-asset HFT because it makes each asset's risk self-contained. The tradeoff is that you need to pre-allocate margin per asset rather than letting the exchange pool it, which means tying up more capital. For most production HFT bots on Hyperliquid, that is the right tradeoff — predictable, bounded, debuggable. Explore all of the Hyperliquid services we build to see where this fits into a full bot stack.
If you want a Hyperliquid bot with these risk controls built in from day one rather than bolted on after the first incident, get in touch — we scope and ship these systems end to end.
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