首页
/ QlibRL 快速上手:在 Qlib 中训练与回测单资产订单执行(Order Execution)强化学习 Agent

QlibRL 快速上手:在 Qlib 中训练与回测单资产订单执行(Order Execution)强化学习 Agent

2026-09-05 17:04:42作者:齐冠琰

本文基于 Qlib 官方文档 QlibRL Quick Start,完整讲解 Qlib 强化学习工具包 QlibRL 在「单资产订单执行」场景下的端到端工作流:如何用一份 YAML 训练配置启动 PPO 训练、如何用另一份回测配置加载训练好的策略并回测,以及训练/回测两条管线背后的源码结构、默认参数与数据准备方式。读完本文,你应当能够独立配置并跑通「数据准备 → 训练 → 回测」全流程,并通过替换配置文件中的类名扩展出自己的模拟器、状态解释器、奖励函数或策略网络。

任务背景:把订单执行建模为强化学习问题

QlibRL 在文档中给出的示例是一个**单资产订单执行(Single Asset Order Execution, SAOE)**任务:把一笔大额订单在一天之内拆分执行,目标是在价格优势(Price Advantage)、交易成本、市场冲击和成交进度之间取得平衡。整个 RL 系统由四要素构成,QlibRL 把每一个要素都抽象成了可在配置文件中替换的组件:

RL 框架:agent、环境、policy 与 reward 信号的交互关系

这套设计的核心含义是:如果你已经定义好自己的 simulator / interpreters / reward / policy,只需修改配置文件中的对应设置即可启动训练与回测管线,无需改动训练框架本身。这一点在文档结尾有明确说明,也在 qlib/rl/contrib/train_onpolicy.pymain() 中得到印证——所有组件都是通过 init_instance_by_config 按配置中的 class / module_path / kwargs 动态实例化的。

训练配置文件:完整结构与逐段解读

文档给出的训练配置(1 分钟粒度场景)如下,可直接复制使用:

simulator:
    # Each step contains 30mins
    time_per_step: 30
    # Upper bound of volume, should be null or a float between 0 and 1, if it is a float, represent upper bound is calculated by the percentage of the market volume
    vol_limit: null
env:
    # Concurrent environment workers.
    concurrency: 1
    # dummy or subproc or shmem.
    parallel_mode: dummy
action_interpreter:
    class: CategoricalActionInterpreter
    kwargs:
        # Candidate actions, it can be a list with length L: [a_1, a_2,..., a_L] or an integer n, in which case the list of length n+1 is auto-generated, i.e., [0, 1/n, 2/n,..., n/n].
        values: 14
        # Total number of steps (an upper-bound estimation)
        max_step: 8
    module_path: qlib.rl.order_execution.interpreter
state_interpreter:
    class: FullHistoryStateInterpreter
    kwargs:
        # Number of dimensions in data.
        data_dim: 6
        # Equal to the total number of records. For example, in SAOE per minute, data_ticks is the length of the day in minutes.
        data_ticks: 240
        # The total number of steps (an upper-bound estimation). For example, 390min / 30min-per-step = 13 steps.
        max_step: 8
        # Provider of the processed data.
        processed_data_provider:
            class: PickleProcessedDataProvider
            module_path: qlib.rl.data.pickle_styled
            kwargs:
                data_dir: ./data/pickle_dataframe/feature
    module_path: qlib.rl.order_execution.interpreter
reward:
    class: PAPenaltyReward
    kwargs:
        # The penalty for a large volume in a short time.
        penalty: 100.0
    module_path: qlib.rl.order_execution.reward
data:
    source:
        order_dir: ./data/training_order_split
        data_dir: ./data/pickle_dataframe/backtest
        # number of time indexes
        total_time: 240
        # start time index
        default_start_time: 0
        # end time index
        default_end_time: 240
        proc_data_dim: 6
    num_workers: 0
    queue_size: 20
network:
    class: Recurrent
    module_path: qlib.rl.order_execution.network
policy:
    class: PPO
    kwargs:
        lr: 0.0001
    module_path: qlib.rl.order_execution.policy
runtime:
    seed: 42
    use_cuda: false
trainer:
    max_epoch: 2
    # Number of episodes collected in each training iteration
    repeat_per_collect: 5
    earlystop_patience: 2
    # Episodes per collect at training.
    episode_per_collect: 20
    batch_size: 16
    # Perform validation every n iterations
    val_every_n_epoch: 1
    checkpoint_path: ./checkpoints
    checkpoint_every_n_iters: 1

下面按配置段拆解各参数的含义:

