4. 策略代码仓库结构设计:模块化目录组织、配置文件分离、依赖管理
量化策略从研究到生产,代码仓库的结构直接决定了团队协作效率、回测复现能力以及线上监控的稳定性。一个设计良好的仓库应当遵循模块化、配置与代码分离、依赖锁定三大原则。本章将给出经过生产验证的目录组织方案,并详细说明配置文件分层策略与依赖管理工具的选择。
4.1 模块化目录组织
推荐采用功能分层 + 领域聚合的混合结构,将策略逻辑、数据管道、回测引擎、监控模块清晰分离。以下是一个典型的多策略仓库结构:
quant-strategy-repo/
├── strategies/ # 策略逻辑层(每个子目录一个独立策略)
│ ├── momentum_etf/ # 动量ETF策略
│ │ ├── __init__.py
│ │ ├── signal.py # 信号生成
│ │ ├── portfolio.py # 持仓计算
│ │ └── config.yaml # 策略专属参数
│ ├── mean_reversion_futures/
│ │ ├── __init__.py
│ │ ├── signal.py
│ │ ├── portfolio.py
│ │ └── config.yaml
│ └── __init__.py
├── core/ # 核心引擎层(回测/实盘共用)
│ ├── engine.py # 事件驱动引擎
│ ├── data_handler.py # 数据接入与清洗
│ ├── order_manager.py # 订单管理
│ └── risk_manager.py # 风控模块
├── data/ # 数据层
│ ├── fetchers/ # 数据获取器
│ ├── processors/ # 特征工程
│ └── storage/ # 存储接口(Parquet/DB)
├── monitoring/ # 监控与漂移检测
│ ├── psi_calculator.py # PSI计算
│ ├── drift_detector.py # 漂移告警逻辑
│ └── dashboard/ # 可视化面板配置
├── tests/ # 测试层
│ ├── unit/ # 单元测试
│ ├── integration/ # 集成测试
│ └── fixtures/ # 测试数据
├── configs/ # 全局配置文件
│ ├── global_config.yaml # 环境、数据库、API密钥
│ └── logging_config.yaml # 日志配置
├── scripts/ # 运维脚本
│ ├── deploy.sh # 部署脚本
│ └── backtest_runner.py # 批量回测启动器
├── requirements.txt # pip依赖锁定
├── environment.yml # conda环境锁定
├── setup.py # 包安装配置
└── README.md # 仓库说明与快速开始
最佳实践:每个策略目录下的
config.yaml 只存放该策略的可变参数(如均线周期、止损比例),而数据库连接、API密钥等环境敏感信息必须放在顶层的 configs/global_config.yaml 中,并通过环境变量注入。
4.2 配置文件分离策略
配置文件分离的核心目标是:代码不变,配置驱动。我们将配置分为三个层级,通过合并策略实现环境无关性。
| 层级 | 文件示例 | 内容范围 | 是否纳入版本控制 |
|---|---|---|---|
| 默认配置 | configs/default.yaml |
策略参数默认值、回测起止时间、手续费率 | 是 |
| 环境配置 | configs/prod.yaml |
数据库连接串、Redis地址、日志级别 | 否(仅存模板) |
| 本地覆盖 | configs/local.yaml |
开发者本地调试参数、模拟账号 | 否(加入.gitignore) |
配置加载优先级:本地覆盖 > 环境配置 > 默认配置。以下是一个 Python 配置加载示例:
import yaml
import os
from pathlib import Path
def load_config(env: str = "dev") -> dict:
config_dir = Path(__file__).parent / "configs"
# 1. 加载默认配置
config = yaml.safe_load((config_dir / "default.yaml").read_text())
# 2. 加载环境配置(覆盖默认)
env_file = config_dir / f"{env}.yaml"
if env_file.exists():
config.update(yaml.safe_load(env_file.read_text()))
# 3. 加载本地覆盖(最高优先级)
local_file = config_dir / "local.yaml"
if local_file.exists():
config.update(yaml.safe_load(local_file.read_text()))
return config
安全警告:切勿将包含明文密码、API Secret Key 的配置文件提交到 Git 仓库。建议使用环境变量或密钥管理服务(如 HashiCorp Vault)注入敏感信息,配置文件中仅填写占位符。
4.3 依赖管理:requirements.txt vs environment.yml
量化策略对 Python 包版本极为敏感,尤其是 NumPy、Pandas、Scikit-learn 等科学计算库的版本差异可能导致回测结果无法复现。因此,锁定全量依赖版本是策略版本管理的基础。
4.3.1 两种工具对比
| 特性 | requirements.txt |
environment.yml |
|---|---|---|
| 包管理器 | pip | conda |
| 非Python依赖 | 不支持 | 支持(如 C++ 库、系统工具) |
| 环境隔离 | 需配合 venv/virtualenv | 原生支持 |
| 版本锁定粒度 | 精确版本(==1.2.3) |
精确版本 + 构建哈希 |
| 跨平台兼容性 | 较好(纯Python包) | 优秀(conda-forge 预编译) |
| 推荐场景 | 轻量策略、纯Python依赖 | 复杂环境、GPU/C++扩展 |
4.3.2 生产级依赖锁定实践
推荐双文件策略:使用 environment.yml 管理 conda 环境(包含系统级依赖),同时导出 requirements.txt 作为 pip 兼容层的快照。
# environment.yml(手动维护,指定主要依赖范围)
name: quant-strategy
channels:
- conda-forge
- defaults
dependencies:
- python=3.10
- numpy=1.24.*
- pandas=1.5.*
- scikit-learn=1.2.*
- pyyaml=6.*
- pip
- pip:
- ta-lib==0.4.28
- psutil==5.9.*
# requirements.txt(自动生成,全量锁定)
# 生成命令:pip freeze > requirements.txt
numpy==1.24.3
pandas==1.5.3
scikit-learn==1.2.2
pyyaml==6.0
TA-Lib==0.4.28
psutil==5.9.5
...
CI/CD 集成建议:在 CI 流水线中增加依赖一致性检查步骤,对比
requirements.txt 与当前环境的 pip freeze 输出,若存在差异则阻断构建并告警,防止因依赖漂移导致策略行为不一致。
4.4 模块化架构与配置分离示意图
下图展示了策略仓库中模块间的调用关系与配置文件的流向,帮助理解各层职责与解耦方式。
4.5 依赖管理流程示意图
下图展示了从开发到部署的依赖管理完整流程,强调锁定与验证环节。
4.6 本章小结
- 模块化目录:按
strategies / core / data / monitoring / tests / configs / scripts分层组织,每个策略独立子目录,便于隔离与复用。 - 配置分离:采用默认配置 + 环境配置 + 本地覆盖三层结构,敏感信息通过环境变量注入,配置代码分离保障安全与灵活性。
- 依赖锁定:使用
environment.yml管理 conda 环境,requirements.txt锁定全量 pip 依赖,CI 中增加一致性检查防止依赖漂移。
生产检查清单:
- 确认
.gitignore已排除local.yaml、__pycache__/、.env等文件。 - 每次修改依赖后,运行
pip freeze > requirements.txt并提交。 - 在 CI 配置中添加步骤:
pip install -r requirements.txt后执行pip freeze | diff - requirements.txt。 - 为每个策略版本创建 Git tag,tag 名称建议包含策略名与版本号,如
momentum_etf_v2.1.0。