4、特征工程(下):基本面因子、另类数据与特征标准化

好,咱们接着聊特征工程的下半场。

上一章我们把技术指标、量价关系这些“盘面内”的东西讲透了。但说实话,做量化选股,光看K线是不够的。你想想看,一只股票突然涨停,可能是因为财报超预期,也可能是因为某个行业政策突然出台。这些信息,K线里可不会直接告诉你。

所以这一章,我们得把视野拉宽——基本面因子、另类数据,还有那个绕不开的特征标准化。我个人习惯把这三块合在一起讲,因为它们本质上都是在解决同一个问题:如何让模型“看到”更真实的市场

4.1 基本面因子:从财报里挖金子

基本面因子,说白了就是公司“家底”的数字化表达。PE、PB、ROE这些,大家应该都不陌生。但我要说的是,直接用原始值扔进模型,效果往往很差

为什么?因为行业不同,估值体系完全不同。一个银行股的PE是5倍,一个科技股的PE是50倍,你能说银行股更便宜吗?不能。所以我们需要做行业中性化处理

核心思路:将原始因子值减去行业均值,再除以行业标准差。这样得到的“行业相对值”,才是模型真正需要的。

举个例子,我曾在项目中处理过A股的PE数据。直接拿PE做因子,模型选出来的全是银行和钢铁。做了行业中性化之后,才真正捕捉到那些“行业内被低估”的标的。

这里给出一个简单的Python实现:

import pandas as pd
import numpy as np

def industry_neutralize(df, factor_col, industry_col):
    """
    行业中性化处理
    df: DataFrame,包含因子值和行业标签
    factor_col: 因子列名
    industry_col: 行业列名
    """
    # 按行业分组,计算均值和标准差
    grouped = df.groupby(industry_col)[factor_col]
    mean = grouped.transform('mean')
    std = grouped.transform('std')
    
    # 中性化
    df[factor_col + '_neutral'] = (df[factor_col] - mean) / std
    return df

小提示:对于市值因子,我建议做对数变换后再中性化。因为市值分布是典型的右偏分布,取对数后更接近正态分布,模型更容易学习。

4.2 另类数据:非结构化信息的结构化之路

另类数据,这个词听起来高大上,其实没那么神秘。简单说,就是传统财报和行情数据之外的一切信息

比如:

  • 新闻舆情:上市公司公告、财经新闻、社交媒体讨论
  • 供应链数据:上下游企业的开工率、物流数据
  • 卫星图像:停车场车辆数、农田作物长势
  • 搜索指数:百度指数、微信指数

嗯,这里要注意。另类数据虽然信息量大,但噪声也大。我曾经试过直接用新闻情感得分作为因子,结果模型跑出来的信号全是噪音——因为很多新闻标题是“标题党”,情感得分和实际影响完全脱节。

那怎么办?我的经验是:先聚合,再降维

比如新闻数据,不要用单条新闻的情感得分,而是用过去N天的情感得分均值、标准差、极值。这样既能平滑噪声,又能保留趋势信息。

避坑指南:我曾经踩过一个坑——把百度指数直接当因子用。结果发现,搜索量高的股票往往是“问题股”(比如暴雷后大家去搜)。后来我改用“搜索量变化率”和“搜索量/成交量”的比值,效果才好起来。

另类数据的处理流程,我画了一张图,方便你理解:

另类数据特征工程流程 原始另类数据 新闻/搜索/卫星 数据清洗 去重/去噪/对齐 时序聚合 均值/标准差/极值 降维 PCA/自编码器 特征融合 与基本面因子、技术因子拼接 模型输入特征

4.3 特征标准化:让模型“看懂”不同尺度的数据

好,数据都准备好了。但有个问题——PE可能是5到50,成交量可能是100万到10亿,新闻情感得分是-1到1。这些数字的量级完全不一样,直接扔进模型,量级大的特征会主导梯度更新

所以,标准化是必须的。

常用的方法有三种:

方法 公式 适用场景
Z-score标准化 (x - μ) / σ 数据近似正态分布时效果最好。我常用于基本面因子。
Min-Max归一化 (x - min) / (max - min) 数据有明确上下界时使用。比如情感得分[-1,1]。
Rank归一化 排序后映射到[0,1] 数据有异常值时首选。我强烈推荐用于市值、成交量这类长尾分布的数据。

我的个人习惯:对于深度学习模型,我几乎只用Rank归一化。为什么?因为金融数据几乎没有严格服从正态分布的,而且异常值特别多。Rank归一化把数据变成均匀分布,既保留了相对顺序,又消除了异常值的影响。你想想看,一个极端值在Z-score下可能变成10个标准差,但在Rank下只是变成了1.0而已。

代码实现也很简单:

from scipy.stats import rankdata

def rank_normalize(series):
    """
    Rank归一化:将数据映射到[0,1]区间
    """
    ranks = rankdata(series)
    return (ranks - 1) / (len(series) - 1)

# 使用示例
df['volume_rank'] = rank_normalize(df['volume'])

小技巧:标准化时,一定要在训练集上计算参数,再应用到验证集和测试集。否则会造成数据泄露。我见过有人对整个数据集做标准化,然后做回测,结果回测收益高得离谱——其实就是用了未来信息。

4.4 实战中的几个坑

讲到这里,我想分享几个实战中遇到的坑,希望能帮你少走弯路。

  1. 基本面因子的滞后性:财报数据通常有1-3个月的滞后。我建议做对齐处理——比如用2023年Q3的财报数据,对应的是2023年10月到12月的交易数据。千万别用未来数据去预测过去。
  2. 另类数据的“幸存者偏差”:比如新闻数据,只有被报道的公司才有数据。那些没被报道的公司怎么办?我的做法是填充中性值(比如情感得分填0,搜索量填最小值)。
  3. 标准化后的可解释性:标准化之后,特征的含义变了。比如Z-score后的PE,不再是“市盈率”,而是“市盈率偏离行业均值多少个标准差”。这一点在向业务方解释模型时特别重要。

避坑指南:我曾经犯过一个低级错误——对离散型特征(比如行业代码)也做了标准化。结果模型完全学不到行业信息。记住:类别特征用One-Hot或Embedding,数值特征才做标准化

好了,这一章的内容就到这里。基本面因子、另类数据、特征标准化,这三块是深度学习选股模型的“基础设施”。基础打好了,后面的模型才能跑得稳。


专注资料整理