simulator:单步粒度与成交量上限

  • time_per_step:每个决策步覆盖多少分钟。示例中为 30,即每 30 分钟决策一次;一天 240 分钟(1 分钟粒度)约对应 8 步,这正是 max_step: 8 的由来。仓库示例配置 exp_configs/train_ppo.yml 使用 5 分钟粒度数据,同样取 time_per_step: 30
  • vol_limit:单步成交量上限。null 表示不限制;若为 0~1 之间的浮点数,则按市场成交量的百分比计算上限(见 qlib/rl/contrib/train_onpolicy.py_simulator_factory_simple 将其直接传入 SingleAssetOrderExecutionSimplevol_threshold)。

env:并发环境与并行模式

  • concurrency:并发环境 worker 数;
  • parallel_modedummy / subproc / shmem 三种取值,对应 tianshou 中 VectorEnv 的不同并行实现。从 qlib/rl/contrib/train_onpolicy.pytrainer_kwargs 可见,这两个参数分别传入训练器的 finite_env_typeconcurrency

状态与动作解释器

CategoricalActionInterpretervalues 可以是长度为 L 的候选动作列表,也可以是整数 n——此时自动生成 [0, 1/n, 2/n, ..., n/n](示例中 values: 14,即 15 个候选比例);max_step 与状态解释器中的含义一致,是步数的上界估计。

FullHistoryStateInterpreter 将「今日截至当前的全部历史 + 昨日全天数据」编码为观测,参数包括 data_dim(特征维数)、data_ticks(记录总数,1 分钟粒度全天即 240)、max_step,以及一个可替换的 processed_data_provider(示例使用 PickleProcessedDataProvider,位于 qlib/rl/data/pickle_styled.py;仓库示例还使用 HandlerProcessedDataProvider,位于 qlib/rl/data/native.py)。从 qlib/rl/order_execution/interpreter.pyFullHistoryObs 定义看,观测是一个字典,包含 data_processed(今日已处理数据)、data_processed_prev(昨日数据)、acquiringcur_tickcur_stepnum_steptargetpositionposition_history 等字段,且对 cur_time 之后的数据做了掩码(_mask_future_info),保证不使用未来信息。同一模块还提供 CurrentStepStateInterpreterTwapRelativeActionInterpreter 等变体可供替换。

奖励函数、数据与策略

  • PAPenaltyReward 以价格优势为奖励,penalty 惩罚短时间内大成交量(示例值 100.0)。仓库示例 exp_configs/train_opds.yml 使用 PAPenaltyReward(penalty 4.0, scale 0.01),而 exp_configs/train_ppo.yml 使用 PPOReward——文档也指出 PPO 与 OPDS 两个方法的主要区别就在奖励函数。
  • data.source 指定训练订单目录(order_dir,示例为 ./data/training_order_split)、回测数据目录(data_dir)、时间索引范围(total_time / default_start_time / default_end_time,1 分钟粒度下为 0~240)与处理数据维度 proc_data_dimnum_workersqueue_size 控制数据队列。
  • policy 指定 PPO 与学习率 lr: 0.0001network 指定 Recurrent 网络。
  • runtime 控制随机种子与是否使用 CUDA。
  • trainer 各参数的作用与底层映射(从 qlib/rl/contrib/train_onpolicy.pytrain() 调用看):
配置项 含义 训练器中的对应
max_epoch 最大训练轮数 max_iters
episode_per_collect 每轮收集的 episode 数 episode_per_iter
batch_size 更新时批大小 update_kwargs.batch_size
repeat_per_collect 每轮收集的 episode 用于更新的重复次数 update_kwargs.repeat
val_every_n_epoch 每 n 轮验证一次 val_every_n_iters
earlystop_patience 早停耐心值 触发 EarlyStopping 回调,监控指标为 val/pa(验证集价格优势)
checkpoint_path / checkpoint_every_n_iters 检查点目录与频率 Checkpoint 回调,会额外保存一份 latest.pth 拷贝

回测配置文件:嵌套策略与撮合规则

训练完成后的回测配置示例如下(1 分钟粒度):

order_file: ./data/backtest_orders.csv
start_time: "9:45"
end_time: "14:44"
qlib:
    provider_uri_1min: ./data/bin
    feature_root_dir: ./data/pickle
    # feature generated by today's information
    feature_columns_today: [
        "$open", "$high", "$low", "$close", "$vwap", "$volume",
    ]
    # feature generated by yesterday's information
    feature_columns_yesterday: [
        "$open_v1", "$high_v1", "$low_v1", "$close_v1", "$vwap_v1", "$volume_v1",
    ]
exchange:
    # the expression for buying and selling stock limitation
    limit_threshold: ['$close == 0', '$close == 0']
    # deal price for buying and selling
    deal_price: ["If($close == 0, $vwap, $close)", "If($close == 0, $vwap, $close)"]
