Skip to main content

priceImpact

This module provides utilities for calculating price impact in GMX protocol operations, including position trades and swaps. It handles price impact calculations based on pool imbalances, virtual inventories, and various impact factors.

Methods

This module exports functions for computing price impact in position and swap operations. Price impact measures how a trade shifts pool balance — moves that improve balance receive positive impact (a rebate), while moves that worsen balance receive negative impact (a cost). All USD values use 30-decimal precision (USD_DECIMALS = 30), meaning 1_000000000000000000000000000000n represents $1.

note

All functions in this module are exported from @gmx-io/sdk/utils/fees. Import them individually as shown in each example.

getPriceImpactByAcceptablePrice

getPriceImpactByAcceptablePrice(p: {
sizeDeltaUsd: bigint;
acceptablePrice: bigint;
indexPrice: bigint;
isLong: boolean;
isIncrease: boolean;
}): {
priceImpactDeltaUsd: bigint;
priceImpactDeltaAmount: bigint;
priceDelta: bigint;
acceptablePriceDeltaBps: bigint;
}

Computes the price impact implied by the difference between your acceptable price and the current index price. Use this to show users the cost of their slippage tolerance before order submission.

ParameterTypeDescription
sizeDeltaUsdbigintTrade size in USD (30-decimal precision)
acceptablePricebigintWorst price the user accepts (30-decimal precision)
indexPricebigintCurrent mark price of the index token (30-decimal precision)
isLongbooleantrue for long positions
isIncreasebooleantrue when opening or increasing a position
import { getPriceImpactByAcceptablePrice } from "@gmx-io/sdk/utils/fees";

// ETH long increase: $1 size, acceptable price $1.95, mark price $2.00
const priceImpact = getPriceImpactByAcceptablePrice({
sizeDeltaUsd: 1_000000000000000000000000000000n, // $1
acceptablePrice: 1_950000000000000000000000000000n, // $1.95
indexPrice: 2_000000000000000000000000000000n, // $2.00
isLong: true,
isIncrease: true,
});

console.log(priceImpact.priceImpactDeltaUsd); // negative — user pays for slippage
console.log(priceImpact.acceptablePriceDeltaBps); // basis-point difference from mark price

applySwapImpactWithCap

applySwapImpactWithCap(
marketInfo: MarketInfo,
token: TokenData,
priceImpactDeltaUsd: bigint
): { impactDeltaAmount: bigint; cappedDiffUsd: bigint }

Converts a USD price impact value to a token amount for swap operations, capping positive impact at the available swap impact pool balance. Positive impact is paid out from the pool; negative impact is collected from the user.

warning

Throws if token is not a collateral token of the given market. Always pass a token that belongs to the market's long or short pool.

ParameterTypeDescription
marketInfoMarketInfoMarket containing the swap impact pool
tokenTokenDataCollateral token being swapped in or out
priceImpactDeltaUsdbigintRaw price impact in USD (30-decimal precision); positive = rebate, negative = cost
import { applySwapImpactWithCap } from "@gmx-io/sdk/utils/fees";

// Apply $0.50 positive impact, capped by the pool's available balance
const result = applySwapImpactWithCap(
marketInfo,
usdcTokenData,
500000000000000000000000000000n // $0.50 impact
);

console.log(result.impactDeltaAmount); // token amount credited or debited
console.log(result.cappedDiffUsd); // USD value lost to pool cap (if any)

getCappedPositionImpactUsd

getCappedPositionImpactUsd(
marketInfo: MarketInfo,
sizeDeltaUsd: bigint,
isLong: boolean,
isIncrease: boolean,
opts?: { fallbackToZero?: boolean; shouldCapNegativeImpact?: boolean }
): { priceImpactDeltaUsd: bigint; balanceWasImproved: boolean }

Computes position price impact bounded by the market's maximum impact factor. This is the main function to use when you need to show a user their estimated price impact before placing a position order.

ParameterTypeDescription
marketInfoMarketInfoTarget market
sizeDeltaUsdbigintAbsolute position size change in USD (30-decimal precision)
isLongbooleantrue for long positions
isIncreasebooleantrue when opening or increasing; false when decreasing
opts.fallbackToZerobooleanReturn zero instead of throwing when pool goes negative
opts.shouldCapNegativeImpactbooleanAlso cap negative impact by the max impact factor
import { getCappedPositionImpactUsd } from "@gmx-io/sdk/utils/fees";

