# Hyperliquid knowledge base

Practical knowledge about Hyperliquid (HL): API, SDK, accounts, balances, orders, TWAP, WebSocket, limits, fees, risk, historical data, backtesting limits, and API operational facts. This is hands-on HL API practice: mechanics, formulas, thresholds, and pitfalls. Every rule has a reason, and every fact verified live has a verification date.

The facts were checked with this stack: `@nktkas/hyperliquid` 0.27.1, `viem` 2.5x, `ws` 8.x, Node ≥ 20, and TypeScript. Freshness: most material was checked through 2026-09-14; later checks carry their own dates.

How to read annotations inside the files:
- **“verified” with a date** (in the text or brackets, for example `[verified 2026-09-23]`) means the fact was checked with live requests or observation; do not simplify such rules;
- **“according to documentation, not verified”** means the statement comes from public HL documentation and was not checked live;
- **“Open questions / not verified”** is the final section of every file; a combined summary appears at the end of this page.

---

## Files

| File | Contents | When to open it |
|---|---|---|
| [sdk-and-api.md](sdk-and-api.md) | Mainnet/testnet endpoints, `/info` and `/exchange` formats, the `@nktkas/hyperliquid` SDK and `viem`, SDK error classes, EIP-712 signing, egress-IP binding | First HL code, choosing the SDK or raw fetch, interpreting SDK exceptions |
| [accounts.md](accounts.md) | Master, agent/API wallet, sub-accounts, vault, builder address, `userRole`, collateral modes (manual / Unified Account / portfolio margin), `agentEnableDexAbstraction` | Connecting a key, trading from a sub-account, the `does not exist` error, HIP-3 through an agent |
| [balance-and-equity.md](balance-and-equity.md) | `clearinghouseState` per dex, `spotClearinghouseState`, `portfolio`, ledger; capital and margin-ratio formulas; phantom reads and their filtering | Displaying balances, equity-based sizing, risk caps, drawdown safeguards |
| [orders.md](orders.md) | Order payload, tif, “market” through IoC, reduceOnly, native TP/SL, price and size rounding, the $10 minimum, responses and errors, cancellations, retries, reading open orders | Any placement, cancellation, or order reconciliation |
| [twap.md](twap.md) | `twapOrder` / `twapCancel`, the `userTwapSliceFills` feed, verification procedure | Placing TWAP, account PnL from both feeds, “the position shrinks but there are no fills” |
| [market-data.md](market-data.md) | `meta`, precision (`szDecimals`, tick), HIP-3 dex and asset id, `allMids`, `l2Book`, `candleSnapshot`, spot, equity-market hours on `xyz`, delisted assets | Market metadata, prices, order book, candles, `xyz:` markets |
| [fills-and-history.md](fills-and-history.md) | `userFills` / `userFillsByTime` and pagination, fill fields, grouping partials, position and PnL reconstruction, `historicalOrders`, ledger, leaderboard, builder-fills dump | Account trade history, PnL, builder-fee accounting |
| [websocket.md](websocket.md) | Subscriptions and frame formats, snapshot vs update, user-tracking limits, ping/pong, reconnect, ghost slots, self-healing | Any WS client, market-data recording, live positions |
| [rate-limits.md](rate-limits.md) | IP weight and request weights, address limit, short 429 window, limiter architecture, retry policy, egress IP, bulk-request pace | Load design, 429 storms, bulk reads and backfills |
| [fees.md](fees.md) | Maker/taker tiers, `userFees`, fee calculation from fills, builder fee and `approveBuilderFee`, builder-revenue accounting, fee effects on strategies | Strategy economics, builder code, backtest fees |
| [risk-and-margin.md](risk-and-margin.md) | `updateLeverage`, cross/isolated, ROE, liquidation, utilization ceiling and rejection loop, `portfolio` drawdown, “naked” positions, native and software stops, reliable closing and exit priority, preflight | Leverage, stops, closing positions, preparing for a live launch |
| [backtest-and-data.md](backtest-and-data.md) | Historical data available from the public API (candles, account fills, funding) and its limits, data the API does not provide, caching and reproducibility, why backtesting on HL is hard and where it lies, recording your own data (one-second candles) | Any backtest, recording your own data |
| [ops-and-deploy.md](ops-and-deploy.md) | API latency, per-IP limits, safe client shutdown, REST/WS telemetry, and open limit questions | API operational properties |
| [pitfalls.md](pitfalls.md) | JS numeric precision, “not read” ≠ “empty,” replica lag, races, caches, remembered state, closing as a critical path, duplicate processes, network, misleading instruments | Trading-code review, diagnosing strange behavior |

