Skip to main content

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 ± collateralAmount as denominator.
  • Non-equivalent collateral (for example, long ETH/USDC collateralized in USDC): uses remainingCollateralUsd and sizeInTokens as 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
);