4. 金融时序滚动统计特征提取

任务与数据

用一段模拟的日频收盘价序列,计算滚动窗口内的均值、标准差、偏度和峰度,拼成特征矩阵,供后续建模使用。滚动统计量能刻画局部趋势、波动率和分布形态,是金融时序特征工程的基础。

实现要点

  • 窗口 window=20,min_periods=window 保证每个统计量都由完整窗口计算,避免前 19 行出现不完整估计。
  • 均值/标准差用 pandas.rolling;偏度/峰度用 rolling().skew() 与 rolling().kurt()(Fisher 定义,正态为 0)。
  • 对价格先取对数收益率 log(price).diff(),再对收益率做滚动统计,量纲更稳定。
  • 丢弃含 NaN 的前 window 行,得到对齐的特征矩阵。

完整代码

import numpy as np
import pandas as pd

# 1. 构造模拟日频收盘价(几何随机游走)
rng = np.random.default_rng(42)
n = 200
ret = rng.normal(0.0005, 0.02, size=n)
price = 100 * np.exp(np.cumsum(ret))
dates = pd.date_range("2023-01-02", periods=n, freq="B")
df = pd.DataFrame({"close": price}, index=dates)

# 2. 对数收益率
df["log_ret"] = np.log(df["close"]).diff()

# 3. 滚动统计特征
w = 20
roll = df["log_ret"].rolling(window=w, min_periods=w)
feat = pd.DataFrame({
    "roll_mean": roll.mean(),
    "roll_std":  roll.std(),
    "roll_skew": roll.skew(),
    "roll_kurt": roll.kurt(),
})

# 4. 对齐并输出特征矩阵
features = feat.dropna()
print("特征矩阵形状:", features.shape)
print(features.head())
print("列名:", list(features.columns))

# 5. 简单验证:窗口内统计量与手工计算一致
i = w  # 第一个完整窗口结束位置
manual_mean = df["log_ret"].iloc[i - w + 1:i + 1].mean()
manual_std  = df["log_ret"].iloc[i - w + 1:i + 1].std()
assert np.isclose(features["roll_mean"].iloc[0], manual_mean)
assert np.isclose(features["roll_std"].iloc[0], manual_std)
print("验证通过:滚动均值/标准差与手工计算一致")

运行与验证

  1. 依赖:pip install numpy pandas,Python 3.9+。
  2. 运行脚本,观察输出形状应为 (180, 4)(200 行减去 1 行 diff 与 19 行窗口预热)。
  3. 通过条件:脚本无异常退出,且断言通过,说明滚动均值与标准差与手工窗口计算一致;偏度、峰度由 pandas 同一窗口机制产出,列名与顺序符合预期。

特征计算流程

收盘价序列 close (n=200) 对数收益率 log_ret rolling(w=20) mean / std skew / kurt min_periods=20 dropna 对齐 特征矩阵 (180, 4)

常见问题

  • 若 min_periods 小于 window,前几行统计量基于不完整窗口,会引入偏差,建模前需注意。
  • 偏度/峰度对异常值敏感,可先对收益率做缩尾(winsorize)再滚动。
  • 若需多窗口(如 5/20/60),对同一 log_ret 循环不同 w 并横向拼接即可。