Nado and Lighter are documented by their dedicated skills and knowledge bases.

---

## Quick answers

**1. How do I obtain account balance and equity?**
Call `clearinghouseState` for every perp dex (without `dex`, only the main dex is returned; obtain the dex list from `perpDexs`) → sum `marginSummary.accountValue`. Add free spot stablecoins from `spotClearinghouseState`: `max(0, total − (spotHold ?? hold))` for USDC/USDT/USDT0/USDH/USDE. Read perp and spot in one `Promise.all`. Do not add all spot balances: on Unified Account that double-counts them. Do not trust a single read. → [balance-and-equity.md](balance-and-equity.md) §3, §6

**2. What account types exist?**
Master (funds, withdrawals, `approveAgent`, `approveBuilderFee`); agent/API wallet (signs trading actions, holds no funds, cannot withdraw, lifetime up to 180 days, limit of 1 unnamed + 3 named + 2 per sub-account); sub-account (its own funds, margin, and address limit); vault. `userRole` (weight 60) identifies the address type. → [accounts.md](accounts.md) §1–4

**3. How does an agent wallet work?**
`ExchangeClient` signs with the agent key, but `user` in info requests is the account address, not the agent address. `User or API Wallet 0x… does not exist` means the agent is unauthorized, expired, or revoked; retries are useless. Verify the binding with a signed `updateLeverage`: `extraAgents` does not show unnamed agents. → [accounts.md](accounts.md) §2

**4. How do I trade from a sub-account?**
The main account's agent signs, while `defaultVaultAddress` in the client is the sub-account address. The sub-account does not need a separate key. `reserveRequestWeight` does not credit purchased capacity to the sub-account. → [accounts.md](accounts.md) §3

**5. How do I place and cancel a TWAP?**
`twapOrder { a, b, s, r, m, t }`, where `m` is minutes and `t` is randomize. The response contains `running.twapId`. Cancel with `twapCancel { a, t: twapId }`. According to documentation, a slice runs about every 30 seconds with slippage ≤3%. Live placement was not verified: smoke-test it first. Slices appear neither in `userFills` nor in `frontendOpenOrders`, only in `userTwapSliceFills`. → [twap.md](twap.md) §2, §7

**6. How do I round price and size?**
A perp price has at most 5 significant digits and at most `6 − szDecimals` decimal places; an integer price is always valid. Floor size to `10^-szDecimals` with an epsilon (`Math.floor(sz*f + 1e-9)/f`). Send both values as strings, and make the planner and executor call the same function. When price crosses a power of ten, the tick changes tenfold. → [orders.md](orders.md) §5, [market-data.md](market-data.md) §3

**7. What is the minimum order?**
$10 notional at the rounded order price: `Order must have minimum value of $10.` Skip openings and partial reductions below the minimum. Do not constrain a full reduceOnly close by the minimum: ceil and lift it to the minimum; the exchange caps it at the position. → [orders.md](orders.md) §6

**8. Which tif values exist, and how do I make a “market” order?**
`Gtc` rests on the book. `Ioc` executes what it can and discards the remainder. `Alo` is post-only: a crossing order is rejected (`badAloPxRejected`). HL has no market-order type: use an IoC limit at mid ± slippage; exactly at mid it will not execute. TP/SL orders are trigger orders activated by mark price. → [orders.md](orders.md) §2, §4

