fix(arbitrage): critical fixes for struct initialization and price impact calculations
- Triangular arbitrage: populate all 25+ ArbitrageOpportunity fields - Direct arbitrage: complete field initialization with gas cost calculation - Price impact: add division-by-zero protection and validation - Absolute value handling for swap amounts to prevent uint256 max display Remaining issue: Some events still show uint256 max - needs investigation of alternative parsing code path (possibly pkg/pools/discovery.go)
This commit is contained in:
@@ -429,17 +429,31 @@ func (s *SwapAnalyzer) calculatePriceMovement(event events.Event, poolData *mark
|
||||
priceDiff := new(big.Float).Sub(swapPrice, currentPrice)
|
||||
priceDiff.Abs(priceDiff)
|
||||
|
||||
priceImpactFloat := new(big.Float).Quo(priceDiff, currentPrice)
|
||||
priceImpact, _ = priceImpactFloat.Float64()
|
||||
// Check if currentPrice is zero to prevent division by zero
|
||||
zero := new(big.Float).SetFloat64(0.0)
|
||||
if currentPrice.Cmp(zero) != 0 {
|
||||
priceImpactFloat := new(big.Float).Quo(priceDiff, currentPrice)
|
||||
priceImpact, _ = priceImpactFloat.Float64()
|
||||
|
||||
// Validate: reject impossible price impacts (>1000% = 10.0)
|
||||
if priceImpact > 10.0 {
|
||||
s.logger.Warn(fmt.Sprintf("Price impact too large (%.2f), capping at 0", priceImpact))
|
||||
priceImpact = 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use absolute values for amounts (UniswapV3 uses signed int256, but amounts should be positive)
|
||||
amountIn := new(big.Int).Abs(event.Amount0)
|
||||
amountOut := new(big.Int).Abs(event.Amount1)
|
||||
|
||||
movement := &market.PriceMovement{
|
||||
Token0: event.Token0.Hex(),
|
||||
Token1: event.Token1.Hex(),
|
||||
Pool: event.PoolAddress.Hex(),
|
||||
Protocol: event.Protocol,
|
||||
AmountIn: event.Amount0,
|
||||
AmountOut: event.Amount1,
|
||||
AmountIn: amountIn,
|
||||
AmountOut: amountOut,
|
||||
PriceBefore: currentPrice,
|
||||
PriceAfter: currentPrice, // For now, assume same price (could be calculated based on swap)
|
||||
PriceImpact: priceImpact,
|
||||
@@ -448,7 +462,11 @@ func (s *SwapAnalyzer) calculatePriceMovement(event events.Event, poolData *mark
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
s.logger.Debug(fmt.Sprintf("Price movement calculated: impact=%.6f%%, amount_in=%s", priceImpact*100, event.Amount0.String()))
|
||||
amount0Str := "0"
|
||||
if event.Amount0 != nil {
|
||||
amount0Str = event.Amount0.String()
|
||||
}
|
||||
s.logger.Debug(fmt.Sprintf("Price movement calculated: impact=%.6f%%, amount_in=%s", priceImpact*100, amount0Str))
|
||||
return movement, nil
|
||||
}
|
||||
|
||||
@@ -501,27 +519,79 @@ func (s *SwapAnalyzer) findArbitrageOpportunities(event events.Event, movement *
|
||||
|
||||
// Calculate price difference
|
||||
priceDiff := new(big.Float).Sub(currentPrice, relatedPrice)
|
||||
// Check if relatedPrice is zero to prevent division by zero
|
||||
zero := new(big.Float).SetFloat64(0.0)
|
||||
if relatedPrice.Cmp(zero) == 0 {
|
||||
s.logger.Debug(fmt.Sprintf("Skipping pool %s: related price is zero", pool.Address.Hex()))
|
||||
continue
|
||||
}
|
||||
|
||||
priceDiffRatio := new(big.Float).Quo(priceDiff, relatedPrice)
|
||||
|
||||
// If there's a significant price difference, we might have an arbitrage opportunity
|
||||
priceDiffFloat, _ := priceDiffRatio.Float64()
|
||||
|
||||
// Validate: reject impossible price differences (>1000% = 10.0)
|
||||
if priceDiffFloat > 10.0 || priceDiffFloat < -10.0 {
|
||||
s.logger.Debug(fmt.Sprintf("Skipping pool %s: price difference too large (%.2f)", pool.Address.Hex(), priceDiffFloat))
|
||||
continue
|
||||
}
|
||||
|
||||
// Take absolute value for comparison
|
||||
priceDiffAbs := priceDiffFloat
|
||||
if priceDiffAbs < 0 {
|
||||
priceDiffAbs = -priceDiffAbs
|
||||
}
|
||||
// Lower threshold for Arbitrum where spreads are smaller
|
||||
arbitrageThreshold := 0.001 // 0.1% threshold instead of 0.5%
|
||||
if priceDiffFloat > arbitrageThreshold {
|
||||
if priceDiffAbs > arbitrageThreshold {
|
||||
// Estimate potential profit
|
||||
estimatedProfit := marketScanner.EstimateProfit(event, pool, priceDiffFloat)
|
||||
|
||||
if estimatedProfit != nil && estimatedProfit.Sign() > 0 {
|
||||
opp := stypes.ArbitrageOpportunity{
|
||||
Path: []string{event.Token0.Hex(), event.Token1.Hex()},
|
||||
Pools: []string{event.PoolAddress.Hex(), pool.Address.Hex()},
|
||||
Profit: estimatedProfit,
|
||||
GasEstimate: big.NewInt(300000), // Estimated gas cost
|
||||
ROI: priceDiffFloat * 100, // Convert to percentage
|
||||
Protocol: fmt.Sprintf("%s->%s", event.Protocol, pool.Protocol),
|
||||
// Calculate gas cost in wei (300k gas * current gas price estimate)
|
||||
gasPrice := big.NewInt(100000000) // 0.1 gwei default
|
||||
gasUnits := big.NewInt(300000)
|
||||
gasCost := new(big.Int).Mul(gasPrice, gasUnits)
|
||||
|
||||
// Calculate net profit after gas
|
||||
netProfit := new(big.Int).Sub(estimatedProfit, gasCost)
|
||||
|
||||
// Only create opportunity if still profitable after gas
|
||||
if netProfit.Sign() > 0 {
|
||||
now := time.Now()
|
||||
// Use a reasonable test amount (e.g., 0.1 ETH in wei)
|
||||
testAmount := big.NewInt(100000000000000000) // 0.1 ETH
|
||||
|
||||
opp := stypes.ArbitrageOpportunity{
|
||||
ID: fmt.Sprintf("arb_%d_%s", now.Unix(), event.PoolAddress.Hex()[:10]),
|
||||
Path: []string{event.Token0.Hex(), event.Token1.Hex()},
|
||||
Pools: []string{event.PoolAddress.Hex(), pool.Address.Hex()},
|
||||
AmountIn: testAmount,
|
||||
Profit: estimatedProfit,
|
||||
NetProfit: netProfit,
|
||||
GasEstimate: gasUnits,
|
||||
GasCost: gasCost,
|
||||
EstimatedProfit: netProfit,
|
||||
RequiredAmount: testAmount,
|
||||
ROI: priceDiffAbs * 100, // Convert to percentage (use absolute value)
|
||||
Protocol: fmt.Sprintf("%s->%s", event.Protocol, pool.Protocol),
|
||||
ExecutionTime: 200, // Estimated 200ms for direct arb
|
||||
Confidence: 0.7, // Higher confidence for direct arb
|
||||
PriceImpact: priceDiffAbs, // Use absolute value for price impact
|
||||
MaxSlippage: 1.0, // 1% max slippage
|
||||
TokenIn: event.Token0,
|
||||
TokenOut: event.Token1,
|
||||
Timestamp: now.Unix(),
|
||||
DetectedAt: now,
|
||||
ExpiresAt: now.Add(3 * time.Second), // 3 second expiry for direct arb
|
||||
Urgency: 7, // Higher urgency
|
||||
Risk: 0.2, // Lower risk
|
||||
Profitable: true,
|
||||
}
|
||||
opportunities = append(opportunities, opp)
|
||||
s.logger.Info(fmt.Sprintf("Found arbitrage opportunity: %+v", opp))
|
||||
}
|
||||
opportunities = append(opportunities, opp)
|
||||
s.logger.Info(fmt.Sprintf("Found arbitrage opportunity: %+v", opp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user