如何用Go计算Uniswap V3与SushiSwap的价格冲击?
SushiSwap(Uniswap V2 模型)价格冲击计算
核心逻辑
基于恒定乘积公式x*y=k,模拟交易前后的储备变化,对比交易前后的代币价格差计算冲击,需注意扣除0.3%的交易手续费。
计算步骤
- 确定交易方向:比如用token0兑换token1,输入金额为
amountIn - 计算扣除手续费后的实际输入金额:
adjustedAmountIn = amountIn * 0.997 - 推导交易后的储备:
- 若输入token0:
newReserve0 = reserve0 + adjustedAmountIn,newReserve1 = reserve1 - (adjustedAmountIn * reserve1) / newReserve0 - 若输入token1:
newReserve1 = reserve1 + adjustedAmountIn,newReserve0 = reserve0 - (adjustedAmountIn * reserve0) / newReserve1
- 若输入token0:
- 计算价格冲击:
- 原价格:
priceBefore = 目标代币储备 / 输入代币储备 - 交易后价格:
priceAfter = 新目标代币储备 / 新输入代币储备 - 冲击值:
((priceAfter - priceBefore) / priceBefore) * 100%
- 原价格:
Go 代码示例
package main import "fmt" // calculateSushiSwapPriceImpact 计算SushiSwap的价格冲击 // reserve0/reserve1: 池中的代币储备 // amountIn: 输入的代币数量(已转换为代币单位,比如USDC直接输100代表100个) // isToken0In: 是否输入的是token0 func calculateSushiSwapPriceImpact(reserve0, reserve1, amountIn float64, isToken0In bool) float64 { const feeRate = 0.997 // 0.3%手续费 var priceBefore, priceAfter float64 if isToken0In { priceBefore = reserve1 / reserve0 adjustedAmount := amountIn * feeRate newRes0 := reserve0 + adjustedAmount newRes1 := reserve1 - (adjustedAmount * reserve1) / newRes0 priceAfter = newRes1 / newRes0 } else { priceBefore = reserve0 / reserve1 adjustedAmount := amountIn * feeRate newRes1 := reserve1 + adjustedAmount newRes0 := reserve0 - (adjustedAmount * reserve0) / newRes1 priceAfter = newRes0 / newRes1 } return ((priceAfter - priceBefore) / priceBefore) * 100 } func main() { // 示例:USDC/WETH池,reserve0=10000 USDC,reserve1=5 WETH,输入100 USDC兑换WETH impact := calculateSushiSwapPriceImpact(10000, 5, 100, true) fmt.Printf("价格冲击: %.2f%%\n", impact) }
Uniswap V3 价格冲击计算
Uniswap V3采用集中流动性模型,定价基于sqrtPriceX96(即sqrt(token1/token0)乘以2^96的整数编码值),流动性liquidity代表当前活跃价格区间内的流动性总量。你之前的错误主要是忽略了编码单位转换,以及误用了V2的恒定乘积模型。
问题1:从sqrtPrice和liquidity推导代币储备
核心公式
假设当前价格P = token1/token0,sqrtP = sqrt(P),则:
- token0储备:
reserve0 = liquidity / sqrtP - token1储备:
reserve1 = liquidity * sqrtP
关键注意事项
sqrtPriceX96需要先转换为实际的sqrtP:sqrtP = sqrtPriceX96 / 2^96- 需处理代币小数位数,将计算结果转换为人类可读的代币单位(比如从wei转换为USDC的6位小数单位)
Go 代码示例(推导储备)
package main import ( "fmt" "math/big" ) // calculateUniswapV3Reserves 从sqrtPriceX96和liquidity计算代币储备 // sqrtPriceX96: API返回的sqrtPrice(X96编码) // liquidity: API返回的流动性值 // decimals0/decimals1: 对应代币的小数位数 func calculateUniswapV3Reserves(sqrtPriceX96, liquidity *big.Int, decimals0, decimals1 uint8) (*big.Float, *big.Float) { pow96 := new(big.Int).Lsh(big.NewInt(1), 96) // 转换sqrtPriceX96为实际的sqrtP sqrtP := new(big.Float).Quo(new(big.Float).SetInt(sqrtPriceX96), new(big.Float).SetInt(pow96)) L := new(big.Float).SetInt(liquidity) // 计算reserve0和reserve1(wei单位) reserve0Wei := new(big.Float).Quo(L, sqrtP) reserve1Wei := new(big.Float).Mul(L, sqrtP) // 转换为代币单位 div0 := new(big.Float).SetFloat64(float64(new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimals0)), nil))) div1 := new(big.Float).SetFloat64(float64(new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimals1)), nil))) reserve0 := new(big.Float).Quo(reserve0Wei, div0) reserve1 := new(big.Float).Quo(reserve1Wei, div1) return reserve0, reserve1 } func main() { // 示例:WETH/USDC池,sqrtPriceX96对应价格约1800 USDC/WETH sqrtPriceX96, _ := new(big.Int).SetString("1461446703485210103287273052203988822378723970342", 10) liquidity := big.NewInt(1000000000000000000) // 1e18流动性 res0, res1 := calculateUniswapV3Reserves(sqrtPriceX96, liquidity, 6, 18) fmt.Printf("Reserve0 (USDC): %.2f\n", res0) fmt.Printf("Reserve1 (WETH): %.6f\n", res1) }
问题2:计算Uniswap V3的价格冲击
核心逻辑
模拟交易对sqrtP的影响,推导交易后的价格,再计算冲击值,需使用高精度整数运算避免精度丢失。
计算步骤
- 扣除手续费后的实际输入金额:
adjustedAmountIn = amountIn * 手续费率(比如0.3%手续费用0.997) - 根据交易方向计算新的
sqrtP:- 输入token0:
newSqrtP = sqrtP + (adjustedAmountIn * sqrtP²) / liquidity - 输入token1:
newSqrtP = sqrtP - adjustedAmountIn / (liquidity * sqrtP)
- 输入token0:
- 计算交易前后的价格:
priceBefore = sqrtP²,priceAfter = newSqrtP² - 价格冲击:
((priceAfter - priceBefore) / priceBefore) * 100%
Go 代码示例(计算价格冲击)
package main import ( "fmt" "math/big" ) const feeRate03 = 0.997 // 0.3%手续费 // calculateUniswapV3PriceImpact 计算Uniswap V3的价格冲击 // amountIn: 输入的代币金额(wei单位) func calculateUniswapV3PriceImpact(sqrtPriceX96, liquidity, amountIn *big.Int, isToken0In bool, decimals0, decimals1 uint8) float64 { pow96 := new(big.Int).Lsh(big.NewInt(1), 96) sqrtP := new(big.Float).Quo(new(big.Float).SetInt(sqrtPriceX96), new(big.Float).SetInt(pow96)) L := new(big.Float).SetInt(liquidity) // 处理手续费 adjustedAmount := new(big.Float).Mul(new(big.Float).SetInt(amountIn), new(big.Float).SetFloat64(feeRate03)) priceBefore := new(big.Float).Mul(sqrtP, sqrtP) var newSqrtP *big.Float if isToken0In { sqrtPSq := new(big.Float).Mul(sqrtP, sqrtP) deltaSqrtP := new(big.Float).Mul(new(big.Float).Mul(adjustedAmount, sqrtPSq), new(big.Float).Quo(big.NewFloat(1), L)) newSqrtP = new(big.Float).Add(sqrtP, deltaSqrtP) } else { denominator := new(big.Float).Mul(L, sqrtP) deltaSqrtP := new(big.Float).Mul(new(big.Float).Quo(adjustedAmount, denominator), big.NewFloat(-1)) newSqrtP = new(big.Float).Add(sqrtP, deltaSqrtP) } priceAfter := new(big.Float).Mul(newSqrtP, newSqrtP) delta := new(big.Float).Sub(priceAfter, priceBefore) impact := new(big.Float).Mul(new(big.Float).Quo(delta, priceBefore), big.NewFloat(100)) impactFloat, _ := impact.Float64() return impactFloat } func main() { sqrtPriceX96, _ := new(big.Int).SetString("1461446703485210103287273052203988822378723970342", 10) liquidity := big.NewInt(1000000000000000000) amountIn := big.NewInt(100000000) // 100 USDC(wei单位,6位小数) impact := calculateUniswapV3PriceImpact(sqrtPriceX96, liquidity, amountIn, true, 6, 18) fmt.Printf("Uniswap V3价格冲击: %.4f%%\n", impact) }
内容的提问来源于stack exchange,提问作者Bongodev
相关产品推荐
相关产品推荐

