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>
606 lines
20 KiB
Go
606 lines
20 KiB
Go
package arbitrum
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math/big"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/fraktal/mev-beta/internal/logger"
|
|
"github.com/fraktal/mev-beta/pkg/types"
|
|
)
|
|
|
|
// MEVStrategyEngine implements profitable MEV strategies
|
|
type MEVStrategyEngine struct {
|
|
logger *logger.Logger
|
|
protocolRegistry *ArbitrumProtocolRegistry
|
|
profitCalculator *RealTimeProfitCalculator
|
|
riskManager *RiskManager
|
|
|
|
// Strategy configuration
|
|
minProfitUSD float64 // Minimum profit in USD
|
|
maxGasPrice *big.Int // Maximum gas price willing to pay
|
|
maxSlippage float64 // Maximum slippage tolerance
|
|
|
|
// Performance tracking
|
|
successfulTrades uint64
|
|
totalProfit *big.Int
|
|
totalGasCost *big.Int
|
|
}
|
|
|
|
// RealTimeProfitCalculator provides real-time profit calculations
|
|
type RealTimeProfitCalculator struct {
|
|
// Token prices (token address -> price in USD)
|
|
tokenPrices map[common.Address]*TokenPrice
|
|
|
|
// Gas pricing
|
|
currentGasPrice *big.Int
|
|
|
|
// Exchange rates and fees
|
|
exchangeFees map[string]float64 // protocol -> fee percentage
|
|
|
|
// Liquidity data
|
|
poolLiquidity map[common.Address]*PoolLiquidity
|
|
}
|
|
|
|
// RiskManager manages risk parameters for MEV strategies
|
|
type RiskManager struct {
|
|
maxPositionSize *big.Int // Maximum position size in wei
|
|
maxDailyLoss *big.Int // Maximum daily loss
|
|
maxConcurrentTxs int // Maximum concurrent transactions
|
|
|
|
// Current risk metrics
|
|
dailyLoss *big.Int
|
|
activeTxs int
|
|
lastResetTime time.Time
|
|
}
|
|
|
|
// TokenPrice represents real-time token pricing data
|
|
type TokenPrice struct {
|
|
Address common.Address
|
|
PriceUSD float64
|
|
LastUpdated time.Time
|
|
Confidence float64 // Price confidence 0-1
|
|
Volume24h float64
|
|
Volatility float64
|
|
}
|
|
|
|
// PoolLiquidity represents pool liquidity information
|
|
type PoolLiquidity struct {
|
|
Pool common.Address
|
|
Token0 common.Address
|
|
Token1 common.Address
|
|
Reserve0 *big.Int
|
|
Reserve1 *big.Int
|
|
TotalLiquidity *big.Int
|
|
Fee float64
|
|
LastUpdated time.Time
|
|
}
|
|
|
|
// ProfitableStrategy represents a profitable MEV strategy
|
|
type ProfitableStrategy struct {
|
|
Type string // "arbitrage", "sandwich", "liquidation"
|
|
Priority int // Higher = more urgent
|
|
ExpectedProfit *big.Int // Expected profit in wei
|
|
GasCost *big.Int // Estimated gas cost
|
|
NetProfit *big.Int // Net profit after gas
|
|
ProfitMarginPct float64 // Profit margin percentage
|
|
RiskScore float64 // Risk score 0-1 (higher = riskier)
|
|
Confidence float64 // Confidence in profit estimate 0-1
|
|
ExecutionTime time.Duration // Estimated execution time
|
|
Parameters map[string]interface{} // Strategy-specific parameters
|
|
}
|
|
|
|
// ArbitrageParams holds arbitrage-specific parameters
|
|
type ArbitrageParams struct {
|
|
TokenIn common.Address `json:"token_in"`
|
|
TokenOut common.Address `json:"token_out"`
|
|
AmountIn *big.Int `json:"amount_in"`
|
|
Path []common.Address `json:"path"`
|
|
Exchanges []string `json:"exchanges"`
|
|
PriceDiff float64 `json:"price_diff"`
|
|
Slippage float64 `json:"slippage"`
|
|
}
|
|
|
|
// SandwichParams holds sandwich attack parameters
|
|
type SandwichParams struct {
|
|
TargetTx string `json:"target_tx"`
|
|
TokenIn common.Address `json:"token_in"`
|
|
TokenOut common.Address `json:"token_out"`
|
|
FrontrunAmount *big.Int `json:"frontrun_amount"`
|
|
BackrunAmount *big.Int `json:"backrun_amount"`
|
|
Pool common.Address `json:"pool"`
|
|
MaxSlippage float64 `json:"max_slippage"`
|
|
}
|
|
|
|
// LiquidationParams holds liquidation-specific parameters
|
|
type LiquidationParams struct {
|
|
Protocol string `json:"protocol"`
|
|
Borrower common.Address `json:"borrower"`
|
|
CollateralToken common.Address `json:"collateral_token"`
|
|
DebtToken common.Address `json:"debt_token"`
|
|
MaxLiquidation *big.Int `json:"max_liquidation"`
|
|
HealthFactor float64 `json:"health_factor"`
|
|
LiquidationBonus float64 `json:"liquidation_bonus"`
|
|
}
|
|
|
|
// NewMEVStrategyEngine creates a new MEV strategy engine
|
|
func NewMEVStrategyEngine(logger *logger.Logger, protocolRegistry *ArbitrumProtocolRegistry) *MEVStrategyEngine {
|
|
return &MEVStrategyEngine{
|
|
logger: logger,
|
|
protocolRegistry: protocolRegistry,
|
|
minProfitUSD: 50.0, // $50 minimum profit
|
|
maxGasPrice: big.NewInt(20000000000), // 20 gwei max
|
|
maxSlippage: 0.005, // 0.5% max slippage
|
|
totalProfit: big.NewInt(0),
|
|
totalGasCost: big.NewInt(0),
|
|
profitCalculator: NewRealTimeProfitCalculator(),
|
|
riskManager: NewRiskManager(),
|
|
}
|
|
}
|
|
|
|
// NewRealTimeProfitCalculator creates a new profit calculator
|
|
func NewRealTimeProfitCalculator() *RealTimeProfitCalculator {
|
|
return &RealTimeProfitCalculator{
|
|
tokenPrices: make(map[common.Address]*TokenPrice),
|
|
exchangeFees: make(map[string]float64),
|
|
poolLiquidity: make(map[common.Address]*PoolLiquidity),
|
|
currentGasPrice: big.NewInt(5000000000), // 5 gwei default
|
|
}
|
|
}
|
|
|
|
// NewRiskManager creates a new risk manager
|
|
func NewRiskManager() *RiskManager {
|
|
return &RiskManager{
|
|
maxPositionSize: big.NewInt(1000000000000000000), // 1 ETH max position
|
|
maxDailyLoss: big.NewInt(100000000000000000), // 0.1 ETH max daily loss
|
|
maxConcurrentTxs: 5,
|
|
dailyLoss: big.NewInt(0),
|
|
activeTxs: 0,
|
|
lastResetTime: time.Now(),
|
|
}
|
|
}
|
|
|
|
// AnalyzeArbitrageOpportunity analyzes potential arbitrage opportunities
|
|
func (engine *MEVStrategyEngine) AnalyzeArbitrageOpportunity(ctx context.Context, swapEvent interface{}) (interface{}, error) {
|
|
// Type assert the swapEvent to *SwapEvent
|
|
swap, ok := swapEvent.(*SwapEvent)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid swap event type")
|
|
}
|
|
|
|
// Parse token addresses
|
|
tokenIn := common.HexToAddress(swap.TokenIn)
|
|
tokenOut := common.HexToAddress(swap.TokenOut)
|
|
|
|
// Get current prices across exchanges
|
|
prices, err := engine.profitCalculator.GetCrossExchangePrices(ctx, tokenIn, tokenOut)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get cross-exchange prices: %w", err)
|
|
}
|
|
|
|
// Find best arbitrage path
|
|
bestArb := engine.findBestArbitragePath(prices, tokenIn, tokenOut)
|
|
if bestArb == nil {
|
|
return nil, nil // No profitable arbitrage
|
|
}
|
|
|
|
// Calculate gas costs
|
|
gasCost := engine.calculateArbitrageGasCost(bestArb)
|
|
|
|
// Calculate net profit
|
|
netProfit := new(big.Int).Sub(bestArb.Profit, gasCost)
|
|
|
|
// Convert to USD for minimum profit check
|
|
profitUSD := engine.profitCalculator.WeiToUSD(netProfit, common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")) // WETH
|
|
|
|
if profitUSD < engine.minProfitUSD {
|
|
return nil, nil // Below minimum profit threshold
|
|
}
|
|
|
|
// Check risk parameters
|
|
riskScore := engine.riskManager.CalculateRiskScore(bestArb)
|
|
if riskScore > 0.7 { // Risk too high
|
|
return nil, nil
|
|
}
|
|
|
|
return &ProfitableStrategy{
|
|
Type: "arbitrage",
|
|
Priority: engine.calculatePriority(profitUSD, riskScore),
|
|
ExpectedProfit: bestArb.Profit,
|
|
GasCost: gasCost,
|
|
NetProfit: netProfit,
|
|
ProfitMarginPct: (float64(netProfit.Uint64()) / float64(bestArb.AmountIn.Uint64())) * 100,
|
|
RiskScore: riskScore,
|
|
Confidence: bestArb.Confidence,
|
|
ExecutionTime: time.Duration(15) * time.Second, // Estimated execution time
|
|
Parameters: map[string]interface{}{
|
|
"arbitrage": &ArbitrageParams{
|
|
TokenIn: tokenIn,
|
|
TokenOut: tokenOut,
|
|
AmountIn: bestArb.AmountIn,
|
|
Path: []common.Address{bestArb.TokenIn, bestArb.TokenOut},
|
|
Exchanges: bestArb.Pools, // Use pools as exchanges
|
|
PriceDiff: engine.calculatePriceDifference(prices),
|
|
Slippage: engine.maxSlippage,
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// AnalyzeSandwichOpportunity analyzes potential sandwich attack opportunities
|
|
func (engine *MEVStrategyEngine) AnalyzeSandwichOpportunity(ctx context.Context, targetTx *SwapEvent) (*ProfitableStrategy, error) {
|
|
// Only analyze large transactions that can be sandwiched profitably
|
|
amountIn, ok := new(big.Int).SetString(targetTx.AmountIn, 10)
|
|
if !ok || amountIn.Cmp(big.NewInt(100000000000000000)) < 0 { // < 0.1 ETH
|
|
return nil, nil
|
|
}
|
|
|
|
// Check if transaction has sufficient slippage tolerance
|
|
if targetTx.PriceImpact < 0.01 { // < 1% price impact
|
|
return nil, nil
|
|
}
|
|
|
|
tokenIn := common.HexToAddress(targetTx.TokenIn)
|
|
tokenOut := common.HexToAddress(targetTx.TokenOut)
|
|
|
|
// Calculate optimal sandwich amounts
|
|
frontrunAmount, backrunAmount := engine.calculateOptimalSandwichAmounts(amountIn, targetTx.PriceImpact)
|
|
|
|
// Estimate profit from price manipulation
|
|
expectedProfit := engine.calculateSandwichProfit(frontrunAmount, backrunAmount, targetTx.PriceImpact)
|
|
|
|
// Calculate gas costs (frontrun + backrun + priority fees)
|
|
gasCost := engine.calculateSandwichGasCost()
|
|
|
|
// Calculate net profit
|
|
netProfit := new(big.Int).Sub(expectedProfit, gasCost)
|
|
|
|
// Convert to USD
|
|
profitUSD := engine.profitCalculator.WeiToUSD(netProfit, tokenOut)
|
|
|
|
if profitUSD < engine.minProfitUSD {
|
|
return nil, nil
|
|
}
|
|
|
|
// Calculate risk (sandwich attacks are inherently risky)
|
|
riskScore := 0.6 + (targetTx.PriceImpact * 0.3) // Base risk + impact risk
|
|
|
|
return &ProfitableStrategy{
|
|
Type: "sandwich",
|
|
Priority: engine.calculatePriority(profitUSD, riskScore),
|
|
ExpectedProfit: expectedProfit,
|
|
GasCost: gasCost,
|
|
NetProfit: netProfit,
|
|
ProfitMarginPct: (float64(netProfit.Uint64()) / float64(amountIn.Uint64())) * 100,
|
|
RiskScore: riskScore,
|
|
Confidence: 0.7, // Moderate confidence due to MEV competition
|
|
ExecutionTime: time.Duration(3) * time.Second, // Fast execution required
|
|
Parameters: map[string]interface{}{
|
|
"sandwich": &SandwichParams{
|
|
TargetTx: targetTx.TxHash,
|
|
TokenIn: tokenIn,
|
|
TokenOut: tokenOut,
|
|
FrontrunAmount: frontrunAmount,
|
|
BackrunAmount: backrunAmount,
|
|
Pool: common.HexToAddress(targetTx.Pool),
|
|
MaxSlippage: engine.maxSlippage,
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// AnalyzeLiquidationOpportunity analyzes potential liquidation opportunities
|
|
func (engine *MEVStrategyEngine) AnalyzeLiquidationOpportunity(ctx context.Context, liquidationEvent *LiquidationEvent) (*ProfitableStrategy, error) {
|
|
// Only analyze under-collateralized positions
|
|
if liquidationEvent.HealthFactor >= 1.0 {
|
|
return nil, nil
|
|
}
|
|
|
|
// Calculate liquidation profitability
|
|
collateralAmount, ok := new(big.Int).SetString(liquidationEvent.CollateralAmount, 10)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid collateral amount")
|
|
}
|
|
|
|
debtAmount, ok := new(big.Int).SetString(liquidationEvent.DebtAmount, 10)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid debt amount")
|
|
}
|
|
|
|
// Calculate liquidation bonus (usually 5-15%)
|
|
bonusAmount, ok := new(big.Int).SetString(liquidationEvent.Bonus, 10)
|
|
if !ok {
|
|
bonusAmount = new(big.Int).Div(collateralAmount, big.NewInt(20)) // 5% default
|
|
}
|
|
|
|
// Estimate gas costs for liquidation
|
|
gasCost := engine.calculateLiquidationGasCost(liquidationEvent.Protocol)
|
|
|
|
// Calculate net profit (bonus - gas costs)
|
|
netProfit := new(big.Int).Sub(bonusAmount, gasCost)
|
|
|
|
// Convert to USD
|
|
collateralToken := common.HexToAddress(liquidationEvent.CollateralToken)
|
|
profitUSD := engine.profitCalculator.WeiToUSD(netProfit, collateralToken)
|
|
|
|
if profitUSD < engine.minProfitUSD {
|
|
return nil, nil
|
|
}
|
|
|
|
// Calculate risk score (liquidations are generally lower risk)
|
|
riskScore := 0.3 + (1.0-liquidationEvent.HealthFactor)*0.2
|
|
|
|
return &ProfitableStrategy{
|
|
Type: "liquidation",
|
|
Priority: engine.calculatePriority(profitUSD, riskScore),
|
|
ExpectedProfit: bonusAmount,
|
|
GasCost: gasCost,
|
|
NetProfit: netProfit,
|
|
ProfitMarginPct: (float64(netProfit.Uint64()) / float64(debtAmount.Uint64())) * 100,
|
|
RiskScore: riskScore,
|
|
Confidence: 0.9, // High confidence for liquidations
|
|
ExecutionTime: time.Duration(10) * time.Second,
|
|
Parameters: map[string]interface{}{
|
|
"liquidation": &LiquidationParams{
|
|
Protocol: liquidationEvent.Protocol,
|
|
Borrower: common.HexToAddress(liquidationEvent.Borrower),
|
|
CollateralToken: collateralToken,
|
|
DebtToken: common.HexToAddress(liquidationEvent.DebtToken),
|
|
MaxLiquidation: collateralAmount,
|
|
HealthFactor: liquidationEvent.HealthFactor,
|
|
LiquidationBonus: float64(bonusAmount.Uint64()) / float64(collateralAmount.Uint64()),
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// GetCrossExchangePrices gets prices across different exchanges
|
|
func (calc *RealTimeProfitCalculator) GetCrossExchangePrices(ctx context.Context, tokenIn, tokenOut common.Address) (map[string]float64, error) {
|
|
prices := make(map[string]float64)
|
|
|
|
// Get prices from major exchanges
|
|
exchanges := []string{"uniswap_v3", "uniswap_v2", "sushiswap", "camelot_v3", "balancer_v2"}
|
|
|
|
for _, exchange := range exchanges {
|
|
price, err := calc.getTokenPairPrice(ctx, exchange, tokenIn, tokenOut)
|
|
if err != nil {
|
|
continue // Skip if price unavailable
|
|
}
|
|
prices[exchange] = price
|
|
}
|
|
|
|
return prices, nil
|
|
}
|
|
|
|
// Helper methods for calculations
|
|
func (engine *MEVStrategyEngine) findBestArbitragePath(prices map[string]float64, tokenIn, tokenOut common.Address) *types.ArbitrageOpportunity {
|
|
if len(prices) < 2 {
|
|
return nil
|
|
}
|
|
|
|
// Find highest and lowest prices
|
|
var minPrice, maxPrice float64
|
|
var minExchange, maxExchange string
|
|
first := true
|
|
|
|
for exchange, price := range prices {
|
|
if first {
|
|
minPrice = price
|
|
maxPrice = price
|
|
minExchange = exchange
|
|
maxExchange = exchange
|
|
first = false
|
|
continue
|
|
}
|
|
|
|
if price < minPrice {
|
|
minPrice = price
|
|
minExchange = exchange
|
|
}
|
|
if price > maxPrice {
|
|
maxPrice = price
|
|
maxExchange = exchange
|
|
}
|
|
}
|
|
|
|
// Calculate potential profit
|
|
priceDiff := (maxPrice - minPrice) / minPrice
|
|
if priceDiff < 0.005 { // Minimum 0.5% price difference
|
|
return nil
|
|
}
|
|
|
|
// Estimate amounts and profit
|
|
amountIn := big.NewInt(100000000000000000) // 0.1 ETH test amount
|
|
expectedProfit := new(big.Int).Mul(amountIn, big.NewInt(int64(priceDiff*1000)))
|
|
expectedProfit = new(big.Int).Div(expectedProfit, big.NewInt(1000))
|
|
|
|
return &types.ArbitrageOpportunity{
|
|
Path: []string{tokenIn.Hex(), tokenOut.Hex()},
|
|
Pools: []string{minExchange + "-pool", maxExchange + "-pool"},
|
|
AmountIn: amountIn,
|
|
Profit: expectedProfit,
|
|
NetProfit: expectedProfit, // Simplified - gas will be calculated later
|
|
GasEstimate: big.NewInt(200000), // Estimate
|
|
ROI: 0.0, // Will be calculated when gas is known
|
|
Protocol: "multi-exchange",
|
|
ExecutionTime: 4000, // 4 seconds
|
|
Confidence: 0.8,
|
|
PriceImpact: 0.005, // 0.5% estimated
|
|
MaxSlippage: 0.02, // 2% max slippage
|
|
TokenIn: tokenIn,
|
|
TokenOut: tokenOut,
|
|
Timestamp: time.Now().Unix(),
|
|
Risk: 0.3, // Medium risk for cross-exchange arbitrage
|
|
}
|
|
}
|
|
|
|
func (engine *MEVStrategyEngine) calculateOptimalSandwichAmounts(targetAmount *big.Int, priceImpact float64) (*big.Int, *big.Int) {
|
|
// Optimal frontrun is typically 10-30% of target transaction
|
|
frontrunPct := 0.15 + (priceImpact * 0.15) // Scale with price impact
|
|
frontrunAmount := new(big.Int).Mul(targetAmount, big.NewInt(int64(frontrunPct*100)))
|
|
frontrunAmount = new(big.Int).Div(frontrunAmount, big.NewInt(100))
|
|
|
|
// Backrun amount should be similar to frontrun
|
|
backrunAmount := new(big.Int).Set(frontrunAmount)
|
|
|
|
return frontrunAmount, backrunAmount
|
|
}
|
|
|
|
func (engine *MEVStrategyEngine) calculateSandwichProfit(frontrunAmount, backrunAmount *big.Int, priceImpact float64) *big.Int {
|
|
// Simplified calculation: profit = frontrun_amount * (price_impact - fees)
|
|
profitPct := priceImpact - 0.006 // Subtract 0.6% for fees and slippage
|
|
if profitPct <= 0 {
|
|
return big.NewInt(0)
|
|
}
|
|
|
|
profit := new(big.Int).Mul(frontrunAmount, big.NewInt(int64(profitPct*1000)))
|
|
profit = new(big.Int).Div(profit, big.NewInt(1000))
|
|
|
|
return profit
|
|
}
|
|
|
|
// Gas cost calculation methods
|
|
func (engine *MEVStrategyEngine) calculateArbitrageGasCost(arb *types.ArbitrageOpportunity) *big.Int {
|
|
// Estimate gas usage: swap + transfer operations
|
|
gasUsage := big.NewInt(300000) // ~300k gas for complex arbitrage
|
|
return new(big.Int).Mul(gasUsage, engine.maxGasPrice)
|
|
}
|
|
|
|
func (engine *MEVStrategyEngine) calculateSandwichGasCost() *big.Int {
|
|
// Frontrun + backrun + priority fees
|
|
gasUsage := big.NewInt(400000) // ~400k gas total
|
|
priorityFee := big.NewInt(10000000000) // 10 gwei priority
|
|
totalGasPrice := new(big.Int).Add(engine.maxGasPrice, priorityFee)
|
|
return new(big.Int).Mul(gasUsage, totalGasPrice)
|
|
}
|
|
|
|
func (engine *MEVStrategyEngine) calculateLiquidationGasCost(protocol string) *big.Int {
|
|
// Different protocols have different gas costs
|
|
gasUsage := big.NewInt(200000) // ~200k gas for liquidation
|
|
if protocol == "gmx" {
|
|
gasUsage = big.NewInt(350000) // GMX is more expensive
|
|
}
|
|
return new(big.Int).Mul(gasUsage, engine.maxGasPrice)
|
|
}
|
|
|
|
// Utility methods
|
|
func (engine *MEVStrategyEngine) calculatePriority(profitUSD, riskScore float64) int {
|
|
// Higher profit and lower risk = higher priority
|
|
priority := int((profitUSD / 10.0) * (1.0 - riskScore) * 100)
|
|
if priority > 1000 {
|
|
priority = 1000 // Cap at 1000
|
|
}
|
|
return priority
|
|
}
|
|
|
|
func (engine *MEVStrategyEngine) calculatePriceDifference(prices map[string]float64) float64 {
|
|
if len(prices) < 2 {
|
|
return 0
|
|
}
|
|
|
|
var min, max float64
|
|
first := true
|
|
for _, price := range prices {
|
|
if first {
|
|
min = price
|
|
max = price
|
|
first = false
|
|
continue
|
|
}
|
|
if price < min {
|
|
min = price
|
|
}
|
|
if price > max {
|
|
max = price
|
|
}
|
|
}
|
|
|
|
return (max - min) / min
|
|
}
|
|
|
|
// WeiToUSD converts wei amount to USD using token price
|
|
func (calc *RealTimeProfitCalculator) WeiToUSD(amount *big.Int, token common.Address) float64 {
|
|
price, exists := calc.tokenPrices[token]
|
|
if !exists {
|
|
return 0
|
|
}
|
|
|
|
// Convert wei to token units (assume 18 decimals)
|
|
tokenAmount := new(big.Float).SetInt(amount)
|
|
tokenAmount.Quo(tokenAmount, big.NewFloat(1e18))
|
|
|
|
tokenAmountFloat, _ := tokenAmount.Float64()
|
|
return tokenAmountFloat * price.PriceUSD
|
|
}
|
|
|
|
func (calc *RealTimeProfitCalculator) getTokenPairPrice(ctx context.Context, exchange string, tokenIn, tokenOut common.Address) (float64, error) {
|
|
// This would connect to actual DEX contracts or price oracles
|
|
// For now, we'll return an error to indicate this needs implementation
|
|
return 0, fmt.Errorf("getTokenPairPrice not implemented - needs connection to actual DEX contracts or price oracles")
|
|
}
|
|
|
|
// CalculateRiskScore calculates risk score for a strategy
|
|
func (rm *RiskManager) CalculateRiskScore(arb *types.ArbitrageOpportunity) float64 {
|
|
// Base risk factors
|
|
baseRisk := 0.1
|
|
|
|
// Size risk - larger positions are riskier
|
|
sizeRisk := float64(arb.AmountIn.Uint64()) / 1e18 * 0.1 // 0.1 per ETH
|
|
|
|
// Confidence risk
|
|
confidenceRisk := (1.0 - arb.Confidence) * 0.3
|
|
|
|
// Path complexity risk
|
|
pathRisk := float64(len(arb.Path)-2) * 0.05 // Additional risk for each hop
|
|
|
|
totalRisk := baseRisk + sizeRisk + confidenceRisk + pathRisk
|
|
|
|
// Cap at 1.0
|
|
if totalRisk > 1.0 {
|
|
totalRisk = 1.0
|
|
}
|
|
|
|
return totalRisk
|
|
}
|
|
|
|
// GetTopStrategies returns the most profitable strategies sorted by priority
|
|
func (engine *MEVStrategyEngine) GetTopStrategies(strategies []*ProfitableStrategy, limit int) []*ProfitableStrategy {
|
|
// Sort by priority (highest first)
|
|
sort.Slice(strategies, func(i, j int) bool {
|
|
return strategies[i].Priority > strategies[j].Priority
|
|
})
|
|
|
|
// Apply limit
|
|
if len(strategies) > limit {
|
|
strategies = strategies[:limit]
|
|
}
|
|
|
|
return strategies
|
|
}
|
|
|
|
// UpdatePerformanceMetrics updates strategy performance tracking
|
|
func (engine *MEVStrategyEngine) UpdatePerformanceMetrics(strategy *ProfitableStrategy, actualProfit *big.Int, gasCost *big.Int) {
|
|
if actualProfit.Sign() > 0 {
|
|
engine.successfulTrades++
|
|
engine.totalProfit.Add(engine.totalProfit, actualProfit)
|
|
}
|
|
engine.totalGasCost.Add(engine.totalGasCost, gasCost)
|
|
}
|
|
|
|
// GetPerformanceStats returns performance statistics
|
|
func (engine *MEVStrategyEngine) GetPerformanceStats() map[string]interface{} {
|
|
netProfit := new(big.Int).Sub(engine.totalProfit, engine.totalGasCost)
|
|
|
|
return map[string]interface{}{
|
|
"successful_trades": engine.successfulTrades,
|
|
"total_profit_wei": engine.totalProfit.String(),
|
|
"total_gas_cost": engine.totalGasCost.String(),
|
|
"net_profit_wei": netProfit.String(),
|
|
"profit_ratio": float64(engine.totalProfit.Uint64()) / float64(engine.totalGasCost.Uint64()),
|
|
}
|
|
}
|