Files
quant-os/docs/AUDIT_2026-07-25.md
T

252 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-07-25 复评:为什么原文还不是“整体 60 分”
审计对象:`个人 A 股量化系统机构级 60 分 Baseline 技术选型设计:从聚宽研究到 QMT 实盘(2026-07-22`
结论先行:
| 刻度 | 分数 | 准确含义 |
| --- | ---: | --- |
| 设计覆盖度 | 约 70/100 | 原文已经讨论研究、数据、风险、成本、组合、执行、运维与合规的大部分责任边界 |
| 可实施规格度 | 49/100 | 只有约一半内容足够明确,工程师无需继续发明字段、默认值、失败语义和验收口径 |
| 原实现证据 | 6/100 | 截至 2026-07-22,主要证据是文章、架构图、外部资料与验收清单;没有可重放代码、数据、测试或平台运行记录 |
| 硬门 | 0/10 | G1—G10 都缺少完整验收证据;不是“部分通过” |
这三条分数不能相加,也不能互相替代。设计覆盖 70 不代表系统得 70;代码文件存在也不代表 Gate 通过。依据原文自己的定义,达到 Baseline 60 必须同时满足:
```text
证据评分 >= 60
且 G1 ... G10 全部通过
```
当前 Quant OS 代码交付是一个可执行、可测试的工程起点;`quant60` 是其第一版
A 股 Baseline 内核。它的新增得分必须在授权数据、真实平台运行、QMT 影子盘、
连续对账和券商合规确认完成后,逐项写回
[`gate_scorecard.json`](../gate_scorecard.json)。本报告不预支这些分数。
截至 2026-07-26 的实现事实快照:
- 截至 2026-07-26 的最新标准库全量测试为 `Ran 224 tests``OK (skipped=6)`
- JQData raw/factor/pre-close/limits/paused + 每日 PIT `is_st`/指数成员
已连接到 semantic snapshot verifier、`snapshot-backtest`
`snapshot-decision`,并拒绝把抓取当日未到 24:00 的日线标成完整收盘,
但尚未用用户授权账号跑真实快照;
- CPython 3.12 + `pyqlib==0.9.7` native momentum fixture 已真实运行并产生
24 条 signal
- Qlib Alpha158 + LightGBM + Recorder fixture smoke 已真实成功;两条
Qlib workflow 均保存表级 hash、边界和从 portfolio report 重算的指标,
runner 会为 local MLflow file store 显式设置
`MLFLOW_ALLOW_FILE_STORE=true`
- fixture 只有两只 synthetic 股票,任何性能数字都没有投资意义;
- JoinQuant/QMT mock parity exporter 已实现,但
`real_platform_pass=false` 且不计 G9
- 2026-07-26 已完成一次 2024 全年真实 JoinQuant hosted smoke,可见日志
无 ERROR;它证明 runtime/path,但尚无完整同输入导出和本地 strict peer
- XtTrader 已有严格只读、HMAC 账户绑定的 observation/snapshot/shadow
plan 与 verifier,但真实 QMT、broker callback/shadow 均未运行,
G1—G10 仍全部 `not_passed`
## 1. 审计方法
本次没有按“名词是否出现”评分,而是对原文每个责任域检查六类问题:
1. 是否明确输入、输出和时钟;
2. 是否给出机器可校验的数据契约;
3. 是否处理异常、恢复和幂等;
4. 是否给出确定默认值或明确标记待确认;
5. 是否有可运行实现和自动化测试;
6. 是否有来自目标平台、真实数据或券商的运行证据。
“设计覆盖度”主要看第 1 类及责任域是否齐全;“可实施规格度”要求前 4 类足够清楚;“实现证据”只认可第 5、6 类已保存的产物。外部文档说明某 API 有能力,不等于本系统已经正确调用它。
## 2. 原文做对了什么
原文比常见的“因子 + 回测净值”方案完整得多,70 分设计覆盖主要来自:
- 明确拆开 `Forecast → Target → Order → Fill`,避免预测、仓位和成交混为一谈;
- 意识到 PIT、历史指数成分、退市样本、公告可得时间和公司行动;
- 把风险、成本、换手和流动性放到事前组合决策,而不是只在绩效末尾扣滑点;
- 讨论部分成交、撤改单、乱序回报、重启、三账对账和 kill switch
- 把本地、聚宽、Qlib、QMT 定位为不同角色,而不是追求单一平台全包;
- 给出十道硬门,且明确“目标 66”只有在实现、模拟和券商确认后才成立;
- 对深模型、混合整数优化、逐笔队列与多券商容灾做了合理的 V1 边界控制。
因此问题不是方向错误,而是大量段落仍处于“正确的架构意图”,没有下降为可执行合同和证据。
## 3. 不足全景
| 责任域 | 原文已有 | 阻止整体 60 的主要缺口 | 最低补证 |
| --- | --- | --- | --- |
| 数据/PIT | 分层目录、`available_time`、数据版本思想 | 无真实字段字典、供应商字段到 canonical 的映射、公告时区/盘前盘后规则、历史修订样例、许可边界和 PIT 注入测试 | 固定数据快照、manifest、字段级时点规范、反未来函数测试 |
| Security Master / Universe | PIT 成分、开/持/卖/冻结状态 | 无状态 schema、生效区间合并优先级、代码变更与退市/重组样例;指数成分来源和发布日期语义未冻结 | 历史 fixture 与逐原因码重放 |
| 特征/标签/验证 | LightGBM + Ridge、purge/embargo、walk-forward | 无特征公式和缺失值策略的完整注册表;标签价格与可执行时钟未机器化;无 purge 算法、切分日历和未触碰测试集 | 训练配置、切分测试、冻结 OOS 报告 |
| 风险 | 透明行业/风格风险模型与收缩思想 | 因子定义、估计窗、缺失暴露、协方差清洗、特异风险下限、行业映射版本和压力场景均未定 | 风险快照 schema、数值稳定性测试、压力报告 |
| 成本/容量 | 日期化费用、价差与冲击公式 | 参数没有账户交割单或真实成交校准;最低佣金的非凸性如何进入候选/rounding 未实现;机会成本缺失 | 费率版本、逐笔 TCA、low/base/high 校准 |
| 组合 | 连续优化 + rounding/repair 方向正确 | 目标函数量纲、风险厌恶系数、约束优先级、不可行诊断、冻结仓位与现金联立细节未形成求解器规格 | 可解释优化输出、不可行/rounding 回归测试 |
| 本地回测 | 要求与执行器同源 | 没有事件时钟、公司行动、现金结算、价格基准、成交量切片和可重放输出合同 | 小型确定性 fixture、黄金输出、费用/T+1/涨跌停测试 |
| 聚宽 | 定位为第二引擎 | 无可上传入口、参数表、调度回调、`avoid_future_data` 设置、结果导出和平台限制清单 | 上传包、平台回测记录、canonical 差异报告 |
| QMT 内置 Python | 只笼统写 QMT/XtQuant | 没有处理内置 Python 3.6 与本地现代环境的边界;未说明 GUI 启动方式、回调入口和产物导出 | Python 3.6 薄核心、QMT 内置回测记录 |
| qmttools | 未与其他 QMT 路径清楚分层 | 原文没有说明 `run_strategy_file` 是原生 Python 驱动 QMT 策略文件的独立运行形态,不能等同于 XtTrader OMS | 独立入口、版本/参数记录、回测结果导出 |
| XtTrader | 异步 OMS 方向正确 | 无券商状态码映射、session/account 配置、连接恢复、查询与回调竞态、撤单确认、未知单处置 | 脱敏回调回放、重启/乱序/重复测试、20 日影子盘 |
| Qlib | 工作流选型合理 | “接 Qlib”与“由 Qlib 完成本系统回测”边界模糊;无 provider URI、dataset handler、workflow YAML、canonical 转换和 Records 产物 | Python 3.12 锁定环境、固定数据、Qlib smoke/OOS 报告 |
| 跨引擎 | 提出双引擎一致性 | 未定义到底比较输入、信号、目标、订单还是净值;没有容差、舍入、交易日历和差异原因码 | 见 [`CROSS_ENGINE_CONSISTENCY.md`](CROSS_ENGINE_CONSISTENCY.md) 的逐层合同 |
| Schema/版本迁移 | 列出九类对象 | 原文只有字段清单,不是 JSON Schema;无 `schema_version`、兼容策略、正反例与验证命令 | 版本化 schema、fixture 和 CI 验证 |
| 可复现 | Git/config/data/model/rules 版本思想 | 无 run manifest 格式、环境锁、随机源清单、原子发布、干净机复跑或 hash 容差 | 可保存 manifest、两次重放差异 |
| 运维/安全 | 提到单机、告警、runbook | 无部署命令、目录权限、凭据注入、日志脱敏、磁盘满/时钟偏差/重复调度/损坏账本处置 | [`SECURITY.md`](SECURITY.md) 与故障演练记录 |
| 合规 | 正确设为硬门 | 只链接通用法规;没有实际券商对账户、软件、频率、报告状态的确认 | 券商书面确认索引与“先报告、后交易”记录 |
## 4. 可实施规格为什么只有 49
原文的伪代码和目录树提供了良好边界,但工程实现仍需自行决定很多会改变结果的事实。例如:
- `AlphaForecast` 写了“分数/预期超额”,却没有决定两者是否都必填、单位是什么、是否允许空值;
- “按公告可得时间进入”没有定义 15:00 后公告应进入哪个交易日、时区和供应商延迟如何处理;
- “Guarded TWAP/POV”没有冻结子单切片、限价、超时、撤单确认和窗口结束残单政策;
- “差异在数值容差内”没有给各层容差,也没有区分浮点误差与平台成交机制差异;
- “QMT adapter”没有区分 QMT 内置 Python、`qmttools.run_strategy_file` 与外部 `XtTrader` 三条生命周期不同的路径;
- “Qlib workflow”没有给 provider、handler、dataset、model、record 和数据转换的可运行组合;
- “三本账”没有定义事件唯一键、追加一致性、崩溃点、重复回调去重和 broker snapshot 的事实优先级。
当实现者必须自行补完这些选择时,文章仍是设计说明,不是完整实施规格。新仓库中的四个 JSON Schema 只是把一小部分边界机器化,不能倒推原文在 7 月 22 日已经拥有这些证据。
## 5. 原实现证据为什么只有 6
6 分不是对文章质量的否定,而是严格区分“说明”和“运行事实”。原文当时可认可的证据主要是:
- 一份结构完整、明确承认条件性的设计文章;
- 一张架构图和可编辑源图;
- 外部官方资料链接;
- 评分公式、Gate 定义、阶段计划与手工验收清单。
缺失的是:
- 可安装仓库与固定 Python 环境;
- 可运行本地回测和确定性 fixture;
- 聚宽/QMT/Qlib 平台入口;
- 自动化测试、schema 验证、run manifest
- 数据质量、PIT、walk-forward、风险、成本和 TCA 报告;
- 连续模拟、QMT 影子盘、故障演练和券商确认。
因此原实现证据只能落在“文档/原型前期”,不能用文章中的目标分 66 回填。
## 6. Quant OS 当前实现的正确能力边界
当前已经不只是目录骨架,以下链路可运行、可测试、可扩展:
- 本地 deterministic event backtest、ledger hash chain、manifest verifier
- A 股 canonical symbol、T+1 可卖量、百股单位、费用、停牌/涨跌停、
部分成交和未成交撤销;
- PIT table、Universe 状态/原因码、透明特征、train-only 预处理、
purge/embargo walk-forward、deterministic Ridge、IC/RankIC
- 日期化费用、impact/capacity、因子风险、约束 target 与失败诊断;
- JQData adapter 使用 `get_bars` 获取 raw OHLCV/money、factor、
`pre_close``high_limit``low_limit``paused`,使用 `get_extras`
获取 PIT `is_st`,并逐交易日获取历史指数成员;
- canonical snapshot 采用 incomplete marker + exact artifact hash
verifier 会重建领域对象、重算质量汇总和 `data_version`
- verified snapshot 可直接进入历史 `snapshot-backtest` 或单日
`snapshot-decision`,后者可接完整 canonical broker snapshot 并
fail closed
- 聚宽/QMT 单文件 bundle、fake harness 与 mock parity exporter
- Qlib 0.9.7 native momentum CLI,以及 Alpha158/LightGBM/Recorder CLI
- QMT/XtTrader 严格只读 shadow CLI:账户 HMAC、broker observation、
T 日 Signal 与 T+1 broker fact 绑定、完整 artifact verifier
- Colab 默认无账号流程,以及显式可选的 JQData/Qlib cell。
最近一次 224-test suite、两条 Qlib fixture smoke 和 Tushare
managed-provider 技术 smoke 证明上述代码路径能执行,
但仍明确不证明:
- synthetic 数据或两股票 Qlib fixture 的收益具有投资价值;
- 当前真实 JQData 账户的数据许可、PIT、质量与保存策略已验收;
- 基本面公告、完整公司行动、Security Master 和退市链已生产化;
- Ridge 或 LightGBM 已冻结为四引擎共同部署模型;
- 聚宽与本地已在真实长样本上对齐;
- QMT 客户端、`xtquant`、数据/账户权限或真实订单回调已经可用;
- QMT 历史 ST 正例、官方 query API 的失败/空列表合同已在真实账号验证;
- OMS 已达到断线恢复、未知单、持续对账和 20 日影子要求;
- 程序化交易报告已经完成。
## 7. 平台事实应分开写
截至本次复评所依据的官方文档和本地运行记录:
- QMT 内置策略环境仍说明为 Python 3.6;它适合运行兼容的薄策略文件,不应直接加载要求 Python ≥3.10 的完整 `quant60` 包;
- XtQuant 原生 Python 文档列出 3.6—3.12 的库,并要求运行前启动 MiniQMT;实际可用版本取决于券商分发;
- qmttools 的 `run_strategy_file` 是从原生 Python 驱动策略回测/运行的路径,应与 XtTrader 的查询、报单、撤单和推送生命周期分开;
- `pyqlib==0.9.7` 已有 CPython 3.12 的主流平台 wheel;本项目已经在该组合
实际跑通 native momentum fixture24 signals)和
Alpha158/LightGBM/Recorder fixture
- Alpha158 runner 使用 `os.environ.setdefault` 自动确认
`MLFLOW_ALLOW_FILE_STORE=true`,以兼容本地 MLflow file store
该确认只影响本地 tracking backend,不构成数据许可或模型有效性证明。
来源:[QMT 内置 Python 快速开始](https://dict.thinktrader.net/innerApi/start_now.html)、[XtQuant 原生 Python 快速开始](https://dict.thinktrader.net/nativeApi/start_now.html)、[原生 Python/qmttools 策略回测](https://dict.thinktrader.net/videos/touyan/ty_native_python.html)、[pyqlib 0.9.7 包与 wheel](https://pypi.org/project/pyqlib/)。
Qlib 的运行状态现在可以写成“固定运行时已通过 fixture smoke”,不能写成
“真实 A 股研究已验收”或“与 portable target/order 一致”。两股票 synthetic
fixture 的 model/portfolio 指标没有投资意义;真实 provider、冻结 OOS 和
版本化 Recorder artifact 仍要单独补证。
对应的最小验证入口:
```bash
python3.12 -m venv .venv-qlib312
source .venv-qlib312/bin/activate
python -m pip install -r requirements/research-py312.txt
python tools/build_qlib_tiny_fixture.py artifacts/qlib-fixture --days 80
PYTHONPATH=src:. python -m platforms.qlib_runner \
--provider-uri artifacts/qlib-fixture \
--market csi300 --benchmark SH000300 \
--start 2024-02-01 --end 2024-04-19 \
--lookback 20 --topk 1 --n-drop 1 --rebalance weekly \
--output-json artifacts/qlib-native/result.json
```
真实 JQData 验证则必须由用户授权登录:
```bash
PYTHONPATH=src:. python tools/jqdata_snapshot.py \
--index 000905.XSHG --start 2024-01-02 --end 2024-12-31 \
--output data/snapshots/csi500-2024
PYTHONPATH=src:. python -c \
"from quant60.data_snapshot import verify_data_snapshot; verify_data_snapshot('data/snapshots/csi500-2024/manifest.json')"
PYTHONPATH=src:. python -m quant60 snapshot-backtest \
--snapshot data/snapshots/csi500-2024/manifest.json \
--config configs/baseline.json --output artifacts/csi500-2024-backtest
```
## 8. 证据更新纪律
更新 [`gate_scorecard.json`](../gate_scorecard.json) 时必须遵守:
1. 先保存不可变证据,再改状态;
2. 每条 evidence 至少包含路径、生成命令、时间、代码版本、配置 hash 和数据版本;
3. `mock`、合成 fixture 和单元测试可证明代码语义,不能证明真实数据、券商或合规;
4. Gate 只有 `passed``not_passed` 两种对外结论;“完成 80%”仍是未通过;
5. G9 需要真实跨引擎产物,G6/G8 需要券商认证环境或脱敏真实回调,G10 只能由实际券商材料证明;
6. 任何代码变更使旧证据超出适用版本时,必须重新降为 `not_passed`
7. 总分达到 60 但任一 Gate 未通过,系统仍必须标记 `NOT_BASELINE_60`
## 9. 下一条最短路径
按错误成本排序,而不是按模型新颖度排序:
1. 用户授权登录 JQData,生成不可变真实 snapshot,保存许可/lineage,并通过
semantic verifier、`snapshot-backtest``snapshot-decision`
2. 基于已完成的聚宽 hosted smoke,冻结一个小型 canonical 平台输入并导出
close arrays/hash、plan、orders/fills
3. 用本地与聚宽对相同输入逐层比较 L1—L4,而不是先比较净值;mock parity
只作为回归辅助;
4. 把基本面公告 available-time、完整公司行动/Security Master 加入真实
PIT data contract
5. 在授权真实 provider 上运行 Qlib OOS,并把 Ridge/LightGBM 中选定的候选
冻结成 versioned model bundle;现有 synthetic Recorder 不能承担这一步;
6. 取得实际券商 QMT client、`xtquant`、数据与账户权限,分别验证 built-in、
qmttools 与 XtTrader
7. 完成 60 个交易日模拟、20 个交易日 QMT shadow、故障演练、连续零未知账差;
8. 由实际券商确认程序化交易报告、接口权限、频率和软件要求,再讨论小资金
canary。
在第 7、8 步完成前,正确表述是“可执行、可继续填证据的 Quant OS 生产候选
框架”,不是“已达到机构级 60 分”。