package marketmanager import ( "math/big" "sort" ) // ArbitrageDetector detects arbitrage opportunities between markets type ArbitrageDetector struct { minProfitThreshold *big.Int // Minimum profit threshold in wei minROIPercentage float64 // Minimum ROI percentage } // NewArbitrageDetector creates a new arbitrage detector func NewArbitrageDetector(minProfitThreshold *big.Int, minROIPercentage float64) *ArbitrageDetector { return &ArbitrageDetector{ minProfitThreshold: minProfitThreshold, minROIPercentage: minROIPercentage, } } // ArbitrageOpportunity represents a detected arbitrage opportunity type ArbitrageOpportunity struct { Market1 *Market Market2 *Market Path []string // Token path Profit *big.Int // Estimated profit in wei GasEstimate *big.Int // Estimated gas cost in wei ROI float64 // Return on investment percentage InputAmount *big.Int // Required input amount } // DetectArbitrageOpportunities detects arbitrage opportunities among markets with the same rawTicker func (ad *ArbitrageDetector) DetectArbitrageOpportunities(markets map[string]*Market) []*ArbitrageOpportunity { var opportunities []*ArbitrageOpportunity // Convert map to slice for sorting marketList := make([]*Market, 0, len(markets)) for _, market := range markets { if market.IsValid() { marketList = append(marketList, market) } } // Sort markets by price (lowest to highest) sort.Slice(marketList, func(i, j int) bool { return marketList[i].Price.Cmp(marketList[j].Price) < 0 }) // Check each combination for arbitrage opportunities for i := 0; i < len(marketList); i++ { for j := i + 1; j < len(marketList); j++ { market1 := marketList[i] market2 := marketList[j] // Check if there's an arbitrage opportunity opportunity := ad.checkArbitrageOpportunity(market1, market2) if opportunity != nil { opportunities = append(opportunities, opportunity) } } } return opportunities } // checkArbitrageOpportunity checks if there's an arbitrage opportunity between two markets func (ad *ArbitrageDetector) checkArbitrageOpportunity(market1, market2 *Market) *ArbitrageOpportunity { // Calculate price difference priceDiff := new(big.Float).Sub(market2.Price, market1.Price) if priceDiff.Sign() <= 0 { return nil // No profit opportunity } // Calculate relative price difference (profit margin) relativeDiff := new(big.Float).Quo(priceDiff, market1.Price) // Estimate optimal trade size based on liquidity optimalTradeSize := ad.calculateOptimalTradeSize(market1, market2) // Calculate price impact on both markets impact1 := ad.calculatePriceImpact(optimalTradeSize, market1) impact2 := ad.calculatePriceImpact(optimalTradeSize, market2) // Adjusted profit after price impact adjustedRelativeDiff := new(big.Float).Sub(relativeDiff, new(big.Float).Add(impact1, impact2)) if adjustedRelativeDiff.Sign() <= 0 { return nil // No profit after price impact } // Calculate gross profit grossProfit := new(big.Float).Mul(new(big.Float).SetInt(optimalTradeSize), adjustedRelativeDiff) // Convert to wei for integer calculations grossProfitWei := new(big.Int) grossProfit.Int(grossProfitWei) // Estimate gas costs gasCost := ad.estimateGasCost(market1, market2) // Calculate net profit netProfit := new(big.Int).Sub(grossProfitWei, gasCost) // Check if profit meets minimum threshold if netProfit.Cmp(ad.minProfitThreshold) < 0 { return nil // Profit too low } // Calculate ROI var roi float64 if optimalTradeSize.Sign() > 0 { roiFloat := new(big.Float).Quo(new(big.Float).SetInt(netProfit), new(big.Float).SetInt(optimalTradeSize)) roi, _ = roiFloat.Float64() roi *= 100 // Convert to percentage } // Check if ROI meets minimum threshold if roi < ad.minROIPercentage { return nil // ROI too low } // Create arbitrage opportunity return &ArbitrageOpportunity{ Market1: market1, Market2: market2, Path: []string{market1.Token0.Hex(), market1.Token1.Hex()}, Profit: netProfit, GasEstimate: gasCost, ROI: roi, InputAmount: optimalTradeSize, } } // calculateOptimalTradeSize calculates the optimal trade size for maximum profit func (ad *ArbitrageDetector) calculateOptimalTradeSize(market1, market2 *Market) *big.Int { // Use a simple approach: 1% of the smaller liquidity liquidity1 := market1.Liquidity liquidity2 := market2.Liquidity minLiquidity := liquidity1 if liquidity2.Cmp(liquidity1) < 0 { minLiquidity = liquidity2 } // Calculate 1% of minimum liquidity optimalSize := new(big.Int).Div(minLiquidity, big.NewInt(100)) // Ensure minimum trade size (0.001 ETH) minTradeSize := big.NewInt(1000000000000000) // 0.001 ETH in wei if optimalSize.Cmp(minTradeSize) < 0 { optimalSize = minTradeSize } // Ensure maximum trade size (10 ETH to avoid overflow) maxTradeSize := new(big.Int).SetInt64(1000000000000000000) // 1 ETH in wei maxTradeSize.Mul(maxTradeSize, big.NewInt(10)) // 10 ETH if optimalSize.Cmp(maxTradeSize) > 0 { optimalSize = maxTradeSize } return optimalSize } // calculatePriceImpact calculates the price impact for a given trade size func (ad *ArbitrageDetector) calculatePriceImpact(tradeSize *big.Int, market *Market) *big.Float { if market.Liquidity.Sign() == 0 { return big.NewFloat(0.01) // 1% default impact for unknown liquidity } // Calculate utilization ratio utilizationRatio := new(big.Float).Quo(new(big.Float).SetInt(tradeSize), new(big.Float).SetInt(market.Liquidity)) // Apply quadratic model for impact: utilizationRatio * (1 + utilizationRatio) impact := new(big.Float).Mul(utilizationRatio, new(big.Float).Add(big.NewFloat(1), utilizationRatio)) // Cap impact at 10% if impact.Cmp(big.NewFloat(0.1)) > 0 { impact = big.NewFloat(0.1) } return impact } // estimateGasCost estimates the gas cost for an arbitrage transaction func (ad *ArbitrageDetector) estimateGasCost(market1, market2 *Market) *big.Int { // Base gas costs for different operations baseGas := big.NewInt(250000) // Base gas for arbitrage transaction // Get current gas price (simplified - in production would fetch from network) gasPrice := big.NewInt(2000000000) // 2 gwei base // Add priority fee for MEV transactions priorityFee := big.NewInt(5000000000) // 5 gwei priority totalGasPrice := new(big.Int).Add(gasPrice, priorityFee) // Calculate total gas cost gasCost := new(big.Int).Mul(baseGas, totalGasPrice) return gasCost } // GetFeePercentage calculates the total fee percentage for two markets func (ad *ArbitrageDetector) GetFeePercentage(market1, market2 *Market) float64 { fee1 := market1.GetFeePercentage() fee2 := market2.GetFeePercentage() return fee1 + fee2 }