hyperliquid-account
Skill418 starsChanged 13 days agoMITView on GitHub
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.
In this repository17 other files it gives its agents.
- AGENTS.mdAGENTS.md
- desk-execution-protocolSkill
- desk-incident-responseSkill
- desk-monitoringSkill
- desk-operating-modelSkill
- desk-post-trade-reviewSkill
- desk-risk-limitsSkill
- desk-strategy-labSkill
- desk-trade-lifecycleSkill
- hypergrok-bootstrapSkill
- hyperliquid-advancedSkill
- hyperliquid-api-referenceSkill
- hyperliquid-market-dataSkill
- hyperliquid-ordersSkill
- hyperliquid-positionsSkill
- hyperliquid-setupSkill
- hyperliquid-websocketSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 14
All reads are POST /info, unsigned.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
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.
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"; }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
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
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
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
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
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 account/network plus (time, coin, tid). A full page that cannot advance beyond a shared timestamp is incomplete coverage; never skip that boundary to force progress. Python: info.user_fills(ADDR), info.user_fills_by_time(ADDR, start_ms, end_ms).
Funding paid and ledger
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
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 current/final status. This bounded history helps reconstruct protection; proving that a stop covered the position throughout also needs the journal and contemporaneous order/position evidence. Python: info.historical_orders(ADDR), info.user_twap_slice_fills(ADDR).
Portfolio history, fees, rate limit, role
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 account's base taker (userCrossRate) and maker (userAddRate) rates; apply the documented referral, collateral, stable-pair and HIP-3 deployer/growth modifiers for the chosen market before estimating costs, then reconcile actual fee and feeToken from fills; 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)
clearinghouseStatefor equity, positions, margin, liquidation prices.frontendOpenOrdersfor protection (reduce-only triggers per position) and orphans.metaAndAssetCtxsfor mark prices to compute liquidation distance.userFundingsince start of day for funding paid.portfolioor 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
openOrdersshows trigger details; it does not. UsefrontendOpenOrders. - Reading
userFillsand forgetting the 2000-item window; useuserFillsByTimewith pagination for reviews. - Treating
withdrawable, spot balances oraccountValue - totalMarginUsedas universal trading capacity. Verify the abstraction mode, use its documented margin basis with headroom, and checkactiveAssetDataand the notional tier. - Mixing networks: a mainnet address on the testnet endpoint is a different (probably empty) account.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

