前向/远期收益通常用于因子挖掘、回测系统或机器学习预测模型的标签生成环节。
前向/远期收益必须与因子值在时间上严格错位,否则会造成未来数据泄漏。
这里尝试示例前向/远期收益的计算,以及背后隐藏的金融原理。
1 前向收益
1.1 前向收益
前向收益即远期收益表(Forward Returns),行为日期(t),列为股票代码(i)。
单元格为对应日期买入后、持有N日的未来收益(如t+1日收盘价/t日收盘价 - 1)。
需要注意的是
远期收益必须与因子值在时间上严格错位,
比如因子值用t日信息,收益用 t+1 或 t+5 日数据,
否则会造成未来数据泄漏。
1.2 代码示例
以下compute_forward_returns是前向收益的经典计算实现。
输入参数说明如下:
1)prices
要求为 DataFrame,索引(Index)为时间戳(Datetime),列(Columns)为股票代码(Ticker)。
这是量化研究中最标准的宽表(Panel Data)结构。
2)horizon
持有期,默认设为 20,通常指 20个交易日,约等于 1 个自然月。
返回参数说明如下:
形状与原DataFrame一致的收益率矩阵。
需要注意的是,最后 horizon 行数据会变成 NaN,因为未来价格不存在。
在实际使用前通常需要 .dropna()。
# ──────────────────────────────────────────────────────────
# Forward returns
# ──────────────────────────────────────────────────────────
def compute_forward_returns(
prices: pd.DataFrame,
horizon: int = 20,
) -> pd.DataFrame:
"""
Compute forward returns (t+1 to t+horizon).
Returns
-------
DataFrame (datetime x stock) of forward returns.
"""
return prices.shift(-horizon) / prices - 1
1.3 核心计算
前向收益的核心计算为:
prices.shift(-horizon) / prices - 1
1)shift(-horizon)
shift(-horizon)将价格数据整体向上平移N行。
这意味在时间点t,我们能取到未来t+horizon时刻的价格。
2) 除法与减一
计算的是简单收益率(Simple Return),即。
2 背后知识
2.1 持有期收益率
持有期收益率(Holding Period Return, HPR)
这是衡量投资在特定时间段内总回报的最基本指标。
代码计算的是非年化、绝对的区间收益。
如果 horizon=20,它代表的是未来一个月的绝对涨跌幅。
2.2 时间维度设定
时间维度设定,20 = 交易月
金融学实证研究表明,比如Fama-French 三因子模型,
月度频率(20~22个交易日)是去除微观市场噪音与捕捉中期动量效应的经典周期。
相比于日频数据,月频数据更能反映基本面信息。
2.3 前向 vs 后向
从前向(forward-looking)与后向(Backward-looking)的区别角度看:
这里代码计算的是前向收益,Forward Returns,即站在当下看未来,对应重要金融实务原则:
在实盘交易或建模时,前向收益绝对不能在信号产生之前执行,否则会引入前视偏差
从量化建模角度:
这行代码生成的forward_returns通常是因变量(Y/Label),
而prices中的历史截面数据(如 PE、PB、动量)则是自变量(X/Features)。
2.4 价格类型的隐含假设
从调整价 vs 原始价的角度看,代码直接使用了prices,应该是调整价。
在专业金融计算中,这里的prices必须是后复权价格(Adjusted Close Price),而非原始收盘价。
股票分红(Dividend)、送转股(Stock Split)会导致价格出现非市场性的缺口(Ex-Dividend Drop)。
如果使用未复权价格计算,会严重低估实际收益。
比如,分红1元,股价除息下跌1元,但投资者账户里多出了现金。
因此,复权价格保证了收益率连续性,使得计算出收益率等同于持有并再投资的总收益近似值。
2.5 简单收益率 vs 对数收益率
代码使用了简单收益率。
在截面(Cross-section)比较中,比如比较100只股票下个月谁涨得最多,
简单收益率具有可加性,即组合收益率是个股权重的加权平均。
缺点是时间不可加,如果要计算2个月的收益,需要做 (1+R1)(1+R2)-1,而非直接相加。
若想计算对数收益率,应改为
np.log(prices.shift(-horizon) / prices)
但这样会丧失截面加权的便利性。
reference
alphalens
https://github.com/quantopian/alphalens
RQFactor用户指南
https://www.ricequant.com/doc/rqfactor/manual/index-rqfactor
多因子模型中如何计算信息系数IC的时间序列