---
title: "Arbitrage"
description: "Arbitrage is the practice of taking advantage of a price difference between two or more markets."
canonical: https://www.quantconnect.com/docs/v2/writing-algorithms/live-trading/arbitrage
markdown: https://www.quantconnect.com/docs/v2/writing-algorithms/live-trading/arbitrage.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/03%20Writing%20Algorithms/40%20Live%20Trading/42%20Arbitrage
modified: 2026-10-30
index: https://www.quantconnect.com/llms.txt
---

# Arbitrage

[Documentation](https://www.quantconnect.com/docs/v2.md) > [Writing Algorithms](https://www.quantconnect.com/docs/v2/writing-algorithms.md) > [Live Trading](https://www.quantconnect.com/docs/v2/writing-algorithms/live-trading.md) > Arbitrage

> Code examples are shown in Python. Append `?lang=csharp` to this URL for the C# version of this page.

## Introduction

Arbitrage is the practice of taking advantage of a price difference between two or more markets. QuantConnect supports arbitrage strategies by allowing you to run multiple live algorithms deployments simultaneously and communicate between them using [commands](https://www.quantconnect.com/docs/v2/writing-algorithms/live-trading/commands.md).

## Basic Implementation

You can [broadcast](https://www.quantconnect.com/docs/v2/writing-algorithms/live-trading/commands.md#05-Broadcast-Command) a command from and execute the event handler in another project. Add the project ID to the command data to identify the source of the command:

```python
class BasicBroadcastCommandAlgorithm(QCAlgorithm):
    def initialize(self):
        self.set_benchmark(lambda x: 0) # So the algorithm needs no asset data.
        broadcast_result = self.broadcast_command({"sender": self.project_id, "ticker": "AAPL", "quantity": 1})

    def on_command(self, data):
        # Receive the command from the other algorithm
        self.log(f'Got command at {self.time} with data: {data}')
        return True
```

## Examples

The following examples demonstrate common practices for implementing arbitrage algorithms.

### Example 1: Long-short Bitcoin in two exchanges

The following algorithms trade an arbitrage strategy with BTCUSDT.

**Primary algorithm**

The primary algorithm trades a simple EMA cross strategy on BTCUSDT in Kraken. It broadcasts a command with the order filled quantity to all live deployments, and logs the command data it receives from the secondary algorithm.

```python
class CryptoArbitrageA(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_account_currency('USDT', 100000)

        # Set the brokerage model to trade on Kraken.
        self.set_brokerage_model(BrokerageName.KRAKEN, AccountType.MARGIN)
        # Request BTCUSDT data to trade.
        self._btc = self.add_crypto("BTCUSDT").symbol
        # Create an EMA indicator to generate trade signals.
        self._ema = self.ema(self._btc, 22)
        # Warm-up indicator for immediate readiness.
        self.warm_up_indicator(self._btc, self._ema)
        # Trade when the EMA is updated.
        self._ema.updated += self.trade
        # Set a threshold value for the EMA cross.
        self._threshold = 0.005

    def trade(self, _, current: IndicatorDataPoint) -> None:
        ema = current.value
        holdings = self.portfolio[self._btc]
        price = holdings.security.price

        # Trend-following strategy using price and EMA.
        # If the price is above EMA, SPY is in an uptrend, and we buy it.
        if not holdings.is_long and price > ema * (1+self._threshold):
            self.set_holdings(self._btc, 1)
        elif not holdings.is_short and price < ema * (1-self._threshold):
            self.set_holdings(self._btc, -1)

    def on_command(self, data: DynamicData) -> Optional[bool]:
        # The algorithm will log the command data from the secondary algorithm.
        self.log(f'Got command at {self.time} with data: {data}')
        return True

    def on_order_event(self, order_event: OrderEvent) -> None:
        # If an order is filled, we call the broadcast_command method to send a command to all live deployments.
        # You can add the project ID to the command data to identigy the sender.
        if not self.live_mode or order_event.status != OrderStatus.FILLED:
            return
        broadcast_result = self.broadcast_command({"sender": self.project_id, "ticker": f"{order_event.symbol.value}", "quantity": self.portfolio[order_event.symbol].quantity})
```

**Secondary algorithm**

The secondary algorithm trades the opposite direction of the primary in Bybit using the command it receives from the primary algorithm. It broadcasts a command with the order filled quantity to all live deployments.

```python
class CryptoArbitrageB(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_account_currency('USDT', 100000)

        # Set the brokerage model to trade on Bybit.
        self.set_brokerage_model(BrokerageName.BYBIT, AccountType.MARGIN)
        # Request BTCUSDT data to trade.
        self._btc = self.add_crypto("BTCUSDT").symbol

    def on_command(self, data: DynamicData) -> Optional[bool]:
        # The algorithm will trade the opposite direction of the primary algorithm.
        self.log(f'Got command at {self.time} with data: {data}')
        quantity = -(self.portfolio[self._btc].quantity + float(data.quantity))
        if quantity != 0:
            self.market_order(self._btc, quantity, tag=str(data))
        return True

    def on_order_event(self, order_event: OrderEvent) -> None:
        # If an order is filled, we call the broadcast_command method to send a command to all live deployments.
        # You can add the project ID to the command data to identify the sender.
        if not self.live_mode or order_event.status != OrderStatus.FILLED:
            return
        broadcast_result = self.broadcast_command({"sender": self.project_id, "ticker": f"{order_event.symbol.value}", "quantity": self.portfolio[order_event.symbol].quantity})
```
