decrease
This module provides utilities for calculating decrease position amounts, fees, and next position values when closing or reducing trading positions. It handles complex calculations including PnL realization, fee deductions, collateral adjustments, and swap requirements.
Methods
The decrease trade module exports functions for computing position close amounts, determining full-close conditions, calculating collateral costs, checking margin after a decrease, and projecting the next position state. Import any function directly from @gmx-io/sdk/utils/trade.
getDecreasePositionAmounts
getDecreasePositionAmounts(p: {
marketInfo: MarketInfo;
collateralToken: TokenData;
isLong: boolean;
position: PositionInfoLoaded | undefined;
closeSizeUsd: bigint;
keepLeverage: boolean;
triggerPrice?: bigint;
fixedAcceptablePriceImpactBps?: bigint;
acceptablePriceImpactBuffer?: number;
userReferralInfo: UserReferralInfo | undefined;
proDiscountFactor?: bigint;
minCollateralUsd: bigint;
minPositionSizeUsd: bigint;
uiFeeFactor: bigint;
isLimit?: boolean;
limitPrice?: bigint;
triggerOrderType?: DecreasePositionAmounts["triggerOrderType"];
isSetAcceptablePriceImpactEnabled: boolean;
receiveToken?: TokenData;
forceDecreaseSwapType?: DecreasePositionSwapType;
isTwap?: boolean;
}): DecreasePositionAmounts
Computes a complete DecreasePositionAmounts result for a Market Decrease, Limit Decrease, Stop-Loss, or Take-Profit order. The result includes all pricing, fee, PnL, collateral, and receive-amount fields needed to build the on-chain order.
When position is undefined or has zero size, only the fee and price fields are computed; amounts relating to PnL and collateral are left at zero.
When receiveToken is omitted, it defaults to collateralToken. forceDecreaseSwapType replaces the swap type the function would otherwise pick. proDiscountFactor applies the pro-tier discount to the position fee.
A call with no triggerOrderType and no isTwap: true counts as a Market Decrease, and these rules apply to it only:
- When the pool can't perform the profit swap, the profit stays in the PnL token.
- When the profit stays in the PnL token, costs come from collateral first and then from that profit.
- With
keepLeverage, the collateral withdrawal can't go below zero, and its output is reduced bypriceImpactDiffUsd. - A swap from the collateral token to the PnL token applies to the collateral output only.
Pass isTwap: true for a TWAP decrease so these rules don't apply to it.
In the result, positionFeeUsd, feeDiscountUsd, and borrowingFeeUsd are plain USD values for every decrease type, and unpaidCostUsd is the cost that neither collateral nor profit could cover. For every decrease type, a profit swap to the collateral token calculates its price impact on the pool after the profit payout.
import { getDecreasePositionAmounts } from "@gmx-io/sdk/utils/trade";
const decreaseAmounts = getDecreasePositionAmounts({
marketInfo,
collateralToken: usdcToken,
isLong: true,
position: currentPosition,
closeSizeUsd: 1000n * 10n ** 30n, // close $1000 of position
keepLeverage: false,
userReferralInfo: undefined,
minCollateralUsd: 5000n * 10n ** 30n,
minPositionSizeUsd: 1000n * 10n ** 30n,
uiFeeFactor: 0n,
isSetAcceptablePriceImpactEnabled: true,
});
console.log("Receive token amount:", decreaseAmounts.receiveTokenAmount);
console.log("Receive USD:", decreaseAmounts.receiveUsd);
console.log("Realized PnL:", decreaseAmounts.realizedPnl);
console.log("Position fee:", decreaseAmounts.positionFeeUsd);
console.log("Acceptable price:", decreaseAmounts.acceptablePrice);
getIsFullClose
getIsFullClose(p: {
position: PositionInfoLoaded;
sizeDeltaUsd: bigint;
indexPrice: bigint;
remainingCollateralUsd: bigint;
minCollateralUsd: bigint;
minPositionSizeUsd: bigint;
}): boolean
Returns true when the decrease must be treated as a full close. This happens when:
- The remaining position size would be
< $1after the decrease, or - A loss would leave the remaining collateral below the minimum factor threshold, and the remaining collateral + PnL is below
minCollateralUsdor the remaining size is belowminPositionSizeUsd.
import { getIsFullClose } from "@gmx-io/sdk/utils/trade";
const isFullClose = getIsFullClose({
position: currentPosition,
sizeDeltaUsd: 500n * 10n ** 30n,
indexPrice: currentPosition.markPrice,
remainingCollateralUsd: 100n * 10n ** 30n,
minCollateralUsd: 5000n * 10n ** 30n,
minPositionSizeUsd: 1000n * 10n ** 30n,
});
getMinCollateralUsdForLeverage
getMinCollateralUsdForLeverage(position: PositionInfoLoaded, openInterestDelta: bigint): bigint
Returns the minimum collateral required to keep the current position's leverage within the market's allowed range, given an open interest change.
import { getMinCollateralUsdForLeverage } from "@gmx-io/sdk/utils/trade";
const minCollateral = getMinCollateralUsdForLeverage(
currentPosition,
-1000n * 10n ** 30n // OI decreasing by $1000
);
payForCollateralCost
payForCollateralCost(p: {
initialCostUsd: bigint;
collateralToken: TokenData;
collateralPrice: bigint;
outputAmount: bigint;
remainingCollateralAmount: bigint;
}): {
outputAmount: bigint;
remainingCollateralAmount: bigint;
paidOutputAmount: bigint;
paidRemainingCollateralAmount: bigint;
unpaidCostAmount: bigint;
}
Deducts initialCostUsd from outputAmount first, then from remainingCollateralAmount. Returns the updated balances, the amounts paid from each source, and unpaidCostAmount, the part of the cost neither source could cover.
import { payForCollateralCost } from "@gmx-io/sdk/utils/trade";
const result = payForCollateralCost({
initialCostUsd: 100n * 10n ** 30n,
collateralToken: usdcToken,
collateralPrice: 1n * 10n ** 30n, // $1 per USDC
outputAmount: 50_000_000n, // 50 USDC (6 decimals)
remainingCollateralAmount: 1_000_000_000n,
});
// result.paidOutputAmount — deducted from output
// result.paidRemainingCollateralAmount — deducted from remaining collateral
// result.unpaidCostAmount — not covered by either source
estimateCollateralCost
estimateCollateralCost(baseUsd: bigint, collateralToken: TokenData, collateralPrice: bigint): {
amount: bigint;
usd: bigint;
}
Converts baseUsd to the collateral token's amount using collateralToken.prices.minPrice, then converts back to USD using collateralPrice. Used to estimate costs denominated in the collateral token.
import { estimateCollateralCost } from "@gmx-io/sdk/utils/trade";
const { amount, usd } = estimateCollateralCost(
50n * 10n ** 30n, // $50
usdcToken,
1n * 10n ** 30n // $1 per USDC
);
// amount: token units of the cost
// usd: cost in USD at collateralPrice
getTotalFeesUsdForDecrease
getTotalFeesUsdForDecrease(p: {
positionFeeUsd: bigint;
borrowingFeeUsd: bigint;
fundingFeeUsd: bigint;
swapProfitFeeUsd: bigint;
swapUiFeeUsd: bigint;
uiFeeUsd: bigint;
pnlUsd: bigint;
totalPendingImpactDeltaUsd: bigint;
}): bigint
Returns the total cost that must be covered by the position's output. Only negative contributions are counted: a negative pnlUsd (loss) and a negative totalPendingImpactDeltaUsd (adverse price impact) are added as absolute values.
import { getTotalFeesUsdForDecrease } from "@gmx-io/sdk/utils/trade";
const totalCost = getTotalFeesUsdForDecrease({
positionFeeUsd: 10n * 10n ** 30n,
borrowingFeeUsd: 5n * 10n ** 30n,
fundingFeeUsd: 2n * 10n ** 30n,
swapProfitFeeUsd: 1n * 10n ** 30n,
swapUiFeeUsd: 0n,
uiFeeUsd: 0n,
pnlUsd: -10n * 10n ** 30n, // $10 loss is added to total cost
totalPendingImpactDeltaUsd: -5n * 10n ** 30n, // negative impact is added
});
// totalCost = 10 + 5 + 2 + 1 + 10 + 5 = $33
getNextPositionValuesForDecreaseTrade
getNextPositionValuesForDecreaseTrade(p: {
existingPosition?: PositionInfo;
marketInfo: MarketInfo;
collateralToken: TokenData;
sizeDeltaUsd: bigint;
sizeDeltaInTokens: bigint;
realizedPnl: bigint;
estimatedPnl: bigint;
collateralDeltaUsd: bigint;
collateralDeltaAmount: bigint;
payedRemainingCollateralUsd: bigint;
payedRemainingCollateralAmount: bigint;
proportionalPendingImpactDeltaUsd: bigint;
showPnlInLeverage: boolean;
isLong: boolean;
minCollateralUsd: bigint;
userReferralInfo: UserReferralInfo | undefined;
priceImpactDiffUsd?: bigint;
}): NextPositionValues
Computes the projected position state after a decrease: remaining size, collateral, leverage, PnL percentage, and liquidation price. Use this to populate UI previews before order submission. Pass priceImpactDiffUsd to reduce the projected collateral withdrawal by it.
import { getNextPositionValuesForDecreaseTrade } from "@gmx-io/sdk/utils/trade";
const nextValues = getNextPositionValuesForDecreaseTrade({
existingPosition: currentPosition,
marketInfo,
collateralToken: usdcToken,
sizeDeltaUsd: decreaseAmounts.sizeDeltaUsd,
sizeDeltaInTokens: decreaseAmounts.sizeDeltaInTokens,
realizedPnl: decreaseAmounts.realizedPnl,
estimatedPnl: decreaseAmounts.estimatedPnl,
collateralDeltaUsd: decreaseAmounts.collateralDeltaUsd,
collateralDeltaAmount: decreaseAmounts.collateralDeltaAmount,
payedRemainingCollateralUsd: decreaseAmounts.payedRemainingCollateralUsd,
payedRemainingCollateralAmount: decreaseAmounts.payedRemainingCollateralAmount,
proportionalPendingImpactDeltaUsd: decreaseAmounts.proportionalPendingImpactDeltaUsd,
showPnlInLeverage: true,
isLong: true,
minCollateralUsd: 5000n * 10n ** 30n,
userReferralInfo: undefined,
});
console.log("Next size:", nextValues.nextSizeUsd);
console.log("Next leverage:", nextValues.nextLeverage);
console.log("Next liq price:", nextValues.nextLiqPrice);
getMaxWithdrawAmount
getMaxWithdrawAmount(p: {
position: PositionInfoLoaded;
minCollateralUsd: bigint | undefined;
collateralPrice: bigint | undefined;
collateralDecimals: number | undefined;
userReferralInfo: UserReferralInfo | undefined;
proDiscountFactor?: bigint;
}): bigint
Returns the most collateral you can withdraw from a position, in collateral token units. The result is the lowest of three limits: the collateral above the position's minimum required collateral, the collateral above the minimum for leverage after pending fees and PnL, and the collateral above the minimum for the market's maximum allowed leverage. It returns 0n when any limit is reached.
import { getMaxWithdrawAmount } from "@gmx-io/sdk/utils/trade";
const maxWithdrawAmount = getMaxWithdrawAmount({
position: currentPosition,
minCollateralUsd: 5000n * 10n ** 30n,
collateralPrice: usdcToken.prices.minPrice,
collateralDecimals: usdcToken.decimals,
userReferralInfo: undefined,
});
getDecreaseResultingPositionMarginState
getDecreaseResultingPositionMarginState(p: {
marketInfo: MarketInfo;
collateralToken: TokenData;
isLong: boolean;
position: {
sizeInUsd: bigint;
sizeInTokens: bigint;
collateralAmount: bigint;
pendingImpactAmount: bigint;
};
sizeDeltaUsd: bigint;
sizeDeltaInTokens: bigint;
collateralDeltaAmount: bigint;
payedRemainingCollateralAmount: bigint;
priceImpactDiffUsd: bigint;
minCollateralUsd: bigint;
minPositionSizeUsd: bigint;
userReferralInfo: UserReferralInfo | undefined;
proDiscountFactor?: bigint;
indexPriceForEvaluation?: bigint;
}): DecreasePositionMarginState | undefined
Checks whether the position a decrease leaves passes the margin checks the contracts run during a decrease. Use it for a Market Decrease, or for a collateral withdrawal that leaves the position open. Pass indexPriceForEvaluation to evaluate at a price other than the current one. The parameter type is exported as DecreaseResultingPositionMarginStateParams.
The function first checks the collateral left after the withdrawal against the minimum for the market's open interest and, together with the remaining PnL, against minCollateralUsd. If that check fails:
- With
sizeDeltaUsdof0n, it returnsisLiquidatable: truewith the reasonUnableToWithdrawCollateral. - With a size change, it continues as if the collateral stayed in the position. When a withdrawal was requested, it sets
isCollateralWithdrawalCancelled: true, because the contracts would cancel it.
It then runs getResultingPositionMarginState on the resulting position with shouldValidateMinCollateralUsd: false.
DecreasePositionMarginState is PositionMarginState plus isCollateralWithdrawalCancelled. The function returns undefined when the check doesn't apply: a full close, a position with no size, a remaining size or collateral below the minimums, or a price of 0n or less. undefined doesn't mean the position passes.
import { getDecreaseResultingPositionMarginState } from "@gmx-io/sdk/utils/trade";
const marginState = getDecreaseResultingPositionMarginState({
marketInfo,
collateralToken: usdcToken,
isLong: true,
position: currentPosition,
sizeDeltaUsd: decreaseAmounts.sizeDeltaUsd,
sizeDeltaInTokens: decreaseAmounts.sizeDeltaInTokens,
collateralDeltaAmount: decreaseAmounts.collateralDeltaAmount,
payedRemainingCollateralAmount: decreaseAmounts.payedRemainingCollateralAmount,
priceImpactDiffUsd: decreaseAmounts.priceImpactDiffUsd,
minCollateralUsd: 5000n * 10n ** 30n,
minPositionSizeUsd: 1000n * 10n ** 30n,
userReferralInfo: undefined,
});
if (marginState?.isLiquidatable) {
console.log("Expected to fail at execution:", marginState.reason);
}
getTriggerDecreaseRemainingPositionMarginState
getTriggerDecreaseRemainingPositionMarginState(p: {
marketInfo: MarketInfo;
collateralToken: TokenData;
isLong: boolean;
position: DecreaseResultingPositionMarginStateParams["position"];
decreaseAmounts: DecreasePositionAmounts;
collateralDeltaAmount: bigint;
minCollateralUsd: bigint;
minPositionSizeUsd: bigint;
userReferralInfo: UserReferralInfo | undefined;
proDiscountFactor?: bigint;
}): DecreasePositionMarginState | undefined
Runs the same checks for a Stop-Loss or Take-Profit at its trigger price. Pass the order's amounts at that price as decreaseAmounts, and the collateral withdrawal the order stores as collateralDeltaAmount. It returns undefined for a full close or when decreaseAmounts has no trigger price. The parameter type is exported as TriggerDecreaseRemainingPositionMarginStateParams.
getIsProfitKeptInPnlToken
getIsProfitKeptInPnlToken(p: {
pnlToken: TokenData;
collateralToken: TokenData;
decreaseSwapType: DecreasePositionSwapType;
isProfitSwapReverted?: boolean;
}): boolean
Returns true when the PnL token and collateral token differ and the profit isn't swapped to the collateral token: either the decrease has no profit swap, or isProfitSwapReverted is true.
getCollateralDeltaAmountAfterPriceImpactDiff
getCollateralDeltaAmountAfterPriceImpactDiff(p: {
collateralDeltaAmount: bigint;
priceImpactDiffUsd: bigint;
collateralToken: TokenData;
}): bigint
Returns the collateral withdrawal reduced by priceImpactDiffUsd, converted at the collateral token's minimum price. The result never goes below 0n. When collateralDeltaAmount or priceImpactDiffUsd is 0n or less, it returns collateralDeltaAmount unchanged.