Files
mev-beta/orig/pkg/marketmanager/arbitrage.go
Administrator 803de231ba feat: create v2-prep branch with comprehensive planning
Restructured project for V2 refactor:

**Structure Changes:**
- Moved all V1 code to orig/ folder (preserved with git mv)
- Created docs/planning/ directory
- Added orig/README_V1.md explaining V1 preservation

**Planning Documents:**
- 00_V2_MASTER_PLAN.md: Complete architecture overview
  - Executive summary of critical V1 issues
  - High-level component architecture diagrams
  - 5-phase implementation roadmap
  - Success metrics and risk mitigation

- 07_TASK_BREAKDOWN.md: Atomic task breakdown
  - 99+ hours of detailed tasks
  - Every task < 2 hours (atomic)
  - Clear dependencies and success criteria
  - Organized by implementation phase

**V2 Key Improvements:**
- Per-exchange parsers (factory pattern)
- Multi-layer strict validation
- Multi-index pool cache
- Background validation pipeline
- Comprehensive observability

**Critical Issues Addressed:**
- Zero address tokens (strict validation + cache enrichment)
- Parsing accuracy (protocol-specific parsers)
- No audit trail (background validation channel)
- Inefficient lookups (multi-index cache)
- Stats disconnection (event-driven metrics)

Next Steps:
1. Review planning documents
2. Begin Phase 1: Foundation (P1-001 through P1-010)
3. Implement parsers in Phase 2
4. Build cache system in Phase 3
5. Add validation pipeline in Phase 4
6. Migrate and test in Phase 5

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 10:14:26 +01:00

216 lines
6.9 KiB
Go

package marketmanager
import (
"math/big"
"sort"
"time"
"github.com/fraktal/mev-beta/pkg/types"
)
// 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,
}
}
// MarketOpportunity represents market-specific arbitrage data (extends canonical ArbitrageOpportunity)
type MarketOpportunity struct {
*types.ArbitrageOpportunity
Market1 *Market
Market2 *Market
}
// DetectArbitrageOpportunities detects arbitrage opportunities among markets with the same rawTicker
func (ad *ArbitrageDetector) DetectArbitrageOpportunities(markets map[string]*Market) []*types.ArbitrageOpportunity {
var opportunities []*types.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) *types.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 canonical arbitrage opportunity
return &types.ArbitrageOpportunity{
Path: []string{market1.Token0.Hex(), market1.Token1.Hex()},
Pools: []string{"market1-pool", "market2-pool"},
AmountIn: optimalTradeSize,
Profit: netProfit,
NetProfit: netProfit,
GasEstimate: gasCost,
ROI: roi,
Protocol: "market-arbitrage",
ExecutionTime: 5000, // 5 seconds
Confidence: 0.9, // High confidence for market data
PriceImpact: 0.01, // 1% estimated
MaxSlippage: 0.02, // 2% max slippage
TokenIn: market1.Token0,
TokenOut: market1.Token1,
Timestamp: time.Now().Unix(),
Risk: 0.1, // Low risk for market arbitrage
}
}
// 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
}