4. 因子与特征构建
在Qlib的组合策略与回测流程中,因子与特征构建是决定模型上限的关键环节。无论你使用LightGBM、线性模型还是深度网络,模型看到的输入都是特征矩阵。特征的质量、覆盖度和表达力,直接决定了策略能否捕捉到有效的市场规律。本节将系统讲解量价因子、基本面因子的构建逻辑,Alpha158与Alpha360因子库的使用方式,以及如何自定义因子并接入Qlib的数据管线。
一、核心原理:因子是市场行为的数值化表达
因子的本质,是把原始行情数据(OHLCV、财务报表、交易状态等)通过数学变换,压缩成能够刻画未来收益差异的数值信号。一个完整的因子构建流程通常包含四个层次:
- 数据层:原始行情、财务、行业分类等基础数据,Qlib通过
DataHandler统一管理。 - 变换层:对原始数据做差分、比率、滚动统计、排序等运算,生成因子表达式。
- 标准化层:截面标准化(如Z-Score、Rank)或时序标准化,消除量纲与市场整体波动影响。
- 特征层:将多个因子拼接为特征矩阵,供模型训练与预测使用。
量价因子关注价格与成交量的统计规律,例如动量、反转、波动率、换手率、量价背离等。基本面因子则从财务数据出发,如市盈率、市净率、ROE、营收增速、资产负债率等。两类因子的共同点是:它们都试图用可计算的数值,描述股票未来收益的横截面差异。
关键认知:因子不是越多越好。冗余因子会引入噪声、增加过拟合风险。Qlib的Alpha158和Alpha360之所以被广泛使用,正是因为它们在因子数量与信息覆盖之间做了工程化的平衡。
二、Alpha158与Alpha360因子库的使用
Qlib内置了两套经典因子库:Alpha158和Alpha360。Alpha158包含158个因子,主要基于日频量价数据,涵盖K线形态、滚动统计、量价关系等;Alpha360则包含360个因子,更侧重多周期、多窗口的时序特征,适合深度学习模型。
在Qlib中使用它们非常简单,只需在配置文件中指定handler类名:
# 使用Alpha158因子库
from qlib.contrib.data.handler import Alpha158
handler_conf = {
"class": "Alpha158",
"module_path": "qlib.contrib.data.handler",
"kwargs": {
"start_time": "2018-01-01",
"end_time": "2022-12-31",
"fit_start_time": "2018-01-01",
"fit_end_time": "2020-12-31",
"instruments": "csi300",
}
}
# 使用Alpha360因子库
from qlib.contrib.data.handler import Alpha360
handler_conf_360 = {
"class": "Alpha360",
"module_path": "qlib.contrib.data.handler",
"kwargs": {
"start_time": "2018-01-01",
"end_time": "2022-12-31",
"fit_start_time": "2018-01-01",
"fit_end_time": "2020-12-31",
"instruments": "csi300",
}
}
配置完成后,Qlib会自动完成因子计算、缺失值处理、标准化和特征拼接。你可以通过handler.fetch()查看生成的DataFrame,列名即为因子名称。Alpha158的因子命名通常带有窗口参数,如ROC5、MA10、STD20等,便于追溯。
| 因子库 | 因子数量 | 主要类型 | 适用场景 |
|---|---|---|---|
| Alpha158 | 158 | K线形态、滚动统计、量价关系 | 树模型、线性模型、快速基线 |
| Alpha360 | 360 | 多周期时序、价格成交量组合 | 深度学习、端到端训练 |
| 自定义因子 | 按需 | 基本面、另类数据、行业中性 | 特定策略增强、因子挖掘 |
三、自定义因子的构建与接入
当内置因子库无法满足策略需求时,需要自定义因子。Qlib提供了灵活的扩展机制,核心是继承DataHandler或使用Expression表达式引擎。以下是一个自定义量价因子的示例:计算20日收益率与20日成交量变化率的比值,用于捕捉量价配合关系。
from qlib.data.dataset.handler import DataHandlerLP
from qlib.data.dataset.processor import DropnaLabel
from qlib.data import D
# 方式一:使用表达式引擎定义因子
fields = [
"Ref($close, -1) / $close - 1", # 未来一日收益率(标签)
"($close - Ref($close, 20)) / Ref($close, 20)", # 20日动量
"($volume - Ref($volume, 20)) / Ref($volume, 20)", # 20日量变
"($close - Ref($close, 20)) / Ref($close, 20) / (($volume - Ref($volume, 20)) / Ref($volume, 20) + 1e-8)", # 量价比值
]
names = ["LABEL", "MOM20", "VOL20", "PV_RATIO"]
# 方式二:继承DataHandlerLP实现自定义处理器
class MyHandler(DataHandlerLP):
def __init__(self, instruments="csi300", start_time=None, end_time=None, **kwargs):
fields = ["$close", "$volume", "$high", "$low"]
names = ["close", "volume", "high", "low"]
super().__init__(
instruments=instruments,
start_time=start_time,
end_time=end_time,
data_loader=..., # 指定数据加载器
infer_processors=[...], # 推理阶段处理器
learn_processors=[DropnaLabel()], # 学习阶段处理器
**kwargs
)
实践建议:自定义因子时,优先使用Qlib的表达式引擎,因为它能自动处理时间对齐、停牌、涨跌停等细节。只有在表达式无法表达复杂逻辑时,才考虑继承DataHandler编写Python代码。
四、常见误区与复盘
在因子构建过程中,初学者容易陷入几个典型误区:
- 未来函数:使用
Ref($close, -1)作为特征而非标签,导致回测虚高。务必确认特征只使用当前及历史数据。 - 标准化泄露:在训练集和测试集上分别做标准化,或使用全样本统计量。正确做法是在训练集上拟合标准化参数,再应用到测试集。
- 因子冗余:盲目堆砌相似因子,如同时加入5日、10日、20日动量,导致多重共线性。建议先做相关性分析,保留信息增量最大的因子。
- 忽略行业与市值中性化:基本面因子往往带有行业和市值暴露,直接使用可能引入风格偏差。Qlib支持在处理器中加入中性化步骤。
复盘要点:因子构建不是一次性工作。建议建立因子评估流水线,定期计算IC、ICIR、换手率、分层收益等指标。只有经过严格样本外验证的因子,才值得进入组合策略。
总结来说,量价因子和基本面因子各有适用场景,Alpha158与Alpha360提供了开箱即用的基线特征,而自定义因子则是策略差异化的来源。掌握Qlib的表达式引擎与DataHandler扩展机制,能够让你在因子构建环节既高效又可控。下一阶段,我们将把这些特征输入模型,观察它们如何转化为预测信号。