如果你正在探索量化投资领域,一定会遇到一个关键问题:如何从原始的市场数据中提取有效的特征?传统方法可能需要依赖TA-Lib这样的技术指标库,或者手动编写复杂的计算逻辑。而Qlib提供的表达式引擎,彻底改变了这个局面。
我第一次接触Qlib时,最让我惊喜的是它统一了特征计算和标签生成的流程。想象一下,你不再需要为每个指标单独编写代码,而是可以用类似Excel公式的方式,直接表达复杂的计算逻辑。比如计算5日动量,只需要写$close/Ref($close,5)-1,系统就会自动处理所有股票、所有时间点的计算。
这种设计带来的最大优势是可扩展性。在机器学习场景中,我们经常需要同时计算上百个特征。如果用传统方法,光是管理这些指标的代码就会成为噩梦。而Qlib的表达式系统让这一切变得异常简单——你只需要维护一个清晰的表达式列表,剩下的工作都交给框架处理。
Qlib的表达式语法非常直观。基本运算符包括加减乘除(+, -, *, /),逻辑比较(>, <, ==等),以及各种数学函数。举个例子,计算当日振幅可以写成:
python复制amplitude = '($high - $low)/$close'
更强大的是内置的时间序列函数。比如计算20日移动平均:
python复制ma20 = 'Mean($close, 20)'
这些表达式可以直接用在QlibDataLoader中:
python复制from qlib.data.dataset.loader import QlibDataLoader
fields = ['$close', '($high - $low)/$close', 'Mean($close, 20)']
names = ['close', 'amplitude', 'ma20']
data_loader_config = {
"feature": (fields, names),
"label": (['Ref($close, -2)/Ref($close, -1) - 1'], ['label'])
}
data_loader = QlibDataLoader(config=data_loader_config)
df = data_loader.load(instruments='csi300', start_time='2020-01-01', end_time='2021-12-31')
让我们看一个更复杂的例子——布林带指标。传统实现可能需要几十行代码,而在Qlib中只需要一个表达式:
python复制upper_band = 'Mean($close, 20) + 2 * Std($close, 20)'
lower_band = 'Mean($close, 20) - 2 * Std($close, 20)'
band_width = '(Mean($close, 20) + 2 * Std($close, 20)) - (Mean($close, 20) - 2 * Std($close, 20))'
MACD指标同样简洁:
python复制macd = '(EMA($close, 12) - EMA($close, 26))/$close - EMA((EMA($close, 12) - EMA($close, 26))/$close, 9)/$close'
这种表达方式不仅节省代码量,更重要的是提高了可读性和可维护性。当需要调整指标参数时,你只需要修改表达式字符串,而不需要重构整个计算流程。
Qlib内置了两个重要的因子库:Alpha158和Alpha360。这些数字代表因子数量,它们覆盖了动量、波动率、流动性、市值等多个维度。虽然可以直接使用这些预定义因子,但理解它们的构造方法更重要。
以动量因子为例,Alpha158中可能包含:
python复制mom5 = '$close/Ref($close, 5) - 1' # 5日动量
mom10 = '$close/Ref($close, 10) - 1' # 10日动量
波动率因子可能这样计算:
python复制vol20 = 'Std($close, 20)/Mean($close, 20)' # 20日波动率
开发自己的Alpha因子时,建议遵循以下流程:
比如,我们可以设计一个结合动量和成交量的复合因子:
python复制mom_vol = '($close/Ref($close, 10)-1) * Mean($volume, 10)/Std($volume, 10)'
这个因子同时考虑了价格变化和成交量变化,可能捕捉到资金推动的行情。
当处理全市场数据时,计算效率变得至关重要。Qlib提供了几种优化手段:
可以通过以下方式检查计算性能:
python复制import time
start = time.time()
df = data_loader.load(instruments='all', start_time='2010-01-01', end_time='2020-12-31')
print(f"计算耗时: {time.time()-start:.2f}秒")
计算好的特征可以保存到磁盘,避免重复计算:
python复制from qlib.data import D
D.save_instruments(instruments, './saved_features')
下次使用时直接加载:
python复制instruments = D.instruments('./saved_features')
这种机制特别适合需要反复实验的场景,可以节省大量计算资源。
在量化研究中,标签定义同样重要。Qlib允许在同一个框架下定义特征和标签。对于A股市场,考虑到T+1交易机制,常用的标签定义是:
python复制label = 'Ref($close, -2)/Ref($close, -1) - 1' # T+2收益率
这表示在T日收盘后生成信号,T+1日买入,T+2日卖出。
在定义特征和标签时要特别注意时间关系。一个常见错误是使用了未来数据,比如:
python复制# 错误的定义 - 使用了未来数据
wrong_feature = '$close/Ref($close, -1)' # 用到了明天的收盘价
# 正确的定义
correct_feature = '$close/Ref($close, 1)' # 用昨天的收盘价
Qlib的Ref函数中,正数表示过去,负数表示未来,这点需要特别注意。
让我们通过一个完整案例,演示如何构建一个包含10个因子的自定义因子库:
python复制factor_definitions = {
'mom5': '$close/Ref($close, 5) - 1',
'mom10': '$close/Ref($close, 10) - 1',
'vol20': 'Std($close, 20)/Mean($close, 20)',
'volume_ratio': '$volume/Mean($volume, 20)',
'amplitude': '($high - $low)/$close',
'close_position': '($close - $low)/($high - $low)',
'ma_cross': 'Mean($close, 5)/Mean($close, 20) - 1',
'bollinger_width': '(Mean($close, 20) + 2*Std($close, 20)) - (Mean($close, 20) - 2*Std($close, 20))',
'rsi14': '100 - 100/(1 + Mean(Greater($close - Ref($close, 1), 0), 14)/Mean(Abs($close - Ref($close, 1)), 14))',
'price_volume_corr': 'Corr($close, $volume, 10)'
}
data_loader_config = {
"feature": (list(factor_definitions.values()), list(factor_definitions.keys())),
"label": (['Ref($close, -2)/Ref($close, -1) - 1'], ['label'])
}
这个因子库涵盖了动量、波动率、成交量、技术形态等多个维度,可以作为机器学习模型的输入特征。
在实际项目中,我发现因子间的相关性是需要特别注意的问题。建议定期检查因子相关性矩阵,移除高度相关的因子。可以使用Qlib结合其他Python数据分析工具来完成这项工作:
python复制import seaborn as sns
corr_matrix = df.corr()
sns.heatmap(corr_matrix, annot=True)
有时我们需要基于现有特征计算更高阶的特征。Qlib支持这种链式计算:
python复制# 先计算基础特征
base_features = {
'mom5': '$close/Ref($close, 5) - 1',
'vol20': 'Std($close, 20)/Mean($close, 20)'
}
# 然后计算组合特征
composite_features = {
'mom_vol_ratio': '$mom5/$vol20',
'rank_mom': 'Rank($mom5, 10)'
}
在量化研究中,经常需要对因子进行行业中性化处理。这也可以在Qlib中实现:
python复制# 假设我们已经有了行业分类数据
industry_neutral_mom = '$mom5 - Mean($mom5, sector)'
不同特征的量纲可能差异很大,需要进行标准化:
python复制standardized_mom = '($mom5 - Mean($mom5, 20))/Std($mom5, 20)'
这种标准化处理对许多机器学习算法非常重要。
在使用Qlib表达式引擎时,可能会遇到各种问题。以下是一些常见情况及解决方法:
$close)在基础数据中存在调试时可以先用单只股票测试:
python复制test_df = data_loader.load(instruments=['600519'], start_time='2020-01-01', end_time='2020-12-31')
print(test_df)
这样可以快速验证表达式的正确性。