9.工作流自动化
本文将详细介绍 QLib 量化投资平台中的工作流自动化功能,包括 qrun 工具的使用方法、配置文件的编写规范、工作流组件的扩展方式以及批量任务的执行与管理。工作流自动化是提升量化研究效率的关键,它能够将数据处理、模型训练、回测分析等多个环节串联起来,实现一键式执行,让研究者能够更专注于策略逻辑的创新而非繁琐的流程管理。通过本章的学习,你将能够构建完整的量化研究自动化流程,提高研究效率和可重复性。
qrun 工具使用指南
qrun 是 QLib 提供的命令行工具,它能够根据用户定义的配置文件,自动执行从数据准备、模型训练到回测分析的完整量化研究流程。使用 qrun 工具可以极大地简化量化研究的操作步骤,提高工作效率,同时保证实验的可重复性。
安装与基本用法
qrun 工具在 QLib 安装过程中会自动配置,无需额外安装。安装完成后,可以通过以下命令验证 qrun 是否可用:
qrun --help
如果安装成功,会显示 qrun 的帮助信息,包括命令格式、参数说明等。qrun 的基本使用方法非常简单,只需指定配置文件路径即可:
qrun configuration.yaml
这条命令会读取 configuration.yaml 文件中定义的工作流配置,并按照配置自动执行整个量化研究流程。在执行过程中,qrun 会输出详细的日志信息,包括每个步骤的执行状态、耗时等,方便用户监控流程进展。
调试模式
在开发新的策略或配置时,经常需要对工作流进行调试。qrun 支持调试模式,可以通过 Python 的 pdb 调试器逐步执行工作流,方便定位问题。启用调试模式的命令如下:
python -m pdb qlib/workflow/cli.py configuration.yaml
执行这条命令后,程序会在工作流的入口处暂停,等待用户输入调试命令。通过调试模式,可以查看变量的值、执行路径等,帮助快速定位配置或代码中的错误。
工作流程解析
qrun 工具执行的工作流程主要包括以下几个步骤:
- QLib 初始化:根据配置文件中的 qlib_init 部分,初始化 QLib 环境,包括数据路径、区域设置等。
- 数据准备:加载并处理数据,根据配置的数据集参数,生成训练、验证和测试数据。
- 模型训练:初始化模型并使用训练数据进行训练,保存训练好的模型参数。
- 预测生成:使用训练好的模型对测试数据进行预测,生成交易信号。
- 回测分析:根据预测信号和策略配置,进行回测,并生成绩效分析报告。
- 结果记录:将模型参数、预测结果、回测报告等实验 artifacts 保存到指定位置,便于后续分析和复现。
整个流程由 qrun 工具自动协调执行,用户无需手动干预每个步骤,大大减少了重复劳动。
常用参数
qrun 工具提供了一些常用参数,用于控制工作流的执行行为:
--force: 强制重新执行所有步骤,即使某些中间结果已经存在。默认情况下,qrun 会跳过已完成的步骤,使用缓存的结果。--log-level: 设置日志级别,可选值包括 DEBUG、INFO、WARNING、ERROR,默认为 INFO。设置为 DEBUG 可以获取更详细的执行日志,有助于调试。--experiment-name: 指定实验名称,用于区分不同的实验结果。实验名称会影响结果的保存路径。
例如,以下命令强制重新执行工作流,并设置日志级别为 DEBUG:
qrun configuration.yaml --force --log-level DEBUG
合理使用这些参数可以更好地控制工作流的执行,满足不同的研究需求。
配置文件编写规范
配置文件是 qrun 工具的核心,它定义了工作流中各个组件的参数和执行逻辑。QLib 的配置文件采用 YAML 格式,具有良好的可读性和可维护性。编写规范的配置文件是确保工作流正确执行的关键。
配置文件结构
一个完整的 QLib 工作流配置文件通常包含以下几个主要部分:
- qlib_init: QLib 环境初始化参数,如数据路径、区域设置等。
- market: 市场名称,用于指定投资范围,如沪深 300、中证 500 等。
- benchmark: 基准指数代码,用于绩效评估。
- data_handler_config: 数据处理器配置,包括时间范围、标的范围等。
- port_analysis_config: 组合分析配置,包括策略参数、回测参数等。
- task: 任务配置,包括模型、数据集、记录器等核心组件的定义。
下面是一个典型的配置文件示例:
qlib_init:
provider_uri: "~/.qlib/qlib_data/cn_data"
region: cn
market: &market csi300
benchmark: &benchmark SH000300
data_handler_config: &data_handler_config
start_time: 2008-01-01
end_time: 2020-08-01
fit_start_time: 2008-01-01
fit_end_time: 2014-12-31
instruments: *market
port_analysis_config: &port_analysis_config
strategy:
class: TopkDropoutStrategy
module_path: qlib.contrib.strategy.strategy
kwargs:
topk: 50
n_drop: 5
signal: <PRED>
backtest:
start_time: 2017-01-01
end_time: 2020-08-01
account: 100000000
benchmark: *benchmark
exchange_kwargs:
limit_threshold: 0.095
deal_price: close
open_cost: 0.0005
close_cost: 0.0015
min_cost: 5
task:
model:
class: LGBModel
module_path: qlib.contrib.model.gbdt
kwargs:
loss: mse
colsample_bytree: 0.8879
learning_rate: 0.0421
subsample: 0.8789
lambda_l1: 205.6999
lambda_l2: 580.9768
max_depth: 8
num_leaves: 210
num_threads: 20
dataset:
class: DatasetH
module_path: qlib.data.dataset
kwargs:
handler:
class: Alpha158
module_path: qlib.contrib.data.handler
kwargs: *data_handler_config
segments:
train: [2008-01-01, 2014-12-31]
valid: [2015-01-01, 2016-12-31]
test: [2017-01-01, 2020-08-01]
record:
- class: SignalRecord
module_path: qlib.workflow.record_temp
kwargs: {}
- class: PortAnaRecord
module_path: qlib.workflow.record_temp
kwargs:
config: *port_analysis_config
这个配置文件定义了一个完整的量化研究流程,包括数据处理、模型训练、回测分析等环节。通过 YAML 的锚点(&)和引用(*)功能,可以避免重复配置,提高配置文件的可维护性。
QLib 初始化部分
qlib_init 部分用于配置 QLib 的全局参数,主要包括:
- provider_uri: 数据存储路径,指定 QLib 数据的位置。该路径应与通过 get_data.py 下载的数据路径一致。
- region: 市场区域,可选值为"cn"(中国市场)或"us"(美国市场)。区域设置应与数据路径中的数据相匹配。
例如:
qlib_init:
provider_uri: "~/.qlib/qlib_data/cn_data"
region: cn
任务部分
task 部分是配置文件的核心,定义了工作流中的主要组件,包括模型(model)、数据集(dataset)和记录器(record)。
模型配置
model 部分定义了预测模型的类型和参数。配置格式如下:
model:
class: LGBModel
module_path: qlib.contrib.model.gbdt
kwargs:
loss: mse
learning_rate: 0.04
max_depth: 8
- class: 模型类名,如 LGBModel、XGBModel 等。
- module_path: 模型类所在的模块路径。
- kwargs: 模型初始化参数,具体参数取决于模型类型。
QLib 支持多种内置模型,如 LightGBM、XGBoost、Linear 等,也支持自定义模型。只需指定正确的类名和模块路径,即可使用相应的模型。
数据集配置
dataset 部分定义了数据的加载和处理方式。配置格式如下:
dataset:
class: DatasetH
module_path: qlib.data.dataset
kwargs:
handler:
class: Alpha158
module_path: qlib.contrib.data.handler
kwargs:
start_time: 2008-01-01
end_time: 2020-08-01
instruments: csi300
segments:
train: [2008-01-01, 2014-12-31]
valid: [2015-01-01, 2016-12-31]
test: [2017-01-01, 2020-08-01]
- class: 数据集类名,如 DatasetH、DatasetLP 等。
- module_path: 数据集类所在的模块路径。
- kwargs: 数据集初始化参数,主要包括 handler 和 segments。
handler 定义了数据的处理方式,包括特征提取、标签生成等。segments 定义了训练、验证和测试数据的时间区间。
记录器配置
record 部分定义了实验结果的记录方式,包括信号记录、组合分析记录等。配置格式如下:
record:
- class: SignalRecord
module_path: qlib.workflow.record_temp
kwargs: {}
- class: PortAnaRecord
module_path: qlib.workflow.record_temp
kwargs:
config: *port_analysis_config
- class: 记录器类名,如 SignalRecord、PortAnaRecord 等。
- module_path: 记录器类所在的模块路径。
- kwargs: 记录器初始化参数,如组合分析配置等。
通过配置多个记录器,可以同时记录不同类型的实验结果,如预测信号、回测报告等。
配置文件最佳实践
编写配置文件时,建议遵循以下最佳实践:
- 使用锚点和引用:通过 YAML 的锚点(&)和引用(*)功能,减少重复配置,提高配置文件的可维护性。例如,将 market 和 benchmark 定义为锚点,在其他地方引用。
- 分阶段配置时间区间:将数据处理、模型训练、回测等不同阶段的时间区间分开配置,确保各阶段使用的数据不重叠,避免数据泄露。
- 明确注释:对关键参数添加注释,说明其含义和取值范围,方便自己和他人理解配置文件。
- 版本控制:将配置文件纳入版本控制系统(如 Git),跟踪配置的变更历史,便于复现不同时期的实验结果。
- 参数调优:对于模型参数等需要调优的部分,可以使用变量或外部文件的方式管理,便于批量生成不同参数组合的配置文件。
工作流组件与扩展
QLib 的工作流系统采用模块化设计,各个组件之间松耦合,用户可以根据自己的需求扩展或替换现有组件。本节将介绍工作流的核心组件及其扩展方式。
核心组件
QLib 工作流的核心组件包括:
- 数据集(Dataset) :负责数据的加载、处理和划分。
- 模型(Model) :负责预测模型的训练和预测。
- 策略(Strategy) :负责根据预测信号生成交易决策。
- 执行器(Executor) :负责模拟交易执行过程。
- 记录器(Recorder) :负责记录实验过程和结果。
这些组件通过统一的接口交互,形成一个完整的工作流。用户可以通过实现这些接口来扩展工作流功能。
自定义数据集
QLib 提供了 Dataset 基类,用户可以通过继承该基类实现自定义数据集。自定义数据集需要实现 prepare 方法,该方法返回处理后的特征和标签数据。
以下是一个自定义数据集的示例:
from qlib.data.dataset import Dataset
class CustomDataset(Dataset):
def __init__(self, handler, segments, **kwargs):
super().__init__(handler, segments, **kwargs)
def prepare(self, segment):
# 调用父类方法获取基础数据
df = super().prepare(segment)
# 自定义数据处理
df = self.custom_process(df)
return df
def custom_process(self, df):
# 实现自定义的数据处理逻辑
# 例如,添加新的特征
df['new_feature'] = df['close'] / df['open'] - 1
return df
然后,在配置文件中指定自定义数据集:
dataset:
class: CustomDataset
module_path: my_dataset
kwargs:
handler:
class: Alpha158
module_path: qlib.contrib.data.handler
kwargs: *data_handler_config
segments:
train: [2008-01-01, 2014-12-31]
valid: [2015-01-01, 2016-12-31]
test: [2017-01-01, 2020-08-01]
自定义模型
QLib 的模型接口基于 sklearn 风格设计,用户可以通过继承 Model 基类实现自定义模型。自定义模型需要实现 fit 和 predict 方法。
以下是一个自定义线性回归模型的示例:
from qlib.model.base import Model
from sklearn.linear_model import LinearRegression
import pandas as pd
class CustomLinearModel(Model):
def __init__(self, **kwargs):
super().__init__(** kwargs)
self.model = LinearRegression(**kwargs)
def fit(self, x_train, y_train, x_valid=None, y_valid=None):
# 训练模型
self.model.fit(x_train, y_train)
# 如果提供了验证集,可以在这里实现早停等逻辑
return self
def predict(self, x_test):
# 预测
y_pred = self.model.predict(x_test)
# 将预测结果转换为Series
return pd.Series(y_pred, index=x_test.index, name='pred')
在配置文件中使用自定义模型:
model:
class: CustomLinearModel
module_path: my_model
kwargs:
fit_intercept: True
normalize: True
自定义记录器
记录器用于记录实验过程中的关键信息和结果。用户可以通过继承 Recorder 基类实现自定义记录器,实现特定的记录逻辑。
以下是一个自定义记录器的示例,用于记录模型的特征重要性:
from qlib.workflow.recorder import Recorder
class FeatureImportanceRecorder(Recorder):
def __init__(self, model, dataset, **kwargs):
super().__init__(**kwargs)
self.model = model
self.dataset = dataset
def generate(self):
# 获取特征名称
feature_names = self.dataset.prepare('train').columns
# 获取特征重要性
importances = self.model.feature_importances_
# 创建特征重要性DataFrame
importance_df = pd.DataFrame({
'feature': feature_names,
'importance': importances
}).sort_values('importance', ascending=False)
# 保存特征重要性
self.save_objects(feature_importance=importance_df)
# 记录特征重要性到日志
self.logger.info('Feature importance:\n{}'.format(importance_df.head(10)))
在配置文件中添加自定义记录器:
record:
- class: SignalRecord
module_path: qlib.workflow.record_temp
kwargs: {}
- class: PortAnaRecord
module_path: qlib.workflow.record_temp
kwargs:
config: *port_analysis_config
- class: FeatureImportanceRecorder
module_path: my_recorder
kwargs:
model: <MODEL>
dataset: <DATASET>
工作流扩展示例
下面通过一个完整的示例,展示如何扩展 QLib 工作流,实现自定义的数据处理、模型训练和结果记录。
- 自定义数据处理器:添加新的技术指标特征。
- 自定义模型:实现一个基于随机森林的预测模型。
- 自定义记录器:记录模型的特征重要性和预测分布。
通过这种方式,用户可以根据自己的研究需求,灵活扩展 QLib 的工作流功能,实现更复杂的量化研究任务。
批量任务执行与管理
在量化研究中,经常需要同时执行多个任务,例如测试不同的模型参数、比较不同的策略逻辑、验证策略在不同市场环境下的表现等。QLib 提供了任务管理模块,支持批量任务的生成、存储、执行和结果收集,极大地提高了多任务处理的效率。
任务生成
任务生成器(TaskGen)用于根据任务模板生成多个相关任务。QLib 提供了 RollingGen 类,支持基于时间滚动的任务生成,用于验证模型在不同时间段的表现稳定性。
以下是使用 RollingGen 生成滚动回测任务的示例:
from qlib.workflow.task.gen import RollingGen
# 定义任务模板
TASK_TEMPLATE = {
"model": {
"class": "LGBModel",
"module_path": "qlib.contrib.model.gbdt",
"kwargs": {
"loss": "mse",
"learning_rate": 0.04,
"max_depth": 8,
}
},
"dataset": {
"class": "DatasetH",
"module_path": "qlib.data.dataset",
"kwargs": {
"handler": {
"class": "Alpha158",
"module_path": "qlib.contrib.data.handler",
"kwargs": {
"start_time": "{start_time}",
"end_time": "{end_time}",
"instruments": "csi300",
}
},
"segments": {
"train": ["{train_start}", "{train_end}"],
"valid": ["{valid_start}", "{valid_end}"],
"test": ["{test_start}", "{test_end}"],
}
}
}
}
# 创建RollingGen实例
rg = RollingGen(
step=6, # 每6个月滚动一次
rtype="date",
ds_extra_len=40, # 额外的数据长度,用于特征计算
train_start="2008-01-01",
valid_start="2014-01-01",
valid_end="2016-12-31",
test_start="2017-01-01",
test_end="2020-08-01",
)
# 生成任务列表
tasks = rg.generate(TASK_TEMPLATE)
RollingGen 会根据指定的时间间隔,自动生成多个任务,每个任务对应不同的训练、验证和测试时间段。这种方式可以有效验证模型的时间稳健性,避免过拟合到特定时间段的数据。
任务存储
生成的任务需要存储到任务队列中,以便后续执行。QLib 使用 MongoDB 作为任务存储数据库,支持任务的持久化和分布式执行。
使用 TaskManager 管理任务的存储和生命周期:
from qlib.workflow.task.manage import TaskManager
from qlib.config import C
# 配置MongoDB
C["mongo"] = {
"task_url": "mongodb://localhost:27017/",
"task_db_name": "quant_tasks"
}
# 创建TaskManager实例
tm = TaskManager()
# 将任务添加到数据库
task_ids = tm.create_task(tasks)
print(f"Created {len(task_ids)} tasks")
TaskManager 会将任务存储到 MongoDB 中,并为每个任务分配唯一的 ID。任务的状态会被实时更新,包括等待中、运行中、已完成、失败等。
任务执行
任务执行可以通过 Trainer 模块实现,支持本地执行和分布式执行。TrainerRM 基于 TaskManager,能够自动从数据库中获取待执行任务,并管理任务的执行过程。
以下是使用 TrainerRM 执行任务的示例:
from qlib.model.trainer import TrainerRM
from qlib.workflow.task.manage import run_task
# 创建TrainerRM实例
trainer = TrainerRM(
task_manager=tm,
task_fn=run_task,
experiment_name="rolling_backtest"
)
# 执行任务
recorders = trainer.train()
TrainerRM 会自动从 MongoDB 中获取状态为"WAITING"的任务,调用 run_task 函数执行任务,并更新任务状态。执行完成后,会返回所有任务的记录器(Recorder),用于后续结果收集和分析。
结果收集与分析
任务执行完成后,需要对多个任务的结果进行收集和分析。QLib 提供了 Collector、Group 和 Ensemble 组件,支持结果的合并、分组和集成。
以下是收集和分析滚动回测结果的示例:
from qlib.workflow.collector import Collector, Group, Ensemble
from qlib.workflow.recorder import Recorder
# 创建Collector实例
collector = Collector(
experiment=recorders,
process_list=[
# 按时间分组
Group(func=lambda rec: rec.info["task"]['dataset']['kwargs']['segments']['test'][0], name="test_start"),
# 集成每个组的结果
Ensemble(mean=True, name="mean"),
]
)
# 执行收集和处理
result = collector()
# 打印集成结果
print("Ensemble result:", result["mean"])
Collector 首先按测试开始时间对结果进行分组,然后对每个组的结果进行平均集成。通过这种方式,可以得到不同时间段的回测结果及其平均值,全面评估策略的稳健性。
批量任务管理最佳实践
批量任务管理时,建议遵循以下最佳实践:
- 合理划分任务:将大规模任务分解为多个子任务,每个子任务专注于特定的参数组合或时间区间,便于并行执行和结果比较。
- 使用分布式执行:对于大量任务,使用分布式执行框架(如 Ray)提高执行效率。QLib 的 Trainer 模块支持与 Ray 集成,实现分布式任务执行。
- 监控任务状态:定期检查任务的执行状态,及时发现和解决失败的任务。TaskManager 提供了查询任务状态的接口,方便监控任务进展。
- 结果可视化:将多个任务的结果可视化,如绘制不同参数下的绩效指标对比图、不同时间段的回测曲线等,直观展示任务结果。
- 自动化报告生成:使用 QLib 的报告生成功能,自动生成包含多个任务结果的分析报告,提高结果分析的效率。
工作流自动化实践案例
为了更好地理解 QLib 工作流自动化的应用,本节将通过一个完整的实践案例,展示如何构建从数据准备到策略评估的自动化工作流。
案例背景
本案例旨在构建一个基于机器学习的量化策略,包括以下步骤:
- 数据准备:使用 Alpha158 因子集提取特征。
- 模型训练:使用 LightGBM 模型训练预测模型。
- 策略回测:使用 TopkDropoutStrategy 进行回测。
- 结果分析:生成回测报告和特征重要性分析。
- 批量验证:通过滚动回测验证策略的稳健性。
配置文件编写
首先,编写工作流配置文件 workflow_config.yaml:
qlib_init:
provider_uri: "~/.qlib/qlib_data/cn_data"
region: cn
market: &market csi300
benchmark: &benchmark SH000300
data_handler_config: &data_handler_config
start_time: 2008-01-01
end_time: 2020-08-01
fit_start_time: 2008-01-01
fit_end_time: 2014-12-31
instruments: *market
port_analysis_config: &port_analysis_config
strategy:
class: TopkDropoutStrategy
module_path: qlib.contrib.strategy.strategy
kwargs:
topk: 50
n_drop: 5
signal: <PRED>
backtest:
start_time: 2017-01-01
end_time: 2020-08-01
account: 100000000
benchmark: *benchmark
exchange_kwargs:
limit_threshold: 0.095
deal_price: close
open_cost: 0.0005
close_cost: 0.0015
min_cost: 5
task:
model:
class: LGBModel
module_path: qlib.contrib.model.gbdt
kwargs:
loss: mse
colsample_bytree: 0.8879
learning_rate: 0.0421
subsample: 0.8789
lambda_l1: 205.6999
lambda_l2: 580.9768
max_depth: 8
num_leaves: 210
num_threads: 20
dataset:
class: DatasetH
module_path: qlib.data.dataset
kwargs:
handler:
class: Alpha158
module_path: qlib.contrib.data.handler
kwargs: *data_handler_config
segments:
train: [2008-01-01, 2014-12-31]
valid: [2015-01-01, 2016-12-31]
test: [2017-01-01, 2020-08-01]
record:
- class: SignalRecord
module_path: qlib.workflow.record_temp
kwargs: {}
- class: PortAnaRecord
module_path: qlib.workflow.record_temp
kwargs:
config: *port_analysis_config
- class: FeatureImportanceRecorder
module_path: my_recorder
kwargs:
model: <MODEL>
dataset: <DATASET>
执行单任务工作流
使用 qrun 命令执行单任务工作流:
qrun workflow_config.yaml
执行完成后,QLib 会自动生成模型文件、预测结果、回测报告和特征重要性分析,并保存到实验记录目录中。
执行批量滚动回测
编写滚动回测脚本 rolling_backtest.py:
import qlib
from qlib.constant import REG_CN
from qlib.workflow.task.gen import RollingGen
from qlib.workflow.task.manage import TaskManager
from qlib.model.trainer import TrainerRM
from qlib.workflow.task.manage import run_task
from qlib.config import C
# 初始化QLib
provider_uri = "~/.qlib/qlib_data/cn_data"
qlib.init(provider_uri=provider_uri, region=REG_CN)
# 配置MongoDB
C["mongo"] = {
"task_url": "mongodb://localhost:27017/",
"task_db_name": "rolling_tasks"
}
# 定义任务模板
TASK_TEMPLATE = {
"model": {
"class": "LGBModel",
"module_path": "qlib.contrib.model.gbdt",
"kwargs": {
"loss": "mse",
"colsample_bytree": 0.8879,
"learning_rate": 0.0421,
"subsample": 0.8789,
"lambda_l1": 205.6999,
"lambda_l2": 580.9768,
"max_depth": 8,
"num_leaves": 210,
"num_threads": 20
}
},
"dataset": {
"class": "DatasetH",
"module_path": "qlib.data.dataset",
"kwargs": {
"handler": {
"class": "Alpha158",
"module_path": "qlib.contrib.data.handler",
"kwargs": {
"start_time": "{start_time}",
"end_time": "{end_time}",
"instruments": "csi300",
}
},
"segments": {
"train": ["{train_start}", "{train_end}"],
"valid": ["{valid_start}", "{valid_end}"],
"test": ["{test_start}", "{test_end}"],
}
}
},
"record": [
{
"class": "SignalRecord",
"module_path": "qlib.workflow.record_temp",
"kwargs": {}
},
{
"class": "PortAnaRecord",
"module_path": "qlib.workflow.record_temp",
"kwargs": {
"config": {
"strategy": {
"class": "TopkDropoutStrategy",
"module_path": "qlib.contrib.strategy.strategy",
"kwargs": {
"topk": 50,
"n_drop": 5,
"signal": "<PRED>"
}
},
"backtest": {
"start_time": "{test_start}",
"end_time": "{test_end}",
"account": 100000000,
"benchmark": "SH000300",
"exchange_kwargs": {
"limit_threshold": 0.095,
"deal_price": "close",
"open_cost": 0.0005,
"close_cost": 0.0015,
"min_cost": 5
}
}
}
}
}
]
}
# 生成滚动任务
rg = RollingGen(
step=12, # 每年滚动一次
rtype="date",
ds_extra_len=40,
train_start="2008-01-01",
valid_start="2013-01-01",
valid_end="2014-12-31",
test_start="2015-01-01",
test_end="2020-08-01",
)
tasks = rg.generate(TASK_TEMPLATE)
# 存储任务到MongoDB
tm = TaskManager()
task_ids = tm.create_task(tasks)
print(f"Created {len(task_ids)} tasks")
# 执行任务
trainer = TrainerRM(task_manager=tm, task_fn=run_task, experiment_name="rolling_backtest")
recorders = trainer.train()
# 收集结果
from qlib.workflow.collector import Collector, Group, Ensemble
collector = Collector(
experiment=recorders,
process_list=[
Group(func=lambda rec: rec.info["task"]['dataset']['kwargs']['segments']['test'][0], name="test_start"),
Ensemble(mean=True, name="mean"),
]
)
result = collector()
print("Rolling backtest ensemble result:", result["mean"])
运行脚本执行批量滚动回测:
python rolling_backtest.py
结果分析与可视化
回测完成后,可以使用 QLib 的可视化工具分析结果:
from qlib.contrib.evaluate import risk_analysis
from qlib.workflow.recorder import Recorder
import matplotlib.pyplot as plt
# 加载回测报告
recorder = Recorder.load_experiment(experiment_name="rolling_backtest")
report_normal = recorder.load_object("portfolio_analysis/report_normal.pkl")
# 计算风险指标
analysis = risk_analysis(report_normal["return"] - report_normal["bench"])
print(analysis)
# 绘制净值曲线
plt.figure(figsize=(12, 6))
plt.plot(report_normal["cum_return"], label="Strategy")
plt.plot(report_normal["cum_bench"], label="Benchmark")
plt.title("Cumulative Return")
plt.xlabel("Date")
plt.ylabel("Cumulative Return")
plt.legend()
plt.show()
通过这个案例,我们展示了如何使用 QLib 的工作流自动化功能,从单任务执行到批量任务管理,构建完整的量化研究流程。这种自动化流程不仅提高了研究效率,还保证了实验的可重复性和稳健性。
工作流自动化的高级应用
QLib 的工作流自动化功能可以进一步扩展,应用于更复杂的量化研究场景。本节将介绍几个高级应用案例,展示工作流自动化的强大能力。
参数优化工作流
参数优化是量化策略开发的重要环节。通过工作流自动化,可以实现参数的批量测试和优化。以下是一个基于 Optuna 的参数优化工作流示例:
- 定义参数空间。
- 使用 TaskGen 生成不同参数组合的任务。
- 执行任务并记录绩效指标。
- 根据绩效指标选择最优参数。
这种方式可以自动寻找最优的模型参数,提高策略的表现。
多策略组合工作流
通过工作流自动化,可以实现多个子策略的并行训练和组合。例如:
- 为每个子策略定义独立的任务。
- 并行执行所有子策略任务。
- 收集各子策略的预测信号。
- 使用集成方法组合多个信号,生成最终的交易决策。
多策略组合可以分散风险,提高整体策略的稳健性。
在线学习工作流
在线学习是指模型能够实时更新以适应市场变化。通过工作流自动化,可以实现定期模型更新和预测:
- 使用 TaskManager 定期生成更新任务。
- 训练新的模型并评估性能。
- 如果新模型表现更好,则更新在线预测服务。
这种方式可以使策略保持时效性,适应不断变化的市场环境。
跨市场回测工作流
QLib 支持多市场数据,通过工作流自动化,可以实现跨市场策略的回测和比较:
- 为不同市场(如 A 股、美股)定义任务模板。
- 生成并执行跨市场任务。
- 比较策略在不同市场的表现,分析策略的普适性。
跨市场回测可以帮助发现具有普遍适用性的策略逻辑,提高策略的可靠性。
工作流自动化的挑战与解决方案
尽管工作流自动化带来了诸多便利,但在实际应用中仍面临一些挑战。本节将讨论常见的挑战及解决方案。
挑战
- 复杂性管理:随着任务数量和复杂度的增加,工作流配置和管理变得困难。
- 资源消耗:批量任务执行需要大量计算资源,可能导致执行效率低下。
- 错误处理:任务执行过程中可能出现各种错误,需要有效的错误处理机制。
- 版本控制:模型、数据、配置等版本众多,难以跟踪和管理。
解决方案
- 模块化设计:将复杂工作流分解为多个模块,每个模块专注于特定功能,降低管理复杂度。
- 分布式计算:使用分布式计算框架(如 Ray、Spark)并行执行任务,提高资源利用率和执行效率。
- 自动化错误处理:在任务执行过程中添加重试机制、错误日志记录和告警功能,及时发现和解决问题。
- 实验管理系统:使用 MLflow、DVC 等实验管理工具,跟踪模型、数据和配置的版本,确保实验的可重复性。
- 监控与可视化:构建工作流监控系统,实时监控任务执行状态和资源使用情况,通过可视化界面直观展示工作流状态。
通过这些解决方案,可以有效应对工作流自动化中的挑战,提高量化研究的效率和可靠性。