feat: preserve Quant OS target-package vertical slice

This commit is contained in:
2026-07-30 21:27:44 +08:00
parent 00efeb7ec2
commit 919c64c679
42 changed files with 15454 additions and 404 deletions
+23 -9
View File
@@ -5,9 +5,8 @@ Quant OS 是项目名;`quant60` 是第一版 A 股 Baseline 内核、CLI names
可无人值守重放、聚宽登录后可直接回测、QMT 授权环境就绪后无需改源码即可完成 可无人值守重放、聚宽登录后可直接回测、QMT 授权环境就绪后无需改源码即可完成
回测/影子验证,并且所有 G1—G10 都由版本绑定的真实证据支持。 回测/影子验证,并且所有 G1—G10 都由版本绑定的真实证据支持。
当前结论仍为 `NOT_BASELINE_60`最近一次标准库全量测试记录为 当前结论仍为 `NOT_BASELINE_60`。标准库全量测试证明代码合同,不证明真实
`Ran 224 tests``OK (skipped=6)`;六个 skip 是可选依赖/运行时路径 平台、券商或合规 Gate;测试总数随合同补充会变化,不作为 Gate 分数
该结果证明代码合同,不证明真实平台、券商或合规 Gate。
## Stage 1: 独立仓库与确定性执行 ## Stage 1: 独立仓库与确定性执行
@@ -36,7 +35,14 @@ Quant OS 是项目名;`quant60` 是第一版 A 股 Baseline 内核、CLI names
- openable / hold-only / sell-only / frozen / excluded 状态与原因码; - openable / hold-only / sell-only / frozen / excluded 状态与原因码;
- 透明价量特征、可执行时钟标签、train-only 预处理; - 透明价量特征、可执行时钟标签、train-only 预处理;
- purged walk-forward、deterministic Ridge challenger、IC/RankIC 报告; - purged walk-forward、deterministic Ridge challenger、IC/RankIC 报告;
- 日期化费用、low/base/high 冲击、透明因子风险、约束优化与失败诊断; - Ridge train-only 预处理、系数、标签、训练截止日与 lineage 的冻结 model
bundle
- 一条真实可达的 synthetic vertical sliceUniverse → Ridge Alpha →
60 日对角风险与日期化成本参与的 deterministic Portfolio → post-risk →
reference Execution,并输出五层 hash trace
- 无账户股数的 `TargetPackageV1`,绑定 model/data/feature/risk/cost/
optimizer/config/source hash 与 T-close/T+1 双时钟;
- 独立组件仍包括透明因子风险、CVXPY optimizer 与 guarded TWAP/POV
- JQData adapter 通过 `get_bars` 获取 raw OHLCV/money、factor、`pre_close` - JQData adapter 通过 `get_bars` 获取 raw OHLCV/money、factor、`pre_close`
每日涨跌停/停牌字段,通过 `get_extras` 获取 PIT `is_st`,并逐交易日 每日涨跌停/停牌字段,通过 `get_extras` 获取 PIT `is_st`,并逐交易日
获取 PIT 指数成员;区间 `get_trade_days` 必须与 `get_all_trade_days` 获取 PIT 指数成员;区间 `get_trade_days` 必须与 `get_all_trade_days`
@@ -48,16 +54,21 @@ Quant OS 是项目名;`quant60` 是第一版 A 股 Baseline 内核、CLI names
fail-closed 账户状态输入; fail-closed 账户状态输入;
- synthetic research artifacts 和 snapshot decision artifacts 都有精确 - synthetic research artifacts 和 snapshot decision artifacts 都有精确
manifest verifier。 manifest verifier。
- 每层部署状态由 reachability contract 分开记录 implemented、unit tested、
local/JQ/QMT entrypoint reachable 与 real-platform observed。
**仍缺** **仍缺**
- 授权 JQData 账户上的真实快照、长样本质量报告和许可记录; - 授权 JQData 账户上的真实快照、长样本质量报告和许可记录;
- 完整 Security Master、基本面公告时点、公司行动与退市链; - 完整 Security Master、基本面公告时点、公司行动与退市链;
- 真实样本风险/冲击/容量校准; - 真实样本风险/冲击/容量校准;
- Ridge 的冻结 model bundle、真实 OOS 报告与跨引擎推理验证。 - 授权真实数据训练的 model bundle、真实 OOS 报告与跨引擎 TargetPackage
执行验证;
- 把 factor risk/CVXPY/TWAP-POV 从组件级测试提升到经过取舍的候选主链。
**状态**功能实现和合成/fake-provider 合同验证完成,生产证据未完成。 **状态**synthetic 五层 vertical slice 与交换合同完成;provider-data
Ridge 仍是 research challenger,不是已部署模型。 Baseline candidate、真实校准和生产证据未完成。Ridge 仍是 research
challenger,不是已部署模型。
## Stage 3: 平台回测与跨引擎证据 ## Stage 3: 平台回测与跨引擎证据
@@ -68,6 +79,9 @@ Ridge 仍是 research challenger,不是已部署模型。
- 本地 deterministic execution/research 与 manifest 重放; - 本地 deterministic execution/research 与 manifest 重放;
- JoinQuant/QMT fake wrapper 合同和 mock parity exporter - JoinQuant/QMT fake wrapper 合同和 mock parity exporter
- JoinQuant/QMT 的 TargetPackage 消费模式:本地上游四层保持唯一权威,
平台只做严格包验证、双时钟命中、账户/价格绑定与 execution;不存在
momentum fallback
- 2026-07-26 使用生成 bundle 完成 2024 全年真实 JoinQuant hosted - 2026-07-26 使用生成 bundle 完成 2024 全年真实 JoinQuant hosted
backtest,可见完成日志无 ERROR;该运行还促成 hosted namespace、 backtest,可见完成日志无 ERROR;该运行还促成 hosted namespace、
科创板保护价与 200 股最小申报的真实修复; 科创板保护价与 200 股最小申报的真实修复;
@@ -86,8 +100,8 @@ Qlib 两次 smoke 使用的是确定性两股票 synthetic fixture,性能没
**仍缺** **仍缺**
- 聚宽完整输入/plan/orders/fills 导出和相同输入本地 peer - 同一个 TargetPackage 的聚宽完整 package/plan/orders/fills 导出和本地 peer
- 授权 QMT built-in 与 qmttools 回测导出; - 同一个 TargetPackage 的授权 QMT built-in 与 qmttools 回测导出;
- 至少 20 个交易日的真实 QMT/broker shadow - 至少 20 个交易日的真实 QMT/broker shadow
- 同一 canonical 输入的 L1—L5 差异报告。 - 同一 canonical 输入的 L1—L5 差异报告。
+91 -23
View File
@@ -7,9 +7,11 @@ Quant OS 是项目名;`quant60` 是第一版 A 股 Baseline 的 Python
本地与 Colab 可零账号运行工程 smoke;JQData 登录后可生成不可变快照并直接 本地与 Colab 可零账号运行工程 smoke;JQData 登录后可生成不可变快照并直接
运行 PIT 本地回测/目标决策;聚宽、QMT 与 Qlib 有各自明确的运行入口。 运行 PIT 本地回测/目标决策;聚宽、QMT 与 Qlib 有各自明确的运行入口。
> 当前状态仍是 `NOT_BASELINE_60`。代码存在、单测、synthetic、fake harness > 当前状态仍是 `NOT_BASELINE_60`。本地五层 vertical slice、单测
> Qlib fixture 和单次真实聚宽 hosted smoke都不能代替同输入跨引擎对比、 > synthetic、fake harness、Qlib fixture真实聚宽 hosted 动量 smoke以及
> 真实 QMT、券商影子盘、连续对账与合规证据。 > 单个 synthetic-research TargetPackage 的真实聚宽 **execution smoke**
> 都不能代替授权真实数据 OOS、逐层同输入 parity、真实 QMT、券商影子盘、
> 连续对账与合规证据。
> 原文章复评为设计覆盖约 70/100、可实施规格 49/100、原实现证据 6/100、 > 原文章复评为设计覆盖约 70/100、可实施规格 49/100、原实现证据 6/100、
> G1—G10 为 0/10。详见 > G1—G10 为 0/10。详见
> [`docs/AUDIT_2026-07-25.md`](docs/AUDIT_2026-07-25.md) 与 > [`docs/AUDIT_2026-07-25.md`](docs/AUDIT_2026-07-25.md) 与
@@ -40,18 +42,21 @@ python3 tools/build_public_status.py --check
| 路径 | 当前能做什么 | 已有证据 | 仍缺什么 | | 路径 | 当前能做什么 | 已有证据 | 仍缺什么 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 本地 synthetic execution | 周度信号、T+1 开盘撮合、费用、涨跌停、部分成交、ledger、重放 | 标准库全量测试、确定性 smoke、manifest verifier | 不代表真实市场或投资价值 | | 本地 synthetic execution | 周度信号、T+1 开盘撮合、费用、涨跌停、部分成交、ledger、重放 | 标准库全量测试、确定性 smoke、manifest verifier | 不代表真实市场或投资价值 |
| 本地 synthetic research | PIT/Universe、透明特征、purged walk-forward、Ridge、成本/风险、约束 target | research artifact 与 verifier | 仍是 syntheticRidge 尚未冻结为跨平台生产模型 | | 本地 synthetic research | Universe → Ridge Alpha → 风险/成本参与的 Portfolio → post-risk → reference execution 五层链 | 冻结 model bundle、无股数 TargetPackage、逐层 hash trace 与 manifest verifier | 仍是 synthetic;当前风险是 60 日对角方差,尚无真实样本校准或投资价值 |
| JQData → 本地 | 原始价、复权因子、前收、涨跌停、停牌、PIT ST 状态与历史指数成员快照;PIT 本地回测与单日目标 | fake-provider 端到端回归测试、完整收盘日 fail-closed 合同 | 需要用户授权登录后保存真实数据运行;基本面/公司行动尚未全接 | | JQData → 本地 | 原始价、复权因子、前收、涨跌停、停牌、PIT ST 状态与历史指数成员快照;PIT 本地回测与单日目标 | fake-provider 端到端回归测试、完整收盘日 fail-closed 合同 | 需要用户授权登录后保存真实数据运行;基本面/公司行动尚未全接 |
| Colab | 自动 clone、测试、bundle、两套 synthetic 流程;可选 JQData/Qlib | notebook 本地逐 cell 执行 | JQData/Qlib 可选 cell 需对应账号/运行时 | | Colab | 自动 clone、测试、bundle、两套 synthetic 流程;可选 JQData/Qlib | notebook 本地逐 cell 执行 | JQData/Qlib 可选 cell 需对应账号/运行时 |
| 聚宽 hosted | 可上传的 Python 单文件,直接回测 portable baseline | 2024 全年、100 万资金的真实 hosted run 已完成且可见日志无 ERROR;另有 bundle/fake/mock 合约 | 仍需完整导出、相同输入本地 peer 逐层差异报告 | | 聚宽 hosted | 默认动量 smoke;显式注入 TargetPackage 后只消费本地 post-risk 权重并绑定平台账户/价格 | 动量模式已有一次 2024 全年 run;一个 synthetic-research TargetPackage 已真实命中、绑定 `day_open` 并产生 3 笔委托/成交 | 只观察 execution consumer;无授权真实长样本/OOS、上游四层 hosted 执行、QMT peer 逐层真实数据 parity |
| QMT built-in/qmttools | 可上传/驱动的硬 backtest-only portable baseline;固定中证 500 benchmark、10% 参与率、PIT 成分/ST | Python 3.6 语法、fake/qmttools 合约 | 需要已授权 QMT 客户端、历史 ST 数据权限/正例探针与实跑导出 | | QMT built-in/qmttools | 默认动量 smokeTargetPackage 模式为 Python 3.6 backtest-only 消费器 | Python 3.6 语法、TargetPackage/fake/qmttools 合约 | 需要已授权 QMT 客户端、历史数据权限与 TargetPackage 实跑导出 |
| Qlib 0.9.7 | native momentum backtestAlpha158/LightGBM CLI/Recorder 工作流 | Python 3.12 两条 fixture 已真实跑通,并保存表级 hash、行列/时间边界和重算指标 | synthetic 两股票没有投资价值;无真实 OOS、L3/L4 parity | | Qlib 0.9.7 | native momentum backtestAlpha158/LightGBM CLI/Recorder 工作流 | Python 3.12 两条 fixture 已真实跑通,并保存表级 hash、行列/时间边界和重算指标 | synthetic 两股票没有投资价值;无真实 OOS、L3/L4 parity |
| Tushare → Qlib | 只读盘点 completed Parquet、校验 checksum、构建/验证不可变 provider、运行本地回测 | 93 个 completed 文件已验证;早期未复权 provider 与两次 byte-identical momentum run | 下载仅到 1993-10;缺复权、真实 benchmark、历史成分/ST/停牌/涨跌停,不能进入生产 decision | | Tushare → Qlib | 只读盘点 completed Parquet、校验 checksum、构建/验证不可变 provider、运行本地回测 | 93 个 completed 文件已验证;早期未复权 provider 与两次 byte-identical momentum run | 下载仅到 1993-10;缺复权、真实 benchmark、历史成分/ST/停牌/涨跌停,不能进入生产 decision |
| XtTrader shadow | 只读账户查询、HMAC 账户绑定、broker observation/snapshot、零下单 target-diff 计划与完整 verifier | fake broker/QMT 合约;所有 broker mutation 均不可达 | 需要授权 QMT 环境的账户合同探针、20 日影子、恢复/对账和券商确认 | | XtTrader shadow | 只读账户查询、HMAC 账户绑定、broker observation/snapshot、零下单 target-diff 计划与完整 verifier | fake broker/QMT 合约;所有 broker mutation 均不可达 | 需要授权 QMT 环境的账户合同探针、20 日影子、恢复/对账和券商确认 |
跨四个引擎当前共同的、可冻结生产基线 `portable-momentum-v1` 只是当前真实跑过聚宽的跨引擎连通性 smoke,不
`portable-momentum-v1`。本地 Ridge 与 Qlib Alpha158/LightGBM 是研究候选 60 分 Baseline。Baseline 的权威决策留在本地:冻结 Ridge model bundle
在出现版本化 model bundle、真实 OOS 证据和跨引擎推理验证前,不会冒充已经部署。 完成五层链并发布无股数的 `TargetPackageV1`;聚宽/QMT 只做精确双时钟查找、
账户状态绑定和执行。当前 synthetic 包已经真实通过聚宽 execution consumer
但这只证明平台边界可运行;尚未证明上游四层的真实数据有效性、QMT peer 或
跨引擎一致性。
## 一条命令验证本地工程 ## 一条命令验证本地工程
@@ -90,6 +95,9 @@ artifacts/
│ └── report.json │ └── report.json
├── research-smoke/ ├── research-smoke/
│ ├── manifest.json │ ├── manifest.json
│ ├── model_bundle.json
│ ├── target_package.json
│ ├── pipeline.json
│ ├── signals.json │ ├── signals.json
│ ├── targets.json │ ├── targets.json
│ ├── forecasts.json │ ├── forecasts.json
@@ -175,6 +183,11 @@ provider calendar 的唯一下一交易日 Asia/Shanghai `[09:00,09:30)`;它
它仍是本地 fill model,不是聚宽/QMT 的真实成交证据。 它仍是本地 fill model,不是聚宽/QMT 的真实成交证据。
注意:现有 `snapshot-decision` 仍是 `portable-momentum-v1` 的 provider-data
路径,不能因为输入来自 JQData 就冒充 Ridge Baseline。完整候选链的权威交换物
`TargetPackageV1`;在真实数据训练、风险/成本校准与 OOS 冻结完成前,
`research-smoke/target_package.json` 只用于验证部署结构。
## Colab ## Colab
打开 [`notebooks/quant_os_colab.ipynb`](notebooks/quant_os_colab.ipynb) 打开 [`notebooks/quant_os_colab.ipynb`](notebooks/quant_os_colab.ipynb)
@@ -197,7 +210,7 @@ python3 tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb --execute
## 聚宽:直接上传回测 ## 聚宽:直接上传回测
先构建 不传目标包时,构建的是历史兼容的动量 smoke
```bash ```bash
python3 tools/bundle_platforms.py python3 tools/bundle_platforms.py
@@ -221,7 +234,7 @@ canonical 周时钟不是固定星期几:某 ISO 周第一实际交易日收
作为限价保护;共享核心拒绝不足 200 股的普通科创板申报,只保留一次性卖清 作为限价保护;共享核心拒绝不足 200 股的普通科创板申报,只保留一次性卖清
不足 200 股余额的交易所例外。 不足 200 股余额的交易所例外。
2026-07-26 已用生成 bundle 完成一次真实聚宽全年 hosted backtest。运行记录、 2026-07-26 已用该动量 smoke bundle 完成一次真实聚宽全年 hosted backtest。运行记录、
bundle hash、指标、真实平台发现的问题和证据边界见 bundle hash、指标、真实平台发现的问题和证据边界见
[`docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md`](docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md)。 [`docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md`](docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md)。
该次运行的可见完成日志没有 `ERROR`,但尚未导出相同输入、plan、orders/fills 该次运行的可见完成日志没有 `ERROR`,但尚未导出相同输入、plan、orders/fills
@@ -238,13 +251,52 @@ bundle hash、指标、真实平台发现的问题和证据边界见
单次 hosted smoke 是外部运行证据,但没有同输入跨引擎 manifest 时仍不算 单次 hosted smoke 是外部运行证据,但没有同输入跨引擎 manifest 时仍不算
真实聚宽 Gate 通过。 真实聚宽 Gate 通过。
要验证五层候选链,必须显式传入已验真的 TargetPackage;打包器不会自动从
动量回退或把 smoke 提升成 Baseline
```bash
python3 tools/bundle_platforms.py \
--target-package artifacts/research-smoke/target_package.json \
--output-dir dist/target-package
```
这个示例包来自 synthetic 数据,只能验证线路。目标包保存 post-risk **权重**
及 model/data/feature/risk/cost/optimizer/config/source hash,不保存账户股数。
聚宽在包声明的 `next_session` 开盘读取真实 equity、持仓、可卖数量和价格,
形成 execution plan;包缺失、双时钟不匹配、hash 篡改或重复决策都会停止,
不会现场改算 momentum。
此时应上传
[`dist/target-package/joinquant_strategy.py`](dist/target-package/joinquant_strategy.py)
并以同目录
[`bundle_manifest.json`](dist/target-package/bundle_manifest.json)
核对 package ID、package SHA 与 tape SHA;不要误传默认 smoke 文件。
2026-07-26`TP-20240311-795dcfba` 已用 FINAL4 在真实 JoinQuant hosted
环境完成 2024-03-11 至 2024-03-13 的 execution smoke3 月 11/13 日为
精确时钟 no-op,3 月 12 日按 `CURRENT_DATA_DAY_OPEN` 命中包,生成 3 笔
委托并全部成交。`600519.XSHG` 虽有 10% 权重,但 1693.94 元开盘价使一手
价值 169,394 元,高于 100,000 元目标名义金额,因而合法取整为 0 股。
- 脱敏机器记录:
[`evidence/joinquant/TP-20240311-795dcfba/2457a7c39a276e09e0fabf99e28978e1/run.json`](evidence/joinquant/TP-20240311-795dcfba/2457a7c39a276e09e0fabf99e28978e1/run.json)
- 证据边界:
[`docs/JOINQUANT_TARGET_PACKAGE_EVIDENCE_2026-07-26.md`](docs/JOINQUANT_TARGET_PACKAGE_EVIDENCE_2026-07-26.md)
该 run 的 `platform_evidence_class``real_platform_runtime`,但输入
`input_evidence_class` 仍是 `synthetic-research`;它只支持
`execution.real_platform_observed=true`。JoinQuant 没有重跑本地冻结的
Universe、Alpha、Portfolio、Risk,三日收益字段仅用于识别 smoke,
`performance_claim=false`,不计 G9 或 Baseline 60。
## QMT built-in 与 qmttools ## QMT built-in 与 qmttools
同一 bundler 生成: 同一 bundler 生成动量 smoke 或显式 TargetPackage 消费器
```text ```text
dist/qmt_builtin_strategy.py dist/qmt_builtin_strategy.py # 默认 momentum smoke
dist/bundle_manifest.json dist/target-package/qmt_builtin_strategy.py # synthetic 候选线路调试
dist/target-package/bundle_manifest.json # 目标包与 bundle 身份
``` ```
在 QMT Python 策略编辑器导入,选择 `1d`**backtest**,主图/基准设为 在 QMT Python 策略编辑器导入,选择 `1d`**backtest**,主图/基准设为
@@ -253,18 +305,20 @@ dist/bundle_manifest.json
QMT 文档所述的 `ContextInfo` 用户属性回滚。它硬编码 backtest-only;参数和 QMT 文档所述的 `ContextInfo` 用户属性回滚。它硬编码 backtest-only;参数和
环境变量均不能把它提升成实盘 `passorder` 环境变量均不能把它提升成实盘 `passorder`
策略按原始毫秒 timetag 查询历史中证 500 成分,并通过 在动量 smoke 模式,策略按原始毫秒 timetag 查询历史中证 500 成分,并通过
`ContextInfo.get_his_st_data` 读取决策日 ST/*ST/PT 区间;接口缺失、格式异常 `ContextInfo.get_his_st_data` 读取决策日 ST/*ST/PT 区间;接口缺失、格式异常
或覆盖不足会阻止计划。首次真实验收还必须用一只已知历史 ST 股票做正例探针, 或覆盖不足会阻止计划。首次真实验收还必须用一只已知历史 ST 股票做正例探针,
证明客户端权限和本地历史 ST 数据确实可用,不能把空结果直接解释成“当天无 证明客户端权限和本地历史 ST 数据确实可用,不能把空结果直接解释成“当天无
ST”。 ST”。在 TargetPackage 模式,Universe/Alpha/Portfolio/Risk 已由本地包冻结;
QMT 不重算这些层,只在包的 signal close 命中后绑定账户和完成日线价格,
`quickTrade=0` 交给下一执行步。两种模式的证据不得混记。
Windows 上、已登录且安装券商分发 `xtquant` 的 qmttools 入口: Windows 上、已登录且安装券商分发 `xtquant` 的 qmttools 入口:
```powershell ```powershell
$env:PYTHONPATH = "src;." $env:PYTHONPATH = "src;."
python -m platforms.qmt_research_runner ` python -m platforms.qmt_research_runner `
dist/qmt_builtin_strategy.py ` dist/target-package/qmt_builtin_strategy.py `
--stock-code 000905.SH ` --stock-code 000905.SH `
--start 20240102 ` --start 20240102 `
--end 20241231 ` --end 20241231 `
@@ -509,6 +563,11 @@ gate_credit = []
- `universe.py`openable / hold-only / sell-only / frozen / excluded - `universe.py`openable / hold-only / sell-only / frozen / excluded
- `features.py`:透明价量特征、可执行时钟标签、train-only 预处理; - `features.py`:透明价量特征、可执行时钟标签、train-only 预处理;
- `research.py` / `experiment.py`purge/embargo、Ridge、OOS IC/RankIC - `research.py` / `experiment.py`purge/embargo、Ridge、OOS IC/RankIC
- `model_bundle.py`:冻结 train-only 预处理、Ridge 参数、标签与训练 lineage
- `baseline_pipeline.py`:唯一五层本地主链和因果 hash trace;
- `target_package.py`:无股数、双时钟、可防篡改的 post-risk 权重合同;
- `release_reachability.py`:逐层区分 implemented、tested、entrypoint
reachable 与 real-platform observed
- `factor_risk.py`:行业/风格暴露、EWMA 收缩因子协方差、特异风险、stress; - `factor_risk.py`:行业/风格暴露、EWMA 收缩因子协方差、特异风险、stress;
- `costs.py`:日期化费率、最低佣金、low/base/high impact 与 capacity - `costs.py`:日期化费率、最低佣金、low/base/high impact 与 capacity
- `optimizer.py`deterministic fallback 与 lazy CVXPY 约束优化; - `optimizer.py`deterministic fallback 与 lazy CVXPY 约束优化;
@@ -519,22 +578,27 @@ gate_credit = []
provider snapshot 到 PIT target 与本地历史回测; provider snapshot 到 PIT target 与本地历史回测;
- `platforms/` / `adapters/`:聚宽、QMT、Qlib 与 XtTrader 边界。 - `platforms/` / `adapters/`:聚宽、QMT、Qlib 与 XtTrader 边界。
这些模块已有自动测试,聚宽 hosted 路径也已有一次真实运行;真实数据校准、 组件存在、单测通过、进入本地入口、进入平台入口和真实平台观察是五种不同
QMT/券商运行和运营证据仍分别计分。 事实。聚宽现有 TargetPackage run 只观察 execution consumer;它不证明
factor risk、CVXPY、TWAP/POV 或 Ridge 上游四层在 hosted 环境被调用。
## 仍未达到 60 的核心原因 ## 仍未达到 60 的核心原因
- 基本面公告时点、完整公司行动、Security Master 与退市链尚未形成生产数据湖; - 基本面公告时点、完整公司行动、Security Master 与退市链尚未形成生产数据湖;
- Ridge/LightGBM 候选尚未冻结成四引擎共同加载的 model bundle - Ridge 已能冻结成可重放 model bundle,但目前只有 synthetic 候选
尚无授权真实长样本 OOS,也没有必要让 hosted 引擎重新加载训练栈;
- 风险协方差、冲击参数和容量尚未用真实长样本校准; - 风险协方差、冲击参数和容量尚未用真实长样本校准;
- 聚宽已有真实 hosted run 但尚无完整同输入导出;QMT 尚无真实导出,QMT - factor risk、CVXPY 和 guarded TWAP/POV 仍是组件级能力,尚未进入当前
Baseline vertical slice
- 聚宽已有单包 TargetPackage execution 导出,但仍缺授权真实数据的多期
TargetPackage、本地/聚宽逐层同输入对账;QMT 尚无真实 peer 导出,QMT
历史 ST 权限正例、账户 query 合同、 历史 ST 权限正例、账户 query 合同、
20 日只读影子、回调/恢复/对账尚未形成真实证据; 20 日只读影子、回调/恢复/对账尚未形成真实证据;
- 自动 scheduler、日终对账服务、监控告警与 kill-switch 演练未形成连续证据; - 自动 scheduler、日终对账服务、监控告警与 kill-switch 演练未形成连续证据;
- 实际券商程序化交易报告、权限、频率与软件要求尚未书面确认。 - 实际券商程序化交易报告、权限、频率与软件要求尚未书面确认。
因此当前是可以继续填真实证据的 Quant OS 生产候选框架,不是可直接投入资金的 因此当前是“已接通本地五层与平台 TargetPackage 边界的候选骨架”,不是已经
成品,也不构成投资建议。 达到 60 分或可直接投入资金的成品,也不构成投资建议。
## 安全与进一步文档 ## 安全与进一步文档
@@ -542,7 +606,11 @@ QMT/券商运行和运营证据仍分别计分。
行情数据一律不得进入 Git。 行情数据一律不得进入 Git。
- 架构:[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) - 架构:[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)
- 五层真实结构与缺口:
[`docs/BASELINE_VERTICAL_SLICE.md`](docs/BASELINE_VERTICAL_SLICE.md)
- 平台矩阵:[`docs/PLATFORM_MATRIX.md`](docs/PLATFORM_MATRIX.md) - 平台矩阵:[`docs/PLATFORM_MATRIX.md`](docs/PLATFORM_MATRIX.md)
- 聚宽 TargetPackage 证据:
[`docs/JOINQUANT_TARGET_PACKAGE_EVIDENCE_2026-07-26.md`](docs/JOINQUANT_TARGET_PACKAGE_EVIDENCE_2026-07-26.md)
- 部署:[`runbooks/PLATFORM_DEPLOYMENT.md`](runbooks/PLATFORM_DEPLOYMENT.md) - 部署:[`runbooks/PLATFORM_DEPLOYMENT.md`](runbooks/PLATFORM_DEPLOYMENT.md)
- 安全:[`docs/SECURITY.md`](docs/SECURITY.md) - 安全:[`docs/SECURITY.md`](docs/SECURITY.md)
- 故障:[`runbooks/INCIDENTS.md`](runbooks/INCIDENTS.md) - 故障:[`runbooks/INCIDENTS.md`](runbooks/INCIDENTS.md)
+35 -10
View File
@@ -2,25 +2,50 @@
"artifacts": { "artifacts": {
"joinquant": { "joinquant": {
"core": "src/quant60/portable_core.py", "core": "src/quant60/portable_core.py",
"core_sha256": "af29e4e5e10705cf6aff89874394c1e80d3780f093c19d90ed53ef9d0188a934", "core_sha256": "f9de9f18e4008bb0fc80edb0b21cae6ea06a61828d89e9b7c11057f98225fc19",
"effective_config_sha256": "3ef9b9efda9ec6cc5f57a5daca57cab3b68242e0368f18fdaaf9e249bdaa7873", "effective_config_sha256": "d4ae2882e9b5d8d673e7c62c0dc54def7c20221c358ac091ee6529bf0413df0b",
"output": "dist/joinquant_strategy.py", "output": "dist/joinquant_strategy.py",
"output_sha256": "22ddb0546beb3bd4e4bbedecb271452111324c3903e45be1f4ac3d39f38cce4f", "output_sha256": "be4ca1d5764416b7c8e6c206121c0f0bd8219e7317679d79bc72a95492286b1a",
"wrapper": "platforms/joinquant_strategy.py", "wrapper": "platforms/joinquant_strategy.py",
"wrapper_sha256": "b5a8e3b0a774a0f45f52f6bcccbe786e234913a1a4cf5b68b85e2bd989ee9294" "wrapper_sha256": "0c672b64665b7c1d4409ba288b00d1608e976800af3a4f9a6467767ce4def592"
}, },
"qmt_builtin": { "qmt_builtin": {
"core": "src/quant60/portable_core.py", "core": "src/quant60/portable_core.py",
"core_sha256": "af29e4e5e10705cf6aff89874394c1e80d3780f093c19d90ed53ef9d0188a934", "core_sha256": "f9de9f18e4008bb0fc80edb0b21cae6ea06a61828d89e9b7c11057f98225fc19",
"effective_config_sha256": "11f189ea5c9d4eaddeb6394dc342abc871941cb3395d7ff79aae1275ce2ad98f", "effective_config_sha256": "7e4739937e4e99e5b58154f53271d8d3bfaed47d8d1d5adc747101babde827f5",
"output": "dist/qmt_builtin_strategy.py", "output": "dist/qmt_builtin_strategy.py",
"output_sha256": "ad503e5ebde60a5deb0ec86334c04d4be2d77ab91d9607d4fe88e2119f22d5dc", "output_sha256": "483877b7ea55c8e0f5e4e47b48a156b3add6cf0a8085011414c5c600913939b5",
"wrapper": "platforms/qmt_builtin_strategy.py", "wrapper": "platforms/qmt_builtin_strategy.py",
"wrapper_sha256": "efb931bbd04d0abf0a576f2422a13403fd4d659dccf59f8170e8b13b1bafa640" "wrapper_sha256": "a1e53dc22ebd9bb01be43675786151606bbed273d8ab883fb933461e379d2727"
} }
}, },
"baseline_config": "configs/baseline.json", "baseline_config": "configs/baseline.json",
"baseline_config_sha256": "939a7df386b06bc82869940572c35544e9361913b94d9e03027411e51b1006e1", "baseline_config_sha256": "939a7df386b06bc82869940572c35544e9361913b94d9e03027411e51b1006e1",
"portable_core_sha256": "af29e4e5e10705cf6aff89874394c1e80d3780f093c19d90ed53ef9d0188a934", "decision_mode": "portable_momentum_smoke",
"schema_version": 1 "joinquant_observation": {
"eod_api_policy": "best_effort_get_orders_get_trades",
"eod_chunk_characters": 1800,
"eod_chunk_encoding": "base64-canonical-json",
"events": [
"INIT",
"TARGET_PACKAGE_NOOP",
"TARGET_PACKAGE_HIT",
"PLATFORM_BINDING_INPUT",
"TARGET_PACKAGE_PLAN",
"SKIPPED_ORDER",
"ORDER_REQUEST",
"ORDER_RETURN",
"EOD_STATUS"
],
"fail_soft": true,
"prefix": "QUANT60_JOINQUANT_EVIDENCE_V1 ",
"schema_version": "1.0",
"target_price_source": "CURRENT_DATA_DAY_OPEN"
},
"portable_core_sha256": "f9de9f18e4008bb0fc80edb0b21cae6ea06a61828d89e9b7c11057f98225fc19",
"schema_version": 1,
"target_package_count": 0,
"target_package_ids": [],
"target_package_sha256": [],
"target_package_tape_sha256": null
} }
+1211 -10
View File
File diff suppressed because it is too large Load Diff
+835 -1
View File
@@ -3,6 +3,8 @@
# __PORTABLE_CORE_BUNDLE_START__ # __PORTABLE_CORE_BUNDLE_START__
# Inlined by tools/bundle_platforms.py; do not edit this region. # Inlined by tools/bundle_platforms.py; do not edit this region.
import hashlib
import json
import math import math
import re import re
@@ -26,6 +28,45 @@ _SHORT_EXCHANGES = {
"XBSE": "BJ", "XBSE": "BJ",
} }
_TARGET_PACKAGE_FIELDS = {
"schema_version",
"package_type",
"package_id",
"decision_id",
"release",
"evidence_class",
"model_id",
"signal_as_of",
"next_session",
"model_sha256",
"data_sha256",
"feature_sha256",
"risk_sha256",
"cost_sha256",
"optimizer_sha256",
"config_sha256",
"source_sha256",
"target_weights",
"universe",
"constraints",
"package_sha256",
}
_TARGET_PACKAGE_HASH_FIELDS = (
"model_sha256",
"data_sha256",
"feature_sha256",
"risk_sha256",
"cost_sha256",
"optimizer_sha256",
"config_sha256",
"source_sha256",
)
class TargetPackageLookupMiss(ValueError, LookupError):
"""An already verified tape has no exact requested decision."""
def _sum_numeric(values): def _sum_numeric(values):
"""Sum numerics without trusting a hosted runtime's global ``sum`` name.""" """Sum numerics without trusting a hosted runtime's global ``sum`` name."""
@@ -36,6 +77,15 @@ def _sum_numeric(values):
return total return total
def _any_true(values):
"""Return built-in ``any`` semantics without trusting hosted globals."""
for value in values:
if value:
return True
return False
def _infer_exchange(code): def _infer_exchange(code):
if code.startswith(("4", "8")): if code.startswith(("4", "8")):
return "XBSE" return "XBSE"
@@ -88,6 +138,396 @@ def convert_symbol(symbol, target="canonical"):
return normalize_symbol(symbol, target) return normalize_symbol(symbol, target)
def _canonical_json(value):
"""Match ``quant60.ledger.canonical_json`` for plain JSON values."""
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
)
def _json_copy(value, name):
try:
return json.loads(_canonical_json(value))
except (TypeError, ValueError) as error:
raise ValueError("%s must contain finite JSON values only" % name) from error
def _canonical_mapping(value, name, value_validator):
if not hasattr(value, "items"):
raise TypeError("%s must be a mapping" % name)
output = {}
for raw_symbol, raw_value in value.items():
symbol = normalize_symbol(raw_symbol)
if symbol in output:
raise ValueError(
"%s contains duplicate normalized symbol %s" % (name, symbol)
)
output[symbol] = value_validator(raw_value, "%s.%s" % (name, symbol))
return dict((symbol, output[symbol]) for symbol in sorted(output))
def _finite_non_negative(value, name):
if isinstance(value, bool):
raise ValueError("%s must be a finite non-negative number" % name)
try:
numeric = float(value)
except (TypeError, ValueError):
raise ValueError("%s must be a finite non-negative number" % name)
if not math.isfinite(numeric) or numeric < 0.0:
raise ValueError("%s must be a finite non-negative number" % name)
return numeric
def _non_negative_quantity(value, name):
numeric = _finite_non_negative(value, name)
if numeric != math.floor(numeric):
raise ValueError("%s must be a whole-share quantity" % name)
return int(numeric)
def _positive_price(value, name):
numeric = _finite_non_negative(value, name)
if numeric <= 0.0:
raise ValueError("%s must be positive" % name)
return numeric
def _optional_non_empty_string(value, name):
if value is None:
return None
if not isinstance(value, str) or not value.strip():
raise ValueError("%s must be a non-empty string when provided" % name)
return value
def _sha256_hex(value, name, allow_none=False):
if value is None and allow_none:
return None
if (
not isinstance(value, str)
or len(value) != 64
or _any_true(
character not in "0123456789abcdef"
for character in value
)
):
raise ValueError(
"%s must be a 64-character lowercase SHA-256 digest" % name
)
if value == "0" * 64:
raise ValueError("%s cannot be an all-zero placeholder" % name)
return value
def _reject_quantity_fields(value, path="$"):
if hasattr(value, "items"):
for raw_key, child in value.items():
key = str(raw_key)
normalized = key.lower().replace("-", "_")
if "quantity" in normalized or "quantities" in normalized:
raise ValueError(
"%s.%s: quantities do not belong in TargetPackageV1"
% (path, key)
)
_reject_quantity_fields(child, "%s.%s" % (path, key))
elif isinstance(value, list):
for index, child in enumerate(value):
_reject_quantity_fields(child, "%s[%d]" % (path, index))
def _verify_identifier(value, name):
if (
not isinstance(value, str)
or re.match(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$", value) is None
):
raise ValueError("%s is not a canonical identifier" % name)
return value
def _verify_target_package_clock(signal_as_of, next_session):
if (
not isinstance(signal_as_of, str)
or re.match(
r"^[0-9]{4}-[0-9]{2}-[0-9]{2}T15:00:00\+08:00$",
signal_as_of,
)
is None
):
raise ValueError("signal_as_of must be canonical China-market close")
if (
not isinstance(next_session, str)
or re.match(r"^[0-9]{4}-[0-9]{2}-[0-9]{2}$", next_session) is None
):
raise ValueError("next_session must use canonical YYYY-MM-DD")
if next_session <= signal_as_of[:10]:
raise ValueError("next_session must follow signal_as_of")
def _target_package_content_sha256(package):
body = dict(package)
body.pop("package_sha256", None)
_reject_quantity_fields(body)
try:
payload = _canonical_json(body).encode("utf-8")
except (TypeError, ValueError) as error:
raise ValueError(
"target package must contain finite JSON values only"
) from error
return hashlib.sha256(payload).hexdigest()
def verify_target_package(
package,
expected_signal_as_of=None,
expected_next_session=None,
expected_decision_id=None,
expected_package_sha256=None,
):
"""Verify the platform-critical subset of a sealed ``TargetPackageV1``.
This intentionally duplicates a small, Python-3.6-compatible safety
boundary from the richer local validator. Hosted runtimes must verify the
embedded package again instead of trusting the bundling process.
"""
if not hasattr(package, "items"):
raise TypeError("target package must be a mapping")
candidate = _json_copy(package, "target package")
_reject_quantity_fields(candidate)
fields = set(candidate)
if fields != _TARGET_PACKAGE_FIELDS:
missing = sorted(_TARGET_PACKAGE_FIELDS - fields)
unknown = sorted(fields - _TARGET_PACKAGE_FIELDS)
raise ValueError(
"target package fields mismatch; missing=%s, unknown=%s"
% (missing, unknown)
)
if candidate["schema_version"] != "1.0":
raise ValueError("unsupported target package schema_version")
if candidate["package_type"] != "TargetPackageV1":
raise ValueError("unsupported target package type")
for name in (
"package_id",
"decision_id",
"release",
"evidence_class",
"model_id",
):
_verify_identifier(candidate[name], name)
_verify_target_package_clock(
candidate["signal_as_of"], candidate["next_session"]
)
for name in _TARGET_PACKAGE_HASH_FIELDS:
_sha256_hex(candidate[name], name)
_sha256_hex(candidate["package_sha256"], "package_sha256")
raw_weights = candidate["target_weights"]
if not hasattr(raw_weights, "items") or not raw_weights:
raise ValueError("target_weights must be a non-empty mapping")
weights = {}
for raw_symbol, raw_weight in raw_weights.items():
symbol = normalize_symbol(raw_symbol)
if symbol != raw_symbol:
raise ValueError(
"target_weights symbol must be canonical: %s" % raw_symbol
)
if symbol in weights:
raise ValueError("duplicate target weight for %s" % symbol)
weight = _finite_non_negative(
raw_weight, "target_weights.%s" % symbol
)
if weight > 1.0:
raise ValueError("target weight exceeds one for %s" % symbol)
weights[symbol] = weight
gross = math.fsum(weights.values())
if gross > 1.0 + 1e-12:
raise ValueError("target_weights gross exceeds one")
universe = candidate["universe"]
if not hasattr(universe, "items") or not universe:
raise ValueError("universe must be a non-empty mapping")
canonical_universe = {}
for raw_symbol, raw_state in universe.items():
symbol = normalize_symbol(raw_symbol)
if symbol != raw_symbol:
raise ValueError("universe symbol must be canonical: %s" % raw_symbol)
if symbol in canonical_universe:
raise ValueError("duplicate universe symbol %s" % symbol)
if not hasattr(raw_state, "items"):
raise ValueError("universe state must be a mapping for %s" % symbol)
state = raw_state.get("state")
if state not in (
"openable",
"hold_only",
"sell_only",
"frozen",
"excluded",
):
raise ValueError("unsupported universe state for %s" % symbol)
canonical_universe[symbol] = state
missing_universe = sorted(set(weights) - set(canonical_universe))
if missing_universe:
raise ValueError(
"target symbols are absent from universe: %s" % missing_universe
)
for symbol, weight in weights.items():
if canonical_universe[symbol] == "excluded" and weight > 1e-12:
raise ValueError("excluded symbol has positive weight: %s" % symbol)
constraints = candidate["constraints"]
if not hasattr(constraints, "items"):
raise ValueError("constraints must be a mapping")
if constraints.get("long_only") is not True:
raise ValueError("TargetPackageV1 must be long-only")
declared_gross = _finite_non_negative(
constraints.get("actual_gross_weight"),
"constraints.actual_gross_weight",
)
max_gross = _finite_non_negative(
constraints.get("max_gross_weight"),
"constraints.max_gross_weight",
)
max_single = _finite_non_negative(
constraints.get("max_single_weight"),
"constraints.max_single_weight",
)
if abs(declared_gross - gross) > 1e-12:
raise ValueError("declared gross does not equal target_weights gross")
if gross > max_gross + 1e-12 or max_gross > 1.0:
raise ValueError("target_weights exceed max_gross_weight")
if max_single <= 0.0 or max_single > 1.0:
raise ValueError("invalid max_single_weight")
if _any_true(
weight > max_single + 1e-12 for weight in weights.values()
):
raise ValueError("target_weights exceed max_single_weight")
model_claim = " ".join(
(
candidate["release"],
candidate["evidence_class"],
)
).lower()
if (
"baseline" in model_claim
and "portable-momentum" in candidate["model_id"].lower()
):
raise ValueError(
"portable-momentum cannot claim a baseline target package"
)
expected_hash = _target_package_content_sha256(candidate)
if candidate["package_sha256"] != expected_hash:
raise ValueError("target package content hash mismatch")
if (
expected_signal_as_of is not None
and candidate["signal_as_of"] != expected_signal_as_of
):
raise ValueError("target package signal_as_of does not match request")
if (
expected_next_session is not None
and candidate["next_session"] != expected_next_session
):
raise ValueError("target package next_session does not match request")
if (
expected_decision_id is not None
and candidate["decision_id"] != expected_decision_id
):
raise ValueError("target package decision_id does not match request")
if expected_package_sha256 is not None:
_sha256_hex(
expected_package_sha256,
"expected_package_sha256",
)
if candidate["package_sha256"] != expected_package_sha256:
raise ValueError("target package digest does not match request")
return candidate
def target_package_tape_sha256(packages):
"""Hash the exact ordered canonical JSON package array."""
if not isinstance(packages, (list, tuple)):
raise TypeError("target package tape must be a list or tuple")
try:
payload = _canonical_json(list(packages)).encode("utf-8")
except (TypeError, ValueError) as error:
raise ValueError(
"target package tape must contain finite JSON values only"
) from error
return hashlib.sha256(payload).hexdigest()
def lookup_target_package_exact(
packages,
signal_as_of,
next_session,
decision_id=None,
expected_tape_sha256=None,
expected_count=None,
):
"""Verify a complete tape, then return one exact dual-clock decision."""
if not isinstance(packages, (list, tuple)) or not packages:
raise ValueError("target package tape must be a non-empty list or tuple")
_verify_target_package_clock(signal_as_of, next_session)
decision_id = _optional_non_empty_string(decision_id, "decision_id")
if expected_count is not None:
if (
isinstance(expected_count, bool)
or not isinstance(expected_count, int)
or expected_count < 0
):
raise ValueError("expected_count must be a non-negative integer")
if len(packages) != expected_count:
raise ValueError("target package tape count mismatch")
if expected_tape_sha256 is not None:
_sha256_hex(expected_tape_sha256, "expected_tape_sha256")
if target_package_tape_sha256(packages) != expected_tape_sha256:
raise ValueError("target package tape digest mismatch")
verified = []
package_ids = set()
decision_ids = set()
clock_keys = set()
for raw_package in packages:
package = verify_target_package(raw_package)
clock_key = (package["signal_as_of"], package["next_session"])
if package["package_id"] in package_ids:
raise ValueError("duplicate target package_id")
if package["decision_id"] in decision_ids:
raise ValueError("duplicate target decision_id")
if clock_key in clock_keys:
raise ValueError("duplicate target package clock")
package_ids.add(package["package_id"])
decision_ids.add(package["decision_id"])
clock_keys.add(clock_key)
verified.append(package)
matches = [
package
for package in verified
if package["signal_as_of"] == signal_as_of
and package["next_session"] == next_session
]
if not matches:
raise TargetPackageLookupMiss(
"no target package for exact signal_as_of/next_session clock"
)
if len(matches) != 1:
raise ValueError("ambiguous exact target package decision")
if (
decision_id is not None
and matches[0]["decision_id"] != decision_id
):
raise ValueError("exact target package decision_id mismatch")
return _json_copy(matches[0], "target package")
def momentum_score(prices, lookback=20, skip=0): def momentum_score(prices, lookback=20, skip=0):
"""Return trailing simple momentum, or ``None`` if history is insufficient. """Return trailing simple momentum, or ``None`` if history is insufficient.
@@ -314,6 +754,108 @@ def order_deltas(
return result return result
def build_target_weight_plan(
target_weights,
prices,
current,
sellable,
equity,
cash_buffer=0.02,
lot_size=100,
decision_id=None,
package_sha256=None,
):
"""Bind canonical post-risk weights to one account without recomputing Alpha.
``prices`` must cover exactly every target or currently managed symbol.
Missing prices fail closed even for a held symbol whose desired weight is
zero. A missing ``sellable`` entry is safe and means zero sellable shares.
"""
weights = _canonical_mapping(
target_weights,
"target_weights",
_finite_non_negative,
)
gross = math.fsum(weights.values())
if _any_true(weight > 1.0 for weight in weights.values()):
raise ValueError("target weight must not exceed one")
if gross > 1.0 + 1e-12:
raise ValueError("target weight gross must not exceed one")
canonical_current = _canonical_mapping(
current,
"current",
_non_negative_quantity,
)
canonical_sellable = _canonical_mapping(
sellable,
"sellable",
_non_negative_quantity,
)
unknown_sellable = sorted(set(canonical_sellable) - set(canonical_current))
if unknown_sellable:
raise ValueError(
"sellable contains symbols absent from current: %s"
% unknown_sellable
)
for symbol, quantity in canonical_sellable.items():
if quantity > canonical_current[symbol]:
raise ValueError(
"sellable exceeds current quantity for %s" % symbol
)
canonical_prices = _canonical_mapping(
prices,
"prices",
_positive_price,
)
managed_symbols = set(weights) | set(canonical_current)
price_symbols = set(canonical_prices)
missing_prices = sorted(managed_symbols - price_symbols)
extra_prices = sorted(price_symbols - managed_symbols)
if missing_prices or extra_prices:
if missing_prices:
raise KeyError(
"prices do not cover managed symbols: missing=%s"
% missing_prices
)
raise ValueError(
"prices contain unmanaged symbols: extra=%s" % extra_prices
)
complete_weights = dict(
(symbol, weights.get(symbol, 0.0))
for symbol in sorted(managed_symbols)
)
targets = target_quantities(
complete_weights,
canonical_prices,
equity,
lot_size,
cash_buffer,
)
orders = order_deltas(
targets,
canonical_current,
canonical_sellable,
lot_size,
)
decision_id = _optional_non_empty_string(decision_id, "decision_id")
package_sha256 = _sha256_hex(
package_sha256,
"package_sha256",
allow_none=True,
)
return {
"decision_id": decision_id,
"package_sha256": package_sha256,
"weights": complete_weights,
"targets": targets,
"orders": orders,
}
def build_rebalance_plan( def build_rebalance_plan(
price_history, price_history,
current, current,
@@ -359,14 +901,19 @@ def build_rebalance_plan(
__all__ = [ __all__ = [
"build_rebalance_plan", "build_rebalance_plan",
"build_target_weight_plan",
"capped_target_weights", "capped_target_weights",
"convert_symbol", "convert_symbol",
"lookup_target_package_exact",
"momentum_score", "momentum_score",
"normalize_symbol", "normalize_symbol",
"order_deltas", "order_deltas",
"rank_momentum", "rank_momentum",
"round_board_lot", "round_board_lot",
"TargetPackageLookupMiss",
"target_package_tape_sha256",
"target_quantities", "target_quantities",
"verify_target_package",
] ]
# __PORTABLE_CORE_BUNDLE_END__ # __PORTABLE_CORE_BUNDLE_END__
@@ -399,6 +946,9 @@ g = G()
# __BASELINE_CONFIG_START__ # __BASELINE_CONFIG_START__
# Generated from configs/baseline.json; do not edit this region. # Generated from configs/baseline.json; do not edit this region.
CONFIG = {'account_id': 'test', CONFIG = {'account_id': 'test',
'decision_mode': 'portable_momentum_smoke',
'target_package_count': 0,
'target_package_tape_sha256': None,
'universe_mode': 'pit_index', 'universe_mode': 'pit_index',
'index_symbol': '000905.XSHG', 'index_symbol': '000905.XSHG',
'qmt_sector_name': '\u4e2d\u8bc1500', 'qmt_sector_name': '\u4e2d\u8bc1500',
@@ -421,6 +971,77 @@ CONFIG = {'account_id': 'test',
'rebalance_schedule': 'weekly_first_close'} 'rebalance_schedule': 'weekly_first_close'}
# __BASELINE_CONFIG_END__ # __BASELINE_CONFIG_END__
# __TARGET_PACKAGE_TAPE_START__
# Generated by tools/bundle_platforms.py; do not edit this region.
TARGET_PACKAGES = []
# __TARGET_PACKAGE_TAPE_END__
MOMENTUM_SMOKE_MODE = "portable_momentum_smoke"
TARGET_PACKAGE_MODE = "target_package"
def _validated_target_packages(config):
mode = config.get("decision_mode")
if mode not in (MOMENTUM_SMOKE_MODE, TARGET_PACKAGE_MODE):
raise UnsupportedStrategyPeriod("unsupported decision_mode")
if not isinstance(TARGET_PACKAGES, list):
raise UnsupportedStrategyPeriod(
"TARGET_PACKAGES must be a list"
)
expected_count = config.get("target_package_count")
expected_sha256 = config.get("target_package_tape_sha256")
if mode == MOMENTUM_SMOKE_MODE:
if (
type(expected_count) is not int
or expected_count != 0
or expected_sha256 is not None
or TARGET_PACKAGES
):
raise UnsupportedStrategyPeriod(
"portable_momentum_smoke cannot carry target packages"
)
return []
if type(expected_count) is not int or expected_count <= 0:
raise UnsupportedStrategyPeriod(
"target_package mode requires a positive target_package_count"
)
if expected_count != len(TARGET_PACKAGES):
raise UnsupportedStrategyPeriod(
"target package count mismatch"
)
actual_sha256 = target_package_tape_sha256(TARGET_PACKAGES)
if expected_sha256 != actual_sha256:
raise UnsupportedStrategyPeriod(
"target package tape sha256 mismatch"
)
verified = []
package_ids = set()
decision_ids = set()
clocks = set()
signal_clocks = set()
for raw_package in TARGET_PACKAGES:
package = verify_target_package(raw_package)
package_id = package["package_id"]
decision_id = package["decision_id"]
clock = (package["signal_as_of"], package["next_session"])
signal_as_of = package["signal_as_of"]
if (
package_id in package_ids
or decision_id in decision_ids
or clock in clocks
or signal_as_of in signal_clocks
):
raise UnsupportedStrategyPeriod(
"duplicate or ambiguous target package decision identity"
)
package_ids.add(package_id)
decision_ids.add(decision_id)
clocks.add(clock)
signal_clocks.add(signal_as_of)
verified.append(package)
return verified
def _param(ContextInfo, name, default=None): def _param(ContextInfo, name, default=None):
params = getattr(ContextInfo, "_param", {}) params = getattr(ContextInfo, "_param", {})
@@ -442,6 +1063,9 @@ def init(ContextInfo):
override = _param(ContextInfo, "q60_" + key, None) override = _param(ContextInfo, "q60_" + key, None)
if override is not None: if override is not None:
g.config[key] = override g.config[key] = override
g.target_packages = _validated_target_packages(g.config)
g.decision_mode = g.config["decision_mode"]
if g.config["decision_mode"] == MOMENTUM_SMOKE_MODE:
if ( if (
g.config.get("rebalance_schedule") g.config.get("rebalance_schedule")
!= "weekly_first_close" != "weekly_first_close"
@@ -487,6 +1111,7 @@ def init(ContextInfo):
g.last_plan = None g.last_plan = None
g.last_universe = None g.last_universe = None
g.last_universe_as_of = None g.last_universe_as_of = None
g.last_target_signal_as_of = None
if ( if (
g.config.get("universe_mode") == "fixed" g.config.get("universe_mode") == "fixed"
and hasattr(ContextInfo, "set_universe") and hasattr(ContextInfo, "set_universe")
@@ -494,6 +1119,16 @@ def init(ContextInfo):
ContextInfo.set_universe(g.config["universe"]) ContextInfo.set_universe(g.config["universe"])
def _decision_mode():
configured = g.config.get("decision_mode")
frozen = getattr(g, "decision_mode", configured)
if configured != frozen:
raise UnsupportedStrategyPeriod(
"decision_mode cannot change after init"
)
return frozen
def _bar_datetime(ContextInfo): def _bar_datetime(ContextInfo):
timetag = ContextInfo.get_bar_timetag(ContextInfo.barpos) timetag = ContextInfo.get_bar_timetag(ContextInfo.barpos)
return datetime.datetime.fromtimestamp(float(timetag) / 1000.0) return datetime.datetime.fromtimestamp(float(timetag) / 1000.0)
@@ -670,7 +1305,10 @@ def _portfolio(ContextInfo, managed):
convert_symbol(symbol, "canonical") convert_symbol(symbol, "canonical")
for symbol in managed for symbol in managed
) )
dynamic = g.config.get("universe_mode") == "pit_index" dynamic = (
_decision_mode() == MOMENTUM_SMOKE_MODE
and g.config.get("universe_mode") == "pit_index"
)
for position in _trade_details(ContextInfo, "position") or []: for position in _trade_details(ContextInfo, "position") or []:
code = str( code = str(
_get_attr(position, ["stock_code", "m_strInstrumentID"], "") _get_attr(position, ["stock_code", "m_strInstrumentID"], "")
@@ -716,6 +1354,8 @@ def _portfolio(ContextInfo, managed):
def compute_plan(ContextInfo, bar_time=None): def compute_plan(ContextInfo, bar_time=None):
if _decision_mode() == TARGET_PACKAGE_MODE:
return _compute_target_package_plan(ContextInfo, bar_time)
if bar_time is None: if bar_time is None:
bar_time = _bar_datetime(ContextInfo) bar_time = _bar_datetime(ContextInfo)
membership_timetag = int( membership_timetag = int(
@@ -748,6 +1388,136 @@ def compute_plan(ContextInfo, bar_time=None):
) )
def _signal_as_of(bar_time):
if (
int(bar_time.hour) != 15
or int(bar_time.minute) != 0
or int(bar_time.second) != 0
or int(bar_time.microsecond) != 0
):
raise UnsupportedStrategyPeriod(
"target_package mode requires the completed 15:00 daily close"
)
return bar_time.strftime("%Y-%m-%dT15:00:00+08:00")
def _target_package_for_close(bar_time):
signal_as_of = _signal_as_of(bar_time)
config = g.config
packages = _validated_target_packages(config)
matches = [
package
for package in packages
if package["signal_as_of"] == signal_as_of
]
if not matches:
return None
if len(matches) != 1:
raise UnsupportedStrategyPeriod(
"ambiguous target package signal clock"
)
package = matches[0]
return lookup_target_package_exact(
packages,
signal_as_of,
package["next_session"],
decision_id=package["decision_id"],
expected_tape_sha256=config["target_package_tape_sha256"],
expected_count=config["target_package_count"],
)
def _target_close_prices(ContextInfo, bar_time, symbols):
platform_symbols = [
convert_symbol(symbol, "qmt")
for symbol in sorted(symbols)
]
raw = ContextInfo.get_market_data_ex(
fields=["close"],
stock_code=platform_symbols,
period="1d",
start_time="",
end_time=bar_time.strftime("%Y%m%d"),
count=1,
dividend_type="front_ratio",
fill_data=False,
subscribe=False,
)
if not hasattr(raw, "get"):
raise PriceHistoryUnavailable(
"target close query returned an invalid payload"
)
prices = {}
for symbol in platform_symbols:
values = _series_close(raw.get(symbol))
if len(values) != 1:
raise PriceHistoryUnavailable(
"%s needs one completed daily close" % symbol
)
try:
price = float(values[0])
except (TypeError, ValueError):
raise PriceHistoryUnavailable(
"%s contains a missing/non-numeric close" % symbol
)
if not math.isfinite(price) or price <= 0.0:
raise PriceHistoryUnavailable(
"%s contains a missing/non-positive close" % symbol
)
prices[convert_symbol(symbol, "canonical")] = price
return prices
def _target_lineage(package):
return {
"package_id": package["package_id"],
"package_sha256": package["package_sha256"],
"decision_id": package["decision_id"],
"signal_as_of": package["signal_as_of"],
"next_session": package["next_session"],
"release": package["release"],
"model_id": package["model_id"],
}
def _compute_target_package_plan(ContextInfo, bar_time=None):
if bar_time is None:
bar_time = _bar_datetime(ContextInfo)
package = _target_package_for_close(bar_time)
if package is None:
return None
managed = sorted(package["universe"])
current, sellable, equity = _portfolio(ContextInfo, managed)
if equity <= 0:
raise ValueError("QMT account equity must be positive")
plan = build_target_weight_plan(
target_weights=package["target_weights"],
prices=_target_close_prices(
ContextInfo,
bar_time,
set(package["target_weights"]) | set(current),
),
current=current,
sellable=sellable,
equity=equity,
cash_buffer=g.config["cash_buffer"],
lot_size=g.config["lot_size"],
decision_id=package["decision_id"],
package_sha256=package["package_sha256"],
)
plan["decision_mode"] = TARGET_PACKAGE_MODE
plan["lineage"] = _target_lineage(package)
plan["universe"] = package["universe"]
plan["execution_contract"] = {
"signal_price_source": "COMPLETED_DAILY_CLOSE",
"submit_trigger": "NEXT_BAR_FIRST_TICK",
"quick_trade": 0,
"declared_next_session": package["next_session"],
"real_platform_observed": False,
}
return plan
def _orders_allowed(ContextInfo): def _orders_allowed(ContextInfo):
# This hosted wrapper is deliberately backtest-only. Its cached account # This hosted wrapper is deliberately backtest-only. Its cached account
# query and in-memory weekly marker cannot provide crash-safe live # query and in-memory weekly marker cannot provide crash-safe live
@@ -810,12 +1580,76 @@ def _submit_delta(ContextInfo, canonical, delta, week_key):
) )
def _submit_target_delta(ContextInfo, canonical, delta, package_sha256):
config = g.config
order_code = convert_symbol(canonical, "qmt")
operation = 23 if delta > 0 else 24
volume = abs(int(delta))
user_order_id = "q60t-%s-%s" % (
str(package_sha256)[:8],
order_code.split(".")[0],
)
function = globals()["passorder"]
arg_count = getattr(getattr(function, "__code__", None), "co_argcount", 11)
if arg_count <= 8:
function(
operation,
1101,
config["account_id"],
order_code,
5,
-1,
volume,
ContextInfo,
)
else:
function(
operation,
1101,
config["account_id"],
order_code,
5,
-1,
volume,
"quant60",
0,
user_order_id,
ContextInfo,
)
def handlebar(ContextInfo): def handlebar(ContextInfo):
if not _is_backtest(ContextInfo) and hasattr(ContextInfo, "is_last_bar"): if not _is_backtest(ContextInfo) and hasattr(ContextInfo, "is_last_bar"):
if not ContextInfo.is_last_bar(): if not ContextInfo.is_last_bar():
return None return None
bar_time = _bar_datetime(ContextInfo) bar_time = _bar_datetime(ContextInfo)
if _decision_mode() == TARGET_PACKAGE_MODE:
signal_as_of = _signal_as_of(bar_time)
if g.last_target_signal_as_of == signal_as_of:
return None
plan = compute_plan(ContextInfo, bar_time)
# Consume the clock before crossing passorder so a callback retry
# cannot duplicate an already accepted order prefix.
g.last_target_signal_as_of = signal_as_of
if plan is None:
return None
g.last_plan = plan
if not _orders_allowed(ContextInfo):
return plan
ordered = sorted(
plan["orders"].items(),
key=lambda item: item[1],
)
for canonical, delta in ordered:
if int(delta) != 0:
_submit_target_delta(
ContextInfo,
canonical,
delta,
plan["lineage"]["package_sha256"],
)
return plan
year, week, unused = bar_time.isocalendar() year, week, unused = bar_time.isocalendar()
del unused del unused
week_key = (year, week) week_key = (year, week)
+55
View File
@@ -0,0 +1,55 @@
{
"artifacts": {
"joinquant": {
"core": "src/quant60/portable_core.py",
"core_sha256": "f9de9f18e4008bb0fc80edb0b21cae6ea06a61828d89e9b7c11057f98225fc19",
"effective_config_sha256": "640980026f397df2e6d0f9e3884b58c7e2343e1be70ca1b629f6111b889ec7cb",
"output": "dist/target-package/joinquant_strategy.py",
"output_sha256": "0586930ddc483f4489f09b562f4d4232b458b703e6a1194af809b707211be8c4",
"wrapper": "platforms/joinquant_strategy.py",
"wrapper_sha256": "0c672b64665b7c1d4409ba288b00d1608e976800af3a4f9a6467767ce4def592"
},
"qmt_builtin": {
"core": "src/quant60/portable_core.py",
"core_sha256": "f9de9f18e4008bb0fc80edb0b21cae6ea06a61828d89e9b7c11057f98225fc19",
"effective_config_sha256": "ace13da661a2921e6bf75e48a574fd605fb6eef87c0025b89460082326a39d36",
"output": "dist/target-package/qmt_builtin_strategy.py",
"output_sha256": "21f634a9831341fef2d86a6df612d01c79121794a48e525da76df226dc0729ed",
"wrapper": "platforms/qmt_builtin_strategy.py",
"wrapper_sha256": "a1e53dc22ebd9bb01be43675786151606bbed273d8ab883fb933461e379d2727"
}
},
"baseline_config": "configs/baseline.json",
"baseline_config_sha256": "939a7df386b06bc82869940572c35544e9361913b94d9e03027411e51b1006e1",
"decision_mode": "target_package",
"joinquant_observation": {
"eod_api_policy": "best_effort_get_orders_get_trades",
"eod_chunk_characters": 1800,
"eod_chunk_encoding": "base64-canonical-json",
"events": [
"INIT",
"TARGET_PACKAGE_NOOP",
"TARGET_PACKAGE_HIT",
"PLATFORM_BINDING_INPUT",
"TARGET_PACKAGE_PLAN",
"SKIPPED_ORDER",
"ORDER_REQUEST",
"ORDER_RETURN",
"EOD_STATUS"
],
"fail_soft": true,
"prefix": "QUANT60_JOINQUANT_EVIDENCE_V1 ",
"schema_version": "1.0",
"target_price_source": "CURRENT_DATA_DAY_OPEN"
},
"portable_core_sha256": "f9de9f18e4008bb0fc80edb0b21cae6ea06a61828d89e9b7c11057f98225fc19",
"schema_version": 1,
"target_package_count": 1,
"target_package_ids": [
"TP-20240311-795dcfba"
],
"target_package_sha256": [
"baa15c8156aa6cb54d3775d0cd562613a9d0a4f15b0c39f2a60e0f8b8c6f1f3a"
],
"target_package_tape_sha256": "5383c376951637b8d4b1ca42fc6db80347abda74c0181c0f70d6c411fb97ea64"
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+53 -30
View File
@@ -13,23 +13,26 @@ JQData (authorized account)
canonical immutable snapshot canonical immutable snapshot
exact-set/hash + semantic reconstruction exact-set/hash + semantic reconstruction
| |
+---------+----------+ +----------------------+
| |
v v
snapshot-backtest snapshot-decision
local event engine Signal -> Target -> Order Delta
| |
+---------+----------+
| |
portable-momentum-v1 v
snapshot-backtest / snapshot-decision
portable-momentum-v1 compatibility path (smoke only)
local Baseline authority:
Universe
-> frozen Ridge model bundle / Alpha
-> risk + cost-aware Portfolio
-> post-risk TargetPackageV1 (weights, no account quantities)
-> five-layer hash trace
| |
+------------+-------------+ +------------+-------------+
| | | | |
JoinQuant bundle QMT bundle local contract oracle JoinQuant consumer QMT consumer
hosted backtest built-in/qmttools backtest-only T+1-open account bind completed-close bind + quickTrade=0
order_target passorder (backtest-only)
synthetic PIT/features -> purged walk-forward -> Ridge challenger Current TargetPackage fixture is synthetic research evidence, not market proof.
(not deployment-frozen)
Qlib 0.9.7: Qlib 0.9.7:
Tushare completed/checksummed Parquet -> immutable managed provider Tushare completed/checksummed Parquet -> immutable managed provider
@@ -159,19 +162,24 @@ terminal/xtquant build 探针时不假设它与 `price × quantity` 的精确误
## Research and model boundary ## Research and model boundary
synthetic research pipeline 已实现 synthetic research vertical slice 当前实际接通
- PIT table 和未来值拒绝; - PIT table 和未来值拒绝;
- Universe 原因码; - Universe 原因码;
- 透明价量特征与 T+1-open 标签时钟; - 透明价量特征与 T+1-open 标签时钟;
- train-only winsor/impute/standardize - train-only winsor/impute/standardize
- purge/embargo walk-forward - purge/embargo walk-forward
- deterministic Ridge、OOS IC/RankIC - deterministic Ridge、OOS IC/RankIC 与可重放 model bundle
- factor risk、dated cost/capacity 和 constrained target。 - 60 日对角 realized variance
- dated explicit fee + spread + square-root impact/capacity
- deterministic constrained portfolio、独立 post-risk gate
- reference execution binding、完整五层 hash trace 与 TargetPackageV1。
这些功能证明研究管线可执行。Ridge 尚未冻结成带版本、输入 schema 和推理 `factor_risk.py`、CVXPY 路径和 guarded TWAP/POV 仍是有单测的独立组件,
hash 的 deployment bundle,也没有授权真实长样本 OOS 与四引擎推理验证,因此 不在上述 vertical slice 中。Ridge 已能冻结预处理、系数、标签、训练截止日
不是生产 champion。 和 lineage,但目前只有 synthetic 候选;没有授权真实长样本 OOS、真实风险/
成本校准、多期逐层 parity 或 QMT peer,因此不是生产 champion。已有的单包
JoinQuant run 只观察 execution consumer。
Qlib 路径固定为 `0.9.7`。CPython 3.12 实际 fixture smoke 已得到: Qlib 路径固定为 `0.9.7`。CPython 3.12 实际 fixture smoke 已得到:
@@ -191,9 +199,15 @@ L6,不声称 L3 target 或 L4 order-intent parity。
## Portable strategy and platform boundary ## Portable strategy and platform boundary
当前可跨 hosted wrapper 冻结的共同基线是 `portable-momentum-v1`。平台 API hosted artifact 有两个互斥模式:
不得进入 symbol normalization、momentum score、target weight、target
quantity 和 order delta 计算。 - `portable_momentum_smoke`:保留历史连通性回归,平台端计算动量;
- `target_package`:本地唯一权威已完成 Universe/Alpha/Portfolio/Risk
hosted wrapper 只验证包、精确匹配 `signal_as_of + next_session`、读取账户
事实并构造 execution plan,绝不回退到 momentum。
`portable-momentum-v1` 不是生产 Baseline。TargetPackage 只有权重,没有股数、
现金、订单或成交;这些属于下一交易日平台账户绑定后的 execution layer。
canonical 股票代码采用 JoinQuant 形式: canonical 股票代码采用 JoinQuant 形式:
@@ -208,8 +222,9 @@ canonical 股票代码采用 JoinQuant 形式:
QMT built-in bundle 和 qmttools runner 都硬限制为 backtest/history。built-in QMT built-in bundle 和 qmttools runner 都硬限制为 backtest/history。built-in
状态存放在模块全局 `g`,不写入可能在下个 `handlebar` 回滚的 状态存放在模块全局 `g`,不写入可能在下个 `handlebar` 回滚的
`ContextInfo` 用户属性。两条路径固定中证 500 benchmark、PIT 历史成分/ST `ContextInfo` 用户属性。动量 smoke 路径固定中证 500 benchmark、PIT 历史
与 baseline 费率;built-in 的 10% 参与率由 QMT GUI 设置,qmttools 成分/ST 与费率;TargetPackage 路径消费本地冻结 Universe/权重,不重新计算
Alpha/Portfolio/Risk。built-in 的 10% 参与率由 QMT GUI 设置,qmttools
程序化传入。live mutation 只存在于外部 XtTrader adapter,并且默认关闭、 程序化传入。live mutation 只存在于外部 XtTrader adapter,并且默认关闭、
未认证、没有 live 启动命令;operator 可运行的 shadow CLI 只接受查询边界。 未认证、没有 live 启动命令;operator 可运行的 shadow CLI 只接受查询边界。
@@ -217,6 +232,9 @@ QMT built-in bundle 和 qmttools runner 都硬限制为 backtest/history。built
最低交换合同是: 最低交换合同是:
- [`model_bundle.schema.json`](../schemas/model_bundle.schema.json)
- [`target_package.schema.json`](../schemas/target_package.schema.json)
- [`release_reachability.schema.json`](../schemas/release_reachability.schema.json)
- [`signal.schema.json`](../schemas/signal.schema.json) - [`signal.schema.json`](../schemas/signal.schema.json)
- [`target.schema.json`](../schemas/target.schema.json) - [`target.schema.json`](../schemas/target.schema.json)
- [`order_event.schema.json`](../schemas/order_event.schema.json) - [`order_event.schema.json`](../schemas/order_event.schema.json)
@@ -228,8 +246,13 @@ saved/current source aggregate、saved/current schema aggregate 和 payload
schema。`verify-snapshot-decision` 与 data snapshot semantic verifier 承担 schema。`verify-snapshot-decision` 与 data snapshot semantic verifier 承担
各自边界的同类职责。 各自边界的同类职责。
这些合同已有自动测试;最近一次标准库套件为 224 tests、OK、6 个可选路径 `release_reachability` 对每层分别记录 `implemented``unit_tested`
skip。测试结果不能替代 schema 迁移演练、真实平台导出或券商回调认证。 `local_entrypoint_reachable``jq_entrypoint_reachable`
`qmt_entrypoint_reachable``real_platform_observed`,并逐文件验证路径与
SHA。源文件总 hash 不能冒充真实运行。测试结果仍不能替代 schema 迁移演练、
真实平台导出或券商回调认证。当前只有
`execution.real_platform_observed=true`,由一个真实 JoinQuant
TargetPackage smoke 支持;其他四层及 all-layer summary 仍为 false。
## Clocks and reconciliation ## Clocks and reconciliation
@@ -274,12 +297,12 @@ synthetic/fake 数据是合同测试,不是市场证据;Qlib synthetic fixtu
当前还缺: 当前还缺:
- 授权 JQData 真实快照、基本面公告 PIT、完整公司行动/Security Master - 授权 JQData 真实快照、基本面公告 PIT、完整公司行动/Security Master
- Ridge/LightGBM 冻结模型包和真实 OOS 证据; - 授权真实数据训练的冻结模型包和真实 OOS 证据;
- 真实数据校准的风险、冲击、容量和 TCA; - 真实数据校准的风险、冲击、容量和 TCA;
- 真实 JoinQuant/QMT 导出; - 授权真实数据的多期 JoinQuant 导出、逐层本地对账与真实 QMT peer
- 券商状态映射、restart recovery daemon、20 日 shadow 与连续对账; - 券商状态映射、restart recovery daemon、20 日 shadow 与连续对账;
- 运维监控、kill-switch 演练和程序化交易合规确认。 - 运维监控、kill-switch 演练和程序化交易合规确认。
所以 Quant OS 是可执行、可继续填证据的生产候选框架,不是已经达到整体 所以 Quant OS 已具备本地五层 vertical slice 与 TargetPackage 平台边界,
60 分的实盘系统。证据政策见 但仍只是可继续填证据的候选骨架,不是已经达到整体 60 分的实盘系统。证据政策见
[`AUDIT_2026-07-25.md`](AUDIT_2026-07-25.md)。 [`AUDIT_2026-07-25.md`](AUDIT_2026-07-25.md)。
+13 -5
View File
@@ -25,7 +25,11 @@ A 股 Baseline 内核。它的新增得分必须在授权数据、真实平台
截至 2026-07-26 的实现事实快照: 截至 2026-07-26 的实现事实快照:
- 截至 2026-07-26 的最新标准库全量测试为 `Ran 224 tests``OK (skipped=6)` > 这是复评当日的历史快照,不是滚动状态页。当前五层主链、测试数和平台证据
> 边界以 [`BASELINE_VERTICAL_SLICE.md`](BASELINE_VERTICAL_SLICE.md)、
> [`gate_scorecard.json`](../gate_scorecard.json) 和生成的公开状态为准。
- 本次复评当时的标准库全量测试为 `Ran 224 tests``OK (skipped=6)`
- JQData raw/factor/pre-close/limits/paused + 每日 PIT `is_st`/指数成员 - JQData raw/factor/pre-close/limits/paused + 每日 PIT `is_st`/指数成员
已连接到 semantic snapshot verifier、`snapshot-backtest` 已连接到 semantic snapshot verifier、`snapshot-backtest`
`snapshot-decision`,并拒绝把抓取当日未到 24:00 的日线标成完整收盘, `snapshot-decision`,并拒绝把抓取当日未到 24:00 的日线标成完整收盘,
@@ -39,8 +43,12 @@ A 股 Baseline 内核。它的新增得分必须在授权数据、真实平台
- fixture 只有两只 synthetic 股票,任何性能数字都没有投资意义; - fixture 只有两只 synthetic 股票,任何性能数字都没有投资意义;
- JoinQuant/QMT mock parity exporter 已实现,但 - JoinQuant/QMT mock parity exporter 已实现,但
`real_platform_pass=false` 且不计 G9 `real_platform_pass=false` 且不计 G9
- 2026-07-26 已完成一次 2024 全年真实 JoinQuant hosted smoke,可见日志 - 2026-07-26 已完成一次 2024 全年真实 JoinQuant hosted momentum smoke,可见
无 ERROR;它证明 runtime/path,但尚无完整同输入导出和本地 strict peer 日志无 ERROR;它证明 `portable_momentum_smoke` runtime/path,不是
Ridge/TargetPackage Baseline 证据,且尚无完整同输入导出和本地 strict peer
- 同日一个 `synthetic-research` TargetPackage 已在真实 JoinQuant hosted
环境完成 exact-clock/`day_open` 绑定、3 笔委托和 3 笔成交;该证据只支持
execution 层真实平台观察,不支持上游四层、G9 或投资表现;
- XtTrader 已有严格只读、HMAC 账户绑定的 observation/snapshot/shadow - XtTrader 已有严格只读、HMAC 账户绑定的 observation/snapshot/shadow
plan 与 verifier,但真实 QMT、broker callback/shadow 均未运行, plan 与 verifier,但真实 QMT、broker callback/shadow 均未运行,
G1—G10 仍全部 `not_passed` G1—G10 仍全部 `not_passed`
@@ -233,8 +241,8 @@ PYTHONPATH=src:. python -m quant60 snapshot-backtest \
1. 用户授权登录 JQData,生成不可变真实 snapshot,保存许可/lineage,并通过 1. 用户授权登录 JQData,生成不可变真实 snapshot,保存许可/lineage,并通过
semantic verifier、`snapshot-backtest``snapshot-decision` semantic verifier、`snapshot-backtest``snapshot-decision`
2. 基于已完成的聚宽 hosted smoke,冻结一个小型 canonical 平台输入并导出 2. 已完成的单包 JoinQuant execution smoke 扩展为授权真实数据的多期
close arrays/hash、planorders/fills TargetPackage,并保存完整 identity/plan/orders/fills
3. 用本地与聚宽对相同输入逐层比较 L1—L4,而不是先比较净值;mock parity 3. 用本地与聚宽对相同输入逐层比较 L1—L4,而不是先比较净值;mock parity
只作为回归辅助; 只作为回归辅助;
4. 把基本面公告 available-time、完整公司行动/Security Master 加入真实 4. 把基本面公告 available-time、完整公司行动/Security Master 加入真实
+134
View File
@@ -0,0 +1,134 @@
# Quant OS Baseline vertical slice:当前真实结构与缺口
更新时间:2026-07-26。
## 一句话结论
Quant OS 现在已经不是“只有一堆互不相连的模块”:本地 synthetic 路径已经把
Universe、Ridge Alpha、风险/成本参与的 Portfolio、post-risk gate 和
reference Execution 串成唯一五层链,并发布冻结模型包与无股数
`TargetPackageV1`
但它仍然是 `NOT_BASELINE_60`。原因不是代码行数,而是当前候选只在 synthetic
数据上形成上游四层;虽然一个 synthetic-research TargetPackage 已真实通过
JoinQuant execution consumer 并成交,真实 JQData 长样本 OOS、上游逐层
真实数据 parity、真实 QMT peer 与 20 日 shadow 都还没有证据。
## 两条不能混记的运行路径
| 模式 | 上游决策在哪里 | 平台上运行什么 | 当前真实平台事实 |
| --- | --- | --- | --- |
| `portable_momentum_smoke` | JoinQuant/QMT wrapper 现场用完成日线算动量 | 动量、封顶权重、股数与 delta | JoinQuant 2024 全年跑过;不是 Baseline |
| `target_package` | 本地 Quant OS 完成 Universe → Alpha → Portfolio → Risk | 验包、精确双时钟命中、读取 equity/position/sellable/price、Execution | JoinQuant 已有一个真实 execution smoke3 单/3 成交);上游仍是 syntheticQMT 未实跑 |
默认 bundler 仍生成 smoke,保持历史回归兼容。只有显式传
`--target-package` 才会生成候选消费者;目标包缺失、重复、篡改或时钟不匹配
都会停止,不会回退到 momentum。
## 五层实际用了什么
| 层 | 当前 vertical slice 的实际实现 | 已接通的策略 | 明确未接通/未证明 |
| --- | --- | --- | --- |
| Universe | `SecurityEligibility` → 五态 state machine,原因码进入 trace/package | openable、hold-only、sell-only、frozen、excluded | synthetic 成员;真实基本面 PIT、Security Master、退市链未完成 |
| Alpha | train-only winsor/impute/standardize + `DeterministicRidge(alpha=5)` | 冻结 feature order、transform、系数、截距、训练截止与标签 | 无授权真实 OOSQlib Alpha158/LightGBM 是另一研究路径 |
| Portfolio | deterministic long-only constrained optimizer | Alpha 减显式成本,按预测方差调整;单名、行业、gross、capacity、turnover 约束 | CVXPY 有组件测试但未进入当前主链 |
| Risk | 60 日 realized diagonal variance 进入优化,随后独立 concentration/gross/turnover post-check | 风险输入变化必须改变 target;风险 gate 不可旁路 | `factor_risk.py` 的因子协方差/stress 尚未进入主链,也未真实校准 |
| Execution | 本地 reference account 生成 quantity/delta;平台从 TargetPackage 权重重新绑定真实账户 | 百股、科创板最小申报、T+1 sellable、卖单优先、平台保护价;JoinQuant 单包已有 3 单/3 fill | guarded TWAP/POV 尚未进入这条 slice;无 QMT peer、多期真实数据回放或长期 callback 证据 |
权威实现和合同:
- `src/quant60/baseline_pipeline.py`
- `src/quant60/model_bundle.py`
- `schemas/model_bundle.schema.json`
- `src/quant60/target_package.py`
- `schemas/target_package.schema.json`
- `src/quant60/release_reachability.py`
- `schemas/release_reachability.schema.json`
- `releases/quant60-research-candidate-v1/reachability.json`
可直接做平台线路调试的 synthetic 单文件在:
- `dist/target-package/joinquant_strategy.py`
- `dist/target-package/qmt_builtin_strategy.py`
- `dist/target-package/bundle_manifest.json`
它们只用于验证 TargetPackage 消费与执行边界,不能当作真实数据 Baseline
或投资结果。
## 为什么 hosted 文件仍然可以很短
“完整 Baseline”不等于把训练、特征工程、协方差估计和优化器全部复制进聚宽或
QMT。那样会制造多个决策权威,也会被平台 Python 版本、依赖和数据语义拖成
不同策略。
目标架构是:
```text
本地唯一权威
冻结数据/模型/配置
-> 五层决策与逐层 hash
-> post-risk TargetPackage(权重)
平台薄消费者
验证同一个 TargetPackage
-> 绑定平台账户事实和执行价格
-> quantity / order delta
-> order API 与导出
```
所以平台代码短本身是合理的;不合理的是薄 wrapper 自己改算 momentum,却被
文档称为完整 Baseline。现在这两种模式已经在配置、manifest、测试和文档中
分开。
## 当前机器可证明到哪里
每一层不再只写一个“已实现”,而是分别记录:
1. `implemented`
2. `unit_tested`
3. `local_entrypoint_reachable`
4. `jq_entrypoint_reachable`
5. `qmt_entrypoint_reachable`
6. `real_platform_observed`
`release_reachability` 要求每个 true 状态有类型匹配、仓库相对路径和实际
SHA-256 证据。源文件 aggregate hash 不能冒充真实平台运行。2026-07-26 的
TargetPackage hosted run 只允许把 `execution.real_platform_observed` 设为
trueUniverse、Alpha、Portfolio、Risk 仍为 false,因此
`all_layers_real_platform_observed` 也必须保持 false。
该真实执行记录使用 `synthetic-research` 输入,只证明验包、时钟、
`day_open`、账户绑定、下单和成交:
- [`JOINQUANT_TARGET_PACKAGE_EVIDENCE_2026-07-26.md`](JOINQUANT_TARGET_PACKAGE_EVIDENCE_2026-07-26.md)
- [`../evidence/joinquant/TP-20240311-795dcfba/2457a7c39a276e09e0fabf99e28978e1/run.json`](../evidence/joinquant/TP-20240311-795dcfba/2457a7c39a276e09e0fabf99e28978e1/run.json)
## 还差什么
按先后顺序:
1. 用授权 JQData/其他合法数据形成真实 PIT 长样本,训练并冻结候选 model
bundle;封住测试集,保存 OOS、容量和多状态报告。
2. 用真实数据校准 risk/cost;决定并接通 factor risk、CVXPY 是否进入
Baseline 60,而不是因为模块存在就自动算完成。
3. 从同一个真实候选生成多期 TargetPackage tape;不能用当前单个 synthetic
package 代替。
4. 把同一真实 tape 分别打入 JoinQuant/QMT;现有 JoinQuant 单包 execution
smoke 作为线路先验,但仍要导出完整多期 identity/plan/orders/fills。
5. 做 local/JoinQuant/QMT 逐层差异;任何 `UNEXPLAINED` 必须为零。当前无
QMT peer,所以 G9 仍未通过。
6. 完成至少 20 个交易日 QMT 只读 shadow、重启/回调/对账和合规确认。
## 用户什么时候需要参与
当前代码、测试、合同和文档整理不需要用户提供任何凭据,可以直接完成。
后续只有三处需要用户环境:
- JQData:在本机授权环境抓取可保存的脱敏快照;
- JoinQuant:已有一个 synthetic 单包 execution smoke;真实候选形成后仍需
上传多期 tape 并导出完整结果;
- Windows QMT/MiniQMT:使用已登录且有历史数据权限的客户端做 backtest 和
read-only shadow。
不要把密码、cookie、token、QMT userdata、account id 或持牌二进制发到聊天、
Git 或文档中。
@@ -0,0 +1,104 @@
# JoinQuant TargetPackage execution evidence — 2026-07-26
This is a conservative record of one real JoinQuant hosted execution smoke.
It proves that the frozen TargetPackage consumer reached the real hosted
account/price/order/fill boundary. It does not prove that JoinQuant recomputed
the upstream Universe, Alpha, Portfolio, or Risk layers, and it is not an
investment-performance claim.
## Frozen identity
- Run:
[JoinQuant backtest detail](https://www.joinquant.com/algorithm/backtest/detail?backtestId=2457a7c39a276e09e0fabf99e28978e1)
- Run ID: `2457a7c39a276e09e0fabf99e28978e1`
- Observed at: `2026-07-26T08:50:15Z`
- Bundle SHA-256:
`0586930ddc483f4489f09b562f4d4232b458b703e6a1194af809b707211be8c4`
- Bundle manifest SHA-256:
`91f4b4dc0bdd0d9f99b3466c830600e2678be78dd991d4499a4154bffec8597a`
- Portable core SHA-256:
`f9de9f18e4008bb0fc80edb0b21cae6ea06a61828d89e9b7c11057f98225fc19`
- Wrapper SHA-256:
`0c672b64665b7c1d4409ba288b00d1608e976800af3a4f9a6467767ce4def592`
- TargetPackage ID: `TP-20240311-795dcfba`
- Package SHA-256:
`baa15c8156aa6cb54d3775d0cd562613a9d0a4f15b0c39f2a60e0f8b8c6f1f3a`
- Tape SHA-256:
`5383c376951637b8d4b1ca42fc6db80347abda74c0181c0f70d6c411fb97ea64`
- Evidence class: `synthetic-research`
The uploaded browser snapshot and the frozen bundle had the same SHA-256.
The machine record distinguishes
`platform_evidence_class=real_platform_runtime` from
`input_evidence_class=synthetic-research` and fixes
`performance_claim=false`.
The tracked, redacted machine record is
[`run.json`](../evidence/joinquant/TP-20240311-795dcfba/2457a7c39a276e09e0fabf99e28978e1/run.json).
It contains no account identifier, cookie, token, credentials, screenshots, or
personal information.
## Runtime and observed sessions
- Period: 2024-03-11 through 2024-03-13
- Initial capital: CNY 1,000,000
- Frequency/runtime: daily, Python 3
- Benchmark: CSI 500 (`000905.XSHG`)
- Decision mode: `target_package`
| Session | Hosted observation | Orders | Fills |
| --- | --- | ---: | ---: |
| 2024-03-11 | Exact-clock miss; `TARGET_PACKAGE_NOOP`; no fallback | 0 | 0 |
| 2024-03-12 | Package hit; prices bound from `CURRENT_DATA_DAY_OPEN` | 3 | 3 |
| 2024-03-13 | Exact-clock miss; `TARGET_PACKAGE_NOOP`; no fallback | 0 | 0 |
The hosted plan preserved the package weights exactly: 10% each for
`600519.XSHG`, `601012.XSHG`, `601318.XSHG`, and `688981.XSHG`; all other
weights were zero.
The resulting targets and fills were:
| Symbol | Weight | Target quantity | Filled quantity | Fill price |
| --- | ---: | ---: | ---: | ---: |
| `600519.XSHG` | 0.10 | 0 | 0 | — |
| `601012.XSHG` | 0.10 | 4,500 | 4,500 | 22.22 |
| `601318.XSHG` | 0.10 | 2,300 | 2,300 | 43.18 |
| `688981.XSHG` | 0.10 | 2,000 | 2,000 | 47.87 |
`600519.XSHG` opened at CNY 1,693.94. One 100-share board lot therefore
required CNY 169,394, above the CNY 100,000 target notional. The deterministic
lot rule correctly produced a zero-share target; this was not a rejected or
skipped order. The STAR Market request for `688981.XSHG` used the configured
hosted limit-price protection.
## What this evidence proves
For the `execution` layer only, this run observes:
- exact bundle/package identity verification;
- exact signal-close/next-session clock matching with no momentum fallback;
- real hosted equity and `day_open` price binding;
- deterministic target-quantity and order construction;
- three accepted orders and three real hosted fills;
- clean no-op behavior on the surrounding sessions.
The release reachability manifest may therefore mark only
`execution.real_platform_observed=true`, backed by the tracked machine record.
Universe, Alpha, Portfolio, and Risk remain
`real_platform_observed=false`: those layers were computed locally and carried
to JoinQuant as a frozen TargetPackage rather than recomputed inside JoinQuant.
## What this evidence does not prove
The package still has evidence class `synthetic-research`. This three-session,
single-package run provides no:
- authorized real-data long-sample or out-of-sample validation;
- real-data calibration of the upstream Alpha, Portfolio, or Risk layers;
- multi-period same-input layer-by-layer local/JoinQuant parity;
- real QMT peer run or 20-trading-day broker shadow;
- G9 cross-engine acceptance, `BASELINE_60`, release verification, live
readiness, or investment-value evidence.
The hosted performance fields are retained in the machine record solely to
identify the completed smoke. They are economically meaningless over this
three-session window and must not be presented as strategy performance.
+22 -12
View File
@@ -5,16 +5,16 @@
| Path | Intended use | Runtime / prerequisite | Current verified fact | Missing release evidence | | Path | Intended use | Runtime / prerequisite | Current verified fact | Missing release evidence |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| Local synthetic execution | 确定性事件回测、ledger/replay | Python ≥3.10,无账号 | 标准库套件最近记录为 224 tests、OK、6 skipsmoke + exact manifest verifier 可运行 | synthetic 不代表真实市场/收益 | | Local synthetic execution | 确定性事件回测、ledger/replay | Python ≥3.10,无账号 | 标准库套件smoke + exact manifest verifier 可运行 | synthetic 不代表真实市场/收益 |
| Local synthetic research | PIT/features/walk-forward/Ridge/risk/cost/target | Python ≥3.10,无账号 | research smoke + manifest verifier 可运行 | Ridge 未冻结部署,未做授权长样本 OOS | | Local synthetic research | 五层 vertical sliceUniverse/Ridge Alpha/risk+cost Portfolio/post-risk/reference Execution | Python ≥3.10,无账号 | 冻结 model bundle、TargetPackageV1、layer trace + manifest verifier 可运行;风险/成本扰动必须改变 target | 仍是 synthetic;当前只接对角风险与 deterministic optimizer,未做授权长样本 OOS |
| JQData ingestion | 授权数据到 canonical immutable snapshot | `jqdatasdk==1.9.8` + 授权账号 | fake-provider 测试覆盖 raw/factor/pre-close/limits/paused/PIT `is_st`/membership、24:00 完整收盘约束和语义 verifier | 尚未登录真实账号保存快照、许可与 lineage | | JQData ingestion | 授权数据到 canonical immutable snapshot | `jqdatasdk==1.9.8` + 授权账号 | fake-provider 测试覆盖 raw/factor/pre-close/limits/paused/PIT `is_st`/membership、24:00 完整收盘约束和语义 verifier | 尚未登录真实账号保存快照、许可与 lineage |
| Snapshot local backtest | PIT membership 的 provider-data 本地回测 | semantic verifier 通过的 snapshot | `snapshot-backtest` 复用 production decision path,输出普通 run manifest | 尚无授权真实 snapshot;仍是本地 fill model | | Snapshot local backtest | PIT membership 的 provider-data 本地回测 | semantic verifier 通过的 snapshot | `snapshot-backtest` 复用 production decision path,输出普通 run manifest | 尚无授权真实 snapshot;仍是本地 fill model |
| Snapshot decision | 某决策日 Signal/Target/Order Delta | snapshot + equity 或完整 broker snapshot | fail-closed broker state 与 exact decision manifest verifier 已实现 | 未接真实账户 snapshot,未冻结生产模型 | | Snapshot decision | 某决策日 Signal/Target/Order Delta | snapshot + equity 或完整 broker snapshot | fail-closed broker state 与 exact decision manifest verifier 已实现 | 未接真实账户 snapshot,未冻结生产模型 |
| Colab | clone、测试、bundle、本地两条 synthetic 链;可选 JQData/Qlib | Colab runtime | notebook 已可本地逐 cell 执行 | 可选账号/依赖 cell 仍需运行时授权;不是平台证据 | | Colab | clone、测试、bundle、本地两条 synthetic 链;可选 JQData/Qlib | Colab runtime | notebook 已可本地逐 cell 执行 | 可选账号/依赖 cell 仍需运行时授权;不是平台证据 |
| JoinQuant local harness | wrapper API 合同 | Python ≥3.10,无账号 | fake lifecycle 与 mock parity 可运行 | 不证明 JoinQuant | | JoinQuant local harness | wrapper API 合同 | Python ≥3.10,无账号 | 动量 smoke 与 TargetPackage 消费模式均有 fake lifecycle;目标包缺失/篡改不回退 | 不证明 JoinQuant |
| JoinQuant hosted bundle | hosted backtest | JoinQuant 登录/数据权限 | 真实 2024 全年 run 已完成且可见日志 0 ERROR;单文件、PIT index/ST、批量 history、费率/滑点/10% 参与率、科创板保护价/200 股约束已运行 | 尚无完整输入/plan/orders/fills 导出和同输入本地比较 | | JoinQuant hosted bundle | hosted backtest | JoinQuant 登录/数据权限 | 动量 smoke 的真实 2024 全年 run 已完成FINAL4 的单个 synthetic-research TargetPackage 已在真实 hosted 环境按 `day_open` 命中并产生 3 单/3 fill | 只观察 execution consumer;无授权真实长样本/OOS、上游四层 hosted 执行、QMT peer 或逐层真实数据 parity |
| QMT local harness | built-in wrapper 合同 | Python ≥3.10,无账号 | Python 3.6 source、`ContextInfo` 回滚模拟和 mock parity | 不证明 QMT | | QMT local harness | built-in wrapper 合同 | Python ≥3.10,无账号 | Python 3.6 source、`ContextInfo` 回滚模拟、动量 smoke 与 TargetPackage 合同 | 不证明 QMT |
| QMT built-in bundle | GUI backtest-only | 授权 QMT 客户端/板块与历史 ST 数据 | bundle 硬限制 backtest;毫秒 timetag PIT 成分/ST;固定中证 500、费率/滑点;模块全局 `g` | 尚无真实 QMT run/export10% GUI 配置与历史 ST 正例探针待取证 | | QMT built-in bundle | GUI backtest-only | 授权 QMT 客户端/历史数据 | 两种模式都硬限制 backtestTargetPackage 模式不重算上游四层;模块全局 `g` | 尚无 TargetPackage 真实 QMT run/export10% GUI 配置和执行时钟待取证 |
| qmttools runner | native-Python QMT backtest/history | 券商分发 `xtquant` + 已登录 terminal | 固定中证 500/10% 参与率、参数、只读 preflight 和 hard backtest/history 合同已测 | 尚无专有运行时实跑 | | qmttools runner | native-Python QMT backtest/history | 券商分发 `xtquant` + 已登录 terminal | 固定中证 500/10% 参与率、参数、只读 preflight 和 hard backtest/history 合同已测 | 尚无专有运行时实跑 |
| XtTrader read-only shadow | 账户绑定 target-diff 与 broker observation | MiniQMT/QMT、账户查询权限、既有本地 HMAC key | HMAC 账户绑定 + authenticated evidence envelope、decision/observation semantic replay、资产/持仓/委托/成交恒等式、fail-closed exact verifier 与 fake broker 合同 | HMAC 不是 broker attestation;仍缺官方 query 失败/空结果合同、callback/restart、20 日真实 shadownot live-ready | | XtTrader read-only shadow | 账户绑定 target-diff 与 broker observation | MiniQMT/QMT、账户查询权限、既有本地 HMAC key | HMAC 账户绑定 + authenticated evidence envelope、decision/observation semantic replay、资产/持仓/委托/成交恒等式、fail-closed exact verifier 与 fake broker 合同 | HMAC 不是 broker attestation;仍缺官方 query 失败/空结果合同、callback/restart、20 日真实 shadownot live-ready |
| Qlib native momentum | signal/model research | CPython 3.12 + exactly `pyqlib==0.9.7` | 真实本地 fixture 成功,24 signal;保存 signal/report hash、表边界和重算 portfolio 指标 | synthetic 两股票;无真实数据/OOS,无 L3/L4 parity | | Qlib native momentum | signal/model research | CPython 3.12 + exactly `pyqlib==0.9.7` | 真实本地 fixture 成功,24 signal;保存 signal/report hash、表边界和重算 portfolio 指标 | synthetic 两股票;无真实数据/OOS,无 L3/L4 parity |
@@ -22,12 +22,14 @@
| Tushare local → Qlib | completed Parquet 的只读盘点、不可变 provider 与本地研究 smoke | 同上 + `pyarrow==24.0.0`;本地镜像 | 93 个 completed 文件全部通过 size/SHA/row-countv4 provider verifier 成功;同一动量命令连续两次 evidence JSON 逐 byte 相同 | `daily` 仅完成约 8.2%,缺复权、真实 benchmark、PIT 成分/ST/停牌/涨跌停;`production_ready=false` | | Tushare local → Qlib | completed Parquet 的只读盘点、不可变 provider 与本地研究 smoke | 同上 + `pyarrow==24.0.0`;本地镜像 | 93 个 completed 文件全部通过 size/SHA/row-countv4 provider verifier 成功;同一动量命令连续两次 evidence JSON 逐 byte 相同 | `daily` 仅完成约 8.2%,缺复权、真实 benchmark、PIT 成分/ST/停牌/涨跌停;`production_ready=false` |
| Mock parity exporter | JoinQuant/QMT wrapper 的 L2—L4 合同回归 | Python ≥3.10 | `mock_contract_only``real_platform_pass=false``gate_credit=[]` | 不计 G9;必须换成真实平台导出 | | Mock parity exporter | JoinQuant/QMT wrapper 的 L2—L4 合同回归 | Python ≥3.10 | `mock_contract_only``real_platform_pass=false``gate_credit=[]` | 不计 G9;必须换成真实平台导出 |
G1—G10 当前都未通过。JoinQuant hosted smoke 与 Qlib fixture smoke 是真实 G1—G10 当前都未通过。JoinQuant TargetPackage run 支持且只支持
runtime 证据,但不是市场有效性或跨平台 Gate 通过证据。 `execution.real_platform_observed=true`;输入仍是 `synthetic-research`
Universe/Alpha/Portfolio/Risk 的真实平台观察均为 false。它与旧 momentum
smoke、Qlib fixture 一样,不是市场有效性或跨平台 Gate 通过证据。
## Stable strategy boundary ## Stable strategy boundary
当前四引擎共同可冻结的是 `portable-momentum-v1` `portable-momentum-v1` 当前只作为 smoke 冻结
- canonical symbol normalization - canonical symbol normalization
- momentum score/rank - momentum score/rank
@@ -36,8 +38,11 @@ runtime 证据,但不是市场有效性或跨平台 Gate 通过证据。
- T+1/sellable-aware order delta。 - T+1/sellable-aware order delta。
- 科创板 200 股最小申报、余额一次性卖清与 hosted 最坏价保护。 - 科创板 200 股最小申报、余额一次性卖清与 hosted 最坏价保护。
本地 Ridge 与 Qlib Alpha158/LightGBM 是研究候选。没有 versioned model 本地 Ridge 已能冻结为 versioned model bundle,并通过
bundle、授权真实 OOS 和跨引擎推理结果前,不能称为已部署 champion。 `TargetPackageV1` 把 post-risk 权重移交给 JoinQuant/QMT;平台不需要、也
不应重新运行训练栈。一个 synthetic 包已通过真实 JoinQuant execution
consumer,但没有授权真实 OOS、真实校准、多期逐层 parity 和 QMT peer
仍不能称为已部署 champion。
Qlib 的单一 `limit_threshold` 是市场级近似,不能表达逐日板块/ST 限制; Qlib 的单一 `limit_threshold` 是市场级近似,不能表达逐日板块/ST 限制;
因此它只参加 L1/L2 和诊断 L6。XtTrader mutation 边界目前只接受 `.SH` 因此它只参加 L1/L2 和诊断 L6。XtTrader mutation 边界目前只接受 `.SH`
@@ -81,8 +86,13 @@ PYTHONPATH=src:. python -m quant60 snapshot-decision \
--config configs/baseline.json --as-of 2024-12-31 \ --config configs/baseline.json --as-of 2024-12-31 \
--equity 1000000 --output artifacts/csi500-2024-decision --equity 1000000 --output artifacts/csi500-2024-decision
# hosted bundle 和 mock-only parity # 默认只构建 portable momentum smoke
python tools/bundle_platforms.py python tools/bundle_platforms.py
# 显式构建无 fallback 的 TargetPackage 消费器
python tools/bundle_platforms.py \
--target-package artifacts/research-smoke/target_package.json \
--output-dir dist/target-package
PYTHONPATH=src:. python tools/export_mock_parity.py \ PYTHONPATH=src:. python tools/export_mock_parity.py \
export --output artifacts/mock-parity/report.json export --output artifacts/mock-parity/report.json
PYTHONPATH=src:. python tools/export_mock_parity.py \ PYTHONPATH=src:. python tools/export_mock_parity.py \
@@ -0,0 +1,205 @@
{
"schema_version": "1.0",
"record_type": "joinquant_target_package_execution_smoke",
"platform": "joinquant",
"status": "completed",
"platform_evidence_class": "real_platform_runtime",
"input_evidence_class": "synthetic-research",
"performance_claim": false,
"observed_at": "2026-07-26T08:50:15Z",
"run_id": "2457a7c39a276e09e0fabf99e28978e1",
"backtest": {
"id": "2457a7c39a276e09e0fabf99e28978e1",
"url": "https://www.joinquant.com/algorithm/backtest/detail?backtestId=2457a7c39a276e09e0fabf99e28978e1",
"start_date": "2024-03-11",
"end_date": "2024-03-13",
"initial_capital_cny": 1000000,
"frequency": "day",
"python": "Python3",
"benchmark": "000905.XSHG",
"timezone": "Asia/Shanghai"
},
"artifact_identity": {
"bundle_sha256": "0586930ddc483f4489f09b562f4d4232b458b703e6a1194af809b707211be8c4",
"browser_snapshot_sha256": "0586930ddc483f4489f09b562f4d4232b458b703e6a1194af809b707211be8c4",
"exact_browser_match": true,
"bundle_manifest_sha256": "91f4b4dc0bdd0d9f99b3466c830600e2678be78dd991d4499a4154bffec8597a",
"portable_core_sha256": "f9de9f18e4008bb0fc80edb0b21cae6ea06a61828d89e9b7c11057f98225fc19",
"wrapper_sha256": "0c672b64665b7c1d4409ba288b00d1608e976800af3a4f9a6467767ce4def592"
},
"target_package": {
"package_id": "TP-20240311-795dcfba",
"decision_id": "RD-20240311",
"release": "quant60-research-candidate-v1",
"evidence_class": "synthetic-research",
"package_sha256": "baa15c8156aa6cb54d3775d0cd562613a9d0a4f15b0c39f2a60e0f8b8c6f1f3a",
"tape_sha256": "5383c376951637b8d4b1ca42fc6db80347abda74c0181c0f70d6c411fb97ea64",
"signal_as_of": "2024-03-11T15:00:00+08:00",
"next_session": "2024-03-12"
},
"session_observations": [
{
"date": "2024-03-11",
"event": "TARGET_PACKAGE_NOOP",
"exact_clock_match": false,
"fallback_invoked": false,
"orders": 0,
"fills": 0
},
{
"date": "2024-03-12",
"event": "TARGET_PACKAGE_HIT",
"exact_clock_match": true,
"price_source": "CURRENT_DATA_DAY_OPEN",
"package_weights_exact": true,
"orders": 3,
"fills": 3
},
{
"date": "2024-03-13",
"event": "TARGET_PACKAGE_NOOP",
"exact_clock_match": false,
"fallback_invoked": false,
"orders": 0,
"fills": 0
}
],
"day_open_prices": {
"000001.XSHE": 10.48,
"000333.XSHE": 65.35,
"000651.XSHE": 40.46,
"002415.XSHE": 34.21,
"002594.XSHE": 201.0,
"300059.XSHE": 13.85,
"300750.XSHE": 183.34,
"600000.XSHG": 7.11,
"600519.XSHG": 1693.94,
"601012.XSHG": 22.22,
"601318.XSHG": 43.17,
"688981.XSHG": 47.86
},
"target_weights": {
"000001.XSHE": 0.0,
"000333.XSHE": 0.0,
"000651.XSHE": 0.0,
"002415.XSHE": 0.0,
"002594.XSHE": 0.0,
"300059.XSHE": 0.0,
"300750.XSHE": 0.0,
"600000.XSHG": 0.0,
"600519.XSHG": 0.1,
"601012.XSHG": 0.1,
"601318.XSHG": 0.1,
"688981.XSHG": 0.1
},
"target_quantities": {
"000001.XSHE": 0,
"000333.XSHE": 0,
"000651.XSHE": 0,
"002415.XSHE": 0,
"002594.XSHE": 0,
"300059.XSHE": 0,
"300750.XSHE": 0,
"600000.XSHG": 0,
"600519.XSHG": 0,
"601012.XSHG": 4500,
"601318.XSHG": 2300,
"688981.XSHG": 2000
},
"lot_rounding": {
"symbol": "600519.XSHG",
"target_weight": 0.1,
"day_open": 1693.94,
"board_lot": 100,
"target_notional_cny": 100000,
"one_board_lot_notional_cny": 169394,
"target_quantity": 0,
"reason": "one board lot costs more than target notional"
},
"orders": [
{
"security": "601012.XSHG",
"target_quantity": 4500,
"submitted_quantity": 4500,
"style": "market",
"status": "held"
},
{
"security": "601318.XSHG",
"target_quantity": 2300,
"submitted_quantity": 2300,
"style": "market",
"status": "held"
},
{
"security": "688981.XSHG",
"target_quantity": 2000,
"submitted_quantity": 2000,
"style": "limit",
"protection_price": 57.59,
"status": "held"
}
],
"fills": [
{
"security": "601012.XSHG",
"quantity": 4500,
"price": 22.22,
"time": "2024-03-12T09:30:00"
},
{
"security": "601318.XSHG",
"quantity": 2300,
"price": 43.18,
"time": "2024-03-12T09:30:00"
},
{
"security": "688981.XSHG",
"quantity": 2000,
"price": 47.87,
"time": "2024-03-12T09:30:00"
}
],
"counts": {
"orders": 3,
"fills": 3,
"skipped_orders": 0
},
"performance": {
"strategy_return": -0.0029,
"annualized_return": -0.2118,
"excess_return": -0.0149,
"benchmark_return": 0.0122,
"alpha": -0.341,
"beta": 0.052,
"sharpe": -4.712,
"max_drawdown": 0.0047,
"final_total_value_cny": 997148.04076,
"scope": "three-session execution wiring smoke only",
"investment_value_claim": false
},
"claim_boundary": {
"real_platform_observed_layers": [
"execution"
],
"upstream_layers_computed_in_joinquant": [],
"upstream_layers_real_platform_observed": false,
"authorized_real_data_oos_observed": false,
"qmt_peer_observed": false,
"same_input_cross_engine_parity_observed": false,
"all_five_layers_real_platform_observed": false,
"g9_passed": false,
"baseline_60_passed": false,
"release_verified": false,
"investment_value_claim": false,
"note": "This run proves only the hosted TargetPackage consumer, account/price binding, order submission, and fills for one synthetic-research package."
},
"redaction": {
"contains_account_identifier": false,
"contains_cookie": false,
"contains_token": false,
"contains_credentials": false,
"contains_personal_information": false,
"raw_screenshots_or_logs_included": false
}
}
+15 -6
View File
@@ -33,22 +33,31 @@
"current_delivery": { "current_delivery": {
"score": null, "score": null,
"status": "not_scored", "status": "not_scored",
"reason": "Quant OS 已有可执行本地流水线、真实 Qlib fixture smoke、带校验但不完整的 Tushare→Qlib 技术 smoke,以及一次完成的真实聚宽 hosted smoke;但仍缺完整复权 PIT 数据、同输入聚宽比较、真实 QMT、券商影子盘、连续对账与合规证据,因此不能计入 Baseline 60。", "reason": "Quant OS 已接通本地 synthetic 五层候选链,能冻结 Ridge ModelBundle、逐层 trace 和无股数 TargetPackageV1;一个 synthetic-research TargetPackage 已在真实 JoinQuant hosted 环境完成消费、账户/开盘价绑定、3 笔委托和 3 笔成交,因此 execution 层已有真实平台观察。但真实 provider-data 长样本/OOS 尚未接入,JoinQuant 没有重跑上游 Universe/Alpha/Portfolio/RiskQMT peer 与逐层真实数据 parity 仍缺失,factor risk、CVXPY 和 guarded TWAP/POV 也尚未进入当前主链。因此它仍是 NOT_BASELINE_60G9 未通过。",
"verified_supporting_facts": [ "verified_supporting_facts": [
"The latest standard-library suite completed 224 tests with OK and 6 optional-runtime skips; the CPython 3.12 pyqlib environment completed the same 224 tests with the native Qlib smoke enabled and no skips.", "The current standard-library suite completed 285 tests with OK and 6 optional-runtime skips; successful local tests do not prove real-platform, broker or compliance gates.",
"The local synthetic research entrypoint now executes one causally linked Universe → Ridge Alpha → cost/risk-aware Portfolio → post-risk gate → reference Execution path, freezes a replayable ModelBundle, and publishes a hash-linked TargetPackageV1 and five-layer trace. This is structural synthetic evidence only.",
"The connected candidate currently uses 60-session diagonal realized variance, explicit spread/fee/square-root-impact cost forecasts, the deterministic constrained optimizer, a post-target risk gate, and reference lot/T+1 order deltas. factor_risk.py, the optional CVXPY solver mode, and guarded TWAP/POV are not selected by the current main slice.",
"JoinQuant and QMT target-package modes verify an exact embedded package tape, match the declared dual clock, bind platform equity/positions/sellable quantities/prices, and reject missing, duplicate or tampered packages without falling back to momentum. Both modes have local harness/contract evidence; JoinQuant execution alone now also has one narrow real hosted smoke.",
"JoinQuant backtest 2457a7c39a276e09e0fabf99e28978e1 ran the exact FINAL4 bundle for 2024-03-11 through 2024-03-13 with CNY 1,000,000, daily Python3 and CSI 500 benchmark. It observed 3/11 and 3/13 no-op, an exact package hit on 3/12 using CURRENT_DATA_DAY_OPEN, exact package weights, three accepted orders and three fills. The input evidence class remains synthetic-research; only execution.real_platform_observed is true, performance_claim is false, and this run earns no G9 or Baseline score.",
"The JQData adapter maps raw bars, adjustment factor, previous close, daily limits, pause state, PIT is_st and daily PIT index membership into a semantically revalidated snapshot; it rejects an end date that has not reached the provider's complete daily-bar boundary, and only fake-provider tests have been run so far.", "The JQData adapter maps raw bars, adjustment factor, previous close, daily limits, pause state, PIT is_st and daily PIT index membership into a semantically revalidated snapshot; it rejects an end date that has not reached the provider's complete daily-bar boundary, and only fake-provider tests have been run so far.",
"The local snapshot can drive snapshot-backtest and snapshot-decision with manifest verification; it freezes a cross-checked provider trading calendar, and a T-close signal can bind only to a broker observation in the unique next session's Asia/Shanghai [09:00,09:30) window without collapsing the two clocks.", "The local snapshot can drive snapshot-backtest and snapshot-decision with manifest verification; it freezes a cross-checked provider trading calendar, and a T-close signal can bind only to a broker observation in the unique next session's Asia/Shanghai [09:00,09:30) window without collapsing the two clocks.",
"CPython 3.12 with pyqlib 0.9.7 completed a native momentum fixture smoke with 24 signals and saved audited portfolio-table hashes, bounds and recomputed metrics.", "CPython 3.12 with pyqlib 0.9.7 completed a native momentum fixture smoke with 24 signals and saved audited portfolio-table hashes, bounds and recomputed metrics.",
"Qlib Alpha158, LightGBM and Recorder completed a real local fixture smoke with audited portfolio output; the deterministic two-stock synthetic fixture has no investment-value meaning.", "Qlib Alpha158, LightGBM and Recorder completed a real local fixture smoke with audited portfolio output; the deterministic two-stock synthetic fixture has no investment-value meaning.",
"The local Tushare inventory verified all 93 completed Parquet files against recorded size, SHA-256 and row count. An immutable managed Qlib provider and the same early-history momentum command produced byte-identical evidence JSON twice with PYTHONHASHSEED=0. The mirror is only about 8.2% complete for daily partitions and lacks adjustment, real benchmark, point-in-time membership, ST, suspension and limit data, so production_ready and investment_value_claim remain false and no gate credit is earned.", "The local Tushare inventory verified all 93 completed Parquet files against recorded size, SHA-256 and row count. An immutable managed Qlib provider and the same early-history momentum command produced byte-identical evidence JSON twice with PYTHONHASHSEED=0. The mirror is only about 8.2% complete for daily partitions and lacks adjustment, real benchmark, point-in-time membership, ST, suspension and limit data, so production_ready and investment_value_claim remain false and no gate credit is earned.",
"A real JoinQuant daily hosted backtest completed for 2024-01-02 through 2024-12-31 with CNY 1,000,000; the visible completed-run log had zero ERROR/Traceback/order-rejection entries. This proves hosted runtime execution only: exact input/plan/order/fill export and a local same-input peer are still missing.", "A real JoinQuant daily hosted backtest completed for 2024-01-02 through 2024-12-31 with CNY 1,000,000; the visible completed-run log had zero ERROR/Traceback/order-rejection entries. That run used portable_momentum_smoke, not the Ridge/TargetPackage Baseline candidate, and earns no candidate or cross-engine gate credit.",
"The QMT shadow CLI is strictly read-only and produces HMAC account-bound broker observation, broker snapshot and target-diff artifacts. QMT_SHADOW_EVIDENCE_V1 authenticates semantic content, deterministic published bytes, the original decision, planner/engine/operator source and fixed policy; semantic replay verifies exact derived state. Independent local QA completed 58 deterministic scenarios, 500 malformed fuzz cases and 300 legal-observation fuzz cases with no P0/P1. This is Quant OS publisher integrity, not broker attestation, and it still has only fake-broker evidence.", "The QMT shadow CLI is strictly read-only and produces HMAC account-bound broker observation, broker snapshot and target-diff artifacts. QMT_SHADOW_EVIDENCE_V1 authenticates semantic content, deterministic published bytes, the original decision, planner/engine/operator source and fixed policy; semantic replay verifies exact derived state. Independent local QA completed 58 deterministic scenarios, 500 malformed fuzz cases and 300 legal-observation fuzz cases with no P0/P1. This is Quant OS publisher integrity, not broker attestation, and it still has only fake-broker evidence.",
"JoinQuant/QMT mock parity is mock_contract_only, real_platform_pass is false and it earns no G9 gate credit." "JoinQuant/QMT mock parity is mock_contract_only, real_platform_pass is false and it earns no G9 gate credit."
], ],
"internal_delivery_gaps": [
"The authorized provider-data path has not yet been connected end to end to the frozen Ridge ModelBundle, calibrated risk/cost inputs, five-layer Baseline candidate and TargetPackage tape; the current connected candidate remains synthetic.",
"factor_risk.py, the optional CVXPY optimizer mode and guarded TWAP/POV exist as components or optional paths but are not selected by the current Baseline main slice; the connected risk model is 60-session diagonal realized variance and execution is the reference one-shot planner.",
"One synthetic-research TargetPackageV1 has been observed at the real JoinQuant execution boundary, but JoinQuant did not recompute the upstream four layers; there is no authorized real-data multi-period tape, same-input layer-by-layer parity, or real QMT peer."
],
"external_evidence_missing": [ "external_evidence_missing": [
"authorized JQData market snapshot and licence/lineage record", "authorized JQData market snapshot, licence/lineage record and a frozen provider-data candidate tape",
"complete JoinQuant hosted input/plan/order/fill export and same-input local comparison", "authorized real-data multi-period JoinQuant TargetPackage run plus same-input local/JoinQuant layer-by-layer reconciliation",
"real QMT built-in and qmttools backtest exports", "real QMT TargetPackage built-in and qmttools backtest exports",
"real QMT historical-ST positive control, terminal/xtquant build record, broker query contract and trade-amount semantic probes, callback/restart and at least 20 trading days of shadow evidence", "real QMT historical-ST positive control, terminal/xtquant build record, broker query contract and trade-amount semantic probes, callback/restart and at least 20 trading days of shadow evidence",
"continuous reconciliation, recovery drills and broker compliance confirmation" "continuous reconciliation, recovery drills and broker compliance confirmation"
] ]
+3 -1
View File
@@ -43,8 +43,10 @@ class FakeJoinQuantHarness(object):
is_st=False, is_st=False,
high_limit=9999.0, high_limit=9999.0,
low_limit=0.01, low_limit=0.01,
day_open=float(values[-1]),
last_price=float(values[-1]),
) )
for symbol in histories for symbol, values in self.histories.items()
} }
self.context = SimpleNamespace( self.context = SimpleNamespace(
current_dt=datetime.datetime(2026, 7, 20, 9, 30), current_dt=datetime.datetime(2026, 7, 20, 9, 30),
+670 -10
View File
@@ -6,9 +6,18 @@ tool replaces the marked import block with the exact portable core source.
""" """
# __PORTABLE_CORE_BUNDLE_START__ # __PORTABLE_CORE_BUNDLE_START__
from quant60.portable_core import build_rebalance_plan, convert_symbol from quant60.portable_core import (
build_rebalance_plan,
build_target_weight_plan,
convert_symbol,
lookup_target_package_exact,
target_package_tape_sha256,
verify_target_package,
)
# __PORTABLE_CORE_BUNDLE_END__ # __PORTABLE_CORE_BUNDLE_END__
import base64 as _q60_base64
import json as _q60_json
import math import math
@@ -26,6 +35,9 @@ class ClockStateError(RuntimeError):
# __BASELINE_CONFIG_START__ # __BASELINE_CONFIG_START__
CONFIG = { CONFIG = {
"decision_mode": "portable_momentum_smoke",
"target_package_count": 0,
"target_package_tape_sha256": None,
"universe_mode": "pit_index", "universe_mode": "pit_index",
"index_symbol": "000905.XSHG", "index_symbol": "000905.XSHG",
"qmt_sector_name": "中证500", "qmt_sector_name": "中证500",
@@ -56,12 +68,353 @@ CONFIG = {
} }
# __BASELINE_CONFIG_END__ # __BASELINE_CONFIG_END__
# __TARGET_PACKAGE_TAPE_START__
TARGET_PACKAGES = []
# __TARGET_PACKAGE_TAPE_END__
MOMENTUM_SMOKE_MODE = "portable_momentum_smoke"
TARGET_PACKAGE_MODE = "target_package"
EVIDENCE_PREFIX = "QUANT60_JOINQUANT_EVIDENCE_V1 "
EVIDENCE_CHUNK_CHARACTERS = 1800
TARGET_PRICE_SOURCE = "CURRENT_DATA_DAY_OPEN"
def _evidence_value(value, depth=0):
"""Return stable JSON data without allowing observation to stop trading."""
if depth > 8:
return "<max-depth>"
if value is None or isinstance(value, (bool, int, str)):
return value
if isinstance(value, float):
return value if math.isfinite(value) else None
if isinstance(value, dict):
return {
str(key): _evidence_value(item, depth + 1)
for key, item in sorted(
value.items(),
key=lambda pair: str(pair[0]),
)
}
if isinstance(value, (list, tuple)):
return [_evidence_value(item, depth + 1) for item in value]
isoformat = getattr(value, "isoformat", None)
if callable(isoformat):
try:
return str(isoformat())
except Exception:
return "<isoformat-error>"
try:
return str(value)
except Exception:
return "<string-error>"
def _evidence_context_time(context):
try:
return _evidence_value(getattr(context, "current_dt", None))
except Exception:
return None
def _emit_evidence(
event,
payload=None,
context=None,
chunk_index=None,
chunk_count=None,
):
"""Print one prefixed canonical JSON record and always fail soft."""
try:
sequence = int(
getattr(g, "quant60_evidence_sequence", 0) or 0
) + 1
g.quant60_evidence_sequence = sequence
record = {
"context_time": _evidence_context_time(context),
"decision_mode": getattr(
g,
"quant60_decision_mode",
CONFIG.get("decision_mode"),
),
"event": str(event),
"payload": _evidence_value(payload or {}),
"schema_version": "1.0",
"sequence": sequence,
"source": "joinquant_hosted",
}
if chunk_index is not None or chunk_count is not None:
if (
type(chunk_index) is not int
or type(chunk_count) is not int
or chunk_index <= 0
or chunk_count <= 0
or chunk_index > chunk_count
):
return None
record["chunk_index"] = chunk_index
record["chunk_count"] = chunk_count
encoded = _q60_json.dumps(
record,
ensure_ascii=True,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
)
print(EVIDENCE_PREFIX + encoded)
except Exception:
# Evidence is observational. A logging/serialization failure must
# never alter an already verified decision or hosted order path.
return None
return record
def _emit_chunked_evidence(event, payload, context=None):
"""Emit reconstructable base64 chunks of one canonical JSON payload."""
try:
canonical = _q60_json.dumps(
_evidence_value(payload),
ensure_ascii=True,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("ascii")
encoded = _q60_base64.b64encode(canonical).decode("ascii")
chunks = [
encoded[index : index + EVIDENCE_CHUNK_CHARACTERS]
for index in range(0, len(encoded), EVIDENCE_CHUNK_CHARACTERS)
] or [""]
count = len(chunks)
for index, content in enumerate(chunks, 1):
_emit_evidence(
event,
{
"chunk_encoding": "base64-canonical-json",
"content": content,
"encoded_length": len(encoded),
},
context,
chunk_index=index,
chunk_count=count,
)
except Exception:
return None
return count
def _observe_object(value, fields):
if value is None:
return None
output = {"object_type": type(value).__name__}
for name in fields:
try:
item = getattr(value, name)
except Exception:
continue
if callable(item):
continue
output[name] = _evidence_value(item)
return output
def _observe_order(value):
return _observe_object(
value,
(
"order_id",
"add_time",
"security",
"amount",
"filled",
"price",
"avg_cost",
"side",
"action",
"status",
"target_quantity",
),
)
def _observe_trade(value):
return _observe_object(
value,
(
"trade_id",
"order_id",
"time",
"security",
"amount",
"price",
),
)
def _observe_platform_collection(api_name, observer):
"""Read an optional hosted collection without assuming API availability."""
function = globals().get(api_name)
if not callable(function):
return {
"available": False,
"reason": "API_UNAVAILABLE",
}
try:
raw = function()
except Exception as exc:
return {
"available": False,
"error_type": type(exc).__name__,
"reason": "API_CALL_FAILED",
}
if hasattr(raw, "items"):
items = [
{
"key": str(key),
"value": observer(value),
}
for key, value in sorted(
raw.items(),
key=lambda pair: str(pair[0]),
)
]
elif isinstance(raw, (list, tuple)):
items = [observer(value) for value in raw]
elif raw is None:
items = []
else:
items = [observer(raw)]
return {
"available": True,
"count": len(items),
"items": items,
}
def _observe_portfolio(context):
try:
portfolio = context.portfolio
except Exception as exc:
return {
"available": False,
"error_type": type(exc).__name__,
}
output = {
"available": True,
"fields": {},
"positions": [],
}
for name in (
"total_value",
"cash",
"available_cash",
"positions_value",
"returns",
):
try:
value = getattr(portfolio, name)
except Exception:
continue
output["fields"][name] = _evidence_value(value)
try:
positions = getattr(portfolio, "positions", {})
if hasattr(positions, "items"):
for symbol, position in sorted(
positions.items(),
key=lambda pair: str(pair[0]),
):
item = _observe_object(
position,
(
"total_amount",
"closeable_amount",
"price",
"avg_cost",
"value",
),
)
item["symbol"] = str(symbol)
output["positions"].append(item)
except Exception as exc:
output["positions_error_type"] = type(exc).__name__
return output
def _plan_evidence(plan):
if not isinstance(plan, dict):
return None
return {
"decision_mode": plan.get("decision_mode"),
"execution_contract": plan.get("execution_contract"),
"lineage": plan.get("lineage"),
"orders": plan.get("orders"),
"targets": plan.get("targets"),
"universe": plan.get("universe"),
"weights": plan.get("weights"),
}
def _validated_target_packages(config):
mode = config.get("decision_mode")
if mode not in (MOMENTUM_SMOKE_MODE, TARGET_PACKAGE_MODE):
raise ValueError("unsupported decision_mode")
if not isinstance(TARGET_PACKAGES, list):
raise ValueError("TARGET_PACKAGES must be a list")
expected_count = config.get("target_package_count")
expected_sha256 = config.get("target_package_tape_sha256")
if mode == MOMENTUM_SMOKE_MODE:
if (
type(expected_count) is not int
or expected_count != 0
or expected_sha256 is not None
or TARGET_PACKAGES
):
raise ValueError(
"portable_momentum_smoke cannot carry target packages"
)
return []
if type(expected_count) is not int or expected_count <= 0:
raise ValueError(
"target_package mode requires a positive target_package_count"
)
if expected_count != len(TARGET_PACKAGES):
raise ValueError("target package count mismatch")
actual_sha256 = target_package_tape_sha256(TARGET_PACKAGES)
if expected_sha256 != actual_sha256:
raise ValueError("target package tape sha256 mismatch")
verified = []
package_ids = set()
decision_ids = set()
clocks = set()
for raw_package in TARGET_PACKAGES:
package = verify_target_package(raw_package)
package_id = package["package_id"]
decision_id = package["decision_id"]
clock = (package["signal_as_of"], package["next_session"])
if (
package_id in package_ids
or decision_id in decision_ids
or clock in clocks
):
raise ValueError("duplicate target package decision identity")
package_ids.add(package_id)
decision_ids.add(decision_id)
clocks.add(clock)
verified.append(package)
return verified
def initialize(context): def initialize(context):
"""Configure a daily-open clock that executes a first-week-close signal.""" """Configure the selected daily-open decision mode."""
set_option("avoid_future_data", True) set_option("avoid_future_data", True)
set_option("use_real_price", True) set_option("use_real_price", True)
g.quant60_evidence_sequence = 0
g.quant60_order_observations = []
g.quant60_config = dict(CONFIG) g.quant60_config = dict(CONFIG)
g.quant60_target_packages = _validated_target_packages(
g.quant60_config
)
g.quant60_decision_mode = g.quant60_config["decision_mode"]
if g.quant60_config["decision_mode"] == MOMENTUM_SMOKE_MODE:
if g.quant60_config.get("rebalance_schedule") != "weekly_first_close": if g.quant60_config.get("rebalance_schedule") != "weekly_first_close":
raise ValueError("unsupported rebalance_schedule") raise ValueError("unsupported rebalance_schedule")
if g.quant60_config.get("universe_mode") not in ("pit_index", "fixed"): if g.quant60_config.get("universe_mode") not in ("pit_index", "fixed"):
@@ -108,6 +461,30 @@ def initialize(context):
g.quant60_pending_first_session = None g.quant60_pending_first_session = None
g.quant60_last_callback_date = None g.quant60_last_callback_date = None
run_daily(rebalance, time="open") run_daily(rebalance, time="open")
_emit_evidence(
"INIT",
{
"benchmark": g.quant60_config["index_symbol"],
"package_count": len(g.quant60_target_packages),
"package_ids": [
package["package_id"]
for package in g.quant60_target_packages
],
"rebalance_callback": "run_daily_open",
"target_package_tape_sha256": g.quant60_config.get(
"target_package_tape_sha256"
),
},
context,
)
def _decision_mode():
configured = g.quant60_config.get("decision_mode")
frozen = getattr(g, "quant60_decision_mode", configured)
if configured != frozen:
raise ValueError("decision_mode cannot change after initialize")
return frozen
def _close_history(symbol, count): def _close_history(symbol, count):
@@ -262,7 +639,10 @@ def _snapshot_portfolio(context, managed):
current = {} current = {}
sellable = {} sellable = {}
managed = set(convert_symbol(symbol, "canonical") for symbol in managed) managed = set(convert_symbol(symbol, "canonical") for symbol in managed)
dynamic = g.quant60_config.get("universe_mode") == "pit_index" dynamic = (
_decision_mode() == MOMENTUM_SMOKE_MODE
and g.quant60_config.get("universe_mode") == "pit_index"
)
positions = getattr(context.portfolio, "positions", {}) positions = getattr(context.portfolio, "positions", {})
for symbol, position in positions.items(): for symbol, position in positions.items():
canonical = convert_symbol(str(symbol), "canonical") canonical = convert_symbol(str(symbol), "canonical")
@@ -342,8 +722,156 @@ def _order_request(symbol, delta):
return {"style": style} return {"style": style}
def _target_package_for_open(context):
current_date = _as_date(getattr(context, "current_dt", None), "current_dt")
previous_date = _as_date(
getattr(context, "previous_date", None),
"previous_date",
)
signal_as_of = (
previous_date.isoformat() + "T15:00:00+08:00"
)
next_session = current_date.isoformat()
config = g.quant60_config
packages = _validated_target_packages(config)
matches = [
package
for package in packages
if package["signal_as_of"] == signal_as_of
and package["next_session"] == next_session
]
if not matches:
_emit_evidence(
"TARGET_PACKAGE_NOOP",
{
"fallback_invoked": False,
"next_session": next_session,
"orders_submitted": 0,
"package_count": len(packages),
"reason": "NO_EXACT_CLOCK_MATCH",
"signal_as_of": signal_as_of,
"target_package_tape_sha256": config[
"target_package_tape_sha256"
],
},
context,
)
return None
if len(matches) != 1:
raise ValueError("ambiguous target package clock")
package = lookup_target_package_exact(
packages,
signal_as_of,
next_session,
decision_id=matches[0]["decision_id"],
expected_tape_sha256=config["target_package_tape_sha256"],
expected_count=config["target_package_count"],
)
_emit_evidence(
"TARGET_PACKAGE_HIT",
{
"lineage": _target_lineage(package),
"target_package_tape_sha256": config[
"target_package_tape_sha256"
],
},
context,
)
return package
def _target_prices(symbols):
try:
current_data = get_current_data()
except Exception as exc:
raise PriceHistoryUnavailable(
"current_data is unavailable: %s" % exc
)
prices = {}
for canonical in sorted(symbols):
symbol = convert_symbol(canonical, "joinquant")
try:
raw_price = getattr(current_data[symbol], "day_open")
price = float(raw_price)
except (KeyError, AttributeError, TypeError, ValueError):
raise PriceHistoryUnavailable(
"%s has no usable current day_open" % symbol
)
if not math.isfinite(price) or price <= 0.0:
raise PriceHistoryUnavailable(
"%s has no usable current day_open" % symbol
)
prices[canonical] = price
return prices
def _target_lineage(package):
return {
"package_id": package["package_id"],
"package_sha256": package["package_sha256"],
"decision_id": package["decision_id"],
"signal_as_of": package["signal_as_of"],
"next_session": package["next_session"],
"release": package["release"],
"model_id": package["model_id"],
}
def _compute_target_package_plan(context):
package = _target_package_for_open(context)
if package is None:
return None
managed = sorted(package["universe"])
current, sellable, equity = _snapshot_portfolio(context, managed)
if equity <= 0:
raise ValueError("portfolio.total_value must be positive")
prices = _target_prices(
set(package["target_weights"]) | set(current)
)
_emit_evidence(
"PLATFORM_BINDING_INPUT",
{
"current": current,
"equity": equity,
"lineage": _target_lineage(package),
"price_source": TARGET_PRICE_SOURCE,
"prices": prices,
"sellable": sellable,
},
context,
)
plan = build_target_weight_plan(
target_weights=package["target_weights"],
prices=prices,
current=current,
sellable=sellable,
equity=equity,
cash_buffer=g.quant60_config["cash_buffer"],
lot_size=g.quant60_config["lot_size"],
decision_id=package["decision_id"],
package_sha256=package["package_sha256"],
)
plan["decision_mode"] = TARGET_PACKAGE_MODE
plan["lineage"] = _target_lineage(package)
plan["universe"] = package["universe"]
plan["execution_contract"] = {
"signal_price_source": TARGET_PRICE_SOURCE,
"submit_trigger": "DECLARED_NEXT_SESSION_OPEN_CALLBACK",
"declared_next_session": package["next_session"],
"real_platform_observed": False,
}
_emit_evidence(
"TARGET_PACKAGE_PLAN",
_plan_evidence(plan),
context,
)
return plan
def compute_plan(context): def compute_plan(context):
"""Return the portable plan without submitting orders.""" """Return the selected portable plan without submitting orders."""
if _decision_mode() == TARGET_PACKAGE_MODE:
return _compute_target_package_plan(context)
config = g.quant60_config config = g.quant60_config
required = int(config["lookback"]) + int(config["skip"]) + 1 required = int(config["lookback"]) + int(config["skip"]) + 1
platform_universe = _decision_universe(context) platform_universe = _decision_universe(context)
@@ -375,13 +903,17 @@ def compute_plan(context):
return plan return plan
def _execute_rebalance(context): def _submit_plan(context, plan, managed):
"""Submit the portable plan's exact executable share deltas.""" """Submit a portable plan's exact executable share deltas."""
plan = compute_plan(context) if plan is None:
return None
observe_target_package = (
plan.get("decision_mode") == TARGET_PACKAGE_MODE
)
order_deltas = plan["orders"] order_deltas = plan["orders"]
current, _, unused_equity = _snapshot_portfolio( current, _, unused_equity = _snapshot_portfolio(
context, context,
g.quant60_last_universe or g.quant60_config["universe"], managed,
) )
del unused_equity del unused_equity
all_symbols = set(current) all_symbols = set(current)
@@ -401,23 +933,107 @@ def _execute_rebalance(context):
if delta == 0: if delta == 0:
continue continue
platform_symbol = convert_symbol(canonical, "joinquant") platform_symbol = convert_symbol(canonical, "joinquant")
skipped_before = len(g.quant60_skipped_orders)
request = _order_request(platform_symbol, delta) request = _order_request(platform_symbol, delta)
if request is None: if request is None:
skipped = g.quant60_skipped_orders[skipped_before:]
if not skipped:
skipped = [
{
"symbol": platform_symbol,
"reason": "ORDER_REQUEST_NOT_BUILT",
}
]
if observe_target_package:
for item in skipped:
_emit_evidence(
"SKIPPED_ORDER",
{
"canonical_symbol": canonical,
"delta": delta,
"lineage": plan.get("lineage"),
"skip": item,
},
context,
)
continue continue
target = int(current.get(canonical, 0)) + delta target = int(current.get(canonical, 0)) + delta
request_evidence = {
"canonical_symbol": canonical,
"current_quantity": int(current.get(canonical, 0)),
"delta": delta,
"lineage": plan.get("lineage"),
"platform_symbol": platform_symbol,
"style": _observe_object(
request["style"],
("kind", "limit_price"),
),
"target_quantity": target,
}
if observe_target_package:
_emit_evidence(
"ORDER_REQUEST",
request_evidence,
context,
)
try:
if request["style"] is None: if request["style"] is None:
order_target(platform_symbol, target) order_result = order_target(platform_symbol, target)
else: else:
order_target( order_result = order_target(
platform_symbol, platform_symbol,
target, target,
style=request["style"], style=request["style"],
) )
except Exception as exc:
if observe_target_package:
_emit_evidence(
"ORDER_RETURN",
{
"error_type": type(exc).__name__,
"lineage": plan.get("lineage"),
"outcome": "EXCEPTION",
"platform_symbol": platform_symbol,
"target_quantity": target,
},
context,
)
raise
observation = {
"lineage": plan.get("lineage"),
"order": _observe_order(order_result),
"outcome": "RETURNED",
"platform_symbol": platform_symbol,
"target_quantity": target,
}
if observe_target_package:
g.quant60_order_observations.append(observation)
_emit_evidence(
"ORDER_RETURN",
observation,
context,
)
g.quant60_last_plan = plan g.quant60_last_plan = plan
return plan return plan
def _execute_rebalance(context):
"""Submit the selected portable plan's exact executable share deltas."""
plan = compute_plan(context)
if plan is None:
return None
managed = (
sorted(plan["universe"])
if "universe" in plan
else (
g.quant60_last_universe
or g.quant60_config["universe"]
)
)
return _submit_plan(context, plan, managed)
def _as_date(value, name): def _as_date(value, name):
if hasattr(value, "date"): if hasattr(value, "date"):
value = value.date() value = value.date()
@@ -439,6 +1055,16 @@ def rebalance(context):
) )
if g.quant60_last_callback_date == current_date: if g.quant60_last_callback_date == current_date:
return None return None
if _decision_mode() == TARGET_PACKAGE_MODE:
# EOD evidence is session-scoped. Clear the prior session only after
# the duplicate-date guard so a same-day retry cannot erase evidence.
g.quant60_last_plan = None
g.quant60_order_observations = []
g.quant60_skipped_orders = []
# Consume the daily clock token before crossing the hosted order
# boundary so callback retries cannot duplicate an accepted prefix.
g.quant60_last_callback_date = current_date
return _execute_rebalance(context)
pending = g.quant60_pending_first_session pending = g.quant60_pending_first_session
is_first_session = ( is_first_session = (
previous_date.isocalendar()[:2] previous_date.isocalendar()[:2]
@@ -463,3 +1089,37 @@ def rebalance(context):
g.quant60_pending_first_session = current_date g.quant60_pending_first_session = current_date
g.quant60_last_callback_date = current_date g.quant60_last_callback_date = current_date
return None return None
def after_trading_end(context):
"""Emit a best-effort end-of-day observation without changing state."""
if _decision_mode() != TARGET_PACKAGE_MODE:
return None
return _emit_chunked_evidence(
"EOD_STATUS",
{
"last_plan": _plan_evidence(
getattr(g, "quant60_last_plan", None)
),
"order_returns": getattr(
g,
"quant60_order_observations",
[],
),
"orders_api": _observe_platform_collection(
"get_orders",
_observe_order,
),
"portfolio": _observe_portfolio(context),
"skipped_orders": getattr(
g,
"quant60_skipped_orders",
[],
),
"trades_api": _observe_platform_collection(
"get_trades",
_observe_trade,
),
},
context,
)
+295 -2
View File
@@ -2,7 +2,14 @@
"""QMT built-in Python 3.6 strategy wrapper. Keep this source ASCII-only.""" """QMT built-in Python 3.6 strategy wrapper. Keep this source ASCII-only."""
# __PORTABLE_CORE_BUNDLE_START__ # __PORTABLE_CORE_BUNDLE_START__
from quant60.portable_core import build_rebalance_plan, convert_symbol from quant60.portable_core import (
build_rebalance_plan,
build_target_weight_plan,
convert_symbol,
lookup_target_package_exact,
target_package_tape_sha256,
verify_target_package,
)
# __PORTABLE_CORE_BUNDLE_END__ # __PORTABLE_CORE_BUNDLE_END__
import datetime import datetime
@@ -34,6 +41,9 @@ g = G()
# __BASELINE_CONFIG_START__ # __BASELINE_CONFIG_START__
CONFIG = { CONFIG = {
"account_id": "test", "account_id": "test",
"decision_mode": "portable_momentum_smoke",
"target_package_count": 0,
"target_package_tape_sha256": None,
"universe_mode": "pit_index", "universe_mode": "pit_index",
"index_symbol": "000905.XSHG", "index_symbol": "000905.XSHG",
"qmt_sector_name": "\u4e2d\u8bc1500", "qmt_sector_name": "\u4e2d\u8bc1500",
@@ -63,6 +73,76 @@ CONFIG = {
} }
# __BASELINE_CONFIG_END__ # __BASELINE_CONFIG_END__
# __TARGET_PACKAGE_TAPE_START__
TARGET_PACKAGES = []
# __TARGET_PACKAGE_TAPE_END__
MOMENTUM_SMOKE_MODE = "portable_momentum_smoke"
TARGET_PACKAGE_MODE = "target_package"
def _validated_target_packages(config):
mode = config.get("decision_mode")
if mode not in (MOMENTUM_SMOKE_MODE, TARGET_PACKAGE_MODE):
raise UnsupportedStrategyPeriod("unsupported decision_mode")
if not isinstance(TARGET_PACKAGES, list):
raise UnsupportedStrategyPeriod(
"TARGET_PACKAGES must be a list"
)
expected_count = config.get("target_package_count")
expected_sha256 = config.get("target_package_tape_sha256")
if mode == MOMENTUM_SMOKE_MODE:
if (
type(expected_count) is not int
or expected_count != 0
or expected_sha256 is not None
or TARGET_PACKAGES
):
raise UnsupportedStrategyPeriod(
"portable_momentum_smoke cannot carry target packages"
)
return []
if type(expected_count) is not int or expected_count <= 0:
raise UnsupportedStrategyPeriod(
"target_package mode requires a positive target_package_count"
)
if expected_count != len(TARGET_PACKAGES):
raise UnsupportedStrategyPeriod(
"target package count mismatch"
)
actual_sha256 = target_package_tape_sha256(TARGET_PACKAGES)
if expected_sha256 != actual_sha256:
raise UnsupportedStrategyPeriod(
"target package tape sha256 mismatch"
)
verified = []
package_ids = set()
decision_ids = set()
clocks = set()
signal_clocks = set()
for raw_package in TARGET_PACKAGES:
package = verify_target_package(raw_package)
package_id = package["package_id"]
decision_id = package["decision_id"]
clock = (package["signal_as_of"], package["next_session"])
signal_as_of = package["signal_as_of"]
if (
package_id in package_ids
or decision_id in decision_ids
or clock in clocks
or signal_as_of in signal_clocks
):
raise UnsupportedStrategyPeriod(
"duplicate or ambiguous target package decision identity"
)
package_ids.add(package_id)
decision_ids.add(decision_id)
clocks.add(clock)
signal_clocks.add(signal_as_of)
verified.append(package)
return verified
def _param(ContextInfo, name, default=None): def _param(ContextInfo, name, default=None):
params = getattr(ContextInfo, "_param", {}) params = getattr(ContextInfo, "_param", {})
@@ -84,6 +164,9 @@ def init(ContextInfo):
override = _param(ContextInfo, "q60_" + key, None) override = _param(ContextInfo, "q60_" + key, None)
if override is not None: if override is not None:
g.config[key] = override g.config[key] = override
g.target_packages = _validated_target_packages(g.config)
g.decision_mode = g.config["decision_mode"]
if g.config["decision_mode"] == MOMENTUM_SMOKE_MODE:
if ( if (
g.config.get("rebalance_schedule") g.config.get("rebalance_schedule")
!= "weekly_first_close" != "weekly_first_close"
@@ -129,6 +212,7 @@ def init(ContextInfo):
g.last_plan = None g.last_plan = None
g.last_universe = None g.last_universe = None
g.last_universe_as_of = None g.last_universe_as_of = None
g.last_target_signal_as_of = None
if ( if (
g.config.get("universe_mode") == "fixed" g.config.get("universe_mode") == "fixed"
and hasattr(ContextInfo, "set_universe") and hasattr(ContextInfo, "set_universe")
@@ -136,6 +220,16 @@ def init(ContextInfo):
ContextInfo.set_universe(g.config["universe"]) ContextInfo.set_universe(g.config["universe"])
def _decision_mode():
configured = g.config.get("decision_mode")
frozen = getattr(g, "decision_mode", configured)
if configured != frozen:
raise UnsupportedStrategyPeriod(
"decision_mode cannot change after init"
)
return frozen
def _bar_datetime(ContextInfo): def _bar_datetime(ContextInfo):
timetag = ContextInfo.get_bar_timetag(ContextInfo.barpos) timetag = ContextInfo.get_bar_timetag(ContextInfo.barpos)
return datetime.datetime.fromtimestamp(float(timetag) / 1000.0) return datetime.datetime.fromtimestamp(float(timetag) / 1000.0)
@@ -312,7 +406,10 @@ def _portfolio(ContextInfo, managed):
convert_symbol(symbol, "canonical") convert_symbol(symbol, "canonical")
for symbol in managed for symbol in managed
) )
dynamic = g.config.get("universe_mode") == "pit_index" dynamic = (
_decision_mode() == MOMENTUM_SMOKE_MODE
and g.config.get("universe_mode") == "pit_index"
)
for position in _trade_details(ContextInfo, "position") or []: for position in _trade_details(ContextInfo, "position") or []:
code = str( code = str(
_get_attr(position, ["stock_code", "m_strInstrumentID"], "") _get_attr(position, ["stock_code", "m_strInstrumentID"], "")
@@ -358,6 +455,8 @@ def _portfolio(ContextInfo, managed):
def compute_plan(ContextInfo, bar_time=None): def compute_plan(ContextInfo, bar_time=None):
if _decision_mode() == TARGET_PACKAGE_MODE:
return _compute_target_package_plan(ContextInfo, bar_time)
if bar_time is None: if bar_time is None:
bar_time = _bar_datetime(ContextInfo) bar_time = _bar_datetime(ContextInfo)
membership_timetag = int( membership_timetag = int(
@@ -390,6 +489,136 @@ def compute_plan(ContextInfo, bar_time=None):
) )
def _signal_as_of(bar_time):
if (
int(bar_time.hour) != 15
or int(bar_time.minute) != 0
or int(bar_time.second) != 0
or int(bar_time.microsecond) != 0
):
raise UnsupportedStrategyPeriod(
"target_package mode requires the completed 15:00 daily close"
)
return bar_time.strftime("%Y-%m-%dT15:00:00+08:00")
def _target_package_for_close(bar_time):
signal_as_of = _signal_as_of(bar_time)
config = g.config
packages = _validated_target_packages(config)
matches = [
package
for package in packages
if package["signal_as_of"] == signal_as_of
]
if not matches:
return None
if len(matches) != 1:
raise UnsupportedStrategyPeriod(
"ambiguous target package signal clock"
)
package = matches[0]
return lookup_target_package_exact(
packages,
signal_as_of,
package["next_session"],
decision_id=package["decision_id"],
expected_tape_sha256=config["target_package_tape_sha256"],
expected_count=config["target_package_count"],
)
def _target_close_prices(ContextInfo, bar_time, symbols):
platform_symbols = [
convert_symbol(symbol, "qmt")
for symbol in sorted(symbols)
]
raw = ContextInfo.get_market_data_ex(
fields=["close"],
stock_code=platform_symbols,
period="1d",
start_time="",
end_time=bar_time.strftime("%Y%m%d"),
count=1,
dividend_type="front_ratio",
fill_data=False,
subscribe=False,
)
if not hasattr(raw, "get"):
raise PriceHistoryUnavailable(
"target close query returned an invalid payload"
)
prices = {}
for symbol in platform_symbols:
values = _series_close(raw.get(symbol))
if len(values) != 1:
raise PriceHistoryUnavailable(
"%s needs one completed daily close" % symbol
)
try:
price = float(values[0])
except (TypeError, ValueError):
raise PriceHistoryUnavailable(
"%s contains a missing/non-numeric close" % symbol
)
if not math.isfinite(price) or price <= 0.0:
raise PriceHistoryUnavailable(
"%s contains a missing/non-positive close" % symbol
)
prices[convert_symbol(symbol, "canonical")] = price
return prices
def _target_lineage(package):
return {
"package_id": package["package_id"],
"package_sha256": package["package_sha256"],
"decision_id": package["decision_id"],
"signal_as_of": package["signal_as_of"],
"next_session": package["next_session"],
"release": package["release"],
"model_id": package["model_id"],
}
def _compute_target_package_plan(ContextInfo, bar_time=None):
if bar_time is None:
bar_time = _bar_datetime(ContextInfo)
package = _target_package_for_close(bar_time)
if package is None:
return None
managed = sorted(package["universe"])
current, sellable, equity = _portfolio(ContextInfo, managed)
if equity <= 0:
raise ValueError("QMT account equity must be positive")
plan = build_target_weight_plan(
target_weights=package["target_weights"],
prices=_target_close_prices(
ContextInfo,
bar_time,
set(package["target_weights"]) | set(current),
),
current=current,
sellable=sellable,
equity=equity,
cash_buffer=g.config["cash_buffer"],
lot_size=g.config["lot_size"],
decision_id=package["decision_id"],
package_sha256=package["package_sha256"],
)
plan["decision_mode"] = TARGET_PACKAGE_MODE
plan["lineage"] = _target_lineage(package)
plan["universe"] = package["universe"]
plan["execution_contract"] = {
"signal_price_source": "COMPLETED_DAILY_CLOSE",
"submit_trigger": "NEXT_BAR_FIRST_TICK",
"quick_trade": 0,
"declared_next_session": package["next_session"],
"real_platform_observed": False,
}
return plan
def _orders_allowed(ContextInfo): def _orders_allowed(ContextInfo):
# This hosted wrapper is deliberately backtest-only. Its cached account # This hosted wrapper is deliberately backtest-only. Its cached account
# query and in-memory weekly marker cannot provide crash-safe live # query and in-memory weekly marker cannot provide crash-safe live
@@ -452,12 +681,76 @@ def _submit_delta(ContextInfo, canonical, delta, week_key):
) )
def _submit_target_delta(ContextInfo, canonical, delta, package_sha256):
config = g.config
order_code = convert_symbol(canonical, "qmt")
operation = 23 if delta > 0 else 24
volume = abs(int(delta))
user_order_id = "q60t-%s-%s" % (
str(package_sha256)[:8],
order_code.split(".")[0],
)
function = globals()["passorder"]
arg_count = getattr(getattr(function, "__code__", None), "co_argcount", 11)
if arg_count <= 8:
function(
operation,
1101,
config["account_id"],
order_code,
5,
-1,
volume,
ContextInfo,
)
else:
function(
operation,
1101,
config["account_id"],
order_code,
5,
-1,
volume,
"quant60",
0,
user_order_id,
ContextInfo,
)
def handlebar(ContextInfo): def handlebar(ContextInfo):
if not _is_backtest(ContextInfo) and hasattr(ContextInfo, "is_last_bar"): if not _is_backtest(ContextInfo) and hasattr(ContextInfo, "is_last_bar"):
if not ContextInfo.is_last_bar(): if not ContextInfo.is_last_bar():
return None return None
bar_time = _bar_datetime(ContextInfo) bar_time = _bar_datetime(ContextInfo)
if _decision_mode() == TARGET_PACKAGE_MODE:
signal_as_of = _signal_as_of(bar_time)
if g.last_target_signal_as_of == signal_as_of:
return None
plan = compute_plan(ContextInfo, bar_time)
# Consume the clock before crossing passorder so a callback retry
# cannot duplicate an already accepted order prefix.
g.last_target_signal_as_of = signal_as_of
if plan is None:
return None
g.last_plan = plan
if not _orders_allowed(ContextInfo):
return plan
ordered = sorted(
plan["orders"].items(),
key=lambda item: item[1],
)
for canonical, delta in ordered:
if int(delta) != 0:
_submit_target_delta(
ContextInfo,
canonical,
delta,
plan["lineage"]["package_sha256"],
)
return plan
year, week, unused = bar_time.isocalendar() year, week, unused = bar_time.isocalendar()
del unused del unused
week_key = (year, week) week_key = (year, week)
File diff suppressed because one or more lines are too long
+190
View File
@@ -0,0 +1,190 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://boat.local/quant60/schemas/model_bundle.schema.json",
"title": "Quant60 deterministic Ridge model bundle",
"description": "Frozen train-only feature transforms, Ridge parameters, training cutoff, label semantics, and lineage hashes for deterministic replay.",
"type": "object",
"additionalProperties": false,
"required": [
"schema_version",
"artifact_type",
"bundle_sha256",
"trained_through",
"label",
"lineage",
"preprocessor",
"model"
],
"properties": {
"schema_version": {
"const": "1.0"
},
"artifact_type": {
"const": "quant60-deterministic-ridge-bundle"
},
"bundle_sha256": {
"$ref": "#/$defs/sha256"
},
"trained_through": {
"type": "string",
"format": "date"
},
"label": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"horizon_trading_days"
],
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"horizon_trading_days": {
"type": "integer",
"minimum": 1
}
}
},
"lineage": {
"type": "object",
"additionalProperties": false,
"required": [
"data_sha256",
"source_sha256",
"config_sha256"
],
"properties": {
"data_sha256": {
"$ref": "#/$defs/sha256"
},
"source_sha256": {
"$ref": "#/$defs/sha256"
},
"config_sha256": {
"$ref": "#/$defs/sha256"
}
}
},
"preprocessor": {
"type": "object",
"additionalProperties": false,
"required": [
"type",
"winsor_fraction",
"feature_order",
"output_feature_order",
"transforms"
],
"properties": {
"type": {
"const": "train-only-winsor-impute-standardize"
},
"winsor_fraction": {
"type": "number",
"minimum": 0,
"maximum": 0.5
},
"feature_order": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "string",
"minLength": 1
}
},
"output_feature_order": {
"type": "array",
"minItems": 2,
"uniqueItems": true,
"items": {
"type": "string",
"minLength": 1
}
},
"transforms": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/feature_transform"
}
}
}
},
"model": {
"type": "object",
"additionalProperties": false,
"required": [
"type",
"alpha",
"fit_intercept",
"coefficients",
"intercept"
],
"properties": {
"type": {
"const": "deterministic-ridge"
},
"alpha": {
"type": "number",
"minimum": 0
},
"fit_intercept": {
"type": "boolean"
},
"coefficients": {
"type": "array",
"minItems": 1,
"items": {
"type": "number"
}
},
"intercept": {
"type": "number"
}
}
}
},
"$defs": {
"sha256": {
"type": "string",
"pattern": "^[0-9a-f]{64}$"
},
"feature_transform": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"lower",
"upper",
"median",
"mean",
"scale"
],
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"lower": {
"type": "number"
},
"upper": {
"type": "number"
},
"median": {
"type": "number"
},
"mean": {
"type": "number"
},
"scale": {
"type": "number",
"exclusiveMinimum": 0
}
}
}
}
}
+221
View File
@@ -0,0 +1,221 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://boat.local/quant60/schemas/release_reachability.schema.json",
"title": "Quant60 Release Reachability",
"description": "Per-layer evidence for implementation, tests, entrypoint reachability and real-platform observation. A repository source hash is not runtime evidence.",
"type": "object",
"additionalProperties": false,
"required": [
"schema_version",
"artifact_type",
"release_id",
"generated_at",
"layers",
"summary",
"manifest_sha256"
],
"properties": {
"schema_version": {
"const": "1.0"
},
"artifact_type": {
"const": "quant60-release-reachability"
},
"release_id": {
"$ref": "#/$defs/identifier"
},
"generated_at": {
"type": "string",
"format": "date-time",
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$"
},
"layers": {
"$ref": "#/$defs/layers"
},
"summary": {
"$ref": "#/$defs/summary"
},
"manifest_sha256": {
"$ref": "#/$defs/sha256"
}
},
"$defs": {
"identifier": {
"type": "string",
"minLength": 1,
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
},
"sha256": {
"type": "string",
"pattern": "^[0-9a-f]{64}$"
},
"artifact": {
"type": "object",
"additionalProperties": false,
"required": [
"artifact_id",
"kind",
"path",
"sha256",
"platform"
],
"properties": {
"artifact_id": {
"$ref": "#/$defs/identifier"
},
"kind": {
"type": "string",
"enum": [
"source",
"test",
"entrypoint",
"platform_run"
]
},
"path": {
"type": "string",
"minLength": 1
},
"sha256": {
"$ref": "#/$defs/sha256"
},
"platform": {
"type": "string",
"enum": [
"repository",
"local",
"joinquant",
"qmt"
]
},
"observed_at": {
"type": "string",
"format": "date-time",
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$"
},
"run_id": {
"$ref": "#/$defs/identifier"
}
},
"dependentRequired": {
"observed_at": [
"run_id"
],
"run_id": [
"observed_at"
]
}
},
"claim": {
"type": "object",
"additionalProperties": false,
"required": [
"status",
"artifacts"
],
"properties": {
"status": {
"type": "boolean"
},
"artifacts": {
"type": "array",
"items": {
"$ref": "#/$defs/artifact"
}
}
}
},
"layer": {
"type": "object",
"additionalProperties": false,
"required": [
"implemented",
"unit_tested",
"local_entrypoint_reachable",
"jq_entrypoint_reachable",
"qmt_entrypoint_reachable",
"real_platform_observed"
],
"properties": {
"implemented": {
"$ref": "#/$defs/claim"
},
"unit_tested": {
"$ref": "#/$defs/claim"
},
"local_entrypoint_reachable": {
"$ref": "#/$defs/claim"
},
"jq_entrypoint_reachable": {
"$ref": "#/$defs/claim"
},
"qmt_entrypoint_reachable": {
"$ref": "#/$defs/claim"
},
"real_platform_observed": {
"$ref": "#/$defs/claim"
}
}
},
"layers": {
"type": "object",
"additionalProperties": false,
"required": [
"universe",
"alpha",
"portfolio",
"risk",
"execution"
],
"properties": {
"universe": {
"$ref": "#/$defs/layer"
},
"alpha": {
"$ref": "#/$defs/layer"
},
"portfolio": {
"$ref": "#/$defs/layer"
},
"risk": {
"$ref": "#/$defs/layer"
},
"execution": {
"$ref": "#/$defs/layer"
}
}
},
"summary": {
"type": "object",
"additionalProperties": false,
"required": [
"all_layers_implemented",
"all_layers_unit_tested",
"all_layers_local_entrypoint_reachable",
"all_layers_jq_entrypoint_reachable",
"all_layers_qmt_entrypoint_reachable",
"all_layers_real_platform_observed"
],
"properties": {
"all_layers_implemented": {
"type": "boolean"
},
"all_layers_unit_tested": {
"type": "boolean"
},
"all_layers_local_entrypoint_reachable": {
"type": "boolean"
},
"all_layers_jq_entrypoint_reachable": {
"type": "boolean"
},
"all_layers_qmt_entrypoint_reachable": {
"type": "boolean"
},
"all_layers_real_platform_observed": {
"type": "boolean"
}
}
}
}
}
+205
View File
@@ -0,0 +1,205 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://boat.local/quant60/schemas/target_package.schema.json",
"title": "Quant60 TargetPackageV1",
"description": "Canonical post-risk portfolio weights. Share quantities, orders, fills and account facts are forbidden and belong to the downstream execution plan.",
"type": "object",
"additionalProperties": false,
"required": [
"schema_version",
"package_type",
"package_id",
"decision_id",
"release",
"evidence_class",
"model_id",
"signal_as_of",
"next_session",
"model_sha256",
"data_sha256",
"feature_sha256",
"risk_sha256",
"cost_sha256",
"optimizer_sha256",
"config_sha256",
"source_sha256",
"target_weights",
"universe",
"constraints",
"package_sha256"
],
"properties": {
"schema_version": {
"const": "1.0"
},
"package_type": {
"const": "TargetPackageV1"
},
"package_id": {
"$ref": "#/$defs/identifier"
},
"decision_id": {
"$ref": "#/$defs/identifier"
},
"release": {
"$ref": "#/$defs/identifier"
},
"evidence_class": {
"$ref": "#/$defs/identifier"
},
"model_id": {
"$ref": "#/$defs/identifier"
},
"signal_as_of": {
"type": "string",
"format": "date-time",
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T15:00:00\\+08:00$"
},
"next_session": {
"type": "string",
"format": "date"
},
"model_sha256": {
"$ref": "#/$defs/sha256"
},
"data_sha256": {
"$ref": "#/$defs/sha256"
},
"feature_sha256": {
"$ref": "#/$defs/sha256"
},
"risk_sha256": {
"$ref": "#/$defs/sha256"
},
"cost_sha256": {
"$ref": "#/$defs/sha256"
},
"optimizer_sha256": {
"$ref": "#/$defs/sha256"
},
"config_sha256": {
"$ref": "#/$defs/sha256"
},
"source_sha256": {
"$ref": "#/$defs/sha256"
},
"target_weights": {
"type": "object",
"minProperties": 1,
"propertyNames": {
"$ref": "#/$defs/canonical_symbol"
},
"additionalProperties": {
"type": "number",
"minimum": 0,
"maximum": 1
}
},
"universe": {
"type": "object",
"minProperties": 1,
"propertyNames": {
"$ref": "#/$defs/canonical_symbol"
},
"additionalProperties": {
"$ref": "#/$defs/universe_member"
}
},
"constraints": {
"$ref": "#/$defs/constraints"
},
"package_sha256": {
"$ref": "#/$defs/sha256"
}
},
"$defs": {
"identifier": {
"type": "string",
"minLength": 1,
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
},
"sha256": {
"type": "string",
"pattern": "^[0-9a-f]{64}$"
},
"canonical_symbol": {
"type": "string",
"pattern": "^[0-9]{6}\\.(XSHG|XSHE|XBSE)$"
},
"universe_member": {
"type": "object",
"additionalProperties": false,
"required": [
"state",
"reasons"
],
"properties": {
"state": {
"type": "string",
"enum": [
"openable",
"hold_only",
"sell_only",
"frozen",
"excluded"
]
},
"reasons": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9_]*$"
}
}
}
},
"constraints": {
"type": "object",
"additionalProperties": false,
"required": [
"constraint_state",
"long_only",
"max_single_weight",
"max_gross_weight",
"max_one_way_turnover",
"actual_gross_weight"
],
"properties": {
"constraint_state": {
"type": "string",
"enum": [
"FEASIBLE",
"REPAIRED",
"RISK_REDUCTION_ONLY",
"NO_TRADE"
]
},
"long_only": {
"const": true
},
"max_single_weight": {
"type": "number",
"exclusiveMinimum": 0,
"maximum": 1
},
"max_gross_weight": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"max_one_way_turnover": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"actual_gross_weight": {
"type": "number",
"minimum": 0,
"maximum": 1
}
}
}
}
}
+502
View File
@@ -0,0 +1,502 @@
"""Canonical five-layer decision pipeline for a Baseline candidate.
This module is deliberately platform-neutral. Hosted adapters may consume its
post-risk target weights, but they must not silently replace any layer with the
portable momentum smoke strategy.
"""
from __future__ import annotations
import math
from dataclasses import dataclass
from typing import Any, Iterable, Mapping
from .ledger import canonical_json
from .optimizer import (
PortfolioCandidate,
PortfolioConstraints,
PortfolioSolution,
optimize_cvxpy,
optimize_deterministic,
)
from .portable_core import normalize_symbol, order_deltas, target_quantities
from .risk import RiskDecision, check_target_weights
from .universe import UniverseState
FIVE_LAYER_ORDER = (
"universe",
"alpha",
"portfolio",
"risk",
"execution",
)
class BaselinePipelineError(ValueError):
"""The canonical Baseline decision cannot be built safely."""
def _finite(value: float, name: str) -> float:
numeric = float(value)
if not math.isfinite(numeric):
raise BaselinePipelineError(f"{name} must be finite")
return numeric
def _sha256(payload: Any) -> str:
import hashlib
return hashlib.sha256(canonical_json(payload).encode("utf-8")).hexdigest()
@dataclass(frozen=True, slots=True)
class AlphaForecastInput:
symbol: str
expected_excess_return: float
model_version: str
feature_version: str
def __post_init__(self) -> None:
object.__setattr__(self, "symbol", normalize_symbol(self.symbol))
_finite(self.expected_excess_return, "expected_excess_return")
if not str(self.model_version).strip():
raise BaselinePipelineError("model_version is required")
if not str(self.feature_version).strip():
raise BaselinePipelineError("feature_version is required")
@dataclass(frozen=True, slots=True)
class RiskForecastInput:
symbol: str
predicted_variance: float
risk_model_version: str
def __post_init__(self) -> None:
object.__setattr__(self, "symbol", normalize_symbol(self.symbol))
variance = _finite(self.predicted_variance, "predicted_variance")
if variance <= 0:
raise BaselinePipelineError("predicted_variance must be positive")
if not str(self.risk_model_version).strip():
raise BaselinePipelineError("risk_model_version is required")
@dataclass(frozen=True, slots=True)
class CostForecastInput:
symbol: str
base_bps: float
max_adv_weight: float
cost_model_version: str
def __post_init__(self) -> None:
object.__setattr__(self, "symbol", normalize_symbol(self.symbol))
base_bps = _finite(self.base_bps, "base_bps")
max_adv_weight = _finite(self.max_adv_weight, "max_adv_weight")
if base_bps < 0:
raise BaselinePipelineError("base_bps must be non-negative")
if not 0 <= max_adv_weight <= 1:
raise BaselinePipelineError("max_adv_weight must lie in [0, 1]")
if not str(self.cost_model_version).strip():
raise BaselinePipelineError("cost_model_version is required")
@dataclass(frozen=True, slots=True)
class SecurityStateInput:
symbol: str
state: UniverseState
industry: str
price: float
current_quantity: int = 0
sellable_quantity: int = 0
reasons: tuple[str, ...] = ()
def __post_init__(self) -> None:
object.__setattr__(self, "symbol", normalize_symbol(self.symbol))
if not isinstance(self.state, UniverseState):
raise BaselinePipelineError("state must be a UniverseState")
if not str(self.industry).strip():
raise BaselinePipelineError("industry is required")
price = _finite(self.price, "price")
if price <= 0:
raise BaselinePipelineError("price must be positive")
if type(self.current_quantity) is not int or self.current_quantity < 0:
raise BaselinePipelineError(
"current_quantity must be a non-negative integer"
)
if (
type(self.sellable_quantity) is not int
or self.sellable_quantity < 0
or self.sellable_quantity > self.current_quantity
):
raise BaselinePipelineError(
"sellable_quantity must lie within current_quantity"
)
if any(not str(reason).strip() for reason in self.reasons):
raise BaselinePipelineError("universe reasons must be non-empty strings")
@dataclass(frozen=True, slots=True)
class BaselinePipelinePolicy:
constraints: PortfolioConstraints = PortfolioConstraints()
solver: str = "deterministic"
lot_size: int = 100
cash_buffer: float = 0.05
release_role: str = "baseline_candidate"
def validate(self) -> None:
self.constraints.validate()
if self.solver not in {"deterministic", "cvxpy"}:
raise BaselinePipelineError("solver must be deterministic or cvxpy")
if type(self.lot_size) is not int or self.lot_size <= 0:
raise BaselinePipelineError("lot_size must be a positive integer")
cash_buffer = _finite(self.cash_buffer, "cash_buffer")
if not 0 <= cash_buffer < 1:
raise BaselinePipelineError("cash_buffer must lie in [0, 1)")
if self.release_role not in {"baseline_candidate", "research_candidate"}:
raise BaselinePipelineError("unsupported release_role")
@dataclass(frozen=True, slots=True)
class BaselinePipelineResult:
pipeline_id: str
release_role: str
solution: PortfolioSolution
risk_decision: RiskDecision
target_quantities: dict[str, int]
order_deltas: dict[str, int]
layer_payloads: dict[str, dict[str, Any]]
layer_trace: tuple[dict[str, Any], ...]
def as_dict(self) -> dict[str, Any]:
return {
"pipeline_id": self.pipeline_id,
"release_role": self.release_role,
"weights": self.solution.weights,
"target_quantities": self.target_quantities,
"order_deltas": self.order_deltas,
"portfolio": {
"solver": self.solution.solver,
"status": self.solution.status,
"gross_weight": self.solution.gross_weight,
"cash_weight": self.solution.cash_weight,
"one_way_turnover": self.solution.one_way_turnover,
"predicted_variance": self.solution.predicted_variance,
"expected_score": self.solution.expected_score,
"estimated_cost_bps_weighted": (
self.solution.estimated_cost_bps_weighted
),
"diagnostics": self.solution.diagnostics,
},
"risk": {
"approved": self.risk_decision.approved,
"gross_weight": self.risk_decision.gross_weight,
"one_way_turnover": self.risk_decision.one_way_turnover,
"violations": [
{
"code": item.code,
"message": item.message,
"symbol": item.symbol,
}
for item in self.risk_decision.violations
],
},
"layer_payloads": self.layer_payloads,
"layer_trace": list(self.layer_trace),
}
def _unique_by_symbol(
values: Iterable[Any],
*,
name: str,
) -> dict[str, Any]:
result: dict[str, Any] = {}
for item in values:
symbol = normalize_symbol(item.symbol)
if symbol in result:
raise BaselinePipelineError(f"duplicate {name} for {symbol}")
result[symbol] = item
if not result:
raise BaselinePipelineError(f"{name} inputs are empty")
return result
def _trace_event(
*,
layer: str,
implementation: str,
input_sha256: str,
output_payload: Mapping[str, Any],
previous_event_sha256: str | None,
) -> dict[str, Any]:
output_sha256 = _sha256(output_payload)
body = {
"sequence": FIVE_LAYER_ORDER.index(layer) + 1,
"layer": layer,
"implementation": implementation,
"input_sha256": input_sha256,
"output_sha256": output_sha256,
"previous_event_sha256": previous_event_sha256,
}
body["event_sha256"] = _sha256(body)
return body
def verify_layer_trace(
trace: Iterable[Mapping[str, Any]],
layer_payloads: Mapping[str, Mapping[str, Any]],
) -> None:
events = tuple(dict(item) for item in trace)
if tuple(item.get("layer") for item in events) != FIVE_LAYER_ORDER:
raise BaselinePipelineError("layer trace is not the canonical five-layer path")
previous = None
for sequence, event in enumerate(events, 1):
if event.get("sequence") != sequence:
raise BaselinePipelineError("layer trace sequence is invalid")
if event.get("previous_event_sha256") != previous:
raise BaselinePipelineError("layer trace hash chain is broken")
layer = str(event["layer"])
if layer not in layer_payloads:
raise BaselinePipelineError(f"missing layer payload for {layer}")
if event.get("output_sha256") != _sha256(layer_payloads[layer]):
raise BaselinePipelineError(f"layer output hash mismatch for {layer}")
body = dict(event)
declared = body.pop("event_sha256", None)
if declared != _sha256(body):
raise BaselinePipelineError(f"layer event hash mismatch for {layer}")
previous = str(declared)
def build_baseline_pipeline(
*,
alpha: Iterable[AlphaForecastInput],
risk: Iterable[RiskForecastInput],
cost: Iterable[CostForecastInput],
securities: Iterable[SecurityStateInput],
equity: float,
policy: BaselinePipelinePolicy = BaselinePipelinePolicy(),
) -> BaselinePipelineResult:
"""Compose all five layers and return their causally linked decision trace."""
policy.validate()
account_equity = _finite(equity, "equity")
if account_equity <= 0:
raise BaselinePipelineError("equity must be positive")
alpha_by_symbol = _unique_by_symbol(alpha, name="alpha")
risk_by_symbol = _unique_by_symbol(risk, name="risk")
cost_by_symbol = _unique_by_symbol(cost, name="cost")
state_by_symbol = _unique_by_symbol(securities, name="universe")
symbol_sets = {
"alpha": set(alpha_by_symbol),
"risk": set(risk_by_symbol),
"cost": set(cost_by_symbol),
"universe": set(state_by_symbol),
}
if len({frozenset(values) for values in symbol_sets.values()}) != 1:
raise BaselinePipelineError(
"alpha/risk/cost/universe symbol sets must match exactly: "
+ ", ".join(
f"{name}={sorted(values)}"
for name, values in sorted(symbol_sets.items())
)
)
symbols = sorted(state_by_symbol)
model_versions = {alpha_by_symbol[symbol].model_version for symbol in symbols}
feature_versions = {
alpha_by_symbol[symbol].feature_version for symbol in symbols
}
risk_versions = {
risk_by_symbol[symbol].risk_model_version for symbol in symbols
}
cost_versions = {
cost_by_symbol[symbol].cost_model_version for symbol in symbols
}
for name, versions in (
("model", model_versions),
("feature", feature_versions),
("risk model", risk_versions),
("cost model", cost_versions),
):
if len(versions) != 1:
raise BaselinePipelineError(f"{name} versions are inconsistent")
model_version = next(iter(model_versions))
if (
policy.release_role == "baseline_candidate"
and model_version == "portable-momentum-v1"
):
raise BaselinePipelineError(
"portable-momentum-v1 is a platform smoke strategy, not a "
"Baseline candidate model"
)
current_quantities = {
symbol: state_by_symbol[symbol].current_quantity for symbol in symbols
}
sellable = {
symbol: state_by_symbol[symbol].sellable_quantity for symbol in symbols
}
prices = {symbol: state_by_symbol[symbol].price for symbol in symbols}
current_weights = {
symbol: (
current_quantities[symbol] * prices[symbol] / account_equity
)
for symbol in symbols
}
candidates = [
PortfolioCandidate(
symbol=symbol,
score=alpha_by_symbol[symbol].expected_excess_return,
variance=risk_by_symbol[symbol].predicted_variance,
current_weight=current_weights[symbol],
cost_bps=cost_by_symbol[symbol].base_bps,
max_adv_weight=cost_by_symbol[symbol].max_adv_weight,
industry=state_by_symbol[symbol].industry,
state=state_by_symbol[symbol].state,
)
for symbol in symbols
]
universe_payload = {
"symbols": [
{
"symbol": symbol,
"state": state_by_symbol[symbol].state.value,
"industry": state_by_symbol[symbol].industry,
"reasons": list(state_by_symbol[symbol].reasons),
"current_quantity": current_quantities[symbol],
"sellable_quantity": sellable[symbol],
}
for symbol in symbols
]
}
alpha_payload = {
"model_version": model_version,
"feature_version": next(iter(feature_versions)),
"forecasts": {
symbol: alpha_by_symbol[symbol].expected_excess_return
for symbol in symbols
},
}
portfolio_input = {
"alpha_sha256": _sha256(alpha_payload),
"risk_model_version": next(iter(risk_versions)),
"risk": {
symbol: risk_by_symbol[symbol].predicted_variance
for symbol in symbols
},
"cost_model_version": next(iter(cost_versions)),
"cost_bps": {
symbol: cost_by_symbol[symbol].base_bps for symbol in symbols
},
"capacity": {
symbol: cost_by_symbol[symbol].max_adv_weight for symbol in symbols
},
"current_weights": current_weights,
}
solution = (
optimize_cvxpy(candidates, policy.constraints)
if policy.solver == "cvxpy"
else optimize_deterministic(candidates, policy.constraints)
)
portfolio_payload = {
"solver": solution.solver,
"status": solution.status,
"weights": solution.weights,
"gross_weight": solution.gross_weight,
"cash_weight": solution.cash_weight,
"one_way_turnover": solution.one_way_turnover,
"predicted_variance": solution.predicted_variance,
"estimated_cost_bps_weighted": solution.estimated_cost_bps_weighted,
"input_sha256": _sha256(portfolio_input),
}
risk_decision = check_target_weights(
solution.weights,
current_weights,
max_single_weight=policy.constraints.single_name_cap,
max_gross_weight=policy.constraints.gross_target,
max_one_way_turnover=policy.constraints.one_way_turnover_cap,
)
risk_decision.require_approved()
risk_payload = {
"approved": risk_decision.approved,
"gross_weight": risk_decision.gross_weight,
"one_way_turnover": risk_decision.one_way_turnover,
"violations": [],
"post_risk_weights": solution.weights,
}
quantities = target_quantities(
solution.weights,
prices,
account_equity,
lot_size=policy.lot_size,
cash_buffer=policy.cash_buffer,
)
deltas = order_deltas(
quantities,
current_quantities,
sellable,
lot_size=policy.lot_size,
)
execution_payload = {
"equity": account_equity,
"prices": prices,
"lot_size": policy.lot_size,
"cash_buffer": policy.cash_buffer,
"target_quantities": quantities,
"order_deltas": deltas,
}
layer_payloads = {
"universe": universe_payload,
"alpha": alpha_payload,
"portfolio": portfolio_payload,
"risk": risk_payload,
"execution": execution_payload,
}
implementation = {
"universe": "quant60.baseline_pipeline:SecurityStateInput",
"alpha": "quant60.baseline_pipeline:AlphaForecastInput",
"portfolio": (
"quant60.optimizer:optimize_cvxpy"
if policy.solver == "cvxpy"
else "quant60.optimizer:optimize_deterministic"
),
"risk": "quant60.risk:check_target_weights",
"execution": "quant60.portable_core:target_quantities+order_deltas",
}
trace: list[dict[str, Any]] = []
previous_event_sha256 = None
previous_output_sha256 = _sha256(
{
"equity": account_equity,
"release_role": policy.release_role,
}
)
for layer in FIVE_LAYER_ORDER:
event = _trace_event(
layer=layer,
implementation=implementation[layer],
input_sha256=previous_output_sha256,
output_payload=layer_payloads[layer],
previous_event_sha256=previous_event_sha256,
)
trace.append(event)
previous_event_sha256 = event["event_sha256"]
previous_output_sha256 = event["output_sha256"]
verify_layer_trace(trace, layer_payloads)
pipeline_id = "q60-pipeline-" + _sha256(
{
"release_role": policy.release_role,
"model_version": model_version,
"layers": [event["event_sha256"] for event in trace],
}
)[:16]
return BaselinePipelineResult(
pipeline_id=pipeline_id,
release_role=policy.release_role,
solution=solution,
risk_decision=risk_decision,
target_quantities=quantities,
order_deltas=deltas,
layer_payloads=layer_payloads,
layer_trace=tuple(trace),
)
+381 -89
View File
@@ -13,20 +13,33 @@ from datetime import date
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from .baseline_pipeline import (
AlphaForecastInput,
BaselinePipelinePolicy,
CostForecastInput,
RiskForecastInput,
SecurityStateInput,
build_baseline_pipeline,
verify_layer_trace,
)
from .config import FeeConfig
from .contracts import validate_records from .contracts import validate_records
from .domain import Bar from .costs import (
CostModelParameters,
DatedFeeRule,
forecast_trade_cost,
)
from .domain import Bar, Side
from .features import ( from .features import (
TrainOnlyFeaturePreprocessor, TrainOnlyFeaturePreprocessor,
build_executable_label, build_executable_label,
compute_price_features, compute_price_features,
) )
from .ledger import canonical_json, jsonable from .ledger import canonical_json, jsonable
from .model_bundle import create_model_bundle, verify_model_bundle
from .optimizer import ( from .optimizer import (
PortfolioCandidate,
PortfolioConstraints, PortfolioConstraints,
optimize_deterministic,
) )
from .portable_core import target_quantities
from .research import ( from .research import (
DeterministicRidge, DeterministicRidge,
pearson_correlation, pearson_correlation,
@@ -34,6 +47,7 @@ from .research import (
spearman_correlation, spearman_correlation,
) )
from .synthetic import generate_synthetic_bars from .synthetic import generate_synthetic_bars
from .target_package import seal_target_package, validate_target_package
from .universe import SecurityEligibility, classify_security from .universe import SecurityEligibility, classify_security
@@ -119,6 +133,32 @@ def _bar_data_version(bars: list[Bar]) -> str:
return _sha256(payload) return _sha256(payload)
def _training_rows_version(rows: list[dict[str, Any]]) -> str:
return _sha256(
[
{
"sample_id": row["sample_id"],
"decision_date": row["decision_date"],
"entry_date": row["entry_date"],
"exit_date": row["exit_date"],
"symbol": row["symbol"],
"features": row["features"],
"label": row["label"],
}
for row in sorted(
rows,
key=lambda item: (item["decision_date"], item["symbol"]),
)
]
)
def _label_is_available(row: dict[str, Any], target_date: date) -> bool:
"""Return whether a completed label is known strictly before a decision."""
return date.fromisoformat(row["exit_date"]) < target_date
def _first_sessions_by_week(dates: list[date]) -> list[date]: def _first_sessions_by_week(dates: list[date]) -> list[date]:
result: list[date] = [] result: list[date] = []
prior_week = None prior_week = None
@@ -320,94 +360,25 @@ def run_research_smoke(
} }
) )
# Train strictly before the final decision and emit a point-in-time target. # Train only on labels whose exit observation was known before the final
# decision. Filtering merely on decision_date would leak the final
# horizon's future opens into the fitted model.
target_date = decision_dates[-1] target_date = decision_dates[-1]
final_train = [ final_train = [
row row
for row in rows for row in rows
if date.fromisoformat(row["decision_date"]) < target_date if _label_is_available(row, target_date)
] ]
final_rows = rows_by_date[target_date] final_rows = rows_by_date[target_date]
final_processor = TrainOnlyFeaturePreprocessor().fit( if not final_train:
[row["features"] for row in final_train] raise ValueError("no labels are available before the final decision")
sessions = sorted({bar.trading_date for bar in bars})
next_session = next(session for session in sessions if session > target_date)
full_data_version = _bar_data_version(bars)
decision_data_version = _bar_data_version(
[bar for bar in bars if bar.trading_date <= target_date]
) )
final_model = DeterministicRidge(alpha=5.0).fit( training_data_version = _training_rows_version(final_train)
_matrix(final_train, final_processor),
[row["label"] for row in final_train],
)
final_predictions = final_model.predict(_matrix(final_rows, final_processor))
centered = statistics.fmean(final_predictions)
scores = {
row["symbol"]: prediction - centered
for row, prediction in zip(final_rows, final_predictions)
}
candidates = []
current_weights = {symbol: 0.0 for symbol in RESEARCH_SYMBOLS}
latest_prices = {}
for symbol in RESEARCH_SYMBOLS:
completed = [
bar for bar in by_symbol[symbol] if bar.trading_date <= target_date
]
latest = completed[-1]
latest_prices[symbol] = float(latest.close)
returns = [
float(completed[index].close)
/ float(completed[index - 1].close)
- 1.0
for index in range(max(1, len(completed) - 60), len(completed))
]
variance = (
statistics.variance(returns) if len(returns) > 1 else 1e-4
)
feature_row = next(
row for row in final_rows if row["symbol"] == symbol
)
turnover = feature_row["features"]["median_turnover_20"] or 0.0
eligibility = SecurityEligibility(
symbol=symbol,
index_member=True,
listing_sessions=500,
median_turnover=turnover,
minimum_turnover=0.0,
held_quantity=0,
sellable_quantity=0,
)
state = classify_security(eligibility).state
candidates.append(
PortfolioCandidate(
symbol=symbol,
score=scores[symbol],
variance=max(1e-6, variance),
current_weight=current_weights[symbol],
cost_bps=15.0,
max_adv_weight=max(
0.0,
min(0.10, turnover * 0.03 / 1_000_000.0),
),
industry=INDUSTRY_BY_SYMBOL[symbol],
state=state,
)
)
solution = optimize_deterministic(
candidates,
PortfolioConstraints(
gross_target=0.90,
single_name_cap=0.10,
industry_cap=0.30,
one_way_turnover_cap=0.45,
risk_aversion=1.0,
cost_aversion=1.0,
),
)
quantities = target_quantities(
solution.weights,
latest_prices,
1_000_000.0,
lot_size=100,
cash_buffer=0.05,
)
data_version = _bar_data_version(bars)
source_sha256, sources = _source_identity() source_sha256, sources = _source_identity()
config = { config = {
"trading_days": trading_days, "trading_days": trading_days,
@@ -423,16 +394,284 @@ def run_research_smoke(
"test_dates": test_size, "test_dates": test_size,
"purge_dates": 1, "purge_dates": 1,
"embargo_dates": 1, "embargo_dates": 1,
"final_label_availability": "exit_date_strictly_before_target",
},
"portfolio": {
"solver": "deterministic",
"gross_target": 0.90,
"single_name_cap": 0.10,
"industry_cap": 0.30,
"one_way_turnover_cap": 0.45,
"risk_aversion": 1.0,
"cost_aversion": 1.0,
},
"cost_model": {
"fee_version": "cn-a-share-synthetic-v1",
"half_spread_bps": 2.0,
"impact_coefficient": 0.10,
"max_participation": 0.10,
"reference_notional_weight": 0.10,
}, },
} }
config_hash = _sha256(config) config_hash = _sha256(config)
final_processor = TrainOnlyFeaturePreprocessor().fit(
[row["features"] for row in final_train]
)
final_model = DeterministicRidge(alpha=5.0).fit(
_matrix(final_train, final_processor),
[row["label"] for row in final_train],
)
trained_through = max(
date.fromisoformat(row["exit_date"]) for row in final_train
)
model_bundle = create_model_bundle(
final_processor,
final_model,
trained_through=trained_through,
label_name=config["label"],
horizon_trading_days=horizon_sessions,
data_sha256=training_data_version,
source_sha256=source_sha256,
config_sha256=config_hash,
)
# Drive the published decision from the frozen artifact replay. The
# model digest carried by TargetPackageV1 is therefore a causal dependency
# of the target, not just a sidecar identity for an in-memory model.
frozen_model = verify_model_bundle(
model_bundle,
expected_data_sha256=training_data_version,
expected_source_sha256=source_sha256,
expected_config_sha256=config_hash,
)
final_predictions = frozen_model.predict(
[row["features"] for row in final_rows]
)
centered = statistics.fmean(final_predictions)
scores = {
row["symbol"]: prediction - centered
for row, prediction in zip(final_rows, final_predictions)
}
alpha_inputs = []
risk_inputs = []
cost_inputs = []
security_inputs = []
cost_forecasts = {}
risk_forecasts = {}
fee_rule = DatedFeeRule(
effective_from=date(2000, 1, 1),
effective_to=None,
fees=FeeConfig(),
version=config["cost_model"]["fee_version"],
)
cost_parameters = CostModelParameters(
half_spread_bps=config["cost_model"]["half_spread_bps"],
impact_coefficient=config["cost_model"]["impact_coefficient"],
max_participation=config["cost_model"]["max_participation"],
)
for symbol in RESEARCH_SYMBOLS:
completed = [
bar for bar in by_symbol[symbol] if bar.trading_date <= target_date
]
latest = completed[-1]
price = float(latest.close)
returns = [
float(completed[index].close)
/ float(completed[index - 1].close)
- 1.0
for index in range(max(1, len(completed) - 60), len(completed))
]
variance = (
statistics.variance(returns) if len(returns) > 1 else 1e-4
)
daily_volatility = (
statistics.stdev(returns) if len(returns) > 1 else 0.01
)
adv_quantity = statistics.median(
max(1, int(bar.volume)) for bar in completed[-20:]
)
reference_quantity = max(
100,
int(
1_000_000.0
* config["cost_model"]["reference_notional_weight"]
/ price
)
// 100
* 100,
)
cost_forecast = forecast_trade_cost(
symbol=symbol,
side=Side.BUY,
quantity=reference_quantity,
price=price,
adv_quantity=adv_quantity,
daily_volatility=max(1e-6, daily_volatility),
fee_rule=fee_rule,
parameters=cost_parameters,
)
max_adv_weight = min(
config["portfolio"]["single_name_cap"],
adv_quantity
* cost_parameters.max_participation
* price
/ 1_000_000.0,
)
feature_row = next(
row for row in final_rows if row["symbol"] == symbol
)
turnover = feature_row["features"]["median_turnover_20"] or 0.0
eligibility = SecurityEligibility(
symbol=symbol,
index_member=True,
listing_sessions=500,
median_turnover=turnover,
minimum_turnover=0.0,
held_quantity=0,
sellable_quantity=0,
)
universe_decision = classify_security(eligibility)
alpha_inputs.append(
AlphaForecastInput(
symbol=symbol,
expected_excess_return=scores[symbol],
model_version="deterministic-ridge-alpha-5",
feature_version=config["feature_version"],
)
)
risk_inputs.append(
RiskForecastInput(
symbol=symbol,
predicted_variance=max(1e-6, variance),
risk_model_version="diagonal-realized-variance-60d-v1",
)
)
cost_inputs.append(
CostForecastInput(
symbol=symbol,
base_bps=cost_forecast.base_bps,
max_adv_weight=max(0.0, max_adv_weight),
cost_model_version="explicit-spread-sqrt-impact-v1",
)
)
security_inputs.append(
SecurityStateInput(
symbol=symbol,
state=universe_decision.state,
industry=INDUSTRY_BY_SYMBOL[symbol],
price=price,
current_quantity=0,
sellable_quantity=0,
reasons=universe_decision.reasons,
)
)
risk_forecasts[symbol] = max(1e-6, variance)
cost_forecasts[symbol] = {
"base_bps": cost_forecast.base_bps,
"participation": cost_forecast.participation,
"capacity_breached": cost_forecast.capacity_breached,
"max_adv_weight": max(0.0, max_adv_weight),
"fee_version": cost_forecast.fee_version,
}
constraints = PortfolioConstraints(
gross_target=config["portfolio"]["gross_target"],
single_name_cap=config["portfolio"]["single_name_cap"],
industry_cap=config["portfolio"]["industry_cap"],
one_way_turnover_cap=config["portfolio"]["one_way_turnover_cap"],
risk_aversion=config["portfolio"]["risk_aversion"],
cost_aversion=config["portfolio"]["cost_aversion"],
)
pipeline = build_baseline_pipeline(
alpha=alpha_inputs,
risk=risk_inputs,
cost=cost_inputs,
securities=security_inputs,
equity=1_000_000.0,
policy=BaselinePipelinePolicy(
constraints=constraints,
solver=config["portfolio"]["solver"],
lot_size=100,
cash_buffer=0.05,
release_role="research_candidate",
),
)
solution = pipeline.solution
quantities = pipeline.target_quantities
risk_sha256 = _sha256(
{
"model_version": "diagonal-realized-variance-60d-v1",
"forecasts": risk_forecasts,
}
)
cost_sha256 = _sha256(
{
"model_version": "explicit-spread-sqrt-impact-v1",
"forecasts": cost_forecasts,
}
)
feature_sha256 = _sha256(
{
"feature_version": config["feature_version"],
"as_of": target_date.isoformat(),
"rows": {
row["symbol"]: row["features"]
for row in sorted(final_rows, key=lambda item: item["symbol"])
},
}
)
optimizer_sha256 = _sha256(
{
"implementation": pipeline.layer_trace[2]["implementation"],
"constraints": config["portfolio"],
"portfolio_output_sha256": pipeline.layer_trace[2]["output_sha256"],
}
)
run_id = "q60-research-" + _sha256( run_id = "q60-research-" + _sha256(
{ {
"data": data_version, "data": full_data_version,
"source": source_sha256, "source": source_sha256,
"config": config_hash, "config": config_hash,
} }
)[:16] )[:16]
target_package = seal_target_package(
{
"schema_version": "1.0",
"package_type": "TargetPackageV1",
"package_id": f"TP-{target_date.strftime('%Y%m%d')}-{run_id[-8:]}",
"decision_id": f"RD-{target_date.strftime('%Y%m%d')}",
"release": "quant60-research-candidate-v1",
"evidence_class": "synthetic-research",
"model_id": "deterministic-ridge-alpha-5",
"signal_as_of": f"{target_date.isoformat()}T15:00:00+08:00",
"next_session": next_session.isoformat(),
"model_sha256": model_bundle["bundle_sha256"],
"data_sha256": decision_data_version,
"feature_sha256": feature_sha256,
"risk_sha256": risk_sha256,
"cost_sha256": cost_sha256,
"optimizer_sha256": optimizer_sha256,
"config_sha256": config_hash,
"source_sha256": source_sha256,
"target_weights": dict(sorted(solution.weights.items())),
"universe": {
item.symbol: {
"state": item.state.value,
"reasons": sorted(item.reasons),
}
for item in sorted(
security_inputs,
key=lambda security: security.symbol,
)
},
"constraints": {
"constraint_state": "FEASIBLE",
"long_only": True,
"max_single_weight": constraints.single_name_cap,
"max_gross_weight": constraints.gross_target,
"max_one_way_turnover": constraints.one_way_turnover_cap,
"actual_gross_weight": solution.gross_weight,
},
}
)
signal_records = [ signal_records = [
{ {
"schema_version": "1.0", "schema_version": "1.0",
@@ -445,7 +684,7 @@ def run_research_smoke(
"expected_excess_return": scores[symbol], "expected_excess_return": scores[symbol],
"confidence": None, "confidence": None,
"model_version": "deterministic-ridge-alpha-5", "model_version": "deterministic-ridge-alpha-5",
"data_version": data_version, "data_version": decision_data_version,
"feature_version": "transparent-price-v1", "feature_version": "transparent-price-v1",
"metadata": { "metadata": {
"evidence_class": "synthetic-research", "evidence_class": "synthetic-research",
@@ -483,7 +722,9 @@ def run_research_smoke(
"evidence_class": "synthetic-research", "evidence_class": "synthetic-research",
"investment_value_claim": False, "investment_value_claim": False,
"config_hash": config_hash, "config_hash": config_hash,
"data_version": data_version, "data_version": decision_data_version,
"full_synthetic_data_version": full_data_version,
"training_data_version": training_data_version,
"engine_source_sha256": source_sha256, "engine_source_sha256": source_sha256,
"sample_count": len(rows), "sample_count": len(rows),
"decision_date_count": len(decision_dates), "decision_date_count": len(decision_dates),
@@ -500,6 +741,19 @@ def run_research_smoke(
else 0.0 else 0.0
), ),
"target_date": target_date.isoformat(), "target_date": target_date.isoformat(),
"next_session": next_session.isoformat(),
"final_training_max_label_exit": trained_through.isoformat(),
"model_bundle_sha256": model_bundle["bundle_sha256"],
"target_package_sha256": target_package["package_sha256"],
"pipeline_id": pipeline.pipeline_id,
"five_layer_trace": [
{
"layer": item["layer"],
"implementation": item["implementation"],
"event_sha256": item["event_sha256"],
}
for item in pipeline.layer_trace
],
"target_solver": solution.solver, "target_solver": solution.solver,
"target_gross_weight": solution.gross_weight, "target_gross_weight": solution.gross_weight,
"target_cash_weight": solution.cash_weight, "target_cash_weight": solution.cash_weight,
@@ -529,7 +783,9 @@ def run_research_smoke(
"evidence_class": "synthetic-research", "evidence_class": "synthetic-research",
"config": config, "config": config,
"config_hash": config_hash, "config_hash": config_hash,
"data_version": data_version, "data_version": decision_data_version,
"full_synthetic_data_version": full_data_version,
"training_data_version": training_data_version,
"engine_source_sha256": source_sha256, "engine_source_sha256": source_sha256,
"engine_sources": sources, "engine_sources": sources,
"deterministic": True, "deterministic": True,
@@ -542,6 +798,9 @@ def run_research_smoke(
"targets": target_records, "targets": target_records,
"forecasts": forecasts, "forecasts": forecasts,
"samples": sample_index, "samples": sample_index,
"model_bundle": model_bundle,
"target_package": target_package,
"pipeline": pipeline.as_dict(),
} }
@@ -551,6 +810,9 @@ RESEARCH_ARTIFACTS = {
"targets": "targets.json", "targets": "targets.json",
"forecasts": "forecasts.json", "forecasts": "forecasts.json",
"samples": "samples.json", "samples": "samples.json",
"model_bundle": "model_bundle.json",
"target_package": "target_package.json",
"pipeline": "pipeline.json",
} }
@@ -612,9 +874,39 @@ def verify_research_manifest(path: str | Path) -> dict[str, Any]:
targets = json.loads((root / RESEARCH_ARTIFACTS["targets"]).read_text()) targets = json.loads((root / RESEARCH_ARTIFACTS["targets"]).read_text())
validate_records(signals, "signal.schema.json") validate_records(signals, "signal.schema.json")
validate_records(targets, "target.schema.json") validate_records(targets, "target.schema.json")
model_bundle = json.loads(
(root / RESEARCH_ARTIFACTS["model_bundle"]).read_text()
)
verified_model = verify_model_bundle(
model_bundle,
expected_source_sha256=manifest["engine_source_sha256"],
expected_config_sha256=manifest["config_hash"],
)
target_package = json.loads(
(root / RESEARCH_ARTIFACTS["target_package"]).read_text()
)
validated_package = validate_target_package(target_package)
if validated_package["model_sha256"] != verified_model.bundle_sha256:
raise ValueError("target package/model bundle identity mismatch")
pipeline = json.loads((root / RESEARCH_ARTIFACTS["pipeline"]).read_text())
verify_layer_trace(pipeline["layer_trace"], pipeline["layer_payloads"])
if validated_package["target_weights"] != pipeline["weights"]:
raise ValueError("target package/pipeline weight mismatch")
report = json.loads((root / RESEARCH_ARTIFACTS["report"]).read_text()) report = json.loads((root / RESEARCH_ARTIFACTS["report"]).read_text())
if report["run_id"] != manifest["run_id"]: if report["run_id"] != manifest["run_id"]:
raise ValueError("research report/manifest run_id mismatch") raise ValueError("research report/manifest run_id mismatch")
if report["model_bundle_sha256"] != verified_model.bundle_sha256:
raise ValueError("research report/model bundle identity mismatch")
if (
report["target_package_sha256"]
!= validated_package["package_sha256"]
):
raise ValueError("research report/target package identity mismatch")
if not (
date.fromisoformat(report["final_training_max_label_exit"])
< date.fromisoformat(report["target_date"])
):
raise ValueError("final model contains a label unavailable at decision")
return { return {
"ok": True, "ok": True,
"run_id": manifest["run_id"], "run_id": manifest["run_id"],
+602
View File
@@ -0,0 +1,602 @@
"""Frozen, dependency-free Ridge model artifacts and deterministic replay.
The bundle is deliberately limited to the model path that Quant60 can replay
without a research environment: ``TrainOnlyFeaturePreprocessor`` followed by
``DeterministicRidge``. A self-hash detects byte/content tampering, while the
semantic verifier checks the relationships that JSON Schema cannot express
(feature order, transform order, and coefficient width).
"""
from __future__ import annotations
import hashlib
import json
import math
import os
import re
import tempfile
from dataclasses import dataclass
from datetime import date
from pathlib import Path
from typing import Any, Mapping, Sequence
from .contracts import load_schema, validate_contract
from .features import FeatureTransform, TrainOnlyFeaturePreprocessor
from .ledger import canonical_json
from .research import DeterministicRidge
MODEL_BUNDLE_SCHEMA_VERSION = "1.0"
MODEL_BUNDLE_ARTIFACT_TYPE = "quant60-deterministic-ridge-bundle"
PREPROCESSOR_TYPE = "train-only-winsor-impute-standardize"
MODEL_TYPE = "deterministic-ridge"
_SHA256_PATTERN = re.compile(r"^[0-9a-f]{64}$")
class ModelBundleIntegrityError(ValueError):
"""A model bundle is malformed, inconsistent, or fails its identity check."""
@dataclass(frozen=True, slots=True)
class FrozenFeatureTransform:
name: str
lower: float
upper: float
median: float
mean: float
scale: float
def as_dict(self) -> dict[str, Any]:
return {
"name": self.name,
"lower": self.lower,
"upper": self.upper,
"median": self.median,
"mean": self.mean,
"scale": self.scale,
}
@dataclass(frozen=True, slots=True)
class FrozenRidgeModelBundle:
"""An immutable, semantically verified model ready for replay."""
bundle_sha256: str
trained_through: date
label_name: str
horizon_trading_days: int
data_sha256: str
source_sha256: str
config_sha256: str
winsor_fraction: float
feature_order: tuple[str, ...]
output_feature_order: tuple[str, ...]
transforms: tuple[FrozenFeatureTransform, ...]
alpha: float
fit_intercept: bool
coefficients: tuple[float, ...]
intercept: float
def as_dict(self) -> dict[str, Any]:
return {
"schema_version": MODEL_BUNDLE_SCHEMA_VERSION,
"artifact_type": MODEL_BUNDLE_ARTIFACT_TYPE,
"bundle_sha256": self.bundle_sha256,
"trained_through": self.trained_through.isoformat(),
"label": {
"name": self.label_name,
"horizon_trading_days": self.horizon_trading_days,
},
"lineage": {
"data_sha256": self.data_sha256,
"source_sha256": self.source_sha256,
"config_sha256": self.config_sha256,
},
"preprocessor": {
"type": PREPROCESSOR_TYPE,
"winsor_fraction": self.winsor_fraction,
"feature_order": list(self.feature_order),
"output_feature_order": list(self.output_feature_order),
"transforms": [
transform.as_dict() for transform in self.transforms
],
},
"model": {
"type": MODEL_TYPE,
"alpha": self.alpha,
"fit_intercept": self.fit_intercept,
"coefficients": list(self.coefficients),
"intercept": self.intercept,
},
}
@property
def canonical_bytes(self) -> bytes:
"""Return the exact deterministic bytes used by ``write_model_bundle``."""
return _published_bytes(self.as_dict())
def _preprocessor(self) -> TrainOnlyFeaturePreprocessor:
processor = TrainOnlyFeaturePreprocessor(self.winsor_fraction)
processor.transforms = {
transform.name: FeatureTransform(
lower=transform.lower,
upper=transform.upper,
median=transform.median,
mean=transform.mean,
scale=transform.scale,
)
for transform in self.transforms
}
return processor
def _model(self) -> DeterministicRidge:
model = DeterministicRidge(
alpha=self.alpha,
fit_intercept=self.fit_intercept,
)
model.coef_ = self.coefficients
model.intercept_ = self.intercept
return model
def predict(
self,
rows: Sequence[Mapping[str, float | None]],
) -> list[float]:
"""Replay predictions using only the state frozen in this bundle."""
processor = self._preprocessor()
transformed = processor.transform(rows)
matrix = [
[row[name] for name in self.output_feature_order]
for row in transformed
]
return self._model().predict(matrix)
def prediction_bytes(
self,
rows: Sequence[Mapping[str, float | None]],
) -> bytes:
"""Return a deterministic prediction envelope suitable for hashing."""
return _published_bytes(
{
"bundle_sha256": self.bundle_sha256,
"predictions": self.predict(rows),
}
)
def _published_bytes(value: Any) -> bytes:
return (canonical_json(value) + "\n").encode("utf-8")
def _bundle_body(payload: Mapping[str, Any]) -> dict[str, Any]:
return {
key: value
for key, value in payload.items()
if key != "bundle_sha256"
}
def model_bundle_sha256(payload: Mapping[str, Any]) -> str:
"""Hash the canonical bundle body, excluding its self-hash field."""
return hashlib.sha256(
canonical_json(_bundle_body(payload)).encode("utf-8")
).hexdigest()
def _strict_json(path: Path) -> Any:
def reject_constant(value: str) -> None:
raise ModelBundleIntegrityError(
f"non-finite JSON constant in model bundle: {value}"
)
def reject_duplicate_keys(
pairs: list[tuple[str, Any]],
) -> dict[str, Any]:
output: dict[str, Any] = {}
for key, value in pairs:
if key in output:
raise ModelBundleIntegrityError(
f"duplicate JSON key in model bundle: {key}"
)
output[key] = value
return output
try:
return json.loads(
path.read_text(encoding="utf-8"),
parse_constant=reject_constant,
object_pairs_hook=reject_duplicate_keys,
)
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
raise ModelBundleIntegrityError(
f"model bundle is not valid UTF-8 JSON: {exc}"
) from exc
def _json_copy(payload: Mapping[str, Any]) -> dict[str, Any]:
try:
copied = json.loads(canonical_json(dict(payload)))
except (TypeError, ValueError, json.JSONDecodeError) as exc:
raise ModelBundleIntegrityError(
f"model bundle is not canonical JSON data: {exc}"
) from exc
if not isinstance(copied, dict):
raise ModelBundleIntegrityError("model bundle root must be an object")
return copied
def _finite_float(value: Any, field: str) -> float:
if isinstance(value, bool):
raise ModelBundleIntegrityError(f"{field} must be a finite number")
try:
normalized = float(value)
except (TypeError, ValueError) as exc:
raise ModelBundleIntegrityError(
f"{field} must be a finite number"
) from exc
if not math.isfinite(normalized):
raise ModelBundleIntegrityError(f"{field} must be a finite number")
return normalized
def _require_sha256(value: Any, field: str) -> str:
normalized = str(value)
if _SHA256_PATTERN.fullmatch(normalized) is None:
raise ModelBundleIntegrityError(
f"{field} must be a lowercase SHA-256 digest"
)
if normalized == "0" * 64:
raise ModelBundleIntegrityError(
f"{field} cannot be an all-zero placeholder"
)
return normalized
def _semantic_model_bundle(payload: Mapping[str, Any]) -> FrozenRidgeModelBundle:
try:
trained_through = date.fromisoformat(str(payload["trained_through"]))
label = payload["label"]
lineage = payload["lineage"]
preprocessor = payload["preprocessor"]
model = payload["model"]
except (KeyError, TypeError, ValueError) as exc:
raise ModelBundleIntegrityError(
f"model bundle metadata is invalid: {exc}"
) from exc
label_name = str(label["name"])
if not label_name.strip() or label_name != label_name.strip():
raise ModelBundleIntegrityError(
"label.name must be non-empty without surrounding whitespace"
)
horizon = label["horizon_trading_days"]
if type(horizon) is not int or horizon <= 0:
raise ModelBundleIntegrityError(
"label.horizon_trading_days must be a positive integer"
)
winsor_fraction = _finite_float(
preprocessor["winsor_fraction"],
"preprocessor.winsor_fraction",
)
if not 0.0 <= winsor_fraction < 0.5:
raise ModelBundleIntegrityError(
"preprocessor.winsor_fraction must lie in [0, 0.5)"
)
feature_order = tuple(preprocessor["feature_order"])
output_order = tuple(preprocessor["output_feature_order"])
if (
not feature_order
or any(
not isinstance(name, str)
or not name
or name != name.strip()
for name in feature_order
)
):
raise ModelBundleIntegrityError(
"preprocessor.feature_order must contain non-empty names"
)
if len(set(feature_order)) != len(feature_order):
raise ModelBundleIntegrityError(
"preprocessor.feature_order contains duplicates"
)
if feature_order != tuple(sorted(feature_order)):
raise ModelBundleIntegrityError(
"preprocessor.feature_order is not canonical"
)
expected_output_order = tuple(
item
for name in feature_order
for item in (name, f"{name}__missing")
)
if output_order != expected_output_order:
raise ModelBundleIntegrityError(
"preprocessor.output_feature_order is inconsistent"
)
if len(set(output_order)) != len(output_order):
raise ModelBundleIntegrityError(
"preprocessor.output_feature_order contains collisions"
)
raw_transforms = preprocessor["transforms"]
if not isinstance(raw_transforms, list):
raise ModelBundleIntegrityError(
"preprocessor.transforms must be an array"
)
transforms: list[FrozenFeatureTransform] = []
for index, raw in enumerate(raw_transforms):
if not isinstance(raw, dict):
raise ModelBundleIntegrityError(
f"preprocessor.transforms[{index}] must be an object"
)
name = raw.get("name")
lower = _finite_float(raw.get("lower"), f"transforms[{index}].lower")
upper = _finite_float(raw.get("upper"), f"transforms[{index}].upper")
median = _finite_float(raw.get("median"), f"transforms[{index}].median")
mean = _finite_float(raw.get("mean"), f"transforms[{index}].mean")
scale = _finite_float(raw.get("scale"), f"transforms[{index}].scale")
if lower > upper:
raise ModelBundleIntegrityError(
f"preprocessor transform {name!r} has lower above upper"
)
if not lower <= median <= upper:
raise ModelBundleIntegrityError(
f"preprocessor transform {name!r} has median outside bounds"
)
if not lower <= mean <= upper:
raise ModelBundleIntegrityError(
f"preprocessor transform {name!r} has mean outside bounds"
)
if scale <= 0:
raise ModelBundleIntegrityError(
f"preprocessor transform {name!r} has non-positive scale"
)
transforms.append(
FrozenFeatureTransform(
name=str(name),
lower=lower,
upper=upper,
median=median,
mean=mean,
scale=scale,
)
)
transform_names = tuple(item.name for item in transforms)
if transform_names != feature_order:
raise ModelBundleIntegrityError(
"preprocessor transform order does not match feature_order"
)
alpha = _finite_float(model["alpha"], "model.alpha")
if alpha < 0:
raise ModelBundleIntegrityError("model.alpha must be non-negative")
fit_intercept = model["fit_intercept"]
if type(fit_intercept) is not bool:
raise ModelBundleIntegrityError("model.fit_intercept must be a bool")
coefficients = tuple(
_finite_float(value, f"model.coefficients[{index}]")
for index, value in enumerate(model["coefficients"])
)
if len(coefficients) != len(output_order):
raise ModelBundleIntegrityError(
"model coefficient width does not match output_feature_order"
)
intercept = _finite_float(model["intercept"], "model.intercept")
bundle = FrozenRidgeModelBundle(
bundle_sha256=_require_sha256(
payload["bundle_sha256"],
"bundle_sha256",
),
trained_through=trained_through,
label_name=label_name,
horizon_trading_days=horizon,
data_sha256=_require_sha256(
lineage["data_sha256"],
"lineage.data_sha256",
),
source_sha256=_require_sha256(
lineage["source_sha256"],
"lineage.source_sha256",
),
config_sha256=_require_sha256(
lineage["config_sha256"],
"lineage.config_sha256",
),
winsor_fraction=winsor_fraction,
feature_order=feature_order,
output_feature_order=output_order,
transforms=tuple(transforms),
alpha=alpha,
fit_intercept=fit_intercept,
coefficients=coefficients,
intercept=intercept,
)
if bundle.as_dict() != payload:
raise ModelBundleIntegrityError(
"model bundle is not in canonical semantic form"
)
return bundle
def create_model_bundle(
preprocessor: TrainOnlyFeaturePreprocessor,
model: DeterministicRidge,
*,
trained_through: date | str,
label_name: str,
horizon_trading_days: int,
data_sha256: str,
source_sha256: str,
config_sha256: str,
) -> dict[str, Any]:
"""Freeze a fitted preprocessor and Ridge model into a verified payload."""
if not isinstance(preprocessor, TrainOnlyFeaturePreprocessor):
raise TypeError(
"preprocessor must be a TrainOnlyFeaturePreprocessor"
)
if preprocessor.transforms is None:
raise ValueError("preprocessor is not fitted")
if not isinstance(model, DeterministicRidge):
raise TypeError("model must be a DeterministicRidge")
if model.coef_ is None:
raise ValueError("model is not fitted")
try:
training_date = (
trained_through
if isinstance(trained_through, date)
else date.fromisoformat(str(trained_through))
)
except ValueError as exc:
raise ValueError("trained_through must be an ISO date") from exc
feature_order = tuple(sorted(preprocessor.transforms))
transforms = [
{
"name": name,
"lower": preprocessor.transforms[name].lower,
"upper": preprocessor.transforms[name].upper,
"median": preprocessor.transforms[name].median,
"mean": preprocessor.transforms[name].mean,
"scale": preprocessor.transforms[name].scale,
}
for name in feature_order
]
payload: dict[str, Any] = {
"schema_version": MODEL_BUNDLE_SCHEMA_VERSION,
"artifact_type": MODEL_BUNDLE_ARTIFACT_TYPE,
"bundle_sha256": "0" * 64,
"trained_through": training_date.isoformat(),
"label": {
"name": label_name,
"horizon_trading_days": horizon_trading_days,
},
"lineage": {
"data_sha256": data_sha256,
"source_sha256": source_sha256,
"config_sha256": config_sha256,
},
"preprocessor": {
"type": PREPROCESSOR_TYPE,
"winsor_fraction": preprocessor.winsor_fraction,
"feature_order": list(feature_order),
"output_feature_order": list(preprocessor.output_names),
"transforms": transforms,
},
"model": {
"type": MODEL_TYPE,
"alpha": model.alpha,
"fit_intercept": model.fit_intercept,
"coefficients": list(model.coef_),
"intercept": model.intercept_,
},
}
payload["bundle_sha256"] = model_bundle_sha256(payload)
return verify_model_bundle(payload).as_dict()
def verify_model_bundle(
value: Mapping[str, Any] | str | Path,
*,
expected_bundle_sha256: str | None = None,
expected_data_sha256: str | None = None,
expected_source_sha256: str | None = None,
expected_config_sha256: str | None = None,
) -> FrozenRidgeModelBundle:
"""Validate schema, self-hash, semantic relationships, and lineage pins."""
if isinstance(value, Mapping):
payload = _json_copy(value)
else:
path = Path(value).expanduser().resolve()
raw = _strict_json(path)
if not isinstance(raw, dict):
raise ModelBundleIntegrityError(
"model bundle root must be an object"
)
payload = raw
try:
validate_contract(
payload,
load_schema("model_bundle.schema.json"),
)
except (FileNotFoundError, KeyError, TypeError, ValueError) as exc:
raise ModelBundleIntegrityError(
f"model bundle schema validation failed: {exc}"
) from exc
actual_hash = model_bundle_sha256(payload)
if payload.get("bundle_sha256") != actual_hash:
raise ModelBundleIntegrityError("model bundle SHA-256 mismatch")
bundle = _semantic_model_bundle(payload)
expected = {
"bundle_sha256": expected_bundle_sha256,
"data_sha256": expected_data_sha256,
"source_sha256": expected_source_sha256,
"config_sha256": expected_config_sha256,
}
actual = {
"bundle_sha256": bundle.bundle_sha256,
"data_sha256": bundle.data_sha256,
"source_sha256": bundle.source_sha256,
"config_sha256": bundle.config_sha256,
}
for field, expected_value in expected.items():
if expected_value is None:
continue
normalized = _require_sha256(expected_value, f"expected_{field}")
if actual[field] != normalized:
raise ModelBundleIntegrityError(
f"model bundle {field} does not match expected identity"
)
return bundle
def write_model_bundle(
bundle: FrozenRidgeModelBundle | Mapping[str, Any],
path: str | Path,
) -> Path:
"""Atomically write a verified bundle in deterministic canonical JSON."""
verified = verify_model_bundle(
bundle.as_dict()
if isinstance(bundle, FrozenRidgeModelBundle)
else bundle
)
destination = Path(path)
destination.parent.mkdir(parents=True, exist_ok=True)
descriptor, temporary_name = tempfile.mkstemp(
prefix=f".{destination.name}.",
suffix=".tmp",
dir=str(destination.parent),
)
try:
with os.fdopen(descriptor, "wb") as handle:
handle.write(verified.canonical_bytes)
handle.flush()
os.fsync(handle.fileno())
os.replace(temporary_name, destination)
except BaseException:
try:
os.unlink(temporary_name)
except FileNotFoundError:
pass
raise
return destination
def load_verified_model_bundle(
path: str | Path,
**expected_identity: str | None,
) -> FrozenRidgeModelBundle:
"""Read a bundle from disk and fail closed unless all checks pass."""
return verify_model_bundle(path, **expected_identity)
+547
View File
@@ -7,6 +7,8 @@ single file when importing the complete ``quant60`` package is not possible.
from __future__ import division from __future__ import division
import hashlib
import json
import math import math
import re import re
@@ -30,6 +32,45 @@ _SHORT_EXCHANGES = {
"XBSE": "BJ", "XBSE": "BJ",
} }
_TARGET_PACKAGE_FIELDS = {
"schema_version",
"package_type",
"package_id",
"decision_id",
"release",
"evidence_class",
"model_id",
"signal_as_of",
"next_session",
"model_sha256",
"data_sha256",
"feature_sha256",
"risk_sha256",
"cost_sha256",
"optimizer_sha256",
"config_sha256",
"source_sha256",
"target_weights",
"universe",
"constraints",
"package_sha256",
}
_TARGET_PACKAGE_HASH_FIELDS = (
"model_sha256",
"data_sha256",
"feature_sha256",
"risk_sha256",
"cost_sha256",
"optimizer_sha256",
"config_sha256",
"source_sha256",
)
class TargetPackageLookupMiss(ValueError, LookupError):
"""An already verified tape has no exact requested decision."""
def _sum_numeric(values): def _sum_numeric(values):
"""Sum numerics without trusting a hosted runtime's global ``sum`` name.""" """Sum numerics without trusting a hosted runtime's global ``sum`` name."""
@@ -40,6 +81,15 @@ def _sum_numeric(values):
return total return total
def _any_true(values):
"""Return built-in ``any`` semantics without trusting hosted globals."""
for value in values:
if value:
return True
return False
def _infer_exchange(code): def _infer_exchange(code):
if code.startswith(("4", "8")): if code.startswith(("4", "8")):
return "XBSE" return "XBSE"
@@ -92,6 +142,396 @@ def convert_symbol(symbol, target="canonical"):
return normalize_symbol(symbol, target) return normalize_symbol(symbol, target)
def _canonical_json(value):
"""Match ``quant60.ledger.canonical_json`` for plain JSON values."""
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
)
def _json_copy(value, name):
try:
return json.loads(_canonical_json(value))
except (TypeError, ValueError) as error:
raise ValueError("%s must contain finite JSON values only" % name) from error
def _canonical_mapping(value, name, value_validator):
if not hasattr(value, "items"):
raise TypeError("%s must be a mapping" % name)
output = {}
for raw_symbol, raw_value in value.items():
symbol = normalize_symbol(raw_symbol)
if symbol in output:
raise ValueError(
"%s contains duplicate normalized symbol %s" % (name, symbol)
)
output[symbol] = value_validator(raw_value, "%s.%s" % (name, symbol))
return dict((symbol, output[symbol]) for symbol in sorted(output))
def _finite_non_negative(value, name):
if isinstance(value, bool):
raise ValueError("%s must be a finite non-negative number" % name)
try:
numeric = float(value)
except (TypeError, ValueError):
raise ValueError("%s must be a finite non-negative number" % name)
if not math.isfinite(numeric) or numeric < 0.0:
raise ValueError("%s must be a finite non-negative number" % name)
return numeric
def _non_negative_quantity(value, name):
numeric = _finite_non_negative(value, name)
if numeric != math.floor(numeric):
raise ValueError("%s must be a whole-share quantity" % name)
return int(numeric)
def _positive_price(value, name):
numeric = _finite_non_negative(value, name)
if numeric <= 0.0:
raise ValueError("%s must be positive" % name)
return numeric
def _optional_non_empty_string(value, name):
if value is None:
return None
if not isinstance(value, str) or not value.strip():
raise ValueError("%s must be a non-empty string when provided" % name)
return value
def _sha256_hex(value, name, allow_none=False):
if value is None and allow_none:
return None
if (
not isinstance(value, str)
or len(value) != 64
or _any_true(
character not in "0123456789abcdef"
for character in value
)
):
raise ValueError(
"%s must be a 64-character lowercase SHA-256 digest" % name
)
if value == "0" * 64:
raise ValueError("%s cannot be an all-zero placeholder" % name)
return value
def _reject_quantity_fields(value, path="$"):
if hasattr(value, "items"):
for raw_key, child in value.items():
key = str(raw_key)
normalized = key.lower().replace("-", "_")
if "quantity" in normalized or "quantities" in normalized:
raise ValueError(
"%s.%s: quantities do not belong in TargetPackageV1"
% (path, key)
)
_reject_quantity_fields(child, "%s.%s" % (path, key))
elif isinstance(value, list):
for index, child in enumerate(value):
_reject_quantity_fields(child, "%s[%d]" % (path, index))
def _verify_identifier(value, name):
if (
not isinstance(value, str)
or re.match(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$", value) is None
):
raise ValueError("%s is not a canonical identifier" % name)
return value
def _verify_target_package_clock(signal_as_of, next_session):
if (
not isinstance(signal_as_of, str)
or re.match(
r"^[0-9]{4}-[0-9]{2}-[0-9]{2}T15:00:00\+08:00$",
signal_as_of,
)
is None
):
raise ValueError("signal_as_of must be canonical China-market close")
if (
not isinstance(next_session, str)
or re.match(r"^[0-9]{4}-[0-9]{2}-[0-9]{2}$", next_session) is None
):
raise ValueError("next_session must use canonical YYYY-MM-DD")
if next_session <= signal_as_of[:10]:
raise ValueError("next_session must follow signal_as_of")
def _target_package_content_sha256(package):
body = dict(package)
body.pop("package_sha256", None)
_reject_quantity_fields(body)
try:
payload = _canonical_json(body).encode("utf-8")
except (TypeError, ValueError) as error:
raise ValueError(
"target package must contain finite JSON values only"
) from error
return hashlib.sha256(payload).hexdigest()
def verify_target_package(
package,
expected_signal_as_of=None,
expected_next_session=None,
expected_decision_id=None,
expected_package_sha256=None,
):
"""Verify the platform-critical subset of a sealed ``TargetPackageV1``.
This intentionally duplicates a small, Python-3.6-compatible safety
boundary from the richer local validator. Hosted runtimes must verify the
embedded package again instead of trusting the bundling process.
"""
if not hasattr(package, "items"):
raise TypeError("target package must be a mapping")
candidate = _json_copy(package, "target package")
_reject_quantity_fields(candidate)
fields = set(candidate)
if fields != _TARGET_PACKAGE_FIELDS:
missing = sorted(_TARGET_PACKAGE_FIELDS - fields)
unknown = sorted(fields - _TARGET_PACKAGE_FIELDS)
raise ValueError(
"target package fields mismatch; missing=%s, unknown=%s"
% (missing, unknown)
)
if candidate["schema_version"] != "1.0":
raise ValueError("unsupported target package schema_version")
if candidate["package_type"] != "TargetPackageV1":
raise ValueError("unsupported target package type")
for name in (
"package_id",
"decision_id",
"release",
"evidence_class",
"model_id",
):
_verify_identifier(candidate[name], name)
_verify_target_package_clock(
candidate["signal_as_of"], candidate["next_session"]
)
for name in _TARGET_PACKAGE_HASH_FIELDS:
_sha256_hex(candidate[name], name)
_sha256_hex(candidate["package_sha256"], "package_sha256")
raw_weights = candidate["target_weights"]
if not hasattr(raw_weights, "items") or not raw_weights:
raise ValueError("target_weights must be a non-empty mapping")
weights = {}
for raw_symbol, raw_weight in raw_weights.items():
symbol = normalize_symbol(raw_symbol)
if symbol != raw_symbol:
raise ValueError(
"target_weights symbol must be canonical: %s" % raw_symbol
)
if symbol in weights:
raise ValueError("duplicate target weight for %s" % symbol)
weight = _finite_non_negative(
raw_weight, "target_weights.%s" % symbol
)
if weight > 1.0:
raise ValueError("target weight exceeds one for %s" % symbol)
weights[symbol] = weight
gross = math.fsum(weights.values())
if gross > 1.0 + 1e-12:
raise ValueError("target_weights gross exceeds one")
universe = candidate["universe"]
if not hasattr(universe, "items") or not universe:
raise ValueError("universe must be a non-empty mapping")
canonical_universe = {}
for raw_symbol, raw_state in universe.items():
symbol = normalize_symbol(raw_symbol)
if symbol != raw_symbol:
raise ValueError("universe symbol must be canonical: %s" % raw_symbol)
if symbol in canonical_universe:
raise ValueError("duplicate universe symbol %s" % symbol)
if not hasattr(raw_state, "items"):
raise ValueError("universe state must be a mapping for %s" % symbol)
state = raw_state.get("state")
if state not in (
"openable",
"hold_only",
"sell_only",
"frozen",
"excluded",
):
raise ValueError("unsupported universe state for %s" % symbol)
canonical_universe[symbol] = state
missing_universe = sorted(set(weights) - set(canonical_universe))
if missing_universe:
raise ValueError(
"target symbols are absent from universe: %s" % missing_universe
)
for symbol, weight in weights.items():
if canonical_universe[symbol] == "excluded" and weight > 1e-12:
raise ValueError("excluded symbol has positive weight: %s" % symbol)
constraints = candidate["constraints"]
if not hasattr(constraints, "items"):
raise ValueError("constraints must be a mapping")
if constraints.get("long_only") is not True:
raise ValueError("TargetPackageV1 must be long-only")
declared_gross = _finite_non_negative(
constraints.get("actual_gross_weight"),
"constraints.actual_gross_weight",
)
max_gross = _finite_non_negative(
constraints.get("max_gross_weight"),
"constraints.max_gross_weight",
)
max_single = _finite_non_negative(
constraints.get("max_single_weight"),
"constraints.max_single_weight",
)
if abs(declared_gross - gross) > 1e-12:
raise ValueError("declared gross does not equal target_weights gross")
if gross > max_gross + 1e-12 or max_gross > 1.0:
raise ValueError("target_weights exceed max_gross_weight")
if max_single <= 0.0 or max_single > 1.0:
raise ValueError("invalid max_single_weight")
if _any_true(
weight > max_single + 1e-12 for weight in weights.values()
):
raise ValueError("target_weights exceed max_single_weight")
model_claim = " ".join(
(
candidate["release"],
candidate["evidence_class"],
)
).lower()
if (
"baseline" in model_claim
and "portable-momentum" in candidate["model_id"].lower()
):
raise ValueError(
"portable-momentum cannot claim a baseline target package"
)
expected_hash = _target_package_content_sha256(candidate)
if candidate["package_sha256"] != expected_hash:
raise ValueError("target package content hash mismatch")
if (
expected_signal_as_of is not None
and candidate["signal_as_of"] != expected_signal_as_of
):
raise ValueError("target package signal_as_of does not match request")
if (
expected_next_session is not None
and candidate["next_session"] != expected_next_session
):
raise ValueError("target package next_session does not match request")
if (
expected_decision_id is not None
and candidate["decision_id"] != expected_decision_id
):
raise ValueError("target package decision_id does not match request")
if expected_package_sha256 is not None:
_sha256_hex(
expected_package_sha256,
"expected_package_sha256",
)
if candidate["package_sha256"] != expected_package_sha256:
raise ValueError("target package digest does not match request")
return candidate
def target_package_tape_sha256(packages):
"""Hash the exact ordered canonical JSON package array."""
if not isinstance(packages, (list, tuple)):
raise TypeError("target package tape must be a list or tuple")
try:
payload = _canonical_json(list(packages)).encode("utf-8")
except (TypeError, ValueError) as error:
raise ValueError(
"target package tape must contain finite JSON values only"
) from error
return hashlib.sha256(payload).hexdigest()
def lookup_target_package_exact(
packages,
signal_as_of,
next_session,
decision_id=None,
expected_tape_sha256=None,
expected_count=None,
):
"""Verify a complete tape, then return one exact dual-clock decision."""
if not isinstance(packages, (list, tuple)) or not packages:
raise ValueError("target package tape must be a non-empty list or tuple")
_verify_target_package_clock(signal_as_of, next_session)
decision_id = _optional_non_empty_string(decision_id, "decision_id")
if expected_count is not None:
if (
isinstance(expected_count, bool)
or not isinstance(expected_count, int)
or expected_count < 0
):
raise ValueError("expected_count must be a non-negative integer")
if len(packages) != expected_count:
raise ValueError("target package tape count mismatch")
if expected_tape_sha256 is not None:
_sha256_hex(expected_tape_sha256, "expected_tape_sha256")
if target_package_tape_sha256(packages) != expected_tape_sha256:
raise ValueError("target package tape digest mismatch")
verified = []
package_ids = set()
decision_ids = set()
clock_keys = set()
for raw_package in packages:
package = verify_target_package(raw_package)
clock_key = (package["signal_as_of"], package["next_session"])
if package["package_id"] in package_ids:
raise ValueError("duplicate target package_id")
if package["decision_id"] in decision_ids:
raise ValueError("duplicate target decision_id")
if clock_key in clock_keys:
raise ValueError("duplicate target package clock")
package_ids.add(package["package_id"])
decision_ids.add(package["decision_id"])
clock_keys.add(clock_key)
verified.append(package)
matches = [
package
for package in verified
if package["signal_as_of"] == signal_as_of
and package["next_session"] == next_session
]
if not matches:
raise TargetPackageLookupMiss(
"no target package for exact signal_as_of/next_session clock"
)
if len(matches) != 1:
raise ValueError("ambiguous exact target package decision")
if (
decision_id is not None
and matches[0]["decision_id"] != decision_id
):
raise ValueError("exact target package decision_id mismatch")
return _json_copy(matches[0], "target package")
def momentum_score(prices, lookback=20, skip=0): def momentum_score(prices, lookback=20, skip=0):
"""Return trailing simple momentum, or ``None`` if history is insufficient. """Return trailing simple momentum, or ``None`` if history is insufficient.
@@ -318,6 +758,108 @@ def order_deltas(
return result return result
def build_target_weight_plan(
target_weights,
prices,
current,
sellable,
equity,
cash_buffer=0.02,
lot_size=100,
decision_id=None,
package_sha256=None,
):
"""Bind canonical post-risk weights to one account without recomputing Alpha.
``prices`` must cover exactly every target or currently managed symbol.
Missing prices fail closed even for a held symbol whose desired weight is
zero. A missing ``sellable`` entry is safe and means zero sellable shares.
"""
weights = _canonical_mapping(
target_weights,
"target_weights",
_finite_non_negative,
)
gross = math.fsum(weights.values())
if _any_true(weight > 1.0 for weight in weights.values()):
raise ValueError("target weight must not exceed one")
if gross > 1.0 + 1e-12:
raise ValueError("target weight gross must not exceed one")
canonical_current = _canonical_mapping(
current,
"current",
_non_negative_quantity,
)
canonical_sellable = _canonical_mapping(
sellable,
"sellable",
_non_negative_quantity,
)
unknown_sellable = sorted(set(canonical_sellable) - set(canonical_current))
if unknown_sellable:
raise ValueError(
"sellable contains symbols absent from current: %s"
% unknown_sellable
)
for symbol, quantity in canonical_sellable.items():
if quantity > canonical_current[symbol]:
raise ValueError(
"sellable exceeds current quantity for %s" % symbol
)
canonical_prices = _canonical_mapping(
prices,
"prices",
_positive_price,
)
managed_symbols = set(weights) | set(canonical_current)
price_symbols = set(canonical_prices)
missing_prices = sorted(managed_symbols - price_symbols)
extra_prices = sorted(price_symbols - managed_symbols)
if missing_prices or extra_prices:
if missing_prices:
raise KeyError(
"prices do not cover managed symbols: missing=%s"
% missing_prices
)
raise ValueError(
"prices contain unmanaged symbols: extra=%s" % extra_prices
)
complete_weights = dict(
(symbol, weights.get(symbol, 0.0))
for symbol in sorted(managed_symbols)
)
targets = target_quantities(
complete_weights,
canonical_prices,
equity,
lot_size,
cash_buffer,
)
orders = order_deltas(
targets,
canonical_current,
canonical_sellable,
lot_size,
)
decision_id = _optional_non_empty_string(decision_id, "decision_id")
package_sha256 = _sha256_hex(
package_sha256,
"package_sha256",
allow_none=True,
)
return {
"decision_id": decision_id,
"package_sha256": package_sha256,
"weights": complete_weights,
"targets": targets,
"orders": orders,
}
def build_rebalance_plan( def build_rebalance_plan(
price_history, price_history,
current, current,
@@ -363,12 +905,17 @@ def build_rebalance_plan(
__all__ = [ __all__ = [
"build_rebalance_plan", "build_rebalance_plan",
"build_target_weight_plan",
"capped_target_weights", "capped_target_weights",
"convert_symbol", "convert_symbol",
"lookup_target_package_exact",
"momentum_score", "momentum_score",
"normalize_symbol", "normalize_symbol",
"order_deltas", "order_deltas",
"rank_momentum", "rank_momentum",
"round_board_lot", "round_board_lot",
"TargetPackageLookupMiss",
"target_package_tape_sha256",
"target_quantities", "target_quantities",
"verify_target_package",
] ]
+619
View File
@@ -0,0 +1,619 @@
"""Tamper-evident, per-layer deployment reachability evidence.
This contract deliberately separates six different claims for every Quant60
layer. A source file can prove that code is implemented; it cannot, by
itself, prove that the code is tested, reachable from an entrypoint, or
observed on a real trading platform.
All evidence is repository-relative, content-addressed, and checked against
the filesystem. The manifest also carries a self-hash so edits to statuses or
evidence fail closed even when every referenced artifact is unchanged.
"""
from __future__ import annotations
import copy
import hashlib
import json
import os
import re
import tempfile
from datetime import datetime, timezone
from pathlib import Path, PurePosixPath
from typing import Any, Mapping
from .contracts import ContractValidationError, load_schema, validate_contract
from .ledger import canonical_json
SCHEMA_VERSION = "1.0"
ARTIFACT_TYPE = "quant60-release-reachability"
LAYERS = ("universe", "alpha", "portfolio", "risk", "execution")
CAPABILITIES = (
"implemented",
"unit_tested",
"local_entrypoint_reachable",
"jq_entrypoint_reachable",
"qmt_entrypoint_reachable",
"real_platform_observed",
)
CAPABILITY_EVIDENCE = {
"implemented": ("source", "repository"),
"unit_tested": ("test", "repository"),
"local_entrypoint_reachable": ("entrypoint", "local"),
"jq_entrypoint_reachable": ("entrypoint", "joinquant"),
"qmt_entrypoint_reachable": ("entrypoint", "qmt"),
}
RUN_EVIDENCE_ROOTS = frozenset({"artifacts", "evidence", "runs"})
ENTRYPOINT_ROOTS = frozenset({"platforms", "src", "tools"})
IDENTIFIER_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$")
SHA256_PATTERN = re.compile(r"^[0-9a-f]{64}$")
class ReleaseReachabilityError(ValueError):
"""A reachability manifest is malformed or its evidence cannot be trusted."""
def _project_root() -> Path:
return Path(__file__).resolve().parents[2]
def _json_copy(value: Any) -> Any:
try:
return json.loads(canonical_json(value))
except (TypeError, ValueError, json.JSONDecodeError) as exc:
raise ReleaseReachabilityError(
"reachability manifest must contain finite JSON values only"
) from exc
def _strict_json(path: Path) -> Any:
def reject_constant(value: str) -> None:
raise ReleaseReachabilityError(
f"non-finite JSON constant in reachability manifest: {value}"
)
def reject_duplicate_keys(
pairs: list[tuple[str, Any]],
) -> dict[str, Any]:
output: dict[str, Any] = {}
for key, value in pairs:
if key in output:
raise ReleaseReachabilityError(
f"duplicate JSON key in reachability manifest: {key}"
)
output[key] = value
return output
try:
return json.loads(
path.read_text(encoding="utf-8"),
parse_constant=reject_constant,
object_pairs_hook=reject_duplicate_keys,
)
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
raise ReleaseReachabilityError(
f"reachability manifest is not valid UTF-8 JSON: {exc}"
) from exc
def _manifest_body(payload: Mapping[str, Any]) -> dict[str, Any]:
return {
key: value
for key, value in payload.items()
if key != "manifest_sha256"
}
def release_reachability_sha256(payload: Mapping[str, Any]) -> str:
"""Return the canonical self-hash, excluding ``manifest_sha256``."""
return hashlib.sha256(
canonical_json(_manifest_body(payload)).encode("utf-8")
).hexdigest()
def _canonical_utc(value: Any, field: str) -> datetime:
if not isinstance(value, str):
raise ReleaseReachabilityError(f"{field} must be an ISO-8601 string")
try:
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
except ValueError as exc:
raise ReleaseReachabilityError(
f"{field} must be a valid ISO-8601 timestamp"
) from exc
if parsed.tzinfo is None or parsed.utcoffset() is None:
raise ReleaseReachabilityError(f"{field} must include a timezone")
normalized = (
parsed.astimezone(timezone.utc)
.replace(microsecond=0)
.isoformat()
.replace("+00:00", "Z")
)
if value != normalized:
raise ReleaseReachabilityError(
f"{field} must use canonical UTC YYYY-MM-DDTHH:MM:SSZ"
)
return parsed.astimezone(timezone.utc)
def _identifier(value: Any, field: str) -> str:
if not isinstance(value, str) or IDENTIFIER_PATTERN.fullmatch(value) is None:
raise ReleaseReachabilityError(
f"{field} must be a non-empty canonical identifier"
)
return value
def _sha256(value: Any, field: str) -> str:
if not isinstance(value, str) or SHA256_PATTERN.fullmatch(value) is None:
raise ReleaseReachabilityError(
f"{field} must be a lowercase SHA-256 digest"
)
if value == "0" * 64:
raise ReleaseReachabilityError(f"{field} cannot be an all-zero placeholder")
return value
def _repository_relative_path(value: Any, field: str) -> str:
if not isinstance(value, str) or not value:
raise ReleaseReachabilityError(
f"{field} must be a non-empty repository-relative path"
)
if "\\" in value:
raise ReleaseReachabilityError(f"{field} must use POSIX separators")
candidate = PurePosixPath(value)
if (
candidate.is_absolute()
or value != candidate.as_posix()
or any(part in {"", ".", ".."} for part in candidate.parts)
):
raise ReleaseReachabilityError(
f"{field} must be a normalized repository-relative path"
)
return value
def _artifact_file(
repository_root: Path,
relative_path: str,
*,
field: str,
) -> Path:
try:
root = repository_root.resolve(strict=True)
except OSError as exc:
raise ReleaseReachabilityError(
"repository_root must be an existing directory"
) from exc
if not root.is_dir():
raise ReleaseReachabilityError("repository_root must be a directory")
lexical = root.joinpath(*PurePosixPath(relative_path).parts)
if lexical.is_symlink():
raise ReleaseReachabilityError(f"{field} cannot reference a symlink")
try:
resolved = lexical.resolve(strict=True)
except OSError as exc:
raise ReleaseReachabilityError(
f"{field} does not reference an existing artifact: {relative_path}"
) from exc
if not resolved.is_relative_to(root) or not resolved.is_file():
raise ReleaseReachabilityError(
f"{field} must reference a regular file inside repository_root"
)
if resolved.stat().st_size <= 0:
raise ReleaseReachabilityError(
f"{field} cannot reference an empty artifact"
)
return resolved
def _validate_artifact_path_class(kind: str, path: str, field: str) -> None:
root = PurePosixPath(path).parts[0]
if kind == "source" and root != "src":
raise ReleaseReachabilityError(
f"{field}: source evidence must live under src/"
)
if kind == "test" and root != "tests":
raise ReleaseReachabilityError(
f"{field}: test evidence must live under tests/"
)
if kind == "entrypoint" and root not in ENTRYPOINT_ROOTS:
raise ReleaseReachabilityError(
f"{field}: entrypoint evidence must live under "
"platforms/, src/, or tools/"
)
if kind == "platform_run" and root not in RUN_EVIDENCE_ROOTS:
raise ReleaseReachabilityError(
f"{field}: real-platform evidence must live under "
"artifacts/, evidence/, or runs/"
)
def build_evidence_artifact(
*,
artifact_id: str,
kind: str,
path: str,
platform: str,
repository_root: str | Path | None = None,
observed_at: str | None = None,
run_id: str | None = None,
) -> dict[str, Any]:
"""Content-address one evidence file for use in a reachability claim."""
root = _project_root() if repository_root is None else Path(repository_root)
normalized_path = _repository_relative_path(path, "artifact.path")
if kind not in {"source", "test", "entrypoint", "platform_run"}:
raise ReleaseReachabilityError("artifact.kind is not supported")
if platform not in {"repository", "local", "joinquant", "qmt"}:
raise ReleaseReachabilityError("artifact.platform is not supported")
_validate_artifact_path_class(kind, normalized_path, "artifact.path")
if kind == "platform_run":
if platform not in {"joinquant", "qmt"}:
raise ReleaseReachabilityError(
"platform_run evidence requires joinquant or qmt"
)
if observed_at is None or run_id is None:
raise ReleaseReachabilityError(
"platform_run evidence requires observed_at and run_id"
)
_canonical_utc(observed_at, "artifact.observed_at")
_identifier(run_id, "artifact.run_id")
elif observed_at is not None or run_id is not None:
raise ReleaseReachabilityError(
"only platform_run evidence may carry observed_at and run_id"
)
resolved = _artifact_file(root, normalized_path, field="artifact.path")
artifact: dict[str, Any] = {
"artifact_id": _identifier(artifact_id, "artifact.artifact_id"),
"kind": kind,
"path": normalized_path,
"sha256": hashlib.sha256(resolved.read_bytes()).hexdigest(),
"platform": platform,
}
if observed_at is not None:
artifact["observed_at"] = observed_at
if run_id is not None:
artifact["run_id"] = run_id
return artifact
def _expected_summary(layers: Mapping[str, Any]) -> dict[str, bool]:
if set(layers) != set(LAYERS):
raise ReleaseReachabilityError(
f"layers must be exactly {list(LAYERS)}"
)
for layer in LAYERS:
claims = layers[layer]
if not isinstance(claims, Mapping) or set(claims) != set(CAPABILITIES):
raise ReleaseReachabilityError(
f"layers.{layer} must contain exactly {list(CAPABILITIES)}"
)
for capability in CAPABILITIES:
claim = claims[capability]
if (
not isinstance(claim, Mapping)
or set(claim) != {"status", "artifacts"}
or type(claim.get("status")) is not bool
or not isinstance(claim.get("artifacts"), list)
):
raise ReleaseReachabilityError(
f"layers.{layer}.{capability} must contain a boolean "
"status and an artifacts array"
)
return {
f"all_layers_{capability}": all(
bool(layers[layer][capability]["status"])
for layer in LAYERS
)
for capability in CAPABILITIES
}
def _validate_artifact(
artifact: Any,
*,
layer: str,
capability: str,
repository_root: Path,
generated_at: datetime,
seen_artifact_ids: set[str],
) -> dict[str, Any]:
field = f"layers.{layer}.{capability}.artifacts"
if not isinstance(artifact, Mapping):
raise ReleaseReachabilityError(f"{field} entries must be objects")
normalized = _json_copy(dict(artifact))
base_fields = {"artifact_id", "kind", "path", "sha256", "platform"}
run_fields = base_fields | {"observed_at", "run_id"}
expected_fields = (
run_fields
if normalized.get("kind") == "platform_run"
else base_fields
)
if set(normalized) != expected_fields:
raise ReleaseReachabilityError(
f"{field} entry fields must be exactly {sorted(expected_fields)}"
)
artifact_id = _identifier(
normalized["artifact_id"],
f"{field}.artifact_id",
)
if artifact_id in seen_artifact_ids:
raise ReleaseReachabilityError(
f"artifact_id {artifact_id!r} is reused across claims"
)
seen_artifact_ids.add(artifact_id)
kind = normalized["kind"]
platform = normalized["platform"]
if capability == "real_platform_observed":
if kind != "platform_run" or platform not in {"joinquant", "qmt"}:
raise ReleaseReachabilityError(
f"{field}: real_platform_observed requires a "
"joinquant/qmt platform_run artifact"
)
observed_at = _canonical_utc(
normalized["observed_at"],
f"{field}.observed_at",
)
if observed_at > generated_at:
raise ReleaseReachabilityError(
f"{field}.observed_at cannot be after generated_at"
)
_identifier(normalized["run_id"], f"{field}.run_id")
else:
expected_kind, expected_platform = CAPABILITY_EVIDENCE[capability]
if (kind, platform) != (expected_kind, expected_platform):
raise ReleaseReachabilityError(
f"{field}: {capability} requires kind={expected_kind!r} "
f"and platform={expected_platform!r}"
)
path = _repository_relative_path(normalized["path"], f"{field}.path")
_validate_artifact_path_class(kind, path, field)
expected_hash = _sha256(normalized["sha256"], f"{field}.sha256")
actual_hash = hashlib.sha256(
_artifact_file(
repository_root,
path,
field=f"{field}.path",
).read_bytes()
).hexdigest()
if actual_hash != expected_hash:
raise ReleaseReachabilityError(
f"{field}: artifact hash mismatch for {path}; "
f"expected {expected_hash}, got {actual_hash}"
)
return normalized
def validate_release_reachability(
payload: Mapping[str, Any],
*,
repository_root: str | Path | None = None,
expected_release_id: str | None = None,
expected_manifest_sha256: str | None = None,
) -> dict[str, Any]:
"""Validate shape, status semantics, hashes, and every evidence file."""
if not isinstance(payload, Mapping):
raise ReleaseReachabilityError("reachability manifest must be an object")
normalized = _json_copy(dict(payload))
try:
validate_contract(
normalized,
load_schema("release_reachability.schema.json"),
)
except (ContractValidationError, FileNotFoundError, ValueError) as exc:
raise ReleaseReachabilityError(
f"reachability schema validation failed: {exc}"
) from exc
release_id = _identifier(normalized["release_id"], "release_id")
if expected_release_id is not None and release_id != expected_release_id:
raise ReleaseReachabilityError(
"release_id does not match expected identity"
)
generated_at = _canonical_utc(normalized["generated_at"], "generated_at")
supplied_hash = _sha256(
normalized["manifest_sha256"],
"manifest_sha256",
)
if (
expected_manifest_sha256 is not None
and supplied_hash != _sha256(
expected_manifest_sha256,
"expected_manifest_sha256",
)
):
raise ReleaseReachabilityError(
"manifest_sha256 does not match expected identity"
)
actual_manifest_hash = release_reachability_sha256(normalized)
if supplied_hash != actual_manifest_hash:
raise ReleaseReachabilityError(
"reachability manifest SHA-256 mismatch"
)
layers = normalized["layers"]
if set(layers) != set(LAYERS):
raise ReleaseReachabilityError(
f"layers must be exactly {list(LAYERS)}"
)
root = _project_root() if repository_root is None else Path(repository_root)
seen_artifact_ids: set[str] = set()
for layer in LAYERS:
claims = layers[layer]
if set(claims) != set(CAPABILITIES):
raise ReleaseReachabilityError(
f"layers.{layer} must contain exactly {list(CAPABILITIES)}"
)
for capability in CAPABILITIES:
claim = claims[capability]
if not isinstance(claim, Mapping) or set(claim) != {
"status",
"artifacts",
}:
raise ReleaseReachabilityError(
f"layers.{layer}.{capability} must contain only "
"status and artifacts"
)
status = claim["status"]
artifacts = claim["artifacts"]
if type(status) is not bool or not isinstance(artifacts, list):
raise ReleaseReachabilityError(
f"layers.{layer}.{capability} has invalid status/artifacts"
)
if status != bool(artifacts):
raise ReleaseReachabilityError(
f"layers.{layer}.{capability}: status must be true "
"iff at least one evidence artifact exists"
)
for artifact in artifacts:
_validate_artifact(
artifact,
layer=layer,
capability=capability,
repository_root=root,
generated_at=generated_at,
seen_artifact_ids=seen_artifact_ids,
)
if claims["unit_tested"]["status"] and not claims["implemented"]["status"]:
raise ReleaseReachabilityError(
f"layers.{layer}: unit_tested requires implemented"
)
for capability in (
"local_entrypoint_reachable",
"jq_entrypoint_reachable",
"qmt_entrypoint_reachable",
):
if (
claims[capability]["status"]
and not claims["implemented"]["status"]
):
raise ReleaseReachabilityError(
f"layers.{layer}: {capability} requires implemented"
)
for artifact in claims["real_platform_observed"]["artifacts"]:
platform_capability = {
"joinquant": "jq_entrypoint_reachable",
"qmt": "qmt_entrypoint_reachable",
}[artifact["platform"]]
if not claims[platform_capability]["status"]:
raise ReleaseReachabilityError(
f"layers.{layer}: real {artifact['platform']} observation "
f"requires {platform_capability}"
)
expected_summary = _expected_summary(layers)
if normalized["summary"] != expected_summary:
raise ReleaseReachabilityError(
"summary is not the exact derived all-layer status"
)
return normalized
def seal_release_reachability(
body: Mapping[str, Any],
*,
repository_root: str | Path | None = None,
) -> dict[str, Any]:
"""Add derived summary/self-hash and perform full fail-closed validation."""
if not isinstance(body, Mapping):
raise ReleaseReachabilityError("reachability body must be an object")
normalized = _json_copy(dict(body))
if "manifest_sha256" in normalized:
raise ReleaseReachabilityError(
"unsealed reachability body cannot contain manifest_sha256"
)
if "summary" in normalized:
raise ReleaseReachabilityError(
"unsealed reachability body cannot contain derived summary"
)
layers = normalized.get("layers")
if not isinstance(layers, Mapping):
raise ReleaseReachabilityError("layers must be an object")
normalized["summary"] = _expected_summary(layers)
normalized["manifest_sha256"] = release_reachability_sha256(normalized)
return validate_release_reachability(
normalized,
repository_root=repository_root,
)
def build_release_reachability(
*,
release_id: str,
generated_at: str,
layers: Mapping[str, Any],
repository_root: str | Path | None = None,
) -> dict[str, Any]:
"""Construct and seal one complete five-layer reachability manifest."""
return seal_release_reachability(
{
"schema_version": SCHEMA_VERSION,
"artifact_type": ARTIFACT_TYPE,
"release_id": release_id,
"generated_at": generated_at,
"layers": copy.deepcopy(dict(layers)),
},
repository_root=repository_root,
)
def write_release_reachability(
payload: Mapping[str, Any],
path: str | Path,
*,
repository_root: str | Path | None = None,
) -> Path:
"""Atomically write a fully verified canonical manifest."""
verified = validate_release_reachability(
payload,
repository_root=repository_root,
)
destination = Path(path)
destination.parent.mkdir(parents=True, exist_ok=True)
descriptor, temporary_name = tempfile.mkstemp(
prefix=f".{destination.name}.",
suffix=".tmp",
dir=str(destination.parent),
)
try:
with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
handle.write(canonical_json(verified))
handle.write("\n")
handle.flush()
os.fsync(handle.fileno())
os.replace(temporary_name, destination)
except BaseException:
try:
os.unlink(temporary_name)
except FileNotFoundError:
pass
raise
return destination
def load_verified_release_reachability(
path: str | Path,
*,
repository_root: str | Path | None = None,
expected_release_id: str | None = None,
expected_manifest_sha256: str | None = None,
) -> dict[str, Any]:
"""Strictly load a manifest and re-check both it and all artifacts."""
raw = _strict_json(Path(path))
return validate_release_reachability(
raw,
repository_root=repository_root,
expected_release_id=expected_release_id,
expected_manifest_sha256=expected_manifest_sha256,
)
+667
View File
@@ -0,0 +1,667 @@
"""Canonical, quantity-free target-package contract.
``TargetPackageV1`` is the boundary between research/portfolio construction
and a platform execution adapter. It deliberately contains post-risk target
*weights*, but never share quantities, orders, fills, cash or account state.
Those values belong to a later, account-bound execution plan.
The validator is intentionally fail closed. JSON Schema provides the wire
shape while this module enforces cross-field invariants that JSON Schema
cannot express compactly: the China-close decision clock, canonical symbols,
gross exposure, content hashing and unambiguous tape lookup.
"""
from __future__ import annotations
import copy
import hashlib
import json
import math
from dataclasses import dataclass
from datetime import date, datetime, time, timedelta, timezone
from pathlib import Path
from typing import Any, Iterable, Iterator, Mapping
from .contracts import ContractValidationError, load_schema, validate_contract
from .ledger import canonical_json
from .portable_core import normalize_symbol
PACKAGE_TYPE = "TargetPackageV1"
SCHEMA_VERSION = "1.0"
SHANGHAI_OFFSET = timedelta(hours=8)
SHA256_FIELDS = (
"model_sha256",
"data_sha256",
"feature_sha256",
"risk_sha256",
"cost_sha256",
"optimizer_sha256",
"config_sha256",
"source_sha256",
)
UNIVERSE_STATES = {
"openable",
"hold_only",
"sell_only",
"frozen",
"excluded",
}
WEIGHT_TOLERANCE = 1e-12
class TargetPackageError(ValueError):
"""A target package is incomplete, ambiguous or internally inconsistent."""
class DuplicateTargetDecisionError(TargetPackageError):
"""A tape contains more than one representation of a decision identity."""
class TargetPackageNotFoundError(TargetPackageError, LookupError):
"""An exact signal/session pair is absent from a target-package tape."""
def _json_copy(value: Any) -> Any:
"""Return a detached JSON value and reject non-JSON/non-finite input."""
try:
return json.loads(canonical_json(value))
except (TypeError, ValueError) as exc:
raise TargetPackageError(
"target package must contain finite JSON values only"
) from exc
def _strict_json_text(value: str, *, source: str) -> Any:
def reject_constant(constant: str) -> None:
raise TargetPackageError(
f"{source} contains a non-finite JSON constant: {constant}"
)
def reject_duplicate_keys(
pairs: list[tuple[str, Any]],
) -> dict[str, Any]:
output: dict[str, Any] = {}
for key, child in pairs:
if key in output:
raise TargetPackageError(
f"{source} contains a duplicate JSON key: {key}"
)
output[key] = child
return output
try:
return json.loads(
value,
parse_constant=reject_constant,
object_pairs_hook=reject_duplicate_keys,
)
except json.JSONDecodeError as exc:
raise TargetPackageError(
f"{source} is not valid JSON: {exc}"
) from exc
def _reject_quantity_fields(value: Any, path: str = "$") -> None:
if isinstance(value, Mapping):
for raw_key, child in value.items():
key = str(raw_key)
normalized = key.lower().replace("-", "_")
if "quantity" in normalized or "quantities" in normalized:
raise TargetPackageError(
f"{path}.{key}: quantities do not belong in TargetPackageV1"
)
_reject_quantity_fields(child, f"{path}.{key}")
elif isinstance(value, list):
for index, child in enumerate(value):
_reject_quantity_fields(child, f"{path}[{index}]")
def _parse_signal_as_of(value: Any) -> datetime:
if not isinstance(value, str):
raise TargetPackageError("signal_as_of must be an ISO-8601 string")
try:
parsed = datetime.fromisoformat(value)
except ValueError as exc:
raise TargetPackageError(
"signal_as_of must be a valid ISO-8601 timestamp"
) from exc
if parsed.tzinfo is None or parsed.utcoffset() is None:
raise TargetPackageError("signal_as_of must include a timezone offset")
if parsed.utcoffset() != SHANGHAI_OFFSET:
raise TargetPackageError(
"signal_as_of must use the Asia/Shanghai +08:00 offset"
)
if parsed.timetz().replace(tzinfo=None) != time(15, 0):
raise TargetPackageError(
"signal_as_of must be the completed 15:00 China-market close"
)
canonical = (
f"{parsed.date().isoformat()}T15:00:00+08:00"
)
if value != canonical:
raise TargetPackageError(
"signal_as_of must use canonical "
"YYYY-MM-DDT15:00:00+08:00 serialization"
)
return parsed
def _parse_next_session(value: Any, signal_as_of: datetime) -> date:
if not isinstance(value, str):
raise TargetPackageError("next_session must be an ISO date string")
try:
parsed = date.fromisoformat(value)
except ValueError as exc:
raise TargetPackageError("next_session must be a valid ISO date") from exc
if value != parsed.isoformat():
raise TargetPackageError("next_session must use canonical YYYY-MM-DD")
if parsed <= signal_as_of.date():
raise TargetPackageError("next_session must be after the signal session")
if parsed.weekday() >= 5:
raise TargetPackageError("next_session cannot be a weekend")
return parsed
def _validate_sha256(value: Any, name: str) -> str:
if (
not isinstance(value, str)
or len(value) != 64
or any(character not in "0123456789abcdef" for character in value)
):
raise TargetPackageError(
f"{name} must be a 64-character lowercase SHA-256 hex digest"
)
if value == "0" * 64:
raise TargetPackageError(f"{name} cannot be an all-zero placeholder")
return value
def _canonical_symbol(value: Any, name: str) -> str:
if not isinstance(value, str):
raise TargetPackageError(f"{name} must be a canonical symbol string")
try:
canonical = normalize_symbol(value)
except (TypeError, ValueError) as exc:
raise TargetPackageError(f"{name} is not a supported A-share symbol") from exc
if value != canonical:
raise TargetPackageError(
f"{name} must be canonical; expected {canonical!r}"
)
return canonical
def _finite_weight(value: Any, name: str) -> float:
if isinstance(value, bool) or not isinstance(value, (int, float)):
raise TargetPackageError(f"{name} must be a finite JSON number")
numeric = float(value)
if not math.isfinite(numeric):
raise TargetPackageError(f"{name} must be finite")
if numeric < 0 or numeric > 1:
raise TargetPackageError(f"{name} must lie in [0, 1]")
return numeric
def _validate_weights(value: Any) -> dict[str, float]:
if not isinstance(value, Mapping) or not value:
raise TargetPackageError("target_weights must be a non-empty object")
output: dict[str, float] = {}
for raw_symbol, raw_weight in value.items():
symbol = _canonical_symbol(raw_symbol, "target_weights symbol")
if symbol in output:
raise TargetPackageError(f"duplicate target weight for {symbol}")
output[symbol] = _finite_weight(
raw_weight,
f"target_weights.{symbol}",
)
if list(value) != sorted(value):
raise TargetPackageError(
"target_weights keys must be in canonical symbol order"
)
return output
def _validate_reasons(value: Any, symbol: str) -> tuple[str, ...]:
if not isinstance(value, list) or not value:
raise TargetPackageError(
f"universe.{symbol}.reasons must be a non-empty array"
)
reasons: list[str] = []
for reason in value:
if (
not isinstance(reason, str)
or not reason
or any(
character not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
for character in reason
)
):
raise TargetPackageError(
f"universe.{symbol}.reasons must contain uppercase reason codes"
)
reasons.append(reason)
if reasons != sorted(set(reasons)):
raise TargetPackageError(
f"universe.{symbol}.reasons must be unique and sorted"
)
return tuple(reasons)
def _validate_universe(value: Any) -> dict[str, dict[str, Any]]:
if not isinstance(value, Mapping) or not value:
raise TargetPackageError("universe must be a non-empty object")
output: dict[str, dict[str, Any]] = {}
for raw_symbol, raw_state in value.items():
symbol = _canonical_symbol(raw_symbol, "universe symbol")
if symbol in output:
raise TargetPackageError(f"duplicate universe symbol {symbol}")
if not isinstance(raw_state, Mapping):
raise TargetPackageError(f"universe.{symbol} must be an object")
if set(raw_state) != {"state", "reasons"}:
raise TargetPackageError(
f"universe.{symbol} must contain only state and reasons"
)
state = raw_state["state"]
if state not in UNIVERSE_STATES:
raise TargetPackageError(
f"universe.{symbol}.state is not a canonical universe state"
)
reasons = _validate_reasons(raw_state["reasons"], symbol)
if state == "openable" and reasons != ("ELIGIBLE",):
raise TargetPackageError(
f"openable universe member {symbol} must have reason ELIGIBLE"
)
if state != "openable" and "ELIGIBLE" in reasons:
raise TargetPackageError(
f"non-openable universe member {symbol} cannot be ELIGIBLE"
)
output[symbol] = {"state": state, "reasons": list(reasons)}
if list(value) != sorted(value):
raise TargetPackageError("universe keys must be in canonical symbol order")
return output
def _validate_constraints(
value: Any,
weights: Mapping[str, float],
) -> dict[str, Any]:
required = {
"constraint_state",
"long_only",
"max_single_weight",
"max_gross_weight",
"max_one_way_turnover",
"actual_gross_weight",
}
if not isinstance(value, Mapping) or set(value) != required:
missing = sorted(required - set(value or {})) if isinstance(value, Mapping) else sorted(required)
unknown = sorted(set(value or {}) - required) if isinstance(value, Mapping) else []
raise TargetPackageError(
f"constraints fields mismatch; missing={missing}, unknown={unknown}"
)
if value["constraint_state"] not in {
"FEASIBLE",
"REPAIRED",
"RISK_REDUCTION_ONLY",
"NO_TRADE",
}:
raise TargetPackageError("constraints.constraint_state is unsupported")
if value["long_only"] is not True:
raise TargetPackageError("TargetPackageV1 is strictly long-only")
max_single = _finite_weight(
value["max_single_weight"],
"constraints.max_single_weight",
)
if max_single <= 0:
raise TargetPackageError(
"constraints.max_single_weight must be positive"
)
max_gross = _finite_weight(
value["max_gross_weight"],
"constraints.max_gross_weight",
)
max_turnover = _finite_weight(
value["max_one_way_turnover"],
"constraints.max_one_way_turnover",
)
declared_gross = _finite_weight(
value["actual_gross_weight"],
"constraints.actual_gross_weight",
)
actual_gross = math.fsum(weights.values())
if abs(declared_gross - actual_gross) > WEIGHT_TOLERANCE:
raise TargetPackageError(
"constraints.actual_gross_weight does not equal target_weights gross"
)
if actual_gross > max_gross + WEIGHT_TOLERANCE:
raise TargetPackageError("target_weights exceed max_gross_weight")
breaches = [
symbol
for symbol, weight in weights.items()
if weight > max_single + WEIGHT_TOLERANCE
]
if breaches:
raise TargetPackageError(
f"target_weights exceed max_single_weight for {breaches}"
)
return {
"constraint_state": value["constraint_state"],
"long_only": True,
"max_single_weight": max_single,
"max_gross_weight": max_gross,
"max_one_way_turnover": max_turnover,
"actual_gross_weight": declared_gross,
}
def _claims_baseline(package: Mapping[str, Any]) -> bool:
claim = " ".join(
str(package.get(name, ""))
for name in ("release", "evidence_class")
).lower()
return "baseline" in claim
def _validate_release_claim(package: Mapping[str, Any]) -> None:
model_id = str(package.get("model_id", "")).strip().lower()
if _claims_baseline(package) and "portable-momentum" in model_id:
raise TargetPackageError(
"portable-momentum is a platform smoke strategy and cannot claim "
"a baseline release"
)
def target_package_sha256(package: Mapping[str, Any]) -> str:
"""Compute the canonical content digest, excluding only its digest field."""
if not isinstance(package, Mapping):
raise TargetPackageError("target package must be an object")
body = dict(package)
body.pop("package_sha256", None)
_reject_quantity_fields(body)
return hashlib.sha256(canonical_json(body).encode("utf-8")).hexdigest()
def seal_target_package(package: Mapping[str, Any]) -> dict[str, Any]:
"""Attach ``package_sha256`` to an unsealed package and validate it."""
if not isinstance(package, Mapping):
raise TargetPackageError("target package must be an object")
if "package_sha256" in package:
raise TargetPackageError("refusing to reseal an already sealed package")
sealed = _json_copy(package)
sealed["package_sha256"] = target_package_sha256(sealed)
return validate_target_package(sealed)
def validate_target_package(
package: Mapping[str, Any],
*,
expected_release: str | None = None,
expected_signal_as_of: str | None = None,
expected_next_session: str | None = None,
) -> dict[str, Any]:
"""Validate and return a detached canonical ``TargetPackageV1`` mapping."""
if not isinstance(package, Mapping):
raise TargetPackageError("target package must be an object")
candidate = _json_copy(package)
_reject_quantity_fields(candidate)
try:
validate_contract(
candidate,
load_schema("target_package.schema.json"),
)
except (ContractValidationError, FileNotFoundError) as exc:
raise TargetPackageError(f"target package schema rejected: {exc}") from exc
signal = _parse_signal_as_of(candidate["signal_as_of"])
_parse_next_session(candidate["next_session"], signal)
for name in SHA256_FIELDS:
_validate_sha256(candidate[name], name)
_validate_sha256(candidate["package_sha256"], "package_sha256")
if not str(candidate["package_id"]).strip():
raise TargetPackageError("package_id must be non-empty")
if not str(candidate["decision_id"]).strip():
raise TargetPackageError("decision_id must be non-empty")
if not str(candidate["release"]).strip():
raise TargetPackageError("release must be non-empty")
if not str(candidate["evidence_class"]).strip():
raise TargetPackageError("evidence_class must be non-empty")
if not str(candidate["model_id"]).strip():
raise TargetPackageError("model_id must be non-empty")
weights = _validate_weights(candidate["target_weights"])
universe = _validate_universe(candidate["universe"])
missing_universe = sorted(set(weights) - set(universe))
if missing_universe:
raise TargetPackageError(
f"target symbols are absent from universe: {missing_universe}"
)
invalid_excluded = sorted(
symbol
for symbol, weight in weights.items()
if universe[symbol]["state"] == "excluded"
and weight > WEIGHT_TOLERANCE
)
if invalid_excluded:
raise TargetPackageError(
f"excluded symbols have positive target weights: {invalid_excluded}"
)
_validate_constraints(candidate["constraints"], weights)
_validate_release_claim(candidate)
expected_hash = target_package_sha256(candidate)
if candidate["package_sha256"] != expected_hash:
raise TargetPackageError("package_sha256 content hash mismatch")
if expected_release is not None and candidate["release"] != expected_release:
raise TargetPackageError("target package release does not match request")
if (
expected_signal_as_of is not None
and candidate["signal_as_of"] != expected_signal_as_of
):
raise TargetPackageError(
"target package signal_as_of does not match request"
)
if (
expected_next_session is not None
and candidate["next_session"] != expected_next_session
):
raise TargetPackageError(
"target package next_session does not match request"
)
return candidate
@dataclass(frozen=True, slots=True)
class TargetPackageV1:
"""Immutable value wrapper around a validated package payload."""
_canonical_payload: str
@classmethod
def from_mapping(
cls,
package: Mapping[str, Any],
**expected: str,
) -> "TargetPackageV1":
validated = validate_target_package(package, **expected)
return cls(canonical_json(validated))
@classmethod
def from_dict(
cls,
package: Mapping[str, Any],
**expected: str,
) -> "TargetPackageV1":
return cls.from_mapping(package, **expected)
def as_dict(self) -> dict[str, Any]:
return json.loads(self._canonical_payload)
@property
def package_id(self) -> str:
return self.as_dict()["package_id"]
@property
def decision_id(self) -> str:
return self.as_dict()["decision_id"]
@property
def signal_as_of(self) -> str:
return self.as_dict()["signal_as_of"]
@property
def next_session(self) -> str:
return self.as_dict()["next_session"]
class TargetPackageTape:
"""Validated multi-decision tape with fail-closed exact lookup."""
def __init__(
self,
packages: Iterable[Mapping[str, Any] | TargetPackageV1],
) -> None:
items: list[TargetPackageV1] = []
package_ids: set[str] = set()
decision_ids: set[str] = set()
clock_keys: set[tuple[str, str]] = set()
for raw in packages:
item = (
raw
if isinstance(raw, TargetPackageV1)
else TargetPackageV1.from_mapping(raw)
)
key = (item.signal_as_of, item.next_session)
if item.package_id in package_ids:
raise DuplicateTargetDecisionError(
f"duplicate package_id {item.package_id!r}"
)
if item.decision_id in decision_ids:
raise DuplicateTargetDecisionError(
f"duplicate decision_id {item.decision_id!r}"
)
if key in clock_keys:
raise DuplicateTargetDecisionError(
"duplicate decision for signal_as_of/next_session "
f"{key!r}"
)
package_ids.add(item.package_id)
decision_ids.add(item.decision_id)
clock_keys.add(key)
items.append(item)
if not items:
raise TargetPackageError("target-package tape is empty")
self._items = tuple(
sorted(
items,
key=lambda item: (
item.signal_as_of,
item.next_session,
item.decision_id,
),
)
)
self._by_clock = {
(item.signal_as_of, item.next_session): item
for item in self._items
}
def __len__(self) -> int:
return len(self._items)
def __iter__(self) -> Iterator[dict[str, Any]]:
for item in self._items:
yield item.as_dict()
def lookup_exact(
self,
*,
signal_as_of: str,
next_session: str,
decision_id: str | None = None,
) -> dict[str, Any]:
signal = _parse_signal_as_of(signal_as_of)
_parse_next_session(next_session, signal)
item = self._by_clock.get((signal_as_of, next_session))
if item is None:
raise TargetPackageNotFoundError(
"no target package for exact signal_as_of/next_session pair"
)
if decision_id is not None and item.decision_id != decision_id:
raise TargetPackageNotFoundError(
"exact clock pair exists but decision_id does not match"
)
return item.as_dict()
def lookup(
self,
*,
signal_as_of: str,
next_session: str,
decision_id: str | None = None,
) -> dict[str, Any]:
return self.lookup_exact(
signal_as_of=signal_as_of,
next_session=next_session,
decision_id=decision_id,
)
def validate_target_package_tape(
packages: Iterable[Mapping[str, Any] | TargetPackageV1],
) -> TargetPackageTape:
return TargetPackageTape(packages)
def lookup_target_package(
packages: Iterable[Mapping[str, Any] | TargetPackageV1],
*,
signal_as_of: str,
next_session: str,
decision_id: str | None = None,
) -> dict[str, Any]:
return TargetPackageTape(packages).lookup_exact(
signal_as_of=signal_as_of,
next_session=next_session,
decision_id=decision_id,
)
def load_target_package_tape(path: str | Path) -> TargetPackageTape:
"""Strictly load one package, an array/``packages`` object, or JSONL."""
source = Path(path)
try:
if source.suffix.lower() == ".jsonl":
packages = [
_strict_json_text(
line,
source=f"target-package tape line {line_number}",
)
for line_number, line in enumerate(
source.read_text(encoding="utf-8").splitlines(),
1,
)
if line.strip()
]
else:
raw = _strict_json_text(
source.read_text(encoding="utf-8"),
source="target-package tape",
)
if isinstance(raw, dict) and raw.get("package_type") == PACKAGE_TYPE:
packages = [raw]
else:
packages = raw.get("packages") if isinstance(raw, dict) else raw
except (OSError, UnicodeError) as exc:
raise TargetPackageError(f"cannot load target-package tape: {exc}") from exc
if not isinstance(packages, list):
raise TargetPackageError(
"target-package tape must be one TargetPackageV1, a JSON array, "
"a packages object, or JSONL"
)
return TargetPackageTape(packages)
+228 -72
View File
@@ -5,25 +5,25 @@ window.QUANT_OS_PUBLIC_STATUS = {
"artifacts": [ "artifacts": [
{ {
"core": "src/quant60/portable_core.py", "core": "src/quant60/portable_core.py",
"coreSha256": "af29e4e5e10705cf6aff89874394c1e80d3780f093c19d90ed53ef9d0188a934", "coreSha256": "f9de9f18e4008bb0fc80edb0b21cae6ea06a61828d89e9b7c11057f98225fc19",
"effectiveConfigSha256": "3ef9b9efda9ec6cc5f57a5daca57cab3b68242e0368f18fdaaf9e249bdaa7873", "effectiveConfigSha256": "d4ae2882e9b5d8d673e7c62c0dc54def7c20221c358ac091ee6529bf0413df0b",
"id": "joinquant", "id": "joinquant",
"integrity": "manifest_hashes_match_repository", "integrity": "manifest_hashes_match_repository",
"output": "dist/joinquant_strategy.py", "output": "dist/joinquant_strategy.py",
"outputSha256": "22ddb0546beb3bd4e4bbedecb271452111324c3903e45be1f4ac3d39f38cce4f", "outputSha256": "be4ca1d5764416b7c8e6c206121c0f0bd8219e7317679d79bc72a95492286b1a",
"wrapper": "platforms/joinquant_strategy.py", "wrapper": "platforms/joinquant_strategy.py",
"wrapperSha256": "b5a8e3b0a774a0f45f52f6bcccbe786e234913a1a4cf5b68b85e2bd989ee9294" "wrapperSha256": "0c672b64665b7c1d4409ba288b00d1608e976800af3a4f9a6467767ce4def592"
}, },
{ {
"core": "src/quant60/portable_core.py", "core": "src/quant60/portable_core.py",
"coreSha256": "af29e4e5e10705cf6aff89874394c1e80d3780f093c19d90ed53ef9d0188a934", "coreSha256": "f9de9f18e4008bb0fc80edb0b21cae6ea06a61828d89e9b7c11057f98225fc19",
"effectiveConfigSha256": "11f189ea5c9d4eaddeb6394dc342abc871941cb3395d7ff79aae1275ce2ad98f", "effectiveConfigSha256": "7e4739937e4e99e5b58154f53271d8d3bfaed47d8d1d5adc747101babde827f5",
"id": "qmt_builtin", "id": "qmt_builtin",
"integrity": "manifest_hashes_match_repository", "integrity": "manifest_hashes_match_repository",
"output": "dist/qmt_builtin_strategy.py", "output": "dist/qmt_builtin_strategy.py",
"outputSha256": "ad503e5ebde60a5deb0ec86334c04d4be2d77ab91d9607d4fe88e2119f22d5dc", "outputSha256": "483877b7ea55c8e0f5e4e47b48a156b3add6cf0a8085011414c5c600913939b5",
"wrapper": "platforms/qmt_builtin_strategy.py", "wrapper": "platforms/qmt_builtin_strategy.py",
"wrapperSha256": "efb931bbd04d0abf0a576f2422a13403fd4d659dccf59f8170e8b13b1bafa640" "wrapperSha256": "a1e53dc22ebd9bb01be43675786151606bbed273d8ab883fb933461e379d2727"
} }
], ],
"baselineConfig": "configs/baseline.json", "baselineConfig": "configs/baseline.json",
@@ -40,21 +40,91 @@ window.QUANT_OS_PUBLIC_STATUS = {
"topN": 20, "topN": 20,
"universeMode": "pit_index" "universeMode": "pit_index"
}, },
"portableCoreSha256": "af29e4e5e10705cf6aff89874394c1e80d3780f093c19d90ed53ef9d0188a934", "portableCoreSha256": "f9de9f18e4008bb0fc80edb0b21cae6ea06a61828d89e9b7c11057f98225fc19",
"schemaVersion": 1 "schemaVersion": 1
}, },
"candidateBoundary": {
"allFiveLayersRealPlatformObserved": false,
"connectedExecution": "reference one-shot quantity/order-delta planner",
"connectedOptimizer": "deterministic constrained optimizer",
"connectedRiskModel": "60-session diagonal realized variance",
"decisionMode": "target_package",
"historicalJoinQuantCountsAsBaseline": false,
"historicalJoinQuantMode": "portable_momentum_smoke",
"joinQuantTargetPackageCountsAsBaseline": false,
"joinQuantTargetPackageCountsAsG9": false,
"joinQuantTargetPackageRun": {
"allFiveLayersObserved": false,
"baseline60Passed": false,
"bundleSha256": "0586930ddc483f4489f09b562f4d4232b458b703e6a1194af809b707211be8c4",
"evidenceClass": "synthetic-research",
"fills": 3,
"g9Passed": false,
"investmentValueClaim": false,
"marketDataFrom": "2024-03-11",
"marketDataThrough": "2024-03-13",
"observedAt": "2026-07-26T08:50:15Z",
"observedLayers": [
"execution"
],
"orders": 3,
"packageId": "TP-20240311-795dcfba",
"qmtPeerObserved": false,
"runId": "2457a7c39a276e09e0fabf99e28978e1"
},
"localEvidenceClass": "synthetic_research",
"reachability": {
"manifest": "releases/quant60-research-candidate-v1/reachability.json",
"manifestSha256": "bf3379b1d8d73ea64d73db8317cfa27307b5dc218884ea2710ffe71dd116c017",
"realPlatformObservedLayers": [
"execution"
],
"realPlatformsObserved": [
"joinquant"
],
"releaseId": "quant60-research-candidate-v1",
"summary": {
"all_layers_implemented": true,
"all_layers_jq_entrypoint_reachable": true,
"all_layers_local_entrypoint_reachable": true,
"all_layers_qmt_entrypoint_reachable": true,
"all_layers_real_platform_observed": false,
"all_layers_unit_tested": true
}
},
"realJoinQuantObserved": true,
"realPlatformObservedLayers": [
"execution"
],
"realQmtObserved": false,
"releaseVerified": false
},
"claim": { "claim": {
"allGatesRequired": true, "allGatesRequired": true,
"baselineThreshold": 60, "baselineThreshold": 60,
"gatesPassed": 0, "gatesPassed": 0,
"gatesTotal": 10, "gatesTotal": 10,
"liveReady": false, "liveReady": false,
"reason": "Quant OS 已有可执行本地流水线、真实 Qlib fixture smoke、带校验但不完整的 Tushare→Qlib 技术 smoke,以及一次完成的真实聚宽 hosted smoke;但仍缺完整复权 PIT 数据、同输入聚宽比较、真实 QMT、券商影子盘、连续对账与合规证据,因此不能计入 Baseline 60。", "reason": "Quant OS 已接通本地 synthetic 五层候选链,能冻结 Ridge ModelBundle、逐层 trace 和无股数 TargetPackageV1;一个 synthetic-research TargetPackage 已在真实 JoinQuant hosted 环境完成消费、账户/开盘价绑定、3 笔委托和 3 笔成交,因此 execution 层已有真实平台观察。但真实 provider-data 长样本/OOS 尚未接入,JoinQuant 没有重跑上游 Universe/Alpha/Portfolio/RiskQMT peer 与逐层真实数据 parity 仍缺失,factor risk、CVXPY 和 guarded TWAP/POV 也尚未进入当前主链。因此它仍是 NOT_BASELINE_60G9 未通过。",
"releaseVerified": false, "releaseVerified": false,
"score": null, "score": null,
"scoreStatus": "not_scored", "scoreStatus": "not_scored",
"status": "NOT_BASELINE_60" "status": "NOT_BASELINE_60"
}, },
"deliveryGaps": {
"externalEvidence": [
"authorized JQData market snapshot, licence/lineage record and a frozen provider-data candidate tape",
"authorized real-data multi-period JoinQuant TargetPackage run plus same-input local/JoinQuant layer-by-layer reconciliation",
"real QMT TargetPackage built-in and qmttools backtest exports",
"real QMT historical-ST positive control, terminal/xtquant build record, broker query contract and trade-amount semantic probes, callback/restart and at least 20 trading days of shadow evidence",
"continuous reconciliation, recovery drills and broker compliance confirmation"
],
"internal": [
"The authorized provider-data path has not yet been connected end to end to the frozen Ridge ModelBundle, calibrated risk/cost inputs, five-layer Baseline candidate and TargetPackage tape; the current connected candidate remains synthetic.",
"factor_risk.py, the optional CVXPY optimizer mode and guarded TWAP/POV exist as components or optional paths but are not selected by the current Baseline main slice; the connected risk model is 60-session diagonal realized variance and execution is the reference one-shot planner.",
"One synthetic-research TargetPackageV1 has been observed at the real JoinQuant execution boundary, but JoinQuant did not recompute the upstream four layers; there is no authorized real-data multi-period tape, same-input layer-by-layer parity, or real QMT peer."
]
},
"evidence": [ "evidence": [
{ {
"id": "scorecard", "id": "scorecard",
@@ -68,6 +138,12 @@ window.QUANT_OS_PUBLIC_STATUS = {
"label": "Portable bundle manifest", "label": "Portable bundle manifest",
"path": "dist/bundle_manifest.json" "path": "dist/bundle_manifest.json"
}, },
{
"id": "release-reachability",
"kind": "machine_record",
"label": "Five-layer release reachability manifest",
"path": "releases/quant60-research-candidate-v1/reachability.json"
},
{ {
"id": "readme", "id": "readme",
"kind": "documentation", "kind": "documentation",
@@ -101,9 +177,21 @@ window.QUANT_OS_PUBLIC_STATUS = {
{ {
"id": "joinquant-hosted-evidence", "id": "joinquant-hosted-evidence",
"kind": "runtime_evidence", "kind": "runtime_evidence",
"label": "JoinQuant hosted smoke boundary", "label": "JoinQuant hosted momentum smoke boundary",
"path": "docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md" "path": "docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md"
}, },
{
"id": "joinquant-target-package-evidence",
"kind": "runtime_evidence",
"label": "JoinQuant TargetPackage execution evidence boundary",
"path": "docs/JOINQUANT_TARGET_PACKAGE_EVIDENCE_2026-07-26.md"
},
{
"id": "joinquant-target-package-run",
"kind": "machine_record",
"label": "Redacted JoinQuant TargetPackage hosted run",
"path": "evidence/joinquant/TP-20240311-795dcfba/2457a7c39a276e09e0fabf99e28978e1/run.json"
},
{ {
"id": "tushare-data", "id": "tushare-data",
"kind": "runtime_evidence", "kind": "runtime_evidence",
@@ -136,14 +224,32 @@ window.QUANT_OS_PUBLIC_STATUS = {
"scorecard" "scorecard"
], ],
"id": "joinquant-hosted-market-window", "id": "joinquant-hosted-market-window",
"label": "JoinQuant hosted smoke 行情区间", "label": "JoinQuant hosted momentum smoke 行情区间",
"marketDataFrom": "2024-01-02", "marketDataFrom": "2024-01-02",
"marketDataThrough": "2024-12-31", "marketDataThrough": "2024-12-31",
"productionReady": false, "productionReady": false,
"semantics": "单次真实 hosted runtime smoke 的回测区间;不代表同输入跨引擎通过。", "semantics": "旧 momentum hosted runtime smoke 的回测区间;不是 TargetPackage 候选或同输入跨引擎证据。",
"status": "runtime_smoke", "status": "runtime_smoke",
"value": "2024-01-02 — 2024-12-31" "value": "2024-01-02 — 2024-12-31"
}, },
{
"evidenceLinks": [
"joinquant-target-package-evidence",
"joinquant-target-package-run",
"release-reachability",
"scorecard"
],
"id": "joinquant-target-package-execution-smoke",
"investmentValueClaim": false,
"label": "JoinQuant TargetPackage execution smoke",
"marketDataFrom": "2024-03-11",
"marketDataThrough": "2024-03-13",
"observedAt": "2026-07-26T08:50:15Z",
"productionReady": false,
"semantics": "真实平台只观察 synthetic-research TargetPackage 的 execution consumer;不代表上游四层真实平台执行、长样本 OOS 或 G9。",
"status": "runtime_smoke",
"value": "2024-03-11 — 2024-03-13; 3 orders / 3 fills"
},
{ {
"completedPartitions": 35, "completedPartitions": 35,
"completedRows": 28731, "completedRows": 28731,
@@ -422,7 +528,7 @@ window.QUANT_OS_PUBLIC_STATUS = {
"Tushare completed-Parquet inventory and immutable Qlib provider", "Tushare completed-Parquet inventory and immutable Qlib provider",
"semantic manifest and data-version verification" "semantic manifest and data-version verification"
], ],
"claim": "不可变快照、PIT 字段合同和 Tushare→Qlib 技术链路已实现并有本地合同/运行证据。", "claim": "不可变快照、PIT 字段合同和 Tushare→Qlib 技术链路已有本地合同/运行证据;这些 provider-data 路径尚未接成当前 Ridge 五层候选。",
"evidenceLinks": [ "evidenceLinks": [
"architecture", "architecture",
"tushare-data", "tushare-data",
@@ -430,7 +536,7 @@ window.QUANT_OS_PUBLIC_STATUS = {
], ],
"id": "data", "id": "data",
"limitations": [ "limitations": [
"尚无授权 JQData 真实快照与许可 lineage;Tushare 镜像仅部分完成且缺生产关键字段。" "尚无授权 JQData 真实快照与许可 lineage;Tushare 镜像仅部分完成且缺生产关键字段;当前五层候选仍使用 synthetic 数据。"
], ],
"name": "数据与时点", "name": "数据与时点",
"releaseVerified": false, "releaseVerified": false,
@@ -438,19 +544,21 @@ window.QUANT_OS_PUBLIC_STATUS = {
}, },
{ {
"capabilities": [ "capabilities": [
"PIT features, purged walk-forward and deterministic Ridge", "train-only preprocessing, purged walk-forward and deterministic Ridge",
"replayable frozen ModelBundle and synthetic TargetPackageV1",
"Qlib native momentum", "Qlib native momentum",
"Qlib Alpha158, LightGBM and Recorder workflow" "Qlib Alpha158, LightGBM and Recorder workflow"
], ],
"claim": "synthetic research pipeline 已通过测试;Qlib 0.9.7 的 momentum Alpha158/LightGBM fixture 已在真实本地运行时完成。", "claim": "synthetic 五层研究入口能冻结 train-only Ridge ModelBundle、逐层 trace 与 TargetPackageV1Qlib momentum Alpha158/LightGBM fixture 是另外的受限研究 smoke。",
"evidenceLinks": [ "evidenceLinks": [
"architecture", "architecture",
"platform-matrix", "platform-matrix",
"release-reachability",
"scorecard" "scorecard"
], ],
"id": "research", "id": "research",
"limitations": [ "limitations": [
"fixture 仅两只 synthetic 股票;没有授权长样本 OOS、冻结模型包或投资价值证据。" "候选和 Qlib fixture 均是 synthetic;尚无授权长样本 OOS、真实数据校准、平台候选观察或投资价值证据。"
], ],
"name": "研究与模型", "name": "研究与模型",
"releaseVerified": false, "releaseVerified": false,
@@ -458,18 +566,20 @@ window.QUANT_OS_PUBLIC_STATUS = {
}, },
{ {
"capabilities": [ "capabilities": [
"portable momentum scoring and capped target weights", "explicit fee/spread/square-root-impact cost forecast",
"lot-rounded target quantities and T+1 sellable deltas", "diagonal realized-variance risk input and deterministic constrained target",
"factor risk, dated cost and constrained target research" "single-name, industry, gross, capacity and turnover constraints",
"post-risk weights with hash-linked lineage"
], ],
"claim": "Signal→Target→Order Delta 合同、成本、风险与约束目标在本地测试路径可重放。", "claim": "当前 synthetic 主链把 Ridge Alpha、显式成本、60 日对角 realized variance、deterministic optimizer 和 post-risk gate 接成可重放 TargetPackage。",
"evidenceLinks": [ "evidenceLinks": [
"architecture", "architecture",
"release-reachability",
"scorecard" "scorecard"
], ],
"id": "signal-portfolio", "id": "signal-portfolio",
"limitations": [ "limitations": [
"风险、冲击和容量尚未用授权真实数据校准G4/G5 均未通过。" "factor_risk.py 与 optional CVXPY solver 未被当前主链选择;风险、冲击和容量尚未用授权真实数据校准G4/G5 均未通过。"
], ],
"name": "信号、组合与事前风险", "name": "信号、组合与事前风险",
"releaseVerified": false, "releaseVerified": false,
@@ -478,20 +588,22 @@ window.QUANT_OS_PUBLIC_STATUS = {
{ {
"capabilities": [ "capabilities": [
"local event and snapshot backtests", "local event and snapshot backtests",
"JoinQuant hosted single-file bundle", "fail-closed JoinQuant TargetPackage single-file consumer",
"QMT built-in/qmttools backtest-only bundle", "fail-closed QMT built-in TargetPackage backtest consumer",
"Qlib research runners" "Qlib research runners"
], ],
"claim": "本地 synthetic、Qlib fixture 与一次真实 JoinQuant hosted smoke 已运行;QMT 仍只有 bundle/harness。", "claim": "本地 synthetic 五层候选、平台 harness 和一次真实 JoinQuant TargetPackage execution smoke 已运行;QMT 尚无真实 peer run。",
"evidenceLinks": [ "evidenceLinks": [
"platform-matrix", "platform-matrix",
"joinquant-hosted-evidence", "joinquant-target-package-evidence",
"joinquant-target-package-run",
"cross-engine", "cross-engine",
"release-reachability",
"scorecard" "scorecard"
], ],
"id": "backtest", "id": "backtest",
"limitations": [ "limitations": [
"没有冻结同输入的本地/聚宽/QMT 逐层对比;真实 QMT 尚未运行G9 未通过。" "聚宽只观察到一个 synthetic-research TargetPackage 的消费、账户/开盘价绑定和三笔成交;无真实长样本 OOS、QMT peer 或逐层真实数据 parityG9 未通过。"
], ],
"name": "回测与跨引擎", "name": "回测与跨引擎",
"releaseVerified": false, "releaseVerified": false,
@@ -501,17 +613,20 @@ window.QUANT_OS_PUBLIC_STATUS = {
"capabilities": [ "capabilities": [
"local and Colab zero-account smoke", "local and Colab zero-account smoke",
"JQData snapshot entrypoint", "JQData snapshot entrypoint",
"JoinQuant upload bundle", "JoinQuant momentum or explicit TargetPackage upload bundle",
"QMT built-in and qmttools entrypoints" "QMT built-in TargetPackage and qmttools entrypoints"
], ],
"claim": "Local、Colab、JoinQuant、Qlib 有可执行入口;JoinQuant/Qlib 有受限真实运行时 smoke。", "claim": "Local、Colab、JoinQuant、QMT、Qlib 有入口;JoinQuant 已真实观察 synthetic-research TargetPackage 的 execution consumerQMT 候选仍未实跑。",
"evidenceLinks": [ "evidenceLinks": [
"platform-matrix", "platform-matrix",
"platform-deployment" "joinquant-target-package-evidence",
"joinquant-target-package-run",
"platform-deployment",
"release-reachability"
], ],
"id": "platforms", "id": "platforms",
"limitations": [ "limitations": [
"JQData 真实账号、QMT 客户端/数据权限及平台完整导出仍是外部前置条件。" "JoinQuant 没有重跑上游四层;JQData 授权真实候选、QMT 客户端/数据权限、QMT peer 导出与逐层真实数据 parity 仍是外部前置条件。"
], ],
"name": "平台适配", "name": "平台适配",
"releaseVerified": false, "releaseVerified": false,
@@ -560,7 +675,7 @@ window.QUANT_OS_PUBLIC_STATUS = {
], ],
"models": [ "models": [
{ {
"claim": "共享 symbol normalization、momentum、target weight/quantity 与 order-delta 计算;已进入 JoinQuant hosted smoke 和本地 Qlib momentum 路径。", "claim": "共享 symbol normalization、momentum、target weight/quantity 与 order-delta 计算;2024 JoinQuant hosted run 只证明这一 smoke 路径。",
"evidenceLinks": [ "evidenceLinks": [
"architecture", "architecture",
"bundle-manifest", "bundle-manifest",
@@ -569,27 +684,29 @@ window.QUANT_OS_PUBLIC_STATUS = {
"id": "portable-momentum-v1", "id": "portable-momentum-v1",
"investmentValueClaim": false, "investmentValueClaim": false,
"limitations": [ "limitations": [
"尚无同输入 L1–L4 真实跨引擎通过证据,不是已验证的生产 champion。" "不是 Ridge/TargetPackage Baseline 候选;旧 hosted run 不得用于候选或 G9 计分。"
], ],
"name": "portable-momentum-v1", "name": "portable-momentum-v1",
"releaseVerified": false, "releaseVerified": false,
"role": "跨引擎冻结计算基线", "role": "平台连通性 smoke 策略",
"status": "real_runtime" "status": "real_runtime"
}, },
{ {
"claim": "purged walk-forward research pipeline 中可确定性训练和评估。", "claim": "train-only 预处理、系数、截距、训练截止和 lineage 可冻结为可重放 ModelBundle;其 synthetic TargetPackageV1 已被真实 JoinQuant execution consumer 消费并成交。",
"evidenceLinks": [ "evidenceLinks": [
"architecture", "architecture",
"platform-matrix" "platform-matrix",
"joinquant-target-package-evidence",
"joinquant-target-package-run"
], ],
"id": "ridge-challenger", "id": "ridge-challenger",
"investmentValueClaim": false, "investmentValueClaim": false,
"limitations": [ "limitations": [
"只在 synthetic 研究路径验证;无真实 OOS、versioned model bundle 或跨平台推理验证。" "JoinQuant 只观察执行边界,未重跑上游四层;无授权真实 OOS、真实校准、QMT peer 或投资价值证据。"
], ],
"name": "Deterministic Ridge", "name": "Deterministic Ridge",
"releaseVerified": false, "releaseVerified": false,
"role": "synthetic 研究候选", "role": "synthetic 五层 Baseline 研究候选",
"status": "tested" "status": "tested"
}, },
{ {
@@ -613,31 +730,35 @@ window.QUANT_OS_PUBLIC_STATUS = {
{ {
"gateIds": [ "gateIds": [
"G1", "G1",
"G2",
"G3", "G3",
"G5",
"G7" "G7"
], ],
"priority": 1, "priority": 1,
"title": "生成授权 JQData 不可变真实快照", "title": "接通真实数据五层候选",
"why": "authorized JQData market snapshot and licence/lineage record" "why": "The authorized provider-data path has not yet been connected end to end to the frozen Ridge ModelBundle, calibrated risk/cost inputs, five-layer Baseline candidate and TargetPackage tape; the current connected candidate remains synthetic. External prerequisite: authorized JQData market snapshot, licence/lineage record and a frozen provider-data candidate tape"
}, },
{ {
"gateIds": [ "gateIds": [
"G3", "G3",
"G5",
"G7",
"G9" "G9"
], ],
"priority": 2, "priority": 2,
"title": "冻结聚宽同输入并完成逐层本地比较", "title": "冻结主链风险、优化与执行选择",
"why": "complete JoinQuant hosted input/plan/order/fill export and same-input local comparison" "why": "factor_risk.py, the optional CVXPY optimizer mode and guarded TWAP/POV exist as components or optional paths but are not selected by the current Baseline main slice; the connected risk model is 60-session diagonal realized variance and execution is the reference one-shot planner."
}, },
{ {
"gateIds": [ "gateIds": [
"G3", "G3",
"G6", "G7",
"G9" "G9"
], ],
"priority": 3, "priority": 3,
"title": "取得真实 QMT built-in 与 qmttools 导出", "title": "扩展 JoinQuant 多期对账与逐层 parity",
"why": "real QMT built-in and qmttools backtest exports" "why": "One synthetic-research TargetPackageV1 has been observed at the real JoinQuant execution boundary, but JoinQuant did not recompute the upstream four layers; there is no authorized real-data multi-period tape, same-input layer-by-layer parity, or real QMT peer. Required evidence: authorized real-data multi-period JoinQuant TargetPackage run plus same-input local/JoinQuant layer-by-layer reconciliation"
}, },
{ {
"gateIds": [ "gateIds": [
@@ -646,6 +767,16 @@ window.QUANT_OS_PUBLIC_STATUS = {
"G9" "G9"
], ],
"priority": 4, "priority": 4,
"title": "运行真实 QMT TargetPackage 候选",
"why": "real QMT TargetPackage built-in and qmttools backtest exports"
},
{
"gateIds": [
"G6",
"G8",
"G9"
],
"priority": 5,
"title": "完成 QMT 券商合同探针与至少 20 日影子盘", "title": "完成 QMT 券商合同探针与至少 20 日影子盘",
"why": "real QMT historical-ST positive control, terminal/xtquant build record, broker query contract and trade-amount semantic probes, callback/restart and at least 20 trading days of shadow evidence" "why": "real QMT historical-ST positive control, terminal/xtquant build record, broker query contract and trade-amount semantic probes, callback/restart and at least 20 trading days of shadow evidence"
}, },
@@ -654,14 +785,14 @@ window.QUANT_OS_PUBLIC_STATUS = {
"G8", "G8",
"G10" "G10"
], ],
"priority": 5, "priority": 6,
"title": "完成连续对账、恢复演练与券商合规确认", "title": "完成连续对账、恢复演练与券商合规确认",
"why": "continuous reconciliation, recovery drills and broker compliance confirmation" "why": "continuous reconciliation, recovery drills and broker compliance confirmation"
} }
], ],
"platforms": [ "platforms": [
{ {
"claim": "标准库套件最近记录为 224 tests、OK、6 skipsmoke + exact manifest verifier 可运行", "claim": "标准库套件smoke + exact manifest verifier 可运行",
"evidenceLinks": [ "evidenceLinks": [
"platform-matrix" "platform-matrix"
], ],
@@ -677,14 +808,14 @@ window.QUANT_OS_PUBLIC_STATUS = {
"status": "tested" "status": "tested"
}, },
{ {
"claim": "research smoke + manifest verifier 可运行", "claim": "冻结 model bundle、TargetPackageV1、layer trace + manifest verifier 可运行;风险/成本扰动必须改变 target",
"evidenceLinks": [ "evidenceLinks": [
"platform-matrix" "platform-matrix"
], ],
"id": "local-synthetic-research", "id": "local-synthetic-research",
"intendedUse": "PIT/features/walk-forward/Ridge/risk/cost/target", "intendedUse": "五层 vertical sliceUniverse/Ridge Alpha/risk+cost Portfolio/post-risk/reference Execution",
"limitations": [ "limitations": [
"Ridge 未冻结部署,未做授权长样本 OOS" "仍是 synthetic;当前只接对角风险与 deterministic optimizer,未做授权长样本 OOS"
], ],
"liveReady": false, "liveReady": false,
"name": "Local synthetic research", "name": "Local synthetic research",
@@ -757,10 +888,12 @@ window.QUANT_OS_PUBLIC_STATUS = {
"status": "tested" "status": "tested"
}, },
{ {
"claim": "fake lifecycle 与 mock parity 可运行", "claim": "动量 smoke 与 TargetPackage 消费模式均有 fake lifecycle;目标包缺失/篡改不回退",
"evidenceLinks": [ "evidenceLinks": [
"platform-matrix", "platform-matrix",
"joinquant-hosted-evidence" "joinquant-hosted-evidence",
"joinquant-target-package-evidence",
"joinquant-target-package-run"
], ],
"id": "joinquant-local-harness", "id": "joinquant-local-harness",
"intendedUse": "wrapper API 合同", "intendedUse": "wrapper API 合同",
@@ -774,15 +907,17 @@ window.QUANT_OS_PUBLIC_STATUS = {
"status": "tested" "status": "tested"
}, },
{ {
"claim": "真实 2024 全年 run 已完成且可见日志 0 ERROR;单文件、PIT index/ST、批量 history、费率/滑点/10% 参与率、科创板保护价/200 股约束已运行", "claim": "动量 smoke 的真实 2024 全年 run 已完成FINAL4 的单个 synthetic-research TargetPackage 已在真实 hosted 环境按 day_open 命中并产生 3 单/3 fill",
"evidenceLinks": [ "evidenceLinks": [
"platform-matrix", "platform-matrix",
"joinquant-hosted-evidence" "joinquant-hosted-evidence",
"joinquant-target-package-evidence",
"joinquant-target-package-run"
], ],
"id": "joinquant-hosted-bundle", "id": "joinquant-hosted-bundle",
"intendedUse": "hosted backtest", "intendedUse": "hosted backtest",
"limitations": [ "limitations": [
"尚无完整输入/plan/orders/fills 导出和同输入本地比较" "只观察 execution consumer;无授权真实长样本/OOS、上游四层 hosted 执行、QMT peer 或逐层真实数据 parity"
], ],
"liveReady": false, "liveReady": false,
"name": "JoinQuant hosted bundle", "name": "JoinQuant hosted bundle",
@@ -791,7 +926,7 @@ window.QUANT_OS_PUBLIC_STATUS = {
"status": "real_runtime" "status": "real_runtime"
}, },
{ {
"claim": "Python 3.6 source、ContextInfo 回滚模拟和 mock parity", "claim": "Python 3.6 source、ContextInfo 回滚模拟、动量 smoke 与 TargetPackage 合同",
"evidenceLinks": [ "evidenceLinks": [
"platform-matrix", "platform-matrix",
"platform-deployment" "platform-deployment"
@@ -808,7 +943,7 @@ window.QUANT_OS_PUBLIC_STATUS = {
"status": "tested" "status": "tested"
}, },
{ {
"claim": "bundle 硬限制 backtest毫秒 timetag PIT 成分/ST;固定中证 500、费率/滑点;模块全局 g", "claim": "两种模式都硬限制 backtestTargetPackage 模式不重算上游四层;模块全局 g",
"evidenceLinks": [ "evidenceLinks": [
"platform-matrix", "platform-matrix",
"platform-deployment" "platform-deployment"
@@ -816,11 +951,11 @@ window.QUANT_OS_PUBLIC_STATUS = {
"id": "qmt-built-in-bundle", "id": "qmt-built-in-bundle",
"intendedUse": "GUI backtest-only", "intendedUse": "GUI backtest-only",
"limitations": [ "limitations": [
"尚无真实 QMT run/export10% GUI 配置与历史 ST 正例探针待取证" "尚无 TargetPackage 真实 QMT run/export10% GUI 配置和执行时钟待取证"
], ],
"liveReady": false, "liveReady": false,
"name": "QMT built-in bundle", "name": "QMT built-in bundle",
"prerequisite": "授权 QMT 客户端/板块与历史 ST 数据", "prerequisite": "授权 QMT 客户端/历史数据",
"releaseVerified": false, "releaseVerified": false,
"status": "implemented" "status": "implemented"
}, },
@@ -928,19 +1063,28 @@ window.QUANT_OS_PUBLIC_STATUS = {
"provenance": { "provenance": {
"baselineConfig": "configs/baseline.json", "baselineConfig": "configs/baseline.json",
"bundleManifest": "dist/bundle_manifest.json", "bundleManifest": "dist/bundle_manifest.json",
"payloadSha256": "1243b3d6821fd7cc24d8473b094dbeb1f1791cc1931f25e5fb683489bbd0bd98", "payloadSha256": "76aad7a50088b9949b9c2549a4da568d14c6bc2ff4acc7df6419989637386bc3",
"payloadSha256Semantics": "SHA-256 of canonical payload before payloadSha256 is added.", "payloadSha256Semantics": "SHA-256 of canonical payload before payloadSha256 is added.",
"platformMatrix": "docs/PLATFORM_MATRIX.md", "platformMatrix": "docs/PLATFORM_MATRIX.md",
"releaseReachability": "releases/quant60-research-candidate-v1/reachability.json",
"scorecard": "gate_scorecard.json", "scorecard": "gate_scorecard.json",
"sourceSetSha256": "e49692c14e142b3bab524763243c16ae4964eb5933d35d867cf89ed52dbcc90c", "sourceSetSha256": "e1b8bb8827c6c051a73872f2b0ac46f18dcf705fb50768eb98090bbbe08f863c",
"sources": [ "sources": [
{ {
"path": "gate_scorecard.json", "path": "gate_scorecard.json",
"sha256": "59e5db5c57d1665ee4a2a2f4cf0ef93a1e7a17e1ef7aa62d0fb64da4ad637c6e" "sha256": "db9ed17d1b56452f973db23461e8298b4f43f319b04be373adb383133500c2b6"
}, },
{ {
"path": "dist/bundle_manifest.json", "path": "dist/bundle_manifest.json",
"sha256": "7290480dbe9c5ec7453896f0ffa6d5cec2f72284cb0f723902d29b37d912bdee" "sha256": "3323a07d0c93c6a9dcf8ead2760025ee8f78136bf9334ca5bd4fd836fbbedb54"
},
{
"path": "releases/quant60-research-candidate-v1/reachability.json",
"sha256": "2a506c2a4a9f4d813c34be4b3d02acdef6a2aa4c6eeff304be5065f1d3d34cbb"
},
{
"path": "evidence/joinquant/TP-20240311-795dcfba/2457a7c39a276e09e0fabf99e28978e1/run.json",
"sha256": "9956c5e61bdc1c82bc24f20f24c1d65a201bab2f71c2c9e4e23eb5bd40eca6ce"
}, },
{ {
"path": "configs/baseline.json", "path": "configs/baseline.json",
@@ -948,19 +1092,19 @@ window.QUANT_OS_PUBLIC_STATUS = {
}, },
{ {
"path": "README.md", "path": "README.md",
"sha256": "3bbd5821ad0641ad9bd6b6dd72a8dcebcddf8655fb0d5fa10ca16a46eaf35a62" "sha256": "d5593bdc4e535502ba817636053dc8ac38f8594be6f9e61f0fe2d11cf883a985"
}, },
{ {
"path": "docs/ARCHITECTURE.md", "path": "docs/ARCHITECTURE.md",
"sha256": "b22fc952a260d0201d3b1a6bf221628c5dd341391b4e814261413e27ab181fd1" "sha256": "93b1981168084f40c6d127dc534ff2e60eb4cd0703c8ef446c6df58b351dfbaa"
}, },
{ {
"path": "docs/AUDIT_2026-07-25.md", "path": "docs/AUDIT_2026-07-25.md",
"sha256": "39731b2c828c01f83c5b8626f2ca02762c376da2cfcd62f2e2c4d8a2f32f780e" "sha256": "c070b49dfc2805c338400697369d886a6dc47b53af4a1834d0dfd383431e74d0"
}, },
{ {
"path": "docs/PLATFORM_MATRIX.md", "path": "docs/PLATFORM_MATRIX.md",
"sha256": "5a084e99df1cd31f89859b390c6cb0e79a68a0f34514bf8a30b3cc1a0c64f1eb" "sha256": "4880b17e54257b794398bf734d80ea40d1180e52845762987443a9c61d562a23"
}, },
{ {
"path": "docs/CROSS_ENGINE_CONSISTENCY.md", "path": "docs/CROSS_ENGINE_CONSISTENCY.md",
@@ -970,6 +1114,10 @@ window.QUANT_OS_PUBLIC_STATUS = {
"path": "docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md", "path": "docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md",
"sha256": "03df249829b3ad9b4fe33c5d93afbd842561458f4ff2fe10e80e4432934eb2d2" "sha256": "03df249829b3ad9b4fe33c5d93afbd842561458f4ff2fe10e80e4432934eb2d2"
}, },
{
"path": "docs/JOINQUANT_TARGET_PACKAGE_EVIDENCE_2026-07-26.md",
"sha256": "99b62e06b843e9866dc88b0479ce474232a5cae84c8595ad5adb2bf707b3d205"
},
{ {
"path": "docs/TUSHARE_LOCAL_DATA.md", "path": "docs/TUSHARE_LOCAL_DATA.md",
"sha256": "fd6cfe6cf45dbdd82e6aa88e80b119e05a888c560b79907370c6e023b5e00e82" "sha256": "fd6cfe6cf45dbdd82e6aa88e80b119e05a888c560b79907370c6e023b5e00e82"
@@ -978,26 +1126,34 @@ window.QUANT_OS_PUBLIC_STATUS = {
"path": "runbooks/PLATFORM_DEPLOYMENT.md", "path": "runbooks/PLATFORM_DEPLOYMENT.md",
"sha256": "f6bb1eda335c0d05f96d39b4d4167ee623fe6cffeef75015ba7985638fa8f635" "sha256": "f6bb1eda335c0d05f96d39b4d4167ee623fe6cffeef75015ba7985638fa8f635"
}, },
{
"path": "tools/build_release_reachability.py",
"sha256": "fecee5980917f86440e96ccfe488e52111db5715b6f3847b87cb98f9a895b4be"
},
{ {
"path": "tools/build_public_status.py", "path": "tools/build_public_status.py",
"sha256": "329332a09238499cf63530d011d4181d26edf506a23d665165e4b3ff49f796ca" "sha256": "38347c2238a38fc603703c7265fd1ca54480b3f3f8c453cde9f21056f207a4ec"
} }
] ]
}, },
"schemaVersion": 1, "schemaVersion": 1,
"summary": { "summary": {
"allFiveLayersRealPlatformObserved": false,
"candidateRealPlatformObservationScope": "target_package_execution_consumer_only",
"candidateRealPlatformObserved": true,
"gateCoverageLabel": "0/10 passed", "gateCoverageLabel": "0/10 passed",
"investmentValueClaim": false, "investmentValueClaim": false,
"optionalSkipsRecorded": 6, "optionalSkipsRecorded": 6,
"productionReady": false, "productionReady": false,
"realRuntimeSmokes": [ "realRuntimeSmokes": [
"JoinQuant hosted daily backtest (runtime/path only)", "JoinQuant TargetPackage execution consumer: one synthetic-research package, 3 orders / 3 fills (not Baseline/G9 evidence)",
"JoinQuant portable_momentum_smoke hosted daily backtest (not Baseline/TargetPackage evidence)",
"Qlib native momentum fixture", "Qlib native momentum fixture",
"Qlib Alpha158 + LightGBM fixture", "Qlib Alpha158 + LightGBM fixture",
"Tushare completed-partition Qlib momentum smoke" "Tushare completed-partition Qlib momentum smoke"
], ],
"testEvidenceQualifier": "Scorecard-recorded run; tests do not prove real-platform or broker gates.", "testEvidenceQualifier": "Scorecard-recorded run; tests do not prove real-platform or broker gates.",
"testsRecorded": 224 "testsRecorded": 285
}, },
"system": "Quant OS / quant60 baseline kernel" "system": "Quant OS / quant60 baseline kernel"
}; };
+135
View File
@@ -11,10 +11,57 @@ sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "src")) sys.path.insert(0, str(ROOT / "src"))
from platforms import joinquant_strategy, qmt_builtin_strategy from platforms import joinquant_strategy, qmt_builtin_strategy
from quant60.target_package import seal_target_package
from tools.bundle_platforms import bundle_all, effective_platform_config from tools.bundle_platforms import bundle_all, effective_platform_config
from tools.capability_probe import probe_capabilities from tools.capability_probe import probe_capabilities
def target_package():
return seal_target_package(
{
"schema_version": "1.0",
"package_type": "TargetPackageV1",
"package_id": "TP-20260724-bundle",
"decision_id": "D-20260724-bundle",
"release": "quant60-research-candidate-v1",
"evidence_class": "synthetic-research",
"model_id": "deterministic-ridge-alpha-5",
"signal_as_of": "2026-07-24T15:00:00+08:00",
"next_session": "2026-07-27",
"model_sha256": "1" * 64,
"data_sha256": "2" * 64,
"feature_sha256": "3" * 64,
"risk_sha256": "4" * 64,
"cost_sha256": "5" * 64,
"optimizer_sha256": "6" * 64,
"config_sha256": "7" * 64,
"source_sha256": "8" * 64,
"target_weights": {
"000001.XSHE": 0.30,
"600000.XSHG": 0.20,
},
"universe": {
"000001.XSHE": {
"state": "openable",
"reasons": ["ELIGIBLE"],
},
"600000.XSHG": {
"state": "openable",
"reasons": ["ELIGIBLE"],
},
},
"constraints": {
"constraint_state": "FEASIBLE",
"long_only": True,
"max_single_weight": 0.40,
"max_gross_weight": 0.80,
"max_one_way_turnover": 0.50,
"actual_gross_weight": 0.50,
},
}
)
class BundleTest(unittest.TestCase): class BundleTest(unittest.TestCase):
def test_source_wrappers_match_canonical_baseline_config(self): def test_source_wrappers_match_canonical_baseline_config(self):
baseline = json.loads( baseline = json.loads(
@@ -66,6 +113,35 @@ class BundleTest(unittest.TestCase):
(ROOT / "configs/baseline.json").read_bytes() (ROOT / "configs/baseline.json").read_bytes()
).hexdigest(), ).hexdigest(),
) )
self.assertEqual(
stored["decision_mode"],
"portable_momentum_smoke",
)
self.assertEqual(stored["target_package_count"], 0)
self.assertIsNone(stored["target_package_tape_sha256"])
self.assertEqual(
stored["joinquant_observation"]["prefix"],
"QUANT60_JOINQUANT_EVIDENCE_V1 ",
)
self.assertTrue(
stored["joinquant_observation"]["fail_soft"]
)
self.assertEqual(
stored["joinquant_observation"]["target_price_source"],
"CURRENT_DATA_DAY_OPEN",
)
self.assertEqual(
stored["joinquant_observation"]["eod_chunk_encoding"],
"base64-canonical-json",
)
self.assertEqual(
stored["joinquant_observation"]["eod_chunk_characters"],
1800,
)
self.assertIn(
"EOD_STATUS",
stored["joinquant_observation"]["events"],
)
def test_committed_dist_is_byte_for_byte_fresh(self): def test_committed_dist_is_byte_for_byte_fresh(self):
with tempfile.TemporaryDirectory() as temp: with tempfile.TemporaryDirectory() as temp:
@@ -112,6 +188,65 @@ class BundleTest(unittest.TestCase):
) )
self.assertTrue(all(value >= 0 for value in quantities.values())) self.assertTrue(all(value >= 0 for value in quantities.values()))
def test_target_package_bundle_is_explicit_pinned_and_no_fallback(self):
with tempfile.TemporaryDirectory() as temp:
root = Path(temp)
package_path = root / "target_package.json"
package = target_package()
package_path.write_text(
json.dumps(package, ensure_ascii=False),
encoding="utf-8",
)
output = root / "bundle"
manifest = bundle_all(ROOT, output, package_path)
self.assertEqual(manifest["decision_mode"], "target_package")
self.assertEqual(manifest["target_package_count"], 1)
self.assertEqual(
manifest["target_package_ids"],
[package["package_id"]],
)
self.assertEqual(
manifest["target_package_sha256"],
[package["package_sha256"]],
)
self.assertNotIn(
str(root),
json.dumps(manifest, ensure_ascii=False),
)
for filename in (
"joinquant_strategy.py",
"qmt_builtin_strategy.py",
):
source = (output / filename).read_text(encoding="utf-8")
self.assertIn(
"'decision_mode': 'target_package'",
source,
)
self.assertNotIn("TARGET_PACKAGES = []", source)
self.assertIn(package["package_sha256"], source)
self.assertIn("build_target_weight_plan", source)
joinquant_source = (
output / "joinquant_strategy.py"
).read_text(encoding="utf-8")
self.assertIn(
"QUANT60_JOINQUANT_EVIDENCE_V1 ",
joinquant_source,
)
self.assertIn("CURRENT_DATA_DAY_OPEN", joinquant_source)
self.assertIn("day_open", joinquant_source)
self.assertNotIn("CURRENT_DATA_LAST_PRICE", joinquant_source)
for event in manifest["joinquant_observation"]["events"]:
self.assertIn(event, joinquant_source)
tampered = dict(package)
tampered["release"] = "tampered"
package_path.write_text(
json.dumps(tampered),
encoding="utf-8",
)
with self.assertRaisesRegex(ValueError, "hash mismatch"):
bundle_all(ROOT, root / "tampered", package_path)
class CapabilityProbeTest(unittest.TestCase): class CapabilityProbeTest(unittest.TestCase):
def test_shallow_probe_does_not_claim_external_verification(self): def test_shallow_probe_does_not_claim_external_verification(self):
@@ -0,0 +1,687 @@
import base64
import copy
import contextlib
import datetime
import io
import json
import sys
import types
import unittest
from pathlib import Path
from types import SimpleNamespace
from unittest import mock
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "src"))
from platforms import joinquant_strategy, qmt_builtin_strategy
from platforms.fake_joinquant_harness import (
FakeJoinQuantHarness,
FakePosition,
)
from platforms.fake_qmt_harness import FakeQmtContext, FakeQmtHarness
from quant60 import portable_core
from quant60.portable_core import target_package_tape_sha256
from quant60.target_package import seal_target_package
def _package(
signal_as_of="2026-07-17T15:00:00+08:00",
next_session="2026-07-20",
package_id="TP-20260717-A",
decision_id="D-20260717-A",
):
return seal_target_package(
{
"schema_version": "1.0",
"package_type": "TargetPackageV1",
"package_id": package_id,
"decision_id": decision_id,
"release": "quant60-baseline-60-v1",
"evidence_class": "provider-snapshot-local",
"model_id": "ridge-alpha-v1",
"signal_as_of": signal_as_of,
"next_session": next_session,
"model_sha256": "1" * 64,
"data_sha256": "2" * 64,
"feature_sha256": "3" * 64,
"risk_sha256": "4" * 64,
"cost_sha256": "5" * 64,
"optimizer_sha256": "6" * 64,
"config_sha256": "7" * 64,
"source_sha256": "8" * 64,
"target_weights": {
"000001.XSHE": 0.30,
"600000.XSHG": 0.0,
},
"universe": {
"000001.XSHE": {
"state": "openable",
"reasons": ["ELIGIBLE"],
},
"600000.XSHG": {
"state": "sell_only",
"reasons": ["RISK_REDUCTION"],
},
},
"constraints": {
"constraint_state": "FEASIBLE",
"long_only": True,
"max_single_weight": 0.40,
"max_gross_weight": 0.80,
"max_one_way_turnover": 0.80,
"actual_gross_weight": 0.30,
},
}
)
def _jq_histories():
return {
"000001.XSHE": [
10.0 + 0.1 * index for index in range(21)
],
"600000.XSHG": [
15.0 - 0.02 * index for index in range(21)
],
}
def _qmt_histories():
return {
symbol.replace(".XSHE", ".SZ").replace(".XSHG", ".SH"): values
for symbol, values in _jq_histories().items()
}
def _target_config(base, packages):
config = dict(base)
config.update(
{
"decision_mode": "target_package",
"target_package_count": len(packages),
"target_package_tape_sha256": target_package_tape_sha256(
packages
),
"universe_mode": "fixed",
"universe": sorted(
{
symbol
for package in packages
for symbol in package["universe"]
}
),
}
)
return config
def _evidence_records(output):
prefix = joinquant_strategy.EVIDENCE_PREFIX
records = []
for line in output.getvalue().splitlines():
if not line.startswith(prefix):
continue
encoded = line[len(prefix) :]
record = json.loads(encoded)
canonical = json.dumps(
record,
ensure_ascii=True,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
)
if canonical != encoded:
raise AssertionError("JoinQuant evidence is not canonical JSON")
records.append(record)
return records
class PlatformTargetPackageExecutionTest(unittest.TestCase):
def setUp(self):
self.jq_config = joinquant_strategy.CONFIG
self.jq_packages = joinquant_strategy.TARGET_PACKAGES
self.qmt_config = qmt_builtin_strategy.CONFIG
self.qmt_packages = qmt_builtin_strategy.TARGET_PACKAGES
def tearDown(self):
joinquant_strategy.CONFIG = self.jq_config
joinquant_strategy.TARGET_PACKAGES = self.jq_packages
qmt_builtin_strategy.CONFIG = self.qmt_config
qmt_builtin_strategy.TARGET_PACKAGES = self.qmt_packages
def _install_package(self, package):
self._install_packages([package])
def _install_packages(self, packages):
packages = copy.deepcopy(packages)
joinquant_strategy.TARGET_PACKAGES = copy.deepcopy(packages)
joinquant_strategy.CONFIG = _target_config(
self.jq_config,
packages,
)
qmt_builtin_strategy.TARGET_PACKAGES = copy.deepcopy(packages)
qmt_config = _target_config(self.qmt_config, packages)
qmt_config["universe"] = [
symbol.replace(".XSHE", ".SZ").replace(".XSHG", ".SH")
for symbol in qmt_config["universe"]
]
qmt_builtin_strategy.CONFIG = qmt_config
def test_same_package_binds_same_weights_orders_and_lineage(self):
package = _package()
self._install_package(package)
jq = FakeJoinQuantHarness(
_jq_histories(),
positions={
"600000.XSHG": FakePosition(
total_amount=1050,
closeable_amount=200,
)
},
)
jq.initialize(joinquant_strategy)
jq_plan = jq.run_scheduled()
qmt_position = SimpleNamespace(
stock_code="600000.SH",
volume=1050,
can_use_volume=200,
)
context = FakeQmtContext(
_qmt_histories(),
positions=[qmt_position],
bar_time=datetime.datetime(2026, 7, 17, 15, 0),
)
qmt = FakeQmtHarness(context)
qmt_plan = qmt.run(qmt_builtin_strategy)
self.assertIsNotNone(jq_plan)
self.assertIsNotNone(qmt_plan)
for field in (
"weights",
"targets",
"orders",
"lineage",
"universe",
):
self.assertEqual(jq_plan[field], qmt_plan[field])
self.assertEqual(
jq_plan["lineage"],
{
"package_id": package["package_id"],
"package_sha256": package["package_sha256"],
"decision_id": package["decision_id"],
"signal_as_of": package["signal_as_of"],
"next_session": package["next_session"],
"release": package["release"],
"model_id": package["model_id"],
},
)
self.assertEqual(
jq_plan["execution_contract"],
{
"signal_price_source": "CURRENT_DATA_DAY_OPEN",
"submit_trigger": "DECLARED_NEXT_SESSION_OPEN_CALLBACK",
"declared_next_session": package["next_session"],
"real_platform_observed": False,
},
)
self.assertEqual(
qmt_plan["execution_contract"],
{
"signal_price_source": "COMPLETED_DAILY_CLOSE",
"submit_trigger": "NEXT_BAR_FIRST_TICK",
"quick_trade": 0,
"declared_next_session": package["next_session"],
"real_platform_observed": False,
},
)
self.assertEqual(jq_plan["orders"]["600000.XSHG"], -200)
self.assertEqual(dict(jq.orders)["600000.XSHG"], 850)
self.assertEqual(qmt_plan["orders"]["600000.XSHG"], -200)
sell_order = [
order
for order in qmt.orders
if order["order_code"] == "600000.SH"
][0]
self.assertEqual(sell_order["volume"], 200)
self.assertEqual(sell_order["quick_trade"], 0)
self.assertTrue(
all(
int(quantity) % 100 == 0
for quantity in jq_plan["targets"].values()
)
)
def test_missing_exact_clock_is_noop_without_momentum_fallback(self):
package = _package(
signal_as_of="2026-07-10T15:00:00+08:00",
next_session="2026-07-13",
)
self._install_package(package)
jq = FakeJoinQuantHarness(_jq_histories())
jq.initialize(joinquant_strategy)
self.assertIsNone(jq.run_scheduled())
self.assertEqual(jq.orders, [])
self.assertIsNone(jq.last_index_request)
self.assertIsNone(jq.last_extras_request)
self.assertIsNone(jq.last_history_request)
context = FakeQmtContext(
_qmt_histories(),
bar_time=datetime.datetime(2026, 7, 17, 15, 0),
)
qmt = FakeQmtHarness(context)
self.assertIsNone(qmt.run(qmt_builtin_strategy))
self.assertEqual(qmt.orders, [])
self.assertIsNone(context.last_sector_request)
self.assertIsNone(context.last_market_request)
def test_tampered_package_fails_closed_during_initialization(self):
package = _package()
tampered = copy.deepcopy(package)
tampered["target_weights"]["000001.XSHE"] = 0.25
tampered["constraints"]["actual_gross_weight"] = 0.25
self._install_package(tampered)
jq = FakeJoinQuantHarness(_jq_histories())
with self.assertRaises(ValueError):
jq.initialize(joinquant_strategy)
self.assertEqual(jq.orders, [])
context = FakeQmtContext(_qmt_histories())
qmt = FakeQmtHarness(context)
with self.assertRaises(ValueError):
qmt.run(qmt_builtin_strategy)
self.assertEqual(qmt.orders, [])
def test_duplicate_package_id_fails_closed_during_initialization(self):
first = _package()
second = _package(
signal_as_of="2026-07-24T15:00:00+08:00",
next_session="2026-07-27",
package_id=first["package_id"],
decision_id="D-20260724-B",
)
self._install_packages([first, second])
jq = FakeJoinQuantHarness(_jq_histories())
with self.assertRaisesRegex(ValueError, "duplicate"):
jq.initialize(joinquant_strategy)
context = FakeQmtContext(_qmt_histories())
qmt = FakeQmtHarness(context)
with self.assertRaisesRegex(
qmt_builtin_strategy.UnsupportedStrategyPeriod,
"duplicate",
):
qmt.run(qmt_builtin_strategy)
def test_joinquant_emits_canonical_observation_stream_and_fail_soft_eod(self):
package = _package()
self._install_package(package)
jq = FakeJoinQuantHarness(
_jq_histories(),
positions={
"600000.XSHG": FakePosition(
total_amount=1050,
closeable_amount=200,
)
},
)
output = io.StringIO()
with contextlib.redirect_stdout(output):
jq.initialize(joinquant_strategy)
plan = jq.run_scheduled()
joinquant_strategy.after_trading_end(jq.context)
self.assertIsNotNone(plan)
records = _evidence_records(output)
events = [record["event"] for record in records]
for event in (
"INIT",
"TARGET_PACKAGE_HIT",
"PLATFORM_BINDING_INPUT",
"TARGET_PACKAGE_PLAN",
"ORDER_REQUEST",
"ORDER_RETURN",
"EOD_STATUS",
):
self.assertIn(event, events)
self.assertEqual(
[record["sequence"] for record in records],
list(range(1, len(records) + 1)),
)
binding = [
record["payload"]
for record in records
if record["event"] == "PLATFORM_BINDING_INPUT"
][0]
self.assertEqual(binding["equity"], 1_000_000.0)
self.assertEqual(binding["current"], {"600000.XSHG": 1050})
self.assertEqual(binding["sellable"], {"600000.XSHG": 200})
self.assertEqual(
binding["price_source"],
"CURRENT_DATA_DAY_OPEN",
)
self.assertEqual(
binding["lineage"]["package_sha256"],
package["package_sha256"],
)
plan_record = [
record["payload"]
for record in records
if record["event"] == "TARGET_PACKAGE_PLAN"
][0]
self.assertEqual(plan_record["weights"], package["target_weights"])
self.assertEqual(plan_record["orders"], plan["orders"])
eod_records = [
record
for record in records
if record["event"] == "EOD_STATUS"
]
self.assertTrue(eod_records)
chunk_count = eod_records[0]["chunk_count"]
self.assertEqual(len(eod_records), chunk_count)
self.assertEqual(
[record["chunk_index"] for record in eod_records],
list(range(1, chunk_count + 1)),
)
self.assertTrue(
all(
record["chunk_count"] == chunk_count
for record in eod_records
)
)
self.assertTrue(
all(
record["payload"]["chunk_encoding"]
== "base64-canonical-json"
for record in eod_records
)
)
encoded = "".join(
record["payload"]["content"]
for record in eod_records
)
self.assertTrue(
all(
record["payload"]["encoded_length"] == len(encoded)
for record in eod_records
)
)
for record in eod_records:
line = joinquant_strategy.EVIDENCE_PREFIX + json.dumps(
record,
ensure_ascii=True,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
)
self.assertLess(len(line.encode("utf-8")), 3 * 1024)
canonical = base64.b64decode(
encoded.encode("ascii"),
validate=True,
)
eod = json.loads(canonical.decode("ascii"))
self.assertEqual(
base64.b64encode(
json.dumps(
eod,
ensure_ascii=True,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("ascii")
).decode("ascii"),
encoded,
)
self.assertFalse(eod["orders_api"]["available"])
self.assertFalse(eod["trades_api"]["available"])
self.assertTrue(eod["portfolio"]["available"])
self.assertEqual(
eod["last_plan"]["lineage"]["package_id"],
package["package_id"],
)
def test_joinquant_target_package_binds_day_open_not_last_price(self):
package = _package()
self._install_package(package)
jq = FakeJoinQuantHarness(_jq_histories())
jq.current_data["000001.XSHE"].day_open = 20.0
jq.current_data["000001.XSHE"].last_price = 999.0
output = io.StringIO()
with contextlib.redirect_stdout(output):
jq.initialize(joinquant_strategy)
plan = jq.run_scheduled()
self.assertIsNotNone(plan)
self.assertEqual(plan["targets"]["000001.XSHE"], 15_000)
self.assertEqual(jq.orders, [("000001.XSHE", 15_000)])
binding = [
record["payload"]
for record in _evidence_records(output)
if record["event"] == "PLATFORM_BINDING_INPUT"
][0]
self.assertEqual(binding["price_source"], "CURRENT_DATA_DAY_OPEN")
self.assertEqual(binding["prices"]["000001.XSHE"], 20.0)
self.assertIsNone(jq.last_history_request)
def test_joinquant_missing_day_open_fails_closed_without_fallback(self):
package = _package()
self._install_package(package)
jq = FakeJoinQuantHarness(_jq_histories())
del jq.current_data["000001.XSHE"].day_open
output = io.StringIO()
with contextlib.redirect_stdout(output):
jq.initialize(joinquant_strategy)
with self.assertRaisesRegex(
joinquant_strategy.PriceHistoryUnavailable,
"day_open",
):
jq.run_scheduled()
self.assertEqual(jq.orders, [])
self.assertIsNone(jq.last_index_request)
self.assertIsNone(jq.last_extras_request)
self.assertIsNone(jq.last_history_request)
events = [
record["event"] for record in _evidence_records(output)
]
self.assertIn("TARGET_PACKAGE_HIT", events)
self.assertNotIn("PLATFORM_BINDING_INPUT", events)
self.assertNotIn("TARGET_PACKAGE_PLAN", events)
self.assertNotIn("ORDER_REQUEST", events)
def test_joinquant_noop_eod_clears_prior_session_evidence(self):
package = _package()
self._install_package(package)
jq = FakeJoinQuantHarness(
_jq_histories(),
positions={
"600000.XSHG": FakePosition(
total_amount=1050,
closeable_amount=200,
)
},
)
jq.current_data["000001.XSHE"].paused = True
def eod_payload(output):
chunks = [
record
for record in _evidence_records(output)
if record["event"] == "EOD_STATUS"
]
self.assertTrue(chunks)
self.assertEqual(
[record["chunk_index"] for record in chunks],
list(range(1, chunks[0]["chunk_count"] + 1)),
)
encoded = "".join(
record["payload"]["content"] for record in chunks
)
return json.loads(
base64.b64decode(
encoded.encode("ascii"),
validate=True,
).decode("ascii")
)
hit_output = io.StringIO()
with contextlib.redirect_stdout(hit_output):
jq.initialize(joinquant_strategy)
self.assertIsNotNone(jq.run_scheduled())
joinquant_strategy.after_trading_end(jq.context)
hit_eod = eod_payload(hit_output)
self.assertIsNotNone(hit_eod["last_plan"])
self.assertTrue(hit_eod["order_returns"])
self.assertTrue(hit_eod["skipped_orders"])
orders_after_hit = list(jq.orders)
self.assertTrue(orders_after_hit)
for unused_day in range(2):
del unused_day
jq.advance_session()
noop_output = io.StringIO()
with contextlib.redirect_stdout(noop_output):
self.assertIsNone(jq.run_scheduled())
joinquant_strategy.after_trading_end(jq.context)
noop_records = _evidence_records(noop_output)
self.assertIn(
"TARGET_PACKAGE_NOOP",
[record["event"] for record in noop_records],
)
noop_eod = eod_payload(noop_output)
self.assertIsNone(noop_eod["last_plan"])
self.assertEqual(noop_eod["order_returns"], [])
self.assertEqual(noop_eod["skipped_orders"], [])
self.assertEqual(jq.orders, orders_after_hit)
def test_joinquant_hostile_any_still_runs_final_three_day_tape(self):
bundled = types.ModuleType("hostile_joinquant_bundle")
source = (
ROOT / "dist/target-package/joinquant_strategy.py"
).read_bytes()
exec(
compile(
source,
"dist/target-package/joinquant_strategy.py",
"exec",
),
bundled.__dict__,
)
package = copy.deepcopy(bundled.TARGET_PACKAGES[0])
histories = {
symbol: [10.0] * 21 for symbol in package["universe"]
}
hostile_any = lambda values: True
def run_three_days(module):
jq = FakeJoinQuantHarness(histories)
jq.context.current_dt = datetime.datetime(
2024,
3,
11,
9,
30,
)
jq.context.previous_date = datetime.date(2024, 3, 8)
daily_orders = []
output = io.StringIO()
with contextlib.redirect_stdout(output):
jq.initialize(module)
for day in range(3):
before = len(jq.orders)
jq.run_scheduled()
daily_orders.append(len(jq.orders) - before)
if day < 2:
jq.advance_session()
self.assertEqual(daily_orders, [0, 4, 0])
with self.subTest(artifact="source-wrapper"):
self._install_package(package)
with (
mock.patch.object(
portable_core,
"any",
hostile_any,
create=True,
),
mock.patch.object(
joinquant_strategy,
"any",
hostile_any,
create=True,
),
):
run_three_days(joinquant_strategy)
with self.subTest(artifact="final-target-bundle"):
bundled.any = hostile_any
self.assertEqual(
bundled.TARGET_PACKAGES[0]["package_sha256"],
package["package_sha256"],
)
run_three_days(bundled)
def test_joinquant_emits_noop_and_skipped_order(self):
missing = _package(
signal_as_of="2026-07-10T15:00:00+08:00",
next_session="2026-07-13",
)
self._install_package(missing)
jq = FakeJoinQuantHarness(_jq_histories())
output = io.StringIO()
with contextlib.redirect_stdout(output):
jq.initialize(joinquant_strategy)
self.assertIsNone(jq.run_scheduled())
events = [
record["event"] for record in _evidence_records(output)
]
self.assertIn("TARGET_PACKAGE_NOOP", events)
self.assertNotIn("TARGET_PACKAGE_PLAN", events)
self.assertNotIn("ORDER_REQUEST", events)
package = _package()
self._install_package(package)
jq = FakeJoinQuantHarness(_jq_histories())
jq.current_data["000001.XSHE"].paused = True
output = io.StringIO()
with contextlib.redirect_stdout(output):
jq.initialize(joinquant_strategy)
plan = jq.run_scheduled()
self.assertIsNotNone(plan)
records = _evidence_records(output)
skipped = [
record["payload"]
for record in records
if record["event"] == "SKIPPED_ORDER"
]
self.assertEqual(len(skipped), 1)
self.assertEqual(skipped[0]["skip"]["reason"], "paused")
self.assertEqual(jq.orders, [])
def test_joinquant_observation_failure_does_not_change_order_path(self):
package = _package()
self._install_package(package)
jq = FakeJoinQuantHarness(_jq_histories())
with mock.patch("builtins.print", side_effect=RuntimeError("log down")):
jq.initialize(joinquant_strategy)
plan = jq.run_scheduled()
joinquant_strategy.after_trading_end(jq.context)
self.assertIsNotNone(plan)
self.assertEqual(
sorted(jq.orders),
[("000001.XSHE", 25000)],
)
if __name__ == "__main__":
unittest.main()
+228
View File
@@ -0,0 +1,228 @@
import copy
import unittest
from quant60.baseline_pipeline import (
AlphaForecastInput,
BaselinePipelineError,
BaselinePipelinePolicy,
CostForecastInput,
FIVE_LAYER_ORDER,
RiskForecastInput,
SecurityStateInput,
build_baseline_pipeline,
verify_layer_trace,
)
from quant60.optimizer import PortfolioConstraints
from quant60.universe import UniverseState
SYMBOLS = (
"600000.XSHG",
"000001.XSHE",
"000333.XSHE",
)
def inputs(*, first_variance=0.03, first_cost_bps=8.0):
alpha = [
AlphaForecastInput(
SYMBOLS[0],
expected_excess_return=0.030,
model_version="ridge-frozen-fixture-v1",
feature_version="transparent-price-v1",
),
AlphaForecastInput(
SYMBOLS[1],
expected_excess_return=0.020,
model_version="ridge-frozen-fixture-v1",
feature_version="transparent-price-v1",
),
AlphaForecastInput(
SYMBOLS[2],
expected_excess_return=0.010,
model_version="ridge-frozen-fixture-v1",
feature_version="transparent-price-v1",
),
]
risk = [
RiskForecastInput(
SYMBOLS[0],
first_variance,
"diagonal-risk-fixture-v1",
),
RiskForecastInput(SYMBOLS[1], 0.03, "diagonal-risk-fixture-v1"),
RiskForecastInput(SYMBOLS[2], 0.03, "diagonal-risk-fixture-v1"),
]
cost = [
CostForecastInput(
SYMBOLS[0],
first_cost_bps,
1.0,
"dated-cost-fixture-v1",
),
CostForecastInput(SYMBOLS[1], 8.0, 1.0, "dated-cost-fixture-v1"),
CostForecastInput(SYMBOLS[2], 8.0, 1.0, "dated-cost-fixture-v1"),
]
securities = [
SecurityStateInput(
SYMBOLS[0],
UniverseState.OPENABLE,
"bank",
10.0,
),
SecurityStateInput(
SYMBOLS[1],
UniverseState.OPENABLE,
"bank",
20.0,
),
SecurityStateInput(
SYMBOLS[2],
UniverseState.OPENABLE,
"consumer",
50.0,
),
]
return alpha, risk, cost, securities
def policy():
return BaselinePipelinePolicy(
constraints=PortfolioConstraints(
gross_target=0.80,
single_name_cap=0.80,
industry_cap=0.80,
one_way_turnover_cap=0.80,
risk_aversion=1.0,
cost_aversion=1.0,
turnover_aversion=0.0,
),
cash_buffer=0.10,
)
class BaselinePipelineTests(unittest.TestCase):
def test_canonical_path_emits_exact_five_layer_hash_chain(self):
alpha, risk, cost, securities = inputs()
result = build_baseline_pipeline(
alpha=alpha,
risk=risk,
cost=cost,
securities=securities,
equity=1_000_000,
policy=policy(),
)
self.assertEqual(
tuple(event["layer"] for event in result.layer_trace),
FIVE_LAYER_ORDER,
)
self.assertTrue(result.risk_decision.approved)
self.assertTrue(result.order_deltas)
verify_layer_trace(result.layer_trace, result.layer_payloads)
def test_risk_and_cost_are_causal_inputs_to_portfolio(self):
alpha, risk, cost, securities = inputs()
base = build_baseline_pipeline(
alpha=alpha,
risk=risk,
cost=cost,
securities=securities,
equity=1_000_000,
policy=policy(),
)
alpha, risk, cost, securities = inputs(first_variance=0.30)
high_risk = build_baseline_pipeline(
alpha=alpha,
risk=risk,
cost=cost,
securities=securities,
equity=1_000_000,
policy=policy(),
)
alpha, risk, cost, securities = inputs(first_cost_bps=250.0)
high_cost = build_baseline_pipeline(
alpha=alpha,
risk=risk,
cost=cost,
securities=securities,
equity=1_000_000,
policy=policy(),
)
symbol = SYMBOLS[0]
self.assertLess(
high_risk.solution.weights[symbol],
base.solution.weights[symbol],
)
self.assertLess(
high_cost.solution.weights[symbol],
base.solution.weights[symbol],
)
self.assertNotEqual(
base.layer_trace[2]["event_sha256"],
high_risk.layer_trace[2]["event_sha256"],
)
self.assertNotEqual(
base.layer_trace[2]["event_sha256"],
high_cost.layer_trace[2]["event_sha256"],
)
def test_missing_layer_input_fails_closed(self):
alpha, risk, cost, securities = inputs()
with self.assertRaisesRegex(
BaselinePipelineError,
"symbol sets must match exactly",
):
build_baseline_pipeline(
alpha=alpha,
risk=risk[:-1],
cost=cost,
securities=securities,
equity=1_000_000,
policy=policy(),
)
def test_smoke_model_cannot_claim_baseline_candidate(self):
alpha, risk, cost, securities = inputs()
alpha = [
AlphaForecastInput(
item.symbol,
item.expected_excess_return,
"portable-momentum-v1",
item.feature_version,
)
for item in alpha
]
with self.assertRaisesRegex(
BaselinePipelineError,
"platform smoke strategy",
):
build_baseline_pipeline(
alpha=alpha,
risk=risk,
cost=cost,
securities=securities,
equity=1_000_000,
policy=policy(),
)
def test_layer_trace_tampering_is_rejected(self):
alpha, risk, cost, securities = inputs()
result = build_baseline_pipeline(
alpha=alpha,
risk=risk,
cost=cost,
securities=securities,
equity=1_000_000,
policy=policy(),
)
trace = copy.deepcopy(list(result.layer_trace))
trace[2]["output_sha256"] = "0" * 64
with self.assertRaisesRegex(
BaselinePipelineError,
"output hash mismatch",
):
verify_layer_trace(trace, result.layer_payloads)
if __name__ == "__main__":
unittest.main()
+134
View File
@@ -0,0 +1,134 @@
import json
import subprocess
import sys
import unittest
from pathlib import Path
from quant60.ledger import canonical_json
from quant60.release_reachability import LAYERS
PROJECT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PROJECT))
from tools.build_release_reachability import ( # noqa: E402
DEFAULT_OUTPUT,
JOINQUANT_TARGET_PACKAGE_OBSERVED_AT,
JOINQUANT_TARGET_PACKAGE_RUN_ID,
JOINQUANT_TARGET_PACKAGE_RUN_PATH,
PLATFORM_ENTRYPOINTS,
RELEASE_ID,
build_manifest,
)
class BuildReleaseReachabilityTests(unittest.TestCase):
def test_checked_in_manifest_exactly_matches_generator(self):
expected = build_manifest(repository_root=PROJECT)
observed = json.loads(DEFAULT_OUTPUT.read_text(encoding="utf-8"))
self.assertEqual(observed, expected)
self.assertEqual(
DEFAULT_OUTPUT.read_bytes(),
(canonical_json(expected) + "\n").encode("utf-8"),
)
def test_only_execution_has_real_joinquant_observation(self):
manifest = build_manifest(repository_root=PROJECT)
self.assertEqual(manifest["release_id"], RELEASE_ID)
for layer in LAYERS:
claims = manifest["layers"][layer]
for declaration in PLATFORM_ENTRYPOINTS.values():
claim = claims[declaration["capability"]]
self.assertTrue(claim["status"])
self.assertEqual(len(claim["artifacts"]), 1)
artifact = claim["artifacts"][0]
self.assertIn("targetpackagev1", artifact["artifact_id"])
self.assertEqual(artifact["path"], declaration["path"])
self.assertEqual(artifact["platform"], declaration["platform"])
observed = claims["real_platform_observed"]
if layer != "execution":
self.assertEqual(
observed,
{"status": False, "artifacts": []},
)
continue
self.assertTrue(observed["status"])
self.assertEqual(len(observed["artifacts"]), 1)
artifact = observed["artifacts"][0]
self.assertEqual(artifact["kind"], "platform_run")
self.assertEqual(artifact["platform"], "joinquant")
self.assertEqual(
artifact["path"],
JOINQUANT_TARGET_PACKAGE_RUN_PATH,
)
self.assertEqual(
artifact["observed_at"],
JOINQUANT_TARGET_PACKAGE_OBSERVED_AT,
)
self.assertEqual(
artifact["run_id"],
JOINQUANT_TARGET_PACKAGE_RUN_ID,
)
self.assertFalse(
manifest["summary"]["all_layers_real_platform_observed"]
)
def test_tracked_joinquant_run_preserves_conservative_boundary(self):
run = json.loads(
(PROJECT / JOINQUANT_TARGET_PACKAGE_RUN_PATH).read_text(
encoding="utf-8"
)
)
self.assertEqual(run["run_id"], JOINQUANT_TARGET_PACKAGE_RUN_ID)
self.assertEqual(run["observed_at"], JOINQUANT_TARGET_PACKAGE_OBSERVED_AT)
self.assertEqual(
run["platform_evidence_class"],
"real_platform_runtime",
)
self.assertEqual(
run["input_evidence_class"],
"synthetic-research",
)
self.assertFalse(run["performance_claim"])
self.assertEqual(
run["target_package"]["evidence_class"],
"synthetic-research",
)
self.assertEqual(run["counts"], {
"orders": 3,
"fills": 3,
"skipped_orders": 0,
})
boundary = run["claim_boundary"]
self.assertEqual(
boundary["real_platform_observed_layers"],
["execution"],
)
self.assertFalse(boundary["upstream_layers_real_platform_observed"])
self.assertFalse(boundary["qmt_peer_observed"])
self.assertFalse(boundary["same_input_cross_engine_parity_observed"])
self.assertFalse(boundary["all_five_layers_real_platform_observed"])
self.assertFalse(boundary["g9_passed"])
self.assertFalse(boundary["baseline_60_passed"])
self.assertFalse(boundary["investment_value_claim"])
def test_check_mode_verifies_current_file(self):
completed = subprocess.run(
[
sys.executable,
str(PROJECT / "tools/build_release_reachability.py"),
"--check",
],
cwd=PROJECT,
check=False,
capture_output=True,
text=True,
)
self.assertEqual(completed.returncode, 0, completed.stderr)
result = json.loads(completed.stdout)
self.assertTrue(result["ok"])
self.assertTrue(result["checked"])
if __name__ == "__main__":
unittest.main()
+16
View File
@@ -19,6 +19,22 @@ class ResearchExperimentTests(unittest.TestCase):
self.assertGreater(first["report"]["fold_count"], 0) self.assertGreater(first["report"]["fold_count"], 0)
self.assertFalse(first["report"]["investment_value_claim"]) self.assertFalse(first["report"]["investment_value_claim"])
self.assertTrue(first["targets"]) self.assertTrue(first["targets"])
self.assertLess(
first["report"]["final_training_max_label_exit"],
first["report"]["target_date"],
)
self.assertEqual(
first["target_package"]["model_sha256"],
first["model_bundle"]["bundle_sha256"],
)
self.assertEqual(
[item["layer"] for item in first["pipeline"]["layer_trace"]],
["universe", "alpha", "portfolio", "risk", "execution"],
)
self.assertEqual(
first["target_package"]["target_weights"],
first["pipeline"]["weights"],
)
with tempfile.TemporaryDirectory() as left_dir, tempfile.TemporaryDirectory() as right_dir: with tempfile.TemporaryDirectory() as left_dir, tempfile.TemporaryDirectory() as right_dir:
left = write_research_artifacts(first, left_dir) left = write_research_artifacts(first, left_dir)
+191
View File
@@ -0,0 +1,191 @@
import copy
import hashlib
import json
import tempfile
import unittest
from pathlib import Path
from quant60.features import TrainOnlyFeaturePreprocessor
from quant60.ledger import canonical_json
from quant60.model_bundle import (
ModelBundleIntegrityError,
create_model_bundle,
load_verified_model_bundle,
model_bundle_sha256,
verify_model_bundle,
write_model_bundle,
)
from quant60.research import DeterministicRidge
HASHES = {
"data_sha256": hashlib.sha256(b"training-data").hexdigest(),
"source_sha256": hashlib.sha256(b"source-tree").hexdigest(),
"config_sha256": hashlib.sha256(b"training-config").hexdigest(),
}
class ModelBundleTests(unittest.TestCase):
def setUp(self):
self.training_rows = [
{"momentum": -0.2, "volatility": 0.10},
{"momentum": -0.1, "volatility": None},
{"momentum": 0.0, "volatility": 0.20},
{"momentum": 0.1, "volatility": 0.25},
{"momentum": 0.2, "volatility": 0.30},
]
self.processor = TrainOnlyFeaturePreprocessor(
winsor_fraction=0.1
).fit(self.training_rows)
transformed = self.processor.transform(self.training_rows)
matrix = [
[row[name] for name in self.processor.output_names]
for row in transformed
]
self.model = DeterministicRidge(alpha=0.5).fit(
matrix,
[-0.03, -0.01, 0.0, 0.02, 0.04],
)
self.bundle = create_model_bundle(
self.processor,
self.model,
trained_through="2024-12-31",
label_name="t_plus_1_open_to_t_plus_6_open_excess",
horizon_trading_days=5,
**HASHES,
)
def test_freezes_preprocessor_model_label_and_lineage(self):
verified = verify_model_bundle(self.bundle)
self.assertEqual(
verified.feature_order,
tuple(sorted(self.training_rows[0])),
)
self.assertEqual(
verified.output_feature_order,
self.processor.output_names,
)
self.assertEqual(
verified.coefficients,
self.model.coef_,
)
self.assertEqual(verified.intercept, self.model.intercept_)
self.assertEqual(verified.trained_through.isoformat(), "2024-12-31")
self.assertEqual(verified.horizon_trading_days, 5)
self.assertEqual(verified.data_sha256, HASHES["data_sha256"])
def test_write_read_and_prediction_replay_are_deterministic(self):
replay_rows = [
{"volatility": None, "momentum": 0.07},
{"momentum": -100.0, "volatility": 100.0},
]
transformed = self.processor.transform(replay_rows)
matrix = [
[row[name] for name in self.processor.output_names]
for row in transformed
]
expected = self.model.predict(matrix)
with tempfile.TemporaryDirectory() as directory:
left = write_model_bundle(
self.bundle,
Path(directory) / "left.json",
)
right = write_model_bundle(
copy.deepcopy(self.bundle),
Path(directory) / "right.json",
)
self.assertEqual(left.read_bytes(), right.read_bytes())
loaded = load_verified_model_bundle(
left,
expected_bundle_sha256=self.bundle["bundle_sha256"],
**{
f"expected_{key}": value
for key, value in HASHES.items()
},
)
self.assertEqual(loaded.predict(replay_rows), expected)
independently_loaded = load_verified_model_bundle(right)
self.assertEqual(
loaded.prediction_bytes(replay_rows),
independently_loaded.prediction_bytes(replay_rows),
)
self.assertEqual(loaded.canonical_bytes, left.read_bytes())
def test_content_tampering_fails_closed(self):
with tempfile.TemporaryDirectory() as directory:
path = write_model_bundle(
self.bundle,
Path(directory) / "bundle.json",
)
tampered = json.loads(path.read_text(encoding="utf-8"))
tampered["model"]["coefficients"][0] += 0.01
path.write_text(
canonical_json(tampered) + "\n",
encoding="utf-8",
)
with self.assertRaisesRegex(
ModelBundleIntegrityError,
"SHA-256 mismatch",
):
load_verified_model_bundle(path)
def test_resealed_semantic_tampering_fails_closed(self):
tampered = copy.deepcopy(self.bundle)
tampered["preprocessor"]["output_feature_order"] = list(
reversed(tampered["preprocessor"]["output_feature_order"])
)
tampered["bundle_sha256"] = model_bundle_sha256(tampered)
with self.assertRaisesRegex(
ModelBundleIntegrityError,
"output_feature_order is inconsistent",
):
verify_model_bundle(tampered)
def test_expected_identity_rejects_a_valid_but_wrong_bundle(self):
with self.assertRaisesRegex(
ModelBundleIntegrityError,
"data_sha256 does not match expected identity",
):
verify_model_bundle(
self.bundle,
expected_data_sha256="f" * 64,
)
def test_placeholder_lineage_hash_is_rejected(self):
with self.assertRaisesRegex(
ModelBundleIntegrityError,
"all-zero placeholder",
):
create_model_bundle(
self.processor,
self.model,
trained_through="2024-12-31",
label_name="label",
horizon_trading_days=5,
data_sha256="0" * 64,
source_sha256=HASHES["source_sha256"],
config_sha256=HASHES["config_sha256"],
)
def test_unfitted_components_and_non_finite_state_are_rejected(self):
with self.assertRaisesRegex(ValueError, "preprocessor is not fitted"):
create_model_bundle(
TrainOnlyFeaturePreprocessor(),
self.model,
trained_through="2024-12-31",
label_name="label",
horizon_trading_days=5,
**HASHES,
)
tampered = copy.deepcopy(self.bundle)
tampered["model"]["intercept"] = float("nan")
with self.assertRaisesRegex(
ModelBundleIntegrityError,
"canonical JSON",
):
verify_model_bundle(tampered)
if __name__ == "__main__":
unittest.main()
+332
View File
@@ -1,16 +1,99 @@
import ast
import copy
import hashlib
import unittest import unittest
from pathlib import Path
from quant60.ledger import canonical_json
from quant60.portable_core import ( from quant60.portable_core import (
TargetPackageLookupMiss,
_any_true,
build_rebalance_plan, build_rebalance_plan,
build_target_weight_plan,
capped_target_weights, capped_target_weights,
lookup_target_package_exact,
momentum_score, momentum_score,
normalize_symbol, normalize_symbol,
order_deltas, order_deltas,
target_package_tape_sha256,
target_quantities, target_quantities,
verify_target_package,
) )
from quant60.target_package import seal_target_package
def sealed_target_package(
package_id="TP-20260724-A",
decision_id="D-20260724-A",
signal_as_of="2026-07-24T15:00:00+08:00",
next_session="2026-07-27",
):
return seal_target_package(
{
"schema_version": "1.0",
"package_type": "TargetPackageV1",
"package_id": package_id,
"decision_id": decision_id,
"release": "quant60-baseline-60-v1",
"evidence_class": "provider-snapshot-local",
"model_id": "alpha158-lightgbm-v1",
"signal_as_of": signal_as_of,
"next_session": next_session,
"model_sha256": "1" * 64,
"data_sha256": "2" * 64,
"feature_sha256": "3" * 64,
"risk_sha256": "4" * 64,
"cost_sha256": "5" * 64,
"optimizer_sha256": "6" * 64,
"config_sha256": "7" * 64,
"source_sha256": "8" * 64,
"target_weights": {
"000001.XSHE": 0.30,
"600000.XSHG": 0.20,
},
"universe": {
"000001.XSHE": {
"state": "openable",
"reasons": ["ELIGIBLE"],
},
"600000.XSHG": {
"state": "hold_only",
"reasons": ["LIQUIDITY_BELOW_FLOOR"],
},
},
"constraints": {
"constraint_state": "FEASIBLE",
"long_only": True,
"max_single_weight": 0.40,
"max_gross_weight": 0.80,
"max_one_way_turnover": 0.20,
"actual_gross_weight": 0.50,
},
}
)
class PortableCoreTests(unittest.TestCase): class PortableCoreTests(unittest.TestCase):
def test_private_any_true_preserves_builtin_semantics(self):
for values in (
[],
[False, 0, None, ""],
[False, 0, "truthy"],
[1],
):
with self.subTest(values=values):
self.assertEqual(_any_true(iter(values)), any(values))
visited = []
def values():
for value in (False, True, "must-not-be-read"):
visited.append(value)
yield value
self.assertTrue(_any_true(values()))
self.assertEqual(visited, [False, True])
def test_symbol_round_trip(self): def test_symbol_round_trip(self):
inputs = [ inputs = [
("600000.XSHG", "600000.SH", "SH600000"), ("600000.XSHG", "600000.SH", "SH600000"),
@@ -166,6 +249,255 @@ class PortableCoreTests(unittest.TestCase):
build_rebalance_plan(**arguments), build_rebalance_plan(**arguments),
) )
def test_target_weight_plan_is_causal_and_preserves_lineage(self):
common = {
"prices": {
"600000.SH": 10.0,
"000001.SZ": 10.0,
},
"current": {},
"sellable": {},
"equity": 100_000,
"cash_buffer": 0.02,
"lot_size": 100,
"decision_id": "D-20260724-A",
"package_sha256": "a" * 64,
}
first = build_target_weight_plan(
{
"600000.XSHG": 0.60,
"000001.XSHE": 0.20,
},
**common
)
second = build_target_weight_plan(
{
"600000.XSHG": 0.20,
"000001.XSHE": 0.60,
},
**common
)
self.assertNotEqual(first["targets"], second["targets"])
self.assertNotEqual(first["orders"], second["orders"])
self.assertEqual(first["decision_id"], "D-20260724-A")
self.assertEqual(first["package_sha256"], "a" * 64)
self.assertNotIn("scores", first)
def test_target_weight_plan_rejects_alias_duplicates_and_bad_weights(self):
common = {
"prices": {"600000.XSHG": 10.0},
"current": {},
"sellable": {},
"equity": 100_000,
}
with self.assertRaisesRegex(ValueError, "duplicate normalized"):
build_target_weight_plan(
{
"600000.SH": 0.20,
"600000.XSHG": 0.20,
},
**common
)
for weights in (
{"600000.XSHG": -0.1},
{"600000.XSHG": float("nan")},
{"600000.XSHG": 1.01},
):
with self.subTest(weights=weights):
with self.assertRaises(ValueError):
build_target_weight_plan(weights, **common)
with self.assertRaisesRegex(ValueError, "gross"):
build_target_weight_plan(
{
"600000.XSHG": 0.60,
"000001.XSHE": 0.50,
},
prices={
"600000.XSHG": 10.0,
"000001.XSHE": 10.0,
},
current={},
sellable={},
equity=100_000,
)
def test_target_weight_plan_respects_t1_and_odd_lot_liquidation(self):
t1_limited = build_target_weight_plan(
{},
{"600000.XSHG": 10.0},
{"600000.XSHG": 1050},
{"600000.XSHG": 250},
equity=100_000,
)
self.assertEqual(t1_limited["weights"], {"600000.XSHG": 0.0})
self.assertEqual(t1_limited["targets"], {"600000.XSHG": 0})
self.assertEqual(t1_limited["orders"], {"600000.XSHG": -200})
complete = build_target_weight_plan(
{},
{"600000.XSHG": 10.0},
{"600000.XSHG": 1050},
{"600000.XSHG": 1050},
equity=100_000,
)
self.assertEqual(complete["orders"], {"600000.XSHG": -1050})
unsellable = build_target_weight_plan(
{},
{"600000.XSHG": 10.0},
{"600000.XSHG": 1050},
{},
equity=100_000,
)
self.assertEqual(unsellable["orders"], {})
def test_target_weight_plan_requires_exact_managed_price_coverage(self):
with self.assertRaisesRegex(KeyError, "missing"):
build_target_weight_plan(
{"600000.XSHG": 0.20},
{"600000.XSHG": 10.0},
{"000001.XSHE": 100},
{"000001.XSHE": 100},
equity=100_000,
)
with self.assertRaisesRegex(ValueError, "unmanaged"):
build_target_weight_plan(
{"600000.XSHG": 0.20},
{
"600000.XSHG": 10.0,
"000001.XSHE": 10.0,
},
{},
{},
equity=100_000,
)
def test_portable_target_package_verifier_matches_local_seal(self):
package = sealed_target_package()
self.assertEqual(verify_target_package(package), package)
self.assertEqual(
verify_target_package(
package,
expected_signal_as_of=package["signal_as_of"],
expected_next_session=package["next_session"],
expected_decision_id=package["decision_id"],
expected_package_sha256=package["package_sha256"],
),
package,
)
tampered = copy.deepcopy(package)
tampered["target_weights"]["000001.XSHE"] = 0.29
tampered["constraints"]["actual_gross_weight"] = 0.49
with self.assertRaisesRegex(ValueError, "hash mismatch"):
verify_target_package(tampered)
quantity_injected = copy.deepcopy(package)
quantity_injected["target_quantity"] = 100
with self.assertRaisesRegex(ValueError, "quantities do not belong"):
verify_target_package(quantity_injected)
noncanonical = copy.deepcopy(package)
noncanonical["target_weights"]["000001.SZ"] = noncanonical[
"target_weights"
].pop("000001.XSHE")
with self.assertRaisesRegex(ValueError, "must be canonical"):
verify_target_package(noncanonical)
def test_target_package_tape_is_pinned_and_exact_only(self):
first = sealed_target_package()
second = sealed_target_package(
package_id="TP-20260731-B",
decision_id="D-20260731-B",
signal_as_of="2026-07-31T15:00:00+08:00",
next_session="2026-08-03",
)
packages = [first, second]
tape_hash = target_package_tape_sha256(packages)
self.assertEqual(
tape_hash,
hashlib.sha256(
canonical_json(packages).encode("utf-8")
).hexdigest(),
)
found = lookup_target_package_exact(
packages,
signal_as_of=second["signal_as_of"],
next_session=second["next_session"],
decision_id=second["decision_id"],
expected_tape_sha256=tape_hash,
expected_count=2,
)
self.assertEqual(found, second)
found["release"] = "mutated"
self.assertEqual(packages[1]["release"], second["release"])
with self.assertRaises(TargetPackageLookupMiss):
lookup_target_package_exact(
packages,
signal_as_of="2026-07-30T15:00:00+08:00",
next_session="2026-07-31",
expected_tape_sha256=tape_hash,
expected_count=2,
)
with self.assertRaisesRegex(ValueError, "decision_id mismatch"):
lookup_target_package_exact(
packages,
signal_as_of=first["signal_as_of"],
next_session=first["next_session"],
decision_id="D-WRONG",
)
with self.assertRaisesRegex(ValueError, "count mismatch"):
lookup_target_package_exact(
packages[:1],
signal_as_of=first["signal_as_of"],
next_session=first["next_session"],
expected_count=2,
)
with self.assertRaisesRegex(ValueError, "digest mismatch"):
lookup_target_package_exact(
packages[:1],
signal_as_of=first["signal_as_of"],
next_session=first["next_session"],
expected_tape_sha256=tape_hash,
)
def test_target_package_tape_rejects_duplicates_and_unmatched_tamper(self):
first = sealed_target_package()
duplicate_clock = sealed_target_package(
package_id="TP-20260724-B",
decision_id="D-20260724-B",
)
with self.assertRaisesRegex(ValueError, "duplicate target package clock"):
lookup_target_package_exact(
[first, duplicate_clock],
signal_as_of=first["signal_as_of"],
next_session=first["next_session"],
)
later = sealed_target_package(
package_id="TP-20260731-B",
decision_id="D-20260731-B",
signal_as_of="2026-07-31T15:00:00+08:00",
next_session="2026-08-03",
)
later["release"] = "tampered"
with self.assertRaisesRegex(ValueError, "hash mismatch"):
lookup_target_package_exact(
[first, later],
signal_as_of=first["signal_as_of"],
next_session=first["next_session"],
)
def test_portable_core_parses_as_python_36(self):
source = (
Path(__file__).parents[1]
/ "src"
/ "quant60"
/ "portable_core.py"
).read_text(encoding="utf-8")
ast.parse(source, feature_version=(3, 6))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
+195
View File
@@ -1,4 +1,5 @@
import importlib.util import importlib.util
import hashlib
import json import json
import os import os
import re import re
@@ -112,10 +113,191 @@ class PublicStatusTests(unittest.TestCase):
platforms = {platform["name"]: platform for platform in payload["platforms"]} platforms = {platform["name"]: platform for platform in payload["platforms"]}
self.assertEqual(platforms["JoinQuant hosted bundle"]["status"], "real_runtime") self.assertEqual(platforms["JoinQuant hosted bundle"]["status"], "real_runtime")
self.assertIn(
"3",
platforms["JoinQuant hosted bundle"]["claim"],
)
self.assertIn(
"execution",
platforms["JoinQuant hosted bundle"]["limitations"][0],
)
self.assertEqual(platforms["QMT built-in bundle"]["status"], "implemented") self.assertEqual(platforms["QMT built-in bundle"]["status"], "implemented")
self.assertFalse(platforms["XtTrader read-only shadow"]["liveReady"]) self.assertFalse(platforms["XtTrader read-only shadow"]["liveReady"])
self.assertIn("fake", platforms["XtTrader read-only shadow"]["claim"].lower()) self.assertIn("fake", platforms["XtTrader read-only shadow"]["claim"].lower())
def test_candidate_records_only_real_joinquant_execution_observation(self):
payload = _read_payload()
candidate = payload["candidateBoundary"]
self.assertEqual(candidate["decisionMode"], "target_package")
self.assertEqual(candidate["localEvidenceClass"], "synthetic_research")
self.assertTrue(candidate["realJoinQuantObserved"])
self.assertFalse(candidate["realQmtObserved"])
self.assertEqual(
candidate["realPlatformObservedLayers"],
["execution"],
)
self.assertFalse(candidate["allFiveLayersRealPlatformObserved"])
run = candidate["joinQuantTargetPackageRun"]
self.assertEqual(
run["runId"],
"2457a7c39a276e09e0fabf99e28978e1",
)
self.assertEqual(run["evidenceClass"], "synthetic-research")
self.assertEqual(run["observedLayers"], ["execution"])
self.assertEqual((run["orders"], run["fills"]), (3, 3))
self.assertFalse(run["allFiveLayersObserved"])
self.assertFalse(run["qmtPeerObserved"])
self.assertFalse(run["g9Passed"])
self.assertFalse(run["baseline60Passed"])
self.assertFalse(run["investmentValueClaim"])
self.assertFalse(candidate["joinQuantTargetPackageCountsAsBaseline"])
self.assertFalse(candidate["joinQuantTargetPackageCountsAsG9"])
self.assertEqual(
candidate["historicalJoinQuantMode"], "portable_momentum_smoke"
)
self.assertFalse(candidate["historicalJoinQuantCountsAsBaseline"])
self.assertTrue(payload["summary"]["candidateRealPlatformObserved"])
self.assertEqual(
payload["summary"]["candidateRealPlatformObservationScope"],
"target_package_execution_consumer_only",
)
self.assertFalse(
payload["summary"]["allFiveLayersRealPlatformObserved"]
)
reachability = candidate["reachability"]
self.assertEqual(
reachability["releaseId"], "quant60-research-candidate-v1"
)
self.assertTrue(
reachability["summary"]["all_layers_implemented"]
)
self.assertTrue(
reachability["summary"]["all_layers_local_entrypoint_reachable"]
)
self.assertFalse(
reachability["summary"]["all_layers_real_platform_observed"]
)
self.assertEqual(
reachability["realPlatformsObserved"],
["joinquant"],
)
self.assertEqual(
reachability["realPlatformObservedLayers"],
["execution"],
)
evidence = {item["id"]: item for item in payload["evidence"]}
self.assertEqual(
evidence["release-reachability"]["path"],
"releases/quant60-research-candidate-v1/reachability.json",
)
self.assertEqual(
evidence["joinquant-target-package-run"]["path"],
"evidence/joinquant/TP-20240311-795dcfba/"
"2457a7c39a276e09e0fabf99e28978e1/run.json",
)
self.assertEqual(
evidence["joinquant-target-package-evidence"]["path"],
"docs/JOINQUANT_TARGET_PACKAGE_EVIDENCE_2026-07-26.md",
)
gaps = payload["deliveryGaps"]
self.assertGreaterEqual(len(gaps["internal"]), 3)
self.assertGreaterEqual(len(gaps["externalEvidence"]), 5)
internal = " ".join(gaps["internal"])
self.assertIn("provider-data", internal)
self.assertIn("factor_risk.py", internal)
self.assertIn("CVXPY", internal)
self.assertIn("TWAP/POV", internal)
self.assertIn("synthetic-research", internal)
self.assertIn("TargetPackageV1", internal)
self.assertIn("real JoinQuant", internal)
self.assertIn("real QMT", internal)
def test_public_builder_rejects_wrong_reachability_evidence_class(self):
module = _load_builder()
path = (
PROJECT
/ "releases"
/ "quant60-research-candidate-v1"
/ "reachability.json"
)
manifest = json.loads(path.read_text(encoding="utf-8"))
artifact = manifest["layers"]["alpha"]["jq_entrypoint_reachable"][
"artifacts"
][0]
artifact["kind"] = "source"
body = {
key: value
for key, value in manifest.items()
if key != "manifest_sha256"
}
manifest["manifest_sha256"] = hashlib.sha256(
json.dumps(
body,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
).hexdigest()
with self.assertRaises(module.PublicStatusError):
module._validate_reachability_manifest(PROJECT, manifest)
def test_public_builder_rejects_upstream_real_platform_overclaim(self):
module = _load_builder()
path = (
PROJECT
/ "releases"
/ "quant60-research-candidate-v1"
/ "reachability.json"
)
manifest = json.loads(path.read_text(encoding="utf-8"))
artifact = dict(
manifest["layers"]["execution"]["real_platform_observed"][
"artifacts"
][0]
)
artifact["artifact_id"] = "alpha-real-platform-overclaim"
manifest["layers"]["alpha"]["real_platform_observed"] = {
"status": True,
"artifacts": [artifact],
}
body = {
key: value
for key, value in manifest.items()
if key != "manifest_sha256"
}
manifest["manifest_sha256"] = hashlib.sha256(
json.dumps(
body,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
).hexdigest()
with self.assertRaisesRegex(
module.PublicStatusError,
"only JoinQuant execution",
):
module._validate_reachability_manifest(PROJECT, manifest)
def test_public_builder_rejects_target_package_performance_claim(self):
module = _load_builder()
path = (
PROJECT
/ "evidence"
/ "joinquant"
/ "TP-20240311-795dcfba"
/ "2457a7c39a276e09e0fabf99e28978e1"
/ "run.json"
)
run = json.loads(path.read_text(encoding="utf-8"))
run["performance_claim"] = True
with self.assertRaisesRegex(
module.PublicStatusError,
"evidence classes are unsafe",
):
module._validate_joinquant_target_package_run(PROJECT, run)
def test_freshness_uses_daily_bars_not_the_later_trade_calendar(self): def test_freshness_uses_daily_bars_not_the_later_trade_calendar(self):
payload = _read_payload() payload = _read_payload()
freshness = {item["id"]: item for item in payload["freshness"]} freshness = {item["id"]: item for item in payload["freshness"]}
@@ -124,6 +306,19 @@ class PublicStatusTests(unittest.TestCase):
freshness["joinquant-hosted-market-window"]["marketDataThrough"], freshness["joinquant-hosted-market-window"]["marketDataThrough"],
"2024-12-31", "2024-12-31",
) )
target_package = freshness[
"joinquant-target-package-execution-smoke"
]
self.assertEqual(target_package["marketDataFrom"], "2024-03-11")
self.assertEqual(target_package["marketDataThrough"], "2024-03-13")
self.assertEqual(
target_package["observedAt"],
"2026-07-26T08:50:15Z",
)
self.assertFalse(target_package["productionReady"])
self.assertFalse(target_package["investmentValueClaim"])
self.assertIn("execution", target_package["semantics"])
self.assertIn("G9", target_package["semantics"])
tushare = freshness["tushare-daily-market-data"] tushare = freshness["tushare-daily-market-data"]
self.assertEqual(tushare["completedPartitions"], 35) self.assertEqual(tushare["completedPartitions"], 35)
+405
View File
@@ -0,0 +1,405 @@
import copy
import hashlib
import json
import tempfile
import unittest
from pathlib import Path
from quant60.ledger import canonical_json
from quant60.release_reachability import (
CAPABILITIES,
LAYERS,
ReleaseReachabilityError,
build_evidence_artifact,
build_release_reachability,
load_verified_release_reachability,
release_reachability_sha256,
seal_release_reachability,
validate_release_reachability,
write_release_reachability,
)
GENERATED_AT = "2026-07-26T12:00:00Z"
class ReleaseReachabilityTests(unittest.TestCase):
def setUp(self):
temporary = tempfile.TemporaryDirectory()
self.addCleanup(temporary.cleanup)
self.root = Path(temporary.name)
for directory in ("src", "tests", "tools", "platforms", "evidence"):
(self.root / directory).mkdir()
for layer in LAYERS:
(self.root / "src" / f"{layer}.py").write_text(
f"LAYER = {layer!r}\n",
encoding="utf-8",
)
(self.root / "tests" / f"test_{layer}.py").write_text(
f"def test_{layer}():\n assert True\n",
encoding="utf-8",
)
(self.root / "tools" / f"{layer}_entry.py").write_text(
f"from src import {layer}\n",
encoding="utf-8",
)
(self.root / "platforms" / "joinquant_entry.py").write_text(
"def execute():\n return 'submitted'\n",
encoding="utf-8",
)
(self.root / "platforms" / "qmt_entry.py").write_text(
"def execute():\n return 'submitted'\n",
encoding="utf-8",
)
(self.root / "evidence" / "jq-execution.json").write_text(
canonical_json(
{
"platform": "joinquant",
"layer": "execution",
"release_id": "quant60-baseline-v1",
"result": "passed",
}
)
+ "\n",
encoding="utf-8",
)
@staticmethod
def _false_claim():
return {"status": False, "artifacts": []}
def _artifact(
self,
*,
artifact_id,
kind,
path,
platform,
observed_at=None,
run_id=None,
):
return build_evidence_artifact(
artifact_id=artifact_id,
kind=kind,
path=path,
platform=platform,
repository_root=self.root,
observed_at=observed_at,
run_id=run_id,
)
def _layers(self):
layers = {}
for layer in LAYERS:
layers[layer] = {
"implemented": {
"status": True,
"artifacts": [
self._artifact(
artifact_id=f"{layer}-source",
kind="source",
path=f"src/{layer}.py",
platform="repository",
)
],
},
"unit_tested": {
"status": True,
"artifacts": [
self._artifact(
artifact_id=f"{layer}-test",
kind="test",
path=f"tests/test_{layer}.py",
platform="repository",
)
],
},
"local_entrypoint_reachable": {
"status": True,
"artifacts": [
self._artifact(
artifact_id=f"{layer}-local-entry",
kind="entrypoint",
path=f"tools/{layer}_entry.py",
platform="local",
)
],
},
"jq_entrypoint_reachable": self._false_claim(),
"qmt_entrypoint_reachable": self._false_claim(),
"real_platform_observed": self._false_claim(),
}
layers["execution"]["jq_entrypoint_reachable"] = {
"status": True,
"artifacts": [
self._artifact(
artifact_id="execution-jq-entry",
kind="entrypoint",
path="platforms/joinquant_entry.py",
platform="joinquant",
)
],
}
layers["execution"]["real_platform_observed"] = {
"status": True,
"artifacts": [
self._artifact(
artifact_id="execution-jq-run",
kind="platform_run",
path="evidence/jq-execution.json",
platform="joinquant",
observed_at="2026-07-26T11:30:00Z",
run_id="jq-backtest-20260726-01",
)
],
}
return layers
def _manifest(self):
return build_release_reachability(
release_id="quant60-baseline-v1",
generated_at=GENERATED_AT,
layers=self._layers(),
repository_root=self.root,
)
def test_build_write_load_preserves_exact_six_claims_per_layer(self):
manifest = self._manifest()
self.assertEqual(
manifest["manifest_sha256"],
release_reachability_sha256(manifest),
)
for layer in LAYERS:
self.assertEqual(
set(manifest["layers"][layer]),
set(CAPABILITIES),
)
self.assertTrue(manifest["summary"]["all_layers_implemented"])
self.assertTrue(manifest["summary"]["all_layers_unit_tested"])
self.assertTrue(
manifest["summary"]["all_layers_local_entrypoint_reachable"]
)
self.assertFalse(
manifest["summary"]["all_layers_jq_entrypoint_reachable"]
)
self.assertFalse(
manifest["summary"]["all_layers_qmt_entrypoint_reachable"]
)
self.assertFalse(
manifest["summary"]["all_layers_real_platform_observed"]
)
path = write_release_reachability(
manifest,
self.root / "reachability.json",
repository_root=self.root,
)
loaded = load_verified_release_reachability(
path,
repository_root=self.root,
expected_release_id="quant60-baseline-v1",
expected_manifest_sha256=manifest["manifest_sha256"],
)
self.assertEqual(loaded, manifest)
self.assertEqual(
path.read_bytes(),
(canonical_json(manifest) + "\n").encode("utf-8"),
)
def test_manifest_and_artifact_tampering_both_fail_closed(self):
manifest = self._manifest()
status_tamper = copy.deepcopy(manifest)
status_tamper["layers"]["alpha"]["implemented"]["status"] = False
with self.assertRaisesRegex(
ReleaseReachabilityError,
"SHA-256 mismatch",
):
validate_release_reachability(
status_tamper,
repository_root=self.root,
)
path = write_release_reachability(
manifest,
self.root / "reachability.json",
repository_root=self.root,
)
(self.root / "src" / "alpha.py").write_text(
"LAYER = 'tampered'\n",
encoding="utf-8",
)
with self.assertRaisesRegex(
ReleaseReachabilityError,
"artifact hash mismatch",
):
load_verified_release_reachability(
path,
repository_root=self.root,
)
def test_source_hash_cannot_masquerade_as_real_platform_observation(self):
layers = self._layers()
source = self.root / "src" / "execution.py"
layers["execution"]["real_platform_observed"] = {
"status": True,
"artifacts": [
{
"artifact_id": "fake-platform-run",
"kind": "platform_run",
"path": "src/execution.py",
"sha256": hashlib.sha256(source.read_bytes()).hexdigest(),
"platform": "joinquant",
"observed_at": "2026-07-26T11:30:00Z",
"run_id": "fake-run",
}
],
}
with self.assertRaisesRegex(
ReleaseReachabilityError,
"real-platform evidence must live under",
):
build_release_reachability(
release_id="quant60-baseline-v1",
generated_at=GENERATED_AT,
layers=layers,
repository_root=self.root,
)
def test_true_status_requires_typed_evidence_and_false_forbids_it(self):
true_without_evidence = self._layers()
true_without_evidence["risk"]["unit_tested"] = {
"status": True,
"artifacts": [],
}
with self.assertRaisesRegex(
ReleaseReachabilityError,
"iff at least one evidence",
):
build_release_reachability(
release_id="quant60-baseline-v1",
generated_at=GENERATED_AT,
layers=true_without_evidence,
repository_root=self.root,
)
wrong_evidence_type = self._layers()
wrong_evidence_type["risk"]["unit_tested"]["artifacts"][0].update(
{"kind": "source", "platform": "repository"}
)
with self.assertRaisesRegex(
ReleaseReachabilityError,
"requires kind='test'",
):
build_release_reachability(
release_id="quant60-baseline-v1",
generated_at=GENERATED_AT,
layers=wrong_evidence_type,
repository_root=self.root,
)
def test_real_observation_requires_corresponding_platform_entrypoint(self):
layers = self._layers()
layers["execution"]["jq_entrypoint_reachable"] = self._false_claim()
with self.assertRaisesRegex(
ReleaseReachabilityError,
"requires jq_entrypoint_reachable",
):
build_release_reachability(
release_id="quant60-baseline-v1",
generated_at=GENERATED_AT,
layers=layers,
repository_root=self.root,
)
def test_path_hash_id_and_clock_validation_are_strict(self):
cases = []
absolute = self._layers()
absolute["alpha"]["implemented"]["artifacts"][0]["path"] = str(
self.root / "src" / "alpha.py"
)
cases.append((absolute, "repository-relative path"))
traversal = self._layers()
traversal["alpha"]["implemented"]["artifacts"][0]["path"] = (
"src/../tests/test_alpha.py"
)
cases.append((traversal, "normalized repository-relative path"))
placeholder = self._layers()
placeholder["alpha"]["implemented"]["artifacts"][0]["sha256"] = (
"0" * 64
)
cases.append((placeholder, "all-zero placeholder"))
duplicate = self._layers()
duplicate["alpha"]["implemented"]["artifacts"][0]["artifact_id"] = (
duplicate["universe"]["implemented"]["artifacts"][0]["artifact_id"]
)
cases.append((duplicate, "reused across claims"))
future_run = self._layers()
future_run["execution"]["real_platform_observed"]["artifacts"][0][
"observed_at"
] = "2026-07-26T12:00:01Z"
cases.append((future_run, "cannot be after generated_at"))
for layers, message in cases:
with self.subTest(message=message):
with self.assertRaisesRegex(
ReleaseReachabilityError,
message,
):
build_release_reachability(
release_id="quant60-baseline-v1",
generated_at=GENERATED_AT,
layers=layers,
repository_root=self.root,
)
def test_strict_loader_rejects_duplicate_json_keys(self):
path = self.root / "duplicate.json"
path.write_text(
'{"schema_version":"1.0","schema_version":"1.0"}',
encoding="utf-8",
)
with self.assertRaisesRegex(
ReleaseReachabilityError,
"duplicate JSON key",
):
load_verified_release_reachability(
path,
repository_root=self.root,
)
def test_resealed_derived_summary_tamper_is_rejected(self):
manifest = self._manifest()
manifest["summary"]["all_layers_qmt_entrypoint_reachable"] = True
manifest["manifest_sha256"] = release_reachability_sha256(manifest)
with self.assertRaisesRegex(
ReleaseReachabilityError,
"summary is not the exact derived",
):
validate_release_reachability(
manifest,
repository_root=self.root,
)
def test_seal_rejects_prepopulated_derived_fields(self):
body = {
"schema_version": "1.0",
"artifact_type": "quant60-release-reachability",
"release_id": "quant60-baseline-v1",
"generated_at": GENERATED_AT,
"layers": self._layers(),
"summary": {},
}
with self.assertRaisesRegex(
ReleaseReachabilityError,
"derived summary",
):
seal_release_reachability(body, repository_root=self.root)
if __name__ == "__main__":
unittest.main()
+314
View File
@@ -0,0 +1,314 @@
import copy
import json
import tempfile
import unittest
from pathlib import Path
from quant60.target_package import (
DuplicateTargetDecisionError,
SHA256_FIELDS,
TargetPackageError,
TargetPackageNotFoundError,
TargetPackageTape,
TargetPackageV1,
load_target_package_tape,
seal_target_package,
target_package_sha256,
validate_target_package,
)
def package_body(
*,
package_id: str = "TP-20260724-A",
decision_id: str = "D-20260724-A",
signal_as_of: str = "2026-07-24T15:00:00+08:00",
next_session: str = "2026-07-27",
release: str = "quant60-baseline-60-v1",
model_id: str = "alpha158-lightgbm-v1",
) -> dict:
return {
"schema_version": "1.0",
"package_type": "TargetPackageV1",
"package_id": package_id,
"decision_id": decision_id,
"release": release,
"evidence_class": "provider-snapshot-local",
"model_id": model_id,
"signal_as_of": signal_as_of,
"next_session": next_session,
"model_sha256": "1" * 64,
"data_sha256": "2" * 64,
"feature_sha256": "3" * 64,
"risk_sha256": "4" * 64,
"cost_sha256": "5" * 64,
"optimizer_sha256": "6" * 64,
"config_sha256": "7" * 64,
"source_sha256": "8" * 64,
"target_weights": {
"000001.XSHE": 0.30,
"600000.XSHG": 0.20,
},
"universe": {
"000001.XSHE": {
"state": "openable",
"reasons": ["ELIGIBLE"],
},
"600000.XSHG": {
"state": "hold_only",
"reasons": ["LIQUIDITY_BELOW_FLOOR"],
},
},
"constraints": {
"constraint_state": "FEASIBLE",
"long_only": True,
"max_single_weight": 0.40,
"max_gross_weight": 0.80,
"max_one_way_turnover": 0.20,
"actual_gross_weight": 0.50,
},
}
def sealed_package(**overrides) -> dict:
return seal_target_package(package_body(**overrides))
class TargetPackageTests(unittest.TestCase):
def test_valid_package_is_deterministic_and_quantity_free(self):
package = sealed_package()
self.assertEqual(
package["package_sha256"],
target_package_sha256(package),
)
self.assertEqual(len(package["package_sha256"]), 64)
self.assertNotIn("quantity", json.dumps(package))
self.assertEqual(validate_target_package(package), package)
wrapped = TargetPackageV1.from_dict(package)
self.assertEqual(wrapped.package_id, package["package_id"])
self.assertEqual(wrapped.as_dict(), package)
def test_tamper_is_rejected(self):
package = sealed_package()
package["target_weights"]["000001.XSHE"] = 0.25
package["constraints"]["actual_gross_weight"] = 0.45
with self.assertRaisesRegex(TargetPackageError, "hash mismatch"):
validate_target_package(package)
def test_every_layer_hash_is_required_and_strict(self):
package = sealed_package()
for field in SHA256_FIELDS:
with self.subTest(field=field):
missing = copy.deepcopy(package)
missing.pop(field)
with self.assertRaises(TargetPackageError):
validate_target_package(missing)
malformed = copy.deepcopy(package)
malformed[field] = "A" * 64
with self.assertRaises(TargetPackageError):
validate_target_package(malformed)
placeholder = copy.deepcopy(package)
placeholder[field] = "0" * 64
placeholder["package_sha256"] = target_package_sha256(
placeholder
)
with self.assertRaisesRegex(
TargetPackageError,
"all-zero placeholder",
):
validate_target_package(placeholder)
def test_portable_momentum_cannot_masquerade_as_baseline(self):
body = package_body(model_id="portable-momentum-v1")
with self.assertRaisesRegex(TargetPackageError, "cannot claim"):
seal_target_package(body)
smoke = package_body(
release="platform-smoke-v1",
model_id="portable-momentum-v1",
)
self.assertEqual(
seal_target_package(smoke)["model_id"],
"portable-momentum-v1",
)
def test_quantities_are_forbidden_at_any_depth(self):
for mutation in (
lambda body: body.update({"target_quantity": 100}),
lambda body: body["constraints"].update({"quantity": 100}),
lambda body: body["universe"]["000001.XSHE"].update(
{"sellable_quantity": 100}
),
):
with self.subTest(mutation=mutation):
body = package_body()
mutation(body)
with self.assertRaisesRegex(
TargetPackageError,
"do not belong",
):
seal_target_package(body)
def test_clock_must_be_canonical_close_and_future_weekday(self):
cases = (
("2026-07-24T14:59:59+08:00", "2026-07-27"),
("2026-07-24T07:00:00+00:00", "2026-07-27"),
("2026-07-24T15:00:00.000000+08:00", "2026-07-27"),
("2026-07-24T15:00:00+08:00", "2026-07-24"),
("2026-07-24T15:00:00+08:00", "2026-07-25"),
)
for signal_as_of, next_session in cases:
with self.subTest(
signal_as_of=signal_as_of,
next_session=next_session,
):
with self.assertRaises(TargetPackageError):
seal_target_package(
package_body(
signal_as_of=signal_as_of,
next_session=next_session,
)
)
def test_symbols_weights_and_gross_fail_closed(self):
noncanonical = package_body()
noncanonical["target_weights"] = {
"000001.SZ": 0.30,
"600000.XSHG": 0.20,
}
with self.assertRaisesRegex(TargetPackageError, "canonical"):
seal_target_package(noncanonical)
missing_universe = package_body()
missing_universe["universe"].pop("000001.XSHE")
with self.assertRaisesRegex(TargetPackageError, "absent from universe"):
seal_target_package(missing_universe)
bad_gross = package_body()
bad_gross["constraints"]["actual_gross_weight"] = 0.49
with self.assertRaisesRegex(TargetPackageError, "does not equal"):
seal_target_package(bad_gross)
gross_breach = package_body()
gross_breach["constraints"]["max_gross_weight"] = 0.49
with self.assertRaisesRegex(TargetPackageError, "max_gross"):
seal_target_package(gross_breach)
single_breach = package_body()
single_breach["constraints"]["max_single_weight"] = 0.25
with self.assertRaisesRegex(TargetPackageError, "max_single"):
seal_target_package(single_breach)
excluded = package_body()
excluded["universe"]["000001.XSHE"] = {
"state": "excluded",
"reasons": ["NOT_PIT_INDEX_MEMBER"],
}
with self.assertRaisesRegex(TargetPackageError, "excluded"):
seal_target_package(excluded)
def test_tape_supports_multiple_decisions_with_exact_lookup(self):
first = sealed_package()
second = sealed_package(
package_id="TP-20260731-B",
decision_id="D-20260731-B",
signal_as_of="2026-07-31T15:00:00+08:00",
next_session="2026-08-03",
)
tape = TargetPackageTape([second, first])
self.assertEqual(len(tape), 2)
found = tape.lookup_exact(
signal_as_of=first["signal_as_of"],
next_session=first["next_session"],
)
self.assertEqual(found["decision_id"], first["decision_id"])
found["release"] = "mutated"
self.assertEqual(
tape.lookup_exact(
signal_as_of=first["signal_as_of"],
next_session=first["next_session"],
)["release"],
first["release"],
)
with self.assertRaises(TargetPackageNotFoundError):
tape.lookup_exact(
signal_as_of="2026-07-30T15:00:00+08:00",
next_session="2026-07-31",
)
with self.assertRaises(TargetPackageNotFoundError):
tape.lookup_exact(
signal_as_of=first["signal_as_of"],
next_session=first["next_session"],
decision_id="D-wrong",
)
def test_duplicate_decision_identity_is_rejected(self):
first = sealed_package()
duplicate_id = sealed_package(
package_id="TP-20260731-B",
decision_id=first["decision_id"],
signal_as_of="2026-07-31T15:00:00+08:00",
next_session="2026-08-03",
)
with self.assertRaises(DuplicateTargetDecisionError):
TargetPackageTape([first, duplicate_id])
duplicate_clock = sealed_package(
package_id="TP-20260724-B",
decision_id="D-20260724-B",
)
with self.assertRaises(DuplicateTargetDecisionError):
TargetPackageTape([first, duplicate_clock])
def test_json_and_jsonl_tapes_load_multiple_decisions(self):
packages = [
sealed_package(),
sealed_package(
package_id="TP-20260731-B",
decision_id="D-20260731-B",
signal_as_of="2026-07-31T15:00:00+08:00",
next_session="2026-08-03",
),
]
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
json_path = root / "tape.json"
json_path.write_text(
json.dumps({"packages": packages}),
encoding="utf-8",
)
jsonl_path = root / "tape.jsonl"
jsonl_path.write_text(
"".join(json.dumps(item) + "\n" for item in packages),
encoding="utf-8",
)
self.assertEqual(len(load_target_package_tape(json_path)), 2)
self.assertEqual(len(load_target_package_tape(jsonl_path)), 2)
def test_single_package_loads_and_strict_json_rejects_ambiguity(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
single = root / "single.json"
single.write_text(
json.dumps(sealed_package()),
encoding="utf-8",
)
self.assertEqual(len(load_target_package_tape(single)), 1)
duplicate = root / "duplicate.json"
duplicate.write_text(
'{"package_type":"TargetPackageV1",'
'"package_type":"TargetPackageV1"}',
encoding="utf-8",
)
with self.assertRaisesRegex(TargetPackageError, "duplicate JSON key"):
load_target_package_tape(duplicate)
non_finite = root / "nan.json"
non_finite.write_text("[NaN]", encoding="utf-8")
with self.assertRaisesRegex(TargetPackageError, "non-finite"):
load_target_package_tape(non_finite)
if __name__ == "__main__":
unittest.main()
+506 -46
View File
@@ -18,10 +18,27 @@ PROJECT = Path(__file__).resolve().parents[1]
DEFAULT_OUTPUT = PROJECT / "status" / "status-data.js" DEFAULT_OUTPUT = PROJECT / "status" / "status-data.js"
GLOBAL_NAME = "QUANT_OS_PUBLIC_STATUS" GLOBAL_NAME = "QUANT_OS_PUBLIC_STATUS"
SCHEMA_VERSION = 1 SCHEMA_VERSION = 1
REACHABILITY_PATH = (
"releases/quant60-research-candidate-v1/reachability.json"
)
JOINQUANT_TARGET_PACKAGE_RUN_PATH = (
"evidence/joinquant/TP-20240311-795dcfba/"
"2457a7c39a276e09e0fabf99e28978e1/run.json"
)
JOINQUANT_TARGET_PACKAGE_DOC_PATH = (
"docs/JOINQUANT_TARGET_PACKAGE_EVIDENCE_2026-07-26.md"
)
JOINQUANT_TARGET_PACKAGE_RUN_ID = "2457a7c39a276e09e0fabf99e28978e1"
JOINQUANT_TARGET_PACKAGE_OBSERVED_AT = "2026-07-26T08:50:15Z"
JOINQUANT_TARGET_PACKAGE_BUNDLE_SHA256 = (
"0586930ddc483f4489f09b562f4d4232b458b703e6a1194af809b707211be8c4"
)
SOURCE_PATHS: Tuple[str, ...] = ( SOURCE_PATHS: Tuple[str, ...] = (
"gate_scorecard.json", "gate_scorecard.json",
"dist/bundle_manifest.json", "dist/bundle_manifest.json",
REACHABILITY_PATH,
JOINQUANT_TARGET_PACKAGE_RUN_PATH,
"configs/baseline.json", "configs/baseline.json",
"README.md", "README.md",
"docs/ARCHITECTURE.md", "docs/ARCHITECTURE.md",
@@ -29,8 +46,10 @@ SOURCE_PATHS: Tuple[str, ...] = (
"docs/PLATFORM_MATRIX.md", "docs/PLATFORM_MATRIX.md",
"docs/CROSS_ENGINE_CONSISTENCY.md", "docs/CROSS_ENGINE_CONSISTENCY.md",
"docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md", "docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md",
JOINQUANT_TARGET_PACKAGE_DOC_PATH,
"docs/TUSHARE_LOCAL_DATA.md", "docs/TUSHARE_LOCAL_DATA.md",
"runbooks/PLATFORM_DEPLOYMENT.md", "runbooks/PLATFORM_DEPLOYMENT.md",
"tools/build_release_reachability.py",
"tools/build_public_status.py", "tools/build_public_status.py",
) )
@@ -245,6 +264,287 @@ def _validate_bundle_manifest(
return public_artifacts return public_artifacts
def _validate_reachability_manifest(
project: Path, manifest: Mapping[str, Any]
) -> Dict[str, Any]:
source_root = project / "src"
inserted = str(source_root) not in sys.path
if inserted:
sys.path.insert(0, str(source_root))
try:
from quant60.release_reachability import ( # pylint: disable=import-outside-toplevel
ReleaseReachabilityError,
validate_release_reachability,
)
try:
manifest = validate_release_reachability(
manifest,
repository_root=project,
expected_release_id="quant60-research-candidate-v1",
)
except ReleaseReachabilityError as exc:
raise PublicStatusError(
f"release reachability manifest failed canonical verification: {exc}"
) from exc
finally:
if inserted:
sys.path.remove(str(source_root))
expected_layers = {"universe", "alpha", "portfolio", "risk", "execution"}
expected_capabilities = {
"implemented",
"unit_tested",
"local_entrypoint_reachable",
"jq_entrypoint_reachable",
"qmt_entrypoint_reachable",
"real_platform_observed",
}
declared_sha = str(manifest.get("manifest_sha256", ""))
body = {
key: value
for key, value in manifest.items()
if key != "manifest_sha256"
}
body_bytes = json.dumps(
body,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
actual_sha = _sha256_bytes(body_bytes)
if not re.fullmatch(r"[0-9a-f]{64}", declared_sha) or declared_sha != actual_sha:
raise PublicStatusError("release reachability manifest self-hash is invalid")
layers = manifest.get("layers")
if not isinstance(layers, dict) or set(layers) != expected_layers:
raise PublicStatusError(
"release reachability manifest must contain the canonical five layers"
)
calculated_summary: Dict[str, bool] = {}
real_platforms: set[str] = set()
real_platform_observed_layers: set[str] = set()
for capability in sorted(expected_capabilities):
statuses: List[bool] = []
for layer in sorted(expected_layers):
claims = layers[layer]
if not isinstance(claims, dict) or set(claims) != expected_capabilities:
raise PublicStatusError(
f"release reachability claims are incomplete for {layer}"
)
claim = claims[capability]
if not isinstance(claim, dict) or not isinstance(
claim.get("status"), bool
):
raise PublicStatusError(
f"release reachability status is invalid for {layer}.{capability}"
)
artifacts = claim.get("artifacts")
if not isinstance(artifacts, list):
raise PublicStatusError(
f"release reachability artifacts are invalid for {layer}.{capability}"
)
status = claim["status"]
if status != bool(artifacts):
raise PublicStatusError(
f"release reachability evidence/status mismatch for "
f"{layer}.{capability}"
)
for index, artifact in enumerate(artifacts):
if not isinstance(artifact, dict):
raise PublicStatusError(
f"release reachability artifact is invalid for "
f"{layer}.{capability}[{index}]"
)
path = _safe_repo_path(
artifact.get("path"),
label=f"{layer}.{capability}[{index}].path",
)
_verify_file_hash(project, path, str(artifact.get("sha256", "")))
if capability == "real_platform_observed":
real_platforms.add(str(artifact.get("platform", "")))
real_platform_observed_layers.add(layer)
statuses.append(status)
calculated_summary[f"all_layers_{capability}"] = all(statuses)
summary = manifest.get("summary")
if not isinstance(summary, dict) or summary != calculated_summary:
raise PublicStatusError(
"release reachability summary does not match per-layer claims"
)
if (
real_platforms != {"joinquant"}
or real_platform_observed_layers != {"execution"}
or calculated_summary["all_layers_real_platform_observed"]
):
raise PublicStatusError(
"current release may claim only JoinQuant execution observation"
)
release_id = str(manifest.get("release_id", ""))
if release_id != "quant60-research-candidate-v1":
raise PublicStatusError("unexpected release reachability release_id")
return {
"releaseId": release_id,
"manifest": REACHABILITY_PATH,
"manifestSha256": declared_sha,
"summary": calculated_summary,
"realPlatformsObserved": sorted(
platform for platform in real_platforms if platform
),
"realPlatformObservedLayers": sorted(real_platform_observed_layers),
}
def _validate_joinquant_target_package_run(
project: Path,
run: Mapping[str, Any],
) -> Dict[str, Any]:
"""Validate the narrow, redacted hosted execution observation."""
if run.get("record_type") != "joinquant_target_package_execution_smoke":
raise PublicStatusError("unexpected JoinQuant TargetPackage run type")
if run.get("platform") != "joinquant" or run.get("status") != "completed":
raise PublicStatusError("JoinQuant TargetPackage run is not completed")
if (
run.get("platform_evidence_class") != "real_platform_runtime"
or run.get("input_evidence_class") != "synthetic-research"
or run.get("performance_claim") is not False
):
raise PublicStatusError(
"JoinQuant TargetPackage evidence classes are unsafe"
)
if run.get("run_id") != JOINQUANT_TARGET_PACKAGE_RUN_ID:
raise PublicStatusError("unexpected JoinQuant TargetPackage run identity")
if run.get("observed_at") != JOINQUANT_TARGET_PACKAGE_OBSERVED_AT:
raise PublicStatusError("unexpected JoinQuant TargetPackage observation time")
backtest = run.get("backtest")
if not isinstance(backtest, dict):
raise PublicStatusError("JoinQuant TargetPackage backtest must be an object")
expected_url = (
"https://www.joinquant.com/algorithm/backtest/detail?backtestId="
+ JOINQUANT_TARGET_PACKAGE_RUN_ID
)
expected_backtest = {
"id": JOINQUANT_TARGET_PACKAGE_RUN_ID,
"url": expected_url,
"start_date": "2024-03-11",
"end_date": "2024-03-13",
"initial_capital_cny": 1_000_000,
"frequency": "day",
"python": "Python3",
"benchmark": "000905.XSHG",
"timezone": "Asia/Shanghai",
}
if backtest != expected_backtest:
raise PublicStatusError("JoinQuant TargetPackage settings drifted")
identity = run.get("artifact_identity")
if not isinstance(identity, dict):
raise PublicStatusError("JoinQuant TargetPackage identity must be an object")
expected_identity = {
"bundle_sha256": JOINQUANT_TARGET_PACKAGE_BUNDLE_SHA256,
"browser_snapshot_sha256": JOINQUANT_TARGET_PACKAGE_BUNDLE_SHA256,
"exact_browser_match": True,
"bundle_manifest_sha256": (
"91f4b4dc0bdd0d9f99b3466c830600e2678be78dd991d4499a4154bffec8597a"
),
"portable_core_sha256": (
"f9de9f18e4008bb0fc80edb0b21cae6ea06a61828d89e9b7c11057f98225fc19"
),
"wrapper_sha256": (
"0c672b64665b7c1d4409ba288b00d1608e976800af3a4f9a6467767ce4def592"
),
}
if identity != expected_identity:
raise PublicStatusError("JoinQuant TargetPackage artifact identity drifted")
target_package = run.get("target_package")
if not isinstance(target_package, dict):
raise PublicStatusError("JoinQuant TargetPackage identity is missing")
if (
target_package.get("package_id") != "TP-20240311-795dcfba"
or target_package.get("evidence_class") != "synthetic-research"
or target_package.get("package_sha256")
!= "baa15c8156aa6cb54d3775d0cd562613a9d0a4f15b0c39f2a60e0f8b8c6f1f3a"
or target_package.get("tape_sha256")
!= "5383c376951637b8d4b1ca42fc6db80347abda74c0181c0f70d6c411fb97ea64"
):
raise PublicStatusError("JoinQuant TargetPackage lineage drifted")
sessions = run.get("session_observations")
if not isinstance(sessions, list) or [
(item.get("date"), item.get("event"), item.get("orders"), item.get("fills"))
for item in sessions
if isinstance(item, dict)
] != [
("2024-03-11", "TARGET_PACKAGE_NOOP", 0, 0),
("2024-03-12", "TARGET_PACKAGE_HIT", 3, 3),
("2024-03-13", "TARGET_PACKAGE_NOOP", 0, 0),
]:
raise PublicStatusError("JoinQuant TargetPackage session evidence drifted")
if run.get("counts") != {
"orders": 3,
"fills": 3,
"skipped_orders": 0,
}:
raise PublicStatusError("JoinQuant TargetPackage order/fill counts drifted")
boundary = run.get("claim_boundary")
if not isinstance(boundary, dict):
raise PublicStatusError("JoinQuant TargetPackage claim boundary is missing")
required_false = (
"upstream_layers_real_platform_observed",
"authorized_real_data_oos_observed",
"qmt_peer_observed",
"same_input_cross_engine_parity_observed",
"all_five_layers_real_platform_observed",
"g9_passed",
"baseline_60_passed",
"release_verified",
"investment_value_claim",
)
if boundary.get("real_platform_observed_layers") != ["execution"] or any(
boundary.get(field) is not False for field in required_false
):
raise PublicStatusError(
"JoinQuant TargetPackage run overclaims its evidence boundary"
)
performance = run.get("performance")
if (
not isinstance(performance, dict)
or performance.get("investment_value_claim") is not False
or performance.get("scope") != "three-session execution wiring smoke only"
):
raise PublicStatusError(
"JoinQuant TargetPackage performance boundary is unsafe"
)
path = _safe_repo_path(
JOINQUANT_TARGET_PACKAGE_RUN_PATH,
label="JoinQuant TargetPackage run",
)
if not (project / path).is_file():
raise PublicStatusError("tracked JoinQuant TargetPackage run is missing")
return {
"runId": JOINQUANT_TARGET_PACKAGE_RUN_ID,
"observedAt": JOINQUANT_TARGET_PACKAGE_OBSERVED_AT,
"marketDataFrom": backtest["start_date"],
"marketDataThrough": backtest["end_date"],
"bundleSha256": identity["bundle_sha256"],
"packageId": target_package["package_id"],
"evidenceClass": target_package["evidence_class"],
"orders": 3,
"fills": 3,
"observedLayers": ["execution"],
"allFiveLayersObserved": False,
"qmtPeerObserved": False,
"g9Passed": False,
"baseline60Passed": False,
"investmentValueClaim": False,
}
def _extract_test_summary(facts: Sequence[Any]) -> Tuple[int | None, int | None]: def _extract_test_summary(facts: Sequence[Any]) -> Tuple[int | None, int | None]:
for fact in facts: for fact in facts:
text = str(fact) text = str(fact)
@@ -288,7 +588,13 @@ def _platform_status(name: str) -> str:
def _platform_evidence_links(name: str) -> List[str]: def _platform_evidence_links(name: str) -> List[str]:
links = ["platform-matrix"] links = ["platform-matrix"]
if "JoinQuant" in name: if "JoinQuant" in name:
links.append("joinquant-hosted-evidence") links.extend(
(
"joinquant-hosted-evidence",
"joinquant-target-package-evidence",
"joinquant-target-package-run",
)
)
if "Qlib" in name or "Tushare" in name: if "Qlib" in name or "Tushare" in name:
links.append("tushare-data") links.append("tushare-data")
if "QMT" in name or "XtTrader" in name: if "QMT" in name or "XtTrader" in name:
@@ -337,6 +643,12 @@ def _build_evidence(project: Path) -> List[Dict[str, Any]]:
"dist/bundle_manifest.json", "dist/bundle_manifest.json",
"machine_record", "machine_record",
), ),
(
"release-reachability",
"Five-layer release reachability manifest",
REACHABILITY_PATH,
"machine_record",
),
("readme", "Quant OS verification guide", "README.md", "documentation"), ("readme", "Quant OS verification guide", "README.md", "documentation"),
( (
"architecture", "architecture",
@@ -364,10 +676,22 @@ def _build_evidence(project: Path) -> List[Dict[str, Any]]:
), ),
( (
"joinquant-hosted-evidence", "joinquant-hosted-evidence",
"JoinQuant hosted smoke boundary", "JoinQuant hosted momentum smoke boundary",
"docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md", "docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md",
"runtime_evidence", "runtime_evidence",
), ),
(
"joinquant-target-package-evidence",
"JoinQuant TargetPackage execution evidence boundary",
JOINQUANT_TARGET_PACKAGE_DOC_PATH,
"runtime_evidence",
),
(
"joinquant-target-package-run",
"Redacted JoinQuant TargetPackage hosted run",
JOINQUANT_TARGET_PACKAGE_RUN_PATH,
"machine_record",
),
( (
"tushare-data", "tushare-data",
"Tushare inventory and Qlib bridge record", "Tushare inventory and Qlib bridge record",
@@ -403,14 +727,14 @@ def _build_layers() -> List[Dict[str, Any]]:
"id": "data", "id": "data",
"name": "数据与时点", "name": "数据与时点",
"status": "tested", "status": "tested",
"claim": "不可变快照、PIT 字段合同和 Tushare→Qlib 技术链路已实现并有本地合同/运行证据。", "claim": "不可变快照、PIT 字段合同和 Tushare→Qlib 技术链路已有本地合同/运行证据;这些 provider-data 路径尚未接成当前 Ridge 五层候选",
"capabilities": [ "capabilities": [
"JQData raw/factor/limits/paused/PIT membership/ST snapshot", "JQData raw/factor/limits/paused/PIT membership/ST snapshot",
"Tushare completed-Parquet inventory and immutable Qlib provider", "Tushare completed-Parquet inventory and immutable Qlib provider",
"semantic manifest and data-version verification", "semantic manifest and data-version verification",
], ],
"limitations": [ "limitations": [
"尚无授权 JQData 真实快照与许可 lineage;Tushare 镜像仅部分完成且缺生产关键字段。" "尚无授权 JQData 真实快照与许可 lineage;Tushare 镜像仅部分完成且缺生产关键字段;当前五层候选仍使用 synthetic 数据"
], ],
"evidenceLinks": ["architecture", "tushare-data", "scorecard"], "evidenceLinks": ["architecture", "tushare-data", "scorecard"],
"releaseVerified": False, "releaseVerified": False,
@@ -419,52 +743,65 @@ def _build_layers() -> List[Dict[str, Any]]:
"id": "research", "id": "research",
"name": "研究与模型", "name": "研究与模型",
"status": "real_runtime", "status": "real_runtime",
"claim": "synthetic research pipeline 已通过测试;Qlib 0.9.7 的 momentum Alpha158/LightGBM fixture 已在真实本地运行时完成", "claim": "synthetic 五层研究入口能冻结 train-only Ridge ModelBundle、逐层 trace 与 TargetPackageV1Qlib momentum Alpha158/LightGBM fixture 是另外的受限研究 smoke",
"capabilities": [ "capabilities": [
"PIT features, purged walk-forward and deterministic Ridge", "train-only preprocessing, purged walk-forward and deterministic Ridge",
"replayable frozen ModelBundle and synthetic TargetPackageV1",
"Qlib native momentum", "Qlib native momentum",
"Qlib Alpha158, LightGBM and Recorder workflow", "Qlib Alpha158, LightGBM and Recorder workflow",
], ],
"limitations": [ "limitations": [
"fixture 仅两只 synthetic 股票;没有授权长样本 OOS、冻结模型包或投资价值证据。" "候选和 Qlib fixture 均是 synthetic;尚无授权长样本 OOS、真实数据校准、平台候选观察或投资价值证据。"
],
"evidenceLinks": [
"architecture",
"platform-matrix",
"release-reachability",
"scorecard",
], ],
"evidenceLinks": ["architecture", "platform-matrix", "scorecard"],
"releaseVerified": False, "releaseVerified": False,
}, },
{ {
"id": "signal-portfolio", "id": "signal-portfolio",
"name": "信号、组合与事前风险", "name": "信号、组合与事前风险",
"status": "tested", "status": "tested",
"claim": "Signal→Target→Order Delta 合同、成本、风险与约束目标在本地测试路径可重放", "claim": "当前 synthetic 主链把 Ridge Alpha、显式成本、60 日对角 realized variance、deterministic optimizer 和 post-risk gate 接成可重放 TargetPackage",
"capabilities": [ "capabilities": [
"portable momentum scoring and capped target weights", "explicit fee/spread/square-root-impact cost forecast",
"lot-rounded target quantities and T+1 sellable deltas", "diagonal realized-variance risk input and deterministic constrained target",
"factor risk, dated cost and constrained target research", "single-name, industry, gross, capacity and turnover constraints",
"post-risk weights with hash-linked lineage",
], ],
"limitations": [ "limitations": [
"风险、冲击和容量尚未用授权真实数据校准G4/G5 均未通过。" "factor_risk.py 与 optional CVXPY solver 未被当前主链选择;风险、冲击和容量尚未用授权真实数据校准G4/G5 均未通过。"
],
"evidenceLinks": [
"architecture",
"release-reachability",
"scorecard",
], ],
"evidenceLinks": ["architecture", "scorecard"],
"releaseVerified": False, "releaseVerified": False,
}, },
{ {
"id": "backtest", "id": "backtest",
"name": "回测与跨引擎", "name": "回测与跨引擎",
"status": "real_runtime", "status": "real_runtime",
"claim": "本地 synthetic、Qlib fixture 与一次真实 JoinQuant hosted smoke 已运行;QMT 仍只有 bundle/harness", "claim": "本地 synthetic 五层候选、平台 harness 和一次真实 JoinQuant TargetPackage execution smoke 已运行;QMT 尚无真实 peer run",
"capabilities": [ "capabilities": [
"local event and snapshot backtests", "local event and snapshot backtests",
"JoinQuant hosted single-file bundle", "fail-closed JoinQuant TargetPackage single-file consumer",
"QMT built-in/qmttools backtest-only bundle", "fail-closed QMT built-in TargetPackage backtest consumer",
"Qlib research runners", "Qlib research runners",
], ],
"limitations": [ "limitations": [
"没有冻结同输入的本地/聚宽/QMT 逐层对比;真实 QMT 尚未运行G9 未通过。" "聚宽只观察到一个 synthetic-research TargetPackage 的消费、账户/开盘价绑定和三笔成交;无真实长样本 OOS、QMT peer 或逐层真实数据 parityG9 未通过。"
], ],
"evidenceLinks": [ "evidenceLinks": [
"platform-matrix", "platform-matrix",
"joinquant-hosted-evidence", "joinquant-target-package-evidence",
"joinquant-target-package-run",
"cross-engine", "cross-engine",
"release-reachability",
"scorecard", "scorecard",
], ],
"releaseVerified": False, "releaseVerified": False,
@@ -473,17 +810,23 @@ def _build_layers() -> List[Dict[str, Any]]:
"id": "platforms", "id": "platforms",
"name": "平台适配", "name": "平台适配",
"status": "real_runtime", "status": "real_runtime",
"claim": "Local、Colab、JoinQuant、Qlib 有可执行入口;JoinQuant/Qlib 有受限真实运行时 smoke", "claim": "Local、Colab、JoinQuant、QMT、Qlib 有入口;JoinQuant 已真实观察 synthetic-research TargetPackage 的 execution consumerQMT 候选仍未实跑",
"capabilities": [ "capabilities": [
"local and Colab zero-account smoke", "local and Colab zero-account smoke",
"JQData snapshot entrypoint", "JQData snapshot entrypoint",
"JoinQuant upload bundle", "JoinQuant momentum or explicit TargetPackage upload bundle",
"QMT built-in and qmttools entrypoints", "QMT built-in TargetPackage and qmttools entrypoints",
], ],
"limitations": [ "limitations": [
"JQData 真实账号、QMT 客户端/数据权限及平台完整导出仍是外部前置条件。" "JoinQuant 没有重跑上游四层;JQData 授权真实候选、QMT 客户端/数据权限、QMT peer 导出与逐层真实数据 parity 仍是外部前置条件。"
],
"evidenceLinks": [
"platform-matrix",
"joinquant-target-package-evidence",
"joinquant-target-package-run",
"platform-deployment",
"release-reachability",
], ],
"evidenceLinks": ["platform-matrix", "platform-deployment"],
"releaseVerified": False, "releaseVerified": False,
}, },
{ {
@@ -526,11 +869,11 @@ def _build_models() -> List[Dict[str, Any]]:
{ {
"id": "portable-momentum-v1", "id": "portable-momentum-v1",
"name": "portable-momentum-v1", "name": "portable-momentum-v1",
"role": "跨引擎冻结计算基线", "role": "平台连通性 smoke 策略",
"status": "real_runtime", "status": "real_runtime",
"claim": "共享 symbol normalization、momentum、target weight/quantity 与 order-delta 计算;已进入 JoinQuant hosted smoke 和本地 Qlib momentum 路径。", "claim": "共享 symbol normalization、momentum、target weight/quantity 与 order-delta 计算;2024 JoinQuant hosted run 只证明这一 smoke 路径。",
"limitations": [ "limitations": [
"尚无同输入 L1–L4 真实跨引擎通过证据,不是已验证的生产 champion" "不是 Ridge/TargetPackage Baseline 候选;旧 hosted run 不得用于候选或 G9 计分"
], ],
"evidenceLinks": [ "evidenceLinks": [
"architecture", "architecture",
@@ -543,13 +886,18 @@ def _build_models() -> List[Dict[str, Any]]:
{ {
"id": "ridge-challenger", "id": "ridge-challenger",
"name": "Deterministic Ridge", "name": "Deterministic Ridge",
"role": "synthetic 研究候选", "role": "synthetic 五层 Baseline 研究候选",
"status": "tested", "status": "tested",
"claim": "purged walk-forward research pipeline 中可确定性训练和评估", "claim": "train-only 预处理、系数、截距、训练截止和 lineage 可冻结为可重放 ModelBundle;其 synthetic TargetPackageV1 已被真实 JoinQuant execution consumer 消费并成交",
"limitations": [ "limitations": [
"只在 synthetic 研究路径验证;无真实 OOS、versioned model bundle 或跨平台推理验证" "JoinQuant 只观察执行边界,未重跑上游四层;无授权真实 OOS、真实校准、QMT peer 或投资价值证据"
],
"evidenceLinks": [
"architecture",
"platform-matrix",
"joinquant-target-package-evidence",
"joinquant-target-package-run",
], ],
"evidenceLinks": ["architecture", "platform-matrix"],
"investmentValueClaim": False, "investmentValueClaim": False,
"releaseVerified": False, "releaseVerified": False,
}, },
@@ -570,8 +918,14 @@ def _build_models() -> List[Dict[str, Any]]:
def _build_next_actions(scorecard: Mapping[str, Any]) -> List[Dict[str, Any]]: def _build_next_actions(scorecard: Mapping[str, Any]) -> List[Dict[str, Any]]:
missing = scorecard.get("current_delivery", {}).get( delivery = scorecard.get("current_delivery", {})
"external_evidence_missing", [] if not isinstance(delivery, dict):
raise PublicStatusError("scorecard current_delivery must be an object")
internal = delivery.get("internal_delivery_gaps", [])
missing = delivery.get("external_evidence_missing", [])
if not isinstance(internal, list) or len(internal) < 3:
raise PublicStatusError(
"scorecard must list the internal delivery gaps blocking Baseline"
) )
if not isinstance(missing, list) or len(missing) < 5: if not isinstance(missing, list) or len(missing) < 5:
raise PublicStatusError( raise PublicStatusError(
@@ -580,30 +934,36 @@ def _build_next_actions(scorecard: Mapping[str, Any]) -> List[Dict[str, Any]]:
return [ return [
{ {
"priority": 1, "priority": 1,
"title": "生成授权 JQData 不可变真实快照", "title": "接通真实数据五层候选",
"why": str(missing[0]), "why": f"{internal[0]} External prerequisite: {missing[0]}",
"gateIds": ["G1", "G3", "G7"], "gateIds": ["G1", "G2", "G3", "G5", "G7"],
}, },
{ {
"priority": 2, "priority": 2,
"title": "冻结聚宽同输入并完成逐层本地比较", "title": "冻结主链风险、优化与执行选择",
"why": str(missing[1]), "why": str(internal[1]),
"gateIds": ["G3", "G9"], "gateIds": ["G3", "G5", "G7", "G9"],
}, },
{ {
"priority": 3, "priority": 3,
"title": "取得真实 QMT built-in 与 qmttools 导出", "title": "扩展 JoinQuant 多期对账与逐层 parity",
"why": str(missing[2]), "why": f"{internal[2]} Required evidence: {missing[1]}",
"gateIds": ["G3", "G6", "G9"], "gateIds": ["G3", "G7", "G9"],
}, },
{ {
"priority": 4, "priority": 4,
"title": "运行真实 QMT TargetPackage 候选",
"why": str(missing[2]),
"gateIds": ["G6", "G8", "G9"],
},
{
"priority": 5,
"title": "完成 QMT 券商合同探针与至少 20 日影子盘", "title": "完成 QMT 券商合同探针与至少 20 日影子盘",
"why": str(missing[3]), "why": str(missing[3]),
"gateIds": ["G6", "G8", "G9"], "gateIds": ["G6", "G8", "G9"],
}, },
{ {
"priority": 5, "priority": 6,
"title": "完成连续对账、恢复演练与券商合规确认", "title": "完成连续对账、恢复演练与券商合规确认",
"why": str(missing[4]), "why": str(missing[4]),
"gateIds": ["G8", "G10"], "gateIds": ["G8", "G10"],
@@ -615,6 +975,7 @@ def _build_freshness(
*, *,
scorecard: Mapping[str, Any], scorecard: Mapping[str, Any],
joinquant_markdown: str, joinquant_markdown: str,
joinquant_target_package_run: Mapping[str, Any],
tushare_markdown: str, tushare_markdown: str,
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
period_match = re.search( period_match = re.search(
@@ -686,15 +1047,44 @@ def _build_freshness(
}, },
{ {
"id": "joinquant-hosted-market-window", "id": "joinquant-hosted-market-window",
"label": "JoinQuant hosted smoke 行情区间", "label": "JoinQuant hosted momentum smoke 行情区间",
"status": "runtime_smoke", "status": "runtime_smoke",
"marketDataFrom": joinquant_start, "marketDataFrom": joinquant_start,
"marketDataThrough": joinquant_end, "marketDataThrough": joinquant_end,
"value": f"{joinquant_start}{joinquant_end}", "value": f"{joinquant_start}{joinquant_end}",
"productionReady": False, "productionReady": False,
"semantics": "单次真实 hosted runtime smoke 的回测区间;不代表同输入跨引擎通过", "semantics": "旧 momentum hosted runtime smoke 的回测区间;不是 TargetPackage 候选或同输入跨引擎证据",
"evidenceLinks": ["joinquant-hosted-evidence", "scorecard"], "evidenceLinks": ["joinquant-hosted-evidence", "scorecard"],
}, },
{
"id": "joinquant-target-package-execution-smoke",
"label": "JoinQuant TargetPackage execution smoke",
"status": "runtime_smoke",
"observedAt": str(joinquant_target_package_run["observedAt"]),
"marketDataFrom": str(
joinquant_target_package_run["marketDataFrom"]
),
"marketDataThrough": str(
joinquant_target_package_run["marketDataThrough"]
),
"value": (
f"{joinquant_target_package_run['marketDataFrom']}"
f"{joinquant_target_package_run['marketDataThrough']}; "
"3 orders / 3 fills"
),
"productionReady": False,
"investmentValueClaim": False,
"semantics": (
"真实平台只观察 synthetic-research TargetPackage 的 execution "
"consumer;不代表上游四层真实平台执行、长样本 OOS 或 G9。"
),
"evidenceLinks": [
"joinquant-target-package-evidence",
"joinquant-target-package-run",
"release-reachability",
"scorecard",
],
},
{ {
"id": "tushare-daily-market-data", "id": "tushare-daily-market-data",
"label": "Tushare daily 行情覆盖", "label": "Tushare daily 行情覆盖",
@@ -806,6 +1196,10 @@ def build_public_status(
project = project.resolve() project = project.resolve()
scorecard = _load_json(project / "gate_scorecard.json") scorecard = _load_json(project / "gate_scorecard.json")
manifest = _load_json(project / "dist" / "bundle_manifest.json") manifest = _load_json(project / "dist" / "bundle_manifest.json")
reachability_manifest = _load_json(project / REACHABILITY_PATH)
joinquant_target_package_run_raw = _load_json(
project / JOINQUANT_TARGET_PACKAGE_RUN_PATH
)
baseline = _load_json(project / "configs" / "baseline.json") baseline = _load_json(project / "configs" / "baseline.json")
platform_markdown = (project / "docs" / "PLATFORM_MATRIX.md").read_text( platform_markdown = (project / "docs" / "PLATFORM_MATRIX.md").read_text(
encoding="utf-8" encoding="utf-8"
@@ -870,6 +1264,13 @@ def build_public_status(
test_count, optional_skips = _extract_test_summary(facts) test_count, optional_skips = _extract_test_summary(facts)
artifacts = _validate_bundle_manifest(project, manifest) artifacts = _validate_bundle_manifest(project, manifest)
reachability = _validate_reachability_manifest(
project, reachability_manifest
)
joinquant_target_package_run = _validate_joinquant_target_package_run(
project,
joinquant_target_package_run_raw,
)
source_records: List[Dict[str, str]] = [] source_records: List[Dict[str, str]] = []
for relative in SOURCE_PATHS: for relative in SOURCE_PATHS:
safe_path = _safe_repo_path(relative, label="provenance source") safe_path = _safe_repo_path(relative, label="provenance source")
@@ -883,6 +1284,19 @@ def build_public_status(
claim_policy = scorecard["claim_policy"] claim_policy = scorecard["claim_policy"]
delivery = scorecard["current_delivery"] delivery = scorecard["current_delivery"]
internal_delivery_gaps = delivery.get("internal_delivery_gaps", [])
external_evidence_missing = delivery.get("external_evidence_missing", [])
if not isinstance(internal_delivery_gaps, list) or len(internal_delivery_gaps) < 3:
raise PublicStatusError(
"scorecard must list the internal delivery gaps blocking Baseline"
)
if (
not isinstance(external_evidence_missing, list)
or len(external_evidence_missing) < 5
):
raise PublicStatusError(
"scorecard must list the external evidence blocking release"
)
payload: Dict[str, Any] = { payload: Dict[str, Any] = {
"schemaVersion": SCHEMA_VERSION, "schemaVersion": SCHEMA_VERSION,
"generatedAt": generated_at_value, "generatedAt": generated_at_value,
@@ -905,8 +1319,20 @@ def build_public_status(
"testsRecorded": test_count, "testsRecorded": test_count,
"optionalSkipsRecorded": optional_skips, "optionalSkipsRecorded": optional_skips,
"testEvidenceQualifier": "Scorecard-recorded run; tests do not prove real-platform or broker gates.", "testEvidenceQualifier": "Scorecard-recorded run; tests do not prove real-platform or broker gates.",
"candidateRealPlatformObserved": bool(
reachability["realPlatformsObserved"]
),
"candidateRealPlatformObservationScope": (
"target_package_execution_consumer_only"
),
"allFiveLayersRealPlatformObserved": bool(
reachability["summary"][
"all_layers_real_platform_observed"
]
),
"realRuntimeSmokes": [ "realRuntimeSmokes": [
"JoinQuant hosted daily backtest (runtime/path only)", "JoinQuant TargetPackage execution consumer: one synthetic-research package, 3 orders / 3 fills (not Baseline/G9 evidence)",
"JoinQuant portable_momentum_smoke hosted daily backtest (not Baseline/TargetPackage evidence)",
"Qlib native momentum fixture", "Qlib native momentum fixture",
"Qlib Alpha158 + LightGBM fixture", "Qlib Alpha158 + LightGBM fixture",
"Tushare completed-partition Qlib momentum smoke", "Tushare completed-partition Qlib momentum smoke",
@@ -915,12 +1341,45 @@ def build_public_status(
"productionReady": False, "productionReady": False,
"investmentValueClaim": False, "investmentValueClaim": False,
}, },
"candidateBoundary": {
"decisionMode": "target_package",
"localEvidenceClass": "synthetic_research",
"connectedRiskModel": "60-session diagonal realized variance",
"connectedOptimizer": "deterministic constrained optimizer",
"connectedExecution": "reference one-shot quantity/order-delta planner",
"realJoinQuantObserved": (
"joinquant" in reachability["realPlatformsObserved"]
),
"realQmtObserved": (
"qmt" in reachability["realPlatformsObserved"]
),
"realPlatformObservedLayers": reachability[
"realPlatformObservedLayers"
],
"allFiveLayersRealPlatformObserved": bool(
reachability["summary"][
"all_layers_real_platform_observed"
]
),
"joinQuantTargetPackageRun": joinquant_target_package_run,
"joinQuantTargetPackageCountsAsBaseline": False,
"joinQuantTargetPackageCountsAsG9": False,
"historicalJoinQuantMode": "portable_momentum_smoke",
"historicalJoinQuantCountsAsBaseline": False,
"reachability": reachability,
"releaseVerified": False,
},
"deliveryGaps": {
"internal": [str(item) for item in internal_delivery_gaps],
"externalEvidence": [str(item) for item in external_evidence_missing],
},
"layers": _build_layers(), "layers": _build_layers(),
"models": _build_models(), "models": _build_models(),
"platforms": _build_platforms(platform_markdown), "platforms": _build_platforms(platform_markdown),
"freshness": _build_freshness( "freshness": _build_freshness(
scorecard=scorecard, scorecard=scorecard,
joinquant_markdown=joinquant_markdown, joinquant_markdown=joinquant_markdown,
joinquant_target_package_run=joinquant_target_package_run,
tushare_markdown=tushare_markdown, tushare_markdown=tushare_markdown,
), ),
"gates": gates, "gates": gates,
@@ -954,6 +1413,7 @@ def build_public_status(
"provenance": { "provenance": {
"scorecard": "gate_scorecard.json", "scorecard": "gate_scorecard.json",
"bundleManifest": "dist/bundle_manifest.json", "bundleManifest": "dist/bundle_manifest.json",
"releaseReachability": REACHABILITY_PATH,
"platformMatrix": "docs/PLATFORM_MATRIX.md", "platformMatrix": "docs/PLATFORM_MATRIX.md",
"baselineConfig": "configs/baseline.json", "baselineConfig": "configs/baseline.json",
"sourceSetSha256": source_set_sha, "sourceSetSha256": source_set_sha,
+308
View File
@@ -0,0 +1,308 @@
"""Build the conservative Quant60 research-candidate reachability manifest.
The hosted-platform claims in this manifest have deliberately narrow
semantics. For Universe, Alpha, Portfolio, and Risk, ``*_entrypoint_reachable``
means that the locally computed output and its lineage reach the hosted
consumer through a verified ``TargetPackageV1``. It does *not* mean that
JoinQuant or QMT execute those upstream Python implementations. Execution is
the only layer implemented inside the hosted wrappers.
One real JoinQuant TargetPackage smoke observes the execution consumer,
account/price binding, orders, and fills. It therefore supports only
``execution.real_platform_observed``. The other four layer observations and
the derived all-layer observation remain false.
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
PROJECT = Path(__file__).resolve().parents[1]
SOURCE_ROOT = PROJECT / "src"
if str(SOURCE_ROOT) not in sys.path:
sys.path.insert(0, str(SOURCE_ROOT))
from quant60.ledger import canonical_json # noqa: E402
from quant60.release_reachability import ( # noqa: E402
LAYERS,
build_evidence_artifact,
build_release_reachability,
load_verified_release_reachability,
write_release_reachability,
)
RELEASE_ID = "quant60-research-candidate-v1"
RELEASE_DECLARED_AT = "2026-07-26T12:00:00Z"
DEFAULT_OUTPUT = (
PROJECT / "releases" / RELEASE_ID / "reachability.json"
)
JOINQUANT_TARGET_PACKAGE_RUN_PATH = (
"evidence/joinquant/TP-20240311-795dcfba/"
"2457a7c39a276e09e0fabf99e28978e1/run.json"
)
JOINQUANT_TARGET_PACKAGE_RUN_ID = "2457a7c39a276e09e0fabf99e28978e1"
JOINQUANT_TARGET_PACKAGE_OBSERVED_AT = "2026-07-26T08:50:15Z"
PLATFORM_CLAIM_NOTE = (
"Upstream layer status means its locally computed output/lineage is "
"carried through TargetPackageV1 to the hosted consumer; it does not "
"claim JoinQuant/QMT executes the upstream Python. Execution alone runs "
"inside the hosted wrapper. One JoinQuant smoke observes only that "
"execution boundary; it does not observe all five layers."
)
# Each path names code used by the connected local five-layer decision path.
# A shared composition module appears in several claims by design; artifact IDs
# remain unique, as required by the reachability contract.
IMPLEMENTED_EVIDENCE: dict[str, tuple[tuple[str, str], ...]] = {
"universe": (
("baseline-pipeline", "src/quant60/baseline_pipeline.py"),
),
"alpha": (
("frozen-model-bundle", "src/quant60/model_bundle.py"),
("baseline-pipeline", "src/quant60/baseline_pipeline.py"),
),
"portfolio": (
("optimizer", "src/quant60/optimizer.py"),
("baseline-pipeline", "src/quant60/baseline_pipeline.py"),
),
"risk": (
("risk-gate", "src/quant60/risk.py"),
("baseline-pipeline", "src/quant60/baseline_pipeline.py"),
),
"execution": (
("portable-execution-core", "src/quant60/portable_core.py"),
("baseline-pipeline", "src/quant60/baseline_pipeline.py"),
),
}
UNIT_TEST_EVIDENCE: dict[str, tuple[tuple[str, str], ...]] = {
"universe": (
("five-layer-causality", "tests/test_baseline_pipeline.py"),
),
"alpha": (
("five-layer-causality", "tests/test_baseline_pipeline.py"),
("frozen-model-replay", "tests/test_model_bundle.py"),
),
"portfolio": (
("five-layer-causality", "tests/test_baseline_pipeline.py"),
),
"risk": (
("five-layer-causality", "tests/test_baseline_pipeline.py"),
),
"execution": (
("five-layer-causality", "tests/test_baseline_pipeline.py"),
("portable-order-plan", "tests/test_portable_core.py"),
),
}
LOCAL_ENTRYPOINT_EVIDENCE: tuple[tuple[str, str], ...] = (
("research-smoke-cli", "src/quant60/cli.py"),
("research-experiment", "src/quant60/experiment.py"),
)
PLATFORM_ENTRYPOINTS = {
"jq": {
"capability": "jq_entrypoint_reachable",
"platform": "joinquant",
"path": "platforms/joinquant_strategy.py",
},
"qmt": {
"capability": "qmt_entrypoint_reachable",
"platform": "qmt",
"path": "platforms/qmt_builtin_strategy.py",
},
}
# These labels are part of the signed artifact IDs. They keep the narrow
# meaning visible even though the v1 schema intentionally has no free-text
# claim field.
PLATFORM_OUTPUT_LABELS = {
"universe": "universe-state-via-targetpackagev1-consumer",
"alpha": "alpha-lineage-derived-target-via-targetpackagev1-consumer",
"portfolio": "portfolio-weight-via-targetpackagev1-consumer",
"risk": "post-risk-weight-constraints-via-targetpackagev1-consumer",
"execution": "targetpackagev1-hosted-execution-entrypoint",
}
def _false_claim() -> dict[str, Any]:
return {"status": False, "artifacts": []}
def _evidence(
*,
repository_root: Path,
artifact_id: str,
kind: str,
path: str,
platform: str,
observed_at: str | None = None,
run_id: str | None = None,
) -> dict[str, Any]:
return build_evidence_artifact(
artifact_id=artifact_id,
kind=kind,
path=path,
platform=platform,
repository_root=repository_root,
observed_at=observed_at,
run_id=run_id,
)
def build_manifest(
*,
repository_root: str | Path = PROJECT,
generated_at: str = RELEASE_DECLARED_AT,
) -> dict[str, Any]:
"""Build a fully content-addressed manifest from the current repository."""
root = Path(repository_root)
layers: dict[str, dict[str, Any]] = {}
for layer in LAYERS:
implemented = [
_evidence(
repository_root=root,
artifact_id=f"{layer}-implemented-{label}",
kind="source",
path=path,
platform="repository",
)
for label, path in IMPLEMENTED_EVIDENCE[layer]
]
unit_tested = [
_evidence(
repository_root=root,
artifact_id=f"{layer}-unit-tested-{label}",
kind="test",
path=path,
platform="repository",
)
for label, path in UNIT_TEST_EVIDENCE[layer]
]
local_entrypoint = [
_evidence(
repository_root=root,
artifact_id=f"{layer}-local-{label}",
kind="entrypoint",
path=path,
platform="local",
)
for label, path in LOCAL_ENTRYPOINT_EVIDENCE
]
claims: dict[str, Any] = {
"implemented": {"status": True, "artifacts": implemented},
"unit_tested": {"status": True, "artifacts": unit_tested},
"local_entrypoint_reachable": {
"status": True,
"artifacts": local_entrypoint,
},
"real_platform_observed": _false_claim(),
}
if layer == "execution":
claims["real_platform_observed"] = {
"status": True,
"artifacts": [
_evidence(
repository_root=root,
artifact_id=(
"execution-real-platform-targetpackagev1-smoke-jq"
),
kind="platform_run",
path=JOINQUANT_TARGET_PACKAGE_RUN_PATH,
platform="joinquant",
observed_at=JOINQUANT_TARGET_PACKAGE_OBSERVED_AT,
run_id=JOINQUANT_TARGET_PACKAGE_RUN_ID,
)
],
}
for name, declaration in PLATFORM_ENTRYPOINTS.items():
capability = declaration["capability"]
claims[capability] = {
"status": True,
"artifacts": [
_evidence(
repository_root=root,
artifact_id=(
f"{layer}-{PLATFORM_OUTPUT_LABELS[layer]}-{name}"
),
kind="entrypoint",
path=declaration["path"],
platform=declaration["platform"],
)
],
}
layers[layer] = claims
return build_release_reachability(
release_id=RELEASE_ID,
generated_at=generated_at,
layers=layers,
repository_root=root,
)
def _build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--output",
type=Path,
default=DEFAULT_OUTPUT,
help="manifest path (default: releases/<release>/reachability.json)",
)
parser.add_argument(
"--generated-at",
default=RELEASE_DECLARED_AT,
help="canonical UTC release declaration time",
)
parser.add_argument(
"--check",
action="store_true",
help="verify that the existing manifest exactly matches regeneration",
)
return parser
def main(argv: list[str] | None = None) -> int:
args = _build_parser().parse_args(argv)
expected = build_manifest(generated_at=args.generated_at)
output = args.output.expanduser().resolve()
if args.check:
observed = load_verified_release_reachability(
output,
repository_root=PROJECT,
expected_release_id=RELEASE_ID,
expected_manifest_sha256=expected["manifest_sha256"],
)
expected_bytes = (canonical_json(expected) + "\n").encode("utf-8")
if observed != expected or output.read_bytes() != expected_bytes:
raise ValueError(
"reachability manifest does not exactly match regeneration"
)
else:
write_release_reachability(
expected,
output,
repository_root=PROJECT,
)
summary = {
"ok": True,
"checked": bool(args.check),
"output": str(output),
"release_id": RELEASE_ID,
"manifest_sha256": expected["manifest_sha256"],
"platform_claim_semantics": PLATFORM_CLAIM_NOTE,
}
print(json.dumps(summary, ensure_ascii=False, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
+196 -14
View File
@@ -7,14 +7,33 @@ import ast
import hashlib import hashlib
import json import json
import pprint import pprint
import sys
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Optional from typing import Any, Dict, List, Optional
START = "# __PORTABLE_CORE_BUNDLE_START__" START = "# __PORTABLE_CORE_BUNDLE_START__"
END = "# __PORTABLE_CORE_BUNDLE_END__" END = "# __PORTABLE_CORE_BUNDLE_END__"
CONFIG_START = "# __BASELINE_CONFIG_START__" CONFIG_START = "# __BASELINE_CONFIG_START__"
CONFIG_END = "# __BASELINE_CONFIG_END__" CONFIG_END = "# __BASELINE_CONFIG_END__"
TARGET_START = "# __TARGET_PACKAGE_TAPE_START__"
TARGET_END = "# __TARGET_PACKAGE_TAPE_END__"
MOMENTUM_SMOKE_MODE = "portable_momentum_smoke"
TARGET_PACKAGE_MODE = "target_package"
JOINQUANT_EVIDENCE_PREFIX = "QUANT60_JOINQUANT_EVIDENCE_V1 "
JOINQUANT_EVIDENCE_CHUNK_CHARACTERS = 1800
JOINQUANT_TARGET_PRICE_SOURCE = "CURRENT_DATA_DAY_OPEN"
JOINQUANT_EVIDENCE_EVENTS = [
"INIT",
"TARGET_PACKAGE_NOOP",
"TARGET_PACKAGE_HIT",
"PLATFORM_BINDING_INPUT",
"TARGET_PACKAGE_PLAN",
"SKIPPED_ORDER",
"ORDER_REQUEST",
"ORDER_RETURN",
"EOD_STATUS",
]
def sha256_bytes(value: bytes) -> str: def sha256_bytes(value: bytes) -> str:
@@ -56,6 +75,73 @@ def inline_core(wrapper_source: str, core_source: str) -> str:
return wrapper_source[:start] + replacement + wrapper_source[end:] return wrapper_source[:start] + replacement + wrapper_source[end:]
def _canonical_target_tape_bytes(
target_packages: List[Dict[str, Any]],
) -> bytes:
return json.dumps(
target_packages,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
def inline_target_packages(
wrapper_source: str,
target_packages: List[Dict[str, Any]],
) -> str:
"""Inline a pre-validated, quantity-free decision tape."""
if (
wrapper_source.count(TARGET_START) != 1
or wrapper_source.count(TARGET_END) != 1
):
raise ValueError(
"wrapper must contain exactly one target-package marker pair"
)
start = wrapper_source.index(TARGET_START)
end = wrapper_source.index(TARGET_END, start) + len(TARGET_END)
body = pprint.pformat(
target_packages,
width=88,
sort_dicts=False,
)
body = body.encode("ascii", "backslashreplace").decode("ascii")
replacement = (
TARGET_START
+ "\n# Generated by tools/bundle_platforms.py; do not edit this region.\n"
+ "TARGET_PACKAGES = "
+ body
+ "\n"
+ TARGET_END
)
return wrapper_source[:start] + replacement + wrapper_source[end:]
def load_verified_target_packages(
project_root: Path,
path: Path,
) -> List[Dict[str, Any]]:
"""Load one package or a multi-decision tape through the canonical verifier."""
root = project_root.expanduser().resolve()
source = path.expanduser().resolve()
src_path = str(root / "src")
inserted = src_path not in sys.path
if inserted:
sys.path.insert(0, src_path)
try:
from quant60.target_package import ( # pylint: disable=import-outside-toplevel
load_target_package_tape,
)
return list(load_target_package_tape(source))
finally:
if inserted:
sys.path.remove(src_path)
def _qmt_symbol(symbol: str) -> str: def _qmt_symbol(symbol: str) -> str:
suffixes = { suffixes = {
".XSHG": ".SH", ".XSHG": ".SH",
@@ -71,20 +157,52 @@ def _qmt_symbol(symbol: str) -> str:
def effective_platform_config( def effective_platform_config(
baseline: Dict[str, Any], baseline: Dict[str, Any],
platform: str, platform: str,
*,
decision_mode: str = MOMENTUM_SMOKE_MODE,
target_packages: Optional[List[Dict[str, Any]]] = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
packages = list(target_packages or [])
if decision_mode not in {MOMENTUM_SMOKE_MODE, TARGET_PACKAGE_MODE}:
raise ValueError(f"unsupported decision_mode: {decision_mode}")
if decision_mode == TARGET_PACKAGE_MODE and not packages:
raise ValueError("target_package mode requires a non-empty package tape")
if decision_mode == MOMENTUM_SMOKE_MODE and packages:
raise ValueError("momentum smoke mode cannot embed target packages")
strategy = baseline["strategy"] strategy = baseline["strategy"]
execution = baseline["execution"] execution = baseline["execution"]
fees = baseline["fees"] fees = baseline["fees"]
universe_config = baseline["universe"] universe_config = baseline["universe"]
symbols = [str(symbol) for symbol in baseline["symbols"]] symbols = (
sorted(
{
str(symbol)
for package in packages
for symbol in package["universe"]
}
)
if packages
else [str(symbol) for symbol in baseline["symbols"]]
)
if platform == "qmt_builtin": if platform == "qmt_builtin":
universe = [_qmt_symbol(symbol) for symbol in symbols] universe = [_qmt_symbol(symbol) for symbol in symbols]
elif platform == "joinquant": elif platform == "joinquant":
universe = symbols universe = symbols
else: else:
raise ValueError(f"unsupported platform config: {platform}") raise ValueError(f"unsupported platform config: {platform}")
tape_sha256 = (
sha256_bytes(_canonical_target_tape_bytes(packages))
if packages
else None
)
common: Dict[str, Any] = { common: Dict[str, Any] = {
"universe_mode": str(universe_config["mode"]), "decision_mode": decision_mode,
"target_package_count": len(packages),
"target_package_tape_sha256": tape_sha256,
"universe_mode": (
"fixed"
if decision_mode == TARGET_PACKAGE_MODE
else str(universe_config["mode"])
),
"index_symbol": str(universe_config["index_symbol"]), "index_symbol": str(universe_config["index_symbol"]),
"qmt_sector_name": str(universe_config["qmt_sector_name"]), "qmt_sector_name": str(universe_config["qmt_sector_name"]),
"dedicated_account_required": bool( "dedicated_account_required": bool(
@@ -145,15 +263,19 @@ def bundle_one(
wrapper_path: Path, wrapper_path: Path,
output_path: Path, output_path: Path,
effective_config: Dict[str, Any], effective_config: Dict[str, Any],
target_packages: Optional[List[Dict[str, Any]]] = None,
require_ascii: bool = False, require_ascii: bool = False,
) -> Dict[str, str]: ) -> Dict[str, str]:
core_bytes = core_path.read_bytes() core_bytes = core_path.read_bytes()
wrapper_bytes = wrapper_path.read_bytes() wrapper_bytes = wrapper_path.read_bytes()
core_source = core_bytes.decode("utf-8") core_source = core_bytes.decode("utf-8")
wrapper_source = wrapper_bytes.decode("utf-8") wrapper_source = wrapper_bytes.decode("utf-8")
bundled = inline_config( bundled = inline_target_packages(
inline_config(
inline_core(wrapper_source, core_source), inline_core(wrapper_source, core_source),
effective_config, effective_config,
),
list(target_packages or []),
) )
output_bytes = bundled.encode("utf-8") output_bytes = bundled.encode("utf-8")
if require_ascii: if require_ascii:
@@ -194,6 +316,7 @@ def bundle_one(
def bundle_all( def bundle_all(
project_root: Path, project_root: Path,
output_dir: Optional[Path] = None, output_dir: Optional[Path] = None,
target_package_path: Optional[Path] = None,
) -> Dict[str, object]: ) -> Dict[str, object]:
root = project_root.expanduser().resolve() root = project_root.expanduser().resolve()
output = ( output = (
@@ -209,43 +332,90 @@ def bundle_all(
baseline = json.loads(baseline_bytes.decode("utf-8")) baseline = json.loads(baseline_bytes.decode("utf-8"))
if not isinstance(baseline, dict): if not isinstance(baseline, dict):
raise ValueError("configs/baseline.json must contain an object") raise ValueError("configs/baseline.json must contain an object")
target_packages = (
load_verified_target_packages(root, target_package_path)
if target_package_path is not None
else []
)
decision_mode = (
TARGET_PACKAGE_MODE if target_packages else MOMENTUM_SMOKE_MODE
)
entries = { entries = {
"joinquant": bundle_one( "joinquant": bundle_one(
core_path=core, core_path=core,
wrapper_path=root / "platforms" / "joinquant_strategy.py", wrapper_path=root / "platforms" / "joinquant_strategy.py",
output_path=output / "joinquant_strategy.py", output_path=output / "joinquant_strategy.py",
effective_config=effective_platform_config(baseline, "joinquant"), effective_config=effective_platform_config(
baseline,
"joinquant",
decision_mode=decision_mode,
target_packages=target_packages,
),
target_packages=target_packages,
), ),
"qmt_builtin": bundle_one( "qmt_builtin": bundle_one(
core_path=core, core_path=core,
wrapper_path=root / "platforms" / "qmt_builtin_strategy.py", wrapper_path=root / "platforms" / "qmt_builtin_strategy.py",
output_path=output / "qmt_builtin_strategy.py", output_path=output / "qmt_builtin_strategy.py",
effective_config=effective_platform_config(baseline, "qmt_builtin"), effective_config=effective_platform_config(
baseline,
"qmt_builtin",
decision_mode=decision_mode,
target_packages=target_packages,
),
target_packages=target_packages,
require_ascii=True, require_ascii=True,
), ),
} }
# Paths in a committed manifest must be portable and must not expose the # Paths in a manifest must be portable and must not expose the local
# local account/home directory. Hashes remain byte-for-byte identities. # account/home directory. Preserve a real project-relative location for
logical_outputs = { # outputs under the project (for example dist/target-package/...). A
# clean-room output outside the project keeps the committed logical names
# so its manifest remains byte-for-byte comparable with committed dist.
default_logical_outputs = {
"joinquant": "dist/joinquant_strategy.py", "joinquant": "dist/joinquant_strategy.py",
"qmt_builtin": "dist/qmt_builtin_strategy.py", "qmt_builtin": "dist/qmt_builtin_strategy.py",
} }
for platform_name, entry in entries.items(): for platform_name, entry in entries.items():
physical_output = Path(entry["output"])
entry["core"] = "src/quant60/portable_core.py" entry["core"] = "src/quant60/portable_core.py"
entry["wrapper"] = ( entry["wrapper"] = (
"platforms/joinquant_strategy.py" "platforms/joinquant_strategy.py"
if "joinquant" in entry["wrapper"] if "joinquant" in entry["wrapper"]
else "platforms/qmt_builtin_strategy.py" else "platforms/qmt_builtin_strategy.py"
) )
# The manifest describes the committed logical artifact, not the try:
# caller's temporary build directory. This keeps clean-room builds entry["output"] = physical_output.relative_to(root).as_posix()
# byte-for-byte comparable with committed ``dist``. except ValueError:
entry["output"] = logical_outputs[platform_name] entry["output"] = default_logical_outputs[platform_name]
manifest = { manifest = {
"schema_version": 1, "schema_version": 1,
"baseline_config": "configs/baseline.json", "baseline_config": "configs/baseline.json",
"baseline_config_sha256": sha256_bytes(baseline_bytes), "baseline_config_sha256": sha256_bytes(baseline_bytes),
"portable_core_sha256": entries["joinquant"]["core_sha256"], "portable_core_sha256": entries["joinquant"]["core_sha256"],
"decision_mode": decision_mode,
"target_package_count": len(target_packages),
"target_package_tape_sha256": (
sha256_bytes(_canonical_target_tape_bytes(target_packages))
if target_packages
else None
),
"target_package_ids": [
package["package_id"] for package in target_packages
],
"target_package_sha256": [
package["package_sha256"] for package in target_packages
],
"joinquant_observation": {
"eod_chunk_characters": JOINQUANT_EVIDENCE_CHUNK_CHARACTERS,
"eod_chunk_encoding": "base64-canonical-json",
"eod_api_policy": "best_effort_get_orders_get_trades",
"events": JOINQUANT_EVIDENCE_EVENTS,
"fail_soft": True,
"prefix": JOINQUANT_EVIDENCE_PREFIX,
"schema_version": "1.0",
"target_price_source": JOINQUANT_TARGET_PRICE_SOURCE,
},
"artifacts": entries, "artifacts": entries,
} }
manifest_path = output / "bundle_manifest.json" manifest_path = output / "bundle_manifest.json"
@@ -266,8 +436,20 @@ def _main(argv: Optional[list[str]] = None) -> int:
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--project-root", type=Path, default=default_root) parser.add_argument("--project-root", type=Path, default=default_root)
parser.add_argument("--output-dir", type=Path) parser.add_argument("--output-dir", type=Path)
parser.add_argument(
"--target-package",
type=Path,
help=(
"verified TargetPackageV1 JSON/JSONL tape; switches both hosted "
"artifacts from momentum smoke to target-package execution"
),
)
args = parser.parse_args(argv) args = parser.parse_args(argv)
manifest = bundle_all(args.project_root, args.output_dir) manifest = bundle_all(
args.project_root,
args.output_dir,
args.target_package,
)
print(json.dumps(manifest, ensure_ascii=False, indent=2, sort_keys=True)) print(json.dumps(manifest, ensure_ascii=False, indent=2, sort_keys=True))
return 0 return 0