Qlib实战复盘:从零构建AI量化策略的完整迭代历程

去年夏天,当我第一次在GitHub上发现微软开源的Qlib平台时,就像找到了量化研究者的"瑞士军刀"。这个号称能统一机器学习与量化投资工作流的工具,究竟能否经得起真实市场的考验?我决定用三个月时间,从零开始构建一个基于价量因子的中频交易策略,并完整记录下这个跌宕起伏的过程。

1. 策略雏形:构建第一个可回测原型

在金融城一家对冲基金担任量化研究员五年后,我形成了自己筛选alpha因子的一套方法论。这次选择Qlib,就是想验证传统量化思维与AI结合能产生怎样的化学反应。第一个版本策略的核心逻辑很简单:

  • 预测目标:未来5日个股超额收益(相对于沪深300指数)
  • 特征工程:37个基础价量因子,包括:
    # 典型因子示例
    factors = {
        'momentum_5': 'Ref($close, -5)/$close - 1',  # 5日动量
        'volatility_20': 'Std($close, 20)',          # 20日波动率
        'volume_ma_ratio': '$volume/Mean($volume, 20)', # 成交量偏离度
        'high_low_spread': '($high-$low)/$close',    # 日内波动幅度
    }
    
  • 模型选择:Qlib内置的LightGBM,这是考虑到:
    • 树模型对金融数据非线性关系的捕捉能力
    • 训练速度远超深度学习模型
    • 特征重要性可解释性强

初始配置直接套用了Qlib示例中的TopKDropoutStrategy,选择每日预测得分最高的30只股票等权持有。当第一次看到回测曲线时,心情就像过山车——2018-2020年测试集年化收益21%,但最大回撤达到惊人的38%。这组矛盾的数字背后,暴露了几个关键问题:

策略诊断:初始回测的三大致命伤

  1. 换手率过高(日均5.2%),交易成本侵蚀大部分收益
  2. 因子在牛市表现优异但熊市完全失效
  3. 部分小市值股票流动性不足造成滑点失真

2. 数据层深度优化:从粗糙到精细

意识到原始因子的局限性后,我着手重建数据管道。Qlib的DataHandler模块提供了极好的扩展性,以下是关键的改进点:

2.1 因子库升级

在原始37个因子基础上,新增了三类因子:

  1. 市场状态感知因子
    • 市场波动率分位数(20日滚动)
    • 行业相对强弱指数
  2. 流动性修正因子
    'liq_adjusted_momentum': '''
        IF($volume/Mean($volume,20)>1.5, 
           Ref($close,-5)/$close-1, 
           Null)
    '''
    
  3. 事件驱动因子
    • 突破20日最高价
    • 成交量突增倍数

2.2 数据预处理增强

通过修改infer_processors配置,增加了:

infer_processors:
  - class: RobustZScoreNorm
    kwargs:
      fields_group: feature
      clip_threshold: 3  # 严控异常值
  - class: CSFillna      # 行业中性化填充
    kwargs:
      fields_group: feature
      fill_method: sector_median

2.3 标签工程创新

原始的未来5日收益率标签存在明显未来函数风险。改进方案:

  • 采用滞后执行机制:预测T+1日信号,实际在T+2日开盘执行
  • 引入动态止损标签:当收益达到2σ波动区间时提前了结

这些改进使数据质量显著提升,在相同模型下夏普比率从1.2提升至1.8。

3. 模型训练的艺术:从默认参数到精细调优

Qlib虽然提供了开箱即用的LightGBM接口,但默认参数在实盘中往往表现平平。经过六轮调优,最终确定的超参数组合如下:

参数初始值优化值调整依据
num_leaves31127金融数据复杂度高
min_data_in_leaf20100防止过拟合
feature_fraction1.00.7增强多样性
bagging_freq05提升稳定性
lambda_l100.3控制权重稀疏性

更关键的是引入了动态样本权重机制:

class CustomizedLightGBM(LightGBMModel):
    def get_sample_weights(self, dataset):
        # 给波动率适中的样本更高权重
        prices = dataset.get_label()
        volatility = prices.rolling(5).std()
        weights = np.exp(-(volatility - 0.02)**2/0.005)
        return weights.values

这个改进使模型在2020年3月疫情波动期间的表现优于基准15个百分点。

4. 策略引擎升级:从简单规则到智能风控

初始的TopK策略存在明显的风险集中问题。通过Qlib的Strategy模块扩展,实现了以下增强功能:

4.1 组合构建优化

class EnhancedTopKStrategy(TopKDropoutStrategy):
    def __init__(self, topk=30, sector_neutral=True, volatility_cap=0.5):
        self.sector_neutral = sector_neutral
        self.volatility_cap = volatility_cap
        
    def generate_target_weight(self, score, current_positions):
        weights = super().generate_target_weight(score, current_positions)
        if self.sector_neutral:
            weights = self._apply_sector_neutral(weights)
        return self._apply_volatility_filter(weights)

4.2 交易执行优化

backtest配置中增加了现实约束:

backtest:
    exchange_kwargs:
        limit_threshold: 0.095
        deal_price: 'next_open'  # 更真实的成交假设
        open_cost: 0.0015
        min_volume_ratio: 0.1    # 避免流动性不足

4.3 动态仓位管理

通过扩展PortAnaRecord,实现了基于市场波动率的仓位调节:

仓位比例 = 基准比例 * (1 - 当前波动率/历史最大波动率)

最终版本的策略表现(2018-2021):

指标初始策略优化策略基准
年化收益21%28%9%
夏普比率1.22.30.8
最大回撤38%22%35%
胜率53%61%51%

在Qlib的模块化架构下,每个改进环节都可以独立验证。例如单独测试因子改进带来的信息比率提升,或者评估风控模块对回撤的改善程度。这种可拆解的研发流程,让策略迭代效率提升了至少3倍。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