QlibRL 快速上手:在 Qlib 中训练与回测单资产订单执行(Order Execution)强化学习 Agent
本文基于 Qlib 官方文档 QlibRL Quick Start,完整讲解 Qlib 强化学习工具包 QlibRL 在「单资产订单执行」场景下的端到端工作流:如何用一份 YAML 训练配置启动 PPO 训练、如何用另一份回测配置加载训练好的策略并回测,以及训练/回测两条管线背后的源码结构、默认参数与数据准备方式。读完本文,你应当能够独立配置并跑通「数据准备 → 训练 → 回测」全流程,并通过替换配置文件中的类名扩展出自己的模拟器、状态解释器、奖励函数或策略网络。
任务背景:把订单执行建模为强化学习问题
QlibRL 在文档中给出的示例是一个**单资产订单执行(Single Asset Order Execution, SAOE)**任务:把一笔大额订单在一天之内拆分执行,目标是在价格优势(Price Advantage)、交易成本、市场冲击和成交进度之间取得平衡。整个 RL 系统由四要素构成,QlibRL 把每一个要素都抽象成了可在配置文件中替换的组件:
- 环境/模拟器(Simulator):模拟订单执行所处的市场,文档示例对应 qlib/rl/order_execution/simulator_qlib.py 中的
SingleAssetOrderExecution(贴近真实约束)和 qlib/rl/order_execution/simulator_simple.py 中的SingleAssetOrderExecutionSimple(训练用简化版); - 状态解释器(State Interpreter):把模拟器状态转换为观测,文档示例使用
FullHistoryStateInterpreter,动作解释器使用CategoricalActionInterpreter,二者都位于 qlib/rl/order_execution/interpreter.py; - 奖励函数(Reward):文档示例为
PAPenaltyReward,位于 qlib/rl/order_execution/reward.py; - 策略(Policy):文档示例为 PPO,位于 qlib/rl/order_execution/policy.py,配套网络
Recurrent位于 qlib/rl/order_execution/network.py。
这套设计的核心含义是:如果你已经定义好自己的 simulator / interpreters / reward / policy,只需修改配置文件中的对应设置即可启动训练与回测管线,无需改动训练框架本身。这一点在文档结尾有明确说明,也在 qlib/rl/contrib/train_onpolicy.py 的 main() 中得到印证——所有组件都是通过 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将其直接传入SingleAssetOrderExecutionSimple的vol_threshold)。
env:并发环境与并行模式
concurrency:并发环境 worker 数;parallel_mode:dummy/subproc/shmem三种取值,对应 tianshou 中 VectorEnv 的不同并行实现。从 qlib/rl/contrib/train_onpolicy.py 的trainer_kwargs可见,这两个参数分别传入训练器的finite_env_type与concurrency。
状态与动作解释器
CategoricalActionInterpreter 的 values 可以是长度为 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.py 的 FullHistoryObs 定义看,观测是一个字典,包含 data_processed(今日已处理数据)、data_processed_prev(昨日数据)、acquiring、cur_tick、cur_step、num_step、target、position、position_history 等字段,且对 cur_time 之后的数据做了掩码(_mask_future_info),保证不使用未来信息。同一模块还提供 CurrentStepStateInterpreter、TwapRelativeActionInterpreter 等变体可供替换。
奖励函数、数据与策略
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_dim;num_workers与queue_size控制数据队列。policy指定 PPO 与学习率lr: 0.0001;network指定Recurrent网络。runtime控制随机种子与是否使用 CUDA。trainer各参数的作用与底层映射(从 qlib/rl/contrib/train_onpolicy.py 的train()调用看):
| 配置项 | 含义 | 训练器中的对应 |
|---|---|---|
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后缀字段)。 - 撮合与限制:
exchange中limit_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.py 的 get_backtest_config_fromfile 会给回测配置补齐一批默认值,配置文件中未显式写出的项会取这些默认:
exchange默认:open_cost: 0.0005、close_cost: 0.0015、min_cost: 5.0、trade_unit: 100.0、cash_limit: None;- 顶层默认:
concurrency: -1、multiplier: 1.0、output_dir: "outputs_backtest/"、generate_report: False、data_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.py 与 qlib/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.yml、exp_configs/train_opds.yml)、对应回测配置(exp_configs/backtest_ppo.yml、exp_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 的流程:
-
获取 Qlib 数据(以 HS300、5 分钟粒度为例):
$ python -m qlib.cli.data qlib_data --target_dir ./data/bin --region hs300 --interval 5min -
生成 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: 48,48 = 240 min / 5 min),订单目录为 ./data/orders,回测测试订单文件为 test_orders.pkl。两者结构一致,只需按自己数据粒度调整 data_ticks、time_per_step 与目录路径即可。
两种模拟器:训练与回测结果的口径差异
文档示例中,训练用 SingleAssetOrderExecutionSimple、回测用 SingleAssetOrderExecution,这一差异直接导致两条管线的结果不可逐点比较。根据 examples/rl_order_execution/README.md 的说明:
- 训练管线使用简化版模拟器
SingleAssetOrderExecutionSimple,出于效率考虑,它对成交金额不做限制——无论下单量多大都假设可以完全成交; - 回测管线使用更贴近现实的
SingleAssetOrderExecution(对应 qlib/rl/contrib/backtest.py 中single_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,只需修改配置文件中的对应设置(替换 class 与 module_path)即可启动训练与回测管线。文档给出的四个参考实现分别是:
- 模拟器示例:
qlib.rl.order_execution.simulator_qlib.SingleAssetOrderExecution、qlib.rl.order_execution.simulator_simple.SingleAssetOrderExecutionSimple; - 解释器示例:
qlib.rl.order_execution.interpreter.FullHistoryStateInterpreter、qlib.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。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
