第 4 章 · 01 特征工程


文档摘要

第 4 章 · 01 特征工程 本节摘要:本节是「Alpha 因子研究与特征工程」的开篇方法论章,讲怎么把原始 OHLCV 与基本面数据系统化地变成「能喂给 ML 模型的特征」。原项目 用 Quandl 美股 2000-2018 年的日频数据,演示了一套完整的特征工程流水线:从月度收益序列的构造、动量因子的计算、滚动 Fama-French 因子暴露、滞后收益作为输入、到持有期收益作为预测目标,再到 IPO 年限(年龄)、市值等横截面特征,最后用 one-hot 处理分类变量并存盘供后续章节复用。读完本节,你能独立构造一套完整的「特征 + 标签」数据集,作为后续任何监督模型的输入。 内容来源:原项目 ,汉化并套用体系化模板。

第 4 章 · 01 特征工程

本节摘要:本节是「Alpha 因子研究与特征工程」的开篇方法论章,讲怎么把原始 OHLCV 与基本面数据系统化地变成「能喂给 ML 模型的特征」。原项目 04_alpha_factor_research/01_feature_engineering.ipynb 用 Quandl 美股 2000-2018 年的日频数据,演示了一套完整的特征工程流水线:从月度收益序列的构造、动量因子的计算、滚动 Fama-French 因子暴露、滞后收益作为输入、到持有期收益作为预测目标,再到 IPO 年限(年龄)、市值等横截面特征,最后用 one-hot 处理分类变量并存盘供后续章节复用。读完本节,你能独立构造一套完整的「特征 + 标签」数据集,作为后续任何监督模型的输入。

内容来源:原项目 04_alpha_factor_research/01_feature_engineering.ipynb,汉化并套用体系化模板。

⚠️ 风险提示:特征工程是数据泄露的高发区——任何用到「未来」信息的变换都会污染回测;务必严守「当前时刻只用历史」,所有滚动/位移操作都要显式避免未来函数。

学习目标

阅读完本节,你应当能够:

  1. 说清特征工程在 ML4T 闭环中的位置与重要性。
  2. pandasrolling/shift/pct_change 构造月度收益与动量
  3. pyfinance.PandasRollingOLS 计算滚动 Fama-French 因子暴露
  4. 设计滞后收益(特征)持有期收益(标签) 的对齐方式。
  5. 构造横截面特征(市值、IPO 年龄)并做分桶。
  6. 处理分类变量的 one-hot 编码,并把成品存盘复用。

一、为什么特征工程是瓶颈

原书第 4 章 README 开宗明义:「如果你熟悉 ML,就知道特征工程是成功预测的关键;在交易领域至少同样重要。」(feature engineering is a key ingredient for successful predictions. It matters at least as much in the trading domain.)

金融机器学习的瓶颈几乎从来不是模型,而是特征。模型再先进,输入是垃圾就只能输出垃圾。本节的目标是把第 2~3 章拿到的原始数据(价格、财报、元数据)加工成一张「**行=日期×标的,列=特征**」的整洁表(称为 feature_engineering.ipynb 的输出),后续第 7 章线性模型、第 9 章树模型都直接用它。

二、数据加载与对齐

notebook 一开始从 assets.h5(第 2 章存储基准推荐的方式)读取 Quandl 2000-2018 年的复权收盘价与股票元数据:

DATA_STORE = '../data/assets.h5' with pd.HDFStore(DATA_STORE) as store: prices = (store['quandl/wiki/prices'] .loc[idx['2000':'2018', :], 'adj_close'] .unstack('ticker')) stocks = store['us_equities/stocks'].loc[:, ['marketcap', 'ipoyear', 'sector']]

要点:

  • pd.IndexSlice 对 MultiIndex 做切片。
  • 价格 unstack 成宽表(行=日期,列=ticker),适合后续按列算指标。
  • 元数据含 marketcap(市值)、ipoyear(上市年)、sector(行业),后两个是横截面特征。

💡 核心心法:先把数据整理成「日期 × 标的」的整齐宽表或长表,后续所有特征工程都在这张表上做。混乱的索引会让每一步变换都痛苦。

三、月度收益与动量

日频数据噪声大、训练慢,notebook 把它重采样为月频:

monthly_prices = prices.resample('M').last()

