package math import ( "fmt" "math" "math/big" ) // PriceImpactCalculator provides a unified interface for calculating price impact across all protocols type PriceImpactCalculator struct { mathCalculator *MathCalculator } // NewPriceImpactCalculator creates a new price impact calculator func NewPriceImpactCalculator() *PriceImpactCalculator { return &PriceImpactCalculator{ mathCalculator: NewMathCalculator(), } } // CalculatePriceImpact calculates price impact for any supported protocol func (pic *PriceImpactCalculator) CalculatePriceImpact( protocol string, amountIn, reserveIn, reserveOut *big.Int, sqrtPriceX96, liquidity *big.Int, // For Uniswap V3 and Kyber ) (float64, error) { switch protocol { case "uniswap_v2", "sushiswap": return pic.mathCalculator.uniswapV2.CalculatePriceImpact(amountIn, reserveIn, reserveOut) case "uniswap_v3", "camelot_v3": return pic.mathCalculator.uniswapV3.CalculatePriceImpact(amountIn, sqrtPriceX96, liquidity) case "curve": return pic.mathCalculator.curve.CalculatePriceImpact(amountIn, reserveIn, reserveOut) case "kyber_elastic", "kyber_classic": return pic.mathCalculator.kyber.CalculatePriceImpact(amountIn, sqrtPriceX96, liquidity) case "balancer": return pic.mathCalculator.balancer.CalculatePriceImpact(amountIn, reserveIn, reserveOut) case "constant_sum": return pic.mathCalculator.constantSum.CalculatePriceImpact(amountIn, reserveIn, reserveOut) case "algebra_v1": return pic.calculateAlgebraPriceImpact(amountIn, reserveIn, reserveOut) case "integral": return pic.calculateIntegralPriceImpact(amountIn, reserveIn, reserveOut) case "oneinch": return pic.calculateOneInchPriceImpact(amountIn, reserveIn, reserveOut) default: return 0, fmt.Errorf("unsupported protocol: %s", protocol) } } // calculateAlgebraPriceImpact calculates price impact for Algebra V1.9 func (pic *PriceImpactCalculator) calculateAlgebraPriceImpact(amountIn, reserveIn, reserveOut *big.Int) (float64, error) { if amountIn.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } // Calculate new reserves after swap amountOut, err := NewAlgebraV1Math().CalculateAmountOutAlgebra(amountIn, reserveIn, reserveOut, 500) if err != nil { return 0, err } newReserveIn := new(big.Int).Add(reserveIn, amountIn) newReserveOut := new(big.Int).Sub(reserveOut, amountOut) // Calculate price before and after swap priceBefore := new(big.Float).Quo(new(big.Float).SetInt(reserveOut), new(big.Float).SetInt(reserveIn)) priceAfter := new(big.Float).Quo(new(big.Float).SetInt(newReserveOut), new(big.Float).SetInt(newReserveIn)) // Calculate price impact impact := new(big.Float).Sub(priceBefore, priceAfter) impact.Quo(impact, priceBefore) impactFloat, _ := impact.Float64() return math.Abs(impactFloat), nil } // calculateIntegralPriceImpact calculates price impact for Integral func (pic *PriceImpactCalculator) calculateIntegralPriceImpact(amountIn, reserveIn, reserveOut *big.Int) (float64, error) { if amountIn.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } // Calculate new reserves after swap amountOut, err := NewIntegralMath().CalculateAmountOutIntegral(amountIn, reserveIn, reserveOut, 100) if err != nil { return 0, err } newReserveIn := new(big.Int).Add(reserveIn, amountIn) newReserveOut := new(big.Int).Sub(reserveOut, amountOut) // Calculate price before and after swap priceBefore := new(big.Float).Quo(new(big.Float).SetInt(reserveOut), new(big.Float).SetInt(reserveIn)) priceAfter := new(big.Float).Quo(new(big.Float).SetInt(newReserveOut), new(big.Float).SetInt(newReserveIn)) // Calculate price impact impact := new(big.Float).Sub(priceBefore, priceAfter) impact.Quo(impact, priceBefore) impactFloat, _ := impact.Float64() return math.Abs(impactFloat), nil } // calculateOneInchPriceImpact calculates price impact for 1Inch aggregation func (pic *PriceImpactCalculator) calculateOneInchPriceImpact(amountIn, reserveIn, reserveOut *big.Int) (float64, error) { if amountIn.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } // 1Inch aggregates multiple DEXs, so we'll calculate an effective price impact // based on the overall route // For this implementation, we'll calculate using a simple weighted average // of the price impact across different paths // Calculate new reserves after swap (simplified) amountOut, err := NewOneInchMath().CalculateAmountOutOneInch(amountIn, []PathElement{ { Protocol: "uniswap_v2", ReserveIn: reserveIn, ReserveOut: reserveOut, Fee: 3000, }, }) if err != nil { return 0, err } newReserveIn := new(big.Int).Add(reserveIn, amountIn) newReserveOut := new(big.Int).Sub(reserveOut, amountOut) // Calculate price before and after swap priceBefore := new(big.Float).Quo(new(big.Float).SetInt(reserveOut), new(big.Float).SetInt(reserveIn)) priceAfter := new(big.Float).Quo(new(big.Float).SetInt(newReserveOut), new(big.Float).SetInt(newReserveIn)) // Calculate price impact impact := new(big.Float).Sub(priceBefore, priceAfter) impact.Quo(impact, priceBefore) impactFloat, _ := impact.Float64() return math.Abs(impactFloat), nil } // CalculatePriceMovementThreshold determines if a swap moves price beyond a certain threshold func (pic *PriceImpactCalculator) CalculatePriceMovementThreshold( protocol string, amountIn, reserveIn, reserveOut *big.Int, sqrtPriceX96, liquidity *big.Int, // For Uniswap V3 and Kyber threshold float64, ) (bool, float64, error) { impact, err := pic.CalculatePriceImpact(protocol, amountIn, reserveIn, reserveOut, sqrtPriceX96, liquidity) if err != nil { return false, 0, err } movesPrice := impact >= threshold return movesPrice, impact, nil } // CalculatePriceImpactWithSlippage combines price impact and slippage calculations func (pic *PriceImpactCalculator) CalculatePriceImpactWithSlippage( protocol string, amountIn, reserveIn, reserveOut *big.Int, sqrtPriceX96, liquidity *big.Int, // For Uniswap V3 and Kyber ) (float64, float64, error) { // Calculate price impact priceImpact, err := pic.CalculatePriceImpact(protocol, amountIn, reserveIn, reserveOut, sqrtPriceX96, liquidity) if err != nil { return 0, 0, err } // Calculate expected output mathCalculator := pic.mathCalculator.GetMathForExchange(protocol) expectedOut, err := mathCalculator.CalculateAmountOut(amountIn, reserveIn, reserveOut, 0) if err != nil { return 0, 0, err } // Calculate actual output after slippage (simplified) actualOut := new(big.Int).Set(expectedOut) // Calculate slippage slippage, err := mathCalculator.CalculateSlippage(expectedOut, actualOut) if err != nil { return 0, 0, err } return priceImpact, slippage, nil }