该策略在风险较高的月份(波动率最高五分之一)之后,做多小盘股(或ETF),做空大盘股,持有投资组合六个月,并根据波动率每月重新平衡。

I. 策略概要

投资范围包括小盘股和大盘股。该投资组合可以通过投资ETF(做多小盘股,做空大盘股)或直接投资小盘股和大盘股来构建。高风险月份被定义为预期市场波动率与历史波动率相比位于最高五分之一的月份。在这种月份之后,投资者做多小盘股,做空大盘股,并持有该投资组合6个月。每月检查基于月度波动率相对于历史波动率的择时指标,以相应地调整投资组合。

II. 策略合理性

股票规模溢价存在正向的风险回报关系,这归因于高风险时期,在这些时期,小盘股面临相对较高的市场波动率、违约和流动性风险。时变的SMB溢价似乎可以为评估股票市场中的跨期风险回报权衡提供有用的依据。

III. 来源论文

Realized Semibetas: Signs of Things to Come [点击查看论文]

<摘要>

我们提出了一种将传统市场贝塔分解为四个半贝塔的新方法,这四个半贝塔取决于市场和个别资产回报之间的有符号协变。与均值-半方差框架的定价含义一致,我们表明,由负市场和负(正)资产回报协变定义的较高半贝塔预测显著较高(较低)的未来回报,而其他两个半贝塔似乎没有定价。结果对于一系列替代测试规范和附加控制变量都是稳健的。我们得出的结论是,与其押注或反对贝塔,不如押注和反对“正确的”半贝塔。

IV. 回测表现

年化回报5.42%
波动率7.81%
β值0.094
夏普比率0.69
索提诺比率-0.095
最大回撤N/A
胜率52%

V. 完整的 Python 代码

import numpy as np
from AlgorithmImports import *

class TimingtheSmallCapEffect(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2000, 1, 1)
        self.SetCash(100000)

        self.period = 21
        self.SetWarmUp(self.period, Resolution.DAILY)
        
        self.market = self.AddEquity('SPY', Resolution.Daily).Symbol
        self.data = RollingWindow[float](self.period)   # spy history
        self.historical_volatility = []
        self.min_vol_history_period = 12
        self.was_high_risk_month = False
        
        self.trade_month_count = 0

        data = self.AddEquity("DIA", Resolution.Daily)
        data.SetLeverage(5)
        self.large_cap = data.Symbol
        
        data = self.AddEquity("IWM", Resolution.Daily)
        data.SetLeverage(5)
        self.small_cap = data.Symbol
        
        self.Schedule.On(self.DateRules.MonthEnd(self.market), self.TimeRules.BeforeMarketClose(self.market), self.Rebalance)
    
    def OnData(self, data):
        # store market prices
        if self.market in data and data[self.market]:
            price = data[self.market].Value
            self.data.Add(price)
        
    def Rebalance(self):
        if self.IsWarmingUp: return
        if not self.data.IsReady: return

        if self.time.year == 2023 and self.time.month == 8:
            foo=3

        self.trade_month_count += 1
        if self.trade_month_count == 6:
            self.trade_month_count = 0
            self.Liquidate()
    
        if self.was_high_risk_month:
            self.was_high_risk_month = False
            self.trade_month_count = 0

            # One month after high risk month.
            self.SetHoldings(self.small_cap, 1)
            self.SetHoldings(self.large_cap, -1)            
        
        market_prices = np.array([x for x in self.data])
        market_returns = market_prices[:-1] / market_prices[1:] - 1
        market_volatility = np.std(market_returns) * np.sqrt(252)
        
        self.historical_volatility.append(market_volatility)
        if len(self.historical_volatility) >= self.min_vol_history_period:
            top_quintile = np.percentile(self.historical_volatility[1:], 80)
            
            if market_volatility > top_quintile:
                # one month lag
                self.was_high_risk_month = True

发表评论

了解 Quant Buffet 的更多信息

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

继续阅读