Files
mev-beta/pkg/profitcalc/slippage_protection.go
Krypto Kajun fac8a64092 feat: Implement comprehensive Market Manager with database and logging
- Add complete Market Manager package with in-memory storage and CRUD operations
- Implement arbitrage detection with profit calculations and thresholds
- Add database adapter with PostgreSQL schema for persistence
- Create comprehensive logging system with specialized log files
- Add detailed documentation and implementation plans
- Include example application and comprehensive test suite
- Update Makefile with market manager build targets
- Add check-implementations command for verification
2025-09-18 03:52:33 -05:00

286 lines
9.2 KiB
Go

package profitcalc
import (
"fmt"
"math/big"
"github.com/fraktal/mev-beta/internal/logger"
)
// SlippageProtector provides slippage calculation and protection for arbitrage trades
type SlippageProtector struct {
logger *logger.Logger
maxSlippageBps int64 // Maximum allowed slippage in basis points
liquidityBuffer float64 // Buffer factor for liquidity calculations
}
// SlippageAnalysis contains detailed slippage analysis for a trade
type SlippageAnalysis struct {
TradeAmount *big.Float // Amount being traded
PoolLiquidity *big.Float // Available liquidity in pool
EstimatedSlippage float64 // Estimated slippage percentage
SlippageBps int64 // Slippage in basis points
PriceImpact float64 // Price impact percentage
EffectivePrice *big.Float // Price after slippage
MinAmountOut *big.Float // Minimum amount out with slippage protection
IsAcceptable bool // Whether slippage is within acceptable limits
RiskLevel string // "Low", "Medium", "High", "Extreme"
Recommendation string // Trading recommendation
}
// NewSlippageProtector creates a new slippage protection manager
func NewSlippageProtector(logger *logger.Logger) *SlippageProtector {
return &SlippageProtector{
logger: logger,
maxSlippageBps: 500, // 5% maximum slippage
liquidityBuffer: 0.8, // Use 80% of available liquidity for calculations
}
}
// AnalyzeSlippage performs comprehensive slippage analysis for a potential trade
func (sp *SlippageProtector) AnalyzeSlippage(
tradeAmount *big.Float,
poolLiquidity *big.Float,
currentPrice *big.Float,
) *SlippageAnalysis {
if tradeAmount == nil || poolLiquidity == nil || currentPrice == nil {
return &SlippageAnalysis{
IsAcceptable: false,
RiskLevel: "Extreme",
Recommendation: "Insufficient data for slippage calculation",
}
}
// Calculate trade size as percentage of pool liquidity
tradeSizeRatio := new(big.Float).Quo(tradeAmount, poolLiquidity)
tradeSizeFloat, _ := tradeSizeRatio.Float64()
// Estimate slippage using simplified AMM formula
// For constant product AMMs: slippage ≈ trade_size / (2 * liquidity)
estimatedSlippage := tradeSizeFloat / 2.0
// Apply curve adjustment for larger trades (non-linear slippage)
if tradeSizeFloat > 0.1 { // > 10% of pool
// Quadratic increase for large trades
estimatedSlippage = estimatedSlippage * (1 + tradeSizeFloat)
}
slippageBps := int64(estimatedSlippage * 10000)
// Calculate price impact (similar to slippage but different calculation)
priceImpact := sp.calculatePriceImpact(tradeSizeFloat)
// Calculate effective price after slippage
slippageFactor := 1.0 - estimatedSlippage
effectivePrice := new(big.Float).Mul(currentPrice, big.NewFloat(slippageFactor))
// Calculate minimum amount out with slippage protection
minAmountOut := new(big.Float).Quo(tradeAmount, effectivePrice)
// Determine risk level and acceptability
riskLevel, isAcceptable := sp.assessRiskLevel(slippageBps, tradeSizeFloat)
recommendation := sp.generateRecommendation(slippageBps, tradeSizeFloat, riskLevel)
analysis := &SlippageAnalysis{
TradeAmount: tradeAmount,
PoolLiquidity: poolLiquidity,
EstimatedSlippage: estimatedSlippage,
SlippageBps: slippageBps,
PriceImpact: priceImpact,
EffectivePrice: effectivePrice,
MinAmountOut: minAmountOut,
IsAcceptable: isAcceptable,
RiskLevel: riskLevel,
Recommendation: recommendation,
}
sp.logger.Debug(fmt.Sprintf("Slippage analysis: Trade=%s, Liquidity=%s, Slippage=%.2f%%, Risk=%s",
tradeAmount.String(), poolLiquidity.String(), estimatedSlippage*100, riskLevel))
return analysis
}
// calculatePriceImpact calculates price impact using AMM mechanics
func (sp *SlippageProtector) calculatePriceImpact(tradeSizeRatio float64) float64 {
// For constant product AMMs (like Uniswap V2):
// Price impact = trade_size / (1 + trade_size)
priceImpact := tradeSizeRatio / (1.0 + tradeSizeRatio)
// Cap at 100%
if priceImpact > 1.0 {
priceImpact = 1.0
}
return priceImpact
}
// assessRiskLevel determines risk level based on slippage and trade size
func (sp *SlippageProtector) assessRiskLevel(slippageBps int64, tradeSizeRatio float64) (string, bool) {
isAcceptable := slippageBps <= sp.maxSlippageBps
var riskLevel string
switch {
case slippageBps <= 50: // <= 0.5%
riskLevel = "Low"
case slippageBps <= 200: // <= 2%
riskLevel = "Medium"
case slippageBps <= 500: // <= 5%
riskLevel = "High"
default:
riskLevel = "Extreme"
isAcceptable = false
}
// Additional checks for trade size
if tradeSizeRatio > 0.5 { // > 50% of pool
riskLevel = "Extreme"
isAcceptable = false
} else if tradeSizeRatio > 0.2 { // > 20% of pool
if riskLevel == "Low" {
riskLevel = "Medium"
} else if riskLevel == "Medium" {
riskLevel = "High"
}
}
return riskLevel, isAcceptable
}
// generateRecommendation provides trading recommendations based on analysis
func (sp *SlippageProtector) generateRecommendation(slippageBps int64, tradeSizeRatio float64, riskLevel string) string {
switch riskLevel {
case "Low":
return "Safe to execute - low slippage expected"
case "Medium":
if tradeSizeRatio > 0.1 {
return "Consider splitting trade into smaller parts"
}
return "Proceed with caution - moderate slippage expected"
case "High":
return "High slippage risk - consider reducing trade size or finding alternative routes"
case "Extreme":
if tradeSizeRatio > 0.5 {
return "Trade too large for pool - split into multiple smaller trades"
}
return "Excessive slippage - avoid this trade"
default:
return "Unable to assess - insufficient data"
}
}
// CalculateOptimalTradeSize calculates optimal trade size to stay within slippage limits
func (sp *SlippageProtector) CalculateOptimalTradeSize(
poolLiquidity *big.Float,
maxSlippageBps int64,
) *big.Float {
if poolLiquidity == nil || poolLiquidity.Sign() <= 0 {
return big.NewFloat(0)
}
// Convert max slippage to ratio
maxSlippageRatio := float64(maxSlippageBps) / 10000.0
// For simplified AMM: optimal_trade_size = 2 * liquidity * max_slippage
optimalRatio := 2.0 * maxSlippageRatio
// Apply safety factor
safetyFactor := 0.8 // Use 80% of optimal to be conservative
optimalRatio *= safetyFactor
optimalSize := new(big.Float).Mul(poolLiquidity, big.NewFloat(optimalRatio))
sp.logger.Debug(fmt.Sprintf("Calculated optimal trade size: %s (%.2f%% of pool) for max slippage %d bps",
optimalSize.String(), optimalRatio*100, maxSlippageBps))
return optimalSize
}
// EstimateGasForSlippage estimates additional gas needed for slippage protection
func (sp *SlippageProtector) EstimateGasForSlippage(analysis *SlippageAnalysis) uint64 {
baseGas := uint64(0)
// Higher slippage might require more complex routing
switch analysis.RiskLevel {
case "Low":
baseGas = 0 // No additional gas
case "Medium":
baseGas = 20000 // Additional gas for price checks
case "High":
baseGas = 50000 // Additional gas for complex routing
case "Extreme":
baseGas = 100000 // Maximum additional gas for emergency handling
}
return baseGas
}
// SetMaxSlippage updates the maximum allowed slippage
func (sp *SlippageProtector) SetMaxSlippage(bps int64) {
sp.maxSlippageBps = bps
sp.logger.Info(fmt.Sprintf("Updated maximum slippage to %d bps (%.2f%%)", bps, float64(bps)/100))
}
// GetMaxSlippage returns the current maximum slippage setting
func (sp *SlippageProtector) GetMaxSlippage() int64 {
return sp.maxSlippageBps
}
// ValidateTradeParameters performs comprehensive validation of trade parameters
func (sp *SlippageProtector) ValidateTradeParameters(
tradeAmount *big.Float,
poolLiquidity *big.Float,
minLiquidity *big.Float,
) error {
if tradeAmount == nil || tradeAmount.Sign() <= 0 {
return fmt.Errorf("invalid trade amount: must be positive")
}
if poolLiquidity == nil || poolLiquidity.Sign() <= 0 {
return fmt.Errorf("invalid pool liquidity: must be positive")
}
if minLiquidity != nil && poolLiquidity.Cmp(minLiquidity) < 0 {
return fmt.Errorf("insufficient pool liquidity: %s < %s required",
poolLiquidity.String(), minLiquidity.String())
}
// Check if trade is reasonable relative to pool size
tradeSizeRatio := new(big.Float).Quo(tradeAmount, poolLiquidity)
tradeSizeFloat, _ := tradeSizeRatio.Float64()
if tradeSizeFloat > 0.9 { // > 90% of pool
return fmt.Errorf("trade size too large: %.1f%% of pool liquidity", tradeSizeFloat*100)
}
return nil
}
// CalculateSlippageAdjustedProfit adjusts profit calculations for slippage
func (sp *SlippageProtector) CalculateSlippageAdjustedProfit(
grossProfit *big.Float,
analysis *SlippageAnalysis,
) *big.Float {
if grossProfit == nil || analysis == nil {
return big.NewFloat(0)
}
// Reduce profit by estimated slippage impact
slippageImpact := big.NewFloat(analysis.EstimatedSlippage)
slippageReduction := new(big.Float).Mul(grossProfit, slippageImpact)
adjustedProfit := new(big.Float).Sub(grossProfit, slippageReduction)
// Ensure profit doesn't go negative due to slippage
if adjustedProfit.Sign() < 0 {
adjustedProfit = big.NewFloat(0)
}
sp.logger.Debug(fmt.Sprintf("Slippage-adjusted profit: %s -> %s (reduction: %s)",
grossProfit.String(), adjustedProfit.String(), slippageReduction.String()))
return adjustedProfit
}