// Estimate impact for a $1 long increase
const impact = getCappedPositionImpactUsd(
marketInfo,
1_000000000000000000000000000000n, // $1
true, // long
true, // increase
{ fallbackToZero: true }
);

console.log(impact.priceImpactDeltaUsd); // negative = cost, positive = rebate
console.log(impact.balanceWasImproved); // true when trade improves pool balance

capPositionImpactUsdByMaxImpactPool

capPositionImpactUsdByMaxImpactPool(
marketInfo: MarketInfo,
positionImpactDeltaUsd: bigint
): bigint

Caps a positive position impact at the value of tokens available in the position impact pool. Negative values pass through unchanged. Call this before distributing a positive impact rebate to ensure the pool can cover it.

ParameterTypeDescription
marketInfoMarketInfoMarket containing the position impact pool
positionImpactDeltaUsdbigintRaw positive impact in USD (30-decimal precision)
import { capPositionImpactUsdByMaxImpactPool } from "@gmx-io/sdk/utils/fees";

const cappedImpact = capPositionImpactUsdByMaxImpactPool(
marketInfo,
750000000000000000000000000000n // $0.75 raw impact
);

console.log(cappedImpact); // at most the pool's available USD value

capPositionImpactUsdByMaxPriceImpactFactor

capPositionImpactUsdByMaxPriceImpactFactor(
marketInfo: MarketInfo,
sizeDeltaUsd: bigint,
positionImpactDeltaUsd: bigint
): bigint

Caps position impact (positive or negative) at the market's configured maximum impact factor multiplied by the trade size. The effective cap is abs(sizeDeltaUsd) × maxImpactFactor.

ParameterTypeDescription
marketInfoMarketInfoMarket with configured max impact factors
sizeDeltaUsdbigintTrade size in USD (signed; negative for decreases)
positionImpactDeltaUsdbigintRaw impact in USD to cap
import { capPositionImpactUsdByMaxPriceImpactFactor } from "@gmx-io/sdk/utils/fees";

const cappedImpact = capPositionImpactUsdByMaxPriceImpactFactor(
marketInfo,
1_000000000000000000000000000000n, // $1 trade size
-500000000000000000000000000000n // $0.50 raw negative impact
);

console.log(cappedImpact); // bounded by maxNegativeImpactFactor × $1

getMaxPositionImpactFactors

getMaxPositionImpactFactors(
marketInfo: MarketInfo
): { maxPositiveImpactFactor: bigint; maxNegativeImpactFactor: bigint }

Returns the maximum position impact factors for a market. The positive cap is set to min(maxPositiveImpactFactorPositive, maxNegativeImpactFactor) to prevent excessive rebates.

ParameterTypeDescription
marketInfoMarketInfoMarket to read impact factors from
import { getMaxPositionImpactFactors } from "@gmx-io/sdk/utils/fees";

const { maxPositiveImpactFactor, maxNegativeImpactFactor } = getMaxPositionImpactFactors(marketInfo);

// Factors are scaled to 30 decimals — divide by 1e30 to get the decimal fraction
console.log(maxPositiveImpactFactor); // bigint, 30-decimal precision
console.log(maxNegativeImpactFactor);

getPriceImpactForPosition

getPriceImpactForPosition(
marketInfo: MarketInfo,
sizeDeltaUsd: bigint,
isLong: boolean,
opts?: { fallbackToZero?: boolean }
): { priceImpactDeltaUsd: bigint; balanceWasImproved: boolean }

Computes the uncapped price impact for a position trade based on the change in open interest. When the market has a non-zero virtual inventory, the function uses the more conservative of the real and virtual impact values. This is a building block used by getCappedPositionImpactUsd.

ParameterTypeDescription
marketInfoMarketInfoMarket with open interest and virtual inventory data
sizeDeltaUsdbigintSigned size delta in USD — positive for longs, negative for shorts
isLongbooleantrue for long positions
opts.fallbackToZerobooleanReturn zero instead of throwing when pool goes negative
import { getPriceImpactForPosition } from "@gmx-io/sdk/utils/fees";

