Quant BuffetRelax, Not Over Thinking

Spread Trading with ADRs

Log in to collect

Academic paper

Strategy in a nutshell

The investment universe consists of two ETFs: SPY (S&P500 ETF) representing the US market and FXI (iShares China Large-Cap ETF) representing Chinese ADRs. The investor sets a threshold of -0.4% for the daily spread between ADR and SPY. When the spread falls below this value, the investor opens positions, going long on FXI and short on SPY. The position is held for one day and closed at market close. The portfolio is equally weighted, and the strategy is rebalanced daily.

Economic rationale

The strategy exploits the time-inconsistent behavior of ADRs, especially those from Asian countries. These ADRs are influenced by both U.S. and home market sentiments, leading to asynchronous price movements. This creates spreads between ADR prices and SPY, which the strategy assumes will revert to the mean, allowing profitable trades based on the threshold.

Backtest performance

Annualised return14%
Volatility16.35%
Beta0.025
Sharpe ratio0.61
Sortino ratio-0.369
Win rate46%

Full Python code

from AlgorithmImports import *
class SpreadTradingADRs(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2000, 1, 1)
self.SetCash(100000)
self.symbols = ['SPY', 'FXI']

self.k = -0.004

for symbol in self.symbols:
    self.AddEquity(symbol, Resolution.Minute)

self.spy_open_price = 0
self.fxi_open_price = 0

self.Schedule.On(self.DateRules.EveryDay(self.symbols[0]), self.TimeRules.AfterMarketOpen(self.symbols[0], 1), self.MarketOpen)
self.Schedule.On(self.DateRules.EveryDay(self.symbols[0]), self.TimeRules.BeforeMarketClose(self.symbols[0], 1), self.Rebalance)

def MarketOpen(self):
if self.Securities.ContainsKey(self.symbols[0]) and self.Securities.ContainsKey(self.symbols[1]):
    spy_price = self.Securities[self.symbols[0]].Open
    fxi_price = self.Securities[self.symbols[1]].Open
    if spy_price != 0 and fxi_price != 0:
        self.spy_open_price = spy_price
        self.fxi_open_price = fxi_price
                
def Rebalance(self):
self.Liquidate()

if self.Securities.ContainsKey(self.symbols[0]) and self.Securities.ContainsKey(self.symbols[1]):
    spy_price = self.Securities[self.symbols[0]].Close
    fxi_price = self.Securities[self.symbols[1]].Close
    
    if spy_price != 0 and fxi_price != 0 and self.spy_open_price != 0 and self.fxi_open_price != 0:
        spy_ret = spy_price / self.spy_open_price - 1
        fxi_ret = fxi_price / self.fxi_open_price - 1
        
        if fxi_ret - spy_ret < self.k:
            self.SetHoldings('FXI', 1/2)
            self.SetHoldings('SPY', -1/2)

        self.spy_open_price = 0
        self.fxi_open_price = 0