Quant Buffet放轻松,别过度思虑

股指期货中的短期反转现象

登录后收藏

学术论文

Contrarian Long-Short Futures Arbitrages and Market Efficiency: Evidence in the Index Futures Markets around the Globe

作者新加坡南洋理工大学(Nanyang Business School

机构
  • ?美国南卫理公会大学(Southern Methodist University)金融学杰出教授
  • ?NTU)金融学副教授
  • ?德克萨斯大学圣安东尼奥分校(University of Texas at San Antonio)商业杰出主席
论文摘要

本文分析了在过去E天中卖出赢家并买入输家后持有H天的逆向多空期货套利,在1992-2002年期间的全球39个指数期货市场中展开研究。结果表明,{5,5}多空期货套利的标准化超额利润在除美国指数期货市场外的所有市场中均具有统计显著性。这些套利在9月现货月的周四/周五尤为显著,主要由回报反转和熊市条件驱动。因此,我们的研究表明,多空期货套利在大多数指数期货市场中可能仍然具有盈利能力。

策略概要

该策略针对欧洲七个国家的股指期货,包括德国、法国、意大利、西班牙、荷兰、比利时和奥地利,仅交易即将到期的最近月合约。根据过去五天的收益表现,将国家分为赢家(收益高于欧洲等权基准)和输家(收益低于基准)。策略内容为做多表现较差的输家并做空表现优异的赢家,仓位权重与其偏离基准的幅度成正比(偏离幅度越大,权重越高)。投资组合每周根据更新的表现进行再平衡,旨在利用欧盟股指期货市场中的均值回归趋势。

策略合理性

短期逆向投资组合利用了过去赢家表现不佳、过去输家表现优异的趋势,从而产生超越基准的超额收益。这种行为符合过度反应假说,即交易者对新闻的调整超出基本面支持的合理范围。这些短期反转模式具有持续性,即使在高效市场中也为基于回报的策略提供了机会。通过利用这些规律,投资者可以从短期价格波动的可预测性中获利,抓住由过度反应及其后续修正驱动的市场低效性带来的机会。

回测表现

年化收益29.6%
贝塔0.03
索提诺比率-0.001
胜率49%

完整 Python 代码

import numpy as np
from AlgorithmImports import *
class ShortTermReversal(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2000, 1, 1)
self.SetCash(100000)

self.symbols = ['EWG', 'EWQ', 'EWI', 'EWP', 'EWN', 'EWK', 'EWO']

# Daily price data.
self.data = {}
self.period = 5
self.SetWarmUp(self.period)

for symbol in self.symbols:
    data = self.AddEquity(symbol, Resolution.Daily)
    data.SetFeeModel(CustomFeeModel())
    data.SetLeverage(5)
    
    self.data[symbol] = RollingWindow[float](self.period)

self.Schedule.On(self.DateRules.Every(DayOfWeek.Thursday), self.TimeRules.AfterMarketOpen(self.symbols[0]), self.Rebalance)
def OnData(self, data):
for symbol in self.data:
    symbol_obj = self.Symbol(symbol)
    if symbol_obj in data.Keys:
        if data[symbol_obj]:
            price = data[symbol_obj].Value
            if price != 0:
                self.data[symbol].Add(price)
def Rebalance(self):
self.Liquidate()

symbol_return = {}
for symbol in self.symbols:
    if self.data[symbol].IsReady: 
        if self.Securities[symbol].GetLastData() and (self.Time.date() - self.Securities[symbol].GetLastData().Time.date()).days < 5:
            closes = [x for x in self.data[symbol]]
            symbol_return[symbol] = closes[0] / closes[-1] - 1

if len(symbol_return) != 0:
    avg_ret = np.average([x[1] for x in symbol_return.items()])
    
    # Average return weighting.
    return_diff = {x[0] : abs(x[1] - avg_ret) for x in symbol_return.items()}

    total_diff = sum(x[1] for x in return_diff.items())
    weight_ratio = float(1 / total_diff)

    # Trade execution.
    winners = [x[0] for x in return_diff.items() if symbol_return[x[0]] > avg_ret]
    losers = [x[0] for x in return_diff.items() if symbol_return[x[0]] < avg_ret]
    
    for symbol in winners + losers:
        weight = return_diff[symbol] * weight_ratio
        if symbol in winners:
            self.SetHoldings(symbol, -weight)
        elif symbol in losers:
            self.SetHoldings(symbol, weight)

# Custom fee model
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))