**9. What are the weights and limits?**
IP: approximately 1200 weight/min across `/info` + `/exchange`. Weights: 2 for `l2Book`, `allMids`, `clearinghouseState`, `spotClearinghouseState`; 60 for `userRole`; 20 for nearly everything else, with a response-volume surcharge on history downloads. Exchange IP weight is `1 + floor(n/40)` per batch. Address capacity is 10,000 + 1 request per $1 of volume; after exhaustion, one request per 10 seconds remains. The limit window is shorter than one minute; configure limiter sustained rate, burst, and concurrency explicitly for the actual workload. → [rate-limits.md](rate-limits.md) §1–4, §9

**10. What should I do on 429 and 5xx?**
A 429 is rejected before the matching engine, so retrying it is safe. A 5xx, timeout, or disconnect means the outcome is unknown: do not repeat OPEN/INCREASE, partial reduceOnly, or transfers; reconcile against the book first. Cancel, `updateLeverage`, `agentEnableDexAbstraction`, and a full reduceOnly close are idempotent. → [orders.md](orders.md) §10, [rate-limits.md](rate-limits.md) §5

**11. Which WS subscriptions exist, and what are their limits?**
Use `wss://api.hyperliquid.xyz/ws`; ping every 30 seconds because the server disconnects after 60 seconds of silence. For user subscriptions (`userFills`, `orderUpdates`, `allDexsClearinghouseState`, `webData3`), documentation states 10 unique addresses per IP, while about 20 total across all connections was measured. New connections from the same IP add no slots. Rejection arrives on `channel:"error"`. There are about 1000 total subscriptions per IP. WS does not provide spot balances. → [websocket.md](websocket.md) §2, §4

**12. What are the candle limitations?**
`candleSnapshot` returns at most about 5000 candles per call; if the window is wider, it returns the newest candles. Depth: 5m ≈ 18 days, 15m ≈ 52 days. The minimum interval is 1m; there are no one-second candles, so record them yourself from WS `trades`. The final bar is open. For HIP-3, use `coin: 'xyz:SP500'` without a `dex` field. → [market-data.md](market-data.md) §8, [backtest-and-data.md](backtest-and-data.md) §2, §8

**13. How much are fees, and how does builder fee work?**
Base perp tier: maker 1.5 bps, taker 4.5 bps, with the tier based on 14-day volume. `userFees` returns the account's rates; actual fees are in each fill's `fee` field. Builder fee uses `builder: { b, f }` in the order, with lowercase `b` and `f` in tenths of a basis point; the perp cap is 100 (0.1%). It requires `approveBuilderFee` signed by the master wallet. If `f` exceeds the approved value, the entire order is rejected. → [fees.md](fees.md) §1, §3

**14. What is the liquidation formula?**
Approximately `ROE_liq = leverage / (2 × maxLeverage) − 1`; the price move to liquidation is `1/leverage − 1/(2·maxLeverage)`. At maximum leverage, liquidation occurs at −50% ROE. This is the base tier without other cross collateral or funding. → [risk-and-margin.md](risk-and-margin.md) §5

**15. How do I set leverage?**
Call `updateLeverage({ asset, isCross: !onlyIsolated, leverage })`, where `leverage = max(1, min(floor(x), maxLeverage))`. Set it before the entry order. Most `xyz` pairs are isolated-only. Per-coin leverage is visible only when a position is open. → [risk-and-margin.md](risk-and-margin.md) §2

**16. Why are orders and positions on `xyz:` missing?**
Each HIP-3 dex is a separate universe: request `clearinghouseState`, `frontendOpenOrders`, `meta`, and `allMids` with `dex: 'xyz'`. Asset id is `100000 + perpDexIndex × 10000 + index`. The agent needs `agentEnableDexAbstraction`. Exception: `userFills`/`userFillsByTime` ignore the `dex` parameter. → [market-data.md](market-data.md) §4–5, [accounts.md](accounts.md) §5.3

