基于滚动窗口的 XGBoost 机器学习因子生成与回测框架。
本项目从原始量化小组项目中提取了 XGBoost 因子生成模块,是一个可独立运行的完整子项目,涵盖:
- 基于滚动窗口(Rolling Window)的 XGBoost 因子生成
- 可选的降维预处理(PCA / AutoEncoder)
- Grid Search 和 Optuna 两种超参数搜索方式
- 增量式断点续跑机制
- 单因子分组回测与绩效分析
quant_group-xgb/
├── README.md
├── setup.py # 包安装配置
├── .gitignore
│
├── configs/ # 配置文件目录
│ ├── project_config.yaml # 【必须修改】项目根目录配置
│ ├── project_config.yaml.example # 配置示例
│ ├── factor_gen/
│ │ ├── xgb_factor.yaml # XGBoost 因子生成配置
│ │ ├── xgb_factor.yaml.example # 配置示例(含全部选项注释)
│ │ ├── train_reducer.yaml # 降维器训练配置
│ │ └── train_reducer.yaml.example # 配置示例
│ └── backtest/
│ ├── backtest.yaml # 回测配置
│ └── backtest.yaml.example # 配置示例
│
├── run/ # 可执行入口脚本
│ ├── gen_xgb_factor.py # 因子生成主脚本
│ ├── train_reducer.py # 降维器训练脚本
│ └── run_backtest.py # 回测脚本
│
├── src/
│ └── quant_library/ # 核心库
│ ├── utils/ # 通用工具
│ │ ├── format_display.py # 控制台输出格式化
│ │ └── miscs.py # 路径解析、频率工具等
│ ├── factor_gen/
│ │ └── rolling_window/ # 滚动窗口因子生成模块
│ │ ├── config.py # 配置管理器(YAML 解析)
│ │ ├── dataset.py # 数据集封装(数据加载与缓存)
│ │ ├── dataloader.py # 滚动窗口数据切分
│ │ ├── factor_generator.py # 因子生成器(主流程编排)
│ │ ├── param_searcher.py # 超参数搜索(Grid / Optuna)
│ │ ├── factor_model/
│ │ │ ├── base_factor_model.py # 模型抽象基类
│ │ │ ├── xgb_factor_model.py # XGBoost 实现
│ │ │ └── utils.py # ModelManager
│ │ └── reducer/
│ │ ├── base_reducer.py # 降维器抽象基类
│ │ ├── pca_reducer.py # PCA 降维实现
│ │ ├── autoencoder_reducer.py # AutoEncoder 降维实现
│ │ └── utils.py # ReducerManager 及工具函数
│ └── backtest/
│ └── single_factor.py # 单因子分组回测引擎
│
└── data/ # 数据目录(不纳入版本控制)
├── source_data/ # 【需要手动复制】原始输入数据
├── factors/ # 【自动生成】生成的因子文件
├── models/ # 【自动生成】训练好的模型文件
├── reducers/ # 【自动生成】训练好的降维器
└── backtest_results/ # 【自动生成】回测结果
Python 版本要求:>= 3.11
核心依赖:
| 依赖包 | 用途 |
|---|---|
| xgboost >= 1.7 | XGBoost 模型训练与预测 |
| optuna >= 3.0 | Optuna 超参数搜索 |
| scikit-learn >= 1.0 | PCA、ParameterGrid、StandardScaler |
| torch >= 2.0 | AutoEncoder 降维(可选) |
| pytorch-lightning >= 2.0 | AutoEncoder 训练框架(可选) |
| pandas >= 1.3 | 数据处理 |
| numpy >= 1.21 | 数值计算 |
| pyarrow >= 11.0 | Parquet 文件读写 |
| pyyaml >= 6.0 | YAML 配置解析 |
| statsmodels >= 0.14 | 市值中性化(回测模块) |
| matplotlib >= 3.7 | 回测结果绘图 |
| tqdm >= 4.65 | 进度条显示 |
| tabulate >= 0.9 | 表格美化打印 |
| joblib >= 1.2 | 降维器模型序列化 |
| scipy >= 1.9 | Spearman 相关系数计算 |
安装命令(推荐使用虚拟环境):
# 创建并激活虚拟环境
python -m venv .venv
# Windows:
.venv\Scripts\activate
# Linux/macOS:
source .venv/bin/activate
# 以可编辑模式安装项目(会自动安装所有依赖)
pip install -e .注意:如果你的机器有 NVIDIA GPU 并希望启用 CUDA 加速,请参考 PyTorch 官网 安装与 CUDA 版本匹配的
torch,再安装本项目。XGBoost 在 CUDA 可用时会自动使用 GPU。
所有输入数据均位于 data/source_data/ 目录下。请从原始项目(quant_group/data/source_data/)中复制以下文件:
| 文件名 | 格式 | 说明 | 使用阶段 |
|---|---|---|---|
data_in_sample_monthly.parquet |
Parquet | 月频训练数据集,包含特征列、目标列(r_shift)、日期(TradingDate)、股票代码(Stkcd) |
因子生成 |
merge_daily_info.parquet |
Parquet | 日频基础数据,用于因子保存时补全日期序列、前向填充缺失值,至少包含 TradingDate、Stkcd 两列 |
因子保存(必须) |
adj_close.parquet |
Parquet | 复权收盘价,包含 TradingDate、Stkcd、price 三列 |
回测 |
Suspension_Limit.parquet |
Parquet | 停牌/涨跌停状态表,包含 TradingDate、Stkcd、Suspension、LimitStatus 四列 |
回测 |
history-all.pq |
Parquet | 股票池矩阵,index 为日期,列为股票代码(如 000001.SZ),值为 0/1 表示是否在池内 |
回测 |
mv_turn.parquet |
Parquet | 市值及换手率数据,包含 TradingDate、Stkcd、MarketValue 等列(市值中性化时使用) |
回测 |
最低要求:如果只运行因子生成(不做回测),只需
data_in_sample_monthly.parquet和merge_daily_info.parquet;如果还需要回测,则需要全部上述文件。
运行后会自动在 data/ 下创建以下目录(无需手动创建):
| 路径 | 内容 |
|---|---|
data/factors/sample_monthly/XGB/ |
生成的因子 Parquet 文件,每个因子一个文件,以因子名命名 |
data/models/sample_monthly/XGB/<factor_name>/ |
各滚动窗口训练好的 XGBoost 模型(.json)和参数搜索记录(.csv) |
data/reducers/sample_monthly/rolling_monthly/<reducer_tag>/ |
各滚动窗口训练好的降维器(.joblib 等) |
data/backtest_results/sample_monthly/XGB/<factor_name>/ |
回测净值曲线图、净值 CSV、绩效分析 CSV |
project_root: "D:/Codes/Projects/quant_group-xgb"请将 project_root 修改为本项目在你机器上的绝对路径。 所有其他配置文件中的相对路径都以此为基础进行解析。
因子生成的核心配置,主要参数说明:
| 配置项 | 说明 |
|---|---|
merge_daily_info_path |
日频基础数据路径,固定为 "data/source_data/merge_daily_info.parquet" |
factor_save_dir |
因子文件输出目录,固定为 "data/factors/sample_monthly/XGB" |
dataset_config.data_in_sample_path |
训练数据集路径,固定为 "data/source_data/data_in_sample_monthly.parquet" |
model_config.model_save_dir |
模型文件保存目录,固定为 "data/models/sample_monthly/XGB" |
reducer_config.reducer_save_dir |
降维器保存目录,固定为 "data/reducers/sample_monthly" |
通常无需修改上述路径,它们与 data/ 下的目录结构完全对应。
| 配置项 | 说明 | 默认值 |
|---|---|---|
data_loader_config.rolling_frequency |
滚动窗口步进频率,列表形式,每个值生成一组因子 | [monthly, yearly] |
data_loader_config.training_window |
训练窗口长度 | 10y |
data_loader_config.val_ratio |
验证集在训练窗口内的占比 | 0.2 |
data_loader_config.factor_min_date |
因子生成起始日期(即测试集最早日期) | '2020' |
| 配置项 | 说明 | 默认值 |
|---|---|---|
model_config.X_rank |
是否对特征做截面排序,列表形式,[true, false] 会生成两组因子 |
[false, true] |
model_config.y_rank |
是否使用 Pairwise Ranking 目标(XGBRanker),同上可列表 |
[false, true] |
rolling_frequency、X_rank、y_rank、reducer_choice四个列表项会做笛卡尔积展开,每个组合独立生成一个因子文件。
| 配置项 | 说明 |
|---|---|
reducer_config.reducer_choice |
降维方法列表,支持 pca(需指定 n_components)和 autoencoder(需指定 encoding_dim);注释掉或留空则不使用降维 |
| 配置项 | 说明 | 默认值 |
|---|---|---|
param_searcher_config.search_method |
搜索方法:optuna 或 grid |
optuna |
param_searcher_config.n_trials |
Optuna 每个窗口的搜索轮次 | 10 |
param_searcher_config.no_search |
设为 true 时跳过搜索,直接使用已有模型(或默认参数)生成预测 |
false |
param_searcher_config.search_space |
Optuna 搜索空间,每个参数指定 type/low/high 等 |
见配置文件 |
param_searcher_config.search_grid |
Grid Search 参数网格(仅 search_method: grid 时生效) |
见配置文件 |
降维器训练配置,只包含 dataset_config、data_loader_config、reducer_config 三部分,结构与 xgb_factor.yaml 对应部分完全一致。仅当 xgb_factor.yaml 中启用了降维器时,才需要先运行此步骤。
需确认 reducer_choice 中的方法和参数与 xgb_factor.yaml 中保持一致,否则因子生成时会找不到对应的降维器文件。
回测配置,各路径均为相对项目根目录的字符串,直接填写即可:
| 配置项 | 说明 |
|---|---|
paths_config.adj_close_path |
复权收盘价文件路径 |
paths_config.suspension_limit_path |
停牌/涨跌停状态文件路径 |
paths_config.stock_pool_path |
股票池文件路径 |
paths_config.market_value_path |
市值文件路径 |
paths_config.factor_path |
因子文件或目录路径;填目录时会对目录下所有 parquet 批量回测 |
paths_config.result_dir |
回测结果输出目录 |
backtest_config.start_date |
回测起始日期 |
backtest_config.backtest_frequency |
调仓频率:monthly / weekly / daily |
backtest_config.neutralize |
中性化方式:cap(市值中性化)或 Null(不中性化) |
backtest_config.save_backtest_result |
是否保存净值图和绩效 CSV |
# 在项目根目录下执行
cd D:/Codes/Projects/quant_group-xgb
# 创建虚拟环境(推荐)
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # Linux/macOS
# 安装项目及所有依赖
pip install -e .按照 第3节 的说明,将所需数据文件复制到 data/source_data/ 目录中。
data/source_data/
├── data_in_sample_monthly.parquet ← 必须
├── merge_daily_info.parquet ← 必须
├── adj_close.parquet ← 回测时需要
├── Suspension_Limit.parquet ← 回测时需要
├── history-all.pq ← 回测时需要
└── mv_turn.parquet ← 回测时需要(市值中性化)
这是启动前最重要的一步,必须执行。
打开 configs/project_config.yaml,将 project_root 修改为本项目在你机器上的实际绝对路径:
# configs/project_config.yaml
project_root: "D:/Codes/Projects/quant_group-xgb" # ← 改成你的实际路径注意:路径使用正斜杠
/或双反斜杠\\,不要使用单反斜杠\。
根据需要调整 configs/factor_gen/xgb_factor.yaml 中的配置(如 rolling_frequency、n_trials 等)。
仅当 xgb_factor.yaml 中 reducer_config.reducer_choice 配置了降维方法(PCA 或 AutoEncoder)时,需要先执行此步骤。如果 reducer_choice 为空或注释掉,则跳过此步。
降维器需要在因子生成之前,针对每个滚动窗口分别训练并保存。
# 在项目根目录下执行
python run/train_reducer.py
# 或指定配置文件路径
python run/train_reducer.py --config configs/factor_gen/train_reducer.yaml确认 train_reducer.yaml 中的 reducer_choice 配置与 xgb_factor.yaml 中一致。
训练好的降维器会保存到 data/reducers/sample_<freq>/rolling_<freq>/<reducer_tag>/ 目录下,每个滚动窗口对应一个文件(以训练区间命名,如 202001_202012_state.joblib)。
支持断点续跑:如果某些窗口的降维器文件已存在,默认跳过(可在配置中设 overwrite: true 强制覆盖)。
# 在项目根目录下执行
python run/gen_xgb_factor.py
# 或指定配置文件路径
python run/gen_xgb_factor.py --config configs/factor_gen/xgb_factor.yaml运行逻辑:
- 读取
xgb_factor.yaml,通过ConfigManager展开所有配置组合(rolling_frequency × X_rank × y_rank × reducer_choice的笛卡尔积)。 - 对每个配置组合,实例化一个
RollingWindowFG因子生成器:- 加载训练数据集(首次加载后会缓存)
- 按滚动窗口切分数据(训练集/验证集/测试集),每个窗口步进一个
rolling_frequency周期 - 若配置了降维器,则加载对应窗口的降维器模型,对特征做降维变换
- 使用 Optuna 或 Grid Search 对 XGBoost 超参数进行搜索,找到验证集上损失最小的参数组合
- 用最优模型对测试集(当期未来收益预测区间)进行预测
- 汇总所有滚动窗口的测试集预测结果,与
merge_daily_info做左连接,补全缺失日期并前向填充,保存为 Parquet 文件。
输出:每个配置组合生成一个因子文件,保存到 data/factors/sample_<freq>/<model_name>/<factor_name>.parquet,文件名即为因子名(如 XGB_optuna_window(10y+1m)_PCAfrac50.parquet)。
增量续跑:若因子文件已存在,程序会读取已有文件中的日期,仅对尚未覆盖的滚动窗口重新计算,无需从头运行。
# 在项目根目录下执行
python run/run_backtest.py
# 或指定配置文件路径
python run/run_backtest.py --config configs/backtest/single_factor.yaml运行前确认 single_factor.yaml 中的 factor_path 指向正确的因子目录或文件。
回测流程:
- 读取复权收盘价、停牌/涨跌停状态、股票池、市值数据
- 对每个因子文件:
- 筛选指定日期范围和股票池(自动排除创业板 300xxx、科创板 688xxx)
- 对因子做截面 Z-score 标准化
- (可选)对数市值中性化(设
neutralize: cap) - 按因子值分为 10 个分位组,进行滚动调仓回测,计入换手成本
- 计算各分位组净值曲线、年化收益、夏普比率、最大回撤等绩效指标
- 结果保存到
data/backtest_results/sample_<freq>/<model_name>/<factor_name>/
data_in_sample_{freq}.parquet
│
▼
FactorDataset ← 加载并缓存原始数据
│
▼
FactorDataLoader ← 按滚动窗口切分 train/val/test
│
├──(可选)──▶ ReducerManager.load() ← 加载预训练降维器
│ │
│ ▼
│ transform(train_X / val_X / test_X)
│
▼
ParamSearcher.search()
│ Grid Search: sklearn.ParameterGrid 枚举
│ Optuna: TPESampler + SQLite 持久化
│ 每轮训练 XGBFactorModel,在验证集计算 MAE / -Spearman 作为损失
▼
best_model.predict(test_X) ← 对测试集预测
│
▼
汇总所有窗口预测结果
│
▼
merge_daily_info.parquet ← 补全日期、前向填充
│
▼
data/factors/.../因子.parquet ← 最终因子输出
因子文件名由以下部分拼接而成:
{model_tag}_{search_tag}_{window_tag}[_{reducer_tag}]
例如:
XGB_optuna_window(10y+1m)— XGBoost,Optuna 搜索,10 年训练窗口,月频步进,无降维XGB_optuna_window(10y+1m)_PCAfrac50— 同上,叠加 PCA 降维(保留 50% 方差)XGB_RankX_optuna_window(10y+1m)— 特征做截面排序后训练XGB_Ranky_grid_window(10y+1y)— 使用 Pairwise Ranking 目标,Grid Search,年频步进
- 因子文件:若
data/factors/.../因子名.parquet已存在,会跳过其中已有日期对应的滚动窗口。 - Grid Search 参数记录:每个窗口的搜索记录保存为
params_{train_range}.csv,下次运行时自动跳过已搜索的参数组合。 - Optuna Study:每个窗口的 Optuna study 以 SQLite 文件(
study_{train_range}.db)持久化,中断后可续跑剩余 trials。 - 降维器:若对应文件已存在,默认跳过训练(
overwrite: false)。
Q:运行时报 FileNotFoundError: Could not find configs/project_config.yaml
A:请确认:
- 运行脚本时的工作目录是项目根目录(
quant_group-xgb/)。 configs/project_config.yaml文件存在且内容正确。
推荐在项目根目录下运行脚本:
cd D:/Codes/Projects/quant_group-xgb
python run/gen_xgb_factor.pyQ:报错 Reducer not found at ...
A:在 xgb_factor.yaml 中配置了降维器,但尚未训练降维器。请先运行:
python run/train_reducer.pyQ:Optuna 搜索速度慢
A:可在 xgb_factor.yaml 中减小 n_trials(如设为 5),或将 no_search 设为 true 跳过搜索直接使用默认参数,快速验证流程。
Q:想不使用降维器,直接用原始特征
A:在 xgb_factor.yaml 中注释掉或删除 reducer_config.reducer_choice 下的所有条目,或将 reducer_choice 设为 null。
Q:回测图显示乱码
A:确保系统安装了 SimHei(黑体)字体。回测模块使用该字体显示中文标签。
Q:如何只对部分因子文件做回测
A:在 single_factor.yaml 的 factor_path 中直接指定具体的因子文件路径,支持通配符(如 data/factors/sample_monthly/XGB/XGB_optuna*.parquet)。