Skip to content
Back to skills

Hyperliquid Account

ASecurity

Read a Hyperliquid account from the desk computer - positions and margin, spot balances, open orders including trigger details, fills, funding paid, ledger updates, order status by oid or cloid, historical orders, portfolio history, fee tier and rate-limit budget - with curl and Python SDK examples. Read-only, needs only the account address. Use for sizing inputs, book checks, reconciliation and reviews.

  • 74 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added August 31, 2026
ai-agentspythongobashapifrontend

Works with

  • api

Security analysis

A100/100

Scanned October 2, 2026

npx -y skills add galleonlabs/hypergrok-trading-desk --skill hyperliquid-account --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Hyperliquid Account?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Hyperliquid Account
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/galleonlabs-hyperliquid-account/badge)](https://www.skillsdirectory.com/skills/galleonlabs-hyperliquid-account)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: hyperliquid-account
description: Read a Hyperliquid account from the desk computer - positions and margin, spot balances, open orders including trigger details, fills, funding paid, ledger updates, order status by oid or cloid, historical orders, portfolio history, fee tier and rate-limit budget - with curl and Python SDK examples. Read-only, needs only the account address. Use for sizing inputs, book checks, reconciliation and reviews.
license: MIT
metadata:
  version: "1.0.1"
  author: Galleon Labs
  category: hyperliquid
  network-default: testnet
---

# Hyperliquid account reads

All reads are `POST /info`, unsigned. Use the **account** address (the main wallet the desk trades for), never the API wallet's address: queries on an agent address return empty results.

```bash
ADDR=$HYPERLIQUID_ACCOUNT_ADDRESS
BASE=$([ "$HYPERLIQUID_NETWORK" = mainnet ] && echo https://api.hyperliquid.xyz || echo https://api.hyperliquid-testnet.xyz)
hl() { curl -sS -m 15 -X POST "$BASE/info" -H 'Content-Type: application/json' -d "$1"; }
```

```python
import os
from hyperliquid.info import Info
from hyperliquid.utils import constants
from hyperliquid.utils.types import Cloid
NETWORK = os.environ.get("HYPERLIQUID_NETWORK", "testnet")
info = Info(constants.MAINNET_API_URL if NETWORK == "mainnet" else constants.TESTNET_API_URL, skip_ws=True)
ADDR = os.environ["HYPERLIQUID_ACCOUNT_ADDRESS"]
```

## Perp account state

```bash
hl "{\"type\":\"clearinghouseState\",\"user\":\"$ADDR\"}" | jq '{
  time, accountValue: .marginSummary.accountValue, totalNtlPos: .marginSummary.totalNtlPos,
  totalMarginUsed: .marginSummary.totalMarginUsed, withdrawable, crossMaintenanceMarginUsed,
  positions: [.assetPositions[].position | {coin, szi, entryPx, positionValue, unrealizedPnl, returnOnEquity,
              liquidationPx, marginUsed, leverage, maxLeverage, cumFunding: .cumFunding.sinceOpen}]}'
```

Python: `info.user_state(ADDR)`. `szi` is signed size; `leverage` is `{type: cross|isolated, value, rawUsd?}`; `crossMarginSummary` mirrors `marginSummary` for the cross portion. Margin ratio for a book check: `crossMaintenanceMarginUsed / crossMarginSummary.accountValue` (`marginSummary` also counts isolated margin).

Read the account's abstraction mode first: `{"type":"userAbstraction","user":ADDR}` returns `default`, `disabled`, `unifiedAccount`, `portfolioMargin` or `dexAbstraction`. Record it with the sizing inputs. The separate spot/perp balance model must not be assumed for unified, portfolio-margin or DEX-abstraction accounts. Check the mode's documented collateral basis and relevant account states, and compare with `activeAssetData` for trading capacity. Do not add spot USDC to perp equity or use `spot total - hold` as universal free margin: collateral can be shared, and a balance alone does not establish capacity. If the mode's equity or margin interpretation cannot be verified, report it as unavailable and reject new risk.

## Spot balances

```bash
hl "{\"type\":\"spotClearinghouseState\",\"user\":\"$ADDR\"}" | jq '.balances[] | {coin, token, total, hold, entryNtl}'
```

Python: `info.spot_user_state(ADDR)`. `hold` is the amount locked in open orders.

## Open orders

```bash
hl "{\"type\":\"openOrders\",\"user\":\"$ADDR\"}" | jq '.[] | {coin, side, limitPx, sz, oid, timestamp}'
hl "{\"type\":\"frontendOpenOrders\",\"user\":\"$ADDR\"}" | jq '.[] | {coin, side, limitPx, sz, origSz, oid, cloid, orderType, tif, reduceOnly, isTrigger, triggerPx, triggerCondition, isPositionTpsl, children}'
```

`side` is `B` (bid/buy) or `A` (ask/sell). Use `frontendOpenOrders` whenever you need to know whether an order is a stop or take-profit and whether it is position-tied. Python: `info.open_orders(ADDR)`, `info.frontend_open_orders(ADDR)`.

## Order status by oid or cloid

```bash
hl "{\"type\":\"orderStatus\",\"user\":\"$ADDR\",\"oid\":1839201122}" | jq '{status, order: .order.status, ts: .order.statusTimestamp, o: .order.order}'
hl "{\"type\":\"orderStatus\",\"user\":\"$ADDR\",\"oid\":\"0x9f3e0c1a2b3c4d5e6f708192a3b4c5d6\"}"
```

Returns `{"status":"order","order":{"order":{...},"status":"...","statusTimestamp":...}}` or `{"status":"unknownOid"}`. Status vocabulary: `open`, `filled`, `canceled`, `triggered`, `rejected`, `marginCanceled`, `reduceOnlyCanceled`, `siblingFilledCanceled`, `scheduledCancel`, `liquidatedCanceled`, plus rejection reasons such as `tickRejected`, `minTradeNtlRejected`, `perpMarginRejected`, `badTriggerPxRejected`, `iocCancelRejected`, `marketOrderNoLiquidityRejected`. Python: `info.query_order_by_oid(ADDR, oid)`, `info.query_order_by_cloid(ADDR, Cloid.from_str("0x..."))`.

This is the reconciliation call after any send whose response was lost.

## Fills

```bash
hl "{\"type\":\"userFills\",\"user\":\"$ADDR\"}" | jq '.[:20][] | {time, coin, side, px, sz, dir, closedPnl, fee, feeToken, crossed, oid, cloid, tid, hash}'
START=$(( $(date +%s000) - 86400000 ))
hl "{\"type\":\"userFillsByTime\",\"user\":\"$ADDR\",\"startTime\":$START}" | jq 'length'
```

`crossed: true` means taker; `fee` includes any builder fee and is negative for a rebate; `dir` reads like `Open Long`, `Close Short`; `startPosition` is the size before the fill; `closedPnl` is realised on that fill. `userFills` returns the most recent 2000; `userFillsByTime` returns up to 2000 per call from the last 10,000. Paginate with `startTime` = the last `time` you received (inclusive, because many fills share one millisecond) and de-duplicate by `tid`. Python: `info.user_fills(ADDR)`, `info.user_fills_by_time(ADDR, start_ms, end_ms)`.

## Funding paid and ledger

```bash
hl "{\"type\":\"userFunding\",\"user\":\"$ADDR\",\"startTime\":$START}" | jq '.[] | {time, coin: .delta.coin, usdc: .delta.usdc, rate: .delta.fundingRate, szi: .delta.szi}'
hl "{\"type\":\"userNonFundingLedgerUpdates\",\"user\":\"$ADDR\",\"startTime\":$START}" | jq '.[] | {time, type: .delta.type, delta}'
```

Funding `usdc` is signed from the account's point of view. Ledger updates cover deposits, withdrawals, transfers, liquidations and vault flows. Python: `info.user_funding_history(ADDR, start_ms)`, `info.user_non_funding_ledger_updates(ADDR, start_ms)`.

## Historical orders and TWAP fills

```bash
hl "{\"type\":\"historicalOrders\",\"user\":\"$ADDR\"}" | jq '.[:20][] | {status, statusTimestamp, o: (.order | {coin, side, limitPx, sz, origSz, oid, cloid, orderType, tif, reduceOnly, isTrigger, triggerPx})}'
hl "{\"type\":\"userTwapSliceFills\",\"user\":\"$ADDR\"}" | jq '.[:5]'
```

Up to 2000 recent orders with their final status; the Trade Reviewer's source for "was the stop on the exchange the whole time". Python: `info.historical_orders(ADDR)`, `info.user_twap_slice_fills(ADDR)`.

## Portfolio history, fees, rate limit, role

```bash
hl "{\"type\":\"portfolio\",\"user\":\"$ADDR\"}" | jq '.[] | select(.[0]=="day" or .[0]=="week") | {period: .[0], pnl: .[1].pnlHistory[-1], value: .[1].accountValueHistory[-1], vlm: .[1].vlm}'
hl "{\"type\":\"userFees\",\"user\":\"$ADDR\"}" | jq '{userCrossRate, userAddRate, userSpotCrossRate, userSpotAddRate, activeReferralDiscount, activeStakingDiscount}'
hl "{\"type\":\"userRateLimit\",\"user\":\"$ADDR\"}" | jq .
hl "{\"type\":\"userRole\",\"user\":\"$ADDR\"}" | jq .
hl "{\"type\":\"extraAgents\",\"user\":\"$ADDR\"}" | jq '.[] | {address, name, validUntil}'
```

`portfolio` gives PnL and account-value history per period (`day`, `week`, `month`, `allTime`, and perp-only variants); `userFees` gives the effective taker (`userCrossRate`) and maker (`userAddRate`) rates for the strategy lab and reviews; `userRateLimit` shows the address's action budget (`nRequestsUsed`, `nRequestsCap`, `cumVlm`); `userRole` classifies an address (`user`, `agent`, `vault`, `subAccount`, `missing`); `extraAgents` lists approved API wallets with expiry. Python: `info.portfolio(ADDR)`, `info.user_fees(ADDR)`, `info.user_rate_limit(ADDR)`, `info.user_role(ADDR)`, `info.extra_agents(ADDR)`.

Per-market account data (leverage setting, available to trade, max trade sizes) without opening a position: `hl "{\"type\":\"activeAssetData\",\"user\":\"$ADDR\",\"coin\":\"ETH\"}"`.

To verify signing scope, query `userRole` for the public **API wallet** address separately: require `role: agent` with `data.user` equal to the ticket's account, on the ticket's network, and check its unexpired approval in `extraAgents`. Record those fresh reads before sending. A successful unsigned `/info` account query works without a key and cannot attest to an API wallet's authority.

## Sub-accounts and vaults (read only on this desk)

`{"type":"subAccounts","user":ADDR}` lists sub-accounts with their states; `{"type":"userVaultEquities","user":ADDR}` lists vault deposits; `{"type":"vaultDetails","vaultAddress":"0x..."}` describes a vault. The desk reads these for completeness and does not move funds between them.

## Book check recipe (Risk Manager)

1. `clearinghouseState` for equity, positions, margin, liquidation prices.
2. `frontendOpenOrders` for protection (reduce-only triggers per position) and orphans.
3. `metaAndAssetCtxs` for mark prices to compute liquidation distance.
4. `userFunding` since start of day for funding paid.
5. `portfolio` or the journal for start-of-day equity, to compute day PnL against the daily loss stop.

Report per `desk-risk-limits` section 3.

## Pitfalls

- Querying the API wallet's address. Everything comes back empty and looks like "no positions".
- Assuming `openOrders` shows trigger details; it does not. Use `frontendOpenOrders`.
- Reading `userFills` and forgetting the 2000-item window; use `userFillsByTime` with pagination for reviews.
- Treating `withdrawable` as free margin for new positions; use `accountValue - totalMarginUsed` with headroom, and check the tier.
- Mixing networks: a mainnet address on the testnet endpoint is a different (probably empty) account.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.