“该策略交易11种商品期货,通过买入过去的隔夜输家和卖出赢家来创建零投资组合,每日重新平衡,并调整权重以管理过度波动。”

I. 策略概要

该策略针对11种商品期货,包括玉米、CBOT乙醇、瘦猪、活牛等,选择流动性最强的合约。通过买入前一天的隔夜输家和卖出隔夜赢家,构建零投资组合。商品权重使用学术论文中的公式确定,使用计算权重的1/2来估计投资组合的回报和波动率,因为全部权重表明波动率过高(每年60%)。投资组合每日重新平衡,交易在形成期后的下一个开盘至收盘期间执行。此方法旨在利用商品中的短期价格波动。

II. 策略合理性

价格反转通常是由于投资者对新闻反应过度,随后出现价格修正。像隔夜或周末休息这样的市场休市,通常流动性和交易活动较低。Nagel(2012)的研究表明,以VIX指数衡量的不确定性在解释收盘至开盘(CO-OC)反转策略的利润方面起着重要作用。这种效应在期货市场尤为明显。CO-OC反转模式与Hong和Wang(2000)的连续时间模型一致,表明市场休市期间的对冲需求促成了这些反转,从而产生了可预测的盈利机会。

III. 来源论文

市场休市和短期反转 [点击查看论文]

<摘要>

一种买入过去隔夜回报较低的证券,卖出过去隔夜回报较高的证券的策略,在所有主要资产类别中都能产生可观的样本外日内回报和夏普比率。这种被称为隔夜-日内反转的策略,其平均回报大约是传统反转策略产生的回报的二到五倍。投资者异质性、情绪、市场不确定性和全市场流动性不足都无法解释这种隔夜-日内反转回报。我们的发现与资产类别特定的做市商流动性供给机制一致,我们发现横截面回报分散可以预测每个资产类别的策略回报。一个由市场和隔夜-日内反转因子组成的全球双因子模型,很好地解释了跨资产类别的多元化投资组合的日内回报变化。

IV. 回测表现

年化回报45.75%
波动率31.02%
β值-0.012
夏普比率1.47
索提诺比率-2.897
最大回撤N/A
胜率44%

V. 完整的 Python 代码

from AlgorithmImports import *
#endregion
class OvernightIntradayDailyReversalinCommodities(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2015, 1, 1)
        self.SetCash(1000000)
        symbols:List[str] = [
            Futures.Grains.Corn,
            Futures.Meats.LeanHogs,
            Futures.Meats.LiveCattle,
            Futures.Forestry.Lumber,
            Futures.Grains.Oats,
            Futures.Grains.SoybeanMeal,
            Futures.Grains.Soybeans,
            Futures.Grains.Wheat,
        ]
        self.traded_percentage:float = 0.2
        self.futures:List[Symbol] = []
        self.recent_close:Dict[Symbol, float] = {}
        
        for symbol in symbols:
            future = self.AddFuture(symbol, Resolution.Minute, dataNormalizationMode=DataNormalizationMode.BackwardsRatio, contractDepthOffset=0)
            self.futures.append(future.Symbol)
        self.day_close_flag:bool = False
        self.day_open_flag:bool = False
        self.Schedule.On(self.DateRules.EveryDay(self.futures[3]), self.TimeRules.BeforeMarketClose(self.futures[3], 1), self.DayClose)
        self.Schedule.On(self.DateRules.EveryDay(self.futures[3]), self.TimeRules.AfterMarketOpen(self.futures[3], 1), self.DayOpen)
    def OnData(self, data: Slice) -> None:
        if self.day_open_flag:
            self.day_open_flag = False
            returns:Dict[Symbol, float] = { symbol : data[symbol].Open / self.recent_close[symbol] - 1 for symbol in self.futures if symbol in self.recent_close and symbol in data and data[symbol] }
            self.recent_close.clear()
            long:List[Symbol] = [x[0] for x in returns.items() if x[1] < 0]
            short:List[Symbol] = [x[0] for x in returns.items() if x[1] > 0]
            
            for i, portfolio in enumerate([long, short]):
                for symbol in portfolio:
                    # notional value = asset price * contract multiplier
                    notional_value:float = (self.Securities[symbol].Price * self.Securities[symbol].SymbolProperties.ContractMultiplier)
                    quantity:int = int((((-1) ** i) * self.Portfolio.TotalPortfolioValue) / len(portfolio) * self.traded_percentage) // notional_value
                    self.MarketOrder(self.Securities[symbol].Mapped, quantity)
        if self.day_close_flag:
            self.day_close_flag = False
            self.Liquidate()
            self.recent_close = { symbol : data[symbol].Close for symbol in self.futures if symbol in data and data[symbol] }
    def DayClose(self) -> None:
        self.day_close_flag = True
    
    def DayOpen(self) -> None:
        self.day_open_flag = True

发表评论

了解 Quant Buffet 的更多信息

立即订阅以继续阅读并访问完整档案。

继续阅读