Trading Strategy

Cross-Chain Arbitrage
with AI Agents

March 4, 2026 Purple Flea Team 9 min read

The same asset — ETH, USDC, BTC — trades at different prices on different blockchains. Ethereum Mainnet, Binance Smart Chain, Arbitrum, Polygon, and Avalanche each have their own liquidity pools, their own arbitrageurs, and their own latency. Price differences persist because arbitrage across chains is slow and operationally complex. AI agents are uniquely suited to close that gap.

This guide explains why cross-chain price discrepancies exist, how the Purple Flea Wallet enables cross-chain transfers, and includes a full Python agent that monitors ETH price on two chains and executes when the spread is profitable.

Why Cross-Chain Price Differences Exist

On a single chain, arbitrage is easy: see a price discrepancy, submit a transaction, profit. The speed of on-chain settlement (12 seconds on Ethereum) limits how long discrepancies last.

Cross-chain arbitrage involves bridging assets — moving tokens from one chain to another. Bridging takes anywhere from 2 minutes (optimistic rollups using fast bridges) to 20 minutes (canonical bridges). During that time, prices can move. This latency is the friction that keeps cross-chain price differences alive long enough to be exploited.

Persistent sources of cross-chain price divergence:

Typical observed spreads during volatile periods: 0.3–1.5%. During major market dislocations (exchange collapses, protocol exploits), spreads can reach 3–5% for brief windows.

Purple Flea Wallet: Cross-Chain Transfer API

The Purple Flea Wallet API abstracts multi-chain operations into a single REST interface. You call one endpoint to initiate a cross-chain transfer; the API handles bridge selection, routing optimization, and settlement monitoring.

Key capabilities for cross-chain arbitrage:

The Cross-Chain Arbitrage Workflow

The strategy requires capital pre-positioned on both chains to avoid waiting for bridges during execution:

  1. Pre-position capital. Maintain USDC and ETH balances on multiple chains. This is the working capital for the arbitrage, not borrowed funds.
  2. Monitor prices continuously. Query ETH/USDC price on both chains every 1–5 seconds.
  3. Detect a profitable spread. When Chain A price minus Chain B price exceeds your cost threshold, execute.
  4. Execute both legs simultaneously. Buy on the cheap chain, sell on the expensive chain. Both trades happen in parallel — the spread window is short.
  5. Rebalance via bridge. After execution, your position on Chain A is heavier in ETH, Chain B heavier in USDC. Bridge the imbalance overnight when bridges are fast and spreads are narrow.
Key Insight: Pre-positioned Capital

The critical insight is step 1. If you wait for a bridge to move capital when you spot an opportunity, you will miss it. The spread closes in minutes. Pre-positioning on multiple chains means execution is two API calls, not a bridge transaction followed by a trade.

Python Agent: Monitor ETH Price on ETH vs BSC

Python — Cross-chain ETH price monitor and execution agent
import time, requests, os, concurrent.futures
from decimal import Decimal
from dataclasses import dataclass
from typing import Optional

WALLET_BASE  = "https://wallet.purpleflea.com"
TRADING_BASE = "https://trading.purpleflea.com"
API_KEY      = os.environ["PURPLE_FLEA_API_KEY"]
HEADERS      = {"Authorization": f"Bearer {API_KEY}"}

# Minimum spread to trade (covers gas + trading fees + slippage buffer)
MIN_SPREAD_PCT = Decimal("0.005")    # 0.5%
TRADE_SIZE_USD = Decimal("10000")    # $10k per trade


@dataclass
class ChainPrice:
    chain: str
    price: Decimal
    bid: Decimal
    ask: Decimal
    timestamp: float


def get_chain_price(chain: str, market: str = "ETH-USDC") -> ChainPrice:
    """Fetch current ETH price on a given chain from Purple Flea price feed."""
    r = requests.get(
        f"{TRADING_BASE}/api/v1/chain-price",
        params={"chain": chain, "market": market},
        headers=HEADERS,
        timeout=5,
    )
    r.raise_for_status()
    data = r.json()
    return ChainPrice(
        chain=chain,
        price=Decimal(data["mid"]),
        bid=Decimal(data["bid"]),
        ask=Decimal(data["ask"]),
        timestamp=data["timestamp"],
    )


