一键导入
gdex-perp-trading
HyperLiquid perpetual futures — open/close positions, set leverage, place market and limit orders with TP/SL, and manage open orders
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
HyperLiquid perpetual futures — open/close positions, set leverage, place market and limit orders with TP/SL, and manage open orders
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
CSS theming system for GDEX trading UIs — dark/light modes, trading colors, responsive breakpoints, and Tailwind CSS configuration
Troubleshoot GDEX SDK errors — error codes, encryption debugging, chain-specific quirks, HL gotchas, and copy trade pitfalls
Start here — GDEX overview, architecture, supported chains, available skills, and quickstart for cross-chain DeFi trading via managed-custody wallets
HyperLiquid HIP-3 outcome / event markets — list markets, place outcome orders, and manage outcome positions
Deposit and withdraw USDC to/from HyperLiquid for perpetual futures trading — constraints, amounts, and managed-custody flow
Import user-defined custom tokens so they appear in token details, balances, and portfolio across the platform
| name | gdex-perp-trading |
| description | HyperLiquid perpetual futures — open/close positions, set leverage, place market and limit orders with TP/SL, and manage open orders |
Trade perpetual futures on HyperLiquid through GDEX managed-custody. Supports long/short positions, configurable leverage, take-profit/stop-loss, and market/limit orders.
@gdexsdk/gdex-skill installedloginWithApiKey() — see gdex-authenticationPerp orders go through hlCreateOrder after a managed sign-in. There is no
openPerpPosition/closePerpPosition/setPerpLeverage — those don't exist. The
real flow:
import { GdexSkill, GDEX_API_KEY_PRIMARY } from '@gdexsdk/gdex-skill';
// ... do the managed sign-in (see gdex-authentication) to get sessionPrivateKey ...
const creds = { apiKey, walletAddress: controlAddress, sessionPrivateKey };
const res = await skill.hlCreateOrder({
coin: 'ETH', // 'BTC'|'ETH'|'SOL'… or a builder market 'xyz:NVDA' (lowercase dex prefix)
isLong: true,
price: String(mark), // mark price; for market orders this is the slippage bound
size: '0.05', // size in CONTRACTS (coin units), not USD
isMarket: true,
tpPrice: String(tp), // '' to skip
slPrice: String(sl), // '' to skip
leverage: 3, // 1–50; see below
...creds,
});
Leverage is supported and is sent as a top-level field in the order
request (the SDK forwards it for you). Verified: setting leverage: 3 opens at
3× (liquidation at the matching buffer) instead of HL's 20× default.
The standalone
/hl/update_leverageendpoint (hlUpdateLeverage) is 404 — do not use it. Leverage is set as part of the order via theleveragefield above.
Close with a reduce-only order (the opposite side, full size), or close everything:
await skill.hlCreateOrder({ coin: 'ETH', isLong: false, price: String(mark),
size: String(positionSize), reduceOnly: true, isMarket: true, tpPrice: '', slPrice: '', ...creds });
await skill.hlCloseAll({ ...creds }); // close all positions
Note HL's $11 minimum order value applies to closes too — you can't reduce a sub-$11 remainder; add to it first or let the stop close it.
Builder-dex assets are named dex:ASSET with a lowercase dex prefix
(xyz:NVDA, xyz:SPCX, flx:OIL). Pass the coin in that exact form (the SDK
normalizes casing). They trade through the same hlCreateOrder flow as default
markets and honor the leverage you pass (live-confirmed: xyz stock perps open
at 3× isolated, not 20×).
Collateral depends on the dex. The xyz stock/commodity perps are
USDC-collateralized and 24-hour — no collateral swap needed; trade them with
your existing HL USDC. Only dexes that settle in a different token (e.g. HYNA →
USDE) need hlSwapCollateral first. The account must be a unified account
(hlEnableTrading) to share margin across dexes.
Reading builder positions is dex-scoped. A xyz:NVDA position lives under the
xyz dex's isolated account, not the default clearinghouse — query
getHlClearinghouseState({ userAddress, dex: 'xyz' }). The default query (and
getHlClearinghouseStateAll, which omits xyz) returns it empty, which looks
like "the order never filled" when it actually did.
// Get open positions
const positions = await skill.getPerpPositions({
walletAddress: '0xYourAddress',
coin: 'BTC', // optional filter
});
// Each: { coin, side, size, entryPrice, markPrice, leverage, unrealizedPnl, liquidationPrice }
// Full account state
const state = await skill.getHlAccountState({ walletAddress: '0xYourAddress' });
// { accountValue, totalNtlPos, totalRawUsd, totalMarginUsed, withdrawable, positions[] }
// Mark price for an asset
const price = await skill.getHlMarkPrice({ coin: 'BTC' });
await skill.hlCreateOrder({
coin: 'ETH',
isLong: true,
price: '0', // '0' for market orders
size: '0.5', // position size
reduceOnly: false,
isMarket: true,
tpPrice: '4000', // take-profit (optional)
slPrice: '3200', // stop-loss (optional)
apiKey,
walletAddress,
sessionPrivateKey,
});
await skill.hlPlaceOrder({
coin: 'SOL',
isLong: false, // short
price: '180', // limit price
size: '10',
reduceOnly: false,
apiKey,
walletAddress,
sessionPrivateKey,
});
// Cancel specific order
await skill.hlCancelOrder({
coin: 'BTC',
orderId: '12345',
apiKey,
walletAddress,
sessionPrivateKey,
});
// Cancel all orders
await skill.hlCancelAllOrders({
apiKey,
walletAddress,
sessionPrivateKey,
});
WARNING: The
hlCloseAll//v1/hl/close_all_positionsendpoint is unreliable — it frequently returnsTIMEOUTor JSON parse errors from the backend. Use a reduce-only order instead (see below).
// ❌ Unreliable — may timeout
await skill.hlCloseAll({
apiKey,
walletAddress, // MUST be control address, not managed address
sessionPrivateKey,
});
// ✅ Reliable — close via reduce-only sell order
// To close a LONG position, place a SHORT reduce-only order for the exact size:
const btcPrice = await skill.getHlMarkPrice('BTC');
await skill.hlCreateOrder({
coin: 'BTC',
isLong: false, // opposite of your position
price: Math.round(btcPrice * 0.97).toString(), // 3% below mid for market sell
size: '0.001', // exact position size
reduceOnly: true, // close only, don't open new position
isMarket: true,
tpPrice: '0',
slPrice: '0',
apiKey,
walletAddress, // MUST be control address
sessionPrivateKey,
});
HL operations use a different crypto pipeline than spot trades. Getting any detail wrong produces 400 Unauthorized (code 103).
| Action | ABI Types | Fields |
|---|---|---|
hl_deposit | ['uint64', 'address', 'uint256', 'string'] | [chainId, tokenAddress, amount, nonce] |
hl_withdraw | ['string', 'string'] | [amount, nonce] |
hl_create_order | ['string', 'bool', 'string', 'string', 'bool', 'string', 'string', 'string', 'bool'] | [coin, isLong, price, size, reduceOnly, nonce, tpPrice, slPrice, isMarket] |
hl_place_order | ['string', 'bool', 'string', 'string', 'bool', 'string'] | [coin, isLong, price, size, reduceOnly, nonce] |
hl_close_all | ['string'] | [nonce] |
hl_cancel_order | ['string', 'string', 'string'] | [nonce, coin, orderId] |
hl_cancel_all_orders | ['string'] | [nonce] |
hl_update_leverage | ['string', 'uint32', 'bool', 'string'] | [coin, leverage, isCross, nonce] |
CRITICAL: hl_deposit chainId is uint64, NOT uint256. This is the #1 cause of Unauthorized errors. The backend re-encodes with uint64 for signature verification — if you encode with uint256, the hex differs and you get code 103.
All HL write operations require HlManagedCredentials:
interface HlManagedCredentials {
apiKey: string; // GDEX API key for AES encryption
walletAddress: string; // CONTROL wallet address (from sign-in), NOT managed address
sessionPrivateKey: string; // Session key from sign-in flow
}
BTC, ETH, SOL, DOGE, AVAX, APE, APT, ARB, ATOM, BCH, BLUR, BNB, COMP, CRV, DOT, EOS, FIL, FTM, HBAR, ICP, IMX, INJ, JUP, KPEPE, LDO, LINK, LTC, MATIC, MKR, NEAR, OP, ORDI, PEPE, PYTH, RNDR, RUNE, SEI, SHIB, SNX, STX, SUI, TIA, TON, TRX, UNI, WIF, WLD, XRP
The #1 cause of 400 Unauthorized (code 103) on HL operations is passing the wrong walletAddress.
0x53D0...2eD).0x9967...0f) that holds the actual funds.{action}-{walletAddress}-{data}, and the backend verifies the signature against the session key registered for that address.// ❌ WRONG — causes 400 Unauthorized (code 103)
const creds = { apiKey, walletAddress: managedAddress, sessionPrivateKey };
// ✅ CORRECT — use the control wallet address from sign-in
const creds = { apiKey, walletAddress: controlAddress, sessionPrivateKey };
| Endpoint | Status | What to Do |
|---|---|---|
hlCloseAll / /v1/hl/close_all_positions | Returns TIMEOUT or 400 | Place a reduce-only hlCreateOrder for the exact position size |
hlUpdateLeverage / /v1/hl/update_leverage | Returns 404 | The backend sets leverage automatically per order. Control effective leverage via position sizing. |
getGbotUsdcBalance | Returns 404 | Use getHlAccountState() or clearinghouse state to check balance |
// Get current positions
const state = await skill.getHlAccountState({ walletAddress: controlAddress });
const positions = state?.assetPositions || [];
for (const p of positions) {
const pos = p.position;
if (Number(pos.szi) !== 0) {
const markPrice = Number(pos.entryPx); // or fetch via getHlMarkPrice
await skill.hlCreateOrder({
coin: pos.coin,
isLong: Number(pos.szi) < 0, // opposite direction
price: '0', // market
size: Math.abs(Number(pos.szi)).toString(),
reduceOnly: true,
isMarket: true,
tpPrice: '0',
slPrice: '0',
apiKey,
walletAddress: controlAddress, // MUST be control address
sessionPrivateKey,
});
}
}
amount × 1.01 (1% fee buffer)Since leverage cannot be set via API:
getHlAccountState())price: '0' and isMarket: truetpPrice and slPrice can be '0' to skip TP/SL