Skip to main content

Overview

NeuraTrade’s arbitrage detection engine continuously scans markets for price discrepancies and funding rate differentials across exchanges. The system supports spot arbitrage, futures funding rate arbitrage, and multi-leg (triangular) arbitrage strategies.

Spot Arbitrage Detection

The spot arbitrage calculator identifies price differences for the same trading pair across multiple exchanges.

How It Works

1

Market Data Collection

The system queries the latest market data for all active trading pairs across configured exchanges, filtering data within a 10-minute window.
2

Price Comparison

For each symbol, the engine compares prices across exchanges to find the lowest buy price and highest sell price.
3

Profit Calculation

Calculate profit percentage: (sell_price - buy_price) / buy_price * 100
4

Opportunity Filtering

Filter opportunities above the minimum profit threshold (default: 0.1%)

Code Reference

services/backend-api/internal/services/arbitrage_service.go
// SpotArbitrageCalculator compares prices across exchanges
type SpotArbitrageCalculator struct{}

func (calc *SpotArbitrageCalculator) CalculateArbitrageOpportunities(
    ctx context.Context,
    marketData map[string][]models.MarketData,
) ([]models.ArbitrageOpportunity, error) {
    var opportunities []models.ArbitrageOpportunity
    
    for _, exchangeData := range marketData {
        if len(exchangeData) < 2 {
            continue // Need at least 2 exchanges
        }
        
        // Find lowest and highest prices
        var lowestPrice, highestPrice decimal.Decimal
        var lowestExchange, highestExchange *models.Exchange
        
        // Calculate profit percentage
        profitPercentage := highestPrice.Sub(lowestPrice)
            .Div(lowestPrice)
            .Mul(decimal.NewFromInt(100))
        
        if profitPercentage.GreaterThan(decimal.NewFromFloat(0.1)) {
            opportunities = append(opportunities, opportunity)
        }
    }
    return opportunities, nil
}
Data Freshness: Only market data from the last 10 minutes is considered to ensure opportunities are still valid.

Futures Arbitrage Detection

Funding rate arbitrage exploits differences in perpetual futures funding rates across exchanges. When one exchange pays significantly more to longs (or shorts) than another, a market-neutral arbitrage position can be opened.

Funding Rate Mechanism

Long Position

Open long on exchange with lower funding rate (receive payments or pay less)

Short Position

Open short on exchange with higher funding rate (receive more payments)

Calculation

services/backend-api/internal/services/futures_arbitrage_calculator.go
type FuturesArbitrageCalculationInput struct {
    Symbol           string
    LongExchange     string
    ShortExchange    string
    LongFundingRate  decimal.Decimal
    ShortFundingRate decimal.Decimal
    LongMarkPrice    decimal.Decimal
    ShortMarkPrice   decimal.Decimal
    FundingInterval  int // Hours (typically 8)
}

// Net funding rate is the profit from the spread
netFundingRate := shortRate.Sub(longRate)

// APY calculation
hourlyRate := netFundingRate.Div(decimal.NewFromInt(fundingInterval))
dailyRate := hourlyRate.Mul(decimal.NewFromInt(24))
apy := dailyRate.Mul(decimal.NewFromInt(365))

Service Implementation

The FuturesArbitrageService runs every 30 seconds to detect funding rate opportunities:
services/backend-api/internal/services/futures_arbitrage_service.go:128-177
func (s *FuturesArbitrageService) runOpportunityCalculator() {
    ticker := time.NewTicker(30 * time.Second)
    defer ticker.Stop()
    
    for {
        select {
        case <-s.ctx.Done():
            return
        case <-ticker.C:
            calcCtx, cancel := context.WithTimeout(s.ctx, 25*time.Second)
            
            err := s.errorRecoveryManager.ExecuteWithRetry(
                calcCtx,
                "calculate_opportunities",
                func() error {
                    return s.calculateAndStoreOpportunities(calcCtx)
                },
            )
            cancel()
        }
    }
}
Price Difference Risk: Large price differences between exchanges may indicate liquidity issues or oracle problems, not true arbitrage.

Multi-Leg Arbitrage (Triangular)

Triangular arbitrage exploits price inefficiencies across three trading pairs on the same exchange.

Example: USDT → BTC → ETH → USDT

