项目

一般

简介

行为

0001-项目章程 » 历史记录 » 修订 1

修订 1/3 | 下一页 »
Huarui Lin, 2026-04-11 19:41


《基金量化定投选基系统》项目章程

项目名称: 基金量化定投选基系统
项目编号: 2026-04-11-01
项目经理: Henry Lin
日期: 2026-04-11
**文档状态:v1.0.0

一、项目背景与立项理由

传统基金挑选依赖历史绝对收益排名或主观定性判断,严重忽略投资者在"周定投"场景下的真实盈利体验(浮盈回撤、止盈周期过长)。本系统旨在通过纯数据驱动与严密的时序逻辑,量化评估基金在"等额周定投"模式下的表现潜力。
立项核心理由:构建一套基于 LightGBM 的机器学习排序模型,精准识别能在 150 周内以 DCA(简单平均成本法)实现 20% 止盈的基金。解决"基金赚钱、基民不赚钱"的痛点,为业务端提供硬核的量化选基支撑,提升资产配置的科学性与客户持有体验。

二、项目目标

编号 目标项 具体描述 衡量标准
G-1 工程交付 8 个自然周内完成端到端流水线 6 个 Step 全部通过验收 CheckList
G-2 技术底线 100% 遵守《最终设计规约 (v1.0)》铁律 CI 强阻断触发率 = 0
G-3 模型指标 Top 20 推荐分桶显著跑赢等权基准 年化收益率、Sharpe、止盈成功率三指标均优于基准
G-4 质量卡控 标签生成器 7 场景单测全过 pytest 通过率 100%

三、成功标准

项目达到"Done"状态的绝对验收条件:

  • 规约符合度:所有代码实现与 /docs 技术文档无任何偏离规约核心参数(150周、20%止盈、年化2.0%无风险利率、MAD边界3.0、共线性阈值 |r|>0.90 等)。
  • 资源安全度:在 64GB/36核 实体机上,端到端全量刷盘重训峰值内存 ≤ 50GB,CPU 峰值使用率 ≤ 80%,不触发 OOM。
  • 产物完整性:产出按年分区 Parquet 数据、SHA-256 数据血缘校验文件、标准化参数持久化文件、MLflow 实验记录、SHAP 归因图、四桶回测净值曲线。
  • 流水线闭环:在 Ubuntu 24 Docker 环境下,通过一条主指令完成"全量刷盘重训 → 自动评估 → 自动归档模型"全流程无人工干预运行。

四、关键干系人

角色 职责 核心关注点
项目经理(PM) 规约守护者、交付卡控 红线不被违反,里程碑按期达成
算法/研发工程师 6 个 Step 模块实施 受限算力下实现高性能、合规代码
数据资产管理员 提供原始数据源 数据规模、质量与可用性
基础架构/运维 Ubuntu 24 实体机与工具链 Docker 部署、Gitea/Redmine/MLflow 可用性

五、项目范围与边界

包含在内(In Scope)

  • 周频数据基础清洗(去重、异常值处理、长缺失截断、建仓期硬删、类型白名单过滤)
  • 5 大类 40+ 维特征工程化计算、T-1 截面标准化、共线性剔除
  • DCA 标签生成(20%止盈、150周窗口、样本平衡)
  • LightGBM 模型训练(TimeSeriesSplit 5折、Optuna 超参搜索)
  • 单基金查询与批量推荐推理服务(含 SHAP 归因与决策文本)
  • Redmine 管理视图 + Gitea /docs 技术真相源双轨制文档建设

坚决不做(Out of Scope)

编号 边界项 理由
OB-1 日频/分钟频数据 仅处理周频,规约已锁定
OB-2 增量更新机制 每周全量刷盘重训,规约 D-5 已锁定
OB-3 真实交易闭环 不涉及资金自动扣款与报盘
OB-4 费用扣除模拟 deduct_fee: false 保持关闭
OB-5 UI 可视化前端 交付物为 API/脚本级推理能力
OB-6 DuckDB 滚动窗口计算 FW-2 铁律严禁
OB-7 Pandas 依赖 全局禁止(LightGBM 内部除外)

六、数据资源基线(已确认)

基于您提供的实际数据样例,已完成数据规模与内存安全评估:

6.1 净值表 fund_net_info

指标 数值 说明
单文件物理大小 920.1 MB (CSV) 全量原始数据
估算总行数 ~2,300 万行 920.1MB ÷ 100行/4KB ≈ 23M
单行字段 id, fund_id, cumulative_net_value, net_value_date id 为主键,清洗阶段丢弃
净值数据范围 2015-12-07 起 样例最早日期,覆盖约 10 年周频
Parquet 预估大小 200-300 MB 列式压缩,通常为 CSV 的 1/3~1/4
Arrow 内存预估 800 MB - 1.2 GB 3 有效列 × 23M 行

6.2 基金信息表 fund_basic_info

指标 数值 说明
总记录数 ~26,000 条 全量基金信息
单文件物理大小 ~4.2 MB (CSV) 10行/1.6KB × 2600 = ~4.16MB
规约使用字段 fund_id, fund_name, fund_type, create_date 仅这 4 列
需丢弃字段 insert_datetime, update_datetime, insert_operator, update_operator ⚠️ 实际表比规约多 4 列,data_loader 必须显式丢弃

