基于股票回购的市场中性策略
登录后收藏学术论文
策略概要
该策略针对在纽约证券交易所、纳斯达克和美国证券交易所上市的股票,市值超过2亿美元,股价超过5美元。每天,投资者形成一个等权重的投资组合,其中包括在过去三个月内宣布股票回购的公司。为了对冲该投资组合,投资者根据投资组合层面的贝塔值做空iShares Russell 2000 ETF (IWM),贝塔值使用最近250天的滚动窗口计算。该策略旨在利用股票回购公告获利,同时通过使用IWM的动态对冲来管理市场风险。
II. 策略合理性
学术研究表明,股票回购与公司目前被低估的预期一致。有强有力的证据表明,公司能够盈利性地回购股票,特别是当公司被大量不成熟的散户投资者持有的时候。
回测表现
波动率7.21%
夏普比率1.34
胜率58%
完整 Python 代码
from QuantConnect.Data.Custom.SmartInsider import *
class MarketNeutralStrategyBasedShareBuybacks(QCAlgorithm):
def Initialize(self):
# SmartInsider data starts in 2015 for most stocks.
self.SetStartDate(2015, 1, 1)
self.SetCash(100000)
data = self.AddEquity('IWM', Resolution.Daily)
data.SetLeverage(10)
self.symbol = data.Symbol
self.course_count = 500
self.last_course = []
self.period = 3 * 30
self.long = []
self.selection_flag = False
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.CoarseSelectionFunction)
# Weekly rebalance.
self.Schedule.On(self.DateRules.Every(DayOfWeek.Friday), self.TimeRules.BeforeMarketClose(self.symbol), self.Selection)
def OnSecuritiesChanged(self, changes):
for security in changes.AddedSecurities:
security.SetFeeModel(CustomFeeModel(self))
security.SetLeverage(10)
def CoarseSelectionFunction(self, coarse):
if not self.selection_flag:
return Universe.Unchanged
selected = sorted([x for x in coarse if x.HasFundamentalData and x.Market == 'usa' and x.Price > 5],
key=lambda x: x.DollarVolume, reverse=True)
self.last_course = [x.Symbol for x in selected[:self.course_count]]
return self.last_course
def OnData(self, data):
if not self.selection_flag:
return
self.selection_flag = False
for symbol in self.last_course:
# Add smart insider data.
smart_insider_symbol = self.AddData(SmartInsiderTransaction, symbol, Resolution.Daily).Symbol
# NOTE:
# v.1 - Iterate over last transactions. There's a weird "lag" between actual date and last buyback date.
# - faster, not so precise I guess.
transactions = self.Securities[symbol].Data.GetAll(SmartInsiderTransaction)
if any(x.BuybackDate >= self.Time - timedelta(days = self.period) for x in transactions):
self.long.append(symbol)
# v.2 Get buyback history.
# - slow due to History() call, more precise.
# history = self.History(SmartInsiderTransaction, smart_insider_symbol, 60, Resolution.Daily)
# if not history.empty:
# self.long.append(symbol)
# Trade execution
count = len(self.long)
stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested]
for symbol in stocks_invested:
if symbol not in self.long:
self.Liquidate(symbol)
for symbol in self.long:
self.SetHoldings(symbol, 1 / count)
# Hedge with IWM with no leverage.
self.SetHoldings(self.symbol, -1)
self.long.clear()
def Selection(self):
self.selection_flag = True
# Custom fee model
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))