
本文详解如何使用 Pandas 的 resample + rolling.max 组合,为每个日历月末生成覆盖未来 1、2、3 个月窗口的累计收益率最大值,解决原始代码中高阶周期列出现大量 NaN 的问题。
本文详解如何使用 pandas 的 `resample` + `rolling.max` 组合,为每个日历月末生成覆盖未来 1、2、3 个月窗口的累计收益率最大值,解决原始代码中高阶周期列出现大量 nan 的问题。
在金融时间序列分析中,常需计算“截至当月月末、向后回溯 N 个月”的累计收益最大值(例如:2023-02-28 对应的 2M cummreturns 应表示 2023-02 至 2023-03 这两个月内累计收益的最大值)。原始实现中直接对不同频率('2M'/'3M')分别重采样,会导致对齐错位与数据稀疏——因为 '2M' 重采样以双月为边界(如 2023-01-31、2023-03-31…),无法为每个日历月末提供有效值。
正确解法是 两步分离策略:
- 先统一按月重采样(resample('1M')),得到每个日历月末的单月累计收益最大值;
- 再在此基础上进行滚动窗口计算(rolling(N).max()),使每一行代表“包含当前月及前 N−1 个月”的滑动窗口最大值。
该方法确保:
- 所有列共享完全一致的时间索引(即每个日历月末一行);
- 2M cummreturns 表示「当前月 + 上一月」共两个月内的最大累计收益;
- 3M cummreturns 表示「当前月 + 上两月」共三个月内的最大累计收益;
- 无缺失值(min_periods=1 保证首行即可计算)。
以下是完整可运行示例代码:
import yfinance as yf
import numpy as np
import pandas as pd
# 获取 SPY 数据(2023 年起)
df = yf.download('SPY', '2023-01-01')
df = df[['Close']]
df['d_returns'] = np.log(df['Close'] / df['Close'].shift(1)) # 更清晰的对数收益率计算
df.dropna(inplace=True)
# 步骤 1:计算累计对数收益 → 转为累计收益率(exp)
cum_ret_series = (df['d_returns'].cumsum()).apply(np.exp)
# 步骤 2:按日历月末重采样,取每月最大累计收益率
monthly_max = cum_ret_series.resample('1M').max()
# 步骤 3:基于月度序列,滚动计算 1M/2M/3M 最大值
N = 3
result_df = pd.DataFrame({
f'{i+1}M cummreturns': monthly_max.rolling(window=i+1, min_periods=1).max()
for i in range(N)
})
print(result_df)✅ 输出结果中每行均含完整三列数值,且语义明确:
- 2023-03-31 行的 2M cummreturns = 1.094428 → 是 2023-02-28 与 2023-03-31 两月中累计收益的最大值;
- 2023-04-30 行的 3M cummreturns = 1.094428 → 是 2023-02-28、2023-03-31、2023-04-30 三个月中的最大值。
⚠️ 注意事项:
- resample('1M') 默认以月末为标签,若需月初或特定工作日,可改用 'MS'(Month Start)或配合 offset 参数;
- rolling(..., min_periods=1) 是关键——它允许首期窗口(仅 1 个值)也输出结果,避免开头 NaN;
- 若原始数据存在非交易日空缺,resample 会自动填充缺失月份(值为 NaN),建议前置检查 monthly_max.isna().sum() 并酌情插值或前向填充;
- 此逻辑天然支持任意 N(如扩展至 6M、12M),只需调整 range(N) 和列名生成逻辑。
总结而言,resample + rolling 的组合替代了错误的多频次独立重采样,既保持时间维度对齐,又精准表达了“滚动 N 个月最大值”的业务含义,是金融时序特征工程中的典型范式。