6.3 类型白名单覆盖率(基于样例推断)

样例中的 fund_type 是否在白名单中
混合型-灵活 ✅ 保留
混合型-偏股 ✅ 保留
债券型-混合二级 ❌ 过滤
债券型-混合一级 ❌ 过滤
指数型-股票 ❌ 过滤
货币型-普通货币 ❌ 过滤

推断:26,000 只基金中,白名单 6 类(股票型、混合型-偏股、混合型-灵活、QDII-普通股票、QDII-混合偏股、QDII-混合灵活)预估保留 5,000-10,000 只。Step 2 数据基建完成后,数据质量审计日志需输出精确的白名单保留率,作为 Redmine M2 验收依据之一。

6.4 内存安全结论

处理阶段 预估内存占用 安全等级
DuckDB 读取原始 CSV → 按年分区 Parquet 5-10 GB 🟢 安全
DuckDB JOIN fund_basic_info(26K 行) 增量 < 1 GB 🟢 安全
Polars 单年份 Arrow 转换(≤300万行 × 3列) 1-2 GB 🟢 安全
Polars 单年份特征计算(≤300万行 × 40+特征) 15-25 GB 🟡 需串行
总计峰值(严格 FW-3 按年串行) ≤ 40 GB 🟢 低于 50GB 软限

核心防线:严格执行 FW-3(按年份串行提取)+ DuckDB memory_limit='40GB' 硬限 + CI 环境 cgroup 50GB 压测。


七、高层级里程碑计划(8 周微步迭代)

Week 1          Week 2-3        Week 4-5         Week 6-7        Week 8
   │               │               │                 │               │
   ▼               ▼               ▼                 ▼               ▼
┌────────┐    ┌──────────┐   ┌────────────┐    ┌────────────┐   ┌──────────┐
│ Step 1 │───>│  Step 2  │──>│Step 3 & 4  │───>│   Step 5   │──>│  Step 6  │
│ 脚手架 │    │ 数据基建 │   │ 并行攻坚    │    │ 模型训练   │   │ 推理收口 │
└────────┘    └──────────┘   └────────────┘    └────────────┘   └──────────┘
   │               │            ┌──┴──┐              │               │
   ▼               ▼            ▼     ▼              ▼               ▼
 CI空跑       Parquet产出   特征引擎  标签器      MLflow记录     Production
 成功         审计日志通过   标准化   7场景单测     四桶回测       模型打标
              白名单覆盖率   参数输出  全绿         跑赢基准       文档闭合
              内存≤40GB                                         全部同步

各 Milestone 详细交付与 Done 标准

MS 周次 交付物 Done 标准(硬卡点,缺一不可)
M1 W1 pyproject.toml、目录树、config.yamlconfig.pylogging.pyhash.py ① CI 空跑流水线在 Gitea 触发成功,无 Error;② config.yaml 参数与规约第七节完全一致
M2 W2-W3 data_loader.py + 按年分区 Parquet + DuckDB VIEW + 数据质量审计日志 ① Parquet 文件产出,Schema 仅含 fund_id/net_value_date/cumulative_net_value;② 实体机压测峰值内存 ≤ 40GB;③ 审计日志包含:白名单保留率、缺失率、异常率、建仓期剔除数、存活期过滤数;④ fund_basic_info 多余 4 列已显式丢弃
M3a W4-W5 feature_engineering.py + features.parquet + standardization_params.parquet + final_feature_list.json ① 特征数 ≥ 40 维且全部为长表形态(严禁 Pivot 宽表);② 首日(数据集第一天)所有标准化特征输出 NULL(T-1 Shift 验证);③ standardization_params.parquet Schema 与规约 4.3 节一致;④ CI 强阻断通过
M3b W4-W5 label_generator.py + labels.parquet + test_label_generator.py ① 7 个必覆盖场景单测 100% 全绿;② label 值域严格为 [0, 149];③ 正样本单基金上限 50 条、负样本上限 15 条;④ 等距抽样使用 np.linspace
M4 W6-W7 trainer.py + 模型文件 + 回测报告 + SHAP 图 + MLflow 记录 ① AUC、NDCG 指标记录完整;② Top 20 桶年化收益率、Sharpe、止盈成功率均优于全标的池等权基准;③ MLflow Production 标签打标成功;④ SHAP summary plot 与四桶回测净值曲线产出
M5 W8 inference.py(单基金 + 批量) ① 单基金输出:得分(0-100) + Top5 正负特征 + 决策文本;② 批量接口返回排序 DataFrame;③ 得分区间文案与规约 6.3 节完全一致;④ Gitea /docs 与 Redmine 状态 100% 同步

里程碑前置依赖(Redmine 强制卡控)

Step 1 ──✓──> Step 2 ──✓──> Step 3 ──┐
                              ──✓──> Step 4 ──┤──✓──> Step 5 ──✓──> Step 6
                                              └───────────────────┘
  • Step 5 任务开启 必须 等待 Step 3 和 Step 4 在 Redmine 中状态均闭合
  • Step 6 任务开启 必须 等待 Step 5 闭合

