positions
This module provides utilities for working with trading positions in the GMX protocol. It includes functions for calculating position metrics, PnL, liquidation prices, leverage, and managing position keys.
Methods
The positions utility module exports functions organized into six groups: position key utilities, position value calculations, leverage and risk, price impact calculations, API data fetching, and position enrichment. Import any function directly from @gmx-io/sdk/utils/positions.
Position key utilities
These functions create and parse the string keys used to identify positions on-chain.
getPositionKey
getPositionKey(account: string, marketAddress: string, collateralAddress: string, isLong: boolean): string
Generates a unique position key from position parameters. The key format is account:marketAddress:collateralAddress:isLong.
import { getPositionKey } from "@gmx-io/sdk/utils/positions";
const positionKey = getPositionKey(
"0x1234567890123456789012345678901234567890",
"0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
"0xfedcbafedcbafedcbafedcbafedcbafedcbafed",
true
);
// "0x1234...5678:0xabcd...abcd:0xfedc...afed:true"
parsePositionKey
parsePositionKey(positionKey: string): {
account: string;
marketAddress: string;
collateralAddress: string;
isLong: boolean;
}
Parses a position key back into its component parts. The isLong field is parsed from the string "true" / "false".
import { parsePositionKey } from "@gmx-io/sdk/utils/positions";
const parsed = parsePositionKey("0x1234567890123456789012345678901234567890:0xabcdef...:0xfedcba...:true");
// { account: "0x1234...", marketAddress: "0xabcdef...", collateralAddress: "0xfedcba...", isLong: true }
Position value calculations
These functions compute USD-denominated metrics from raw position data. All values use 30-decimal fixed-point representation.
getEntryPrice
getEntryPrice(p: { sizeInUsd: bigint; sizeInTokens: bigint; indexToken: Token }): bigint | undefined
Calculates the entry price of a position as sizeInUsd × 10^indexToken.decimals / sizeInTokens. Returns undefined when sizeInTokens <= 0.
import { getEntryPrice } from "@gmx-io/sdk/utils/positions";
const entryPrice = getEntryPrice({
sizeInUsd: 1800n * 10n ** 30n, // $1800 (30-decimal)
sizeInTokens: 1n * 10n ** 18n, // 1 WETH (18-decimal)
indexToken: wethToken,
});
// 1800n * 10n ** 30n — price in 30-decimal format
getPositionValueUsd
getPositionValueUsd(p: { indexToken: Token; sizeInTokens: bigint; markPrice: bigint }): bigint
Calculates the current USD value of a position: convertToUsd(sizeInTokens, indexToken.decimals, markPrice).
import { getPositionValueUsd } from "@gmx-io/sdk/utils/positions";
const valueUsd = getPositionValueUsd({
indexToken: wethToken,
sizeInTokens: 1n * 10n ** 18n, // 1 WETH
markPrice: 2100n * 10n ** 30n, // $2100 (30-decimal)
});
// 2100n * 10n ** 30n
getPositionPnlUsd
getPositionPnlUsd(p: {
marketInfo: MarketInfo;
sizeInUsd: bigint;
sizeInTokens: bigint;
markPrice: bigint;
isLong: boolean;
}): bigint
Calculates the unrealized PnL of a position in USD (30-decimal). For profitable positions, the result is scaled down proportionally if the pool's total PnL exceeds its cap.
- Long PnL:
positionValueUsd - sizeInUsd - Short PnL:
sizeInUsd - positionValueUsd
import { getPositionPnlUsd } from "@gmx-io/sdk/utils/positions";
const pnl = getPositionPnlUsd({
marketInfo,
sizeInUsd: 1800n * 10n ** 30n, // entry value
sizeInTokens: 1n * 10n ** 18n, // 1 WETH
markPrice: 2100n * 10n ** 30n, // current price
isLong: true,
});
// +300n * 10n ** 30n ($300 profit, before pool cap adjustment)
getPositionPendingFeesUsd
getPositionPendingFeesUsd(p: { pendingFundingFeesUsd: bigint; pendingBorrowingFeesUsd: bigint }): bigint
Returns pendingBorrowingFeesUsd + pendingFundingFeesUsd.
import { getPositionPendingFeesUsd } from "@gmx-io/sdk/utils/positions";
const pendingFees = getPositionPendingFeesUsd({
pendingFundingFeesUsd: 1n * 10n ** 30n, // $1
pendingBorrowingFeesUsd: 5n * 10n ** 29n, // $0.5
});
// 1_500_000n * 10n ** 24n ($1.5 in 30-decimal)
getPositionNetValue
getPositionNetValue(p: {
totalPendingImpactDeltaUsd: bigint;
priceImpactDiffUsd: bigint;
collateralUsd: bigint;
pendingFundingFeesUsd: bigint;
pendingBorrowingFeesUsd: bigint;
pnl: bigint;
closingFeeUsd: bigint;
uiFeeUsd: bigint;
}): bigint
Returns the net value of a position:
collateralUsd - pendingFees - closingFeeUsd - uiFeeUsd + pnl + totalPendingImpactDeltaUsd + priceImpactDiffUsd
import { getPositionNetValue } from "@gmx-io/sdk/utils/positions";
const netValue = getPositionNetValue({
totalPendingImpactDeltaUsd: 0n,
priceImpactDiffUsd: 0n,
collateralUsd: 500n * 10n ** 30n, // $500 collateral
pendingFundingFeesUsd: 1n * 10n ** 30n,
pendingBorrowingFeesUsd: 5n * 10n ** 29n,
pnl: 50n * 10n ** 30n, // $50 profit
closingFeeUsd: 2n * 10n ** 30n,
uiFeeUsd: 0n,
});
// $546.50 in 30-decimal
getPositionPnlAfterFees
getPositionPnlAfterFees(params: {
pnl: bigint;
pendingBorrowingFeesUsd: bigint;
pendingFundingFeesUsd: bigint;
closingFeeUsd: bigint;
uiFeeUsd: bigint;
totalPendingImpactDeltaUsd: bigint;
priceImpactDiffUsd: bigint;
}): bigint
Returns the position PnL after deducting all fees and adding price impact:
pnl - pendingBorrowingFeesUsd - pendingFundingFeesUsd - closingFeeUsd - uiFeeUsd + totalPendingImpactDeltaUsd + priceImpactDiffUsd
import { getPositionPnlAfterFees } from "@gmx-io/sdk/utils/positions";
const pnlAfterFees = getPositionPnlAfterFees({
pnl: 100n * 10n ** 30n,
pendingBorrowingFeesUsd: 1n * 10n ** 30n,
pendingFundingFeesUsd: 5n * 10n ** 29n,
closingFeeUsd: 2n * 10n ** 30n,
uiFeeUsd: 0n,
totalPendingImpactDeltaUsd: 0n,
priceImpactDiffUsd: 0n,
});
// $96.50 in 30-decimal
Leverage and risk
These functions compute leverage and liquidation price for a position.
getLeverage
getLeverage(p: {
sizeInUsd: bigint;
collateralUsd: bigint;
pnl: bigint | undefined;
pendingFundingFeesUsd: bigint;
pendingBorrowingFeesUsd: bigint;
}): bigint | undefined
Returns the current leverage in basis points (sizeInUsd × BASIS_POINTS_DIVISOR / remainingCollateralUsd), where remainingCollateralUsd = collateralUsd + pnl - pendingFees. Returns undefined when remainingCollateralUsd <= 0.
BASIS_POINTS_DIVISOR = 10000, so a return value of 20000n means 2× leverage.
import { getLeverage } from "@gmx-io/sdk/utils/positions";
const leverage = getLeverage({
sizeInUsd: 1000n * 10n ** 30n,
collateralUsd: 100n * 10n ** 30n,
pnl: 10n * 10n ** 30n,
pendingFundingFeesUsd: 1n * 10n ** 30n,
pendingBorrowingFeesUsd: 5n * 10n ** 29n,
});
// ~92593n bps ≈ 9.26×
getLiquidationPrice
getLiquidationPrice(p: {
sizeInUsd: bigint;
sizeInTokens: bigint;
collateralAmount: bigint;
collateralUsd: bigint;
collateralToken: TokenData;
marketInfo: MarketInfo;
pendingFundingFeesUsd: bigint;
pendingBorrowingFeesUsd: bigint;
pendingImpactAmount: bigint;
minCollateralUsd: bigint;
isLong: boolean;
useMaxPriceImpact?: boolean;
userReferralInfo: UserReferralInfo | undefined;
}): bigint | undefined
Calculates the liquidation price for a position. Returns undefined when sizeInUsd <= 0, sizeInTokens <= 0, the denominator is zero, or when the computed price is <= 0.
Two formulas are used depending on whether the collateral token is equivalent to the index token:
- Equivalent collateral (for example, long ETH/USDC collateralized in ETH): combines token amounts directly with
sizeInTokens ± collateralAmountas denominator. - Non-equivalent collateral (for example, long ETH/USDC collateralized in USDC): uses
remainingCollateralUsdandsizeInTokensas denominator.
When useMaxPriceImpact is true, uses marketInfo.maxPositionImpactFactorForLiquidations instead of computing the actual price impact.
import { getLiquidationPrice } from "@gmx-io/sdk/utils/positions";
const liquidationPrice = getLiquidationPrice({
sizeInUsd: 1800n * 10n ** 30n,
sizeInTokens: 1n * 10n ** 18n,
collateralAmount: 100n * 10n ** 6n, // 100 USDC (6-decimal collateral)
collateralUsd: 100n * 10n ** 30n,
collateralToken: usdcTokenData,
marketInfo,
pendingFundingFeesUsd: 1n * 10n ** 30n,
pendingBorrowingFeesUsd: 5n * 10n ** 29n,
pendingImpactAmount: 0n,
minCollateralUsd: 10n * 10n ** 30n,
isLong: true,
useMaxPriceImpact: false,
userReferralInfo: undefined,
});
Price impact calculations
These functions compute the net price impact when decreasing a position, including contributions from the pending impact pool.
getNetPriceImpactDeltaUsdForDecrease
getNetPriceImpactDeltaUsdForDecrease(params: {
marketInfo: MarketInfo;
sizeInUsd: bigint;
pendingImpactAmount: bigint;
priceImpactDeltaUsd: bigint;
sizeDeltaUsd: bigint;
}): {
totalImpactDeltaUsd: bigint;
proportionalPendingImpactDeltaUsd: bigint;
priceImpactDiffUsd: bigint;
}
Calculates the net price impact when decreasing a position by combining the current price impact with a proportional share of the pending impact pool. The positive total is capped by maxPositionImpactFactor; the negative total is floored by the max impact pool limit.
import { getNetPriceImpactDeltaUsdForDecrease } from "@gmx-io/sdk/utils/positions";
const impact = getNetPriceImpactDeltaUsdForDecrease({
marketInfo,
sizeInUsd: 1800n * 10n ** 30n,
pendingImpactAmount: 0n,
priceImpactDeltaUsd: -5n * 10n ** 30n, // $5 adverse price impact
sizeDeltaUsd: 900n * 10n ** 30n, // closing half the position
});
// impact.totalImpactDeltaUsd — net impact after caps
// impact.proportionalPendingImpactDeltaUsd — share of pending impact pool
// impact.priceImpactDiffUsd — capped-off portion (goes to holding pool)
getPriceImpactDiffUsd
getPriceImpactDiffUsd(params: {
totalImpactDeltaUsd: bigint;
marketInfo: MarketInfo;
sizeDeltaUsd: bigint;
}): bigint
Returns the portion of a negative price impact that exceeds the market's maximum negative impact factor. Returns 0n when totalImpactDeltaUsd >= 0.
import { getPriceImpactDiffUsd } from "@gmx-io/sdk/utils/positions";
const diff = getPriceImpactDiffUsd({
totalImpactDeltaUsd: -10n * 10n ** 30n, // $10 adverse impact
marketInfo,
sizeDeltaUsd: 500n * 10n ** 30n,
});
// 0n if impact is within the max factor, otherwise the excess
API data fetching
Use fetchApiPositionsInfo to retrieve position data from the GMX REST API as an alternative to on-chain multicalls.
fetchApiPositionsInfo
fetchApiPositionsInfo(ctx: { api: IHttp }, params: {
address: string;
includeRelatedOrders?: boolean;
}): Promise<ApiPositionInfo[]>
Fetches position data from the GMX REST API instead of on-chain multicalls. Available on chains where isApiSupported(chainId) returns true (Arbitrum, Avalanche, and Arbitrum Sepolia). The response is deserialized with deserializeBigIntsInObject.
import { GmxApiSdk } from "@gmx-io/sdk/v2";
import { fetchApiPositionsInfo } from "@gmx-io/sdk/utils/positions";
const apiSdk = new GmxApiSdk({ chainId: 42161 });
const positions = await fetchApiPositionsInfo(
apiSdk.ctx,
{ address: "0x1234...5678", includeRelatedOrders: true }
);
Position enrichment
These functions build fully computed position summaries from raw on-chain data and current market state.
getPositionInfo
getPositionInfo(p: {
position: Position;
marketInfo: MarketInfo;
minCollateralUsd: bigint;
userReferralInfo?: UserReferralInfo;
showPnlInLeverage?: boolean;
uiFeeFactor?: bigint;
}): PositionInfo
Computes a comprehensive position summary from a raw position and market data. Returns an object with entry price, mark price, PnL, PnL after fees, leverage, liquidation price, net value, pending fees, and closing costs. This is the main function to use when you need a full picture of a position's current state.
import { getPositionInfo } from "@gmx-io/sdk/utils/positions";
const info = getPositionInfo({
position,
marketInfo,
minCollateralUsd: 10n * 10n ** 30n,
userReferralInfo: undefined,
});
console.log(info.entryPrice); // entry price in 30-decimal
console.log(info.pnl); // unrealized PnL
console.log(info.pnlAfterFees); // PnL minus all fees
console.log(info.leverage); // current leverage in basis points
console.log(info.liquidationPrice); // liquidation price
console.log(info.netValue); // net position value
getContractPositionDynamicFees
getContractPositionDynamicFees(p: {
position: {
sizeInUsd: bigint;
collateralTokenAddress: string;
isLong: boolean;
borrowingFactor: bigint;
fundingFeeAmountPerSize: bigint;
longTokenClaimableFundingAmountPerSize: bigint;
shortTokenClaimableFundingAmountPerSize: bigint;
};
marketInfo: MarketInfo;
marketFeeState: {
cumulativeBorrowingFactorLong: bigint;
cumulativeBorrowingFactorShort: bigint;
fundingFeeAmountPerSizeLongLong: bigint;
fundingFeeAmountPerSizeLongShort: bigint;
fundingFeeAmountPerSizeShortLong: bigint;
fundingFeeAmountPerSizeShortShort: bigint;
claimableFundingAmountPerSizeLongLong: bigint;
claimableFundingAmountPerSizeLongShort: bigint;
claimableFundingAmountPerSizeShortLong: bigint;
claimableFundingAmountPerSizeShortShort: bigint;
};
referralInfo?: UserReferralInfo;
}): {
pendingBorrowingFeesUsd: bigint;
fundingFeeAmount: bigint;
claimableLongTokenAmount: bigint;
claimableShortTokenAmount: bigint;
positionFeeAmount: bigint;
traderDiscountAmount: bigint;
uiFeeAmount: bigint;
}
Calculates the dynamic fees for a position based on the current market fee state. Unlike the static fee fields on a position object, this function uses the latest cumulative borrowing and funding factors to compute up-to-date fee values.
import { getContractPositionDynamicFees } from "@gmx-io/sdk/utils/positions";
const fees = getContractPositionDynamicFees({
position,
marketInfo,
marketFeeState,
referralInfo: undefined,
});
console.log(fees.pendingBorrowingFeesUsd); // current borrowing fees in USD
console.log(fees.fundingFeeAmount); // funding fee in collateral token units
Collateral factor
getMinCollateralFactorForPosition
getMinCollateralFactorForPosition(position: PositionInfoLoaded, openInterestDelta: bigint): bigint
Returns the minimum collateral factor (in PRECISION units) required for the position's market side, given the current open interest plus openInterestDelta. The result is the greater of the OI-based factor and the market's baseline minCollateralFactor.
import { getMinCollateralFactorForPosition } from "@gmx-io/sdk/utils/positions";
const minFactor = getMinCollateralFactorForPosition(
positionInfo,
-1000n * 10n ** 30n // OI decreasing by $1000
);