volume_threshold:
    # volume limits are both buying and selling, "cum" means that this is a cumulative value over time
    all: ["cum", "0.2 * DayCumsum($volume, '9:45', '14:44')"]
    # the volume limits of buying
    buy: ["current", "$close"]
    # the volume limits of selling, "current" means that this is a real-time value and will not accumulate over time
    sell: ["current", "$close"]
strategies:
    30min:
        class: TWAPStrategy
        module_path: qlib.contrib.strategy.rule_strategy
        kwargs: {}
    1day:
        class: SAOEIntStrategy
        module_path: qlib.rl.order_execution.strategy
        kwargs:
        state_interpreter:
            class: FullHistoryStateInterpreter
            module_path: qlib.rl.order_execution.interpreter
            kwargs:
            max_step: 8
            data_ticks: 240
            data_dim: 6
            processed_data_provider:
                class: PickleProcessedDataProvider
                module_path: qlib.rl.data.pickle_styled
                kwargs:
                data_dir: ./data/pickle_dataframe/feature
        action_interpreter:
            class: CategoricalActionInterpreter
            module_path: qlib.rl.order_execution.interpreter
            kwargs:
            values: 14
            max_step: 8
        network:
            class: Recurrent
            module_path: qlib.rl.order_execution.network
            kwargs: {}
        policy:
            class: PPO
            module_path: qlib.rl.order_execution.policy
            kwargs:
                lr: 1.0e-4
                # Local path to the latest model. The model is generated during training, so please run training first if you want to run backtest with a trained policy. You could also remove this parameter file to run backtest with a randomly initialized policy.
                weight_file: ./checkpoints/latest.pth
# Concurrent environment workers.
concurrency: 5

各部分要点:

  • 订单与时间窗order_file 指向待回测的订单文件,start_time / end_time 定义日内执行窗口(示例 9:45–14:44)。
  • qlib 数据源provider_uri_1min 指向 Qlib bin 数据,feature_root_dir 指向 pickle 特征数据,feature_columns_today / feature_columns_yesterday 分别定义今日/昨日特征表达式(含 $vwap 与昨日 _v1 后缀字段)。
  • 撮合与限制exchangelimit_threshold 表达涨跌停(示例中用 $close == 0 表示停牌限制),deal_price 定义买卖成交价;volume_threshold["cum", ...](随时间累积的上限,如全天成交量 20% 的日内累积值)与 ["current", ...](实时值,不累积)分别约束总、买、卖三侧成交量。
  • 两级策略嵌套1day 级的 SAOEIntStrategy(RL 策略,位于 qlib/rl/order_execution/strategy.py)与 30min 级的 TWAPStrategy(规则策略,位于 qlib/contrib/strategy/rule_strategy.py)组合使用。从 qlib/rl/contrib/backtest.py_get_multi_level_executor_config 看,回测入口会按时间粒度对 strategies 排序,逐层包裹成 NestedExecutor,最终交给 SimulatorExecutor 执行:日级的 RL 策略产出 30 分钟粒度的目标量,30 分钟级的 TWAP 策略再在窗口内将其均分执行。
  • 策略权重policy.kwargs.weight_file 指向训练产物(如 ./checkpoints/latest.pth)。不设置该参数时策略以随机权重运行,结果没有意义——文档对此有明确提示。
  • 并发:顶层 concurrency(示例为 5)控制回测并发度。

此外,qlib/rl/contrib/naive_config_parser.pyget_backtest_config_fromfile 会给回测配置补齐一批默认值,配置文件中未显式写出的项会取这些默认:

  • exchange 默认:open_cost: 0.0005close_cost: 0.0015min_cost: 5.0trade_unit: 100.0cash_limit: None
  • 顶层默认:concurrency: -1multiplier: 1.0output_dir: "outputs_backtest/"generate_report: Falsedata_granularity: "1min"
  • 配置支持 _base_ 字段引用并合并其他配置文件(子配置优先覆盖基础配置),且列表会被转换为元组。

启动训练与回测

按文档,得到上述两份配置后,训练命令为:

$ python -m qlib.rl.contrib.train_onpolicy.py --config_path train_config.yml

训练完成后的回测命令为:

$ python -m qlib.rl.contrib.backtest.py --config_path backtest_config.yml

结合 qlib/rl/contrib/train_onpolicy.pyqlib/rl/contrib/backtest.py 中的 argparse 定义,两个入口还提供以下可选参数:

