3. 因子构建基础:数据获取与清洗、因子计算逻辑、因子标准化处理

各位同学好,我是老张。今天咱们聊聊因子构建的底层功夫。说实话,很多做量化的人一上来就搞复杂模型,结果数据源就有问题,那后面全是白搭。我个人习惯,花在数据清洗上的时间,至少占整个因子开发周期的40%。

核心观点:因子构建不是写代码,是处理脏数据。数据干净了,因子就成功了一半。

3.1 数据获取与清洗——地基不牢,地动山摇

数据获取这块,我踩过的坑太多了。早期我用过某免费数据源,结果某天突然发现因子回测收益高得离谱——后来一查,是数据里混入了未来信息。嗯,这教训值十万块。

我个人常用的数据源有几种:

  • Wind / Choice:机构常用,数据质量高,但贵
  • Tushare / AKShare:开源免费,适合个人研究
  • 自建数据库:大团队必备,我习惯用InfluxDB存时序数据

数据清洗这块,我总结了一个「三查」原则:

  1. 查缺失:停牌、退市、新股,这些都会导致数据缺失。我一般用前向填充,但要注意——如果连续缺失超过5天,直接剔除。
  2. 查异常:比如某股票一天涨了1000%,这明显是数据错误。我习惯用3倍标准差法做截断。
  3. 查对齐:不同股票的交易日期可能不一样,必须统一对齐到交易日历。

我的小技巧:数据清洗完,一定要做一次「反向验证」。比如拿复权价格算一下收益率,跟原始收益率对比,差太多就是有问题。

3.2 因子计算逻辑——从原始数据到有效信号

因子计算,说白了就是把原始数据变成有预测能力的信号。我见过太多人直接拿原始指标当因子,比如直接用「市盈率」——这其实不对,因为不同行业的市盈率天然不同,你得做行业中性化处理。

举个例子,咱们算一个经典的「动量因子」:

import pandas as pd
import numpy as np

def calc_momentum_factor(price_df, window=20):
    """
    计算动量因子:过去N日累计收益率
    """
    # 计算每日收益率
    ret = price_df.pct_change()
    
    # 滚动求和
    momentum = ret.rolling(window=window).sum()
    
    # 剔除最近1日,避免微观结构噪声
    momentum = momentum.shift(1)
    
    return momentum

这里有个细节要注意——为什么要shift(1)?因为我们要避免未来信息。我曾经犯过这个错,回测时因子跟当天收益率相关,结果实盘一塌糊涂。

常见的因子类型我列了个表:

因子类型 典型因子 计算逻辑 注意事项
动量因子 过去N日收益率 滚动求和/求平均 需剔除最近1日
价值因子 BP、EP 账面价值/市值 需做行业中性化
质量因子 ROE、毛利率 财务指标计算 注意财报发布日期
波动率因子 过去N日波动率 收益率标准差 建议用对数收益率

避坑指南:我曾经在计算波动率因子时,直接用简单收益率算标准差。后来发现,对数收益率才是正确的选择——因为对数收益率具有可加性,而且更符合正态分布假设。

3.3 因子标准化处理——让不同因子站在同一起跑线

因子标准化,说白了就是把不同量纲的因子拉到同一个尺度上。你想想看,动量因子的值可能是-0.5到0.5,而市盈率可能是10到100,直接比较肯定不行。

我常用的标准化方法有三种:

  • Z-score标准化:减去均值,除以标准差。这是最常用的方法,但要注意——如果因子分布有厚尾,效果会打折扣。
  • 排序标准化:把因子值排序,然后映射到0-1之间。这个方法对异常值不敏感,我比较推荐。
  • 分位数标准化:把因子值映射到标准正态分布的分位数上。这个方法能保证因子分布接近正态,但计算量稍大。

代码实现如下:

def standardize_factor(factor_df, method='zscore'):
    """
    因子标准化处理
    """
    if method == 'zscore':
        # Z-score标准化
        mean = factor_df.mean()
        std = factor_df.std()
        standardized = (factor_df - mean) / std
        
    elif method == 'rank':
        # 排序标准化
        rank = factor_df.rank()
        standardized = rank / len(factor_df)
        
    elif method == 'quantile':
        # 分位数标准化
        from scipy.stats import norm
        rank = factor_df.rank()
        standardized = norm.ppf(rank / (len(factor_df) + 1))
    
    # 截断极端值
    standardized = standardized.clip(-3, 3)
    
    return standardized

我的经验:标准化之后一定要做截断。我一般截断到±3倍标准差,这样能避免极端值对后续组合构建的干扰。记住,因子极端值往往不是信号,而是噪声。

3.4 本章知识体系总览

下面这张图是我自己画的,把本章的核心逻辑串起来了。你仔细看看,数据清洗、因子计算、标准化处理,这三步是环环相扣的。

因子构建核心流程 数据获取与清洗 缺失值处理 · 异常值截断 因子计算逻辑 动量 · 价值 · 质量 · 波动率 因子标准化处理 Z-score · 排序 · 分位数 关键细节: • 数据清洗:前向填充缺失值,3倍标准差截断异常值 • 因子计算:注意避免未来信息(shift操作),行业中性化处理 • 标准化:截断到±3倍标准差,防止极端值干扰 三者缺一不可,任何一个环节出问题,因子质量都会大打折扣

嗯,以上就是本章的全部内容。数据清洗、因子计算、标准化处理,这三步看起来简单,但真正做好需要大量实践。我建议你拿真实数据跑一遍,遇到问题再回来翻翻这节课。

专注资料整理