Skip to main content

Overview

Drift Protocol uses a cross-margined system where all collateral is shared across positions. Margin requirements are calculated based on position size, asset volatility, and market conditions.

Margin Types

  • Initial Margin - Required to open a new position
  • Maintenance Margin - Minimum margin to keep position open
  • Free Collateral - Available collateral for new positions

Initial Margin Calculation

The margin required to open a trade is calculated based on the position’s notional value:
/**
 * Calculate margin required for a trade in USDC
 * 
 * @param driftClient - DriftClient instance
 * @param targetMarketIndex - Market index
 * @param baseSize - Position size (BASE_PRECISION: 10^9)
 * @param userMaxMarginRatio - Optional user max margin ratio override
 * @param userHighLeverageMode - Whether user has high leverage mode enabled
 * @param entryPrice - Optional entry price override
 * @returns Margin required (QUOTE_PRECISION: 10^6)
 */
export function calculateMarginUSDCRequiredForTrade(
  driftClient: DriftClient,
  targetMarketIndex: number,
  baseSize: BN,
  userMaxMarginRatio?: number,
  userHighLeverageMode?: boolean,
  entryPrice?: BN
): BN {
  const targetMarket = driftClient.getPerpMarketAccount(targetMarketIndex);

  const price = entryPrice ?? 
    driftClient.getOracleDataForPerpMarket(targetMarket.marketIndex).price;

  const perpLiabilityValue = calculatePerpLiabilityValue(
    baseSize,
    price,
    isVariant(targetMarket.contractType, 'prediction')
  );

  const marginRequired = new BN(
    calculateMarketMarginRatio(
      targetMarket,
      baseSize.abs(),
      'Initial',
      userMaxMarginRatio,
      userHighLeverageMode
    )
  )
    .mul(perpLiabilityValue)
    .div(MARGIN_PRECISION);

  return marginRequired;
}

Formula

marginRequired = (marginRatio × liabilityValue) / MARGIN_PRECISION

where:
  liabilityValue = |baseAssetAmount| × price / BASE_PRECISION
  MARGIN_PRECISION = 10,000

Example

import { 
  calculateMarginUSDCRequiredForTrade,
  BASE_PRECISION,
  QUOTE_PRECISION,
  convertToNumber
} from '@drift-labs/sdk';

const driftClient = /* initialized DriftClient */;
const marketIndex = 0; // SOL-PERP
const positionSize = new BN(10).mul(BASE_PRECISION); // 10 SOL

// Calculate margin required
const marginBN = calculateMarginUSDCRequiredForTrade(
  driftClient,
  marketIndex,
  positionSize
);

const marginUSD = convertToNumber(marginBN, QUOTE_PRECISION);
console.log(`Margin required: $${marginUSD}`);

Liability Value Calculation

For standard perpetuals:
export function calculatePerpLiabilityValue(
  baseAssetAmount: BN,
  price: BN,
  isPredictionMarket: boolean
): BN {
  if (isPredictionMarket) {
    if (baseAssetAmount.gt(ZERO)) {
      return baseAssetAmount.mul(price).div(BASE_PRECISION);
    } else {
      return baseAssetAmount
        .abs()
        .mul(MAX_PREDICTION_PRICE.sub(price))
        .div(BASE_PRECISION);
    }
  } else {
    return baseAssetAmount.abs().mul(price).div(BASE_PRECISION);
  }
}
For prediction markets, short positions have different liability calculations based on the max price.

Oracle Price for Margin

Margin calculations use conservative oracle prices with offsets:
export function calculateOraclePriceForPerpMargin(
  perpPosition: PerpPosition,
  market: PerpMarketAccount,
  oraclePriceData: OraclePriceData
): BN {
  const oraclePriceOffset = BN.min(
    new BN(market.amm.maxSpread)
      .mul(oraclePriceData.price)
      .div(BID_ASK_SPREAD_PRECISION),
    oraclePriceData.confidence.add(
      new BN(market.amm.baseSpread)
        .mul(oraclePriceData.price)
        .div(BID_ASK_SPREAD_PRECISION)
    )
  );

  let marginPrice: BN;
  if (perpPosition.baseAssetAmount.gt(ZERO)) {
    // Long position: use lower price (more conservative)
    marginPrice = oraclePriceData.price.sub(oraclePriceOffset);
  } else {
    // Short position: use higher price (more conservative)
    marginPrice = oraclePriceData.price.add(oraclePriceOffset);
  }

  return marginPrice;
}

Size-Based IMF Adjustments

Margin requirements scale with position size using the Initial Margin Fraction (IMF):

Liability Weight (for shorts/liabilities)