命令 参数 说明
train_onpolicy --config_path(必填) 训练配置路径
train_onpolicy --no_training 跳过训练,只跑训练管线中的测试/回测阶段(可用于加载已有检查点复现训练管线内的测试结果)
train_onpolicy --run_backtest 在训练管线内附加运行回测阶段
backtest --config_path(必填) 回测配置路径
backtest --use_simulator 使用 SingleAssetOrderExecution 模拟器作为回测后端
backtest --n_jobs 回测并行任务数(覆盖配置中的 concurrency

训练侧的产物(指标、日志、检查点)落在 trainer.checkpoint_path 指定的目录(示例为 ./checkpoints,其中 latest.pth 即最新检查点);回测结果由 qlib/rl/contrib/backtest.py 写入 output_dir 下的 backtest_result.csv,其中 pa 指标会乘以 10000 以与训练管线的度量口径对齐。

仓库在 examples/rl_order_execution 提供了可直接运行的完整示例:5 分钟粒度下 PPO 与 OPDS 两套训练配置(exp_configs/train_ppo.ymlexp_configs/train_opds.yml)、对应回测配置(exp_configs/backtest_ppo.ymlexp_configs/backtest_opds.yml)以及弱基线 TWAP(exp_configs/backtest_twap.yml)。其典型训练命令为 python -m qlib.rl.contrib.train_onpolicy --config_path exp_configs/train_opds.yml --run_backtest,产物位于 outputs/opds

数据准备:从 Qlib 数据到 Pickle 特征

上述配置引用的 ./data/bin./data/pickle_dataframe/..../data/training_order_split 等目录需要先准备。按照 examples/rl_order_execution/README.md 的流程:

  1. 获取 Qlib 数据(以 HS300、5 分钟粒度为例):

    $ python -m qlib.cli.data qlib_data --target_dir ./data/bin --region hs300 --interval 5min
    
  2. 生成 pickle 风格特征与训练订单(脚本位于 examples/rl_order_execution/scripts/):

    $ python scripts/gen_pickle_data.py -c scripts/pickle_data_config.yml
    $ python scripts/gen_training_orders.py
    $ python scripts/merge_orders.py
    

完成后 data/ 目录结构应为:

data
├── bin
├── orders
└── pickle

注意:官方 quickstart 文档中的配置面向 1 分钟粒度、data_ticks: 240、数据目录为 ./data/pickle_dataframe/feature 等路径;而仓库示例配置面向 5 分钟粒度(data_ticks: 4848 = 240 min / 5 min),订单目录为 ./data/orders,回测测试订单文件为 test_orders.pkl。两者结构一致,只需按自己数据粒度调整 data_tickstime_per_step 与目录路径即可。

两种模拟器:训练与回测结果的口径差异

文档示例中,训练用 SingleAssetOrderExecutionSimple、回测用 SingleAssetOrderExecution,这一差异直接导致两条管线的结果不可逐点比较。根据 examples/rl_order_execution/README.md 的说明:

  • 训练管线使用简化版模拟器 SingleAssetOrderExecutionSimple,出于效率考虑,它对成交金额不做限制——无论下单量多大都假设可以完全成交;
  • 回测管线使用更贴近现实的 SingleAssetOrderExecution(对应 qlib/rl/contrib/backtest.pysingle_with_simulator--use_simulator 路径),它会引入真实约束(例如成交量必须是最小交易单位的整数倍),实际成交量可能与期望成交量不一致。

若希望回测结果与训练管线内的测试完全一致,可以在训练配置中指定检查点(policy.kwargs.weight_file),然后只跑回测阶段:

$ python -m qlib.rl.contrib.train_onpolicy --config_path PATH/TO/CONFIG --run_backtest --no_training
policy:
  class: PPO  # PPO, DQN
  kwargs:
    lr: 0.0001
    weight_file: PATH/TO/CHECKPOINT
  module_path: qlib.rl.order_execution.policy

自定义扩展与后续方向

文档明确了 QlibRL 的扩展方式:单资产订单执行任务中,如果开发者已经定义了自己的 simulator / interpreters / reward 函数 / policy,只需修改配置文件中的对应设置(替换 classmodule_path)即可启动训练与回测管线。文档给出的四个参考实现分别是:

  • 模拟器示例:qlib.rl.order_execution.simulator_qlib.SingleAssetOrderExecutionqlib.rl.order_execution.simulator_simple.SingleAssetOrderExecutionSimple
  • 解释器示例:qlib.rl.order_execution.interpreter.FullHistoryStateInterpreterqlib.rl.order_execution.interpreter.CategoricalActionInterpreter
  • 策略示例:qlib.rl.order_execution.policy.PPO
  • 奖励示例:qlib.rl.order_execution.reward.PAPenaltyReward

此外,文档指出 Qlib 后续将提供更多场景(如基于 RL 的组合构建 portfolio construction)的示例。更多 RL 框架背景可参考同目录的 Reinforcement Learning 概览QlibRL 框架 文档;示例代码的更多说明见 examples/rl_order_execution/README.md

登录后查看全文
热门项目推荐
相关项目推荐