const impact = getPriceImpactForPosition(
marketInfo,
1_000000000000000000000000000000n, // $1 long increase
true,
{ fallbackToZero: true }
);

console.log(impact.priceImpactDeltaUsd); // uncapped impact in USD
console.log(impact.balanceWasImproved);

getProportionalPendingImpactValues

getProportionalPendingImpactValues(p: {
sizeInUsd: bigint;
pendingImpactAmount: bigint;
sizeDeltaUsd: bigint;
indexToken: TokenData;
}): {
proportionalPendingImpactDeltaAmount: bigint;
proportionalPendingImpactDeltaUsd: bigint;
}

Splits a pending price impact proportionally when partially closing a position. Use this when a user closes part of a position that has an accrued impact amount.

ParameterTypeDescription
sizeInUsdbigintFull position size in USD (30-decimal precision)
pendingImpactAmountbigintAccrued pending impact in index tokens
sizeDeltaUsdbigintSize being closed in USD
indexTokenTokenDataIndex token with price data for USD conversion
import { getProportionalPendingImpactValues } from "@gmx-io/sdk/utils/fees";

// Close $1 of a $5 position with 0.1 ETH pending impact
const impact = getProportionalPendingImpactValues({
sizeInUsd: 5_000000000000000000000000000000n, // $5 full position
pendingImpactAmount: 100000000000000000n, // 0.1 ETH
sizeDeltaUsd: 1_000000000000000000000000000000n, // $1 being closed
indexToken: ethTokenData,
});

console.log(impact.proportionalPendingImpactDeltaAmount); // token amount for this close
console.log(impact.proportionalPendingImpactDeltaUsd); // USD value of that amount

getPriceImpactForSwap

getPriceImpactForSwap(
marketInfo: MarketInfo,
tokenA: TokenData,
tokenB: TokenData,
usdDeltaTokenA: bigint,
usdDeltaTokenB: bigint,
opts?: { fallbackToZero?: boolean }
): { priceImpactDeltaUsd: bigint; balanceWasImproved: boolean }

Computes the uncapped price impact for a swap between two tokens in the same market pool. When the market has non-zero virtual inventory, the function uses the more conservative of the real and virtual impact values.

warning

Throws if either token is not a collateral token of the given market, or if both tokens map to the same pool side on a non-same-collateral market.

ParameterTypeDescription
marketInfoMarketInfoMarket containing both tokens
tokenATokenDataFirst swap token
tokenBTokenDataSecond swap token
usdDeltaTokenAbigintUSD added to the pool for token A (positive = deposit, negative = withdrawal)
usdDeltaTokenBbigintUSD added to the pool for token B
opts.fallbackToZerobooleanReturn zero instead of throwing on pool underflow
import { getPriceImpactForSwap } from "@gmx-io/sdk/utils/fees";

// Swap $1 USDC in, $1 WETH out
const impact = getPriceImpactForSwap(
marketInfo,
usdcTokenData,
wethTokenData,
1_000000000000000000000000000000n, // +$1 USDC into pool
-1_000000000000000000000000000000n, // -$1 WETH from pool
{ fallbackToZero: true }
);

console.log(impact.priceImpactDeltaUsd); // negative = user pays, positive = user receives
console.log(impact.balanceWasImproved);

getNextPoolAmountsParams

getNextPoolAmountsParams(p: {
longToken: TokenData;
shortToken: TokenData;
longPoolAmount: bigint;
shortPoolAmount: bigint;
longDeltaUsd: bigint;
shortDeltaUsd: bigint;
}): {
longPoolUsd: bigint;
shortPoolUsd: bigint;
nextLongPoolUsd: bigint;
nextShortPoolUsd: bigint;
}

Converts current pool token amounts to USD and applies deltas to produce the next-state pool values. This is a helper used by getPriceImpactForSwap and getPriceImpactUsd.

ParameterTypeDescription
longTokenTokenDataLong-side collateral token with price data
shortTokenTokenDataShort-side collateral token with price data
longPoolAmountbigintCurrent long pool amount in token units
shortPoolAmountbigintCurrent short pool amount in token units
longDeltaUsdbigintUSD change to the long pool (30-decimal precision)
shortDeltaUsdbigintUSD change to the short pool
import { getNextPoolAmountsParams } from "@gmx-io/sdk/utils/fees";