八、模型训练时间规划(关键算力约束)

已确认决策:Optuna 采用 策略 A(单折搜索)——Optuna 仅在 TimeSeriesSplit 最后一个 Train/Val 折上搜索超参,找到最优后用该超参在其余折做最终评估。搜索与评估解耦,兼顾效率与质量。

Step 5 整体时间拆解(纯 CPU / 36核)

阶段 预估耗时 说明 时间属性
① 数据加载与 JOIN 2-5 分钟 Polars 读取 features + labels 固定
② TimeSeriesSplit 数据切分 < 1 分钟 纯索引操作 固定
③ 每折:截面标准化 + 共线性筛选 10-20 分钟(×5折) 串行执行,每折独立 固定
④ Optuna 超参搜索(单折) ≤ 60 分钟 ⚠️ 1小时硬限仅此阶段 硬限
⑤ 用最优参数 5 折重训最终模型 5-10 分钟 单次训练 固定
⑥ 评估:AUC / NDCG / 四桶回测 15-30 分钟 含回测净值曲线计算 固定
⑦ SHAP 归因分析 10-20 分钟 TreeExplainer 固定
⑧ MLflow 日志写入 < 2 分钟 Artifact 序列化 固定
Step 5 总计 约 2-3.5 小时 可设在非交易时段执行
关键保障措施
  • Optuna 配置 early_stopping_rounds=50,加速无效试验的提前终止
  • 1 小时到达时通过 optuna.study.Study.optimize(timeout=3600) 强制截断,取已有最优超参
  • 最终模型质量仍通过完整 5 折交叉验证评估,搜索与评估完全解耦

九、预算与资源分配

9.1 计算资源

资源项 规格 使用策略
CPU 36 核(实体机) Polars 重度计算节点显式指定 n_threads=24,预留 12 核给 OS / DuckDB IO 缓冲
内存 64 GB DDR DuckDB 硬限 memory_limit='40GB';Polars 按年串行处理;整体峰值 ≤ 50GB
GPU 不依赖,LightGBM 纯 CPU 训练
存储 SSD(容量充足) Parquet 分区 + MLflow Artifact 归档 + 日志
操作系统 Ubuntu 24 Docker 部署,隔离运行环境

9.2 工具链

工具 用途 核心约束
Gitea 代码仓库 + 技术真相源 /docs 所有技术文档 Markdown 存放于仓库 /docs 目录
Redmine 任务管理 + 规约追踪中枢 规约条款写入自定义字段-验收标准
MLflow 实验追踪 + 模型版本管理 Production/Archived 标签管理
Docker 运行环境隔离 + CI/CD 质量红线强阻断/软预警
DuckDB Parquet IO / JOIN / 过滤 严禁滚动窗口(FW-2)
Polars 滚动窗口 / 特征计算 / 标签生成 按年份串行(FW-3)
LightGBM 模型训练 唯一允许隐式调 Pandas 的环节
Optuna 超参搜索 单折搜索,1小时硬限

十、关键风险预警

编号 风险项 触发条件 影响 应对策略(规约级防御)
R-1 内存溢出 (OOM) 违反 FW-3 多年份并发 Arrow 转换 🔴 致命 ① DuckDB 硬限 40GB;② CI 设 cgroup 限 50GB 压测;③ 代码 Review 重点检查按年循环
R-2 Optuna 在 CPU 下搜索不充分 36 核 CPU、大训练集 🟡 可控 1小时硬限截断取最优;early_stopping_rounds=50;单折搜索策略最大化试验次数
R-3 T-1 未来数据泄露 标准化未执行 .shift(1) 🔴 致命 ① 单测强制覆盖首日输出 NULL;② CI 加专项断言;③ 代码 Review 最高优检查项
R-4 8 周紧凑致规约妥协 并行开发时省略 Type Hints / 单测 🟡 可控 CI 强阻断红线,PR 触发 Error 则绝对禁止合并
R-5 fund_basic_info 多余字段污染 data_loader 未丢弃 4 列非规约字段 🟢 可防 data_loader.py 显式指定列投影 SELECT fund_id, fund_name, fund_type, create_date;② M2 审计日志输出实际列名
R-6 白名单覆盖率异常低 实际数据中白名单类型基金占比过低 🟡 待观测 Step 2 审计日志必须输出精确的白名单保留率;若 < 15%,PM 组织专项评审

十一、项目经理职权

编号 职权 说明
A-1 一票否决权 任何违背《最终设计规约 v1.0》的代码提交或设计妥协,直接打回
A-2 流程阻断权 Redmine 前置依赖未达"产物正确+单测全过+CI不报错"标准时,锁定下游任务
A-3 资源调度权 64GB 内存约束下,随时要求研发调整 Polars 并发度或暂停非核心进程
A-4 技术债登记权 客观原因导致临时偏离规约时,强制责任方在 Redmine 录入技术债单并设定清理 Deadline

项目经理签字:
Henry Lin

日期:
2026-04-11

Huarui Lin 更新于 6 天 之前 · 1 修订 已锁定