export function calculateSizePremiumLiabilityWeight(
  size: BN,
  imfFactor: BN,
  liabilityWeight: BN,
  precision: BN,
  isBounded = true
): BN {
  if (imfFactor.eq(ZERO)) {
    return liabilityWeight;
  }

  const sizeSqrt = squareRootBN(size.abs().mul(new BN(10)).add(new BN(1)));

  const liabilityWeightNumerator = liabilityWeight.sub(
    liabilityWeight.div(new BN(5))
  );

  const denom = new BN(100_000).mul(SPOT_MARKET_IMF_PRECISION).div(precision);

  const sizePremiumLiabilityWeight = liabilityWeightNumerator.add(
    sizeSqrt
      .mul(imfFactor)
      .div(denom)
  );

  let maxLiabilityWeight;
  if (isBounded) {
    maxLiabilityWeight = BN.max(liabilityWeight, sizePremiumLiabilityWeight);
  } else {
    maxLiabilityWeight = sizePremiumLiabilityWeight;
  }

  return maxLiabilityWeight;
}

Asset Weight (for longs/assets)

export function calculateSizeDiscountAssetWeight(
  size: BN,
  imfFactor: BN,
  assetWeight: BN
): BN {
  if (imfFactor.eq(ZERO)) {
    return assetWeight;
  }

  const sizeSqrt = squareRootBN(size.abs().mul(new BN(10)).add(new BN(1)));
  const imfNumerator = SPOT_MARKET_IMF_PRECISION.add(
    SPOT_MARKET_IMF_PRECISION.div(new BN(10))
  );

  const sizeDiscountAssetWeight = imfNumerator
    .mul(SPOT_MARKET_WEIGHT_PRECISION)
    .div(
      SPOT_MARKET_IMF_PRECISION.add(
        sizeSqrt
          .mul(imfFactor)
          .div(new BN(100_000))
      )
    );

  const minAssetWeight = BN.min(assetWeight, sizeDiscountAssetWeight);

  return minAssetWeight;
}
Larger positions require more margin due to:
  • Increased liquidation risk
  • Greater market impact
  • Reduced liquidity for larger sizes

Collateral Requirements

Calculate how much collateral is needed for a trade:
export function calculateCollateralDepositRequiredForTrade(
  driftClient: DriftClient,
  targetMarketIndex: number,
  baseSize: BN,
  collateralIndex: number,
  userMaxMarginRatio?: number,
  userHighLeverageMode?: boolean,
  estEntryPrice?: BN
): BN {
  const marginRequiredUsdc = calculateMarginUSDCRequiredForTrade(
    driftClient,
    targetMarketIndex,
    baseSize,
    userMaxMarginRatio,
    userHighLeverageMode,
    estEntryPrice
  );

  const collateralMarket = driftClient.getSpotMarketAccount(collateralIndex);
  const collateralOracleData = 
    driftClient.getOracleDataForSpotMarket(collateralIndex);

  const scaledAssetWeight = calculateScaledInitialAssetWeight(
    collateralMarket,
    collateralOracleData.price
  );

  // baseAmountRequired = (marginRequiredUsdc / priceOfAsset) / assetWeight
  const baseAmountRequired = driftClient
    .convertToSpotPrecision(collateralIndex, marginRequiredUsdc)
    .mul(PRICE_PRECISION)
    .mul(SPOT_MARKET_WEIGHT_PRECISION)
    .div(collateralOracleData.price)
    .div(scaledAssetWeight)
    .div(QUOTE_PRECISION);

  return baseAmountRequired;
}

Example: Calculate SOL Collateral Needed

import { 
  calculateCollateralDepositRequiredForTrade,
  BASE_PRECISION,
  convertToNumber
} from '@drift-labs/sdk';

const driftClient = /* initialized DriftClient */;
const perpMarketIndex = 0;  // SOL-PERP
const spotMarketIndex = 1;  // SOL spot market
const positionSize = new BN(10).mul(BASE_PRECISION); // 10 SOL position

// How much SOL collateral needed?
const solNeededBN = calculateCollateralDepositRequiredForTrade(
  driftClient,
  perpMarketIndex,
  positionSize,
  spotMarketIndex
);

const solNeeded = convertToNumber(solNeededBN, BASE_PRECISION);
console.log(`SOL collateral needed: ${solNeeded} SOL`);

Liquidation Price

Calculate the price at which a position would be liquidated:
export function calculateLiquidationPrice(
  freeCollateral: BN,
  freeCollateralDelta: BN,
  oraclePrice: BN
): BN {
  const liqPriceDelta = freeCollateral
    .mul(QUOTE_PRECISION)
    .div(freeCollateralDelta);

  const liqPrice = oraclePrice.sub(liqPriceDelta);

  if (liqPrice.lt(ZERO)) {
    return new BN(-1);
  }

  return liqPrice;
}

Example

import { calculateLiquidationPrice } from '@drift-labs/sdk';

const user = /* User instance */;
const freeCollateral = user.getFreeCollateral();

// Calculate liquidation price for a specific position
const perpPosition = user.getPerpPosition(0); // SOL-PERP
const oraclePrice = driftClient.getOracleDataForPerpMarket(0).price;