const poolParams = getNextPoolAmountsParams({
longToken: wethTokenData,
shortToken: usdcTokenData,
longPoolAmount: 1_000000000000000000n, // 1 WETH
shortPoolAmount: 2_000000000000000000n, // 2 USDC (6-decimal token, adjust per token.decimals)
longDeltaUsd: 500000000000000000000000000000n, // +$0.50 into long pool
shortDeltaUsd: -250000000000000000000000000000n, // -$0.25 from short pool
});

console.log(poolParams.longPoolUsd); // current long pool in USD
console.log(poolParams.nextLongPoolUsd); // long pool after delta

getPriceImpactUsd

getPriceImpactUsd(p: {
currentLongUsd: bigint;
currentShortUsd: bigint;
nextLongUsd: bigint;
nextShortUsd: bigint;
factorPositive: bigint;
factorNegative: bigint;
exponentFactorPositive: bigint;
exponentFactorNegative: bigint;
fallbackToZero?: boolean;
}): { priceImpactDeltaUsd: bigint; balanceWasImproved: boolean }

Core price impact calculation. Compares the current and next pool state to determine whether the trade improves or worsens balance, then applies the appropriate factor and exponent. Uses separate exponent factors for positive and negative impact paths.

See the GMX synthetics SwapPricingUtils contract for the matching on-chain implementation.

ParameterTypeDescription
currentLongUsdbigintCurrent long-side USD value
currentShortUsdbigintCurrent short-side USD value
nextLongUsdbigintPost-trade long-side USD value
nextShortUsdbigintPost-trade short-side USD value
factorPositivebigintImpact factor when balance improves (30-decimal fraction)
factorNegativebigintImpact factor when balance worsens
exponentFactorPositivebigintExponent applied to positive-impact calculations (30-decimal)
exponentFactorNegativebigintExponent applied to negative-impact calculations
fallbackToZerobooleanReturn zero instead of throwing when pool goes negative
import { getPriceImpactUsd } from "@gmx-io/sdk/utils/fees";

// Long pool increases from $10 to $11, short stays at $8 — worsens imbalance
const impact = getPriceImpactUsd({
currentLongUsd: 10_000000000000000000000000000000n, // $10
currentShortUsd: 8_000000000000000000000000000000n, // $8
nextLongUsd: 11_000000000000000000000000000000n, // $11
nextShortUsd: 8_000000000000000000000000000000n, // $8 (unchanged)
factorPositive: 5000000000000000000000000n, // 0.0005% factor
factorNegative: 5000000000000000000000000n, // 0.0005% factor
exponentFactorPositive: 2_000000000000000000000000000000n, // exponent 2.0
exponentFactorNegative: 2_000000000000000000000000000000n, // exponent 2.0
fallbackToZero: true,
});

console.log(impact.priceImpactDeltaUsd); // negative — trade worsens pool balance
console.log(impact.balanceWasImproved); // false

calculateImpactForSameSideRebalance

calculateImpactForSameSideRebalance(p: {
currentDiff: bigint;
nextDiff: bigint;
hasPositiveImpact: boolean;
factor: bigint;
exponentFactor: bigint;
}): bigint

Computes impact when a trade stays on the same side of the pool imbalance — that is, the larger pool stays larger after the trade. Returns positive or negative bigint depending on hasPositiveImpact.

See the GMX PricingUtils contract for the matching on-chain implementation.

ParameterTypeDescription
currentDiffbigintAbsolute difference between current long and short pools (30-decimal USD)
nextDiffbigintAbsolute difference between next long and short pools
hasPositiveImpactbooleantrue when the trade reduces the imbalance
factorbigintImpact factor (30-decimal fraction)
exponentFactorbigintExponent (30-decimal)
import { calculateImpactForSameSideRebalance } from "@gmx-io/sdk/utils/fees";

// Imbalance narrows from $2 to $1.50 — positive impact
const impact = calculateImpactForSameSideRebalance({
currentDiff: 2_000000000000000000000000000000n, // $2 imbalance
nextDiff: 1_500000000000000000000000000000n, // $1.50 imbalance
hasPositiveImpact: true,
factor: 5000000000000000000000000n, // 0.0005%
exponentFactor: 2_000000000000000000000000000000n, // 2.0
});

