“投资池由纽约证券交易所(NYSE)、美国证券交易所(AMEX)和纳斯达克(NASDAQ)的股票组成。每年6月根据净派息收益率排名形成10个投资组合,买入最高收益率组合,持有一年后重新平衡。”
资产类别:股票 | 地区:美国 | 频率:每年 | 市场:股票 | 关键词:净派息收益率(NPY)
策略概述
该策略的投资范围包括纽约证券交易所、美国证券交易所和纳斯达克的股票。每年6月,依据净派息收益率(Net Payout Yield)对股票进行排名,并根据排名构建10个投资组合。净派息收益率定义为股息加上股票回购减去股票发行,然后除以年末的市值。我们使用基于现金流表的股票回购指标来计算净派息收益率。买入净派息收益率最高的组合,持有一年并在次年6月重新平衡。
策略合理性
传统上,股息被认为是衡量股票健康状况的可靠指标,但近期研究表明,需要更广泛的预测指标。过去,高股息收益率常被视为股票相对低估的信号,因为它表明公司向股东返还了更多现金。调整这一逻辑,净派息收益率考虑了多种现金分配方式,包括股息、股票回购和股票发行。研究显示,与股息收益率相比,总派息收益率与股票回报之间的关联更强。Fama-MacBeth回归分析表明,股息收益率与回报之间的关联并不显著,而派息收益率则具有显著关联。派息收益率在不同时间段内具有一致的预测能力,并且在样本外的测试中也表现出显著的可预测性。基于派息收益率的交易策略能够生成盈利的投资组合,这些组合通常具有负市场贝塔和规模因子负荷,表明其回报超出了传统风险度量指标的解释。
论文来源
On the Importance of Measuring Payout Yield: Implications for Empirical Asset Pricing [点击浏览原文]
- Jacob Boudoukh, 英国国家经济研究局
- Roni Michaely, 英国国家经济研究局
- Matthew Richardson, 英国国家经济研究局
- Michael Roberts, 英国国家经济研究局
<摘要>
之前的研究表明,股息价格比率在1980年代和1990年代发生了显著变化,而总派息比率(股息加回购与价格的比值)变化很小。我们探讨了这一差异对资产定价模型的影响。特别是,时间序列回归中股息对超额股票回报的预测能力下降的现象被大大夸大了。当使用总派息收益率而非股息收益率时,在短期和长期内均能发现统计上和经济上显著的可预测性。我们还提供了证据,表明总派息收益率在跨期预期股票回报方面的信息量超过了股息收益率,并且高低派息收益率组合是一个定价因子。


回测表现
| 年化收益率 | 22.13% |
| 波动率 | N/A |
| Beta | 0.885 |
| 夏普比率 | 0.355 |
| 索提诺比率 | 0.371 |
| 最大回撤 | 52.6% |
| 胜率 | 82% |
完整python代码
from AlgoLib import *
import numpy as np
class NetPayoutYieldEffect(XXX):
def Initialize(self):
self.SetStartDate(2000, 1, 1)
self.SetCash(100_000)
self.UniverseSettings.Leverage = 5
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.FundamentalFunction)
self.Settings.MinimumOrderMarginPortfolioPercentage = 0.0
# Fundamental Filter Parameters
self.exchange_codes = ['NYS', 'NAS', 'ASE']
self.fundamental_count = 500
self.quantile = 10
self.long_symbols = []
self.rebalancing_month = 6
self.selection_flag = True
self.exchange = self.AddEquity('SPY', Resolution.Daily).Symbol
self.Schedule.On(self.DateRules.MonthEnd(self.exchange),
self.TimeRules.AfterMarketOpen(self.exchange),
self.Selection)
def FundamentalFunction(self, fundamental):
if not self.selection_flag:
return Universe.Unchanged
filtered = [f for f in fundamental if f.HasFundamentalData
and f.SecurityReference.ExchangeId in self.exchange_codes
and not np.isnan(f.MarketCap)
and f.MarketCap !=0
and not np.isnan(f.ValuationRatios.TotalYield)
and f.ValuationRatios.TotalYield != 0
and not np.isnan(f.FinancialStatements.CashFlowStatement.CommonStockIssuance.TwelveMonths)
and f.FinancialStatements.CashFlowStatement.CommonStockIssuance.TwelveMonths != 0]
top_by_dollar_volume = sorted(filtered,
key=lambda x: x.DollarVolume,
reverse=True)[:self.fundamental_count]
payout_yield = lambda x: ((x.ValuationRatios.TotalYield * (x.MarketCap)) - \
(x.FinancialStatements.CashFlowStatement.CommonStockIssuance.TwelveMonths / (x.MarketCap)))
sorted_by_payout = sorted(top_by_dollar_volume, key = payout_yield, reverse=True)
if len(sorted_by_payout) >= self.quantile:
quantile = int(len(sorted_by_payout) / self.quantile)
self.long_symbols = [x.Symbol for x in sorted_by_payout[:quantile]]
return self.long_symbols
def OnData(self, slice):
if not self.selection_flag:
return
self.selection_flag = False
# Trade Execution
portfolio = [PortfolioTarget(symbol, 1 / len(self.long_symbols))
for symbol in self.long_symbols
if slice.ContainsKey(symbol) and slice[symbol] is not None]
self.SetHoldings(portfolio, True)
self.long_symbols.clear()
def Selection(self):
if self.Time.month == self.rebalancing_month:
self.selection_flag = True
def OnSecuritiesChanged(self, changes):
for security in changes.AddedSecurities:
security.SetFeeModel(CustomFeeModel())
# Custom fee model
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))
