Inter-Exchange Arbitrage with Atreyu Brokerage

With the latest Atreyu Brokerage integration, QuantConnect members can route orders directly to any US exchange, with their trades cleared by a prime brokerage. Today, we take a look at utilizing this new functionality to capture inter-exchange arbitrage opportunities that occur when the asking price on one exchange is lower than the bid price on another exchange. In this situation, traders can buy on the underpriced exchange and sell on the overpriced exchange for a relatively low-risk profit.

Our Process

We first set our brokerage model to be the Atreyu brokerage model and subscribed to Telsa tick data.

AddEquity("TSLA", Resolution.Tick);

As batches of ticks were passed to our algorithm, we located the exchange with the lowest ask price and the exchange with the highest bid price. From these data points, we determined if there was an arbitrage opportunity in the current quotes. If there were no opportunities, we simply waited for the next batch of ticks to be passed into the algorithm. The batch of tick quotes spans 1 millisecond. This last quote in the batch is the most recent one. There is no guarantee that by the time our orders reach the exchanges that the quotes will still be available, but our algorithm assumes they will be available.

When we located an arbitrage opportunity, we sized our trade to be the minimum quantity of the bid and ask. If we exceeded that bid-ask size, the order would likely fill using offers deeper into the order book. In addition to limiting the size of the order by the quantity on the bid and ask, we made sure not to exceed the largest number of shares our portfolio’s equity could afford.

var quantity = Math.Min(lowestSellQuote.AskSize, highestBuyQuote.BidSize);
quantity = Math.Min(quantity, CalculateOrderQuantity(_symbol, 1.0m));

After we determined the order size, we created a buy market order and sent it to the underpriced exchange.

var orderProperties = new AtreyuOrderProperties();
orderProperties.Exchange = _exchangeMapper.getExchangeFromName(lowestSellQuote.Exchange);
MarketOrder(_symbol, quantity, true, $"{lowestSellQuote.Exchange}", orderProperties);

Following the buy order, we created a sell market order of the same size and sent it to the overpriced exchange.

orderProperties.Exchange = _exchangeMapper.getExchangeFromName(highestBuyQuote.Exchange);
MarketOrder(_symbol, -quantity, true, $"{highestBuyQuote.Exchange}", orderProperties=orderProperties);

To model the trades being filled at the price presented by the exchange, we created a custom fill model which matches the order’s target exchange with the tick quotes from the exchange. This is very optimistic and likely would not happen in live trading.

// Set the fill price of buy orders to be the price of lowest sell quote on the exchange
fill.FillPrice = quotes.Where(quote => quote.AskPrice != 0)
		       .OrderBy(quote => quote.AskPrice)


We tested the strategy over the month of January 2021. The backtest identified 2,221 inter-exchange arbitrage opportunities and executed 4,442 trades for a total volume traded of $386M. Despite the standard Atreyu brokerage fees that applied, the algorithm was able to produce a 34 Sharpe ratio.

There are many risks in inter-exchange arbitrage trading. For instance, the quotes you’re trying to fill can be filled before your orders reach the exchanges. In the algorithm we tested, we assumed all of the orders were filled completely at the best bid and ask prices. More work needs to be done before deploying a strategy like this to live trading.

Derek Melchin

By: Derek Melchin

img Back to Blog

Related Articles

algorithmic trading

Living Our Mission 🚀

By: Jared Broad • 20.09.2022 algorithmic trading

Docs V2 Released

By: Jared Broad • 13.07.2022 atreyu

Smart Order Routing with Atreyu

By: Derek Melchin • 29.10.2021 algorithmic trading

LEAN CLI for Streamlining Cloud and Local Workflows

By: Jared Broad • 07.06.2021 bond yields

Idea Streams #12 – Yield Rates

By: Derek Melchin • 01.06.2021