1. Start with 1000 USDT
2. Buy BTC with USDT  → 0.025 BTC  (BTC/USDT = 40000)
3. Buy ETH with BTC   → 0.625 ETH  (ETH/BTC = 0.025)
4. Sell ETH for USDT  → 1005 USDT  (ETH/USDT = 1608)

Net Profit: 5 USDT (0.5%)
Fee Consideration: All three legs incur trading fees. The calculator accounts for taker fees on each trade to compute net profitability.

Configuration

Arbitrage detection behavior is controlled through the application configuration:
arbitrage:
  enabled: true
  interval_seconds: 60          # Scan frequency
  min_profit_threshold: 0.5     # Minimum profit % to consider
  max_age_minutes: 30           # Discard stale opportunities
  batch_size: 100               # Database batch size

Key Configuration Parameters

How frequently the arbitrage service scans for opportunities. Lower values increase API load but catch fleeting opportunities.
Minimum profit percentage required to store an opportunity. Set higher to reduce noise from marginal opportunities.
Maximum age of market data to consider. Older data may represent stale prices that no longer exist.

API Endpoints

Retrieve active arbitrage opportunities via the REST API:
# Get active spot arbitrage opportunities
GET /api/v1/arbitrage/opportunities?limit=10

# Get active futures arbitrage opportunities
GET /api/v1/arbitrage/futures?limit=10

# Get multi-leg opportunities
GET /api/v1/arbitrage/multi-leg?limit=10

Response Example

{
  "opportunities": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "buy_exchange": "binance",
      "sell_exchange": "coinbase",
      "symbol": "BTC/USDT",
      "buy_price": 40000.50,
      "sell_price": 40250.00,
      "profit_percentage": 0.62,
      "detected_at": "2026-03-03T10:30:00Z",
      "expires_at": "2026-03-03T10:35:00Z"
    }
  ]
}

Performance & Observability

Sentry Integration

All arbitrage calculations are instrumented with Sentry spans for performance tracking:
services/backend-api/internal/services/arbitrage_service.go:342-357
ctx, span := observability.StartSpan(s.ctx, observability.SpanOpArbitrage, "arbitrage_calculation_cycle")
defer observability.FinishSpan(span, nil)

span.SetTag("service", "arbitrage")
span.SetData("min_profit_threshold", s.arbitrageConfig.MinProfit)

observability.AddBreadcrumb(ctx, "arbitrage", "Starting arbitrage calculation cycle", sentry.LevelInfo)

Diagnostics

When no market data is available, the service runs diagnostics to identify the root cause:
services/backend-api/internal/services/arbitrage_service.go:569-639
func (s *ArbitrageService) diagnoseNoMarketData() {
    var totalRows, freshRows, activeExchanges, activePairs int
    
    // Count total market_data rows
    s.db.QueryRow(s.ctx, "SELECT COUNT(*) FROM market_data").Scan(&totalRows)
    
    // Count fresh data (within 10 minutes)
    s.db.QueryRow(s.ctx, freshDataQuery).Scan(&freshRows)
    
    // Count active exchanges and trading pairs
    s.db.QueryRow(s.ctx, "SELECT COUNT(*) FROM exchanges WHERE status = 'active'").Scan(&activeExchanges)
    s.db.QueryRow(s.ctx, "SELECT COUNT(*) FROM trading_pairs WHERE is_active = true").Scan(&activePairs)
    
    s.logger.WithFields(map[string]interface{}{
        "total_market_data_rows": totalRows,
        "fresh_rows_10min":       freshRows,
        "active_exchanges":       activeExchanges,
        "active_trading_pairs":   activePairs,
        "recommendation":         s.getDiagnosticRecommendation(...),
    }).Warn("Market data diagnostic")
}

Best Practices

Monitor Latency

Use observability spans to track calculation time. High latency reduces profit capture.

Verify Liquidity

Check order book depth before executing. Price may move against you with large orders.

Account for Fees

All calculations must include trading fees, withdrawal fees, and potential slippage.

Test in Paper Mode

Always validate arbitrage strategies in paper trading mode before committing real capital.

Autonomous Trading

Learn how arbitrage opportunities trigger automated quest execution

Risk Management

Understand position limits and circuit breakers for arbitrage trades

Build docs developers (and LLMs) love