// Would need to calculate free collateral delta based on price change
const freeCollateralDelta = /* calculated delta */;

const liqPrice = calculateLiquidationPrice(
  freeCollateral,
  freeCollateralDelta,
  oraclePrice
);

High Leverage Mode

High leverage mode provides lower margin requirements:
export function calcHighLeverageModeInitialMarginRatioFromSize(
  preSizeAdjMarginRatio: BN,
  sizeAdjMarginRatio: BN,
  defaultMarginRatio: BN
): BN {
  let result: BN;

  if (sizeAdjMarginRatio.lt(preSizeAdjMarginRatio)) {
    const sizePctDiscountFactor = PERCENTAGE_PRECISION.sub(
      preSizeAdjMarginRatio
        .sub(sizeAdjMarginRatio)
        .mul(PERCENTAGE_PRECISION)
        .div(preSizeAdjMarginRatio.div(new BN(5)))
    );

    const hlmMarginDelta = BN.max(
      preSizeAdjMarginRatio.sub(defaultMarginRatio),
      new BN(1)
    );

    const hlmMarginDeltaProportion = hlmMarginDelta
      .mul(sizePctDiscountFactor)
      .div(PERCENTAGE_PRECISION);

    result = hlmMarginDeltaProportion.add(defaultMarginRatio);
  } else if (sizeAdjMarginRatio.eq(preSizeAdjMarginRatio)) {
    result = defaultMarginRatio;
  } else {
    result = sizeAdjMarginRatio;
  }

  return result;
}
High leverage mode increases liquidation risk. Use with caution.

Worst Case Position

Margin calculations consider open orders (worst case scenario):
export function calculateWorstCasePerpLiabilityValue(
  perpPosition: PerpPosition,
  perpMarket: PerpMarketAccount,
  oraclePrice: BN,
  includeOpenOrders: boolean = true
): { worstCaseBaseAssetAmount: BN; worstCaseLiabilityValue: BN } {
  const isPredictionMarket = isVariant(perpMarket.contractType, 'prediction');
  
  if (!includeOpenOrders) {
    return {
      worstCaseBaseAssetAmount: perpPosition.baseAssetAmount,
      worstCaseLiabilityValue: calculatePerpLiabilityValue(
        perpPosition.baseAssetAmount,
        oraclePrice,
        isPredictionMarket
      ),
    };
  }
  
  const allBids = perpPosition.baseAssetAmount.add(perpPosition.openBids);
  const allAsks = perpPosition.baseAssetAmount.add(perpPosition.openAsks);

  const allBidsLiabilityValue = calculatePerpLiabilityValue(
    allBids,
    oraclePrice,
    isPredictionMarket
  );
  const allAsksLiabilityValue = calculatePerpLiabilityValue(
    allAsks,
    oraclePrice,
    isPredictionMarket
  );

  if (allAsksLiabilityValue.gte(allBidsLiabilityValue)) {
    return {
      worstCaseBaseAssetAmount: allAsks,
      worstCaseLiabilityValue: allAsksLiabilityValue,
    };
  } else {
    return {
      worstCaseBaseAssetAmount: allBids,
      worstCaseLiabilityValue: allBidsLiabilityValue,
    };
  }
}

Practical Examples

Check if User Can Open Position

import { 
  calculateMarginUSDCRequiredForTrade,
  BASE_PRECISION,
  QUOTE_PRECISION,
  convertToNumber
} from '@drift-labs/sdk';

const user = /* User instance */;
const marketIndex = 0;
const desiredSize = new BN(5).mul(BASE_PRECISION); // 5 SOL

// Get user's free collateral
const freeCollateral = user.getFreeCollateral();
const freeCollateralNum = convertToNumber(freeCollateral, QUOTE_PRECISION);

// Calculate required margin
const requiredMargin = calculateMarginUSDCRequiredForTrade(
  driftClient,
  marketIndex,
  desiredSize
);
const requiredMarginNum = convertToNumber(requiredMargin, QUOTE_PRECISION);

if (freeCollateralNum >= requiredMarginNum) {
  console.log('Can open position');
  console.log(`Required: $${requiredMarginNum}`);
  console.log(`Available: $${freeCollateralNum}`);
} else {
  console.log('Insufficient collateral');
  console.log(`Need $${requiredMarginNum - freeCollateralNum} more`);
}

Calculate Maximum Position Size

import { User } from '@drift-labs/sdk';

const user = new User({
  driftClient,
  userAccountPublicKey: await driftClient.getUserAccountPublicKey(),
});

await user.subscribe();

const marketIndex = 0; // SOL-PERP
const direction = PositionDirection.LONG;

const { tradeSize } = user.getMaxTradeSizeUSDCForPerp(marketIndex, direction);

const maxSize = convertToNumber(tradeSize, BASE_PRECISION);
console.log(`Max position size: ${maxSize} SOL`);

Build docs developers (and LLMs) love