Files
mev-beta/pkg/marketmanager/arbitrage.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

208 lines
6.6 KiB
Go

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
}