**17. How do I export trade history?**
`userFills` returns up to 2000 latest fills. `userFillsByTime` returns up to 2000 **oldest** fills from `startTime`, so page forward with `startTime = max(time)` from the page (without `+ 1`: a page can end inside a millisecond; corrected 2026-09-23), deduplicate by `tid`, and do not pass `dex`. Group partials by `oid`. Fetch TWAP slices separately from `userTwapSliceFills`. → [fills-and-history.md](fills-and-history.md) §2–5

**18. Why is backtesting on HL hard?**
Fine candle intervals are available only for the latest ~5000 bars (5m ≈ 18 days), and the public API provides neither one-second candles nor historical order books. Intrabar price order and book queue position are invisible, and funding is included in neither candles nor `closedPnl`. Therefore an HL backtest is, at best, a relative comparison of alternatives; absolute return figures are almost always optimistic. Missing data can be obtained only through your own recording started in advance. → [backtest-and-data.md](backtest-and-data.md) §5, §7, §8

**19. How do I test code without real orders?**
Use isolated tests with a stubbed network interface and representative response fixtures. Test partial batch success and ambiguous outcomes separately: “the exchange applied it, but the client timed out” requires reconciliation against the book, not a blind retry. A test fixture is not evidence that an exact error string was observed on the exchange. → [orders.md](orders.md) §7, §10, §11

---

## Open questions (summary)

Everything below is either not verified live or contradictory. Details and adopted working assumptions are in the “Open questions” section of the named file.

**SDK, signing, accounts**
- Whether `fetchOptions.dispatcher` reaches the SDK transport or `setGlobalDispatcher` is required: verify with an echo request. → sdk-and-api.md
- `agentEnableDexAbstraction`: exact signature and idempotency; a call through `vaultAddress` on a sub-account and on a fresh `disabled` account was not verified with a live key. → sdk-and-api.md, accounts.md
- The asset-id formula was verified only for `xyz`; other HIP-3 dexes were not checked. → sdk-and-api.md
- `hyperliquidChain: 'Testnet'` for user-signed actions, `usdSend` on a non-unified account, the 0.27.x SDK call for `approveBuilderFee`. → sdk-and-api.md, fees.md
- Agents: semantics of `validUntil = 0` (assumed “no expiry”), weight of `extraAgents` (assumed 20), error text when the agent limit is exceeded, repeated `approveAgent` behavior, expiration of an agent created in the UI. → accounts.md
- Relationship between portfolio margin and Unified Account; `usd` units in `subAccountTransfer`. → accounts.md

**Balance and margin**
- A jump in `accountValue` on a unified account without fills after placing orders (double-counted `hold` or reads taken at different moments). → balance-and-equity.md
- Capital with borrowing (`borrowed`, `ltv`); USDC under isolated positions outside `accountValue`; meaning of `spotState.totalRawUsd`; `withdrawable` semantics on unified/PM. → balance-and-equity.md
- HIP-3 dex collateral isolation in different account modes: shared pool or separate funds. → balance-and-equity.md, risk-and-margin.md
- Whether selected leverage affects liquidation price on cross; liquidation formula without margin tiers; ADL, partial liquidation, `updateIsolatedMargin`. → risk-and-margin.md

**Orders and TWAP**
- Whether HL accepts a reduceOnly IoC below $10 without lifting the size (the reliable approach remains lifting it). → orders.md
- Weight of `openOrders` (assumed 20) and its fields. → orders.md, rate-limits.md
- `modify`/`batchModify` (whether oid and queue position are preserved), `cancelByCloid`, `scheduleCancel`, how long `orderStatus` is retained before `unknownOid`. → orders.md
- Exact Alo and other rejection strings, `orderType` on trigger orders, spot price rule. → orders.md
- `twapOrder`/`twapCancel` were not verified live: `randomize` semantics, minimum TWAP and slice notional, `reduceOnly` after the position closes, boundaries for `m`. → twap.md
- Slice pace (5–9 per minute observed versus one per 30 seconds in documentation); whether `userTwapSliceFills` honors `startTime`; `userTwapSliceFillsByTime`, `twapHistory`, WS `userTwapHistory`. → twap.md

