mirror of
https://ghfast.top/https://github.com/aeroxw/tick-stock-panel.git
synced 2026-09-12 16:44:15 +08:00
feat(platform): 因子平台与因子↔策略双向联动 v0.2.3
- 因子平台: /factors 一级页(检验/因子库/编辑器/组合/挖掘), DSL 公式因子(25 算子点选、双语字段、我的因子模板、脏公式守卫), 版本与生命周期, 自动挖掘 L1 统计筛选 - 因子↔策略四条桥: 触发器 Zap 快建因子条件信号、因子一键生成排名策略、自定义信号 AI 提示词接入因子分组、策略回测因子归因(胜/败单入场信号日因子均值, 独立 tab, 双语因子名) - 回测: 统计卡新增盈亏比(≥1 红/<1 绿), 蒙卡回撤合并为中位/95% 双值卡(自适应字号), 高级设置基础过滤与策略编辑器参数对齐(5 组区间) - 信号库独立页 /signals(原设置 tab 迁出), 持仓提醒入导航; 挖掘并入因子页第 5 tab, /mining 旧链接重定向 - 研究线配套: 因子目录 61→77(评分/矩阵双内核), stats_v2(Newey-West/BH-FDR/DSR), enriched 管道与异动/报价服务配套调整 - 文档: README 导航与特性表、features.md 因子平台章节、操作说明书 9.2、factor-platform-plan 执行状态与 §5、二开文档桥接说明; 交流与支持节改版 - 版本 0.2.2 → 0.2.3; 后端全量 1625 passed(1 例环境性跳过), 前端 build 通过
This commit is contained in:
@@ -0,0 +1,403 @@
|
||||
# 因子平台总体方案(研究线产品与架构设计 v1)
|
||||
|
||||
> 状态标记约定与 `factor-system-design.md` 一致:
|
||||
> 【现状】= 当前仓库已存在、已核对代码;【设计】= 本方案提出、尚未实现。
|
||||
> 所有阶段代号 P0–P4 见 §10 路线图。
|
||||
|
||||
## 执行状态(2026-09-04)
|
||||
|
||||
P0–P3 已全部实现并通过验证(后端 98 项因子相关测试 + 全量套件 + 前端 build + 真实数据 E2E):
|
||||
|
||||
- **P0 已实现**:`/factors` 一级页(检验/因子库/编辑器/组合 四 tab)、检验页 UX 六项、回测页 tab 移除与跳转、候选方案注入策略页。
|
||||
- **跟进整合(2026-09-04)**:侧边栏「挖掘」菜单移除,AI 挖掘并入 `/factors?tab=mining`(第 5 个 tab);`/mining` 与 `/backtest?tab=mining` 旧链接保留参数重定向。
|
||||
- **自动挖掘(2026-09-05,P3 后续)**:检验页新增「自动挖掘」入口(`POST /api/backtest/mining/auto`)——L1 全量统计筛选(`app/services/auto_mining.py`,门槛按探索/均衡/严格三档)自动生成达标因子池,随请求持久化筛选摘要(达标清单 + 未达标原因分布),L2 相关性剪枝 / L3 束搜索组合 / L4 嵌套样本外验证与达标门槛全部复用现有挖掘引擎;worker 子进程启动时加载因子注册表(自定义/复合因子可入池)。组合搜索本身不依赖 LLM;AI 的位置留给后续"从失败模式生成新 DSL 因子假设"。
|
||||
- **因子 ↔ 策略结合(2026-09-05,四条桥全部已实现)**:① 策略触发器编辑内 Zap 快捷建因子条件信号(`SignalTriggerActions` + `AddFactorSignalDialog`,阈值给建议值,`csg_f_*` 自动挂接当前策略入场/卖出触发器);② 因子库「生成策略」一键产出单因子排名策略(`GenerateFactorStrategyDialog` 渲染 `FactorRankResearchMatrixStrategy` 模板,matrix_native,保存为 `custom_factor_*`);③ 自定义信号 AI 提示词注入全部因子分组(`custom_signals_ai.py::_format_fields`,行前缀「因子·」,白名单 `allowed_fields()` 收口);④ 策略回测因子归因(`strategy.py` 入场候选因子快照 → 胜/败单入场信号日因子均值,结果区第 4 个 tab,`meta.scoring` 非空即产出;矩阵单因子排名类策略因 META.scoring 设计为空,暂不产出归因,为已知边界)。
|
||||
- **编辑器交互重构(2026-09-05)**:算子区合并为单一可点面板(全部 25 个 DSL 算子按「时序/截面/工具」分组,点击插入光标处,悬停显示签名与参数约束,替代"16 高频 chips + 静态速查表"双列表);可用字段 chip 双语(英文 id 等宽 + 中文标签浅色);「从模板开始」下拉新增「我的因子」分组(导入已保存自定义因子公式),非初始公式替换前弹确认防误覆盖。
|
||||
- **因子库扩充(2026-09-05)**:目录 61 → 77(新增 16 个虚拟因子,追加于目录尾部,挖掘调度前 48 项顺序不变):规模(流通市值对数,由换手率反推流通股本)、收益分解(隔夜/日内 20 日累计)、120 日动量与动量加速度、RSI 边际变化、下行波动、波动率状态(5/60)、振幅趋势、量能潮 OBV、20 日均成交额、均换手/换手波动(低换手溢价)、一年价格位置与距一年高点、KDJ K-D 差。双内核同步实现(`strategy/scoring.py` 评分路径 + `backtest/matrix.py` 矩阵内核,ddof 口径对齐存量约定);批量检验上限 64 → 96。
|
||||
- **P1 已实现**:`backend/app/factors/registry.py` 注册表(61 内置因子单一事实源)、快照/契约测试、`GET /api/factors`。
|
||||
- **P2 已实现**:`backend/app/factors/dsl.py` 公式编译器(词法→EBNF 语法→语义校验 E001–E016→Polars 表达式,两阶段求值解决跨截面套时序窗口)、`POST /api/factors/validate|trial`、编辑器 tab。
|
||||
- **P3 已实现**:`stats_v2.py`(Newey-West t / BH-FDR q / DSR,纯 numpy)、批量检验接线与前端服务端判读;`store.py` 自定义/复合因子持久化 + 评分桥(与策略共用同一条物化路径)+ CRUD API + 启动多轮加载;组合 tab;生命周期状态(草稿/激活/观察/退役)与状态流转 API。
|
||||
- **P4 部分实现**:生命周期状态已落地;**巡检 tab 与后台衰减定时任务延后**(见 §10),复合因子权重 ICIR 自动配置模式延后。
|
||||
- 验证环境备注:因子批量检验对 custom/composite 的底层依赖先经内置补算路径物化(`factor.py::_compute_missing_factors`),已按 fail-closed 语义处理缺失列。
|
||||
|
||||
## 0. 文档定位与阅读顺序
|
||||
|
||||
| 文档 | 角色 | 生效裁定 |
|
||||
| --- | --- | --- |
|
||||
| 本文(factor-platform-plan.md) | **产品与架构总体方案**:两线架构、导航与页面、交互设计、功能全集、路线图 | 本文 §2–§8 生效后,`factor-system-design.md` §11 的"不新增顶层页面"结论**由本文取代** |
|
||||
| `factor-system-design.md` | **后端技术 spec**:FactorSpec schema、DSL 文法与算子表、统计口径(NW/BH-FDR/DSR)、API 契约、缓存键、测试矩阵 | 除 §11 外全部继续有效;本文引用不复制 |
|
||||
|
||||
两文冲突时:产品/前端/导航以本文为准;后端 schema、统计公式、API 字段以旧文为准。
|
||||
|
||||
## 1. 架构总览:两线模型
|
||||
|
||||
### 1.1 模型
|
||||
|
||||
```text
|
||||
研究线(发现问题) 交易线(执行战法)
|
||||
┌─────────────────────────┐ ┌─────────────────────────┐
|
||||
│ 因子库(注册表) │ │ 策略引擎(战法) │
|
||||
│ ↑ 检验(IC/IR/多空) │ │ 时机/买卖规则/仓位/风控 │
|
||||
│ ↑ 公式DSL(自定义因子) │ │ ↑ 评分选股 │
|
||||
│ ↑ 组合因子(加权合成) │ │ ↑ 验证(鲁棒性/步进) │
|
||||
│ 挖掘(自动搜索因子组合) │ │ 回测(策略绩效) │
|
||||
└───────────┬─────────────┘ └───────────↑─────────────┘
|
||||
│ 唯一桥梁:策略 scoring 配置 │
|
||||
└────────── 因子(组合)权重 ──────────────────┘
|
||||
```
|
||||
|
||||
- **因子层**回答"此刻全市场谁排前面"(截面排序);**战法层**回答"何时进、买几只、多重仓、何时出、怎么止损"。
|
||||
- 两线只通过**评分配置**结合:`{"scoring": {"cf_hotmom": 0.6, ...}}`。因子不会"变成"策略,因子给策略供货。
|
||||
- 【现状】桥已存在:`backend/app/strategy/scoring.py:13` `VIRTUAL_SCORING_DEPENDENCIES`(35 个虚拟因子依赖声明)、`scoring_value_expr`(按名取值表达式)、`mining.py:342` 挖掘候选产物本身就是 `scoring = dict(zip(factor_names, weights))`。
|
||||
- 【现状】研究线已分裂在两处:因子检验是 `/backtest?tab=factor`,挖掘是独立页 `/mining`(`frontend/src/router.tsx:126-127`)。本方案将研究线收拢。
|
||||
|
||||
### 1.2 各能力现状对照
|
||||
|
||||
| 能力 | 现状 | 本方案落点 |
|
||||
| --- | --- | --- |
|
||||
| 因子检验(批量/单因子) | 【现状】`backtest/factor.py` 61 因子、Rank IC/分年/衰减/分环境;前端 FactorDiscovery | §3(搬迁 + UX 重设计) |
|
||||
| 因子注册 | 【现状】硬编码 `FACTOR_COLUMNS`(`factor.py:36`),加因子=改代码 | §4 + 旧文 §2 L-REG 注册表 |
|
||||
| 公式配置 | 【现状】无(公式即代码) | §5 + 旧文 §3 DSL |
|
||||
| 自动挖掘 | 【现状】`mining.py` beam search + 嵌套验证 + 晋升门槛,`/mining` 页 | §7(协同设计,不搬家) |
|
||||
| 因子→策略 | 【现状】scoring 机制可用;复合因子需改代码 | §6 + 旧文 §8 FactorCompositeSpec |
|
||||
| 生命周期管理 | 【现状】无 | §8(P4) |
|
||||
|
||||
## 2. 信息架构与导航【设计】
|
||||
|
||||
### 2.1 导航变化
|
||||
|
||||
```text
|
||||
现状: 回测(/backtest: 因子|策略|验证) 挖掘(/mining) …
|
||||
目标: 回测(/backtest: 策略|验证) 因子(/factors) 挖掘(/mining) …
|
||||
↑ 导航顺序:回测 → 因子 → 挖掘 相邻
|
||||
```
|
||||
|
||||
- 新增一级路由 `/factors`,导航标签「因子」,插入在回测与挖掘之间(`router.tsx` + `Layout.tsx`,两者为二开文档 §5.2 高冲突热点,diff 必须最小)。
|
||||
- `/backtest` 瘦身为「策略」「验证」两个 tab。
|
||||
- **旧链接兼容**:`/backtest?tab=factor` → `<Navigate to="/factors" replace>`,沿用现有先例——`Backtest.tsx:38-43` 已用同一模式把 `tab=mining` 重定向到 `/mining` 并保留 query 参数。`tab=factor` 的重定向须保留 `run/candidate` 类参数的传递习惯(对齐 mining 先例)。
|
||||
|
||||
### 2.2 候选方案抽屉的归属
|
||||
|
||||
【现状】候选方案抽屉内联在 `Backtest.tsx:114`,头部门按钮打开;因子候选(FactorDiscovery「保存候选」)与策略候选共用该存储,且已有"载入复测"流转(`Backtest.tsx:35`)。
|
||||
|
||||
【设计】P0 将抽屉提取为共享组件 `CandidateDrawer`,**回测页与因子页都放入口**。裁定理由:候选是研究线的产物、交易线的原料,两边都要能开;提取是搬移不是复制(CONTRIBUTING §7 共享组件原则)。
|
||||
|
||||
### 2.3 因子页骨架
|
||||
|
||||
```text
|
||||
/factors
|
||||
├─ tab=inspect(检验) P0:现有 FactorDiscovery 整体迁入 + UX 六项改造(§3)
|
||||
├─ tab=library(因子库) P1:注册表浏览器(§4)
|
||||
├─ tab=editor(编辑器) P2:自定义公式因子(§5)
|
||||
├─ tab=composite(组合) P3:组合因子构建器(§6)
|
||||
└─ tab=lifecycle(巡检) P4:衰减监控看板(§8)
|
||||
```
|
||||
|
||||
- tab 状态走 URL query(`?tab=`),与回测页习惯一致,保证可收藏、可跳转。
|
||||
- P0 只落地 `inspect`,其余 tab 以"规划中"占位不渲染入口(YAGNI:不提前放空壳 tab)。
|
||||
|
||||
## 3. 检验 tab 详细设计(P0,纯前端)
|
||||
|
||||
### 3.1 页面线框
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ 因子检验 [候选方案] [AI 挖掘→] │
|
||||
│ ┌ 三句话说清楚(可折叠,默认展开,关后记忆到 localStorage)─────────┐ │
|
||||
│ │ ① 因子 = 用来给股票打分排序的特征(如 20日涨幅、换手率变化)。 │ │
|
||||
│ │ ② 这里检验:过去一段日子里,哪些特征真的能预测次日涨跌。 │ │
|
||||
│ │ ③ IC = 预测准确度(绝对值越大越准),IR = 稳定度(越大越稳)。 │ │
|
||||
│ └──────────────────────────────────────────────────────────────────┘ │
|
||||
│ ┌ 筛选配置 ────────────────────────────────────────────────────────┐ │
|
||||
│ │ 想好要测什么了吗?没有的话从预设开始: │ │
|
||||
│ │ [全面体检 61] [快速体检 10] [趋势动量 14] [超跌反转 9] │ │
|
||||
│ │ [量价资金 11] [财务价值 7] 当前: 全面体检 ✓(点预设=替换选择) │ │
|
||||
│ │ 已选 61/61 全选 清空 [🔍 搜索因子 (名称/公式)] │ │
|
||||
│ │ ┌─ 限高 45vh 内部滚动列表(现状保留)────────────────────────┐ │ │
|
||||
│ │ │ ▾ 动量 (6) 本组测:过去 N 日涨了多少 … │ │ │
|
||||
│ │ │ ☑ 5日动量 5个交易日累计收益率 │ │ │
|
||||
│ │ └────────────────────────────────────────────────────────────┘ │ │
|
||||
│ │ 资产与范围 / 日期 / 调仓 / 分组 / 佣金(现状保留) │ │
|
||||
│ │ [ 筛选 61 个因子 ] │ │
|
||||
│ └──────────────────────────────────────────────────────────────────┘ │
|
||||
│ ┌ 筛选结果 ─────────────────────────────────────────────────────────┐ │
|
||||
│ │ 结论句:61 个因子中 7 个有效*。最强:5日反转(IC −0.041)。 │ │
|
||||
│ │ 表格: 排名 | 因子(含方向箭头) | 预测力IC | 稳定度IR | IC胜率 | │ │
|
||||
│ │ 多空收益 | 最大回撤 | [结论徽章] | 操作 │ │
|
||||
│ └──────────────────────────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 预设场景入口
|
||||
|
||||
**设计动机**:现状默认全选 61 个,用户第一个动作是"做选择题";预设把第一个动作变成"按一下"。
|
||||
|
||||
预设清单(数量已按 `factor.py` FACTOR_COLUMNS 实际分组核对:动量 6、趋势 8、均线偏离 10、量价 9、价格位置 6、超买超卖 3、收益形态 3、波动率 5、流动性 2、涨停基因 2、财务 7,共 61):
|
||||
|
||||
| 预设 | 数量 | 构成规则 | 适合谁 |
|
||||
| --- | --- | --- | --- |
|
||||
| 全面体检(默认) | 61 | 全部因子 | 不知道从哪开始的人;标注"耗时最长" |
|
||||
| 快速体检 | 10 | 除财务组外每组取窗口中位代表因子(如动量组取 20日动量) | 想快速看一圈的人 |
|
||||
| 趋势动量 | 14 | 动量组 + 趋势组 | 关心趋势行情下什么有效 |
|
||||
| 超跌反转 | 9 | 超买超卖组 + 价格位置组 | 关心抄底反转逻辑 |
|
||||
| 量价资金 | 11 | 量价组 + 流动性组 | 关心资金与量能信号 |
|
||||
| 财务价值 | 7 | 财务组 | 关心基本面(需财务数据源能力) |
|
||||
|
||||
交互规则:
|
||||
|
||||
1. 点预设 = **替换**当前选择集(不是叠加),按钮立即显示"当前预设 ✓"。
|
||||
2. 用户手动改勾选后,状态变为「自定义」,所有预设按钮取消高亮。
|
||||
3. 预设是**纯前端选择集**,不新增后端概念;构成规则在代码里以"组名 + 组内规则/显式 id 列表"声明,实现时从 `columns.data` 按组过滤生成(不硬编码 61 个 id 的第二份清单)。
|
||||
4. 「快速体检」的代表因子实现时从 FACTOR_COLUMNS 按"窗口中位"解析;解析失败的组整组纳入(fail-open 但数量标注会变)。
|
||||
|
||||
### 3.3 三句话引导条
|
||||
|
||||
- 位置:页面标题下方,结果区上方;`<details>` 式可折叠,默认展开,关闭状态记 `localStorage`(key 纳入现有 preferences 机制则更好,实现时择一,不新增后端字段)。
|
||||
- 文案即 §3.1 线框中三句,不许加第四句——解释超过三句就没人读。
|
||||
- 每句中的术语(IC/IR)与表头 tooltip(§3.4)文案保持一致。
|
||||
|
||||
### 3.4 表头判读 tooltip(文案定稿)
|
||||
|
||||
悬停表头即出,内容一句话 + 判读标准。列名同步通俗化(括号内保留原术语供专业用户):
|
||||
|
||||
| 列 | 通俗列名 | tooltip 文案 |
|
||||
| --- | --- | --- |
|
||||
| IC 均值 | 预测力 IC | 每天用因子给股票打分、与次日真实涨跌算相关性(Rank IC)的均值。\|IC\|≥0.02 且稳定,即有预测力;负值同样有效(反向使用)。 |
|
||||
| IR | 稳定度 IR | IC 均值 ÷ IC 波动。≥0.3 值得关注,≥0.5 相当稳定。 |
|
||||
| IC 胜率 | 预测日占比 | IC 与预测方向一致的天数占比。50% 是抛硬币,55%+ 不错。 |
|
||||
| 多空收益 | 多空收益 | 每期买因子最高组、(模拟)卖最低组的累计收益差。注意 A 股做空受限,此列为理论口径。 |
|
||||
| 最大回撤 | 最大回撤 | 上述多空组合最痛的一段亏损幅度,衡量拿得住不住。 |
|
||||
| 排名 | 排名 | 按当前排序键排序的名次,默认按 \|IC\|。 |
|
||||
|
||||
### 3.5 结果判读(徽章 + 结论句)
|
||||
|
||||
- 【现状】结果行字段:`ic_mean / ir / ic_win_rate / long_short_return / long_short_max_drawdown / error`(`FactorDiscovery.tsx:372-376`);**当前 API 无 t 值**。
|
||||
- 【设计·P0】客户端经验阈值判读(须在 UI 上明示"经验规则"):
|
||||
- `有效`:`|ic_mean| ≥ 0.02 且 |ir| ≥ 0.3`
|
||||
- `边缘`:`|ic_mean| ≥ 0.02 或 |ir| ≥ 0.3`(其一成立)
|
||||
- `无效`:其余
|
||||
- `计算失败`:`error` 非空(现状已有 error 显示)
|
||||
- 【设计·P3 起】后端 metrics_v2(旧文 §6,`t_newey_west`)落地后,判读切换为**服务端显著性与阈值双条件**,前端仅渲染服务端 verdict;P0 的客户端阈值作为降级路径保留。
|
||||
- 结论句模板:`{N} 个因子中 {M} 个有效。最强:{label}(IC {值})。`N=参与计算的因子数(排除 error),M=有效徽章数。结论句挂在表头右侧,字号小、颜色 muted。
|
||||
|
||||
### 3.6 因子方向标识
|
||||
|
||||
- 位置:**结果表**因子名旁,非复选框列表(检验前方向未知,不预测)。
|
||||
- 推导:`ic_mean > 0` → `↑ 值大看多`;`ic_mean < 0` → `↓ 值小看多`;null/error → 不显示。
|
||||
- 这是从数据推导的事实(样本内 IC 符号),非先验声明,因此**不进因子库元数据**(P1 因子库的方向列同样取最近一次检验的 IC 符号,并标注样本区间)。
|
||||
|
||||
### 3.7 路径闭环
|
||||
|
||||
1. **保存候选**后 toast:`已保存到候选方案(右上角查看)`;因子页头部有「候选方案」按钮(§2.2 共享抽屉)。
|
||||
2. **单因子检验**页(FactorDiscovery 内部 mode=single)顶部加返回链:`← 返回批量结果`,保留现有列表参数不丢。
|
||||
3. **因子库 → 检验**联动(P1):`/factors?tab=inspect&focus={factor_id}` 打开检验页并只勾选该因子。
|
||||
4. **AI 挖掘 →**:因子页头部的 `/mining` 链接(研究线互链,§7)。
|
||||
|
||||
### 3.8 状态设计(CONTRIBUTING §7 强制项)
|
||||
|
||||
| 状态 | 触发 | 展示 |
|
||||
| --- | --- | --- |
|
||||
| 加载(因子清单) | columns.isLoading | 列表区骨架/「因子加载中…」(现状已有) |
|
||||
| 加载(运行中) | run.isPending | 按钮禁用 + 进度文案(现状已有),期间**保留上次结果**不闪空(现状行为核对) |
|
||||
| 空结果 | results=[] | EmptyState + 引导语「试试更长时间范围或减少因子」 |
|
||||
| 错误 | run.isError | 错误条 + 重试按钮,保留左侧配置 |
|
||||
| 无权限/无数据 | 财务预设但无财务数据源能力 | 预设按钮禁用 + title 说明「需要财务数据能力」(对齐能力矩阵门控习惯) |
|
||||
|
||||
### 3.9 迁移清单(P0 范围内的代码动作,供实施 PR 参考)
|
||||
|
||||
| # | 动作 | 文件 | 性质 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | 新建 `/factors` 路由 + `Factors.tsx` 页壳(tab=inspect) | `router.tsx`、`pages/Factors.tsx` | 新增 |
|
||||
| 2 | FactorDiscovery 连同其 queryKeys 原样迁入 | `pages/backtest/FactorDiscovery.tsx` → 组件复用 | 搬移 |
|
||||
| 3 | `tab=factor` 重定向 `/factors`(保参,对齐 mining 先例) | `pages/Backtest.tsx` | 修改 |
|
||||
| 4 | Backtest 移除因子 tab,MODES 剩 策略/验证 | `pages/Backtest.tsx` | 修改 |
|
||||
| 5 | 候选抽屉提取共享组件,两页挂入口 | `Backtest.tsx` → `components/CandidateDrawer.tsx` | 提取 |
|
||||
| 6 | UX 六项(§3.2–§3.7) | `FactorDiscovery.tsx`(或其子组件拆分) | 新增 |
|
||||
| 7 | 导航项「因子」 | `Layout.tsx` | 修改 |
|
||||
|
||||
不做的:不改 queryKeys.ts 键结构、不改任何 API、不动 factor.py。
|
||||
|
||||
## 4. 因子库 tab(P1,依赖 L-REG 注册表)
|
||||
|
||||
### 4.1 列表
|
||||
|
||||
- 数据源:`GET /api/factors`(旧文 §10 已定义),P1 前无此接口时**本 tab 不上线**(不放假数据)。
|
||||
- 列:`id / 中文名 / 分组 / 类型(base|virtual|composite|custom)/ 公式(desc 或 DSL)/ 最近 IC(含样本区间)/ 状态徽章 / 资产类型 / warmup 天数`。
|
||||
- 筛选:搜索框(id/名称/公式,复用检验 tab 同款逻辑)+ 类型、分组、状态下拉。
|
||||
- 行点击 → 详情抽屉。
|
||||
|
||||
### 4.2 详情抽屉
|
||||
|
||||
- FactorSpec 全字段(旧文 §2.2 的 17 字段)按人话展示:公式、方向(最近检验 IC 符号 + 样本区间)、warmup("需要 X 日历史预热")、依赖列、适用资产。
|
||||
- 操作:「去检验」(§3.7 联动)、「克隆到编辑器」(P2 后可用)、custom/composite 显示「编辑」「停用」。
|
||||
- 底部:最近一次检验报告摘要(IC/IR/多空/回撤)+「查看完整报告」跳检验 tab。
|
||||
|
||||
### 4.3 状态徽章
|
||||
|
||||
与 §8 状态机一致:`active / watch / retired / draft`。P1 只有 `active`(全部内置因子)与 `draft`(编辑器产物),watch/retired 在 P4 激活。
|
||||
|
||||
## 5. 因子编辑器 tab(P2,依赖 DSL 编译器)
|
||||
|
||||
### 5.1 布局
|
||||
|
||||
```text
|
||||
┌────────────────────────┬──────────────────────────┐
|
||||
│ 公式编辑区 │ 校验与预览区 │
|
||||
│ textarea (等宽字体) │ ✓/✗ 语法校验(错误码+定位) │
|
||||
│ 算子面板 (25 个, 三组) │ 依赖列集合 │
|
||||
│ 时序/截面/工具, 点击插入│ warmup = max(窗口) 提示 │
|
||||
│ 可用字段 ▾ (双语 chip) │ [试算最近 40 日 IC] │
|
||||
│ 从模板开始/我的因子 ▾ │ 试算结果: IC 走势 sparkline │
|
||||
│ [校验] [试算 40 日] │ 注册/更新表单 │
|
||||
└────────────────────────┴──────────────────────────┘
|
||||
```
|
||||
|
||||
### 5.2 交互流
|
||||
|
||||
1. 输入公式 → 调 `POST /api/factors/validate`(旧文 §10;错误码 E001–E016 见旧文 §3.5,含负 shift 即未来函数的 E005)。
|
||||
2. 校验通过 → 展示编译产物:依赖列集合、warmup_bars(旧文 §3 规则 4)。
|
||||
3. 试算:选近 40 交易日 + 当前资产,跑一次 Rank IC 快照(复用检验引擎计算路径,**不另写计算逻辑**);展示 IC 序列 sparkline 与均值。
|
||||
4. 注册:状态 `draft`,写入注册表;后续在检验 tab 跑完整检验后转 `active`(§8 状态机)。编辑已有因子时公式变化保存为新版本并回草稿态。
|
||||
5. 模板起点:「从模板开始 / 我的因子」下拉——经典模板(5 个范式)+ 我的因子(已保存自定义因子公式导入);替换非初始公式前弹确认。
|
||||
6. 点选插入:全部 25 个 DSL 算子按「时序(13)/截面(3)/工具(9)」分组,点击插入光标处(与后端 `dsl.OPERATORS` 同步维护),悬停显示签名与参数约束;79 个可用字段 chip 双语显示(英文 id 等宽 + 中文标签),点击插入。
|
||||
|
||||
### 5.3 约束(红线)
|
||||
|
||||
- 编辑器产物只经 DSL 编译到 Polars 表达式这一条计算路径(旧文 §3),**禁止**出现第二套求值器(CONTRIBUTING 禁止平行实现)。
|
||||
- 注册因子 id 冲突拒绝,不自增后缀静默改名。
|
||||
- 每一步的服务端校验 fail-closed:校验不过,注册按钮禁用且展示原因。
|
||||
|
||||
## 6. 组合 tab(P3,依赖 FactorCompositeSpec)
|
||||
|
||||
### 6.1 组合构建器
|
||||
|
||||
- 成分:从因子库多选(上限 8 个,对齐挖掘 max_factors 习惯),每行显示该因子最近 IC/IR。
|
||||
- 权重三模式:
|
||||
1. **手动**:逐项权重滑杆,实时归一显示;
|
||||
2. **ICIR 自动**:`w_i ∝ max(ICIR_i, 0)`,窗口 504 交易日,**只用 (t−window, t−1] 数据**(旧文 §8 点时规则,严禁 t 日数据参与 t 日权重);
|
||||
3. **均等**:1/N。
|
||||
- 预览:选定回看区间内,组合 IC/IR 曲线 vs 各成分——证明"组合优于单因子"或及时劝退。
|
||||
|
||||
### 6.2 注册与桥接
|
||||
|
||||
- 「注册为复合因子」→ FactorCompositeSpec 入注册表,类型 `composite`。
|
||||
- 桥接机制**零引擎改动**(旧文 §8 已论证):注册时自动写入 `VIRTUAL_SCORING_DEPENDENCIES` 对应条目 → 立即出现在策略评分可选列表。
|
||||
- 「一键创建策略」:打开策略新建表单并预填 `scoring = {组合id: 1.0}`,用户补战法参数——完成研究线→交易线的最后一步。
|
||||
|
||||
## 7. 与挖掘页协同【设计】
|
||||
|
||||
裁定:**挖掘保持独立页**(已有用户习惯 + 任务型界面本就该独立),做研究线互链而非合并。
|
||||
|
||||
1. 因子页头部「AI 挖掘 →」链到 `/mining`。
|
||||
2. 挖掘候选详情加「查看因子检验」:跳 `/factors?tab=inspect&focus=...` 预选该候选的成分因子。
|
||||
3. P4 可选:挖掘晋升的候选自动以 `watch` 状态写入因子库(人工确认后转 active)——默认不自动转正,晋升门槛(`mining.py` GATE_*)只证明样本外可行,不过不代表可持续。
|
||||
|
||||
## 8. 因子生命周期(P4)
|
||||
|
||||
### 8.1 状态机
|
||||
|
||||
```text
|
||||
draft ──(完整检验通过)──▶ active ──(衰减告警/人工)──▶ watch ──(持续衰减/人工)──▶ retired
|
||||
▲ │
|
||||
└──────────────(重新编辑公式, 重新走检验)──────────────────┘
|
||||
```
|
||||
|
||||
| 迁移 | 触发条件 | 动作 |
|
||||
| --- | --- | --- |
|
||||
| draft→active | 检验报告满足:\|IC\|≥0.02 且 \|IR\|≥0.3(或 metrics_v2 后 t 显著) | 进策略可选列表 |
|
||||
| active→watch | 滚动 63 日 IR < 0,或滚动 63 日 \|IC\| 较全样本减半 | 因子库标黄,不影响策略运行 |
|
||||
| watch→retired | 连续两个观察窗(126 日)仍衰减,或人工 | 从策略可选列表隐藏(已引用它的策略配置**不破坏**,仅新建不可选) |
|
||||
| retired→draft | 人工重新编辑 | 重走检验 |
|
||||
|
||||
### 8.2 衰减监控
|
||||
|
||||
- 计算:后台低频任务(日线收盘后),对 active/watch 因子重算滚动 63 日 Rank IC/IR——复用检验引擎同一条 IC 计算路径。
|
||||
- 展示:因子库 tab 状态列 + P4 的 `lifecycle` 看板(衰减榜:谁最近不行了)。
|
||||
- 通知:首版只做页面内徽章;接通知服务(`NotificationFormatter` 继承点)为可选后续,防打扰。
|
||||
|
||||
## 9. 后端支撑映射
|
||||
|
||||
| 本方案功能 | 后端依赖 | 旧文章节 | 代码落点 |
|
||||
| --- | --- | --- | --- |
|
||||
| 检验 tab(§3) | 无新依赖(P0 纯前端) | — | — |
|
||||
| 因子库(§4) | L-REG 注册表 + `GET /api/factors` | §2、§10 | 【设计】`app/factors/registry.py`(三处合一:factor.py:36 + scoring.py 虚拟因子 + pipeline ENRICHED_COLUMNS) |
|
||||
| 编辑器(§5) | DSL 编译器 + `POST /api/factors/validate` | §3、§10 | 【设计】`app/factors/dsl.py` |
|
||||
| 判读升级(§3.5) | metrics_v2(t_newey_west 等) | §6 | factor.py `_calc_ic` 扩展(现状 `factor.py:738-750`) |
|
||||
| 组合(§6) | FactorCompositeSpec + scoring 桥 | §8 | scoring.py `VIRTUAL_SCORING_DEPENDENCIES`(现状 `scoring.py:13`,写路径自动化) |
|
||||
| 生命周期(§8) | 衰减重算任务 + 状态字段 | §6(IC 口径复用) | 【设计】注册表状态字段 + 低频任务 |
|
||||
|
||||
## 10. 路线图(PR 切分)
|
||||
|
||||
> 与旧文 §14 的十个 PR 合并视图:数据线四个 PR(st_history / delisted_kline / industry_pit / index_ext)独立并行,不受本路线图阻塞。
|
||||
|
||||
### P0 — 因子页拆分 + 检验 UX(1 个 PR,纯前端)
|
||||
|
||||
- 范围:§2 导航/路由/重定向 + §3.9 迁移清单 7 项 + §3.2–§3.7 UX 六项。
|
||||
- 依赖:无。
|
||||
- 验收:`/factors` 可用;旧链接 301 语义跳转;候选抽屉两页可用;六项 UX 逐项截图;窄屏检查;`pnpm build` 过;queryKeys 无变更。
|
||||
- 回滚:revert 单 PR 即回到现状,无数据迁移。
|
||||
|
||||
### P1 — 注册表 + 因子库(2 个 PR:后端 / 前端)
|
||||
|
||||
- 后端:L-REG 注册表(旧文 §2,FACTOR_COLUMNS 由注册表生成、对外常量保留兼容别名;scoring.py 改读注册表、函数签名不变)+ `GET /api/factors`。
|
||||
- 前端:因子库 tab 上线(§4)。
|
||||
- 验收:注册表生成结果与现 61 因子逐项一致(快照测试);旧候选方案引用的历史 id 全部可读。
|
||||
|
||||
### P2 — DSL + 编辑器(2 个 PR)
|
||||
|
||||
- 后端:DSL 编译器 + validate/试算端点(旧文 §3,含 E001–E016)。
|
||||
- 前端:编辑器 tab(§5)。
|
||||
- 验收:错误因子公式全路径测试;试算与检验引擎同源计算对拍一致。
|
||||
|
||||
### P3 — metrics_v2 + 组合 + 判读升级(2 个 PR)
|
||||
|
||||
- 后端 a:metrics_v2(NW t 值等,旧文 §6;测试=黄金参考向量 + 可选 dev 组 statsmodels 对拍)。
|
||||
- 后端 b+前端:FactorCompositeSpec + scoring 桥自动化 + 组合 tab(§6)+ 结果判读切服务端(§3.5)。
|
||||
- 验收:组合因子在策略评分中可选且数值路径唯一;DSR/BH-FDR 数值测试过。
|
||||
|
||||
### P4 — 生命周期(1 个 PR,可延后)
|
||||
|
||||
- §8 状态机 + 衰减任务 + 巡检看板。
|
||||
- 验收:衰减告警可复现(构造样本);retired 因子不破坏存量策略配置(兼容测试)。
|
||||
|
||||
## 11. 验证矩阵(对齐 CONTRIBUTING §9)
|
||||
|
||||
| 改动 | 最低验证 |
|
||||
| --- | --- |
|
||||
| P0 前端 | `pnpm build`;手工检查:加载/空/错/禁用/无权限五态、预设切换与自定义态、搜索不丢选择(回归本轮已验证的行为)、旧链接重定向、候选抽屉双页、窄屏 |
|
||||
| P1 注册表 | 后端 pytest:注册表快照=现 61 因子、历史 id 兼容、scoring 行为不变(对拍测试);API 契约测试 |
|
||||
| P2 DSL | 编译器单测(合法/非法公式全错误码)、防未来函数(E005 用例)、试算与检验同源对拍 |
|
||||
| P3 统计 | NW/BH-FDR/DSR 数值断言(黄金向量);组合因子端到端:注册→策略评分可选→回测出数 |
|
||||
| P4 生命周期 | 衰减触发用例、retired 兼容用例、低频任务不阻塞启动 |
|
||||
| 全阶段 | `git diff --check`;每 PR 附界面证据(截图覆盖改动前后) |
|
||||
|
||||
## 12. 兼容性与风险
|
||||
|
||||
| 项 | 影响 | 对策 |
|
||||
| --- | --- | --- |
|
||||
| 旧链接 `/backtest?tab=factor` | 收藏/外链失效 | Navigate 重定向保参(mining 先例) |
|
||||
| 候选方案存储 | 无 schema 变化 | 仅抽屉组件提取,存储与"载入复测"链路不动 |
|
||||
| 因子 id | 不变 | P0 不碰 factor.py;P1 注册表生成需逐项快照对齐 |
|
||||
| `router.tsx`/`Layout.tsx` 高冲突热点 | 上游合并冲突面 | diff 最小化:只加一条路由/一个导航项,不动既有结构 |
|
||||
| P0 判读阈值是经验值 | 可能误判"有效" | UI 明示"经验规则";P3 换服务端显著性后降级为兜底 |
|
||||
| 方向箭头取自样本内 IC 符号 | 过拟合误读 | tooltip 注明"样本内方向,历史不代表未来" |
|
||||
| 编辑器产生大量低质因子 | 注册表膨胀 | draft 不进策略可选列表;必须过完整检验才 active |
|
||||
|
||||
## 13. 不做什么(YAGNI,续旧文 §16)
|
||||
|
||||
1. 因子市场/分享/导入第三方因子包——无真实需求前不做。
|
||||
2. 因子自动再训练/自动调参流水线——自动权重窗口已是点时滚动,更细即过拟合。
|
||||
3. 实时因子流(盘中逐 tick 因子值)——研究线是日频截面,实时因子属于监控线的事。
|
||||
4. 跨市场(港美股)因子——数据源与交易规则均不支持。
|
||||
5. 因子血缘图谱可视化——注册表依赖列字段已可追溯,图形化为炫技。
|
||||
6. 把挖掘页合并进因子页——任务型界面独立存在(§7 裁定)。
|
||||
|
||||
## 14. 完成标准
|
||||
|
||||
本方案"完成"= 路线图 P0–P3 全部合入且:
|
||||
|
||||
1. `/factors` 四 tab(检验/库/编辑器/组合)可用,旧链接可达;
|
||||
2. 一个不懂量化的用户按 §3.1 引导能独立跑出一次检验并读懂结论句(可用性自测标准);
|
||||
3. 一个自定义公式因子走完 编辑→校验→试算→注册→检验→进策略评分 全链路无断点;
|
||||
4. 全程未产生第二套计算逻辑(注册/编辑器/组合均编译到同一条 Polars/检验路径);
|
||||
5. P0–P3 各 PR 验收标准逐项通过,界面证据与测试结果真实可查。
|
||||
@@ -0,0 +1,842 @@
|
||||
# 因子体系专业化设计(提案)
|
||||
|
||||
> **状态声明**:本文是设计提案,**尚未实现**。凡标注【现状】的条目引用当前仓库真实代码(基于 main@2ce8b4b1),可直接核对;凡标注【设计】的条目是目标契约,**不得当作已存在的 API 导入或调用**(遵循 `docs/secondary-development.md` 第 1 节的状态区分要求)。
|
||||
>
|
||||
> 全部【现状】引用已于 2026-09-04 逐条核对,Polars API 与依赖可行性已实测(polars 1.40.1),验证记录见 §17;一处初稿引用错误(pipeline.py:1738→1795)已修正。
|
||||
>
|
||||
> 涉及改动均按二次开发分级标注(L1 配置 / L2 扩展点 / L3 核心源码修改,见 `docs/secondary-development.md` 第 2 节)。
|
||||
|
||||
---
|
||||
|
||||
## 0. 设计目标
|
||||
|
||||
1. **因子定义单一事实源**:公式、元数据、计算、测试同处一地,可审计、可版本化。
|
||||
2. **研究结论可辩护**:宇宙可解释、风险调整显式、统计检验完备、指标口径唯一。
|
||||
3. **策略接入零摩擦**【用户核心诉求】:因子研究成果(含用户自定义因子、复合因子、挖掘产物)以统一形态被策略评分、选股、回测、监控四端消费,一处定义、处处生效。
|
||||
4. **不推倒重来**:挖掘框架(purge/embargo/嵌套样本外)、enriched 列体系、虚拟评分机制全部保留,只做补层和收口。
|
||||
|
||||
---
|
||||
|
||||
## 1. 分层总览与现状映射
|
||||
|
||||
| 层 | 目标模块 | 现状代码 | 动作 | 分级 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| L-REG 因子注册表 | `app/factors/registry.py`【设计】 | `backtest/factor.py:36` FACTOR_COLUMNS + `strategy/scoring.py` 虚拟因子 + `indicators/pipeline.py` ENRICHED_COLUMNS | 三处合一收口 | L3(重构) |
|
||||
| L-DSL 表达式层 | `app/factors/dsl/`【设计】 | 无(`strategy/custom_signals.py` 白名单模式可借鉴) | 新增 | L2 |
|
||||
| L-UNI 宇宙构建 | `app/factors/universe.py`【设计】 | 无(tradable/limit_up_locked/listing_date 素材已存在) | 新增 | L2 |
|
||||
| L-NEU 风险调整 | `app/factors/neutralize.py`【设计】 | 无(`get_index_daily`、行业 preset、`share_capital.py` 素材已存在) | 新增 | L2 |
|
||||
| L-INF 统计检验 | `app/factors/stats.py`【设计】 | 无 | 新增 | L2 |
|
||||
| L-MET 指标统一 | `app/factors/metrics.py`【设计】 | `backtest/engine.py` 三种 Sharpe(:2899/:2993/:3116) | 收敛 + 版本化 | L3(热点) |
|
||||
| L-CMP 复合因子→策略 | `app/factors/composite.py`【设计】 | `strategy/scoring.py` 虚拟评分字段机制【现状·已可用】 | 扩展既有机制 | L2→L3 接线 |
|
||||
| 数据契约 | provider dataset 声明 | 无 ST 历史/退市股/点时行业 | 新增 dataset | L1(YAML)+provider 实现 |
|
||||
|
||||
模块落点说明:新建 `app/factors/` 包而不是塞进 `backtest/`,因为因子目录、宇宙、中性化被选股(`strategy/`)、回测(`backtest/`)、挖掘(`backtest/mining.py`)三方消费,放任一方都会造成反向依赖(违反 CONTRIBUTING 2.3 模块边界)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 因子注册表(L-REG)
|
||||
|
||||
### 2.1 现状问题
|
||||
|
||||
因子元数据目前分散在四处,互相漂移无感知:
|
||||
|
||||
| 位置 | 内容 | 缺陷 |
|
||||
| --- | --- | --- |
|
||||
| `backtest/factor.py:36-109` | 62+ 因子目录(id/label/group/desc) | desc 是自然语言,与计算无绑定 |
|
||||
| `strategy/scoring.py:13-51` | VIRTUAL_SCORING_DEPENDENCIES | 虚拟因子的依赖声明,但与因子目录是两套清单 |
|
||||
| `strategy/scoring.py:80+` | `scoring_value_expr` | 虚拟因子的 Polars 表达式,硬编码 if-else 分发 |
|
||||
| `strategy/scoring.py:53-66` | `_ROLLING_SCORING_WARMUP` | 预热窗口第三套清单 |
|
||||
|
||||
### 2.2 FactorSpec 完整 schema【设计】
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class FactorSpec:
|
||||
id: str # 全局唯一,^f_[a-z0-9_]{1,40}$;内置因子保持现有列名不变(如 momentum_20d)
|
||||
version: int # 因子语义版本;公式变更必须 +1,进缓存键
|
||||
label: str # 中文显示名
|
||||
group: str # 展示分组(沿用现有:动量/均线偏离/超买超卖/趋势/波动率/量价/…)
|
||||
kind: Literal["base", "virtual", "composite", "custom"]
|
||||
# base: 已物化在 enriched parquet
|
||||
# virtual: 按需由 base 列编译计算(如 ma5_bias)
|
||||
# composite: 复合因子(见 §8)
|
||||
# custom: 用户 DSL 因子(见 §3)
|
||||
expr_factory: Callable[[frozenset[str]], pl.Expr | None] | None
|
||||
# virtual/custom 的计算:输入可用列集合,依赖不完整返回 None(fail-closed)
|
||||
formula_text: str # 人类可读公式;virtual 由表达式自动生成,base 手写并配特征化测试锁定
|
||||
dependencies: frozenset[str] # 展开到 enriched base 列(自递归展开 composite/custom 依赖)
|
||||
direction: Literal["high", "low", "none"]
|
||||
# 预期信号方向;进复合因子默认权重与 UI 排序展示
|
||||
unit: Literal["ratio", "pct", "score", "count", "days", "currency", "none"]
|
||||
# 单位口径,UI 格式化与 sanity check 用(禁止"数值<1 乘 100"启发式)
|
||||
warmup_bars: int # 历史窗口需求(交易日数);对齐 _ROLLING_SCORING_WARMUP 语义
|
||||
pit: bool # 是否点时数据依赖(财务因子 = True)
|
||||
pit_source: Literal["financial_announce", "share_capital_announce", "none"]
|
||||
asset_types: frozenset[Literal["stock", "etf"]]
|
||||
incremental_safe: bool # 盘中增量路径(pipeline.py:1795)能否复算;False 则盘中不含该列
|
||||
scale_free: bool # 跨标的可比(可直接截面排序);如 atr_14 原值 = False,atr_pct = True
|
||||
null_policy: Literal["keep", "drop_row"]
|
||||
# 研究路径默认 keep(不填零,沿用 fundamentals.py 纪律)
|
||||
stability: Literal["stable", "experimental", "deprecated"]
|
||||
tags: tuple[str, ...] # 风格标签:"momentum"/"value"/"size"/"lottery"/"liquidity"/…
|
||||
```
|
||||
|
||||
注册表 API(仅内部 Python 接口,不新增 HTTP):
|
||||
|
||||
```python
|
||||
register_factor(spec) # 启动期注册;重复 id 且 version 未增 → 拒绝启动(fail-closed)
|
||||
get_factor(fid) -> FactorSpec
|
||||
all_factors(asset_type=None, stable_only=False) -> list[FactorSpec]
|
||||
factor_dependencies(fids) -> frozenset[str] # 递归展开
|
||||
factor_value_exprs(available_cols) -> dict[str, pl.Expr | None]
|
||||
```
|
||||
|
||||
### 2.3 迁移策略【设计】
|
||||
|
||||
1. **特征化测试先行**:固定样本(≥50 只股票 × 含除权日、停牌日、涨跌停日的窗口)快照当前全部 62+ 因子在 enriched 与 `scoring_value_expr` 两条路径的输出,重构后断言逐位一致。扩展 `backend/tests/backtest/test_factor_library_v2.py`。
|
||||
2. `VIRTUAL_SCORING_DEPENDENCIES`、`scoring_value_expr` 的 if-else 分发、`_ROLLING_SCORING_WARMUP` 逐一改读注册表,**函数签名不变**(`scoring.py` 对外契约保持)。
|
||||
3. `FACTOR_COLUMNS` 改由注册表生成,`factor.py` 对外常量保留为兼容别名。
|
||||
4. desc 公式与 `formula_text` 不一致处,以特征化测试输出的实际计算为准修正文档。
|
||||
|
||||
### 2.4 因子分类学与补全清单【设计】
|
||||
|
||||
现有 11 组保留;补全以下专业常用因子(标注数据依赖,缺数据不注册、不静默):
|
||||
|
||||
| 族 | 建议新增 | 公式要点 | 依赖 |
|
||||
| --- | --- | --- | --- |
|
||||
| 动量 | 特质动量 `f_idio_mom_20d` | 个股日收益对基准收益回归残差的 20 日累计 | 指数日K(已有) |
|
||||
| 动量 | 52 周新高接近度 `f_near_high_52w` | close / 250 日最高 close − 1 | 已有 |
|
||||
| 反转 | 短期反转 `f_rev_5d` | −momentum_5d(direction=low 的语义化封装) | 已有 |
|
||||
| 波动 | 已实现波动偏度差、高低频波动分解 | 简化:`f_vol_ratio_short_long` = vol_5d/vol_60d | 已有 |
|
||||
| 波动(条件) | 条件波动率 `f_ewma_vol` | RiskMetrics EWMA(λ=0.94) 条性日波动年化;比等权 rolling_std 对近端冲击响应更快,低成本低争议 | 已有(`ewm_std` 向量化) |
|
||||
| 波动(条件) | 波动的波动 `f_vol_of_vol_60d` / 波动区制 `f_vol_regime` | 波动率的滚动 std / EWMA 波动 ÷ 长期波动;区分"高波市场"与"波动突变",A 股风格切换敏感因子 | 已有 |
|
||||
| 波动(条件) | GARCH(1,1) 条件波动 `f_garch_vol` | **标记 experimental、按需实现**:逐 symbol 递归拟合与全向量化管线冲突,若引入必须走 `numba_runtime.py`【现状】路径或 numba/arch 依赖,先以 EWMA 交付(日频下 EWMA ≈ GARCH 的 90% 价值) | 已有 + numba |
|
||||
| 量价 | 量价背离 `f_pv_divergence_20d` | −vol_price_corr_20d 语义化 | 已有 |
|
||||
| 流动性 | 非流动性变化 `f_amihud_chg` | amihud_20d / amihud_60d − 1 | 已有 |
|
||||
| 规模 | 流通市值对数 `f_log_float_mv` | ln(历史流通股本 × raw_close)【点时股本,share_capital.py 已有】 | 已有 |
|
||||
| 价值 | `f_ep_latest`、`f_ep_ttm` | 1/PE 口径(E/P 比 PE 统计性质更好);ttm 需财务四表滚动 | 财务(已有);ttm 需扩展 |
|
||||
| 质量 | 应收/存货增速差、商誉/净资产 | 财务表字段 | 财务(需字段核对) |
|
||||
| 涨停 | 首板/连板区分、炸板后回封率 | 基于 consecutive_limit_ups、炸板列 | 已有 |
|
||||
| 财务 | SUE(盈余惊喜) | (E_t − E_{t-4}) / σ(ΔE, 4期),公告日口径 | 财务历史(已有 `_merge_report_history`) |
|
||||
|
||||
---
|
||||
|
||||
### 2.5 全量因子目录映射表(代码推导,PR-6 注册蓝本)
|
||||
|
||||
下表覆盖现有全部 61 个因子【现状:`factor.py:36-109`】,kind 与依赖由 `scoring.py:13-51` VIRTUAL_SCORING_DEPENDENCIES 逐字推导(virtual = 表中出现的键,base = 未出现即已物化列),运行时预热取自 `scoring.py:53-66` `_ROLLING_SCORING_WARMUP` 代码值。direction 列为**建议初值**(high=因子值大预期收益高;"待标定"= 振荡类/方向依市场状态,PR-6 注册时依 IC 实证方向标定并允许研究层覆盖)。
|
||||
|
||||
| 因子 | 组 | kind | 依赖(virtual 展开) | 预热 | direction |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| momentum_5d/10d/20d/30d/60d | 动量 | base | — | 全局120日 | high |
|
||||
| change_pct | 动量 | base | — | 全局 | high |
|
||||
| ma5..60_bias(5个) | 均线偏离 | virtual | {close, maN} | 全局 | high |
|
||||
| ema5..60_bias(5个) | 均线偏离 | virtual | {close, emaN} | 全局 | high |
|
||||
| rsi_6 / rsi_14 / rsi_24 | 超买超卖 | base | — | 全局 | 待标定 |
|
||||
| macd_hist | 趋势 | base | — | 全局 | 待标定 |
|
||||
| macd_dif_pct / macd_dea_pct / macd_hist_pct | 趋势 | virtual | {close, macd_dif/dea/hist} | 全局 | high |
|
||||
| kdj_k / kdj_d / kdj_j | 趋势 | base | — | 全局 | 待标定 |
|
||||
| boll_position | 趋势 | virtual | {close, boll_upper, boll_lower} | 全局 | high |
|
||||
| annual_vol_20d | 波动率 | base | — | 全局 | low |
|
||||
| atr_14 | 波动率 | base | — | 全局 | 待标定 |
|
||||
| atr_pct | 波动率 | virtual | {close, atr_14} | 全局 | low |
|
||||
| amplitude | 波动率 | base | — | 全局 | low |
|
||||
| boll_width | 波动率 | virtual | {ma20, boll_upper, boll_lower} | 全局 | low |
|
||||
| vol_ratio_5d | 量价 | base | — | 全局 | 待标定 |
|
||||
| vol_ratio_10d | 量价 | virtual | {volume} | 11 | 待标定 |
|
||||
| vol_trend_5_10 | 量价 | virtual | {vol_ma5, vol_ma10} | 全局 | high |
|
||||
| turnover_rate | 量价 | base | — | 全局 | low |
|
||||
| turnover_ratio_5d | 量价 | virtual | {turnover_rate} | 6 | high |
|
||||
| log_amount | 量价 | virtual | {amount} | 全局 | 待标定 |
|
||||
| amount_ratio_5d | 量价 | virtual | {amount} | 6 | high |
|
||||
| gap_return | 价格位置 | virtual | {open, prev_close} | 全局 | 待标定 |
|
||||
| intraday_return | 价格位置 | virtual | {open, close} | 全局 | 待标定 |
|
||||
| close_position | 价格位置 | virtual | {high, low, close} | 全局 | 待标定 |
|
||||
| distance_to_high_60d | 价格位置 | virtual | {close, high_60d} | 全局 | high |
|
||||
| distance_from_low_60d | 价格位置 | virtual | {close, low_60d} | 全局 | high |
|
||||
| vwap_bias | 价格位置 | virtual | {close, volume, amount} | 全局 | 待标定 |
|
||||
| max_ret_20d | 收益形态 | virtual | {close} | 21 | low |
|
||||
| ret_skew_20d | 收益形态 | virtual | {close} | 21 | low |
|
||||
| up_days_20d | 收益形态 | virtual | {close} | 21 | 待标定 |
|
||||
| amihud_20d | 流动性 | virtual | {close, amount} | 21 | low |
|
||||
| turnover_z_60d | 流动性 | virtual | {turnover_rate} | 61 | 待标定 |
|
||||
| vol_price_corr_20d | 量价 | virtual | {close, volume} | 21 | 待标定 |
|
||||
| vol_trend_5_60 | 量价 | virtual | {volume} | 60 | high |
|
||||
| limit_up_count_20d | 涨停基因 | virtual | {consecutive_limit_ups} | 21 | high |
|
||||
| limit_up_count_60d | 涨停基因 | virtual | {consecutive_limit_ups} | 61 | high |
|
||||
| pb_latest | 财务 | base(点时联表) | — | 公告日机制 | low |
|
||||
| roe_latest | 财务 | base(点时联表) | — | 公告日机制 | high |
|
||||
| gross_margin_latest | 财务 | base(点时联表) | — | 公告日机制 | high |
|
||||
| net_margin_latest | 财务 | base(点时联表) | — | 公告日机制 | high |
|
||||
| revenue_yoy_latest | 财务 | base(点时联表) | — | 公告日机制 | high |
|
||||
| net_income_yoy_latest | 财务 | base(点时联表) | — | 公告日机制 | high |
|
||||
| debt_ratio_latest | 财务 | base(点时联表) | — | 公告日机制 | low |
|
||||
|
||||
计数核对:virtual 35 + base 非财务 19 + 财务 7 = 61,与 FACTOR_COLUMNS 一致。base 因子的研究预热由 `FACTOR_WARMUP_DAYS=120`(`factor.py:111`【现状】)统一承担;财务因子 pit=true、pit_source=financial_announce。
|
||||
|
||||
## 3. 因子表达式层(L-DSL)
|
||||
|
||||
### 3.1 语法与算子表【设计】
|
||||
|
||||
表达式 = `expr ::= operand | expr op expr | func(expr[, expr[, const]])`;中缀 + 函数调用,无变量赋值、无循环。
|
||||
|
||||
**操作数**:基准列(open/high/low/close/volume/amount/turnover_rate/prev_close/raw_close)、白名单指标列(注册表中 base 因子)、已注册因子 id(virtual/composite/custom,递归内联展开)、数值常量。
|
||||
|
||||
**时序算子**(`over("symbol")`,窗口 n ∈ [2, 512],全部只向后看):
|
||||
|
||||
| 算子 | 语义 | Polars 编译 |
|
||||
| --- | --- | --- |
|
||||
| `ts_mean(x,n)` / `ts_std(x,n)` / `ts_sum(x,n)` | 滚动均值/样本标准差/求和 | `rolling_mean/std/sum(n)` |
|
||||
| `ts_max(x,n)` / `ts_min(x,n)` | 滚动极值 | `rolling_max(n)` / `rolling_min(n)` |
|
||||
| `ts_delta(x,n)` | x − ts_delay(x,n) | `x - x.shift(n)` |
|
||||
| `ts_delay(x,n)` | n 期前的值(n ∈ [1, 512],**禁止负数**——负数即未来函数,编译期报错) | `x.shift(n)` |
|
||||
| `ts_rank(x,n)` | 当期值在滚动窗口内的分位 | `rolling_rank(n)`【已验证:polars 1.40.1 存在且行为正确,§17】 |
|
||||
| `ts_zscore(x,n)` | (x − ts_mean)/ts_std | 组合表达式 |
|
||||
| `ts_corr(x,y,n)` / `ts_cov(x,y,n)` | 滚动相关/协方差 | 顶层函数 `pl.rolling_corr(x,y,window_size=n)` / `pl.rolling_cov`【已验证:Expr 上无此方法,必须走顶层函数,§17】 |
|
||||
| `ts_quantile(x,n,q)` | 滚动分位(q ∈ (0,1) 常量) | `rolling_quantile` |
|
||||
| `decay_linear(x,n)` | 线性衰减加权均值(近端权重大) | 手写权重组合表达式 |
|
||||
|
||||
**v1 不提供的时序算子及原因**:`ts_argmax/ts_argmin`——Polars 无向量化实现(`rolling_map` 为逐窗 Python 回调,违反向量化约束,已验证 Expr 无 `rolling_arg_max`);"距极值天数"类需求以具体因子的组合表达式实现(如 `distance_to_high_60d` 模式),确有高频需求再经 numba 扩展。
|
||||
|
||||
**截面算子**(按日期分组,逐日横截面):
|
||||
|
||||
| 算子 | 语义 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `rank(x)` | 横截面百分位排名 ∈ (0,1] | null 不参与排名 |
|
||||
| `zscore(x)` | 横截面 (x−μ)/σ | σ=0 → null |
|
||||
| `winsorize(x,k)` | 截尾至 μ±kσ(k ∈ [1,6] 常量,默认 3) | 截面口径 |
|
||||
|
||||
**算术/工具**:`+ − * /`(除零 → null)、`log abs sign sqrt min max power(x,c) clamp(x,lo,hi)`、三元 `if_else(cond, a, b)`、比较与逻辑 `> >= < <= == != and or not`(产出布尔,配合 if_else)。
|
||||
|
||||
### 3.2 校验规则(编译期全部强制)【设计】
|
||||
|
||||
1. 标识符必须在基准列/白名单/已注册因子内,否则报错(防注入,沿用 `custom_signals.py` 白名单哲学)。
|
||||
2. `ts_delay`/`ts_delta` 的 n ≥ 0;任何窗口 n ∈ [2, 512];`power` 指数 |c| ≤ 4;AST 深度 ≤ 12;表达式 token 数 ≤ 200。
|
||||
3. 常量折叠后若产生 `x/0` 类静态除零 → 编译失败。
|
||||
4. 依赖列集合 = 递归展开;warmup_bars = max(各 ts 算子窗口);超出即注册表标记,研究 UI 提示所需历史长度。
|
||||
5. 产出类型必须为数值或布尔(布尔经 `cast` 视为 0/1)。
|
||||
6. **禁止未来引用的总闸**:所有时序算子 shift 语义已内建,语法层不存在负 shift;code review checklist 补一条"新增算子必须只向后看"。
|
||||
|
||||
### 3.3 编译流水线【设计】
|
||||
|
||||
`text → tokenizer → Pratt 解析 → AST → 语义检查(§3.2) → 依赖/预热推导 → Polars Expr 工厂`。产出缓存(表达式文本 → 编译产物 LRU,键含依赖列版本);编译失败返回结构化错误(位置 + 原因),不抛裸异常。
|
||||
|
||||
### 3.4 形式文法(EBNF)与错误码目录【设计】
|
||||
|
||||
```ebnf
|
||||
expr = or_expr ;
|
||||
or_expr = and_expr { "or" and_expr } ;
|
||||
and_expr = cmp_expr { "and" cmp_expr } ;
|
||||
cmp_expr = add_expr [ (">" | ">=" | "<" | "<=" | "==" | "!=") add_expr ] ;
|
||||
add_expr = mul_expr { ("+" | "-") mul_expr } ;
|
||||
mul_expr = unary { ("*" | "/") unary } ;
|
||||
unary = "-" unary | primary ;
|
||||
primary = NUMBER | IDENT | func_call | "(" expr ")" ;
|
||||
func_call = IDENT "(" [ arglist ] ")" ;
|
||||
arglist = expr { "," expr } ;
|
||||
(* IDENT:基准列/白名单指标列/已注册因子 id/算子名;NUMBER:十进制与负号经 unary 处理 *)
|
||||
```
|
||||
|
||||
运算符优先级由产生式层级固定(or < and < 比较 < 加减 < 乘除 < 一元负号 < 原子),与 Python/JS 语义一致,降低用户迁移成本。
|
||||
|
||||
**错误码目录**(编译与运行校验的唯一错误词汇表,API/编辑器/UI 共用):
|
||||
|
||||
| 码 | 含义 | 触发 |
|
||||
| --- | --- | --- |
|
||||
| E001 | 未知标识符 | IDENT 不在白名单/注册表 |
|
||||
| E002 | 未知函数 | 函数名不在算子表 |
|
||||
| E003 | 参数数量/类型不符 | 算子签名不匹配(含常量参数位置) |
|
||||
| E004 | 窗口越界 | n∉[2,512] 或 q∉(0,1) |
|
||||
| E005 | 负 shift | ts_delay/ts_delta 的 n<0 |
|
||||
| E006 | 嵌套深度超限 | AST 深度>12 |
|
||||
| E007 | 规模超限 | token 数>200 |
|
||||
| E008 | 静态除零 | 常量折叠检出分母恒 0 |
|
||||
| E009 | 产出类型非法 | 非数值/布尔 |
|
||||
| E010 | power 指数越界 | \|c\|>4 |
|
||||
| E011 | winsorize k 越界 | k∉[1,6] |
|
||||
| E012 | 循环引用 | 因子依赖成环(含自定义因子链) |
|
||||
| E013 | 依赖列不可用 | 面板缺列(运行时) |
|
||||
| E014 | 语法错误 | 解析失败(附位置) |
|
||||
| E015 | 预热不足 | warmup > 研究窗口(运行时) |
|
||||
| E016 | 常量表达式 | 无任何标识符,拒绝保存 |
|
||||
|
||||
错误响应统一结构:`{"code": "E001", "message": "未知标识符: clos", "position": {"offset": 12, "line": 1}, "detail": {...}}`。
|
||||
|
||||
### 3.5 用户因子生命周期与存储【设计】
|
||||
|
||||
- 存储路径:`data/user_data/custom_factors/*.json`(对齐 custom_signals 目录约定),schema:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "uf_my_rev", // ^uf_[a-z0-9_]{1,40}$,前缀与 csg_ 同哲学
|
||||
"version": 1,
|
||||
"label": "我的反转因子",
|
||||
"formula": "rank(-ts_sum(change_pct, 5))",
|
||||
"direction": "low",
|
||||
"description": "5 日累计涨幅的截面倒数",
|
||||
"created_at": "2026-09-04T00:00:00",
|
||||
"updated_at": "2026-09-04T00:00:00"
|
||||
}
|
||||
```
|
||||
|
||||
- 生命周期:草稿(编辑器内试算,不落盘)→ 保存(编译通过 + 试算有非空输出才可保存,fail-closed)→ 引用(策略 scoring / 因子研究 / 复合因子)→ 版本化(公式变更 version+1,旧结果按 version 键隔离)→ 删除(有引用时列出引用方并二次确认,对齐策略删除的 fail-closed 要求)。
|
||||
- 加载失败的单个文件只禁用该因子并提示,不影响启动与其他因子(对齐 plugins 隔离要求,CONTRIBUTING 第 4 节)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 宇宙构建(L-UNI)
|
||||
|
||||
### 4.1 UniverseSpec 完整 schema【设计】
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class UniverseSpec:
|
||||
exclude_suspended: bool = True # 停牌(tradable 矩阵口径,matrix.py:1476+)
|
||||
exclude_limit_locked: bool = True # 调仓时点一字涨停不可买入者(buy_limit_up 口径)
|
||||
exclude_st: bool = False # 非点时(今日名称),开启时报告中必须出现降级注记
|
||||
min_listing_days: int = 0 # 次新剔除;listing_date 已入库未使用(api/data.py:765)
|
||||
min_amount_quantile: float | None = None # 流动性过滤:当日成交额截面分位下限 (0,1)
|
||||
cap_quantile_range: tuple[float, float] | None = None # 市值分位区间;依赖 f_log_float_mv
|
||||
max_names: int | None = None # 截面数量上限;排序键由研究上下文显式传入(因子值或复合分,不隐式默认),用于微型宇宙研究
|
||||
```
|
||||
|
||||
### 4.2 执行语义【设计】
|
||||
|
||||
- 按日生成 `universe[date] -> set[symbol]`,**每个过滤条件独立短路、独立计数**,产出 `filter_stats`(每日各过滤器剔除数),研究报告展示"宇宙从 5200 → 4980 → 4890"漏斗。
|
||||
- `universe_id = sha256(canonical_json(spec))[:12]`,进一切下游缓存键。
|
||||
- 过滤顺序固定(先便宜的列过滤,后需联表的),顺序本身进 canonical_json。
|
||||
- **as_of 语义**:宇宙内一切判定只用当日及以前数据。ST 例外必须显式标注 `degraded: ["st_not_point_in_time"]` 并在报告 UI 渲染黄条。
|
||||
|
||||
### 4.3 数据缺口降级矩阵【设计】
|
||||
|
||||
| 过滤器 | 数据缺失时行为 |
|
||||
| --- | --- |
|
||||
| exclude_suspended / exclude_limit_locked | 素材必在(enriched 必算列);缺失 = 数据本身异常 → fail 报错 |
|
||||
| exclude_st | instruments 名称缺失 → 过滤器跳过 + 降级注记(不静默假装过滤了) |
|
||||
| min_listing_days | listing_date 缺失的标的视为"不满足"剔除(保守),计数展示 |
|
||||
| cap_quantile_range | 历史股本缺失标的退出该过滤(不参与分位),降级注记 |
|
||||
| 退市股(未来) | 依赖新 dataset(§9);无数据源时 universe 定义退化为"当前上市 ∪ 本地历史",报告中永久注记幸存者偏差警示 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 风险调整与基准(L-NEU)
|
||||
|
||||
### 5.1 NeutralizationSpec【设计】
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class NeutralizationSpec:
|
||||
benchmark: str | None = "000001.SH" # 上证指数(index_const.py:12【现状】核心四只之一);扩展指数见 §9
|
||||
return_basis: Literal["raw", "excess"] = "excess" # IC/分层收益口径
|
||||
method: Literal["none", "industry_demean", "industry_zscore", "regression_industry_size"] = "none"
|
||||
winsorize_sigma: float | None = 3.0 # 因子值截面截尾;None = 不截尾
|
||||
# neutralization_id = sha256(canonical_json)[:12],进缓存键
|
||||
```
|
||||
|
||||
### 5.2 方法规格【设计】
|
||||
|
||||
- 超额收益:`r_ex = r_stock − r_bench`(基准同日收益;基准停市日沿用最近交易日,日历由数据轴驱动)。
|
||||
- `industry_demean`:`f' = f − mean_ind(f)`(THS 行业一级,ext preset 快照)。
|
||||
- `industry_zscore`:组内标准化 `f' = (f − μ_ind)/σ_ind`(σ=0 组 → null)。
|
||||
- `regression_industry_size`:`f ~ 1 + 行业哑变量 + log_float_mv` 的残差(逐日 OLS,Polars 表达式实现,n<30 或共线 → 回退 demean + 注记)。
|
||||
- 固定管线顺序:`宇宙过滤 → winsorize → 中性化 → 标准化(zscore 或 rank)`;顺序进 spec 哈希。
|
||||
- **行业快照局限**(当前归属回填历史)写入 `degraded` 注记并在报告显示;点时行业表到位后(§9)仅切换数据源,spec 不变。
|
||||
|
||||
### 5.3 报告口径并列【设计】
|
||||
|
||||
IC 报告同时输出三列:`原始 / 超额 / 超额+中性化`,默认排序以最后一列为准——旧结论可查,新结论更严,不静默替换。
|
||||
|
||||
---
|
||||
|
||||
## 6. 统计检验(L-INF)
|
||||
|
||||
**依赖原则**:后端当前无 scipy/statsmodels(已验证,§17),运行时保持零新增第三方依赖——NW/BH-FDR/DSR 全部以 numpy 手写实现(各约 20-40 行);statsmodels 仅允许加入 uv dev 依赖组用于测试对拍,不进运行时 import。
|
||||
|
||||
### 6.1 统计量精确定义【设计】
|
||||
|
||||
| 统计量 | 定义 | 备注 |
|
||||
| --- | --- | --- |
|
||||
| Rank IC | 逐日 Spearman(factor_t, fwd_ret_{t→t+h}),现有口径不变(factor.py:740-750) | — |
|
||||
| IC t 值(朴素) | `t = mean(IC) / (std(IC, ddof=1)/√N)` | 仅作对照展示 |
|
||||
| IC t 值(NW) | Newey-West HAC 稳健标准误,滞后 `L = h`(h 日前瞻收益使 IC 序列存在 h−1 阶移动平均自相关) | **主口径**;numpy 手写 Bartlett 核加权,测试用固定黄金参考向量 + 可选 dev 组 statsmodels 对拍 |
|
||||
| ICIR | mean(IC)/std(IC),已有 | — |
|
||||
| IC 自相关 & 半衰期 | ACF(1..10);半衰期 = ACF 首次 < 0.5 的滞后(线性插值);无收敛 → null | 换手率预期管理 |
|
||||
| 分层单调性 | Spearman(组序号, 组均超额收益) + 线性趋势斜率 t 值 | 判定"梯子是否成立" |
|
||||
| 多空 t 值 | 顶组−底组日超额收益序列的 NW t | 滞后 = 调仓周期的收益重叠阶数 |
|
||||
| BH-FDR q 值 | 对 optimizer/mining 排行榜全体 p 值(每行 = 其 OOS/IS 最优组合的 IC 或收益 t 值双尾 p)做 Benjamini-Hochberg,q_i = min_{j≥i}(N·p_j/j) 单调化 | 排行榜级,不进单因子报告 |
|
||||
| Deflated Sharpe | Bailey-López de Prado:以试验次数 N(挖掘 trial 预算已计数,mining.py:1041+)与偏度峰度校正 SR₀,DSR = Φ((SR−SR₀)·√(T−1) / √(1−γ̂₃SR+((γ̂₄−1)/4)SR²)) | 挖掘晋升报告展示"考虑搜索后的置信" |
|
||||
| 覆盖率/换手率 | 已有(factor.py:1123-1166),补充宇宙过滤后口径 | — |
|
||||
|
||||
### 6.2 显著性标注约定【设计】
|
||||
|
||||
|t| < 1.645 无标注;≥1.645 `*`(10%);≥1.96 `**`(5%);≥2.576 `***`(1%)。报告 UI 图标化,q ≥ 0.10 的挖掘候选禁止晋升(现有晋升门槛 mining.py:26-31 之上叠加,未达标给出具体差值)。
|
||||
|
||||
### 6.3 版本命名规则与报告完整字段【设计】
|
||||
|
||||
**方法论版本命名**(单一规则,全文档统一):
|
||||
|
||||
- `factor_v3` = 本设计交付的因子研究方法论(三口径 + 统计检验 + 宇宙/中性化 spec 进键);现有 `factor_v2`(factor.py:112【现状】)结果按旧版本读取展示,不重算。
|
||||
- `metrics_v2` = §7 指标统一后的口径;与 `factor_v3` 独立演进,报告分别携带。
|
||||
- 因子个体 `version`(FactorSpec)与研究方法论版本正交:因子公式变更不改方法论版本,反之亦然。
|
||||
|
||||
**IC 研究报告完整字段定义**(`POST /api/factor-research/ic` 响应,§10 示例为其节选):
|
||||
|
||||
```text
|
||||
methodology_version: str # "factor_v3"
|
||||
factor_id / factor_version: str / int
|
||||
universe_id / neutralization_id: str # 两 spec 哈希
|
||||
universe: object # 回显生效 UniverseSpec
|
||||
neutralization: object # 回显生效 NeutralizationSpec
|
||||
date_range: {start, end, rebalance, n_groups}
|
||||
ic: {mean, std, icir, t_naive, t_newey_west, nw_lag, significance,
|
||||
half_life_days|null, acf: float[10], win_rate, coverage, n_days}
|
||||
ic_decay: [{horizon, ic_mean, icir}] # 沿用现有 1/3/5 日结构
|
||||
ic_yearly: [{year, ic_mean, icir, n_days}] # 沿用现有结构
|
||||
ic_by_basis: {raw, excess, excess_neutralized} × {mean, t_newey_west}
|
||||
monotonicity: {spearman, trend_t, verdict} # verdict ∈ 成立/弱/不成立
|
||||
groups: [{group, excess_return_annual, nav, turnover, n_names,
|
||||
t_stat, avg_name_count}] # 每组含 t 值
|
||||
long_short: {annual_return, t_newey_west, max_drawdown, executable_short: false}
|
||||
turnover_top_group: float
|
||||
costs: {commission_pct, stamp_tax_pct, slippage_bps, round_trip}
|
||||
universe_funnel: [{date, raw, after_suspended, after_limit_locked,
|
||||
after_st|null, after_new_listing|null, after_filters}]
|
||||
degraded: [str] # 如 st_not_point_in_time / industry_snapshot
|
||||
warnings: [str] # 非降级类提示(预热边界、覆盖不足等)
|
||||
```
|
||||
|
||||
新增字段全部带默认值,历史(factor_v2)缓存结果缺字段时前端显示为空,不报错。
|
||||
|
||||
---
|
||||
|
||||
## 7. 绩效指标统一(L-MET)
|
||||
|
||||
### 7.1 唯一口径【设计】
|
||||
|
||||
- **Sharpe**:净值曲线日收益 `r_t = nav_t/nav_{t−1} − 1`,`Sharpe = mean(r)/std(r, ddof=1) × √A`;无风险利率参数 `rf_annual`(默认 0,单位/年,日化按 A 折算)。引擎三种旧口径(逐笔/仅卖出日/净值)收敛为净值口径;前两者字段保留一个版本周期并标 `deprecated_mode`。
|
||||
- **年化天数 A**:默认 243(近五年 A 股实际均值区间),`metrics_methodology_version = "metrics_v2"`;报告展示口径徽章。
|
||||
- **年化收益**:`(nav_T/nav_0)^(A/n_bars) − 1`(统一按 K 线数折算,废除 365.25 自然日混用,engine.py:2871-2874 收敛)。
|
||||
- **MaxDD**:现有算法(峰值下限 1.0)不变。
|
||||
- **基准相对新增**:`excess_annual`、`tracking_error = std(r−r_b)×√A`、`information_ratio = mean(r−r_b)/std(r−r_b)×√A`、`beta/alpha`(OLS,rf 处理同上)、`excess_win_rate`。
|
||||
- `engine.py`/`strategy.py`/`factor.py` 全部改 import `app/factors/metrics.py`,禁止本地重算(Ruff 检查加入 noqa 禁用清单之外无豁免)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 复合因子与策略接入(L-CMP)——核心章节
|
||||
|
||||
### 8.1 现有桥(【现状·已可用】,设计的锚点)
|
||||
|
||||
- 策略配置:`"scoring": {"factor_name": weight}` + `scoring_directions` 覆盖高低方向(`builtin/*.py` 均此形态)。
|
||||
- `scoring.py`:虚拟因子按需编译 Polars 表达式(`scoring_value_expr`)、依赖展开(`scoring_dependencies`)、预热推导(`scoring_warmup_bars`)。
|
||||
- 回测矩阵按 `score` 排序建仓(`engine.py` `max_positions` + `score_min/max`)。
|
||||
- 挖掘产物 = 因子排名组合(mining.py),候选库 `candidates.py` 已有 `factor`/`strategy` 双形态。
|
||||
|
||||
### 8.2 FactorCompositeSpec【设计】
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class FactorCompositeSpec:
|
||||
id: str # ^cf_[a-z0-9_]{1,40}$;策略 scoring 里以 "cf_xxx" 引用
|
||||
version: int
|
||||
label: str
|
||||
factors: tuple[CompositeMember, ...] # 1..10 个成员
|
||||
transform: Literal["rank", "zscore"] = "rank" # 成员标准化方式(截面)
|
||||
weighting: Literal["manual", "equal", "icir", "max_ic"] = "manual"
|
||||
auto_weight_window: int = 504 # 自动权重的滚动窗口(交易日)
|
||||
direction: Literal["high", "low"] = "high"
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CompositeMember:
|
||||
factor: str # 任意已注册因子 id(含 uf_/cf_ 前缀,禁止自引用,环检测)
|
||||
weight: float | None # manual 模式必填;自动模式忽略
|
||||
direction_override: Literal["high", "low"] | None = None
|
||||
```
|
||||
|
||||
**计算管线(顺序固定,进 spec 哈希)**:
|
||||
|
||||
```
|
||||
宇宙(可选, 默认不过滤以兼容现有策略)
|
||||
→ 各成员因子值(注册表展开,含 warmup 检查)
|
||||
→ winsorize(3σ, 可关)
|
||||
→ 截面 transform(rank/zscore)
|
||||
→ 方向统一(low → 取负)
|
||||
→ 加权求和(manual 权重归一化校验 |Σw−1|<1e-9;
|
||||
icir → w_i ∝ max(ICIR_i, 0),ICIR 取 (t−auto_weight_window, t−1] 窗口——**权重只用于过去,严禁 t 日数据参与 t 日权重**;
|
||||
max_ic → 同窗口 mean(rank IC) 单调权重)
|
||||
→ 输出复合分 cf_xxx(rank 基础下近似 ∈ [−1,1],文档声明分布性质)
|
||||
```
|
||||
|
||||
**接入策略(零引擎改动)**:
|
||||
|
||||
- 复合因子注册进注册表(kind="composite"),`scoring_value_expr` 机制天然支持:策略写 `"scoring": {"cf_hotmom": 0.6, "vol_ratio_5d": 0.2, "amount": 0.2}` 即生效;依赖/预热自动递归展开进矩阵构建,选股/回测/监控三端无需感知"这是复合因子"。
|
||||
- 挖掘产物一键导出为 CompositeSpec:mining 的排名组合本来 = factors+weights,导出即 `cf_mined_<run>`,闭合"挖掘 → 复合因子 → 策略"回路。
|
||||
- 前置校验:成员因子任一 warmup 超研究窗口 → 启动期注册成功但使用时返回明确"预热不足"错误(不产出半截分数)。
|
||||
|
||||
**策略侧引用形态(确切 JSON)**——复合因子编辑器"导出 scoring 片段"产出,直接粘贴进策略配置:
|
||||
|
||||
```json
|
||||
{
|
||||
"scoring": {"cf_hotmom": 0.6, "vol_ratio_5d": 0.2, "amount": 0.2},
|
||||
"scoring_directions": {"cf_hotmom": "high", "amount": "high"}
|
||||
}
|
||||
```
|
||||
|
||||
复合因子与普通字段混用、权重语义不变;`scoring_dependencies`/`scoring_warmup_bars` 自动递归展开(`scoring.py:91-104`【现状】机制不动,仅数据源换成注册表)。
|
||||
|
||||
### 8.3 一致性契约【设计】
|
||||
|
||||
同一 `cf_xxx` 在**因子研究(IC/分层)、选股、回测、监控**四端必须逐位同值——单测直接断言四路径对同一 (date,symbol) 的输出相等。这是 CONTRIBUTING 5.3"同一候选集和排序方向"的推广。
|
||||
|
||||
### 8.4 监控端数据流澄清【设计】
|
||||
|
||||
监控不重算复合因子:`monitor.py:1314-1322`【现状】消费的是**策略结果缓存**里的 `result.scores`(`score_min/score_max` 过滤)。因此复合因子进监控的路径 = 策略执行时算好分 → 结果缓存 → 监控读缓存。**实时行情线程零新增计算**(CONTRIBUTING 6.3 硬约束)。推论:修改复合因子定义后,必须走策略参数变更的既有失效链路(重算策略结果缓存 → 监控实例刷新),该链路已存在(CONTRIBUTING 5.1),设计只复用不新造。
|
||||
|
||||
### 8.5 自定义/复合因子的盘中行为【设计】
|
||||
|
||||
- 选股(盘后批量):`incremental_safe=True` 的成员因子照常参与当日计算。
|
||||
- `incremental_safe=False` 成员(若有):当日选股对该因子返回"预热/路径不足"的明确不可计算状态(对齐 CONTRIBUTING 5.1"空值不得伪装成零分"),UI 标注原因;**不降级用部分成员算半截复合分**。
|
||||
- 盘中增量路径(`pipeline.py:1795` `compute_enriched_today`【现状】):自定义与复合因子默认不进入(§12 缓存策略),分时选股若引用则同样返回不可计算状态,盘后恢复。
|
||||
|
||||
---
|
||||
|
||||
## 9. 数据契约扩展(provider dataset)
|
||||
|
||||
新增 dataset 声明(capabilities.py 注册表 + 对应 provider 实现,均【设计】):
|
||||
|
||||
| dataset | 内容 | 解锁能力 | 无数据源时 |
|
||||
| --- | --- | --- | --- |
|
||||
| `st_history` | 点时风险警示状态 | 历史涨跌停幅度修正、宇宙 ST 点时过滤 | 涨跌停用当前名推断 + 注记(现状) |
|
||||
| `delisted_kline` | 退市标的日 K + 退市维表 | 幸存者偏差修复(宇宙回补) | 报告永久幸存者注记 |
|
||||
| `industry_pit` | 点时行业归属 | 中性化升级为点时 | 行业快照 + 注记(现状) |
|
||||
| `index_ext` | 扩展指数日 K | 基准升级(当前限核心四只,index_const.py:12-15【现状】) | 基准限核心四只 |
|
||||
|
||||
各 dataset 完整字段 schema(provider 归一后落 Parquet,命名对齐现有 normalized 契约):
|
||||
|
||||
**`st_history`**(分区 `data/parquet/st_history/`,按年):
|
||||
|
||||
| 字段 | 类型 | 语义 |
|
||||
| --- | --- | --- |
|
||||
| symbol | str | 标准代码(如 600000.SH) |
|
||||
| flag_date | date | 状态生效日(戴帽/摘帽公告后的首个交易日) |
|
||||
| st_flag | bool | true=风险警示(ST/*ST),false=摘帽;行区间语义:自 flag_date 起至下一条记录 |
|
||||
| flag_type | str | "ST" / "*ST" / "摘帽";缺失填 "ST" |
|
||||
| source | str | provider 标识 |
|
||||
|
||||
查询语义:`st_at(symbol, t) = flag_date ≤ t 的最后一条记录的 st_flag`(asof-backward)。同步:全量快照 + 增量 append,`(symbol, flag_date)` 去重幂等(对齐 kline_sync:358-367【现状】模式)。
|
||||
|
||||
**`delisted_kline`**:K 线部分复用 `daily` dataset 完整 schema(symbol/date/OHLC/volume/amount/…);另需维表 `delisted_instruments`:
|
||||
|
||||
| 字段 | 类型 | 语义 |
|
||||
| --- | --- | --- |
|
||||
| symbol / name | str | 代码/退市前简称 |
|
||||
| list_date / delist_date | date | 上市/退市日 |
|
||||
| delist_reason | str | "面值"/"财务"/"重组"/"主动"/"其他";缺失填 "其他" |
|
||||
|
||||
宇宙回补语义:`as_of=t 的可交易池 = instruments(上市≤t<退市) ∪ delisted(上市≤t<退市)`;`_resolve_universe`(daily_pipeline.py:92-125【现状】)扩展为两源合并。
|
||||
|
||||
**`industry_pit`**(分区 `data/parquet/industry_pit/`):
|
||||
|
||||
| 字段 | 类型 | 语义 |
|
||||
| --- | --- | --- |
|
||||
| symbol | str | 标准代码 |
|
||||
| effective_date | date | 归属生效日 |
|
||||
| industry_l1 / industry_l2 | str | 一级/二级行业名(如 计算机/软件开发,对齐 market_mainline.py:35【现状】的两级口径) |
|
||||
| source | str | provider(如 ths) |
|
||||
|
||||
查询语义:asof-backward join(同 fundamentals.py:107-114【现状】模式);effective_date 缺失 = 供应商不提供历史,整表降级为快照并触发 §5 注记。
|
||||
|
||||
**`index_ext`**:schema 与 `kline_index_daily` 完全一致(symbol/date/OHLC/volume/amount),仅标的白名单扩展(默认建议:000300.SH 沪深300、000905.SH 中证500、000852.SH 中证1000、000985.SH 中证全指);白名单由 preset 配置声明,不进代码硬编码(对齐 ext_presets 模式)。
|
||||
|
||||
**能力注册示例**(provider 侧 `plugin.yaml` datasets 声明,对齐 `docs/plugin-development.md` 契约):
|
||||
|
||||
```yaml
|
||||
datasets:
|
||||
st_history:
|
||||
enabled: true
|
||||
description: 点时风险警示状态(戴帽/摘帽区间)
|
||||
delisted_kline:
|
||||
enabled: true
|
||||
description: 退市标的日K + 退市维表
|
||||
industry_pit:
|
||||
enabled: false # 供应商无历史归属时声明 false,不注册能力
|
||||
description: 点时行业归属
|
||||
```
|
||||
|
||||
能力矩阵(`capabilities.py` 注册表)同步各 dataset 的展示元数据与路由偏好字段;provider 未声明 = 该能力全局不可用,研究路径按 §4.3 降级矩阵处理,不静默。
|
||||
|
||||
---
|
||||
|
||||
## 10. API 契约【设计】
|
||||
|
||||
新路由前缀 `/api/factors`(薄层,重计算在 services/factors_research.py 编排层):
|
||||
|
||||
| 端点 | 方法 | 请求要点 | 响应要点 |
|
||||
| --- | --- | --- | --- |
|
||||
| `/api/factors/catalog` | GET | asset_type, group, stability 过滤 | 因子清单(含 formula_text/warmup/direction/pit/scale_free/usage_count) |
|
||||
| `/api/factors/validate` | POST | formula 文本 | 编译错误(位置+原因)或成功(依赖/预热推导) |
|
||||
| `/api/factors/preview` | POST | formula + symbols + date_range | 试算表格(最新 5 日 × 前 20 标的)+ 非空率 |
|
||||
| `/api/factors/custom` | GET/POST/DELETE | §3.4 JSON | CRUD;删除带引用清单 |
|
||||
| `/api/factor-research/ic` | POST | factor_id, universe_id/UniverseSpec, NeutralizationSpec, start/end, rebalance, n_groups | §6 全套统计 + 三口径并列 + 漏斗 filter_stats + degraded 注记 |
|
||||
| `/api/factor-research/composite` | GET/POST/DELETE | CompositeSpec | CRUD + 一键"作为评分字段试策略"跳转链接 |
|
||||
| `/api/factor-research/universes` | GET/POST | UniverseSpec 存档 | 命名宇宙 CRUD(研究配置复用) |
|
||||
|
||||
全部响应新增字段带默认值;错误响应含 `code/message/detail`,不泄漏内部栈(CONTRIBUTING 第 8 节)。SSE 进度复用现有回测 SSE 模式(长任务:批量 IC 扫描)。
|
||||
|
||||
**鉴权**:新路由经 `api/routes.py`【现状】注册,继承应用级部署口令鉴权(`docs/deploy-password.md` 模式),不引入独立权限模型。写操作(自定义因子/复合因子/宇宙存档 CRUD)只落 `data/user_data/`,路径校验沿用策略目录的防穿越规则(CONTRIBUTING 5.1 删除策略 fail-closed 要求同样适用)。
|
||||
|
||||
**并发**:批量 IC 扫描与复合分批量计算走 `services/heavy_job_limiter.py`【现状】限流,SSE 进度事件结构复用回测现有契约;用户取消走现有回测 worker 取消机制。
|
||||
|
||||
**核心端点示例**(其余端点按同构风格推导):
|
||||
|
||||
`GET /api/factors/catalog?group=动量&asset_type=stock` →
|
||||
|
||||
```json
|
||||
{
|
||||
"factors": [
|
||||
{
|
||||
"id": "momentum_20d", "version": 1, "label": "20日动量", "group": "动量",
|
||||
"kind": "base", "formula_text": "20个交易日累计收益率",
|
||||
"direction": "high", "unit": "ratio", "warmup_bars": 20,
|
||||
"pit": false, "scale_free": true, "stability": "stable",
|
||||
"tags": ["momentum"], "usage_count": 7, "custom": false
|
||||
}
|
||||
],
|
||||
"total": 61, "degraded": []
|
||||
}
|
||||
```
|
||||
|
||||
`POST /api/factors/validate` `{"formula": "rank(ts_delta(close, -5))"}` →
|
||||
|
||||
```json
|
||||
{"ok": false, "errors": [{"code": "E005", "message": "负 shift: ts_delay 的 n 必须 ≥ 0(负数即未来函数)", "position": {"offset": 18, "line": 1}, "detail": {"n": -5}}]}
|
||||
```
|
||||
|
||||
`POST /api/factor-research/ic`:
|
||||
|
||||
```json
|
||||
{
|
||||
"factor_id": "momentum_20d",
|
||||
"universe": {"exclude_suspended": true, "exclude_limit_locked": true, "exclude_st": true, "min_listing_days": 60},
|
||||
"neutralization": {"benchmark": "000001.SH", "return_basis": "excess", "method": "industry_demean", "winsorize_sigma": 3.0},
|
||||
"start": "2023-01-01", "end": "2025-12-31",
|
||||
"rebalance": "monthly", "n_groups": 5
|
||||
}
|
||||
```
|
||||
|
||||
响应(节选,完整字段见 §6):
|
||||
|
||||
```json
|
||||
{
|
||||
"methodology_version": "factor_v3",
|
||||
"universe_id": "a1b2c3d4e5f6", "neutralization_id": "9f8e7d6c5b4a",
|
||||
"ic": {"mean": 0.031, "icir": 0.42, "t_naive": 2.9, "t_newey_west": 1.87, "nw_lag": 1,
|
||||
"significance": "*", "half_life_days": 4, "acf": [0.21, 0.08, ...], "coverage": 0.97},
|
||||
"ic_by_basis": {"raw": {"mean": 0.041, "t_newey_west": 2.2}, "excess": {"mean": 0.031, "t_newey_west": 1.87},
|
||||
"excess_neutralized": {"mean": 0.019, "t_newey_west": 1.02}},
|
||||
"monotonicity": {"spearman": 0.9, "trend_t": 2.4, "verdict": "成立"},
|
||||
"universe_funnel": [{"date": "2025-12-31", "raw": 5412, "after_suspended": 5390, "after_limit_locked": 5320, "after_st": 5180, "after_new_listing": 5090}],
|
||||
"degraded": ["st_not_point_in_time", "industry_snapshot"],
|
||||
"turnover_top_group": 0.31, "costs_round_trip": 0.0013
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 前端界面【设计】
|
||||
|
||||
按此前结论:**不新增顶层页面**,组件级落点。每个组件给出区块级线框与交互流:
|
||||
|
||||
1. **ResearchProfile 共享面板**(新组件,因子回测/挖掘/验证三视图共用):UniverseSpec + NeutralizationSpec 的受控表单,可存档命名(对应 `/api/factor-research/universes`);degraded 注记黄条;查询键含两 spec 哈希(queryKeys.ts 集中新增 `factorResearch` 键族)。
|
||||
线框:`[存档下拉 ▾] [另存为] | 折叠区1·宇宙(6 个过滤器开关/输入 + 漏斗摘要行) | 折叠区2·调整(基准/口径/方法/截尾) | [重置] [应用到当前视图]`;spec 哈希变化即触发查询键切换。
|
||||
2. **因子目录对话框**(因子回测 tab 内,仿 ResearchCandidatesDialog):分组树 + 搜索 + 公式/方向/预热/PIT 徽章/引用数;"研究此因子"按钮回填选择器。
|
||||
线框:`左侧分组树(带计数) | 右侧表格[因子/公式/方向/预热/PIT/引用] | 底部[研究此因子][加入复合候选]`;"加入复合候选"把因子暂存到复合编辑器的选择篮(跨组件轻状态,放 TanStack Query 缓存而非全局 store)。
|
||||
3. **IC 报告增强**:t 值列(NW 主口径,显著性星标)、三口径并列、单调性判定、IC 半衰期、宇宙漏斗、降级注记条。
|
||||
线框:IC 摘要卡新增 `t(NW)=2.31** 半衰期=4d 单调性=成立(ρ=0.9)` 一行;分层表头新增口径切换 tab(原始/超额/超额+中性化),切换不改数据只换列;宇宙漏斗为横向递减条形(5200→4980→4890,hover 显示过滤器名)。
|
||||
4. **自定义因子编辑器**(Settings 新面板"因子库",与信号库并列):公式输入 + 算子速查侧栏 + 实时校验 + 试算预览 + 版本列表 + 引用关系展示。
|
||||
线框:`左列: 版本列表(当前高亮)+元信息表单 | 中列: 公式输入框(等宽,校验错误行内红标+光标定位) + 算子速查(点击插入) | 右列: 试算预览表(最新5日×前20标的+非空率) [校验] [试算] [保存]`;保存按钮在校验+试算双绿前禁用。
|
||||
5. **复合因子编辑器**(同 Settings 面板内 tab):成员表(因子搜索、权重、方向)、自动权重开关与窗口、管线预览图;"试用于策略"向导生成 scoring 片段。
|
||||
线框:`上: 成员表[因子搜索器|方向|权重|剔除] + weighting 单选 + 窗口输入 | 中: 成员相关性热力图(§11-8, >0.8 对红标提示去重) + 管线预览(过滤→截尾→中性化→标准化→加权) | 下: [导出 scoring 片段] [试用于策略]`。
|
||||
6. **因子相关性探索器**(复合编辑器内嵌 + 因子回测 tab 的独立对话框):任选 2-10 个因子,展示区间内日均截面秩相关矩阵热力图。后端复用 `mining.py:446` `compute_rank_correlation`【现状】抽出的公共函数,不新建第二套计算。
|
||||
7. **挖掘工作台**:排行榜加 t/q 值列与 DSR;候选卡新增"导出为复合因子"。
|
||||
8. 全部新组件覆盖 加载/空/错误/禁用/无权限 五态(CONTRIBUTING 第 7 节)**并在 1280px 常用宽度与窄屏(≤768px)检查截断、遮挡、弹窗可操作性**;前端类型同步进 `lib/api.ts`;所有轮询/长任务按钮带进行中禁用态。
|
||||
|
||||
**查询键新增**(`queryKeys.ts` 集中定义,spec 哈希必须进键):
|
||||
|
||||
```text
|
||||
factorCatalog({assetType, group}) // 目录
|
||||
factorValidate() // mutation,无需键
|
||||
factorCustomList() / factorCustomPreview({formulaHash}) // CRUD / 试算
|
||||
factorIcReport({factorId, factorVersion, universeId, neuId,
|
||||
methodology, rangeHash, rebalance, nGroups})
|
||||
factorCompositeList() / factorCompositeEval({cmpSpecHash, matrixGeneration})
|
||||
factorCorrelation({factorIds[], rangeHash})
|
||||
researchUniverses()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 缓存与性能【设计】
|
||||
|
||||
| 缓存 | 键 | 失效 |
|
||||
| --- | --- | --- |
|
||||
| 因子 IC 报告 | `fr:ic:{factor_id}:{v}:{universe_id}:{neu_id}:{methodology}:{range_hash}:{rebalance}:{n_groups}` | enriched generation 变更或键任一分量变 |
|
||||
| 复合因子定义 | `fr:cmp:{id}:{spec_hash}` | 定义编辑 |
|
||||
| 复合分值(研究期) | `fr:cmpv:{cmp_spec_hash}:{matrix_generation}` | 矩阵重建 |
|
||||
| DSL 编译产物 | 进程内 LRU(表达式文本 → Expr) | 进程重启 |
|
||||
| 宇宙快照 | `fr:uni:{universe_id}:{matrix_generation}` | 矩阵重建 |
|
||||
|
||||
约束:复合/自定义因子**默认不物化进 enriched parquet**(避免用户定义污染核心管道与增量路径);只在研究/评分请求期计算并按上表缓存。`incremental_safe=False` 的因子盘中路径直接缺失而非降级计算(对齐 pipeline 增量路径现有行为)。中性化逐日截面计算全部 Polars 表达式化;统计层 O(N·G) 极小。禁止任何新增逻辑进入实时行情线程(CONTRIBUTING 6.3)。
|
||||
|
||||
**性能预算(实现验收线,超线必须先优化再合入)**:
|
||||
|
||||
| 操作 | 预算 | 基准场景 |
|
||||
| --- | --- | --- |
|
||||
| 单因子 IC 全报告(含三口径+t 值+分层) | ≤ 现有报告耗时 × 1.3 | 全 A 股 × 3 年日线(现有 `factor.py` 同窗基线,PR 里附前后数据,CONTRIBUTING 6.3) |
|
||||
| DSL 编译(含校验) | ≤ 5ms/表达式 | 深度 12、token 200 上限样例 |
|
||||
| 复合因子单期截面计算 | ≤ 成员因子独立计算耗时之和 × 1.2 | 10 成员 × 全 A 股 |
|
||||
| 宇宙过滤全期 | ≤ 全期 IC 计算的 10% | 同上基准 |
|
||||
| 因子目录接口 | ≤ 50ms | 全量 62+ 因子元数据 |
|
||||
| 相关性探索器 | ≤ 现有 mining 同规模秩相关耗时 × 1.1 | 10 因子 × 1 年 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 测试矩阵(最低要求清单)【设计】
|
||||
|
||||
| 模块 | 必测 |
|
||||
| --- | --- |
|
||||
| 注册表重构 | 特征化快照(62+ 因子两条计算路径逐位一致);重复 id/未增版本拒绝启动 |
|
||||
| DSL | 每算子黄金用例(含 null/除零/σ=0/全常数);负 shift 编译失败;深度/窗口/白名单越界拒绝;与手写 Polars 等价性;注入样例(`__import__`、列名穿越)拒绝 |
|
||||
| 宇宙 | 每过滤器独立单测(构造含 ST/停牌/涨停锁死/次新/微额的合成面板);漏斗计数;降级注记触发 |
|
||||
| 中性化 | 合成数据数值断言(demean/zscore/回归残差 vs statsmodels 对拍);行业缺组回退;基准停市日 |
|
||||
| 统计 | NW t:黄金参考向量(离线计算硬编码期望值)+ dev 组 statsmodels 对拍(可选);FDR:BH 已知 p 向量解析解;DSR:已构造解析例(对称正态收益 + 已知试验数);单调性边界(平梯/倒梯) |
|
||||
| 指标 | Sharpe/年化/超额/IR 已知序列解析解;243 口径回归 |
|
||||
| 复合因子 | 权重归一;**自动权重无未来函数**(t 日权重不随 t 日数据变化——篡改 t 日数据断言权重不变);四端同值断言(§8.3);环引用拒绝 |
|
||||
| API | 成功/空数据/编译错误/预热不足/无权限 |
|
||||
| 缓存 | 键覆盖测试(改 spec 必换键);generation 失效 |
|
||||
| 前端 | pnpm build + 五态检查 |
|
||||
|
||||
---
|
||||
|
||||
## 14. 实施路线图(PR 粒度,每 PR 独立可合)
|
||||
|
||||
| PR | 内容 | 依赖 | 主要文件 | 分级 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | stats 模块:IC t(NW)/单调性/半衰期 + IC 报告新字段 | 无 | 新 `app/factors/stats.py` + factor.py 增量 | L2 |
|
||||
| 2 | metrics 统一 + metrics_v2 版本化 | PR-1 | 新 metrics.py;engine/strategy/factor 改引用 | L3(热点,最小接线) |
|
||||
| 3 | 宇宙构建器 + 过滤器 + IC 接入 + 缓存键 | PR-1 | 新 universe.py + factor.py | L2 |
|
||||
| 4 | 基准超额 + 中性化 + 三口径并列 | PR-3 | 新 neutralize.py + factor.py | L2 |
|
||||
| 5 | FDR + DSR 进 optimizer/mining 排行榜与晋升门槛 | PR-1 | optimizer/mining 增量 | L3 |
|
||||
| 6 | 因子注册表重构(特征化测试先行) | 无(可与 1-5 并行) | 新 registry.py;factor.py/scoring.py/pipeline.py 收口 | L3 |
|
||||
| 7 | 复合因子 + 策略 scoring 桥 + 挖掘导出 | PR-6 | 新 composite.py;scoring.py 最小接线 | L2→L3 |
|
||||
| 8 | DSL 编译器 + 自定义因子 CRUD + Settings 因子库 UI | PR-6 | 新 dsl/ + api + settings 前端 | L2 |
|
||||
| 9 | 前端:ResearchProfile + 目录 + 报告增强 | PR-3/4 后端就绪 | 前端组件族 | 前端 |
|
||||
| 10+ | 数据契约:st_history / delisted / industry_pit / index_ext | provider 侧 | capabilities + 各 provider | L1+L2 |
|
||||
|
||||
每个 PR 按 CONTRIBUTING 第 10 节模板出描述(问题/根因/方案/兼容/性能/验证/界面证据/回滚)。
|
||||
|
||||
**配置白名单联动**:`backtest/candidates.py:28-49`【现状】的 `_CONFIG_FIELDS["factor"]` 是冻结字段集,PR-3/PR-4 必须同步扩展 `universe`、`neutralization` 两个配置字段(沿用 `_MINING_SOURCE_CONFIG_FIELDS` 的 frozenset 合并模式),否则保存候选会静默丢弃 spec——这是缓存一致性之外的第二个容易漏的接线点,测试须覆盖"保存→载入→spec 哈希不变"。
|
||||
|
||||
**各 PR 回滚要点**:PR-1/3/4/5 新增模块 + 增量字段,回滚 = revert 即可(旧缓存键不含新分量,自动回旧路径);PR-2 指标统一保留 `metrics_methodology` 开关,回滚 = 切回 v1 计算分支并保留数据;PR-6 注册表重构通过特征化测试保证行为等价,回滚 = revert(无持久化迁移);PR-7/8 用户数据(自定义因子/复合因子 JSON)为新增目录,回滚代码后文件残留但不再加载,重新部署即恢复——**不存在任何需要用户手动清数据的回滚**(CONTRIBUTING 第 12 节红线)。
|
||||
|
||||
**文档同步任务**(各 PR 内完成,不单开):PR-1/3/4 更新 `docs/features.md` 因子回测章节;PR-5 更新 `docs/mining.md` 门槛说明;PR-7 更新 `docs/strategy.md` 评分字段说明与 `操作说明书.md`;PR-8 更新 `docs/custom-data-source.md` 无关则跳过;本设计文档在每个 PR 合入后把对应条目从【设计】改标【已实现】。
|
||||
|
||||
---
|
||||
|
||||
## 15. 兼容性影响与风险清单
|
||||
|
||||
| 变更 | 功能影响 | 结果口径影响 | 缓解 |
|
||||
| --- | --- | --- | --- |
|
||||
| PR-1/5 统计字段 | 无 | 无(纯新增) | 字段默认值 |
|
||||
| PR-2 指标统一 | 无 | **Sharpe/年化数字变化(有意)** | metrics_v2 版本徽章;旧字段一版周期弃用 |
|
||||
| PR-3/4 宇宙/中性化 | 无 | IC/分层数字变化(有意,通常回落) | 默认开关显式;三口径并列;缓存键含 spec |
|
||||
| PR-6 注册表 | 无 | 要求逐位一致 | 特征化测试是合入硬门槛 |
|
||||
| PR-7/8 复合/DSL | 无(纯新增能力) | 无 | 注入列模式,custom_signals 先例 |
|
||||
| 数据契约 | 无 | 退市股回补后回测数字变化(修复) | 独立 dataset,无源时明确降级注记 |
|
||||
|
||||
剩余风险:~~① ts_rank 的 Polars 原生可用性需实现期确认~~【已解决:§17 验证 polars 1.40.1 `Expr.rolling_rank` 存在且行为正确】;② 行业快照回填历史的偏差在点时表到位前无法消除(注记透明化);③ 幸存者偏差的根本修复依赖数据源,代码侧已尽(注记 + 回补接口预留);④ ST 非点时在 st_history dataset 到位前仅能注记;⑤ 统计函数运行时零新增第三方依赖(后端当前无 scipy/statsmodels,已验证),NW/BH-FDR/DSR 以 numpy 手写实现,statsmodels 仅允许加入 uv dev 依赖组做测试对拍,不进运行时。
|
||||
|
||||
---
|
||||
|
||||
## 16. 明确不做清单(YAGNI 边界)
|
||||
|
||||
以下能力**刻意不在本设计范围内**,防止范围蔓延(依据 `docs/secondary-development.md` 第 10 节:不为未来可能出现的需求预埋框架)。出现真实需求时再按需立项:
|
||||
|
||||
| 不做项 | 理由 |
|
||||
| --- | --- |
|
||||
| 因子市场/分享/导入导出社区 | 单用户自托管定位,无真实需求 |
|
||||
| 全 Barra 风格回归(Beta/动量/流动性/非线性市值等十因子) | 数据与维护成本高;industry+size lite 已覆盖主要混杂,收益边际低 |
|
||||
| 自动机器学习/遗传规划因子搜索 | 与现有 beam search + 嵌套样本外定位重叠,且加剧多重检验问题 |
|
||||
| 港美股/加密资产因子 | 数据源与交易规则(T+0/无涨跌停)完全是另一套引擎 |
|
||||
| Tick 级/高频因子 | 分钟数据集能力有限,且与现有日线研究框架口径不同 |
|
||||
| 因子值的实时盘中推送(SSE 逐笔更新) | 违反实时热路径约束;监控经由策略结果缓存已覆盖时效需求 |
|
||||
| 复合因子权重在线学习/逐日再优化 | 自动权重窗口已是点时滚动;更细粒度会显著推高换手且引入过拟合面 |
|
||||
| ARIMA/VAR 预测、协整与配对交易 | 本平台定位是**横截面因子研究**;时间序列预测与统计套利是另一条业务线(指数择时/配对),数据、引擎与交易规则均不同,混入即范围蔓延 |
|
||||
| GARCH 全族 / 卡尔曼滤波 / 时变 Beta 状态空间模型 | EWMA 条件波动已覆盖日频主要价值;逐 symbol 递归拟合与全向量化管线冲突,机构级边际收益不抵维护成本(单 GARCH(1,1) 为 §2.4 的 experimental 按需项,不在冲突内) |
|
||||
| HMM/马尔可夫区制检测 | 现有情绪周期 6 阶段(启发式)+ 分环境 IC(factor.py:813-903【现状】)已覆盖区制条件分析;统计区制模型列为未来探索项不进本期 |
|
||||
| 独立权限体系(多用户/角色) | 应用级部署口令已满足自托管场景 |
|
||||
|
||||
---
|
||||
|
||||
## 17. 验证附录(本设计的验证记录)
|
||||
|
||||
> 验证日期 2026-09-04,基准 main@2ce8b4b1,后端 polars 1.40.1。分三部分:代码引用逐条核对、技术可行性实测、内部一致性检查。结论:**全部引用属实或已修正,可行性风险清零或已有替代方案,一致性检查通过**。
|
||||
|
||||
### 17.1 代码引用核对(【现状】条目逐条对账)
|
||||
|
||||
| 引用 | 核对内容 | 结果 |
|
||||
| --- | --- | --- |
|
||||
| factor.py:36-109 | FACTOR_COLUMNS 61 因子目录 | ✓(全文读取) |
|
||||
| factor.py:111/112/122 | warmup 120 / factor_v2 / n_groups=5 | ✓ |
|
||||
| factor.py:740-750 / 752-778 / 780-811 / 813-903 | Rank IC / 分年 / 衰减 / 分环境 | ✓(函数定义与实现均在引用区间) |
|
||||
| factor.py:1059-1067 / 1095-1098 | 双边佣金+印花税+滑点成本 / 每调仓期扣减 | ✓ |
|
||||
| factor.py:1123-1166 / 1001-1035 / 1294-1295 | 换手率 / tie-aware 分层 / executable_short=False | ✓ |
|
||||
| factor.py:1184-1186 / 1274-1277 | 年化系数匹配调仓频率的注释与实现 | ✓(注释原文核实) |
|
||||
| mining.py:26-31 / 91-100 / 1041-1073 / 1503-1530 / 446 | 晋升门槛 / purge30+embargo5 / trial 预算 / 折构造 / 秩相关 | ✓ |
|
||||
| mining.py:492-493 | `pl.corr(..., method="spearman")` 可用性 | ✓(代码在用,即证 API 存在) |
|
||||
| engine.py:2871-2874 / 2899-2903 / 2993-3030 / 3116-3122 | 365.25 年化 / 逐笔 Sharpe(含"非严格正确"注释)/ 仅卖出日聚合 / 净值口径 | ✓(四种口径全部原文核实) |
|
||||
| engine.py:51 / 54-94 / 903-933 | matching 默认 close_t / 成本模型 / 涨跌停与停牌成交闸 | ✓(全文精读) |
|
||||
| pipeline.py:970-984 | filter_halt_days(函数头 973) | ✓ |
|
||||
| pipeline.py:1795 | compute_enriched_today 盘中增量入口 | ✓ **(修正:初稿误引 1738+,该行实为复权因子读取;已改)** |
|
||||
| matrix.py:1476-1495 | _write_tradable_matrix | ✓ |
|
||||
| repository.py:1488 | get_index_daily | ✓ |
|
||||
| api/data.py:765 | listing_date 已暴露未用于研究 | ✓ |
|
||||
| fundamentals.py:107-115 | join_asof backward + date>_announce 严格公告日后 | ✓ |
|
||||
| share_capital.py:54-56 | announce_date 优先、period_end 兜底 | ✓ |
|
||||
| price_limits.py:87-101 | numpy_limit_pct_vectors(当前名推断) | ✓ |
|
||||
| capabilities.py:44-45 | 复权口径一致性"不做路由耦合"注释 | ✓ |
|
||||
| index_const.py:12-15 | 核心四只代码(000001.SH/399001.SZ/399006.SZ/000680.SH) | ✓ |
|
||||
| daily_pipeline.py:92-125 | _resolve_universe(CN_Equity_A 当前池) | ✓ |
|
||||
| kline_sync.py:358-367 | (symbol, trade_date) 去重 keep=last 原子合并 | ✓ |
|
||||
| scoring.py:13-51 / 53-66 / 91-104 / 108+ | VIRTUAL 依赖 35 项 / 预热表 / 依赖展开与预热推导 / scoring_value_expr | ✓(全文读取,附录A 由其逐字推导) |
|
||||
| monitor.py:1314-1322 | score_min/max 消费 result.scores | ✓ |
|
||||
| strategy/builtin/*.py | "scoring": {字段: 权重} 配置形态 | ✓(8 个内置策略抽样) |
|
||||
| candidates.py | factor/strategy 双形态候选配置字段 | ✓ |
|
||||
| services/heavy_job_limiter.py、backtest/numba_runtime.py、services/ext_presets.py、services/market_mainline.py:35 | 模块存在性 / 行业两级口径 | ✓ |
|
||||
|
||||
### 17.2 技术可行性实测(`uv run python` 于 backend 环境)
|
||||
|
||||
| 项 | 实测结果 | 设计影响 |
|
||||
| --- | --- | --- |
|
||||
| polars 版本 | 1.40.1(pyproject pin >=1.0) | — |
|
||||
| `Expr.rolling_rank` | **存在**,递增序列 4 点窗输出 [null,null,null,4,4,4] 行为正确 | §15 风险①**解除**,ts_rank 用原生实现 |
|
||||
| `Expr.ewm_std` / `ewm_var` | 存在 | f_ewma_vol 直接可实现 |
|
||||
| `pl.rolling_corr`(顶层)/ `pl.rolling_cov` | 存在且可算出正确相关值(3 点窗样例 0.6547) | ts_corr/ts_cov 编译目标为顶层函数 |
|
||||
| `pl.rolling_corr(...).over("symbol")` 分组组合 | **实测通过**:A/B 两组各自窗口内相关(0.6547 / −1.0),无串组 | ts_corr 多标的面板场景确认可行(初稿未验证,本轮补测) |
|
||||
| `Expr.rolling_corr` | **不存在**(仅顶层函数) | 算子表已按顶层函数修正 |
|
||||
| `Expr.rolling_arg_max` | **不存在**;rolling_map 为 Python 回调 | ts_argmax/ts_argmin v1 移除(§3.1 已注) |
|
||||
| rolling_quantile/var/mean/std/max/min/sum、shift、pct_change、diff、pow、sign、clip、log、cum_prod | 全部存在 | 其余算子无阻碍 |
|
||||
| `pl.corr` spearman | mining.py:492 在用 | 无阻碍 |
|
||||
| numba | 已安装(numba_runtime 真实存在) | GARCH/矩阵核扩展路径成立 |
|
||||
| statsmodels / scipy | **均未安装** | §6 零依赖原则:运行时 numpy 手写 NW/BH-FDR/DSR,statsmodels 仅可进 dev 依赖组 |
|
||||
|
||||
### 17.3 内部一致性检查
|
||||
|
||||
1. 缓存键 ↔ Spec 字段:UniverseSpec 7 字段、NeutralizationSpec 5 字段(含 winsorize_sigma 与管线顺序)全部进入各自 canonical_json/哈希 → 键覆盖完整(§4.2/§5.2 ↔ §12)。✓
|
||||
2. API ↔ 前端组件:catalog↔目录对话框、validate/preview/custom↔因子编辑器、ic↔报告增强、composite↔复合编辑器、universes↔ResearchProfile,无孤立端点或无后端组件。✓
|
||||
3. 测试矩阵 ↔ 模块:§13 十行覆盖 §2-§12 全部新增模块,无模块缺测试项。✓
|
||||
4. 路线图 ↔ 章节:PR-1↔§6.1、PR-2↔§7、PR-3↔§4、PR-4↔§5、PR-5↔§6.1(FDR/DSR)、PR-6↔§2、PR-7↔§8、PR-8↔§3、PR-9↔§11、PR-10↔§9,全部章节有落点 PR。✓
|
||||
5. §2.4 GARCH(1,1) experimental 与 §16"GARCH 全族不做"边界一致(单一按需项 vs 全族排除,§16 已加注)。✓
|
||||
6. EBNF ↔ 算子表:if_else/比较/逻辑均以函数调用或中缀产生式覆盖;优先级链 or<and<cmp<add<mul<unary 明确无歧义。✓
|
||||
7. 错误码目录 ↔ §3.2 校验规则:六条编译规则 + 三条运行时规则(E013/E015/E016)全部有码可映射。✓
|
||||
8. 附录A 计数:virtual 35 + base 非财务 19 + 财务 7 = 61,与 FACTOR_COLUMNS 行数一致;方向"待标定"仅用于振荡/环境依赖因子,不虚构。✓
|
||||
9. 三口径并列(§5.3)↔ API 响应 ic_by_basis 三键(§10)↔ 前端口径切换 tab(§11-3)三处一致。✓
|
||||
10. §16 不做清单 8 项与 §0 设计目标无冲突。✓
|
||||
+24
-1
@@ -52,7 +52,9 @@
|
||||
|
||||
**组合管理**:最大持仓数 · 敞口控制 · 等权 / 自定义仓位。
|
||||
|
||||
输出净值曲线 · 夏普 · 最大回撤 · 胜率 · 交易明细。SSE 流式进度支持切页重连,不会丢失回测任务。
|
||||
输出净值曲线 · 夏普 · 最大回撤 · 胜率 · 盈亏比(平均盈利单/平均亏损单,≥1 红 <1 绿) · 蒙卡回撤(中位/95% 边界,按成交顺序重抽样估计回撤分布)。SSE 流式进度支持切页重连,不会丢失回测任务。
|
||||
|
||||
**因子归因**:策略评分(scoring)引用了因子的回测,结果区提供「因子归因」tab——对比盈利单与亏损单在入场信号日的因子均值(胜单均值/败单均值/差值/样本数),判断每个因子在本轮交易中贡献了正筛选力还是在拖后腿。数据来自入场候选快照,零额外计算;因子名显示「英文 id + 中文标签」。覆盖 `meta.scoring` 非空的策略(内置与自定义 polars 策略),矩阵单因子排名类策略暂不产出归因。
|
||||
|
||||
**分钟策略回测**:分钟级策略逐交易日回放信号、以分钟收盘价入场,输出分钟级成交明细,与日线回测同一套 T+1 / 费用约束。
|
||||
|
||||
@@ -66,6 +68,27 @@
|
||||
|
||||
---
|
||||
|
||||
## 🔬 因子平台(Factors)
|
||||
|
||||
`/factors` 一级页,检验 / 因子库 / 编辑器 / 组合四个 tab,与策略线双向联动。设计与阶段规划见 [factor-platform-plan.md](./factor-platform-plan.md)。
|
||||
|
||||
**因子编辑器**:写 DSL 公式(如 `rank(-ts_sum(change_pct, 5))`)→ 校验语法(错误点击定位到字符)→ 试算 40 日 IC → 保存草稿。辅助输入:全部 25 个算子按「时序/截面/工具」分组点击插入(悬停显示签名与参数约束)、79 个可用字段双语 chip 点击插入、「从模板开始」下拉含经典模板与**我的因子**(导入已保存公式作为起点,非初始公式替换前有确认)。保存为草稿态,完整检验通过后在因子库激活。
|
||||
|
||||
**检验与组合**:服务端 IC/IR、Newey-West t 值、BH-FDR q 值、分层收益与多空对比;组合 tab 管理多因子权重。自定义/复合因子与策略评分共用同一条物化路径。
|
||||
|
||||
**因子 ↔ 策略联动**(四条桥):
|
||||
|
||||
| 方向 | 入口 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| 因子 → 策略 | 因子库「生成策略」 | 一键生成单因子排名策略(matrix_native,entry/exit 评分阈值 + top_rank 参数),保存为自定义策略可直接回测 |
|
||||
| 策略 → 因子 | 策略触发器编辑的 Zap 按钮 | 从因子快速创建条件信号(阈值给建议值),自动挂接到当前策略的入场/卖出触发器 |
|
||||
| AI → 因子 | 自定义信号的 AI 生成 | 提示词包含全部因子分组与 id,可直接写「因子·动量: momentum_20d > 0.8」类条件 |
|
||||
| 回测 → 因子 | 策略回测「因子归因」tab | 见上文回测引擎章节 |
|
||||
|
||||
**版本与生命周期**:公式修改保存为新版本并回草稿态(需重新检验);状态机 active / watch / retired / draft。
|
||||
|
||||
---
|
||||
|
||||
## 📡 监控中心(Monitor)
|
||||
|
||||
统一规则引擎,一个页面管理**四类监控**:
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
- 后端源码扩展注册:`backend/app/custom/<module>.py`,支持 FastAPI 路由、启动钩子和通知格式化器。
|
||||
- 当前前端插槽:`layout.navigation.extra`、`stock-preview.footer`、`watchlist.toolbar`。
|
||||
- 当前后端继承点:`NotificationFormatter`。
|
||||
- 因子平台与策略线的既有桥接(已实现,二开时直接复用、勿重复实现):因子库一键生成单因子排名策略(`GenerateFactorStrategyDialog` → `custom_factor_*`);策略触发器引用因子条件信号(`AddFactorSignalDialog`,`csg_f_*`);自定义信号 AI 提示词含因子分组(`custom_signals_ai.py`);策略回测因子归因(`strategy.py` 的 `factor_attribution`,覆盖 `meta.scoring` 非空的策略)。
|
||||
|
||||
尚未实现、只能在真实需求出现后增加的能力:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user