
“该策略涉及交易CRSP价值加权指数,在FOMC扩张性决策后做多,在收缩性决策后做空,并在公告发布后持有头寸15天。”
资产类别: 差价合约、ETFs、期货 | 地区: 美国 | 周期: 每日 | 市场: 股票 | 关键词: FOMC、动量
I. 策略概要
该投资范围包括CRSP价值加权指数,该指数包含在纽约证券交易所、美国证券交易所或纳斯达克交易的所有普通股。该策略涉及对FOMC公告做出反应:在扩张性政策决定后做多股票,或在收缩性政策决定后做空股票。头寸在公告发布后持有15天。
II. 策略合理性
动量是一种在许多资产类别中观察到的持续异常现象,包括股票指数。这项研究确定了围绕美国联邦公开市场委员会(FOMC)决策的时间序列动量策略。这种效应是市场范围和全球性的,延伸到各个行业和主要股票指数。该策略的功能性可能由不确定性驱动,不确定性可以用VIX指数来衡量。在平静的市场中,当VIX低于历史水平时,FOMC会议前会有一个小的漂移。然而,在高不确定性时期(当VIX较高时),扩张性政策决定后的漂移从3%增加到4.65%,表明动量效应更强。
III. 来源论文
Monetary Momentum [点击查看论文]
- 安德烈亚斯·诺伊希尔(Andreas Neuhierl)和迈克尔·韦伯(Michael Weber)
<摘要>
我们记录了联邦公开市场委员会(FOMC)货币政策公告前后的大幅回报漂移。股票回报在扩张性货币政策意外公布前25天开始上涨,而在收缩性意外公布前则下跌。扩张性政策决策和收缩性政策决策之间的累积回报差异在政策决策日之前达到2.5%,并在会议后15天继续增加到4.5%以上。这种漂移在高不确定性时期更为明显,它是一种市场范围的现象,并且存在于所有行业和许多国际股票市场。标准回报因子和时间序列动量并不能涵盖FOMC政策决策周围的回报漂移。一个利用FOMC会议周围漂移的简单交易策略将夏普比率相对于买入并持有投资提高了4倍。FOMC会议前的累积回报显著预测了随后的政策意外。


IV. 回测表现
| 年化回报 | 13.02% |
| 波动率 | 10.56% |
| β值 | 0.388 |
| 夏普比率 | 0.57 |
| 索提诺比率 | N/A |
| 最大回撤 | N/A |
| 胜率 | 50% |
V. 完整的 Python 代码
from AlgorithmImports import *
class MonetaryFOMCMomentuminStocks(QCAlgorithm):
def Initialize(self):
self.SetStartDate(1998, 1, 1)
self.SetCash(100000)
data = self.AddEquity("SPY", Resolution.Minute)
data.SetFeeModel(CustomFeeModel())
self.symbol = data.Symbol
self.fed_funds = self.AddData(FederalFundsTargetRate, 'FederalFundsTargetRate', Resolution.Daily).Symbol
# import quandl federal rate data
self.target_rate = self.AddData(QuandlValue, 'FRED/DFEDTARL', Resolution.Daily).Symbol
# Federal funds target rate history.
self.rate_history = RollingWindow[float](2)
self.days_to_liquidate = -1
self.Schedule.On(self.DateRules.EveryDay(self.symbol), self.TimeRules.AfterMarketOpen(self.symbol, 1), self.SPYLiquidate)
def OnData(self, data):
# Until 2009 we are taking data from Quantpedia,
# then we take data from Quandl
if self.fed_funds in data and data[self.fed_funds]:
rate = data[self.fed_funds].Value
self.rate_history.Add(rate)
self.Trade() # Trade spy
elif self.target_rate in data and data[self.target_rate] and self.Time.year > 2008:
rate = data[self.target_rate].Value
self.rate_history.Add(rate)
self.Trade() # Trade spy
def Trade(self):
if self.rate_history.IsReady:
rates = [x for x in self.rate_history]
previous_rate = rates[-1]
current_rate = rates[0]
if current_rate > previous_rate: # contractionary policy decision
self.SetHoldings(self.symbol, -1)
elif current_rate < previous_rate: # expansionary policy decision
self.SetHoldings(self.symbol, 1)
self.days_to_liquidate = 15
def SPYLiquidate(self):
if self.Portfolio[self.symbol].Invested:
self.days_to_liquidate -= 1
if self.days_to_liquidate == 0:
self.Liquidate(self.symbol)
# Quandl "value" data
class QuandlValue(PythonQuandl):
def __init__(self):
self.ValueColumnName = 'Value'
# Custom fee model.
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00001
return OrderFee(CashAmount(fee, "USD"))
class FederalFundsTargetRate(PythonData):
def GetSource(self, config, date, isLiveMode):
return SubscriptionDataSource("data.quantpedia.com/backtesting_data/economic/federal_funds_target_rate_history.csv", SubscriptionTransportMedium.RemoteFile, FileFormat.Csv)
def Reader(self, config, line, date, isLiveMode):
data = FederalFundsTargetRate()
data.Symbol = config.Symbol
if not line[0].isdigit(): return None
split = line.split(';')
data.Time = datetime.strptime(split[0], "%Y-%m-%d") + timedelta(days=1)
data['rate'] = float(split[1])
data.Value = float(split[1])
return data