
该策略在风险较高的月份(波动率最高五分之一)之后,做多小盘股(或ETF),做空大盘股,持有投资组合六个月,并根据波动率每月重新平衡。
资产类别: ETF、股票 | 地区: 美国 | 周期: 每月 | 市场: 股票 | 关键词: 择时、小盘股
I. 策略概要
投资范围包括小盘股和大盘股。该投资组合可以通过投资ETF(做多小盘股,做空大盘股)或直接投资小盘股和大盘股来构建。高风险月份被定义为预期市场波动率与历史波动率相比位于最高五分之一的月份。在这种月份之后,投资者做多小盘股,做空大盘股,并持有该投资组合6个月。每月检查基于月度波动率相对于历史波动率的择时指标,以相应地调整投资组合。
II. 策略合理性
股票规模溢价存在正向的风险回报关系,这归因于高风险时期,在这些时期,小盘股面临相对较高的市场波动率、违约和流动性风险。时变的SMB溢价似乎可以为评估股票市场中的跨期风险回报权衡提供有用的依据。
III. 来源论文
Realized Semibetas: Signs of Things to Come [点击查看论文]
- 戈登·S·朗商学院与经济学院,圭尔夫大学,加拿大圭尔夫 – 经济与金融系。美联储理事会
<摘要>
我们提出了一种将传统市场贝塔分解为四个半贝塔的新方法,这四个半贝塔取决于市场和个别资产回报之间的有符号协变。与均值-半方差框架的定价含义一致,我们表明,由负市场和负(正)资产回报协变定义的较高半贝塔预测显著较高(较低)的未来回报,而其他两个半贝塔似乎没有定价。结果对于一系列替代测试规范和附加控制变量都是稳健的。我们得出的结论是,与其押注或反对贝塔,不如押注和反对“正确的”半贝塔。


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