“该策略通过在SPY(纽交所)和IUSA(瑞士证券交易所)之间进行套利操作,当价差超过1.002时,卖空被高估的ETF并买入被低估的ETF,价差收敛后平仓,每年执行约40笔交易。”

I. 策略概要

该策略针对SPY(标普500ETF,纽交所交易)和IUSA(标普500ETF,瑞士证券交易所交易),仅在两个市场同时开放时交易。

交易频率: 每年执行约40笔交易,通过两个ETF之间的定价效率差异实现套利收益。

套利机会识别: 当以下条件满足时触发交易:

bid SPY / ask IUSA > 1.002

bid IUSA / ask SPY > 1.002

操作方式:

卖空被高估的ETF

买入被低估的ETF

平仓: 当买卖价差收敛后,关闭头寸。

II. 经济基础

ETF旨在紧密跟踪其基础指数,尽量减少跟踪误差。然而,由于每日“成分股偏差”导致的价格偏离时有发生。这种偏离可能源于:

这些偏离为套利创造了机会。投资者可以通过买入被低估的ETF并卖空被高估的ETF,从价格差异中获利,同时锁定利润。这种机制利用了ETF的短期定价效率差,同时反映了其长期与基础指数表现一致的特性。

III. 论文来源

ETF Arbitrage: Intraday Evidence [点击浏览原文]

作者: 本·R·马歇尔(Ben R. Marshall),阮H·阮(Nhut H. Nguyen),和努塔瓦特·维萨尔塔纳乔提(Nuttawat Visaltanachoti)。梅西大学经济与金融学院,奥克兰理工大学,梅西大学经济与金融系。

<摘要>

我们分析了两只极为流动的标普500ETF在定价错误条件下的交易机会。尽管这些ETF并非完全可互换,但我们表明,它们之间的细微差异并非导致定价错误的原因。价差在套利机会出现前增大,这与流动性下降一致。随着市场变得单边化,订单不平衡增加,价差波动加剧,这表明流动性风险上升。这些价格偏离在经济上具有显著意义(扣除价差后的平均年化收益为6.6%),并且通常迅速向均值回归。

IV. 回测表现

年化收益率28.91%
波动率14.69%
Beta-0.002
夏普比率1.7
索提诺比率N/A
最大回撤N/A
胜率40%

V. 完整python代码

from AlgorithmImports import *
from typing import List, Union
# endregion
class HighFrequencyArbitragewithETFTwins(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2000, 1, 1)
        self.SetCash(100000)
        self.spread_threshold:float = 1.002
        self.spy_voo_ratio:Union[None, float] = None
        self.voo_spy_ratio:Union[None, float] = None
        self.symbols:List[Symbol] = [self.AddEquity(x, Resolution.Minute).Symbol for x in ['SPY', 'VOO']]
        self.trade_direction_flag:Union[None, bool] = None
    def OnData(self, data: Slice) -> None:
        if self.symbols[0] in data and data[self.symbols[0]] and self.symbols[1] in data and data[self.symbols[1]]:
            # get ratio of etfs
            if self.Time.hour == 9 and self.Time.minute == 35:
                self.spy_voo_ratio = self.Securities[self.symbols[0]].BidPrice / self.Securities[self.symbols[1]].AskPrice
                self.voo_spy_ratio = self.Securities[self.symbols[1]].BidPrice / self.Securities[self.symbols[0]].AskPrice
            if self.spy_voo_ratio is not None and self.voo_spy_ratio is not None and not self.Portfolio.Invested:
                # decide on trading direction
                self.trade_direction_flag = True \
                    if (self.Securities[self.symbols[0]].BidPrice / self.Securities[self.symbols[1]].AskPrice) >= self.spy_voo_ratio * self.spread_threshold \
                    else False \
                    if (self.Securities[self.symbols[1]].BidPrice / self.Securities[self.symbols[0]].AskPrice) >= self.voo_spy_ratio * self.spread_threshold \
                    else None
            
                # trade execution
                if self.trade_direction_flag is not None:
                    self.SetHoldings(self.symbols[0], (-1 if self.trade_direction_flag else 1) * 1)
                    self.SetHoldings(self.symbols[1], (-1 if self.trade_direction_flag else 1) * -1)
            # closing trade
            if self.Portfolio.Invested:
                if self.trade_direction_flag:
                    if (self.Securities[self.symbols[0]].BidPrice / self.Securities[self.symbols[1]].AskPrice) < self.spy_voo_ratio * self.spread_threshold:
                        self.Liquidate()
                        self.trade_direction_flag = None
                        self.spy_voo_ratio = None
                        self.voo_spy_ratio = None
                
                else:
                    if (self.Securities[self.symbols[1]].BidPrice / self.Securities[self.symbols[0]].AskPrice) < self.voo_spy_ratio * self.spread_threshold:
                        self.Liquidate()
                        self.trade_direction_flag = None
                        self.spy_voo_ratio = None
                        self.voo_spy_ratio = None
        # close before market close
        if self.Time.hour == 15 and self.Time.minute == 59 and self.Portfolio.Invested:
            self.Liquidate()
            self.trade_direction_flag = None
            self.spy_voo_ratio = None
            self.voo_spy_ratio = None


Leave a Reply

Discover more from Quant Buffet

Subscribe now to keep reading and get access to the full archive.

Continue reading