然后计算不同周期的历史收益作为动量因子:

# 不同回看周期的收益率 for t in [1, 2, 3, 6, 9, 12]: out[out.columns] = monthly_prices.pct_change(t)

关键后处理:

  1. 去极值(Winsorize):用 .clip() 把 1% 与 99% 分位数之外的极端值截断,防离群点主导。
  2. 几何平均化:把多期收益转成等价月度复利。
  3. 去缺失:剔除历史不足 10 年的股票,最终 notebook 保留了 1670 只股票。

动量因子的常见构造方式:用 3~12 月历史收益之差,或近期月收益与长期均值之差。

⚠️ 警告:pct_change(n) 看的是「过去 n 期」,这是合法的历史信息;但如果你误用 pct_change(-n)(未来 n 期),就是数据泄露。所有 shift/pct_change 都要确认方向。

四、滚动 Fama-French 因子暴露

Fama-French 五因子(市场、规模、价值、盈利、投资)是经典的风险解释因子。把股票收益对这五因子做滚动回归,得到的 β 就是该股票随时间变化的因子暴露,可作为特征:

from pyfinance.ols import PandasRollingOLS factor_data = web.DataReader('F-F_Research_Data_5_Factors_2x3', 'famafrench', start='2000')[0].drop('RF', axis=1) factor_data.index = factor_data.index.to_timestamp() # 对每只股票做滚动 24 个月 OLS betas = PandasRollingOLS(window=24, min_periods=12).fit( y=monthly_returns, x=factor_data)

缺失的 β(回归窗口不足)用均值填充,避免丢样本。

五、特征与标签的对齐

这是本节最关键也最容易出错的部分:

角色 怎么构造 含义
特征(滞后收益) 历史 .shift(+n) 把过去收益对齐到当前 t 时刻「能用」的信息
标签(持有期收益) 未来收益 .shift(-h) 对齐到当前 t 时刻入场、持有 h 期的结果

💡 核心心法:同一行 DataFrame 里,特征必须是 t 之前已发生的信息,标签是 t 之后才发生的结果。任何违反这条规则的行都构成数据泄露。shift(+n) 用于特征,shift(-h) 用于标签,符号别搞反。

六、横截面特征:年龄与市值

notebook 还构造了两类不随时间剧烈变化的横截面特征:

  1. 公司年龄:用 IPO 年份的分位数作为代理。
  2. 动态市值:用 NASDAQ 元数据的 marketcap,但要按历史价格调整,使其反映历史时点市值(而非当前市值),最后按月分十进制桶(deciles)。
# 按月分十进制桶,避免未来市值泄露 size = ... # 调整后的历史市值 size_decile = size.groupby('date').apply(lambda x: pd.qcut(x, 10, labels=False, duplicates='drop'))

⚠️ 警告:市值与当前价格绑定,直接用会泄露未来价格信息。必须构造调整因子让历史市值反映当时价格水平,这是常被忽略的泄露点。

七、分类变量与存盘

最后,对 sector 这类分类变量做 one-hot 编码:

dummy_sectors = pd.get_dummies(stocks['sector'], prefix='sector', drop_first=True)

整理完后存盘,供第 7 章起复用:

with pd.HDFStore(DATA_STORE) as store: store['engineered_features'] = data

本节要点回顾

  1. 特征工程是瓶颈:模型再好,输入垃圾也徒劳;先把数据整理成「日期×标的」整齐表。
  2. 月度收益+动量:重采样到月频,Winsorize 去极值,剔短历史。
  3. 滚动 Fama-French β:用 PandasRollingOLS 算随时间变化的风险暴露。
  4. 特征 vs 标签:特征用 shift(+n)(过去),标签用 shift(-h)(未来),符号别搞反。
  5. 横截面特征:IPO 年龄、动态市值(必须按历史价格调整避免泄露),按月分桶。
  6. one-hot + 存盘:分类变量 one-hot,结果存 HDF5 供后续章节复用。
  7. 泄露高发区:每一步都要问「这个值在 t 时刻能拿到吗?」

下一节,我们看 TA-Lib 技术指标——除了用 pandas 手搓特征,还可以用 TA-Lib 库直接调几十种经典技术指标。


发布者: 作者: 灏天文库 转发
评论区 (0)
U