**Data and history**
- Exact `candleSnapshot` weight and response-volume surcharge; `1m` history depth; weekly and monthly intervals. → market-data.md, rate-limits.md
- `userFillsByTime` depth (documentation says only about the latest 10,000 fills for an address); `aggregateByTime` and pagination; shapes of `userFunding` and the `liquidation` fill field. → fills-and-history.md, backtest-and-data.md
- WS `trades`, `candle`, `bbo`, and `activeAssetCtx` were not checked on a live socket; whether a trade snapshot is sent on reconnect. → websocket.md
- Trading hours for HIP-3 dexes other than `xyz`; funding and OI fields in `metaAndAssetCtxs`. → market-data.md

**Limits and WebSocket**
- User tracking: 10 (documentation), 15 (error text), ~20 (measurement)—what exactly each number counts; WS connections per IP: 10 or 100 (plan for 10). → websocket.md, rate-limits.md
- Length of the short IP-limit window; sustained ~200–300 info requests/min during bulk sequential requests from one IP versus the theoretical 600. → rate-limits.md
- How `nRequestsCap`/`nRequestsSurplus` change after `reserveRequestWeight`; address-limit error text; “429 on exchange = not executed” was verified live but not in documentation. → rate-limits.md
- Built-in SDK retries and whether the WS transport's `maxRetries=3` can be removed. → rate-limits.md, websocket.md

**Fees**
- Tiers above the base tier and rebates have medium confidence; spot and HIP-3 dex rates; whether builder fee is included in the `fee` field; whether `builder` may be attached to trigger orders. → fees.md

**Historical data and backtesting limitations**
- The one-second-candle recording recipe in this knowledge base was not verified live; the effect of one-second resolution on stops was not measured. → backtest-and-data.md
- Candles do not reveal book queue, actual latency, or slippage; the effect of look-ahead on the last bar was not measured. The `userFunding` response cap (500 rows in one measurement) was not checked separately. → backtest-and-data.md

**Operations and other topics**
- Exact limits for user-specific addresses and total WS connections per IP conflict and require another check. → ops-and-deploy.md §4
- oid and the 2^53 boundary: at what value precision loss becomes real (store it as a string already). → pitfalls.md
- A safe interval for REST fallback position reads during WS degradation is unknown. → pitfalls.md

---

## Disclaimer

This is not financial advice or a recommendation to trade. Numbers from backtests and observations describe particular periods and do not guarantee results. The Hyperliquid API, limits, fees, and response shapes change without notice: before use, verify facts against current official HL documentation and live requests, especially anything marked “not verified.” Run code snippets in dry-run mode and with minimal capital first. You are responsible for how you use them.

## Code: the @markpaper/hl-kit package

The rules in this knowledge base are implemented in the `packages/hl-kit` library in the markpaper repository (npm `@markpaper/hl-kit`, Apache-2.0): price and size rounding and the $10 minimum, asset ids and metadata registry, `/info` and `/exchange` throttling, equity across all dexes, fills with TWAP, candles, fees and liquidation, a WS client, and safe order placement. Documentation and examples are on the [hl-kit SDK](https://markpaper.xyz/sdks/hl-kit) page. Functions tagged `@experimental` correspond to “according to documentation, not verified” notes.

## License

The `hyperliquid` knowledge base and skill are distributed under **CC BY 4.0**. You may copy, adapt, and use them, including commercially, but every publication must credit “markpaper — Hyperliquid knowledge base,” link to the original and the license, and indicate changes. See [LICENSE.md](LICENSE.md) for the terms.

---

<!-- license-footer -->
_© markpaper authors. Licensed under [CC BY 4.0](LICENSE.md): when publishing or adapting the material, credit “markpaper — Hyperliquid knowledge base” and link to the original and the license._
