0001-项目章程 » 历史记录 » 版本 2
Huarui Lin, 2026-04-11 19:41
| 1 | 1 | Huarui Lin | # 《基金量化定投选基系统》项目章程 |
|---|---|---|---|
| 2 | 2 | Huarui Lin | |
| 3 | 1 | Huarui Lin | **项目名称:** 基金量化定投选基系统 |
| 4 | **项目编号:** 2026-04-11-01 |
||
| 5 | **项目经理:** Henry Lin |
||
| 6 | **日期:** 2026-04-11 |
||
| 7 | **文档状态:v1.0.0 |
||
| 8 | 2 | Huarui Lin | |
| 9 | 1 | Huarui Lin | --- |
| 10 | ## 一、项目背景与立项理由 |
||
| 11 | 2 | Huarui Lin | |
| 12 | 1 | Huarui Lin | 传统基金挑选依赖历史绝对收益排名或主观定性判断,严重忽略投资者在"周定投"场景下的真实盈利体验(浮盈回撤、止盈周期过长)。本系统旨在通过纯数据驱动与严密的时序逻辑,量化评估基金在"等额周定投"模式下的表现潜力。 |
| 13 | 立项核心理由:构建一套基于 LightGBM 的机器学习排序模型,精准识别能在 **150 周内以 DCA(简单平均成本法)实现 20% 止盈**的基金。解决"基金赚钱、基民不赚钱"的痛点,为业务端提供硬核的量化选基支撑,提升资产配置的科学性与客户持有体验。 |
||
| 14 | ## 二、项目目标 |
||
| 15 | | 编号 | 目标项 | 具体描述 | 衡量标准 | |
||
| 16 | |------|--------|---------|---------| |
||
| 17 | | G-1 | 工程交付 | **8 个自然周**内完成端到端流水线 | 6 个 Step 全部通过验收 CheckList | |
||
| 18 | | G-2 | 技术底线 | 100% 遵守《最终设计规约 (v1.0)》铁律 | CI 强阻断触发率 = 0 | |
||
| 19 | | G-3 | 模型指标 | Top 20 推荐分桶显著跑赢等权基准 | 年化收益率、Sharpe、止盈成功率三指标均优于基准 | |
||
| 20 | | G-4 | 质量卡控 | 标签生成器 7 场景单测全过 | pytest 通过率 100% | |
||
| 21 | 2 | Huarui Lin | |
| 22 | 1 | Huarui Lin | ## 三、成功标准 |
| 23 | 项目达到"Done"状态的绝对验收条件: |
||
| 24 | - **规约符合度**:所有代码实现与 `/docs` 技术文档无任何偏离规约核心参数(150周、20%止盈、年化2.0%无风险利率、MAD边界3.0、共线性阈值 |r|>0.90 等)。 |
||
| 25 | - **资源安全度**:在 64GB/36核 实体机上,端到端全量刷盘重训峰值内存 ≤ 50GB,CPU 峰值使用率 ≤ 80%,不触发 OOM。 |
||
| 26 | - **产物完整性**:产出按年分区 Parquet 数据、SHA-256 数据血缘校验文件、标准化参数持久化文件、MLflow 实验记录、SHAP 归因图、四桶回测净值曲线。 |
||
| 27 | - **流水线闭环**:在 Ubuntu 24 Docker 环境下,通过一条主指令完成"全量刷盘重训 → 自动评估 → 自动归档模型"全流程无人工干预运行。 |
||
| 28 | 2 | Huarui Lin | |
| 29 | 1 | Huarui Lin | ## 四、关键干系人 |
| 30 | | 角色 | 职责 | 核心关注点 | |
||
| 31 | |------|------|-----------| |
||
| 32 | | 项目经理(PM) | 规约守护者、交付卡控 | 红线不被违反,里程碑按期达成 | |
||
| 33 | | 算法/研发工程师 | 6 个 Step 模块实施 | 受限算力下实现高性能、合规代码 | |
||
| 34 | | 数据资产管理员 | 提供原始数据源 | 数据规模、质量与可用性 | |
||
| 35 | | 基础架构/运维 | Ubuntu 24 实体机与工具链 | Docker 部署、Gitea/Redmine/MLflow 可用性 | |
||
| 36 | ## 五、项目范围与边界 |
||
| 37 | ### 包含在内(In Scope) |
||
| 38 | - 周频数据基础清洗(去重、异常值处理、长缺失截断、建仓期硬删、类型白名单过滤) |
||
| 39 | - 5 大类 40+ 维特征工程化计算、T-1 截面标准化、共线性剔除 |
||
| 40 | - DCA 标签生成(20%止盈、150周窗口、样本平衡) |
||
| 41 | - LightGBM 模型训练(TimeSeriesSplit 5折、Optuna 超参搜索) |
||
| 42 | - 单基金查询与批量推荐推理服务(含 SHAP 归因与决策文本) |
||
| 43 | - Redmine 管理视图 + Gitea `/docs` 技术真相源双轨制文档建设 |
||
| 44 | ### 坚决不做(Out of Scope) |
||
| 45 | | 编号 | 边界项 | 理由 | |
||
| 46 | |------|--------|------| |
||
| 47 | | OB-1 | 日频/分钟频数据 | 仅处理周频,规约已锁定 | |
||
| 48 | | OB-2 | 增量更新机制 | 每周全量刷盘重训,规约 D-5 已锁定 | |
||
| 49 | | OB-3 | 真实交易闭环 | 不涉及资金自动扣款与报盘 | |
||
| 50 | | OB-4 | 费用扣除模拟 | `deduct_fee: false` 保持关闭 | |
||
| 51 | | OB-5 | UI 可视化前端 | 交付物为 API/脚本级推理能力 | |
||
| 52 | | OB-6 | DuckDB 滚动窗口计算 | FW-2 铁律严禁 | |
||
| 53 | | OB-7 | Pandas 依赖 | 全局禁止(LightGBM 内部除外) | |
||
| 54 | --- |
||
| 55 | ## 六、数据资源基线(已确认) |
||
| 56 | 基于您提供的实际数据样例,已完成数据规模与内存安全评估: |
||
| 57 | ### 6.1 净值表 `fund_net_info` |
||
| 58 | | 指标 | 数值 | 说明 | |
||
| 59 | |------|------|------| |
||
| 60 | | 单文件物理大小 | **920.1 MB** (CSV) | 全量原始数据 | |
||
| 61 | | 估算总行数 | **~2,300 万行** | 920.1MB ÷ 100行/4KB ≈ 23M | |
||
| 62 | | 单行字段 | `id`, `fund_id`, `cumulative_net_value`, `net_value_date` | `id` 为主键,清洗阶段丢弃 | |
||
| 63 | | 净值数据范围 | 2015-12-07 起 | 样例最早日期,覆盖约 10 年周频 | |
||
| 64 | | Parquet 预估大小 | **200-300 MB** | 列式压缩,通常为 CSV 的 1/3~1/4 | |
||
| 65 | | Arrow 内存预估 | **800 MB - 1.2 GB** | 3 有效列 × 23M 行 | |
||
| 66 | ### 6.2 基金信息表 `fund_basic_info` |
||
| 67 | | 指标 | 数值 | 说明 | |
||
| 68 | |------|------|------| |
||
| 69 | | 总记录数 | **~26,000 条** | 全量基金信息 | |
||
| 70 | | 单文件物理大小 | **~4.2 MB** (CSV) | 10行/1.6KB × 2600 = ~4.16MB | |
||
| 71 | | 规约使用字段 | `fund_id`, `fund_name`, `fund_type`, `create_date` | 仅这 4 列 | |
||
| 72 | | **需丢弃字段** | `insert_datetime`, `update_datetime`, `insert_operator`, `update_operator` | **⚠️ 实际表比规约多 4 列,data_loader 必须显式丢弃** | |
||
| 73 | ### 6.3 类型白名单覆盖率(基于样例推断) |
||
| 74 | | 样例中的 fund_type | 是否在白名单中 | |
||
| 75 | |-------------------|--------------| |
||
| 76 | | 混合型-灵活 | ✅ 保留 | |
||
| 77 | | 混合型-偏股 | ✅ 保留 | |
||
| 78 | | 债券型-混合二级 | ❌ 过滤 | |
||
| 79 | | 债券型-混合一级 | ❌ 过滤 | |
||
| 80 | | 指数型-股票 | ❌ 过滤 | |
||
| 81 | | 货币型-普通货币 | ❌ 过滤 | |
||
| 82 | > **推断**:26,000 只基金中,白名单 6 类(股票型、混合型-偏股、混合型-灵活、QDII-普通股票、QDII-混合偏股、QDII-混合灵活)预估保留 **5,000-10,000 只**。Step 2 数据基建完成后,数据质量审计日志需输出精确的白名单保留率,作为 Redmine M2 验收依据之一。 |
||
| 83 | ### 6.4 内存安全结论 |
||
| 84 | | 处理阶段 | 预估内存占用 | 安全等级 | |
||
| 85 | |---------|-------------|---------| |
||
| 86 | | DuckDB 读取原始 CSV → 按年分区 Parquet | 5-10 GB | 🟢 安全 | |
||
| 87 | | DuckDB JOIN fund_basic_info(26K 行) | 增量 < 1 GB | 🟢 安全 | |
||
| 88 | | Polars 单年份 Arrow 转换(≤300万行 × 3列) | 1-2 GB | 🟢 安全 | |
||
| 89 | | Polars 单年份特征计算(≤300万行 × 40+特征) | 15-25 GB | 🟡 需串行 | |
||
| 90 | | **总计峰值(严格 FW-3 按年串行)** | **≤ 40 GB** | 🟢 低于 50GB 软限 | |
||
| 91 | > **核心防线**:严格执行 FW-3(按年份串行提取)+ DuckDB `memory_limit='40GB'` 硬限 + CI 环境 cgroup 50GB 压测。 |
||
| 92 | --- |
||
| 93 | ## 七、高层级里程碑计划(8 周微步迭代) |
||
| 94 | ``` |
||
| 95 | Week 1 Week 2-3 Week 4-5 Week 6-7 Week 8 |
||
| 96 | │ │ │ │ │ |
||
| 97 | ▼ ▼ ▼ ▼ ▼ |
||
| 98 | ┌────────┐ ┌──────────┐ ┌────────────┐ ┌────────────┐ ┌──────────┐ |
||
| 99 | │ Step 1 │───>│ Step 2 │──>│Step 3 & 4 │───>│ Step 5 │──>│ Step 6 │ |
||
| 100 | │ 脚手架 │ │ 数据基建 │ │ 并行攻坚 │ │ 模型训练 │ │ 推理收口 │ |
||
| 101 | └────────┘ └──────────┘ └────────────┘ └────────────┘ └──────────┘ |
||
| 102 | │ │ ┌──┴──┐ │ │ |
||
| 103 | ▼ ▼ ▼ ▼ ▼ ▼ |
||
| 104 | CI空跑 Parquet产出 特征引擎 标签器 MLflow记录 Production |
||
| 105 | 成功 审计日志通过 标准化 7场景单测 四桶回测 模型打标 |
||
| 106 | 白名单覆盖率 参数输出 全绿 跑赢基准 文档闭合 |
||
| 107 | 内存≤40GB 全部同步 |
||
| 108 | ``` |
||
| 109 | ### 各 Milestone 详细交付与 Done 标准 |
||
| 110 | | MS | 周次 | 交付物 | Done 标准(硬卡点,缺一不可) | |
||
| 111 | |----|------|--------|------------------------------| |
||
| 112 | | **M1** | W1 | `pyproject.toml`、目录树、`config.yaml`、`config.py`、`logging.py`、`hash.py` | ① CI 空跑流水线在 Gitea 触发成功,无 Error;② `config.yaml` 参数与规约第七节完全一致 | |
||
| 113 | | **M2** | W2-W3 | `data_loader.py` + 按年分区 Parquet + DuckDB VIEW + 数据质量审计日志 | ① Parquet 文件产出,Schema 仅含 `fund_id`/`net_value_date`/`cumulative_net_value`;② 实体机压测峰值内存 ≤ 40GB;③ 审计日志包含:白名单保留率、缺失率、异常率、建仓期剔除数、存活期过滤数;④ `fund_basic_info` 多余 4 列已显式丢弃 | |
||
| 114 | | **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 强阻断通过 | |
||
| 115 | | **M3b** | W4-W5 | `label_generator.py` + `labels.parquet` + `test_label_generator.py` | ① 7 个必覆盖场景单测 100% 全绿;② label 值域严格为 [0, 149];③ 正样本单基金上限 50 条、负样本上限 15 条;④ 等距抽样使用 `np.linspace` | |
||
| 116 | | **M4** | W6-W7 | `trainer.py` + 模型文件 + 回测报告 + SHAP 图 + MLflow 记录 | ① AUC、NDCG 指标记录完整;② Top 20 桶年化收益率、Sharpe、止盈成功率均优于全标的池等权基准;③ MLflow Production 标签打标成功;④ SHAP summary plot 与四桶回测净值曲线产出 | |
||
| 117 | | **M5** | W8 | `inference.py`(单基金 + 批量) | ① 单基金输出:得分(0-100) + Top5 正负特征 + 决策文本;② 批量接口返回排序 DataFrame;③ 得分区间文案与规约 6.3 节完全一致;④ Gitea `/docs` 与 Redmine 状态 100% 同步 | |
||
| 118 | ### 里程碑前置依赖(Redmine 强制卡控) |
||
| 119 | ``` |
||
| 120 | Step 1 ──✓──> Step 2 ──✓──> Step 3 ──┐ |
||
| 121 | ──✓──> Step 4 ──┤──✓──> Step 5 ──✓──> Step 6 |
||
| 122 | └───────────────────┘ |
||
| 123 | ``` |
||
| 124 | - Step 5 任务开启 **必须** 等待 Step 3 和 Step 4 在 Redmine 中状态均闭合 |
||
| 125 | - Step 6 任务开启 **必须** 等待 Step 5 闭合 |
||
| 126 | --- |
||
| 127 | ## 八、模型训练时间规划(关键算力约束) |
||
| 128 | > **已确认决策**:Optuna 采用 **策略 A(单折搜索)**——Optuna 仅在 TimeSeriesSplit 最后一个 Train/Val 折上搜索超参,找到最优后用该超参在其余折做最终评估。搜索与评估解耦,兼顾效率与质量。 |
||
| 129 | ### Step 5 整体时间拆解(纯 CPU / 36核) |
||
| 130 | | 阶段 | 预估耗时 | 说明 | 时间属性 | |
||
| 131 | |------|---------|------|---------| |
||
| 132 | | ① 数据加载与 JOIN | 2-5 分钟 | Polars 读取 features + labels | 固定 | |
||
| 133 | | ② TimeSeriesSplit 数据切分 | < 1 分钟 | 纯索引操作 | 固定 | |
||
| 134 | | ③ 每折:截面标准化 + 共线性筛选 | 10-20 分钟(×5折) | 串行执行,每折独立 | 固定 | |
||
| 135 | | **④ Optuna 超参搜索(单折)** | **≤ 60 分钟** | ⚠️ **1小时硬限仅此阶段** | 硬限 | |
||
| 136 | | ⑤ 用最优参数 5 折重训最终模型 | 5-10 分钟 | 单次训练 | 固定 | |
||
| 137 | | ⑥ 评估:AUC / NDCG / 四桶回测 | 15-30 分钟 | 含回测净值曲线计算 | 固定 | |
||
| 138 | | ⑦ SHAP 归因分析 | 10-20 分钟 | TreeExplainer | 固定 | |
||
| 139 | | ⑧ MLflow 日志写入 | < 2 分钟 | Artifact 序列化 | 固定 | |
||
| 140 | | **Step 5 总计** | **约 2-3.5 小时** | 可设在非交易时段执行 | — | |
||
| 141 | **关键保障措施**: |
||
| 142 | - Optuna 配置 `early_stopping_rounds=50`,加速无效试验的提前终止 |
||
| 143 | - 1 小时到达时通过 `optuna.study.Study.optimize(timeout=3600)` 强制截断,取已有最优超参 |
||
| 144 | - 最终模型质量仍通过完整 5 折交叉验证评估,搜索与评估完全解耦 |
||
| 145 | --- |
||
| 146 | ## 九、预算与资源分配 |
||
| 147 | ### 9.1 计算资源 |
||
| 148 | | 资源项 | 规格 | 使用策略 | |
||
| 149 | |--------|------|---------| |
||
| 150 | | CPU | **36 核**(实体机) | Polars 重度计算节点显式指定 `n_threads=24`,预留 12 核给 OS / DuckDB IO 缓冲 | |
||
| 151 | | 内存 | **64 GB** DDR | DuckDB 硬限 `memory_limit='40GB'`;Polars 按年串行处理;整体峰值 ≤ 50GB | |
||
| 152 | | GPU | **无** | 不依赖,LightGBM 纯 CPU 训练 | |
||
| 153 | | 存储 | **SSD(容量充足)** | Parquet 分区 + MLflow Artifact 归档 + 日志 | |
||
| 154 | | 操作系统 | **Ubuntu 24** | Docker 部署,隔离运行环境 | |
||
| 155 | ### 9.2 工具链 |
||
| 156 | | 工具 | 用途 | 核心约束 | |
||
| 157 | |------|------|---------| |
||
| 158 | | Gitea | 代码仓库 + 技术真相源 `/docs` | 所有技术文档 Markdown 存放于仓库 `/docs` 目录 | |
||
| 159 | | Redmine | 任务管理 + 规约追踪中枢 | 规约条款写入自定义字段-验收标准 | |
||
| 160 | | MLflow | 实验追踪 + 模型版本管理 | Production/Archived 标签管理 | |
||
| 161 | | Docker | 运行环境隔离 + CI/CD | 质量红线强阻断/软预警 | |
||
| 162 | | DuckDB | Parquet IO / JOIN / 过滤 | 严禁滚动窗口(FW-2) | |
||
| 163 | | Polars | 滚动窗口 / 特征计算 / 标签生成 | 按年份串行(FW-3) | |
||
| 164 | | LightGBM | 模型训练 | 唯一允许隐式调 Pandas 的环节 | |
||
| 165 | | Optuna | 超参搜索 | 单折搜索,1小时硬限 | |
||
| 166 | --- |
||
| 167 | ## 十、关键风险预警 |
||
| 168 | | 编号 | 风险项 | 触发条件 | 影响 | 应对策略(规约级防御) | |
||
| 169 | |------|--------|---------|------|----------------------| |
||
| 170 | | R-1 | 内存溢出 (OOM) | 违反 FW-3 多年份并发 Arrow 转换 | 🔴 致命 | ① DuckDB 硬限 40GB;② CI 设 cgroup 限 50GB 压测;③ 代码 Review 重点检查按年循环 | |
||
| 171 | | R-2 | Optuna 在 CPU 下搜索不充分 | 36 核 CPU、大训练集 | 🟡 可控 | 1小时硬限截断取最优;`early_stopping_rounds=50`;单折搜索策略最大化试验次数 | |
||
| 172 | | R-3 | T-1 未来数据泄露 | 标准化未执行 `.shift(1)` | 🔴 致命 | ① 单测强制覆盖首日输出 NULL;② CI 加专项断言;③ 代码 Review 最高优检查项 | |
||
| 173 | | R-4 | 8 周紧凑致规约妥协 | 并行开发时省略 Type Hints / 单测 | 🟡 可控 | CI 强阻断红线,PR 触发 Error 则绝对禁止合并 | |
||
| 174 | | R-5 | fund_basic_info 多余字段污染 | data_loader 未丢弃 4 列非规约字段 | 🟢 可防 | ① `data_loader.py` 显式指定列投影 `SELECT fund_id, fund_name, fund_type, create_date`;② M2 审计日志输出实际列名 | |
||
| 175 | | R-6 | 白名单覆盖率异常低 | 实际数据中白名单类型基金占比过低 | 🟡 待观测 | Step 2 审计日志必须输出精确的白名单保留率;若 < 15%,PM 组织专项评审 | |
||
| 176 | --- |
||
| 177 | ## 十一、项目经理职权 |
||
| 178 | | 编号 | 职权 | 说明 | |
||
| 179 | |------|------|------| |
||
| 180 | | A-1 | 一票否决权 | 任何违背《最终设计规约 v1.0》的代码提交或设计妥协,直接打回 | |
||
| 181 | | A-2 | 流程阻断权 | Redmine 前置依赖未达"产物正确+单测全过+CI不报错"标准时,锁定下游任务 | |
||
| 182 | | A-3 | 资源调度权 | 64GB 内存约束下,随时要求研发调整 Polars 并发度或暂停非核心进程 | |
||
| 183 | | A-4 | 技术债登记权 | 客观原因导致临时偏离规约时,强制责任方在 Redmine 录入技术债单并设定清理 Deadline | |
||
| 184 | --- |
||
| 185 | **项目经理签字:** |
||
| 186 | Henry Lin |
||
| 187 | |||
| 188 | **日期:** |
||
| 189 | 2026-04-11 |