
“该策略涉及两个ETF:SPY(标准普尔500指数)和FXI(中国ADR)。当它们之间的价差达到-0.4%时,投资者开设FXI的多头头寸和SPY的空头头寸。”
资产类别: ETF | 地区: 美国 | 周期: 每日 | 市场: 股票 | 关键词: 价差、ADR
I. 策略概要
投资范围包括两个ETF:代表美国市场的SPY(标准普尔500指数ETF)和代表中国ADR的FXI(iShares中国大盘ETF)。投资者为ADR和SPY之间的每日价差设定-0.4%的阈值。当价差跌至该值以下时,投资者开仓,做多FXI,做空SPY。持仓一天,并在收盘时平仓。投资组合等权重,策略每日重新平衡。
II. 策略合理性
该策略利用了ADR的时间不一致行为,特别是来自日本、香港、中国、台湾、印度和韩国等亚洲国家的ADR。这些ADR受到美国和其本国市场情绪的影响。随着美国市场收盘,这些国家的市场开盘,导致异步的价格波动。这导致ADR价格及其基础股票出现波动,经常在ADR价格和标准普尔500指数(SPY)之间产生价差。该策略假设该价差将回归均值,通过基于阈值开仓并持有至收盘,从而利用这些差异获利。
III. 来源论文
Asynchronous ADRs: Overnight vs Intraday Returns and Trading Strategies [点击查看论文]
- Tim Leung 和 Jamie Kang,华盛顿大学 – 应用数学系,哥伦比亚大学;斯坦福大学
<摘要>
美国存托凭证(ADR)是代表非美国公司证券的交易所交易凭证。它们是投资外国公司的主要金融工具。在异步市场背景下,我们重点关注亚洲ADR,并介绍其回报的实证分析方法和结果。特别是,我们根据美国市场交易时间,将其回报分解为日内和隔夜部分。我们发现,通过SPDR标准普尔500指数ETF(SPY)交易的标准普尔500指数与每个ADR之间的回报差异是一个均值回复时间序列,并通过最大似然估计(MLE)拟合到Ornstein-Uhlenbeck过程。我们的实证观察还促使我们开发和回测配对交易策略,以利用均值回复的ADR-SPY价差。我们发现,在选定的进出场水平同时执行ADR多头头寸和SPY空头头寸时,能获得持续的正收益。


IV. 回测表现
| 年化回报 | 14% |
| 波动率 | 16.35% |
| β值 | 0.025 |
| 夏普比率 | 0.61 |
| 索提诺比率 | -0.369 |
| 最大回撤 | N/A |
| 胜率 | 46% |
V. 完整的 Python 代码
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