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.
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.
| Parameter | Type | Description |
|---|---|---|
sizeDeltaUsd | bigint | Trade size in USD (30-decimal precision) |
acceptablePrice | bigint | Worst price the user accepts (30-decimal precision) |
indexPrice | bigint | Current mark price of the index token (30-decimal precision) |
isLong | boolean | true for long positions |
isIncrease | boolean | true 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.
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.
| Parameter | Type | Description |
|---|---|---|
marketInfo | MarketInfo | Market containing the swap impact pool |
token | TokenData | Collateral token being swapped in or out |
priceImpactDeltaUsd | bigint | Raw 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.
| Parameter | Type | Description |
|---|---|---|
marketInfo | MarketInfo | Target market |
sizeDeltaUsd | bigint | Absolute position size change in USD (30-decimal precision) |
isLong | boolean | true for long positions |
isIncrease | boolean | true when opening or increasing; false when decreasing |
opts.fallbackToZero | boolean | Return zero instead of throwing when pool goes negative |
opts.shouldCapNegativeImpact | boolean | Also 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.
| Parameter | Type | Description |
|---|---|---|
marketInfo | MarketInfo | Market containing the position impact pool |
positionImpactDeltaUsd | bigint | Raw 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.
| Parameter | Type | Description |
|---|---|---|
marketInfo | MarketInfo | Market with configured max impact factors |
sizeDeltaUsd | bigint | Trade size in USD (signed; negative for decreases) |
positionImpactDeltaUsd | bigint | Raw 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.
| Parameter | Type | Description |
|---|---|---|
marketInfo | MarketInfo | Market 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.
| Parameter | Type | Description |
|---|---|---|
marketInfo | MarketInfo | Market with open interest and virtual inventory data |
sizeDeltaUsd | bigint | Signed size delta in USD — positive for longs, negative for shorts |
isLong | boolean | true for long positions |
opts.fallbackToZero | boolean | Return 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.
| Parameter | Type | Description |
|---|---|---|
sizeInUsd | bigint | Full position size in USD (30-decimal precision) |
pendingImpactAmount | bigint | Accrued pending impact in index tokens |
sizeDeltaUsd | bigint | Size being closed in USD |
indexToken | TokenData | Index 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.
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.
| Parameter | Type | Description |
|---|---|---|
marketInfo | MarketInfo | Market containing both tokens |
tokenA | TokenData | First swap token |
tokenB | TokenData | Second swap token |
usdDeltaTokenA | bigint | USD added to the pool for token A (positive = deposit, negative = withdrawal) |
usdDeltaTokenB | bigint | USD added to the pool for token B |
opts.fallbackToZero | boolean | Return 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.
| Parameter | Type | Description |
|---|---|---|
longToken | TokenData | Long-side collateral token with price data |
shortToken | TokenData | Short-side collateral token with price data |
longPoolAmount | bigint | Current long pool amount in token units |
shortPoolAmount | bigint | Current short pool amount in token units |
longDeltaUsd | bigint | USD change to the long pool (30-decimal precision) |
shortDeltaUsd | bigint | USD 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.
| Parameter | Type | Description |
|---|---|---|
currentLongUsd | bigint | Current long-side USD value |
currentShortUsd | bigint | Current short-side USD value |
nextLongUsd | bigint | Post-trade long-side USD value |
nextShortUsd | bigint | Post-trade short-side USD value |
factorPositive | bigint | Impact factor when balance improves (30-decimal fraction) |
factorNegative | bigint | Impact factor when balance worsens |
exponentFactorPositive | bigint | Exponent applied to positive-impact calculations (30-decimal) |
exponentFactorNegative | bigint | Exponent applied to negative-impact calculations |
fallbackToZero | boolean | Return 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.
| Parameter | Type | Description |
|---|---|---|
currentDiff | bigint | Absolute difference between current long and short pools (30-decimal USD) |
nextDiff | bigint | Absolute difference between next long and short pools |
hasPositiveImpact | boolean | true when the trade reduces the imbalance |
factor | bigint | Impact factor (30-decimal fraction) |
exponentFactor | bigint | Exponent (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.
| Parameter | Type | Description |
|---|---|---|
currentDiff | bigint | Absolute imbalance before the trade (30-decimal USD) |
nextDiff | bigint | Absolute imbalance after the trade (now on the opposite side) |
factorPositive | bigint | Factor applied to the balance-improving portion |
factorNegative | bigint | Factor applied to the balance-worsening portion |
exponentFactorPositive | bigint | Exponent for the positive portion (30-decimal) |
exponentFactorNegative | bigint | Exponent 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.
| Parameter | Type | Description |
|---|---|---|
diff | bigint | Pool imbalance value (30-decimal USD) |
factor | bigint | Impact factor (30-decimal fraction) |
exponent | bigint | Exponent (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.
| Parameter | Type | Description |
|---|---|---|
fees | TradeFees | undefined | Pre-computed trade fees object |
isSwap | boolean | true 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.
| Parameter | Type | Description |
|---|---|---|
marketInfo | MarketInfo | Market 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