Qlib Online 模块详解:模拟真实账户的每日订单生成、执行与组合更新实战
本篇指南围绕 Qlib 的 Online 模块(对应文档 docs/hidden/online.rst)展开:它把"训练好的模型 + 交易策略"放进一个持久化的模拟账户里,按真实交易的节奏——前一交易日收盘后出预测、下一交易日执行订单、收盘后更新账户——逐日推进。读完后,你将能够编写 Online 配置文件、使用 online simulate / generate / execute / update / show / add_user / remove_user 命令完成从区间回放到逐日实盘模拟的完整流程,并理解账户文件夹中每个文件(pickle 模型、订单 JSON、持仓报表)的来龙去脉。
Online 模拟的是什么:一个真实交易账户的生命周期
Online 模块的核心目标是:模拟"如果我们真的用模型和策略去实盘交易,会发生什么"。与 Qlib 其他模块一致,它通过配置文件确定模型与策略参数,并把账户保存在一个文件夹中;之后每个交易日,模块都用最新信息为你的账户执行交易,性能可以随时通过 API 查看。
每个账户会经历以下三个过程。其中 pred_date 是你预测目标持仓的日期(使用截至该日收盘的数据),trade_date 是你实际下单交易的日期,两者相差一个交易日:
- 生成订单列表(Generate the order list,在 pred_date 进行);
- 执行订单列表(Execute the order list,在 trade_date 进行);
- 更新账户(Update account,在 trade_date 进行)。
除此之外,你也可以直接创建一个账户,用该模块测试它在一段区间内的表现,即一次性的 Simulate(start_date, end_date)。
这个"前一日预测、当日交易"的时间语义在源码中有直接体现。qlib/contrib/online/operator.py 中 Operator.init 会校验 trade_date 是否为可交易日期,然后调用 get_pre_trading_date(trade_date, future=True) 求出预测日:
trade_date = pd.Timestamp(date)
if not is_tradable_date(trade_date):
raise ValueError("trade date is not tradable date".format(trade_date.date()))
pred_date = get_pre_trading_date(trade_date, future=True)
而区间模拟 simulate 的 docstring 明确描述了循环内容(见 qlib/contrib/online/operator.py):
Run the ( generate_trade_decision -> execute_order_list -> update_account ) process everyday from start date to end date.
其内部实现将日期序列错位一位(for pred_date, trade_date in zip(dates[:-2], dates[1:-1])),即每天先取 pred_date 的数据算分数、更新策略、生成并落盘订单列表,再由 SimulatorExecutor 在 trade_date 执行订单、最后 update_account 推进账户状态——与文档描述的三步流程一一对应。
账户的持久化方式:模型与策略保存为 pickle 文件,持仓(position)与报表(report)保存为 Excel/CSV,具体目录结构见下文"账户数据结构"。
账户数据结构:每个用户一个文件夹
文档约定:Online 需要把账户保存在一个文件夹中(下文以 user_data 指代根目录),多账户按 user_id 分目录存放。完整的文件结构如下(加粗表示文件夹):
{user_folder}
│ users.csv: (每个用户的初始日期)
│
└───{user_id1}:(用户子文件夹,保存其数据)
│ position.xlsx
│ report.csv
│ model_{user_id1}.pickle
│ strategy_{user_id1}.pickle
│
└───score
│ └───{YYYY}
│ └───{MM}
│ │ score_{YYYY-MM-DD}.csv
│
└───trade
└───{YYYY}
└───{MM}
│ orderlist_{YYYY-MM-DD}.json
│ transaction_{YYYY-MM-DD}.csv
│
└───{user_id2}
│ position.xlsx
│ report.csv
│ model_{user_id2}.pickle
│ strategy_{user_id2}.pickle
│
└───score
└───trade
...
这套结构在源码中可以得到逐项印证(qlib/contrib/online/manager.py 与 qlib/contrib/online/utils.py):
users.csv:由create_user_folder创建,列为user_id, add_date(utils.py);UserManager以它为索引load_users(manager.py)。model_{user_id}.pickle/strategy_{user_id}.pickle:UserManager.add_user通过save_instance分别落盘(manager.py);加载时使用受限的restricted_pickle_load(utils.py)。position.xlsx/report.csv:由账户对象Account.save_account(...)写出,见UserManager.save_user_data(manager.py)。score/、trade/子目录:分别存放预测分数与订单/成交文件,由分数与订单的读写工具维护(save_score_series、save_order_list等,由 operator.py 引入)。
每个账户在内存中的抽象是 User,它持有 account / strategy / model 三件套,并能在每个交易日开始 init_state、随时用 showReport 对照基准(默认 SH000905)输出超额收益分析,见 qlib/contrib/online/user.py。
配置文件:模型、策略与初始资金
Online 的配置文件必须包含模型与策略两部分信息,再加上账户初始资金。文档给出的原始示例如下:
strategy:
class: TopkAmountStrategy
module_path: qlib.contrib.strategy
args:
market: csi500
trade_freq: 5
model:
class: ScoreFileModel
module_path: qlib.contrib.online.online_model
args:
loss: mse
model_path: ./model.bin
init_cash: 1000000000
UserManager.add_user 会解析这份 YAML:用 init_instance_by_config(config["model"]) 构建模型、用 init_instance_by_config(config["strategy"]) 构建策略,调用 strategy.get_init_args_from_model(model, add_date) 完成策略与模型的对接,最后以 config["init_cash"] 初始化 Account(manager.py)。
关于模型(model)
model 字段决定在预测日生成分数(score)时使用什么模型。文档给出了两种 ScoreFileModel 配置示例:一种是携带模型参数直接打分(旧版写法,loss: mse 等),另一种是读取现成的分数文件:
model:
class: ScoreFileModel
module_path: qlib.contrib.online.online_model
args:
score_path: <your score path>
对照当前仓库的实现(qlib/contrib/online/online_model.py),ScoreFileModel 现在的构造函数只接受 score_path:它把分数 CSV 读入内存(索引为 (stock_id, datetime) 的多级索引,含 score 列),get_data_with_date(date) 取出该日分数序列,predict 原样返回——即"给定预测日,返回当日全市场分数"。
如果你的模型不属于上述类型,需要自行实现。从源码结构看,Online 对模型的最小契约写在 qlib/contrib/online/init.py 的 TODO 说明中:模型必须实现 get_data_with_date(self, date, **kwargs),返回"用于预测 date 日分数(label)的输入数据",配合 predict 完成打分。
关于策略(strategy)
strategy 字段定义在预测日生成订单列表所用策略。文档示例为 Topk 类策略:
strategy:
class: TopkDropoutStrategy
module_path: qlib.contrib.strategy.strategy
args:
topk: 100
n_drop: 10
需要注意版本差异:TopkDropoutStrategy 在当前仓库位于 qlib/contrib/strategy/signal_strategy.py,因此 module_path 应写为 qlib.contrib.strategy.signal_strategy;其完整参数为 topk(组合股票数)、n_drop(每个交易日替换的股票数)、method_sell(默认 "bottom")、method_buy(默认 "top")、hold_thresh(默认 1)、only_tradable(默认 False)、forbid_all_trade_at_limit(默认 True)。而示例开头的 TopkAmountStrategy(market, trade_freq) 属于旧版写法,当前源码中未提供该类——若要在当前版本跑通配置,建议改用上面的 TopkDropoutStrategy。
结合当前仓库的完整配置
综合以上修正,一份贴近当前仓库实现的配置可以写成:
strategy:
class: TopkDropoutStrategy
module_path: qlib.contrib.strategy.signal_strategy
args:
topk: 100
n_drop: 10
model:
class: ScoreFileModel
module_path: qlib.contrib.online.online_model
args:
score_path: ./score.csv
init_cash: 1000000000
一键体验:simulate 区间模拟
在写好配置文件后,可以用一条命令创建账户文件夹,并从 2017-01-01 模拟交易到 2018-08-01:
online simulate -id v-test -config ./config/config.yaml -exchange_config ./config/exchange.yaml -start 2017-01-01 -end 2018-08-01 -path ./user_data/
日期语义(文档原文语义):
- start date(2017-01-01) 是用户的添加日期(add date),同时也是第一个预测日期;
- end date(2018-08-01) 是最后一个交易日期;
- 区间结束后,可用
online generate -date 2018-08-02 ...在下一个交易日继续生成订单列表。
从源码看(qlib/contrib/online/operator.py),simulate 的完整动作是:清空同名旧账户并 add_user,然后对区间内每个 (pred_date, trade_date) 依次执行——① 取数并保存分数序列;② user.strategy.update(score_series, pred_date, trade_date) 更新策略(与模型联动);③ 生成并保存订单列表;④ 由 SimulatorExecutor 自动执行订单并保存成交文件;⑤ update_account 推进账户状态。循环结束后汇总组合指标并调用 show 输出。
exchange.yaml:交易所撮合参数
-exchange_config 指定撮合行为。文档给出的简版示例:
open_cost: 0.003
close_cost: 0.003
limit_threshold: 0.095
deal_price: vwap
这些参数最终透传给 Exchange 构造(qlib/contrib/online/utils.py 的 prepare 读取 YAML 后执行 Exchange(trade_dates=dates, **exchange_paras))。对照 qlib/backtest/exchange.py 中的定义,各参数含义与默认值为:
open_cost:开仓成本费率,默认 0.0015;close_cost:平仓成本费率(含卖出税费),默认 0.0025;min_cost:最小佣金,默认 5.0;deal_price:成交价取值方式,如vwap(文档示例),也可配置买/卖分别取价;limit_threshold:涨跌停限制阈值(如 0.095 对应约 ±9.5%);impact_cost:市场冲击成本(滑点)费率。
文档中的 0.003/0.003/0.095/vwap 组合更贴近 A 股双边佣金约 0.3%、涨跌停约 10% 的设定,适合配合 csi500 这类市场使用。
online show:对照基准查看绩效
若账户保存在 ./user_data/,可以用下面的命令查看相对基准的绩效(SH000905 代表 csi500,SH000300 代表 csi300):
online show -id v-test -path ./user_data/ -bench SH000905
文档示例的输出:
Result of porfolio:
risk
excess_return_without_cost mean 0.000605
std 0.005481
annualized_return 0.152373
information_ratio 1.751319
max_drawdown -0.059055
excess_return_with_cost mean 0.000410
std 0.005478
annualized_return 0.103265
information_ratio 1.187411
max_drawdown -0.075024
该结果对应 Operator.show 的实现(operator.py):取基准标的的 $change 序列作为 bench,分别对"组合收益 − 基准"与"组合收益 − 基准 − 交易成本"调用 risk_analysis(来自 qlib/contrib/evaluate.py),输出 mean、std、annualized_return、information_ratio、max_drawdown 五项风险指标。
日常操作:账户管理与逐日交易
除了 simulate,文档定义了完整的账户管理命令集(全部基于 qlib/contrib/online/operator.py 中 Operator 类,通过 fire 生成 CLI,见 operator.py):
| 命令 | 作用 | 源码位置 |
|---|---|---|
online add_user -id {user_id} -config {config_file} -path {folder_path} -date {add_date} |
在 folder 中新增账户(add_date 会取不晚于该日的最近可交易日) | operator.py |
online remove_user -id {user_id} -path {folder_path} |
删除账户(删除 {user_id} 子目录并更新 users.csv) |
operator.py |
online show -id {user_id} -path {folder_path} -bench {benchmark} |
展示相对基准的绩效 | operator.py |
online generate -date {date} -path {folder_path} |
在交易日的预测日生成订单列表 | operator.py |
online execute -date {date} -exchange_config {exchange_config_path} -path {folder_path} |
执行订单列表并产出成交结果 | operator.py |
online update -date {date} -path {folder_path} |
根据成交结果更新账户 | operator.py |
例如:
>> online add_user -id v-test -config config.yaml -path ./user_data/ -date 2019-10-15
>> online remove_user -id v-test -path ./user_data/
>> online show -id v-test -path ./user_data/ -bench SH000905
>> online generate -date 2019-10-16 -path ./user_data/
>> online execute -date 2019-10-16 -exchange_config ./config/exchange.yaml -path ./user_data/
>> online update -date 2019-10-16 -path ./user_data/
使用规则与注意事项:
date参数默认是交易日期(若今天是交易日且 qlib 数据已更新,即"今天");generate与update会校验输入日期是否合法(非可交易日期直接抛ValueError);上述三个过程应当在每一个交易日依次调用一次,顺序为 generate → execute → update;execute会检查账户数据新鲜度:若账户最后一次交易日晚于本次指定的预测日,会抛出 "The account data is not newest!"(operator.py),update同样有类似的滞后检查(operator.py)。这意味着逐日流程不能跳日、不能乱序;update的type参数指定订单由哪种执行器执行,当前仅支持'SIM'(SimulatorExecutor),传其他值会报 "not found executor"(operator.py)。
生成的文件与订单列表格式
online generate 命令会在 {folder_path}/{user_id}/temp/ 下创建订单列表文件 orderlist_{YYYY-MM-DD}.json,其中 YYYY-MM-DD 是这些订单将要被执行的日期。文档描述的 JSON 格式为:
{
'sell': {
{'$stock_id1': '$amount1'},
{'$stock_id2': '$amount2'}, ...
},
'buy': {
{'$stock_id1': '$amount1'},
{'$stock_id2': '$amount2'}, ...
}
}
即分 sell / buy 两个方向,每个方向内是"股票代码 → 数量"的条目列表。订单执行后(无论通过 online execute 还是其他执行器),成交文件同样生成在 {folder_path}/{user_id}/temp/(transaction_{YYYY-MM-DD}.csv),随后由 update 读入并推进账户。
与当前 Online Serving 框架的关系
需要说明版本背景:本页描述的 online 命令行模块属于 Qlib 早期的在线模拟实现,其代码主体是 qlib/contrib/online/ 下的 Operator / UserManager / User。当前仓库持续维护的在线化能力是 Online Serving 框架(文档见 docs/component/online.rst),它包含 Online Manager、Online Strategy、Online Tool 与 Updater 四类组件,配套可运行的示例位于 examples/online_srv/(如 online_management_simulate.py、rolling_online_management.py、update_online_pred.py),并基于任务管理(Task Management)中的 TrainerRM、Collector 等组件实现模型滚动更新与在线推理。
从源码结构看,还有一个值得注意的现状:当前快照中 operator.py 引用的订单/分数读写与 SimulatorExecutor 封装(from .executor import ...)所在模块未包含在 qlib/contrib/online 目录内,说明该旧版 CLI 处于维护过渡状态。因此在当前仓库中复现"在线模拟",推荐路径是:先用本文的 online 概念模型(pred_date/trade_date、generate/execute/update 三步骤、账户文件夹结构)理解设计,再参照 examples/online_srv/ 与 qlib/workflow/online/ 中维护中的 Online Manager / OnlineStrategy / Updater 落地实际流程;同时注意 Online Serving 要求数据源保持更新(例如用仓库 scripts/data_collector/ 中的脚本滚动更新日线数据)。
小结
- 核心流程:pred_date 预测 → trade_date 生成订单 → 执行 → 更新账户;区间模式
online simulate一次跑完,逐日模式按generate / execute / update顺序每天调用。 - 配置三要素:
strategy(如TopkDropoutStrategy)、model(如ScoreFileModel+score_path)、init_cash;撮合行为由exchange.yaml(open_cost / close_cost / limit_threshold / deal_price等)控制。 - 账户即文件夹:
users.csv+ 每用户一个子目录(pickle 模型/策略、position.xlsx、report.csv、score/、trade/),可直接被online show汇总为相对基准(SH000905/SH000300)的五项风险指标。 - 版本提示:旧版
onlineCLI 与当前维护中的 Online Serving(qlib/workflow/online/ 与 examples/online_srv/)概念一致、实现分属两代,实践时以后者为准。
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
