Price Impact

Calculate how your swap affects price as it consumes liquidity across multiple bins. Essential for understanding large trade costs and market impact.

What is Price Impact?

Price impact measures the difference between:

  • Spot Price: Current price at active bin

  • Execution Price: Average price after swap

Small swap:  Uses 1 bin   → Low impact
Large swap:  Uses 5 bins  → High impact

Basic Impact Calculation

async function calculatePriceImpact(
  pair: LBPair,
  amountIn: bigint,
  xtoy: boolean
): Promise<number> {
  // Get spot price
  const spotPrice = getPriceFromBinId(
    pair.parameters.active_id,
    Number(pair.binStep)
  );
  
  // Calculate output
  const amountOut = await calculateSwapOutput(pair, amountIn, xtoy);
  
  // Calculate execution price
  const executionPrice = xtoy
    ? Number(amountOut) / Number(amountIn)
    : Number(amountIn) / Number(amountOut);
  
  // Price impact percentage
  return ((executionPrice - spotPrice) / spotPrice) * 100;
}

Bin-by-Bin Analysis

async function analyzeBinImpact(
  pair: LBPair,
  amountIn: bigint,
  xtoy: boolean
) {
  const activeId = pair.parameters.active_id;
  const binStep = Number(pair.binStep);
  
  // Get affected bins
  const binRange = xtoy 
    ? [activeId, activeId + 50]
    : [activeId - 50, activeId];
    
  const bins = await sdk.Pair.getPairBins(pair, binRange);
  
  let remaining = amountIn;
  let totalOut = 0n;
  const binImpacts = [];
  
  for (let i = 0; i < bins.length; i++) {
    const binId = binRange[0] + i;
    const bin = bins[i];
    const binPrice = getPriceFromBinId(binId, binStep);
    
    // How much liquidity consumed from this bin
    const available = xtoy ? bin.reserve_y : bin.reserve_x;
    const consumed = remaining > available ? available : remaining;
    
    if (consumed > 0n) {
      binImpacts.push({
        binId,
        price: binPrice,
        consumed: consumed.toString(),
        percentOfTotal: (Number(consumed) / Number(amountIn) * 100).toFixed(2)
      });
      
      remaining -= consumed;
      totalOut += consumed; // Simplified
    }
    
    if (remaining === 0n) break;
  }
  
  return {
    binsUsed: binImpacts.length,
    startPrice: binImpacts[0]?.price,
    endPrice: binImpacts[binImpacts.length - 1]?.price,
    details: binImpacts
  };
}

Impact Thresholds

function categorizeImpact(impactPercent: number): string {
  if (impactPercent < 0.1) return "Negligible";
  if (impactPercent < 0.5) return "Low";
  if (impactPercent < 1.0) return "Moderate";
  if (impactPercent < 3.0) return "High";
  return "Severe";
}

// Warning system
async function checkSwapViability(
  pair: LBPair,
  amountIn: bigint,
  xtoy: boolean
) {
  const impact = await calculatePriceImpact(pair, amountIn, xtoy);
  
  return {
    impact: impact.toFixed(2) + "%",
    category: categorizeImpact(Math.abs(impact)),
    warning: Math.abs(impact) > 1.0 ? "Consider smaller trade size" : null
  };
}

Depth Chart Analysis

// Calculate cumulative depth and impact
async function getMarketDepth(
  pair: LBPair,
  xtoy: boolean,
  maxBins: number = 50
) {
  const activeId = pair.parameters.active_id;
  const binRange = xtoy
    ? [activeId, activeId + maxBins]
    : [activeId - maxBins, activeId];
    
  const bins = await sdk.Pair.getPairBins(pair, binRange);
  
  let cumulativeLiquidity = 0n;
  const depthLevels = [];
  
  for (let i = 0; i < bins.length; i++) {
    const bin = bins[i];
    const liquidity = xtoy ? bin.reserve_y : bin.reserve_x;
    cumulativeLiquidity += liquidity;
    
    const impact = await calculatePriceImpact(
      pair,
      cumulativeLiquidity,
      xtoy
    );
    
    depthLevels.push({
      cumulative: cumulativeLiquidity,
      impactPercent: impact,
      binId: binRange[0] + i
    });
  }
  
  return depthLevels;
}

Optimal Trade Size

// Find max trade size for target impact
async function findOptimalTradeSize(
  pair: LBPair,
  maxImpactPercent: number,
  xtoy: boolean
): Promise<bigint> {
  const testAmounts = [
    parseEther("0.1"),
    parseEther("1"),
    parseEther("10"),
    parseEther("100"),
    parseEther("1000")
  ];
  
  let optimalAmount = 0n;
  
  for (const amount of testAmounts) {
    const impact = await calculatePriceImpact(pair, amount, xtoy);
    
    if (Math.abs(impact) <= maxImpactPercent) {
      optimalAmount = amount;
    } else {
      break;
    }
  }
  
  return optimalAmount;
}

Real-Time Display

// User-friendly impact display
async function displayPriceImpact(
  pair: LBPair,
  inputAmount: string,
  decimals: number,
  xtoy: boolean
) {
  const amount = parseUnits(inputAmount, decimals);
  const analysis = await analyzeBinImpact(pair, amount, xtoy);
  const impact = await calculatePriceImpact(pair, amount, xtoy);
  
  return {
    priceImpact: impact.toFixed(2) + "%",
    binsAffected: analysis.binsUsed,
    priceRange: {
      start: analysis.startPrice?.toFixed(4),
      end: analysis.endPrice?.toFixed(4)
    },
    recommendation: Math.abs(impact) > 2 
      ? "Consider splitting into smaller trades"
      : "Acceptable impact"
  };
}

Key Insights

  • Linear in bins: Each bin adds similar impact

  • Exponential in size: Doubling size > doubles impact

  • Direction matters: Buy/sell impacts differ

  • Time sensitive: Impact changes with liquidity

Last updated