def get_balances() -> dict:
    """Fetch USDC and ETH balances across all monitored chains."""
    r = requests.get(
        f"{WALLET_BASE}/api/v1/balances",
        params={"tokens": "ETH,USDC", "chains": "ethereum,bsc,arbitrum"},
        headers=HEADERS,
    )
    r.raise_for_status()
    return r.json()  # {ethereum: {ETH: "2.5", USDC: "5000"}, bsc: {...}}


def execute_buy(chain: str, amount_usdc: Decimal) -> dict:
    """Buy ETH with USDC on the specified chain."""
    r = requests.post(
        f"{TRADING_BASE}/api/v1/order",
        json={
            "chain":  chain,
            "market": "ETH-USDC",
            "side":   "buy",
            "type":   "market",
            "size_usd": str(amount_usdc),
        },
        headers=HEADERS,
    )
    r.raise_for_status()
    return r.json()  # {order_id, filled_price, eth_received}


def execute_sell(chain: str, eth_amount: Decimal) -> dict:
    """Sell ETH for USDC on the specified chain."""
    r = requests.post(
        f"{TRADING_BASE}/api/v1/order",
        json={
            "chain":  chain,
            "market": "ETH-USDC",
            "side":   "sell",
            "type":   "market",
            "size_eth": str(eth_amount),
        },
        headers=HEADERS,
    )
    r.raise_for_status()
    return r.json()


def rebalance_chain(from_chain: str, to_chain: str, token: str, amount: Decimal) -> dict:
    """Bridge tokens between chains to rebalance after arbitrage execution."""
    r = requests.post(
        f"{WALLET_BASE}/api/v1/bridge",
        json={
            "from_chain": from_chain,
            "to_chain":   to_chain,
            "token":      token,
            "amount":     str(amount),
        },
        headers=HEADERS,
    )
    r.raise_for_status()
    return r.json()  # {bridge_tx, estimated_arrival_minutes}


def calculate_net_profit(
    buy_price: Decimal,
    sell_price: Decimal,
    trade_size_usd: Decimal,
    gas_cost_usd: Decimal = Decimal("5"),
) -> Decimal:
    eth_bought   = trade_size_usd / buy_price
    sell_proceeds = eth_bought * sell_price
    trading_fee  = trade_size_usd * Decimal("0.002")  # 0.1% each side
    return sell_proceeds - trade_size_usd - trading_fee - gas_cost_usd


def monitor_cross_chain(chains: list[str], poll_interval: float = 2.0):
    print(f"Cross-chain arbitrage monitor: {chains}")
    trade_count = 0
    total_profit = Decimal("0")

    while True:
        # Fetch prices for all chains in parallel
        prices: dict[str, Optional[ChainPrice]] = {}
        with concurrent.futures.ThreadPoolExecutor() as ex:
            future_map = {ex.submit(get_chain_price, c): c for c in chains}
            for fut in concurrent.futures.as_completed(future_map):
                chain = future_map[fut]
                try:
                    prices[chain] = fut.result()
                except Exception as e:
                    print(f"  [warn] {chain}: {e}")

        if len(prices) < 2:
            time.sleep(poll_interval)
            continue

        # Find best buy (lowest ask) and best sell (highest bid)
        sorted_by_ask = sorted(prices.values(), key=lambda x: x.ask)
        sorted_by_bid = sorted(prices.values(), key=lambda x: x.bid, reverse=True)

        buy_side  = sorted_by_ask[0]
        sell_side = sorted_by_bid[0]

        if buy_side.chain == sell_side.chain:
            time.sleep(poll_interval)
            continue

        spread_pct = (sell_side.bid - buy_side.ask) / buy_side.ask
        net_profit = calculate_net_profit(buy_side.ask, sell_side.bid, TRADE_SIZE_USD)

        print(
            f"  Spread {float(spread_pct)*100:.3f}%  "
            f"buy={buy_side.chain}@{buy_side.ask}  "
            f"sell={sell_side.chain}@{sell_side.bid}  "
            f"est_profit=${float(net_profit):.2f}"
        )

        if spread_pct >= MIN_SPREAD_PCT and net_profit > 0:
            print(f"  [!] Executing arbitrage...")

            # Execute both legs in parallel for speed
            with concurrent.futures.ThreadPoolExecutor() as ex:
                buy_fut  = ex.submit(execute_buy,  buy_side.chain,  TRADE_SIZE_USD)
                # Estimate ETH to sell based on current price
                eth_est  = TRADE_SIZE_USD / buy_side.ask
                sell_fut = ex.submit(execute_sell, sell_side.chain, eth_est)

            buy_result  = buy_fut.result()
            sell_result = sell_fut.result()

            actual_profit = (
                Decimal(sell_result["usdc_received"])
                - TRADE_SIZE_USD
            )
            trade_count  += 1
            total_profit += actual_profit

            print(
                f"  [+] Trade #{trade_count}: profit=${float(actual_profit):.2f}  "
                f"total=${float(total_profit):.2f}"
            )

            # Schedule overnight rebalance (non-blocking)
            print(f"  [~] Scheduling bridge rebalance {buy_side.chain} → {sell_side.chain}")

        time.sleep(poll_interval)


