From gmx-io
Trades perpetuals and spot tokens on GMX V2 DEX across Arbitrum, Avalanche, Botanix via TypeScript SDK or REST API. Supports market/limit/stop orders up to 100x leverage.
How this skill is triggered — by the user, by Claude, or both
Slash command
/gmx-io:gmx-tradingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
GMX V2 is a decentralized perpetual and spot exchange using oracle-based pricing (Chainlink Data Streams) instead of AMM curves. Traders get CEX-like execution with onchain settlement.
GMX V2 is a decentralized perpetual and spot exchange using oracle-based pricing (Chainlink Data Streams) instead of AMM curves. Traders get CEX-like execution with onchain settlement.
Supported chains:
| Chain | Chain ID | Native Token |
|---|---|---|
| Arbitrum | 42161 | ETH |
| Avalanche | 43114 | AVAX |
| Botanix | 3637 | BTC |
Two integration paths:
@gmx-io/sdk) — Full read + write: fetch markets, create orders, manage positionsTrading modes:
Install the SDK:
npm install @gmx-io/sdk viem
Import note: The SDK's ESM build has broken imports (missing file extensions). Use CommonJS require or configure your bundler to resolve extensionless imports. In Node.js scripts, use
const { GmxSdk } = require("@gmx-io/sdk").
Create an SDK instance:
const { GmxSdk } = require("@gmx-io/sdk");
const sdk = new GmxSdk({
chainId: 42161,
rpcUrl: "https://arb1.arbitrum.io/rpc",
oracleUrl: "https://arbitrum-api.gmxinfra.io",
subsquidUrl: "https://gmx.squids.live/gmx-synthetics-arbitrum:prod/api/graphql",
});
Chain configuration:
| Chain | chainId | oracleUrl | subsquidUrl |
|---|---|---|---|
| Arbitrum | 42161 | https://arbitrum-api.gmxinfra.io | https://gmx.squids.live/gmx-synthetics-arbitrum:prod/api/graphql |
| Avalanche | 43114 | https://avalanche-api.gmxinfra.io | https://gmx.squids.live/gmx-synthetics-avalanche:prod/api/graphql |
| Botanix | 3637 | https://botanix-api.gmxinfra.io | https://gmx.squids.live/gmx-synthetics-botanix:prod/api/graphql |
Set up a wallet for write operations:
const { createWalletClient, http } = require("viem");
const { privateKeyToAccount } = require("viem/accounts");
const { arbitrum } = require("viem/chains");
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const walletClient = createWalletClient({
account,
chain: arbitrum,
transport: http("https://arb1.arbitrum.io/rpc"),
});
const sdk = new GmxSdk({
chainId: 42161,
rpcUrl: "https://arb1.arbitrum.io/rpc",
oracleUrl: "https://arbitrum-api.gmxinfra.io",
subsquidUrl: "https://gmx.squids.live/gmx-synthetics-arbitrum:prod/api/graphql",
account: account.address,
walletClient,
});
Lightweight alternative (read-only, no RPC needed):
const { GmxApiSdk } = require("@gmx-io/sdk/v2");
const apiSdk = new GmxApiSdk({ chainId: 42161 });
const markets = await apiSdk.fetchMarketsInfo(); // Returns array-like of MarketInfo objects
// Access: markets[0].marketTokenAddress, markets[0].indexTokenAddress, etc.
The SDK provides convenience methods that handle amount calculation and transaction submission automatically.
Important: Never hardcode market or token addresses. Always fetch them dynamically — addresses differ per chain and can change between deployments.
// Fetch all markets and tokens
const { marketsInfoData, tokensData } = await sdk.markets.getMarketsInfo();
// Find ETH/USD market by index token symbol
// Note: On Arbitrum, perpetual markets use "WETH" as the index token symbol.
// Markets with symbol "ETH" are spot-only swap pools.
const ethUsdMarket = Object.values(marketsInfoData).find(
(m) => tokensData[m.indexTokenAddress]?.symbol === "WETH" && !m.isSpotOnly
);
// Get token addresses from market
const marketAddress = ethUsdMarket.marketTokenAddress;
const longToken = ethUsdMarket.longTokenAddress; // e.g., WETH
const shortToken = ethUsdMarket.shortTokenAddress; // e.g., USDC
// Find a token by symbol
const usdcAddress = Object.values(tokensData).find((t) => t.symbol === "USDC")?.address;
Open a long position:
await sdk.orders.long({
marketAddress, // from step 1
payTokenAddress: usdcAddress, // token you're paying with
collateralTokenAddress: longToken, // WETH as collateral for longs
payAmount: 100000000n, // 100 USDC (6 decimals)
leverage: 50000n, // 5x leverage (basis points)
allowedSlippageBps: 100, // 1% slippage
});
Open a short position:
await sdk.orders.short({
marketAddress,
payTokenAddress: usdcAddress,
collateralTokenAddress: shortToken, // USDC as collateral for shorts
payAmount: 100000000n,
leverage: 50000n,
});
Swap tokens:
const arbAddress = Object.values(tokensData).find((t) => t.symbol === "ARB")?.address;
const linkAddress = Object.values(tokensData).find((t) => t.symbol === "LINK")?.address;
await sdk.orders.swap({
fromTokenAddress: arbAddress,
toTokenAddress: linkAddress,
fromAmount: 1000000000000000000n, // 1 ARB (18 decimals)
allowedSlippageBps: 100,
});
Limit orders — add limitPrice for positions or triggerPrice for swaps:
await sdk.orders.long({
marketAddress,
payTokenAddress: usdcAddress,
collateralTokenAddress: longToken,
payAmount: 100000000n,
leverage: 50000n,
limitPrice: 3000000000000000000000000000000000n, // $3000 (30 decimals)
});
Key parameters:
leverage — In basis points: 10000n = 1x, 50000n = 5x, 1000000n = 100xallowedSlippageBps — Default 100 (1%). Range: 1-500payAmount — Pay this much collateral. Alternative: use sizeAmount to specify position sizefromAmount / toAmount — For swaps, specify input or desired output amountThere is no convenience close() method. Closing requires computing decrease amounts via getDecreasePositionAmounts() from @gmx-io/sdk/utils/trade, then calling createDecreaseOrder().
Important: Always re-fetch
marketsInfoDataandtokensDataright before closing. These contain oracle prices that go stale within seconds — using old data produces anacceptablePricethe keeper will reject.
const { getDecreasePositionAmounts } = require("@gmx-io/sdk/utils/trade");
// 1. Fetch FRESH market data (prices go stale quickly)
const { marketsInfoData, tokensData } = await sdk.markets.getMarketsInfo();
// 2. Get the position to close
const positionsInfo = await sdk.positions.getPositionsInfo({
marketsInfoData, tokensData, showPnlInLeverage: false,
});
const position = Object.values(positionsInfo).find(
(p) => p.marketAddress === marketAddress && p.isLong === true
);
// 3. Compute decrease amounts
const marketInfo = marketsInfoData[position.marketAddress];
const collateralToken = tokensData[position.collateralTokenAddress];
const { minCollateralUsd, minPositionSizeUsd } = await sdk.positions.getPositionsConstants();
const uiFeeFactor = await sdk.utils.getUiFeeFactor();
const decreaseAmounts = getDecreasePositionAmounts({
marketInfo,
collateralToken,
isLong: position.isLong,
position,
closeSizeUsd: position.sizeInUsd, // Full close. Use a smaller value for partial close.
keepLeverage: false,
userReferralInfo: undefined,
minCollateralUsd,
minPositionSizeUsd,
uiFeeFactor,
isSetAcceptablePriceImpactEnabled: false,
});
// 4. Submit the decrease order
await sdk.orders.createDecreaseOrder({
marketInfo,
marketsInfoData,
tokensData,
isLong: position.isLong,
allowedSlippage: 300, // 3% — use higher slippage for decrease to avoid keeper rejection
decreaseAmounts,
collateralToken,
});
| Module | Key Methods | Description |
|---|---|---|
sdk.markets | getMarkets(), getMarketsInfo(), getDailyVolumes() | Market data and liquidity info |
sdk.tokens | getTokensData(), getTokensBalances() | Token metadata, prices, balances |
sdk.positions | getPositions(), getPositionsInfo(), getPositionsConstants() | Open position data |
sdk.orders | long(), short(), swap(), getOrders(), cancelOrders() | Order creation and management |
sdk.trades | getTradeHistory() | Historical trade actions |
sdk.utils | getGasLimits(), getGasPrice(), getExecutionFee(), getUiFeeFactor() | Gas and fee estimation |
sdk.oracle | getTickers(), getMarkets(), getTokens() | Direct oracle data access |
Typical read flow:
const { marketsInfoData, tokensData } = await sdk.markets.getMarketsInfo();
const positionsInfo = await sdk.positions.getPositionsInfo({
marketsInfoData, tokensData, showPnlInLeverage: false,
});
const { ordersInfoData } = await sdk.orders.getOrders({
marketsInfoData, tokensData,
});
The SDK has two tiers for order creation:
Convenience methods — handle amount calculation, execution fee, and tx submission automatically. Use these for opening positions and swaps:
| Method | Purpose | Key Params |
|---|---|---|
sdk.orders.long() | Open long position | marketAddress, payTokenAddress, collateralTokenAddress, payAmount, leverage |
sdk.orders.short() | Open short position | Same as long() |
sdk.orders.swap() | Swap tokens | fromTokenAddress, toTokenAddress, fromAmount |
sdk.orders.cancelOrders() | Cancel pending orders | orderKeys: string[] |
Low-level methods — require you to pre-compute amounts, provide full market/token objects, and handle execution fees. Required for closing positions (no convenience close() method exists):
| Method | Purpose | Required Setup |
|---|---|---|
sdk.orders.createIncreaseOrder() | Open position (full control) | IncreasePositionAmounts, marketInfo, tokensData |
sdk.orders.createDecreaseOrder() | Close/reduce position | DecreasePositionAmounts via getDecreasePositionAmounts() |
sdk.orders.createSwapOrder() | Swap (full control) | SwapAmounts, swap path |
Key gap: There is no
sdk.orders.close(). To close a position, usegetDecreasePositionAmounts()from@gmx-io/sdk/utils/trade+createDecreaseOrder(). See Step 3: Close a position for the full pattern.
| Type | Enum | Behavior |
|---|---|---|
| Market | MarketSwap(0), MarketIncrease(2), MarketDecrease(4) | Execute immediately at current oracle price |
| Limit | LimitSwap(1), LimitIncrease(3), LimitDecrease(5) | Execute when oracle price reaches trigger price |
| Stop Increase | StopIncrease(8) | Open position when price moves past trigger (breakout entry) |
| Stop-Loss | StopLossDecrease(6) | Auto-close position to limit losses |
| Liquidation | Liquidation(7) | System-triggered when position falls below maintenance margin |
Trigger conditions:
Auto-cancel limits: Maximum concurrent auto-cancel orders per position: 11 on Arbitrum, 6 on Avalanche and Botanix.
Sidecar orders: Stop-loss and take-profit orders can be attached to increase orders via createSltpEntries, cancelSltpEntries, and updateSltpEntries parameters in createIncreaseOrder().
TWAP orders: Split a large order into 2–30 parts executed over a configurable duration. TWAP utilities (getTwapDurationInSeconds, getIsValidTwapParams) are exported from @gmx-io/sdk/utils/twap but full TWAP order creation is only available via the frontend UI.
Position fees:
Swap fees:
Funding rate:
Borrowing rate:
Execution fee:
sdk.utils.getExecutionFee() to estimateBase URL: https://{network}-api.gmxinfra.io
| Endpoint | Method | Description |
|---|---|---|
/prices/tickers | GET | Current min/max prices for all tokens |
/prices/candles | GET | OHLC price candles (?tokenSymbol=ETH&period=1h) |
/signed_prices/latest | GET | Signed oracle prices for order execution |
/tokens | GET | Token list with addresses and decimals |
/markets | GET | Market configuration (index/long/short tokens) |
/markets/info | GET | Extended market info with pool sizes and utilization |
All endpoints are served from the Oracle base URL above. The legacy gmx-api-{network}.gmx.io domain is no longer available.
Base URL: https://gmx.squids.live/gmx-synthetics-{network}:prod/api/graphql
Example — fetch recent trade actions:
query {
tradeActions(
where: { account_eq: "0x..." }
orderBy: timestamp_DESC
limit: 10
) {
id
eventName
orderType
sizeDeltaUsd
timestamp
transactionHash
}
}
| Chain | Primary | Fallback 1 | Fallback 2 |
|---|---|---|---|
| Arbitrum | arbitrum-api.gmxinfra.io | arbitrum-api-fallback.gmxinfra.io | arbitrum-api-fallback.gmxinfra2.io |
| Avalanche | avalanche-api.gmxinfra.io | avalanche-api-fallback.gmxinfra.io | avalanche-api-fallback.gmxinfra2.io |
| Botanix | botanix-api.gmxinfra.io | botanix-api-fallback.gmxinfra.io | botanix-api-fallback.gmxinfra2.io |
Oracle-based pricing: GMX does not use an AMM. Prices come from Chainlink Data Streams, giving traders zero-slippage execution at the oracle price (subject to price impact from pool utilization).
Two-phase execution: Orders follow a create → execute pattern. The user submits an order transaction, then a keeper executes it with fresh oracle prices. This typically takes 1–5 seconds.
BigInt amounts: All amounts use BigInt. Prices are scaled to 30 decimals (1 USD = 10^30). Token amounts use their native decimals (e.g., USDC = 6, ETH = 18).
Stale data: marketsInfoData and tokensData contain oracle prices at fetch time. These go stale within seconds. Always re-fetch fresh data before operations that depend on current prices — especially createDecreaseOrder(), which computes acceptablePrice from the data you provide. Using stale prices causes keeper rejection.
Multicall batching: The SDK batches RPC calls automatically. Production chains use batchSize: 1024 * 1024 bytes per multicall with no waiting. This is configured per-chain in BATCH_CONFIGS.
GMX Account: Cross-chain trading from Ethereum, Base, or BNB Chain via LayerZero/Stargate bridge. Users can trade on Arbitrum/Avalanche without bridging manually.
Subaccounts: Delegate trading to a subaccount address for one-click trading. The subaccount can execute orders without requiring the main wallet signature each time.
End-to-end flow that opens a long position, monitors it, and closes it.
const { GmxSdk } = require("@gmx-io/sdk");
const { getDecreasePositionAmounts } = require("@gmx-io/sdk/utils/trade");
const { createWalletClient, http } = require("viem");
const { privateKeyToAccount } = require("viem/accounts");
const { arbitrum } = require("viem/chains");
// ─── Setup ───────────────────────────────────────────────────────────────────
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const sdk = new GmxSdk({
chainId: 42161,
rpcUrl: "https://arb1.arbitrum.io/rpc",
oracleUrl: "https://arbitrum-api.gmxinfra.io",
subsquidUrl: "https://gmx.squids.live/gmx-synthetics-arbitrum:prod/api/graphql",
account: account.address,
walletClient: createWalletClient({
account, chain: arbitrum, transport: http("https://arb1.arbitrum.io/rpc"),
}),
});
// ─── 1. Resolve addresses ───────────────────────────────────────────────────
const { marketsInfoData, tokensData } = await sdk.markets.getMarketsInfo();
const ethMarket = Object.values(marketsInfoData).find(
(m) => tokensData[m.indexTokenAddress]?.symbol === "WETH" && !m.isSpotOnly
);
const marketAddress = ethMarket.marketTokenAddress;
const usdcAddress = Object.values(tokensData).find((t) => t.symbol === "USDC").address;
// ─── 2. Open long ───────────────────────────────────────────────────────────
await sdk.orders.long({
marketAddress,
payTokenAddress: usdcAddress,
collateralTokenAddress: usdcAddress,
payAmount: 10_000000n, // 10 USDC
leverage: 30000n, // 3x
allowedSlippageBps: 100,
skipSimulation: true,
});
// ─── 3. Wait for position to appear (keeper executes in 1-30s) ──────────────
let position;
for (let i = 0; i < 40; i++) {
await new Promise((r) => setTimeout(r, 3000));
const info = await sdk.positions.getPositionsInfo({
marketsInfoData, tokensData, showPnlInLeverage: false,
});
position = Object.values(info).find(
(p) => p.marketAddress === marketAddress && p.isLong === true
);
if (position) break;
}
if (!position) throw new Error("Position did not appear within 120s");
console.log("Position opened:", {
sizeUsd: position.sizeInUsd.toString(),
leverage: position.leverage.toString(),
entryPrice: position.entryPrice.toString(),
});
// ─── 4. Close position (re-fetch fresh data first!) ─────────────────────────
const fresh = await sdk.markets.getMarketsInfo();
const freshPositions = await sdk.positions.getPositionsInfo({
marketsInfoData: fresh.marketsInfoData,
tokensData: fresh.tokensData,
showPnlInLeverage: false,
});
const pos = Object.values(freshPositions).find(
(p) => p.marketAddress === marketAddress && p.isLong === true
);
const marketInfo = fresh.marketsInfoData[pos.marketAddress];
const collateralToken = fresh.tokensData[pos.collateralTokenAddress];
const { minCollateralUsd, minPositionSizeUsd } = await sdk.positions.getPositionsConstants();
const uiFeeFactor = await sdk.utils.getUiFeeFactor();
const decreaseAmounts = getDecreasePositionAmounts({
marketInfo, collateralToken, isLong: pos.isLong, position: pos,
closeSizeUsd: pos.sizeInUsd, keepLeverage: false,
userReferralInfo: undefined, minCollateralUsd, minPositionSizeUsd, uiFeeFactor,
isSetAcceptablePriceImpactEnabled: false,
});
await sdk.orders.createDecreaseOrder({
marketInfo, marketsInfoData: fresh.marketsInfoData, tokensData: fresh.tokensData,
isLong: pos.isLong, allowedSlippage: 300, decreaseAmounts, collateralToken,
});
console.log("Close order submitted — keeper will execute in 1-30s");
@gmx-io/sdk/utils/twap) but no SDK method to create TWAP orders programmatically.sdk.orders.cancelOrders()).skipSimulation parameter exists but simulation is deprecated. Set skipSimulation: true.@gmx-io/sdk on npm — SDK packagenpx claudepluginhub gmx-io/gmx-ai --plugin gmx-ioBuy and sell meme coins and crypto tokens on Solana, BSC, Base, or Ethereum via GMGN API. Supports single swap, multi-wallet batch trading, limit orders, stop loss, take profit, trailing orders, and order status queries.
Executes spot and perpetual futures trades on Hyperliquid with market (IOC) and limit (GTC) orders, sets leverage, manages positions/balances/orders, deposits USDC from Arbitrum.
Places and cancels perpetual futures orders on Phoenix DEX with pre-trade checks, TP/SL attachments, and paper/live mode support.