“在FOMC会议期间,投资者在会议前一天收盘时买入股票(如标普500 ETF、基金、期货或差价合约),并在会议结束后卖出。其余时间资金则持有为现金。由于该策略的股票市场敞口极低(平均每年仅8天),因此可轻松加杠杆以获取显著回报。”
资产类别:差价合约、ETF、基金、期货 | 地区:全球 | 频率:每日 | 市场:股票 | 关键词:美联储
策略概述
在FOMC会议期间,投资者在会议前一天的收盘时买入股票(标普500 ETF、基金、期货或差价合约),并在会议结束后收盘时卖出。在剩余时间里,投资者将资金持有为现金。由于该策略对股票市场的敞口非常低(平均每年仅8天),因此可以轻松加杠杆以获取显著回报。
策略合理性
FOMC会议通常对股票市场有积极影响。美联储的主要目的是解决银行危机、维持金融体系稳定、遏制金融市场的系统性风险,并促进经济增长。因此,FOMC会议的结论很少会对股票产生高度负面的影响,这是产生正向漂移的主要原因。
论文来源
The Pre-FOMC Announcement Drift [点击浏览原文]
- David O. Lucca, 美国纽约联邦储备银行
- Emanuel Moench, 德国法兰克福金融管理学院;德国经济政策研究中心(CEPR)
<摘要>
我们记录了过去几十年中,在美联储公开市场委员会(FOMC)会议前对美国股票的平均超额回报。这些FOMC公告前的回报随着时间的推移有所增加,且占据了年度股票总回报的一大部分。虽然其他主要国际股票指数也经历了类似的FOMC公告前回报,但我们发现在美国国债和货币市场期货中没有类似效应。其他主要的美国宏观经济新闻公告也没有引发公告前的超额股票回报。我们还发现,当国债收益率曲线斜率较低、股票市场隐含波动率较高以及过去FOMC公告前回报较高时,公告前回报更为显著。我们讨论了这些回报在标准资产定价理论中所面临的解释挑战。


回测表现
| 年化收益率 | 6.19% |
| 波动率 | N/A |
| Beta | 0.036 |
| 夏普比率 | -0.274 |
| 索提诺比率 | -0.091 |
| 最大回撤 | 11.9% |
| 胜率 | 55% |
完整python代码
from AlgoLib import *
from pandas.tseries.offsets import BDay
from datetime import datetime
class FederalOpenMarketCommitteeMeetingEffectinStocks(XXX):
def Initialize(self) -> None:
self.SetStartDate(2000, 1, 1)
self.SetCash(100000)
self.market:Symbol = self.AddEquity("SPY", Resolution.Minute).Symbol
self.fed_days_symbol:Symbol = self.AddData(FedDays, 'fed_days', Resolution.Daily, TimeZones.NewYork).Symbol
self.SetWarmUp(1, Resolution.Daily)
FedDays.set_algo(self)
self.recent_day:int = -1
def OnData(self, data: Slice) -> None:
if self.IsWarmingUp: return
if self.fed_days_symbol in data and data[self.fed_days_symbol]:
self.Log(f"New FOMC meeting data arrived: {self.Time}; submitting an MOC order...")
# new fed day data arrived
quantity:float = self.CalculateOrderQuantity(self.market, 1.)
self.MarketOnCloseOrder(self.market, quantity)
self.recent_day = self.Time.day
else:
# other new minute resolution data arrived
if self.Portfolio[self.market].Invested:
if self.Time.day != self.recent_day:
self.recent_day = self.Time.day
self.Log(f"FOMC meeting day; submitting an MOC order to close opened position...")
self.MarketOnCloseOrder(self.market, -self.Portfolio[self.market].Quantity)
class FedDays(PythonData):
algo = None
@staticmethod
def set_algo(algo):
FedDays.algo = algo
def GetSource(self, config:SubscriptionDataConfig, date:datetime, isLiveMode:bool) -> SubscriptionDataSource:
if isLiveMode:
# FedDays.algo.Log(f"Edited GetSource date {FedDays.algo.Time}")
return SubscriptionDataSource("https://data.quantpedia.com/backtesting_data/economic/fed_days.json", SubscriptionTransportMedium.RemoteFile, FileFormat.UnfoldingCollection)
return SubscriptionDataSource("https://data.quantpedia.com/backtesting_data/economic/fed_days.csv", SubscriptionTransportMedium.RemoteFile)
def Reader(self, config:SubscriptionDataConfig, line:str, date:datetime, isLiveMode:bool) -> BaseData:
if isLiveMode:
try:
# FedDays.algo.Log(f"Reader")
objects = []
data = json.loads(line)
end_time = None
for index, sample in enumerate(data):
custom_data = FedDays()
custom_data.Symbol = config.Symbol
custom_data.Time = (datetime.strptime(str(sample["fed_date"]), "%Y-%m-%d") - BDay(1)).replace(hour=9, minute=31)
# FedDays.algo.Log(f"{custom_data.Time}")
end_time = custom_data.Time
objects.append(custom_data)
return BaseDataCollection(end_time, config.Symbol, objects)
except ValueError:
# FedDays.algo.Log(f"Reader Error")
return None
else:
if not (line.strip() and line[0].isdigit()):
return None
custom = FedDays()
custom.Symbol = config.Symbol
custom.Time = (datetime.strptime(line, "%Y-%m-%d") - BDay(1)).replace(hour=9, minute=31)
custom.Value = 0.
custom["fed_date_str"] = line
return custom