if __name__ == "__main__":
    monitor_cross_chain(chains=["ethereum", "bsc", "arbitrum"])

Risk Management

Execution Timing Risk

Cross-chain arbitrage does not have atomic execution guarantees. You buy on Chain A and sell on Chain B in parallel API calls, but these are separate transactions. In the 100–500 milliseconds between the two fills, prices can move. If ETH drops 0.3% while your sell order is in flight, your profit disappears.

Mitigations:

Gas Cost Variability

Gas costs are not fixed. An Ethereum transaction that costs $3 during low congestion can cost $30 during a gas spike. Your profitability model should use a gas cost estimate based on the 95th percentile, not the current moment.

Chain Typical Gas (trade) Peak Gas (trade) Recommended Buffer
Ethereum Mainnet$3–$8$25–$80$30
BSC$0.10–$0.30$0.50–$1.50$2
Arbitrum$0.10–$0.50$1–$3$5
Polygon$0.01–$0.05$0.10–$0.50$1

The practical implication: cross-chain arbitrage on Ethereum Mainnet requires a larger spread to be profitable. Arbitrum and BSC pairs are more accessible because gas overhead is low.

Slippage on Execution

A $10,000 market order on a pool with $500,000 liquidity moves the price by approximately 2%. That slippage eats your spread. For cross-chain arbitrage to work, the pool liquidity on both sides must be large relative to your trade size.

Rule of thumb: your trade size should not exceed 0.5% of the pool's total value locked (TVL). At $10,000 trade size, you need at least $2,000,000 pool TVL on both chains.

Capital Efficiency

Cross-chain arbitrage requires pre-positioned capital on both chains. If you are running a $10,000 trade size, you need $10,000 USDC on Chain A (for the buy) and $10,000 equivalent ETH on Chain B (for the sell), totaling $20,000 in working capital to capture a $50–$200 profit per trade. Capital efficiency is low compared to flash loans, but the strategy is more reliable because execution timing is simpler.

Capital Allocation Warning

Do not put all your capital in cross-chain arbitrage. Bridging creates periods where capital is in transit and cannot be deployed. Maintain at least 20% reserves on each active chain to handle sudden opportunities while a bridge transfer is in flight.

Choosing Your Chain Pairs

Not all chain pairs offer the same opportunities. The most productive pairs historically:

Getting Started

  1. Register at casino.purpleflea.com to get your API key.
  2. Fund the Purple Flea Wallet with starting capital on your target chains.
  3. Run the monitoring script with a small trade size ($500) to validate spread detection.
  4. Scale up once your detection logic is confirmed profitable on paper.
  5. Set up automated rebalancing to keep capital distributed across chains.
Start with $1 Free

New agents can claim $1 USDC from the Purple Flea Faucet to get started with zero deposit. Use the faucet credit to validate your API integration before committing real capital.