console.log(impact); // positive bigint rebate in USD (30-decimal)

calculateImpactForCrossoverRebalance

calculateImpactForCrossoverRebalance(p: {
currentDiff: bigint;
nextDiff: bigint;
factorPositive: bigint;
factorNegative: bigint;
exponentFactorPositive: bigint;
exponentFactorNegative: bigint;
}): bigint

Computes impact when a trade crosses over the pool balance point — the previously larger side becomes the smaller side. This is a split calculation: the portion that moves the pool toward balance uses factorPositive; the portion past the balance point uses factorNegative.

See the GMX PricingUtils contract for the matching on-chain implementation.

ParameterTypeDescription
currentDiffbigintAbsolute imbalance before the trade (30-decimal USD)
nextDiffbigintAbsolute imbalance after the trade (now on the opposite side)
factorPositivebigintFactor applied to the balance-improving portion
factorNegativebigintFactor applied to the balance-worsening portion
exponentFactorPositivebigintExponent for the positive portion (30-decimal)
exponentFactorNegativebigintExponent for the negative portion
import { calculateImpactForCrossoverRebalance } from "@gmx-io/sdk/utils/fees";

// Imbalance of $1 crosses zero to -$0.50 on the other side
const impact = calculateImpactForCrossoverRebalance({
currentDiff: 1_000000000000000000000000000000n, // $1 imbalance
nextDiff: 500000000000000000000000000000n, // $0.50 imbalance, opposite side
factorPositive: 5000000000000000000000000n, // 0.0005%
factorNegative: 5000000000000000000000000n, // 0.0005%
exponentFactorPositive: 2_000000000000000000000000000000n, // 2.0
exponentFactorNegative: 2_000000000000000000000000000000n, // 2.0
});

console.log(impact); // net impact bigint in USD (30-decimal), may be positive or negative

applyImpactFactor

applyImpactFactor(diff: bigint, factor: bigint, exponent: bigint): bigint

Low-level helper that raises diff to the power of exponent and multiplies by factor. Both diff and exponent are converted to floating-point for the exponentiation, then the result is converted back to a 30-decimal bigint. Returns 0n if the intermediate value is not finite.

ParameterTypeDescription
diffbigintPool imbalance value (30-decimal USD)
factorbigintImpact factor (30-decimal fraction)
exponentbigintExponent (30-decimal)
import { applyImpactFactor } from "@gmx-io/sdk/utils/fees";

const result = applyImpactFactor(
1_000000000000000000000000000000n, // $1 imbalance
5000000000000000000000000n, // 0.0005% factor
2_000000000000000000000000000000n // exponent 2.0
);

console.log(result); // bigint result (30-decimal)

getCappedPriceImpactPercentageFromFees

getCappedPriceImpactPercentageFromFees(p: {
fees: TradeFees | undefined;
isSwap: boolean;
}): bigint | undefined

Extracts the capped price impact percentage from an already-computed TradeFees object. Returns swapPriceImpact.precisePercentage for swaps or positionNetPriceImpact.precisePercentage for position trades. Returns 0n when the relevant fee item is absent.

ParameterTypeDescription
feesTradeFees | undefinedPre-computed trade fees object
isSwapbooleantrue to read swap impact; false to read position impact
import { getCappedPriceImpactPercentageFromFees } from "@gmx-io/sdk/utils/fees";

// Read position price impact percentage from an existing TradeFees object
const impactPercentage = getCappedPriceImpactPercentageFromFees({
fees: tradeFees,
isSwap: false,
});

// impactPercentage is a 30-decimal fraction — divide by 1e28 for a basis-point value
console.log(impactPercentage);

getMaxNegativeImpactBps

getMaxNegativeImpactBps(marketInfo: MarketInfo): bigint

Converts a market's maxPositionImpactFactorNegative from a 30-decimal factor to basis points. Use this to display the maximum adverse price impact a user can experience in a given market.

ParameterTypeDescription
marketInfoMarketInfoMarket to read the negative impact factor from
import { getMaxNegativeImpactBps } from "@gmx-io/sdk/utils/fees";

const maxNegImpactBps = getMaxNegativeImpactBps(marketInfo);
// For example, 50n means 0.50% max negative price impact