feat: establish Quant OS production-80 architecture

This commit is contained in:
2026-07-30 22:59:25 +08:00
parent 919c64c679
commit 26cc814f3f
67 changed files with 7005 additions and 142 deletions
+29
View File
@@ -0,0 +1,29 @@
name: quant-os-ci
on:
push:
pull_request:
jobs:
deterministic-local:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Validate deterministic Quant OS
run: bash scripts/validate_all.sh
- name: Reject generated bundle drift
run: git diff --exit-code -- dist status/status-data.js
- name: Reject tracked credential paths
shell: bash
run: |
if git ls-files \
| grep -E '(^|/)(\.env(\.[^/]+)?|secrets|credentials|userdata_mini)(/|$)' \
| grep -Ev '(^|/)\.env\.example$'; then
echo "credential-like tracked path detected"
exit 1
fi
- name: Reject credential-like tracked content
run: python3 tools/check_tracked_secrets.py
+7 -5
View File
@@ -14,13 +14,15 @@ htmlcov/
venv/ venv/
# Generated evidence and local data # Generated evidence and local data
artifacts/ /artifacts/
reports/ reports/
runs/ runs/
data/raw/ /build/
data/canonical/ /data/
data/snapshots/ /data/raw/
data/qlib/ /data/canonical/
/data/snapshots/
/data/qlib/
mlruns/ mlruns/
*.parquet *.parquet
*.duckdb *.duckdb
+12 -6
View File
@@ -1,15 +1,18 @@
# CLAUDE.md # CLAUDE.md
This directory is the executable source of truth for Quant OS. `quant60` is This independent repository is the executable source of truth for Quant OS.
its first A-share Baseline kernel/package; the project itself also owns data, `quant_os` is the long-lived product namespace. `quant60` is the compatibility
research, portfolio, execution, evidence, and platform adapters. Obsidian namespace for the first A-share Baseline protocol and V1 artifacts; maturity
contains research and status documentation only. scores belong to profiles, never to package names. Obsidian contains research
and status documentation only.
## Development commands ## Development commands
Run all commands from this directory: Run all commands from this directory:
```bash ```bash
make architecture
make standard
make test make test
make bundle make bundle
make smoke make smoke
@@ -50,5 +53,8 @@ A change is complete only when:
1. unit/integration tests pass; 1. unit/integration tests pass;
2. hosted bundles rebuild and pass Python 3.6 syntax checks; 2. hosted bundles rebuild and pass Python 3.6 syntax checks;
3. the deterministic smoke and manifest verifier pass; 3. the deterministic smoke and manifest verifier pass;
4. docs state the exact evidence class and remaining external gate; 4. architecture and maturity profile checks pass;
5. no secret or machine-specific absolute path is added to tracked artifacts. 5. docs state the exact evidence class and remaining external gate;
6. no secret or machine-specific absolute path is added to executable or public
artifacts. Migration/getting-started docs may name the user-confirmed local
checkout, but generated evidence must stay repository-relative.
+4 -2
View File
@@ -102,7 +102,8 @@ Qlib 两次 smoke 使用的是确定性两股票 synthetic fixture,性能没
- 同一个 TargetPackage 的聚宽完整 package/plan/orders/fills 导出和本地 peer - 同一个 TargetPackage 的聚宽完整 package/plan/orders/fills 导出和本地 peer
- 同一个 TargetPackage 的授权 QMT built-in 与 qmttools 回测导出; - 同一个 TargetPackage 的授权 QMT built-in 与 qmttools 回测导出;
- 至少 20 个交易日的真实 QMT/broker shadow - Baseline 60 至少 20 个交易日、Production 80 同一冻结候选至少 60 个交易日
的真实 QMT/broker shadow
- 同一 canonical 输入的 L1—L5 差异报告。 - 同一 canonical 输入的 L1—L5 差异报告。
**状态**:部分完成;JoinQuant runtime smoke 已执行,但 strict comparison、 **状态**:部分完成;JoinQuant runtime smoke 已执行,但 strict comparison、
@@ -130,7 +131,8 @@ guarded TWAP/POV 和影子证据。
- 券商实际状态码映射与脱敏回调; - 券商实际状态码映射与脱敏回调;
- 实际 QMT query API 的失败/空列表合同探针; - 实际 QMT query API 的失败/空列表合同探针;
- durable scheduler/recovery daemon - durable scheduler/recovery daemon
- 20 个交易日 shadow、日终零未知账差和故障恢复演练; - Baseline 60 的 20 个交易日 shadowProduction 80 为同一冻结候选 60 日)、
日终零未知账差和故障恢复演练;
- 券商权限、频率、软件和程序化交易报告确认。 - 券商权限、频率、软件和程序化交易报告确认。
**状态**:仅有未认证的安全边界;不可视为 live-ready。 **状态**:仅有未认证的安全边界;不可视为 live-ready。
+76
View File
@@ -0,0 +1,76 @@
# Quant OS 独立仓迁移记录
迁移日期:2026-07-30
## 目标位置
- 本地:`/Users/zhangbiao/boat-workspace/Code/quant-os`
- 远端:`https://git.gomars.fun/boat/quant-os.git`
- 公共状态:`https://gomars.fun/quant-os/status/`
- 原仓:`https://git.gomars.fun/boat/quants-strategies.git`
## 历史保留方式
从原仓已提交 `master` 执行:
```text
git subtree split --prefix=quant-os
```
路径提交映射:
| 原仓提交 | 独立历史提交 | 内容 |
| --- | --- | --- |
| `e561efd` | `48c5f64` | A 股 60 分 Baseline 初始实现 |
| `f998f4d` | `e5911ca` | Tushare → Qlib 数据桥 |
| `692f6ef` | `00efeb7` | 公共状态控制台 |
SHA 改变是路径提取的正常结果;作者、时间、顺序和文件历史保留。
原工作树中尚未提交的 TargetPackage、五层链、release reachability 和真实
JoinQuant execution smoke 被叠加到独立分支,并形成单独 checkpoint
`919c64c`。原 `quants-strategies` 的 cash-layer 脏改没有进入新仓。
## 本地运行状态
以下目录被 Git 忽略,不进入远端;这是数据安全边界,不只是迁移便利:
- `data/`
- `artifacts/`
- `mlruns/`
- `.venv-*`
迁移时复制前三类并校验清单;只记录文件数、总字节和相对路径/hash,不把
数据内容或绝对源路径写入公开文档。旧 `.venv-qlib312` 接近 1 GB,脚本中包含原
绝对路径,不能把“目录搬过去”视为有效环境;新仓应按固定 requirements 重建。
旧环境在新仓完整验证前不删除。
清单与复核命令:
```bash
python3 tools/inventory_local_assets.py <old-project-root> \
--output /tmp/quant-os-local-assets.json
python3 tools/inventory_local_assets.py <new-project-root> \
--compare /tmp/quant-os-local-assets.json
```
清单不记录根目录,只记录 `data/``artifacts/``mlruns/` 的相对路径、字节数
和 SHA-256;遇到 symlink 会拒绝继续。
## 兼容政策
- `quant-os` / `quant_os` 是新产品入口;
- `quant60` CLI/import 和 V1 schemas 保留至少两个发布周期;
- `portable-momentum` 只保留 smoke/example 身份;
- 旧公共 URL 先保持 302 兼容跳转;稳定观察后再单独升级 308,不能直接 404;
- 原仓最终只保留独立项目指针,不复制代码。
## 完成条件
1. 独立仓 clean clone 通过 `make local`
2. generated bundle/status 无 drift
3. secret/绝对路径检查通过;
4. 新远端 commit 与本地 commit 相同;
5. 新公共 URL 返回正确独立仓快照;
6. 旧 URL 有兼容跳转;
7. 最后才从旧仓移除 `quant-os/` 和旧专属 CI。
+13 -2
View File
@@ -4,11 +4,18 @@ SMOKE_DIR := artifacts/local-smoke
RESEARCH_DIR := artifacts/research-smoke RESEARCH_DIR := artifacts/research-smoke
PARITY_REPORT := artifacts/mock-parity/report.json PARITY_REPORT := artifacts/mock-parity/report.json
.PHONY: test bundle smoke verify research research-verify parity parity-verify doctor status status-check notebook-check notebook-smoke local .PHONY: test architecture standard bundle smoke verify research research-verify parity parity-verify doctor secret-scan status status-check notebook-check notebook-smoke local
test: test:
PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) -m unittest discover -s tests -p '*.py' -q PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) -m unittest discover -s tests -p '*.py' -q
architecture:
PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) -m quant_os architecture check
standard:
PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) -m quant_os standard validate
PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) -m quant_os standard evaluate
bundle: bundle:
$(PYTHON) tools/bundle_platforms.py $(PYTHON) tools/bundle_platforms.py
@@ -31,8 +38,12 @@ parity-verify:
PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) tools/export_mock_parity.py verify $(PARITY_REPORT) PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) tools/export_mock_parity.py verify $(PARITY_REPORT)
doctor: doctor:
PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) -m quant_os doctor
PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) tools/capability_probe.py PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) tools/capability_probe.py
secret-scan:
$(PYTHON) tools/check_tracked_secrets.py
status: status:
$(PYTHON) tools/build_public_status.py $(PYTHON) tools/build_public_status.py
@@ -45,4 +56,4 @@ notebook-check:
notebook-smoke: notebook-smoke:
$(PYTHON) tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb --execute $(PYTHON) tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb --execute
local: test bundle smoke verify research research-verify parity parity-verify doctor status-check notebook-check notebook-smoke local: architecture standard test bundle smoke verify research research-verify parity parity-verify doctor secret-scan status-check notebook-check notebook-smoke
+49 -22
View File
@@ -1,13 +1,20 @@
# Quant OS:个人 A 股 60 分 Baseline 工程 # Quant OS:个人 A 股量化研究到受限生产的独立工程
Quant OS 是项目名;`quant60` 是第一版 A 股 Baseline 的 Python Quant OS 已从 `quants-strategies` 拆成独立项目。`quant-os` / `quant_os`
包、CLI namespace 与 artifact 协议前缀。 是长期 CLI/import`quant60` 是第一版 A 股 Baseline 的兼容包、CLI namespace
与 V1 artifact 协议前缀。60/80 是证据 profile,不再写进产品名。
本项目把 2026-07-22 的技术选型文章落成一个可执行、可审计的模块化单体: > 当前交易执行范围仅覆盖上交所、深交所(沪深)。北交所尚未接入,
本地与 Colab 可零账号运行工程 smoke;JQData 登录后可生成不可变快照并直接 > 所有北交所委托必须 `fail closed`;代码映射支持 `.BJ` / `.XBSE`
运行 PIT 本地回测/目标决策;聚宽、QMT 与 Qlib 有各自明确的运行入口 > 不代表具备北交所回测或实盘交易能力
> 当前状态仍是 `NOT_BASELINE_60`。本地五层 vertical slice、单测、 本项目正在把 2026-07-22 的技术选型实现,从 `quant60` legacy 内核迁移成
严格边界、可执行、可审计的模块化单体:本地与 Colab 可零账号运行工程
smoke;JQData 登录后可生成不可变快照并直接运行 PIT 本地回测/目标决策;
聚宽、QMT 与 Qlib 有各自明确的运行入口。
> 当前状态仍是 `NOT_BASELINE_60`80 分评估为
> `BLOCKED_FROM_LIMITED_LIVE`、0/100 已验证证据分。本地五层 vertical slice、单测、
> synthetic、fake harness、Qlib fixture、真实聚宽 hosted 动量 smoke,以及 > synthetic、fake harness、Qlib fixture、真实聚宽 hosted 动量 smoke,以及
> 单个 synthetic-research TargetPackage 的真实聚宽 **execution smoke** > 单个 synthetic-research TargetPackage 的真实聚宽 **execution smoke**
> 都不能代替授权真实数据 OOS、逐层同输入 parity、真实 QMT、券商影子盘、 > 都不能代替授权真实数据 OOS、逐层同输入 parity、真实 QMT、券商影子盘、
@@ -17,15 +24,30 @@ Quant OS 是项目名;`quant60` 是第一版 A 股 Baseline 的 Python
> [`docs/AUDIT_2026-07-25.md`](docs/AUDIT_2026-07-25.md) 与 > [`docs/AUDIT_2026-07-25.md`](docs/AUDIT_2026-07-25.md) 与
> [`gate_scorecard.json`](gate_scorecard.json)。 > [`gate_scorecard.json`](gate_scorecard.json)。
> 新依赖检查当前只覆盖 `src/quant_os/`;约 26k 行 legacy 仍位于
> `src/quant60/`、根级 `adapters/`、`platforms/` 与 `tools/`,按 vertical
> slice 迁移。`doctor.ok = true` 只表示独立仓必需文件和**新 namespace**
> 边界有效,不表示 legacy 已分层,也不表示达到 60/80。生产 trust provider /
> issuer registry 尚未实现,仓库不能自行签发正向 80 分资格。
先读:
- [80 分标准](docs/standards/QUANT_OS_80_STANDARD.md)
- [60 与 80 的差别](docs/standards/BASELINE_60_VS_PRODUCTION_80.md)
- [面向 80 的三平面架构](docs/architecture/ARCHITECTURE_80.md)
- [新架构上手](docs/getting-started/GETTING_STARTED_80.md)
- [独立仓迁移记录](MIGRATION.md)
## 在线公开验收面板 ## 在线公开验收面板
公开只读入口: 公开只读入口:
[`https://gomars.fun/quants-strategies/quant-os/status/index.html`](https://gomars.fun/quants-strategies/quant-os/status/index.html) [`https://gomars.fun/quant-os/status/`](https://gomars.fun/quant-os/status/)
页面从 `gate_scorecard.json`、平台矩阵、bundle manifest 和证据文档生成 页面从 Production 80 machine profile、`gate_scorecard.json`、平台矩阵、
脱敏的构建时快照,逐层展示数据、模型、回测平台、券商影子盘与 G1—G10 bundle manifest 和证据文档生成脱敏的构建时快照,逐层展示数据、模型、
缺口。它不是实时服务健康页,不发布本地行情、账户状态或运行 artifacts。 回测平台、券商影子盘与证据缺口。它不是实时服务健康页,不发布本地行情、
账户状态或运行 artifacts。
更新权威记录后必须重新生成并检查页面数据: 更新权威记录后必须重新生成并检查页面数据:
@@ -49,7 +71,7 @@ python3 tools/build_public_status.py --check
| QMT built-in/qmttools | 默认动量 smokeTargetPackage 模式为 Python 3.6 backtest-only 消费器 | Python 3.6 语法、TargetPackage/fake/qmttools 合约 | 需要已授权 QMT 客户端、历史数据权限与 TargetPackage 实跑导出 | | 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 环境的账户合同探针、恢复/对账和券商确认Baseline 60 至少 20 个交易日,Production 80 当前缺口为同一冻结候选至少 60 个交易日 |
`portable-momentum-v1` 只是当前真实跑过聚宽的跨引擎连通性 smoke,不是 `portable-momentum-v1` 只是当前真实跑过聚宽的跨引擎连通性 smoke,不是
60 分 Baseline。Baseline 的权威决策留在本地:冻结 Ridge model bundle 60 分 Baseline。Baseline 的权威决策留在本地:冻结 Ridge model bundle
@@ -68,13 +90,14 @@ make local
它依次执行: 它依次执行:
1. 全量单元/合约测试 1. 新 namespace 依赖边界和 80 分 profile 评估
2. 聚宽与 QMT 单文件 bundle 2. 全量单元/合约测试
3. synthetic execution smoke 与完整 manifest verifier 3. 聚宽与 QMT 单文件 bundle
4. synthetic research-to-target 与 research manifest verifier 4. synthetic execution smoke 与完整 manifest verifier
5. JoinQuant/QMT mock parity 报告及校验 5. synthetic research-to-target 与 research manifest verifier
6. capability probe 6. JoinQuant/QMT mock parity 报告及校验
7. Colab notebook 语法检查和逐 cell 本地执行。 7. capability probe
8. Colab notebook 语法检查和逐 cell 本地执行。
等价的脚本入口: 等价的脚本入口:
@@ -437,7 +460,9 @@ key。它必须稳定备份、限制访问且绝不
官方 query API 对“失败”和“空列表”的返回合同,并记录实际 官方 query API 对“失败”和“空列表”的返回合同,并记录实际
terminal/xtquant build、确认 `XtTrade.traded_amount` 成交金额语义与允许 terminal/xtquant build、确认 `XtTrade.traded_amount` 成交金额语义与允许
误差。当前 amount 只是被认证的 informational fact,不参与 target quantity 误差。当前 amount 只是被认证的 informational fact,不参与 target quantity
或 proposed delta。之后还要累计至少 20 个交易日、恢复演练和零未知账差。 或 proposed delta。之后还要先满足 Baseline 60 的至少 20 个交易日 shadow、
恢复演练和零未知账差;Production 80 当前要求同一冻结候选累计至少 60 个
交易日。
## Qlib 0.9.7 ## Qlib 0.9.7
@@ -553,7 +578,8 @@ real_platform_pass = false
gate_credit = [] gate_credit = []
``` ```
真实 G9 仍要求聚宽/QMT 导出与至少 20 个交易日 QMT 影子记录。完整层级、 Baseline 60 的真实 G9 仍要求聚宽/QMT 导出与至少 20 个交易日 QMT 影子记录
Production 80 当前要求同一冻结候选至少 60 个交易日。完整层级、
容差和 reason code 见 容差和 reason code 见
[`docs/CROSS_ENGINE_CONSISTENCY.md`](docs/CROSS_ENGINE_CONSISTENCY.md)。 [`docs/CROSS_ENGINE_CONSISTENCY.md`](docs/CROSS_ENGINE_CONSISTENCY.md)。
@@ -593,7 +619,8 @@ factor risk、CVXPY、TWAP/POV 或 Ridge 上游四层在 hosted 环境被调用
- 聚宽已有单包 TargetPackage execution 导出,但仍缺授权真实数据的多期 - 聚宽已有单包 TargetPackage execution 导出,但仍缺授权真实数据的多期
TargetPackage、本地/聚宽逐层同输入对账;QMT 尚无真实 peer 导出,QMT TargetPackage、本地/聚宽逐层同输入对账;QMT 尚无真实 peer 导出,QMT
历史 ST 权限正例、账户 query 合同、 历史 ST 权限正例、账户 query 合同、
20 日只读影子、回调/恢复/对账尚未形成真实证据; Baseline 60 所需 20 日只读影子、回调/恢复/对账尚未形成真实证据;
Production 80 还要求同一冻结候选累计至少 60 个交易日 shadow;
- 自动 scheduler、日终对账服务、监控告警与 kill-switch 演练未形成连续证据; - 自动 scheduler、日终对账服务、监控告警与 kill-switch 演练未形成连续证据;
- 实际券商程序化交易报告、权限、频率与软件要求尚未书面确认。 - 实际券商程序化交易报告、权限、频率与软件要求尚未书面确认。
+7
View File
@@ -0,0 +1,7 @@
# Public status application
The deployable static artifact currently remains in `status/` so the canonical
public URL stays `/quant-os/status/`. It is a build-time, non-secret evidence
snapshot, not a live health endpoint.
The next migration replaces Markdown parsing with a machine claims index.
+3 -2
View File
@@ -300,8 +300,9 @@ synthetic/fake 数据是合同测试,不是市场证据;Qlib synthetic fixtu
- 授权真实数据训练的冻结模型包和真实 OOS 证据; - 授权真实数据训练的冻结模型包和真实 OOS 证据;
- 真实数据校准的风险、冲击、容量和 TCA; - 真实数据校准的风险、冲击、容量和 TCA;
- 授权真实数据的多期 JoinQuant 导出、逐层本地对账与真实 QMT peer; - 授权真实数据的多期 JoinQuant 导出、逐层本地对账与真实 QMT peer;
- 券商状态映射、restart recovery daemon、20 日 shadow 与连续对账; - 券商状态映射、restart recovery daemon、连续对账,以及 Production 80
- 运维监控、kill-switch 演练和程序化交易合规确认。 要求的同一冻结候选 60 日 shadowBaseline 60 的前置门槛为 20 日);
- 运维监控、kill-switch 演练和券商程序化交易报告/核查确认。
所以 Quant OS 已具备本地五层 vertical slice 与 TargetPackage 平台边界, 所以 Quant OS 已具备本地五层 vertical slice 与 TargetPackage 平台边界,
但仍只是可继续填证据的候选骨架,不是已经达到整体 60 分的实盘系统。证据政策见 但仍只是可继续填证据的候选骨架,不是已经达到整体 60 分的实盘系统。证据政策见
+3 -1
View File
@@ -117,7 +117,9 @@ trueUniverse、Alpha、Portfolio、Risk 仍为 false,因此
smoke 作为线路先验,但仍要导出完整多期 identity/plan/orders/fills。 smoke 作为线路先验,但仍要导出完整多期 identity/plan/orders/fills。
5. 做 local/JoinQuant/QMT 逐层差异;任何 `UNEXPLAINED` 必须为零。当前无 5. 做 local/JoinQuant/QMT 逐层差异;任何 `UNEXPLAINED` 必须为零。当前无
QMT peer,所以 G9 仍未通过。 QMT peer,所以 G9 仍未通过。
6. 完成至少 20 个交易日 QMT 只读 shadow、重启/回调/对账和合规确认。 6. 完成 Baseline 60 所需的至少 20 个交易日 QMT 只读 shadow、重启/回调/
对账和券商程序化交易报告/核查确认。Production 80 当前要求同一冻结候选
至少 60 个交易日。
## 用户什么时候需要参与 ## 用户什么时候需要参与
+1 -1
View File
@@ -244,7 +244,7 @@ PYTHONPATH=src:. python -m platforms.qlib_runner \
| real JoinQuant export vs local canonical run | Yes,覆盖的 layers/dates | | real JoinQuant export vs local canonical run | Yes,覆盖的 layers/dates |
| licensed qmttools run with saved version/result | Yes,覆盖的 layers | | licensed qmttools run with saved version/result | Yes,覆盖的 layers |
| XtTrader shadow using real broker snapshot | Yespre-trade/order semantics | | XtTrader shadow using real broker snapshot | Yespre-trade/order semantics |
| ≥20 trading days QMT shadow, zero unexplained differences | 文章 G9 必需 | | ≥20 trading days QMT shadow, zero unexplained differences | Baseline 60 的 G9 必需;Production 80 另要求同一冻结候选 ≥60 日 |
真实 JoinQuant smoke 已运行;QMT 和 broker shadow 尚未运行,而且 真实 JoinQuant smoke 已运行;QMT 和 broker shadow 尚未运行,而且
JoinQuant 仍缺同输入导出与 local peer,所以 G9 继续为 `not_passed` JoinQuant 仍缺同输入导出与 local peer,所以 G9 继续为 `not_passed`
+3 -1
View File
@@ -68,6 +68,8 @@ still missing:
portable plan, orders and fills; portable plan, orders and fills;
- a local canonical run over the exact same frozen inputs; - a local canonical run over the exact same frozen inputs;
- layer-by-layer L1—L4 comparison with enumerated difference reasons; - layer-by-layer L1—L4 comparison with enumerated difference reasons;
- a real QMT peer run and at least 20 trading days of broker shadow evidence. - a real QMT peer run and the Baseline-60 minimum of 20 trading days of broker
shadow evidence; Production 80 requires at least 60 trading days for the same
frozen candidate.
Accordingly, `gate_scorecard.json` remains `NOT_BASELINE_60`. Accordingly, `gate_scorecard.json` remains `NOT_BASELINE_60`.
+1 -1
View File
@@ -16,7 +16,7 @@
| QMT local harness | built-in wrapper 合同 | Python ≥3.10,无账号 | Python 3.6 source、`ContextInfo` 回滚模拟、动量 smoke 与 TargetPackage 合同 | 不证明 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 客户端/历史数据 | 两种模式都硬限制 backtestTargetPackage 模式不重算上游四层;模块全局 `g` | 尚无 TargetPackage 真实 QMT run/export10% GUI 配置和执行时钟待取证 | | 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/restartBaseline 60 至少 20 个交易日,Production 80 当前缺口为同一冻结候选至少 60 个交易日not 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 |
| Qlib Alpha158/LightGBM | model fit + Recorder workflow | 同上 + LightGBM/MLflow stack | 真实 fixture 完成 model/Recorder;读取 portfolio report,保存表 hash、边界、重算指标与 artifact path | synthetic 两股票性能无意义;真实 provider/OOS 和冻结模型缺失 | | Qlib Alpha158/LightGBM | model fit + Recorder workflow | 同上 + LightGBM/MLflow stack | 真实 fixture 完成 model/Recorder;读取 portfolio report,保存表 hash、边界、重算指标与 artifact path | synthetic 两股票性能无意义;真实 provider/OOS 和冻结模型缺失 |
| 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` |
+10
View File
@@ -0,0 +1,10 @@
# Quant OS documentation map
- 80 分标准:[`standards/QUANT_OS_80_STANDARD.md`](standards/QUANT_OS_80_STANDARD.md)
- 60 与 80[`standards/BASELINE_60_VS_PRODUCTION_80.md`](standards/BASELINE_60_VS_PRODUCTION_80.md)
- 新架构:[`architecture/ARCHITECTURE_80.md`](architecture/ARCHITECTURE_80.md)
- 上手:[`getting-started/GETTING_STARTED_80.md`](getting-started/GETTING_STARTED_80.md)
- 独立仓 ADR[`adr/0001-independent-project-and-three-planes.md`](adr/0001-independent-project-and-three-planes.md)
- 当前实现架构:[`../docs/ARCHITECTURE.md`](ARCHITECTURE.md)
- 平台矩阵:[`PLATFORM_MATRIX.md`](PLATFORM_MATRIX.md)
- Tushare 数据:[`TUSHARE_LOCAL_DATA.md`](TUSHARE_LOCAL_DATA.md)
+1 -1
View File
@@ -111,7 +111,7 @@ Signal/Target/Order/Broker 合同。当前 Tushare adapter 只实现第一条链
从 Quant OS 根目录执行。镜像路径只放在当前 shell 环境变量中,不写入 Git: 从 Quant OS 根目录执行。镜像路径只放在当前 shell 环境变量中,不写入 Git:
```bash ```bash
export QUANT_OS_ROOT=/path/to/quants-strategies/quant-os export QUANT_OS_ROOT=/path/to/quant-os
export TUSHARE_MIRROR_ROOT=/path/to/tushare-mirror export TUSHARE_MIRROR_ROOT=/path/to/tushare-mirror
cd "$QUANT_OS_ROOT" cd "$QUANT_OS_ROOT"
@@ -0,0 +1,35 @@
# ADR-0001Quant OS 独立仓与三平面模块化单体
- 状态:Accepted
- 日期:2026-07-30
## 背景
Quant OS 原位于 `quants-strategies/quant-os`,把操作系统框架和具体策略仓库
混在一起;包名 `quant60` 又把成熟度写进产品身份。继续迭代到 80 会造成路径、
CI、发布 URL、import 和 artifact 身份持续破坏。
## 决策
1. Quant OS 迁为独立项目 `boat/quant-os`
2. `quant-os` / `quant_os` 是长期产品 CLI/import。
3. `baseline-60` / `production-80` 是 profile,不是新包。
4. 使用三平面模块化单体,专有 SDK 通过 adapter/runtime 隔离。
5. `quant60` 和 V1 artifact 至少保留两个兼容发布周期。
6. 迁仓和大模块重构分成独立提交;先证明行为等价,再按 vertical slice 拆分。
## 后果
正面:
- 策略仓与框架生命周期解耦;
- 80 分标准成为机器可验收控制面;
- QMT mutation authority 可以独立封闭;
- 历史证据和 Colab/平台入口有稳定产品名。
代价:
- 一段时间内同时存在 `quant_os``quant60`
- 根级 legacy adapters/platforms 仍需逐步迁移;
- 新旧公开 URL 需要并行和重定向;
- subtree split 会重写原路径提交 SHA,必须保存映射。
+167
View File
@@ -0,0 +1,167 @@
# Quant OS 面向 80 分的目标架构
状态:本轮已建立边界、长期 namespace、机器标准和兼容入口;大模块拆分按
vertical slice 继续进行,不声称已经完成 26k 行重写。
## 1. 架构选择
采用**严格边界的模块化单体 + 专有 runtime 代理**,不是微服务。
原因:
- 个人系统不需要承担微服务的网络、部署和分布式一致性成本;
- QMT、聚宽、Qlib、JQData 有互不兼容的 Python/SDK 运行时,必须物理隔离;
- 决策身份、证据链和本地重放需要单一仓库和明确的事务边界;
- 模块边界可以先在进程内强制,达到真实吞吐或隔离需求后再拆服务。
## 2. 三平面
```mermaid
flowchart LR
B["Composition root<br/>CLI · runtime wiring"] --> C
B --> X
C["Control plane<br/>RunSpec · orchestration · release policy"] --> A["Application use cases"]
A --> D["Data plane<br/>PIT → research → portfolio → execution"]
D --> P["Ports"]
X["Adapters / proprietary runtimes<br/>JQData · Qlib · JoinQuant · QMT · XtTrader"] --> P
D --> E["EvidenceSink port"]
C --> E
X --> E
E --> V["Evidence plane<br/>manifest · parity · scorecard · claims"]
```
硬规则:
- Control plane 只编排 RunSpec、状态和发布,不计算信号,不直接调用券商 SDK。
- Data plane 只消费不可变输入,产生 model、weight 和 execution intent;它不读
网页状态或 Gate。
- Evidence plane 只记录已经发生的事实,不得修改 target、order 或 broker fact。
- Adapter 向内实现 portcore 永远不 import `jqdatasdk``qlib``xtquant`
或 hosted 平台全局 API。
- `ReadBrokerPort``TradeBrokerPort` 分离。拥有查询权不等于拥有下单权。
- `composition` 是唯一同时依赖 application/control 与 adapters 的受检装配根;
adapter 只实现 port,不反向 import application。
## 3. 依赖方向
```text
foundation
contracts / ports
data | research | portfolio | execution
application
control
composition / CLI
adapters ──implements──> ports
evidence <── consumes immutable envelopes
composition ──wires──> application + control + adapters
```
新 namespace 的边界由 `quant_os.architecture.validate_architecture` 扫描 AST
执行。CI 会拒绝例如 `research -> execution``data -> control` 的倒置依赖,
也会拒绝新层绕回根级 `quant60``adapters``platforms``tools`
只有 `composition` 可以临时调用 compatibility 内核;每个 vertical slice
迁完后继续收窄。
## 4. 目录
```text
src/quant_os/
foundation/ # ID、双时钟、symbol、money
contracts/ # DTO、schema registry、版本迁移
ports/ # Data/Engine/ReadBroker/TradeBroker/EvidenceSink
data/ # PIT snapshot、catalog、quality
research/ # feature、split、model、evaluate
portfolio/ # risk、cost、capacity、optimizer
execution/ # intent、OMS、reconcile、kill switch
application/ # ingest/train/backtest/publish/shadow use cases
control/ # RunSpec、orchestrator、release policy
evidence/ # manifest、claim、parity、maturity
adapters/ # provider/platform/broker implementations
composition/ # 唯一受检进程装配根
runtimes/ # portable36、JoinQuant、QMT built-in、Colab
profiles/ # baseline-60、production-80
examples/ # 永不晋级为候选的 smoke 策略
status/ # 可发布静态验收快照
```
`quant_os` 是长期产品 namespace;成熟度只存在于 profile。当前 AST checker
只覆盖新 namespace(现为迁移壳层与新增控制代码),不会把约 26k 行 legacy
自动宣称为已分层。`quant60` 保留两个
兼容发布周期,继续读取/写入现有 V1 artifact,避免历史证据失效。
## 5. 当前代码如何迁移
| 当前模块 | 问题 | 目标位置 | 迁移条件 |
| --- | --- | --- | --- |
| `portable_core.py` | symbol、TargetPackage、动量、sizing、delta 混合 | foundation/contracts/portfolio/execution + example | golden vectors 先行 |
| `experiment.py` | fixture、研究、编排、证据混合 | research + application + evidence | real snapshot Ridge slice 接通 |
| `qmt_shadow.py` | 规划、签名、落盘、重放 3595 行 | execution/application/evidence | 真 QMT contract probe 后冻结 |
| `snapshot_pipeline.py` | 数据、决策、broker clock 混合并依赖 backtest 私有函数 | data + application + execution | 先建立公共合同 |
| `backtest.py` | engine 与 artifact publisher 混合 | local adapter + evidence | V1 manifest reader 保持兼容 |
| 平台 wrapper | JoinQuant/QMT 重复执行逻辑 | shared portable36 consumer + thin mapping | Python 3.6 golden test |
| status builder | 解析 Markdown 形成机器状态 | 只读 machine claims index | 标准/assessment 已先机器化 |
## 6. 唯一决策权威
当前最大结构问题不是文件太大,而是存在两个上游决策权威:
1. 真实 provider snapshot 仍走 `portable-momentum-v1`
2. Ridge 五层链只在 synthetic research 中产生 TargetPackage。
目标必须统一为:
```text
authorized immutable PIT snapshot
-> frozen feature/model
-> portfolio + independent post-risk
-> TargetPackage (weights, no account quantities)
-> JoinQuant/QMT thin execution consumer
```
hosted runtime 不再现场重算 Alpha。`portable-momentum` 永远留在 example/smoke
不能被默认 bundler 静默提升成生产候选。
跨引擎 strict peer 是 local、JoinQuant 与 QMT 的 L1—L5。Qlib 只比较其声明
参与的层级;只有显式导出同一冻结模型和组合实现时,才可提升比较范围。
Alpha158/LightGBM 研究工作流不能冒充 portable candidate 的 L3/L4 peer。
## 7. 分阶段路线
### 已在本轮落地
- 独立仓历史分支;
- `quant_os` 长期 CLI/import`quant60` 兼容;
- 七维 80 分 machine profile 和 fail-closed evaluator
- standard/profile canonical hash、可信 Baseline 前置、不可跳过的 77→canary→80
授权链及 `TEST_ONLY` 信任隔离;
- 新 namespace 层级和依赖检查;
- composition root 与 adapter 单向依赖;
- ReadBroker/TradeBroker/EvidenceSink port
- 独立根 CI
- 独立仓 clean-clone 测试修复;
- 架构、标准、迁移和上手文档。
### 下一阶段
1. 建立 golden vectors 和 V1/V2 schema registry
2. 把真实 snapshot 接入 Ridge 主链,消除双决策权威;
3. 先拆一个完整 vertical slice,而不是按文件批量搬家;
4. 平台 wrapper 收敛为同一个 portable consumer
5. status builder 改读统一 claims index,不再解析 Markdown
6. 取得真实 QMT 后接通 read-only contract probe、shadow 和对账。
### 依赖外部环境的阶段
- 完整授权 JQData/OOS
- 多期真实聚宽导出;
- 真 QMT/qmttools/XtTrader
- 60 个交易日 shadow
- 10 个交易日受限资金 canary;
- 生产 SLO、RPO/RTO、凭据轮换、券商程序化交易确认。
+299
View File
@@ -0,0 +1,299 @@
# Quant OS 新架构上手
这份文档只给出当前真实可运行入口。每一步都标明它需要什么、产生什么,以及
不能证明什么。
## 0. 先认清当前声明
```text
Baseline 60: NOT_BASELINE_60 (0/10 gates)
Production 80: BLOCKED_FROM_LIMITED_LIVE (0/100 verified evidence points)
Broker mutation: disabled
Investment value claim: false
```
这是 fail-closed 结论,不妨碍运行本地工程 smoke。
## 1. 本地路径和安装
独立仓:
```text
~/boat-workspace/Code/quant-os
```
推荐 Python 3.12
```bash
cd ~/boat-workspace/Code/quant-os
python3.12 -m venv .venv-core
source .venv-core/bin/activate
python -m pip install -e .
```
核心无第三方运行依赖。JQData、Qlib 和 QMT 必须使用各自独立环境。
当前 CLI 的 profile、配置和 bundle 是仓库资产,不嵌入 wheel。已安装的
`quant-os` 应在 checkout 内运行;从其他目录调用新命令时显式写:
```bash
quant-os --project-root ~/boat-workspace/Code/quant-os doctor
```
wheel 不是独立的数据/runtime 分发包。
## 2. 十分钟理解三平面
- Control plane:选择 RunSpec、编排 use case、做发布判定。
- Data plane:不可变 PIT 数据 → 特征/模型 → 权重 → execution intent。
- Evidence plane:记录 hash、来源、平台事实、差异和 Gate;不能修改决策。
平台和供应商只通过 adapter/port 进入。读券商与下单是两个独立接口。
## 3. 工程体检
状态:`[已实现|无需账号]`
```bash
quant-os doctor
quant-os architecture check
quant-os standard validate
quant-os standard evaluate
```
预期:
- `doctor.ok = true`:目录、入口、profile 和新 namespace 边界有效;
- `standard evaluate.ok = true`:评估过程有效;
- `qualified = false`:当前没有达到 80,属于正确结果。
`doctor.architecture.scope = new_namespace_only` 表示 AST checker 只覆盖
`src/quant_os/`legacy `quant60` 和根级 adapter/platform/tool 仍在迁移。
它证明工程和标准可验证,不证明全部旧代码已经分层、策略有效或平台可用。
没有 editable install 时可用:
```bash
PYTHONPATH=src:. python -m quant_os doctor
```
## 4. 无账号本地 demo
状态:`[已实现|无需账号]`
完整验证:
```bash
make local
```
较短路径:
```bash
quant-os smoke \
--config configs/baseline.json \
--output artifacts/local-smoke
quant-os verify-manifest artifacts/local-smoke/manifest.json
quant-os research-smoke --output artifacts/research-smoke
quant-os verify-research-manifest artifacts/research-smoke/manifest.json
```
`quant-os` 在这些命令上调用兼容的 `quant60` V1 内核。产物仍使用 V1 identity
因此历史 verifier 不会失效。
本地 demo 证明:
- 核心规则、ledger、manifest 和 hash 可重放;
- synthetic 五层链能够生成 ModelBundle 和 TargetPackage
- 平台 bundle 可生成并通过 fake contract。
它不证明:
- 真实市场收益;
- 真实 JQData 完整性;
- 真实 QMT 或券商执行;
- 60/80 达标。
## 5. Tushare 数据与 Qlib
状态:`[已实现到技术 smoke|真实镜像仍不完整]`
先阅读 [`../TUSHARE_LOCAL_DATA.md`](../TUSHARE_LOCAL_DATA.md)。当前盘点表明
completed Parquet 能校验并转换为 managed Qlib provider,但缺复权、真实
benchmark、历史成分、ST、停牌和涨跌停,不能进入生产 decision。
典型流程:
```bash
PYTHONPATH=src:. python tools/tushare_qlib.py inventory --help
PYTHONPATH=src:. python tools/tushare_qlib.py build --help
PYTHONPATH=src:. python tools/tushare_qlib.py verify --help
PYTHONPATH=src:. python -m platforms.qlib_runner --help
```
具体参数以 Tushare 文档和本机 inventory 为准。不要为了跑通而静默补缺失字段。
这条路径证明数据桥和 Qlib runtime 可用,不证明 Ridge Baseline、G1/G2 或
投资价值。
## 6. JQData → 本地 PIT
状态:`[已实现 adapter|需要授权账号]`
```bash
python3.12 -m venv .venv-jqdata
source .venv-jqdata/bin/activate
python -m pip install -r requirements/jqdata.txt
PYTHONPATH=src:. python tools/jqdata_snapshot.py \
--index 000905.XSHG \
--start 2024-01-02 \
--end 2024-12-31 \
--output data/snapshots/csi500-2024
quant-os snapshot-backtest \
--snapshot data/snapshots/csi500-2024/manifest.json \
--config configs/baseline.json \
--output artifacts/csi500-2024-backtest
```
凭据只通过无回显登录或本地 secret source 注入,不进入命令历史、Git、OB、
notebook 或 artifact。
重要限制:当前 snapshot backtest/decision 仍走
`portable-momentum-v1` compatibility path;它尚未接入真实 Ridge 五层权威。
## 7. Colab
状态:`[默认流程已实现|JQData/Qlib cell 需对应授权/运行时]`
打开 `notebooks/quant_os_colab.ipynb`。独立仓 clone 目标为:
```text
https://git.gomars.fun/boat/quant-os.git
```
默认 cell 不需要账号。`RUN_JQDATA=True``RUN_QLIB=True` 是显式可选。
Colab 禁止上传券商凭据、账户号、持仓/成交、QMT userdata 或未获许可的行情。
本地先验证 notebook
```bash
python tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb
python tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb --execute
```
## 8. 聚宽
状态:`[bundle 已实现|上传/导出需要登录聚宽]`
动量 smoke
```bash
python tools/bundle_platforms.py
```
TargetPackage consumer
```bash
python tools/bundle_platforms.py \
--target-package artifacts/research-smoke/target_package.json \
--output-dir dist/target-package
```
上传 `dist/target-package/joinquant_strategy.py` 后,必须保存平台运行参数、日志、
订单、成交和同输入 hash。当前已有一次 synthetic-research package 的真实
execution smoke,只证明 execution consumer,不计 G9/60/80。
## 9. QMT
状态:`[bundle/fake/read-only 框架已实现|真运行需要 Windows QMT 账号]`
当前可做:
```bash
python tools/bundle_platforms.py
PYTHONPATH=src:. python tools/export_mock_parity.py export \
--output artifacts/mock-parity/report.json
PYTHONPATH=src:. python tools/export_mock_parity.py verify \
artifacts/mock-parity/report.json
```
取得 QMT 后按顺序启用:
1. built-in 历史回测;
2. qmttools 回测导出;
3. XtTrader 只读 contract probe
4. broker snapshot 与 shadow plan
5. callback/restart/reconcile
6. 完成券商程序化交易报告确认后,才讨论 TradeBrokerPort。
账号、userdata 路径和账户标识不放进 Git/OB。当前没有任何 operator 可运行的
实盘下单命令,这是有意的安全边界。
## 10. 如何读 60/80 状态
```bash
quant-os standard evaluate
```
重点字段:
- `raw_score`:只有达到最低 evidence tier 且 hash 有效的控制项得分;
- `effective_score`:应用 60/canary cap 后的分数;
- `hard_gate_failures`:任何一项都阻断上线;
- `domain_floor_failures`:防止用文档/单一领域堆分;
- `prerequisites`60、QMT、券商确认、shadow、canary、P0/P1
- `standard_canonical_sha256`:本次评估解释所使用的精确标准版本;
- `pre_canary_authorization`:授权 ID/hash、有效期和 verifier/issuer provenance
- `pre_canary_authorization_valid_as_of_assessment`:授权在评估日是否仍可开始或继续
canary;过期后为 false,但不会否定期限内已完成的历史 canary chain
- `canary_authorization_chain_verified`:E4 canary 是否引用同一有效授权、处于合法
时间窗,并以实际最大资本/notional、标的、流量和 breach/stop 记录证明未突破
hard caps
- `verification_environment` / `test_only`:测试 verifier 永远不能冒充生产信任;
- `pre_canary_authorized`:只有生产 issuer registry 核验后才可能为真;
- `trusted_attestation_verified`:最终 promotion attestation 是否由信任边界核验;
- `qualified`:唯一可用于 promotion policy 的布尔值。
当前 CLI 没有注入 production trust provider / issuer registry,所以它只能
诚实评估当前全负状态,不能接受手工伪造的 `passed` 或自行颁发 60/80。
单元测试即使构造满分证据,也只能得到 `TEST_ONLY_STRUCTURAL_PASS`
production verifier 是明确 backlog。
公开页只展示脱敏静态快照,不是实时健康页:
```text
https://gomars.fun/quant-os/status/
```
## 11. 新策略如何接入
状态:`[目标 API|本轮只定义边界,尚未完成 Strategy SDK]`
策略最终应留在 `quants-strategies`,通过版本化 StrategySpec/port 进入 Quant OS。
策略只提供特征、预测或目标权重逻辑,不能自行:
- 读取券商 secret
- 调用 TradeBrokerPort
- 修改 evidence
- 绕过独立风险;
- 把 hosted 平台变成第二个 Alpha 权威。
当前新增策略仍按现有 `quant60` 实验接口工作,待 V2 StrategySpec 和 golden
vectors 完成后迁移。
## 12. 建议的学习顺序
1.`doctor``standard evaluate`
2.`make local`,打开 manifest 看 hash/identity
3. 对照 60/80 文档解释为什么 synthetic 不得分;
4. 阅读三平面架构和 ReadBroker/TradeBroker 拆分;
5. 盘点 Tushare provider,再跑 Qlib technical smoke
6. 获得 JQData 后生成真实 snapshot
7. 先做真实聚宽多期导出,再接真实 QMT;
8. 最后才累计 shadow/canary。
任何时候发现“代码可以下单,但证据/规则/券商确认还没到”,正确行为都是让
系统保持停机。
@@ -0,0 +1,57 @@
# Quant OS 60 分与 80 分的边界
## 一句话区别
- 60 分回答:同一策略研究链是否**可运行、可重放、可审计,并可进入只读 shadow**?
- 80 分回答:同一已报告版本是否已经在真实券商和受限资金 canary 下,满足
**本 profile 明示的账户、报告、风控、恢复与审计控制项**
80 不是替代 60;60 的 G1—G10 全部通过是 80 的前置。
## 对照表
| 责任 | 60 分 Baseline | 80 分受限生产 | 当前状态 |
| --- | --- | --- | --- |
| 系统定位 | research-to-shadow baseline;无资金发送权 | limited-production/canary-ready | 未达到 60 |
| 数据 | 有 PIT 合同、不可变快照和真实抽样重放 | 完整生产 PIT、许可、SLO、质量告警、恢复/回填 | fake JQData + 不完整 Tushare |
| 研究 | purge/embargo walk-forward、冻结 OOS 和成本后结果 | 再加真实容量、选择治理、registry、drift、champion/challenger | Ridge 只接 synthetic |
| 策略收益 | 可报告冻结 OOS,但不能用 synthetic 冒充 | 仍按预注册 mandate 判断,不设全局 Sharpe 门槛 | 无投资价值声明 |
| 风险 | A 股规则和事前/事后风险合同 | 独立且不可绕过的真实账户风控,失效即拒单 | 组件存在,未接真实券商 |
| 执行 | 本地 fill model、平台薄 wrapper、订单状态机合同 | 有效期规则包、真实 OMS、幂等/重连/重启、真实 TCA | 聚宽仅窄执行 smoke |
| 跨引擎 | local/JQ/QMT 同输入层级 parity,未知差异为零 | local/JQ/QMT 做 L1—L5 strict peerQlib 只在声明层级比较,另有订单/成交 difference ledger | mock parity,无真 QMT |
| QMT | bundle、qmttools、只读 shadow 入口 | 真账号 query/callback/reject/partial/cancel/reconnect/cross-day | fake contract |
| 对账 | 影子计划与 broker snapshot 有可重放合同 | 每个交易日现金、持仓、可卖、订单、成交 100% 对账 | 无真实连续序列 |
| Shadow | 至少 20 个交易日,验证研究到券商只读链 | 同一冻结候选至少 60 个交易日;重大变更重置 | 0 日真实 QMT |
| 实盘 | 明确禁止宣称可实盘 | 先以 E3 独立授权冻结资本/标的/流量/停机 hard caps;只有授权仍有效才能开始或继续 canary。E4 canary claim 反向引用授权,并逐项证明实际资本/notional、标的、流量未超限以及 breach 已触发 fail-closed stop,完成至少 10 个交易日;禁止自动扩容。已在期限内完成的历史 canary 可于到期后晋级复核 | 未授权 |
| 运维 | runbook、停机条件、恢复设计 | 监控告警、P0/P1、RPO=0、交易窗口 RTO≤15 分钟及故障演练 | 无生产演练 |
| 发布 | 可复现 manifest、schema 和 bundle hash | 原子晋级、审批、签名、依赖清单、回滚演练 | 独立 CI 正在建立 |
| 安全 | 凭据不入 Git/OB/artifact,默认关闭券商 mutation | secret store、最小权限、轮换/撤销、日志脱敏、供应链治理 | 仅仓库级边界 |
| 合规 | 明确程序化交易义务和需券商确认 | 已完成首次报告,实际版本与报告一致,重大变更受控 | 无券商确认 |
| 资本权限 | `NONE` | 小额、单策略、单账户、有限标的且有 hard cap | `NONE` |
| 达标标签 | `BASELINE_60_VERIFIED` | `PRE_CANARY_AUTHORIZED``LIMITED_LIVE_VERIFIED` | `NOT_BASELINE_60` |
80 分的 21 个生产 Hard Gate 权重合计正好 80,缺一项都不能用软项补偿。额外
20 分描述数据冗余、模型选择治理、真实 TCA 和未来监管模式等成熟度余量。
所有正向 evidence 都必须绑定同一 subject、standard/profile canonical hash
并经过外部可信 verifier60 分也不能靠 scorecard 手填。当前 production
issuer registry 尚未实现,所以当前只能得到负向、fail-closed 结论;测试
verifier 最多得到 `TEST_ONLY_*`,不会得到正式 60/80。
## 不能混淆的三类分数
1. **系统成熟度**:本标准 0—100,关心数据、执行、运维、合规。
2. **策略证据**:按预注册 mandate 判断 OOS、容量、风险和 canary,不用基础设施
分数掩盖没有 Alpha。
3. **实际收益**:只来自真实账户或具备适用授权、版本冻结的研究报告,不是系统
成熟度分数。
一个工程完美但没有有效策略的系统,可以有很高的系统成熟度,但不能发布一个
没有通过策略 mandate 的 model;一个回测收益很好但缺少风控、券商确认和恢复
能力的策略,同样不能上线。
## 为什么 80 仍不是“机构满分”
80 面向个人自有资金的受限生产。完整机构化还需要组织隔离、独立复核、多账户/
多策略资本分配、法务与合规职能、供应商管理、审计留存、业务连续性、人员权限
和可能的持牌/私募义务。未来进入外部资金模式时必须切换独立的
`REGULATED_MODE`,不能沿用个人 profile。
+220
View File
@@ -0,0 +1,220 @@
# Quant OS 80 分受限生产验证标准
基准日:2026-07-30
机器权威:[`profiles/production-80/standard.json`](../../profiles/production-80/standard.json)
当前评估:[`profiles/production-80/current-assessment.json`](../../profiles/production-80/current-assessment.json)
## 结论先说
80 分不是“代码功能比 60 分多一点”,而是系统责任发生了变化:
> 60 分证明系统能在受控数据和研究/影子环境中被复现;80 分证明同一个已报告、
> 已版本化、可审计的系统,已经在真实券商链路下完成受限资金验证,并且故障时
> 会自动停,而不是继续错。
当前 Quant OS 为 `NOT_BASELINE_60`80 分机器评估是
`BLOCKED_FROM_LIMITED_LIVE`,已验证得分为 0/100。这不等于“没有代码”,而是
目前的代码、synthetic、mock 和单次聚宽执行 smoke 没有达到本标准规定的
E2—E4 外部证据等级。当前 production trust provider / issuer registry 尚未
实现,因此 evaluator 能验证 profile、subject、claim envelope 与 hash 结构,
但不能自行颁发正向 80 分资格。
80 分代表个人自有资金系统的**受限生产已验证**。它不代表:
- 有超额收益或任何投资回报保证;
- 获得机构牌照;
- 可以管理第三方资金,或接受他人委托并代其决策、执行交易;
- 券商必然开放程序化交易权限;
- 法律意见。
## 1. 判定公式
状态分两步,避免“尚未实盘,却要先拿实盘证据”的循环:
1. `PRE_CANARY_AUTHORIZED`:除 `D6-CANARY` 外的 20 个 Hard Gate 全部通过,
得分至少 77,60 分、真实 QMT、券商确认、60 日 shadow、P0/P1 和同一冻结
subject 全部满足,并取得独立的 E3 可信 canary 授权。该不可变授权绑定标准
hash、pre-canary projection、账户/策略、有效期、资本/notional、标的白名单、
“申报+撤单”流量和停机条件;只有评估日仍在授权有效期内,状态才能保持
`PRE_CANARY_AUTHORIZED``CANARY_RUNNING`。它不是 E4 实盘结果,也不是
常规实盘许可。
2. `LIMITED_LIVE_VERIFIED`:完成至少 10 个交易日 canary 后,21 个 Hard Gate
全部通过,七个维度**合计**证据得分不低于 80/100,并取得绑定整个 assessment
hash 的可信 E4 promotion attestation。D6-CANARY 的 E4 claim 必须反向引用
前述授权 ID/hash,并证明 `authorized_at < canary window <= expires_on`
claim 还必须记录 canary 实际最大资本、gross notional、标的、每秒/每日
“申报+撤单”流量、是否自动扩容,以及 breach 与 fail-closed stop event 的
逐项关联。任一实际值超过授权即不得通过。若 canary 已在授权到期前合法完成,
后续 E4 promotion 可以在授权到期后复核这段历史链;到期不能倒推抹除已经发生
的受控观察,但也不能继续开放新的 canary 交易。
完整 80 分必须同时满足:
1. 七个维度合计证据得分不低于 80/100;
2. 60 分 G1—G10 全部通过;
3. 所有 Hard Gate 通过;
4. 每个维度至少获得该维度 60% 的分数;
5. 同一冻结候选累计至少 60 个交易日真实 QMT shadow
6. 受限资金 canary 至少 10 个交易日;
7. 没有未关闭 P0/P1
8. 已取得券商程序化交易报告/核查确认,部署版本与报告事实一致;
9. 已有真实 QMT 运行证据;
10. assessment、每份 evidence claim 和最终 attestation 都绑定相同的
candidate、release、model、dataset、账户 pseudonym 与 material-change
epoch,以及同一个 canonical `standard.json` SHA-256Baseline 60 还必须
引用可信 evaluator 的签名结果和 canonical profile hash。
机器 profile 中 21 个 Hard Gate 的权重恰好合计 80;其余 20 分是数据冗余、
champion/challenger、真实 TCA 与未来监管模式等成熟度余量。换句话说,80
不能靠软项补偿任何生产核心门;81—100 表示更高的冗余和治理深度。
不可补偿规则:
- 60 分未通过,最终分封顶 59
- 没有 canary,最终分封顶 79;
- 任一 Hard Gate 失败,即使原始分超过 80,也不得上线;
- 代码、文档、mock、fixture 和单元测试不能替代真实数据、真实平台、真实券商
或持续运行证据;
- 不允许人工把 `not_passed` 改成 `passed`。通过项必须提供仓库相对路径、
证据等级、SHA-256、绑定同一 subject 的 claim envelope,并由配置在外部
trust provider 中的 verifier 核验;最终 attestation 还必须绑定整个
assessment 的 canonical hash。
- canary 不能跳过 77 分授权:授权使用独立 E3 authoritycanary observation
使用 E4,并以授权 hash、评估日有效期、时间窗和“授权上限 vs 实际观测”
hard caps 形成不可断开的链;风险、数据、规则或对账 breach 必须引用同窗
stop event,且该 stop 明确停止交易、禁止自动恢复。
- 当前仓库没有生产 verifier 或信任根;单独编辑 JSON、复用无关文件或拿
`standard.json` 冒充 E4 都不能产生正向资格。测试 verifier 只能产生
`TEST_ONLY_PRE_CANARY_PASS` / `TEST_ONLY_STRUCTURAL_PASS``qualified`
仍为 false。
cap 用于分数呈现,Hard Gate/prerequisite 用于资格阻断,二者是纵深防御。
这些时长、维度和内部限额是 Quant OS 的工程标准,不是监管机关原文。
## 2. 证据等级
| 等级 | 定义 | 可证明什么 | 不能证明什么 |
| --- | --- | --- | --- |
| E0 | 口头结论、设计文档、源文件存在 | 意图和设计边界 | 代码可运行、市场有效、平台有效 |
| E1 | 确定性本地测试、synthetic、fixture、mock | 算法/合同/故障路径在受控环境成立 | 真实数据、平台、券商和收益 |
| E2 | 授权真实数据、冻结 OOS,或外部权威原始证据 | 数据/研究可重放,或规则/权属来源可核验 | 券商执行、持续运行 |
| E3 | 真实平台/券商 shadow,或绑定生产环境的运维、数据、安全、合规控制观察/独立 canary 授权 | 真实平台、账户状态、生产控制链路或受限实盘授权边界 | 已发生的资金实盘行为 |
| E4 | 受限资金、受控 live canary | 小范围真实生产行为 | 自动扩容、机构级规模或投资收益 |
每个控制项声明最低证据等级。更高等级可以覆盖更低等级,但 claim 的 control、
subject、evidence type、authority、时点和来源 hash 必须满足该项 acceptance
不能拿一份无关 E4 文件替代数据或研究证据。当前实现已经强制 control/subject/
standard/profile hash、pre-authorization chain 和带 verifier/issuer/key/policy
provenance 的验证决定;生产 issuer registry、允许的 evidence type/
authority/freshness policy 仍是下一阶段交付,因此当前不能颁发正向 60/80。
## 3. 七个评分维度
| 维度 | 分值 | 核心问题 | 关键 Hard Gate |
| --- | ---: | --- | --- |
| D1 数据与 PIT 真值 | 15 | 决策日看到的是否真是当时可得数据,数据是否有许可、时效和恢复能力 | 完整 PIT、许可/lineage/数据 SLO |
| D2 研究与模型治理 | 15 | 是否在真实长样本 OOS 下冻结,模型是否可追责、过期和回滚 | OOS、成本/容量、Model Registry |
| D3 跨引擎一致性 | 10 | 本地、聚宽、QMT strict peer 是否一致;Qlib 是否只在声明层级比较 | local/JQ/QMT L1—L5 identity、difference ledger |
| D4 执行与独立风控 | 15 | 策略能否绕过验资验券、规则、OMS 和 kill-switch | 独立风控、2026 规则包、幂等 OMS |
| D5 真实 QMT 与券商真值 | 15 | 查询、回报、重连、持仓、可卖、订单和成交是否由券商事实驱动 | 真 QMT、逐日 100% 对账、可重放证据 |
| D6 运维、灾备与审计 | 15 | 是否经历足够长的 shadow/canary,故障能否恢复 | 60 日 shadow、10 日 canary、RPO/RTO |
| D7 合规与安全 | 15 | 是否先报告后交易、账户和业务符合本 profile 边界、系统与报告一致、凭据受控 | 个人自营范围、券商确认、异常交易防线、安全 |
25 个控制项、权重和 acceptance 以机器 profile 为准。文档只解释标准,不替代
profile。
## 4. 当前差距
当前评估全部保持 `not_passed`,主要原因如下:
- G1—G10 仍是 0/10,未满足 80 分前置;
- Ridge 五层链的上游研究输入仍是 synthetic;
- JQData 真实完整 PIT 长样本未进入冻结模型和 TargetPackage tape
- 聚宽只有一次 synthetic-research TargetPackage execution consumer 观察;
- QMT 只有语法、fake contract 与只读 shadow 框架,没有真实账号证据;
- 没有 60 个交易日真实 shadow;
- 没有受限资金 canary
- 没有券商程序化交易报告/核查确认;
- 2026-07-06 生效(含部分继续暂缓实施条款)的交易规则尚未形成状态化规则包;
- 没有生产数据 SLO、真实 TCA、持续监控、RPO/RTO 演练、签名发布和凭据轮换证据。
- 没有 production trust provider、issuer registry 和可验证的 promotion
attestation,因此 evaluator 即使未来看到手工 `passed` 也不会自行授予资格。
因此,本轮交付只建立“能客观判断什么时候达到 80”的标准和架构,不声称已经
达到 80。
## 5. 监管硬事实与内部工程门槛
下面只概括与系统设计直接相关的规则事实。券商协议和后续有效规则可能更严格,
实盘以券商书面确认及当时有效规则为准。
### 5.1 规则事实
1. 《证券法》第 45 条将程序自动生成或下达交易指令纳入程序化交易,要求符合
证监会规定并向交易所报告,不得影响交易系统安全或正常交易秩序。
2. 《证券市场程序化交易管理规定(试行)》自 2024-10-08 实施。客户必须向
券商报告账户、资金/杠杆、策略、指令执行、最高申报速率、日最高申报笔数、
软件名称/版本/开发主体和联系人等;券商核查确认后才能开始程序化交易。
3. 沪、深、北程序化交易实施细则自 2025-07-07 施行,个人投资者明确纳入。
资金、杠杆、联系人、策略、速率、软件或停止交易等重大变化需要按规定变更
报告,实际交易行为会与报告事实比对。
4. 交易所重点监控瞬时申报速率异常、频繁瞬时撤单、频繁拉抬打压和短时间大额
成交。低于高频定义不等于监管安全。
5. 单账户申报加撤单最高达到 300 笔/秒,或单日达到 20,000 笔,属于高频交易。
这是高频分类阈值,不是低于阈值即可免责的安全港。
6. 沪深《交易规则(2026 年修订)》自 2026-07-06 施行,但上交所通知同时
明确部分既有条文继续暂缓实施。规则生效不等于每一条款均已启用;系统不能
继续依赖旧硬编码,也必须建模 `effective/suspended/resumed/repealed` 状态。
7. 对实施细则列明情形,会员应当按照委托协议等约定采取拒绝程序化交易委托、
撤销相关申报等措施;具体适用仍以券商协议和当时有效规则为准。
8. 突发故障或重大差错时,系统必须具备暂停、撤单、错误处理和应急处置能力。
### 5.2 Quant OS 内部门槛
- 账户模式固定为 `PERSONAL_PROPRIETARY`。出现第三方资金、接受他人委托并代其
决策或执行交易、向他人提供收费投资顾问服务,或与他人共享交易控制权时,
系统 fail closed,转入单独的法律/合规适用性评估。仅购买或阅读第三方研究
信息本身不等于代客指令。
- 没有券商确认、实际委托协议和版本一致性证据,TradeBrokerPort 保持禁用。
- machine profile 的日频 Baseline 默认上限按“Quant OS 发出的全部委托申报
尝试 + 撤单请求”合计,每账户及实际控制账户组均不超过 10 条/秒、
1,000 条/交易日。日撤单率 = 策略主动撤单请求 / 已接受委托申报;拒单不进
分母,部分成交后主动撤单进分子,交易所自动撤单不进分子。达到 30% 预警并
人工复核;更严规则优先。这是内部限额,不是监管安全港。
- 当前交易能力范围明确为沪深;北交所为 `excluded/fail_closed`。规则包按市场、
板块、证券类型及发布、生效、暂缓、恢复、废止日期版本化。规则未知、过期、
暂缓或不完整时拒单。
- Colab 只允许上传供应商许可范围内的脱敏研究数据,绝不上传券商凭据、账户号、
持仓、成交或个人敏感信息。
## 6. 官方来源
- [中华人民共和国证券法(全国人大)](https://www.npc.gov.cn/c2/c30834/201912/t20191231_304436.html)
- [证券市场程序化交易管理规定(试行)(证监会 PDF)](https://www.csrc.gov.cn/csrc/c101954/c7480579/7480579/files/%E9%99%84%E4%BB%B61%EF%BC%9A%E8%AF%81%E5%88%B8%E5%B8%82%E5%9C%BA%E7%A8%8B%E5%BA%8F%E5%8C%96%E4%BA%A4%E6%98%93%E7%AE%A1%E7%90%86%E8%A7%84%E5%AE%9A%EF%BC%88%E8%AF%95%E8%A1%8C%EF%BC%89.pdf)
- [上交所程序化交易管理实施细则](https://www.sse.com.cn/lawandrules/sselawsrules2025/trade/universal/c/c_20250612_10781696.shtml)
- [深交所程序化交易管理实施细则通知](https://www.szse.cn/lawrules/rule/allrules/bussiness/t20250403_612770.html)
- [北交所程序化交易管理实施细则](https://www.bse.cn/jygl_list/200025383.html)
- [上交所交易规则(2026 年修订)](https://www.sse.com.cn/lawandrules/sselawsrules2025/stocks/exchange/c/c_20260424_10816482.shtml)
- [深交所交易规则(2026 年修订)](https://www.szse.cn/lawrules/rule/trade/current/t20260424_620190.html)
- [程序化交易委托协议示范文本(中证协)](https://www.sac.net.cn/zlgl/zlgz/202512/t20251231_69956.html)
- [JR/T 0303—2024 投资研究时序数据参考模型](https://www.csrc.gov.cn/csrc/c100028/c7483073/content.shtml)
- [个人信息保护法(全国人大)](https://www.npc.gov.cn/npc/c2/c30834/202108/t20210820_313088.html)
如果未来管理外部资金,个人 profile 立即失效。量化私募还涉及系统/数据安全、
策略上线、防自成交/反向交易、超限控制以及长期保存等要求。参见
[私募证券投资基金运作指引(中基协 PDF)](https://www.amac.org.cn/xwfb/xhyw/202304/P020231126355219356728.pdf)。
## 7. 机器验收
```bash
PYTHONPATH=src:. python -m quant_os standard validate
PYTHONPATH=src:. python -m quant_os standard evaluate
```
第一条验证标准本身权重、控制项和证据等级是否自洽;第二条重新评估当前证据。
评估成功运行只表示“当前结构与负向结论有效”,不表示达到 80。必须看
`qualification_state``qualified``pre_canary_authorization`
`canary_authorization_chain_verified``standard_canonical_sha256`
`verification_environment``trusted_attestation_verified``hard_gate_failures`
`domain_floor_failures``prerequisites`。当前 CLI 不注入生产 verifier
这是明确的未完成项,而不是允许手工绕过的入口。
@@ -0,0 +1,5 @@
# portable-momentum smoke
`portable-momentum-v1` is a connectivity and contract example. It must never be
promoted as the Ridge Baseline or as investment evidence. The default bundler
compatibility mode will move here after golden-vector parity is frozen.
+5 -5
View File
@@ -1,7 +1,7 @@
{ {
"schema_version": "1.0", "schema_version": "1.0",
"system": "Quant OS / quant60 baseline kernel", "system": "Quant OS / quant60 baseline kernel",
"audit_as_of": "2026-07-26", "audit_as_of": "2026-07-30",
"claim_policy": { "claim_policy": {
"baseline_threshold": 60, "baseline_threshold": 60,
"all_gates_required": true, "all_gates_required": true,
@@ -35,7 +35,7 @@
"status": "not_scored", "status": "not_scored",
"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 未通过。", "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 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 independent-repository standard-library suite completed 315 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 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.", "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 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.",
@@ -58,8 +58,8 @@
"authorized JQData market snapshot, licence/lineage record and a frozen provider-data candidate tape", "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", "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 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, and callback/restart evidence; Baseline 60 requires at least 20 trading days of shadow, while the current Production 80 gap requires at least 60 trading days for the same frozen candidate",
"continuous reconciliation, recovery drills and broker compliance confirmation" "continuous reconciliation, recovery drills and broker programmatic-trading report/verification confirmation"
] ]
}, },
"gates": [ "gates": [
@@ -167,7 +167,7 @@
"required_evidence": [ "required_evidence": [
"真实平台跨引擎 manifest", "真实平台跨引擎 manifest",
"逐层 signal/target/order 差异", "逐层 signal/target/order 差异",
"QMT 至少 20 个交易日影子记录" "Baseline 60 所需的 QMT 至少 20 个交易日影子记录Production 80 另要求同一冻结候选至少 60 个交易日"
], ],
"evidence": [] "evidence": []
}, },
+14 -5
View File
@@ -45,8 +45,9 @@
"import shutil\n", "import shutil\n",
"import subprocess\n", "import subprocess\n",
"import sys\n", "import sys\n",
"import tempfile\n",
"\n", "\n",
"REPO_URL = \"https://git.gomars.fun/boat/quants-strategies.git\"\n", "REPO_URL = \"https://git.gomars.fun/boat/quant-os.git\"\n",
"REPO_BRANCH = \"master\"\n", "REPO_BRANCH = \"master\"\n",
"\n", "\n",
"def run(command, *, env=None):\n", "def run(command, *, env=None):\n",
@@ -54,19 +55,23 @@
" subprocess.run(command, check=True, env=env)\n", " subprocess.run(command, check=True, env=env)\n",
"\n", "\n",
"cwd = Path.cwd().resolve()\n", "cwd = Path.cwd().resolve()\n",
"if cwd.name == \"quant-os\" and (cwd / \"pyproject.toml\").is_file():\n", "if (cwd / \"pyproject.toml\").is_file() and (cwd / \"src\" / \"quant60\").is_dir():\n",
" project = cwd\n", " project = cwd\n",
"elif (cwd / \"quant-os\" / \"pyproject.toml\").is_file():\n", "elif (cwd / \"quant-os\" / \"pyproject.toml\").is_file():\n",
" project = cwd / \"quant-os\"\n", " project = cwd / \"quant-os\"\n",
"else:\n", "else:\n",
" checkout = Path(\"/content/quants-strategies\")\n", " checkout = Path(\"/content/quant-os\")\n",
" if not checkout.exists():\n", " if not checkout.exists():\n",
" run([\"git\", \"clone\", \"--depth\", \"1\", \"--branch\", REPO_BRANCH, REPO_URL, str(checkout)])\n", " run([\"git\", \"clone\", \"--depth\", \"1\", \"--branch\", REPO_BRANCH, REPO_URL, str(checkout)])\n",
" project = checkout / \"quant-os\"\n", " project = checkout\n",
"\n", "\n",
"if not (project / \"pyproject.toml\").is_file():\n", "if not (project / \"pyproject.toml\").is_file():\n",
" raise RuntimeError(f\"Quant OS project not found at {project}\")\n", " raise RuntimeError(f\"Quant OS project not found at {project}\")\n",
"os.chdir(project)\n", "os.chdir(project)\n",
"cache_root = Path(tempfile.gettempdir()) / \"quant-os-notebook-cache\"\n",
"cache_root.mkdir(parents=True, exist_ok=True)\n",
"os.environ.setdefault(\"MPLCONFIGDIR\", str(cache_root / \"matplotlib\"))\n",
"os.environ.setdefault(\"XDG_CACHE_HOME\", str(cache_root / \"xdg\"))\n",
"print({\"project\": str(project), \"python\": sys.version})" "print({\"project\": str(project), \"python\": sys.version})"
] ]
}, },
@@ -114,7 +119,11 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {}, "metadata": {
"tags": [
"optional"
]
},
"outputs": [], "outputs": [],
"source": [ "source": [
"equity = json.loads(Path(\"artifacts/colab-smoke/equity.json\").read_text(encoding=\"utf-8\"))\n", "equity = json.loads(Path(\"artifacts/colab-smoke/equity.json\").read_text(encoding=\"utf-8\"))\n",
+59
View File
@@ -0,0 +1,59 @@
# Quant OS public status operations
The repository is the source of truth for the public status page. A deployment
contains exactly:
```text
/srv/www/quant-os/
releases/<git-sha>/
index.html
status-data.js
status -> releases/<git-sha>
```
Deployment invariants:
1. run `python3 tools/build_public_status.py --check` and the full local suite;
2. deploy from a clean, pushed commit and name the release with that full SHA;
3. acquire the web-root deployment lock and copy into a same-filesystem
`.incoming-<sha>` directory;
4. require exactly two regular, non-symlink payload files and compare local and
staged SHA-256 for both;
5. atomically rename the verified incoming directory to the immutable release;
6. create a temporary relative symlink and activate it with one atomic rename;
7. install [`nginx/quant-os-static.conf`](nginx/quant-os-static.conf), run
`nginx -t`, then reload;
8. verify the canonical page, generated JavaScript, cache header and all legacy
redirects from a public client;
9. keep the previous release for rollback.
The server-side deploy and activation are executable and fail closed:
```bash
ops/deploy_static_status.sh <full-git-sha> <staged-status-dir>
ops/activate_static_status.sh <previous-full-git-sha>
```
The first command rejects a symlinked source root, symlink payloads,
directories and any third source entry,
holds `.status-deploy.lock` across staging and activation, verifies both
SHA-256 values, atomically promotes `.incoming-<sha>`, and refuses to overwrite
an existing release. Promoted payload files and their release directory are
owner read-only. The second command is the rollback/activation primitive;
when called directly it takes the same lock and switches only a relative
symlink. A stale lock or incoming directory means a prior process may have
crashed: inspect it and confirm no deploy is running before removing it.
Nginx is deliberately two-stage:
1. include `nginx/quant-os-static.conf` in the existing HTTPS server block,
run `nginx -t`, reload, and verify `/quant-os/status/`;
2. only then include `nginx/quant-os-legacy-redirects-302.conf`, preserving the
existing broad `/quants-strategies/` route for other projects;
3. after an observation period and a separate production change, replace the
302 include with `quant-os-legacy-redirects-308.conf`.
Rollback only changes the `status` symlink to the previously verified release
and reloads no application state. The page is a static, redacted build-time
snapshot; it must never contain credentials, local paths, market data, account
identifiers, positions or fills.
+109
View File
@@ -0,0 +1,109 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ "$#" -lt 1 || "$#" -gt 2 ]]; then
echo "usage: $0 <40-char-git-sha> [web-root]" >&2
exit 2
fi
release_sha="$1"
web_root="${2:-/srv/www/quant-os}"
if [[ ! "${release_sha}" =~ ^[0-9a-f]{40}$ ]]; then
echo "release SHA must be a 40-character lowercase Git SHA" >&2
exit 2
fi
if [[ "${web_root}" != /* || "${web_root}" == "/" ]]; then
echo "web root must be an explicit absolute non-root path" >&2
exit 2
fi
if [[ ! -d "${web_root}" ]]; then
echo "web root does not exist: ${web_root}" >&2
exit 1
fi
lock_dir="${web_root}/.status-deploy.lock"
lock_acquired=false
next_link=""
next_link_created=false
cleanup() {
exit_status="$?"
if [[ "${next_link_created}" == "true" && -L "${next_link}" ]]; then
rm -f "${next_link}"
fi
if [[ "${lock_acquired}" == "true" ]]; then
rmdir "${lock_dir}" 2>/dev/null || true
fi
exit "${exit_status}"
}
trap cleanup EXIT HUP INT TERM
if [[ "${QUANT_OS_STATUS_DEPLOY_LOCK_HELD:-0}" == "1" ]]; then
if [[ ! -d "${lock_dir}" || -L "${lock_dir}" ]]; then
echo "caller asserted a deployment lock that is not held" >&2
exit 1
fi
else
if ! mkdir "${lock_dir}" 2>/dev/null; then
echo "another status deployment or activation holds ${lock_dir}" >&2
exit 1
fi
lock_acquired=true
fi
release_dir="${web_root}/releases/${release_sha}"
if [[ ! -d "${release_dir}" || -L "${release_dir}" ]]; then
echo "release must be a regular directory: ${release_dir}" >&2
exit 1
fi
release_entries=()
while IFS= read -r -d '' entry; do
release_entries+=("${entry}")
done < <(find "${release_dir}" -mindepth 1 -maxdepth 1 -print0 2>/dev/null)
if [[ "${#release_entries[@]}" != "2" ]]; then
echo "release must contain exactly index.html and status-data.js" >&2
exit 1
fi
for required in index.html status-data.js; do
if [[ ! -f "${release_dir}/${required}" || -L "${release_dir}/${required}" ]]; then
echo "release must contain a regular non-symlink ${required}" >&2
exit 1
fi
done
for entry in "${release_entries[@]}"; do
case "$(basename "${entry}")" in
index.html|status-data.js)
;;
*)
echo "unexpected release entry: $(basename "${entry}")" >&2
exit 1
;;
esac
done
next_link="${web_root}/.status-next-${release_sha}"
if [[ -e "${next_link}" || -L "${next_link}" ]]; then
echo "temporary activation link already exists: ${next_link}" >&2
exit 1
fi
ln -s "releases/${release_sha}" "${next_link}"
next_link_created=true
if [[ -e "${web_root}/status" && ! -L "${web_root}/status" ]]; then
echo "active status path exists and is not a symlink" >&2
exit 1
fi
if mv --version >/dev/null 2>&1; then
mv -Tf "${next_link}" "${web_root}/status"
else
mv -fh "${next_link}" "${web_root}/status"
fi
next_link_created=false
active_target="$(readlink "${web_root}/status")"
if [[ "${active_target}" != "releases/${release_sha}" ]]; then
echo "active release verification failed" >&2
exit 1
fi
echo "activated ${release_sha}"
+164
View File
@@ -0,0 +1,164 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ "$#" -lt 2 || "$#" -gt 3 ]]; then
echo "usage: $0 <40-char-git-sha> <source-dir> [web-root]" >&2
exit 2
fi
release_sha="$1"
source_dir="$2"
web_root="${3:-/srv/www/quant-os}"
if [[ ! "${release_sha}" =~ ^[0-9a-f]{40}$ ]]; then
echo "release SHA must be a 40-character lowercase Git SHA" >&2
exit 2
fi
if [[ ! -d "${source_dir}" ]]; then
echo "source directory does not exist" >&2
exit 2
fi
if [[ -L "${source_dir}" ]]; then
echo "source directory must not be a symlink" >&2
exit 2
fi
if [[ "${web_root}" != /* || "${web_root}" == "/" ]]; then
echo "web root must be an explicit absolute non-root path" >&2
exit 2
fi
source_entries=()
while IFS= read -r -d '' entry; do
source_entries+=("${entry}")
done < <(find "${source_dir}" -mindepth 1 -maxdepth 1 -print0)
if [[ "${#source_entries[@]}" != "2" ]]; then
echo "source must contain exactly index.html and status-data.js" >&2
exit 1
fi
for required in index.html status-data.js; do
if [[ ! -f "${source_dir}/${required}" || -L "${source_dir}/${required}" ]]; then
echo "source must contain a regular non-symlink ${required}" >&2
exit 1
fi
done
for entry in "${source_entries[@]}"; do
case "$(basename "${entry}")" in
index.html|status-data.js)
;;
*)
echo "unexpected source entry: $(basename "${entry}")" >&2
exit 1
;;
esac
if [[ ! -f "${entry}" || -L "${entry}" ]]; then
echo "source entries must be regular non-symlink files" >&2
exit 1
fi
done
release_dir="${web_root}/releases/${release_sha}"
incoming_dir="${web_root}/.incoming-${release_sha}"
lock_dir="${web_root}/.status-deploy.lock"
incoming_created=false
lock_acquired=false
cleanup() {
exit_status="$?"
if [[ "${incoming_created}" == "true" ]]; then
chmod 0755 "${incoming_dir}" 2>/dev/null || true
rm -f \
"${incoming_dir}/index.html" \
"${incoming_dir}/status-data.js"
rmdir "${incoming_dir}" 2>/dev/null || true
fi
if [[ "${lock_acquired}" == "true" ]]; then
rmdir "${lock_dir}" 2>/dev/null || true
fi
exit "${exit_status}"
}
trap cleanup EXIT HUP INT TERM
install -d -m 0755 "${web_root}"
if ! mkdir "${lock_dir}" 2>/dev/null; then
echo "another status deployment or activation holds ${lock_dir}" >&2
exit 1
fi
lock_acquired=true
if [[ -L "${web_root}/releases" ]]; then
echo "releases directory must not be a symlink" >&2
exit 1
fi
install -d -m 0755 "${web_root}/releases"
if [[ -L "${web_root}/releases" ]]; then
echo "releases directory must not be a symlink" >&2
exit 1
fi
if [[ -e "${release_dir}" || -L "${release_dir}" ]]; then
echo "refusing to overwrite existing release: ${release_dir}" >&2
exit 1
fi
if [[ -e "${incoming_dir}" || -L "${incoming_dir}" ]]; then
echo "stale incoming path must be inspected and removed: ${incoming_dir}" >&2
exit 1
fi
install -d -m 0755 "${incoming_dir}"
incoming_created=true
install -m 0644 "${source_dir}/index.html" "${incoming_dir}/index.html"
install -m 0644 \
"${source_dir}/status-data.js" \
"${incoming_dir}/status-data.js"
hash_command=()
if command -v sha256sum >/dev/null 2>&1; then
hash_command=(sha256sum)
elif command -v shasum >/dev/null 2>&1; then
hash_command=(shasum -a 256)
else
echo "sha256sum or shasum is required" >&2
exit 1
fi
for required in index.html status-data.js; do
source_hash="$("${hash_command[@]}" "${source_dir}/${required}" | awk '{print $1}')"
incoming_hash="$("${hash_command[@]}" "${incoming_dir}/${required}" | awk '{print $1}')"
if [[ "${source_hash}" != "${incoming_hash}" ]]; then
echo "release hash mismatch: ${required}" >&2
exit 1
fi
echo "${required} ${incoming_hash}"
done
incoming_entries=()
while IFS= read -r -d '' entry; do
incoming_entries+=("${entry}")
done < <(find "${incoming_dir}" -mindepth 1 -maxdepth 1 -print0)
if [[ "${#incoming_entries[@]}" != "2" ]]; then
echo "incoming release must contain exactly two files" >&2
exit 1
fi
for required in index.html status-data.js; do
if [[ ! -f "${incoming_dir}/${required}" || -L "${incoming_dir}/${required}" ]]; then
echo "incoming release is not a regular immutable payload: ${required}" >&2
exit 1
fi
done
chmod 0444 \
"${incoming_dir}/index.html" \
"${incoming_dir}/status-data.js"
if mv --version >/dev/null 2>&1; then
mv -T "${incoming_dir}" "${release_dir}"
else
if [[ -e "${release_dir}" || -L "${release_dir}" ]]; then
echo "refusing to overwrite concurrently created release" >&2
exit 1
fi
mv "${incoming_dir}" "${release_dir}"
fi
incoming_created=false
chmod 0555 "${release_dir}"
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
export QUANT_OS_STATUS_DEPLOY_LOCK_HELD=1
"${script_dir}/activate_static_status.sh" "${release_sha}" "${web_root}"
@@ -0,0 +1,26 @@
# Temporary compatibility redirects. Install only after the new canonical
# route has passed public verification. Keep these as 302 during observation.
location = /quants-strategies/quant-os {
return 302 https://$host/quant-os/status/;
}
location = /quants-strategies/quant-os/ {
return 302 https://$host/quant-os/status/;
}
location = /quants-strategies/quant-os/status {
return 302 https://$host/quant-os/status/;
}
location = /quants-strategies/quant-os/status/ {
return 302 https://$host/quant-os/status/;
}
location = /quants-strategies/quant-os/status/index.html {
return 302 https://$host/quant-os/status/;
}
location = /quants-strategies/quant-os/status/status-data.js {
return 302 https://$host/quant-os/status/status-data.js;
}
@@ -0,0 +1,26 @@
# Stable compatibility redirects. Replace the temporary 302 include with this
# file only after an observation period and an explicit production change.
location = /quants-strategies/quant-os {
return 308 https://$host/quant-os/status/;
}
location = /quants-strategies/quant-os/ {
return 308 https://$host/quant-os/status/;
}
location = /quants-strategies/quant-os/status {
return 308 https://$host/quant-os/status/;
}
location = /quants-strategies/quant-os/status/ {
return 308 https://$host/quant-os/status/;
}
location = /quants-strategies/quant-os/status/index.html {
return 308 https://$host/quant-os/status/;
}
location = /quants-strategies/quant-os/status/status-data.js {
return 308 https://$host/quant-os/status/status-data.js;
}
+29
View File
@@ -0,0 +1,29 @@
# Quant OS public status routes.
#
# This snippet belongs inside the gomars.fun HTTPS server block. The release
# payload is published under /srv/www/quant-os/releases/<git-sha>/ and the
# /srv/www/quant-os/status symlink is switched atomically after verification.
location = /quant-os {
return 308 https://$host/quant-os/status/;
}
location = /quant-os/ {
return 308 https://$host/quant-os/status/;
}
location = /quant-os/status {
return 308 https://$host/quant-os/status/;
}
location = /quant-os/status/index.html {
return 308 https://$host/quant-os/status/;
}
location ^~ /quant-os/status/ {
root /srv/www;
index index.html;
try_files $uri $uri/ =404;
add_header Cache-Control "no-cache" always;
add_header X-Content-Type-Options "nosniff" always;
}
+13
View File
@@ -0,0 +1,13 @@
# Quant OS maturity profiles
`baseline-60` and `production-80` are evidence profiles. They are not package
names and they do not change the trading strategy.
- `baseline-60`: the existing G1G10 research-to-shadow baseline.
- `production-80`: the limited-production standard and fail-closed evaluator.
The score is never inferred from source-file count. A control earns points only
when its required evidence tier, immutable file hash, frozen subject and
canonical standard/profile hash are present and a verifier returns provenance.
The repository has no production issuer registry; test verification can never
emit a production qualification.
+30
View File
@@ -0,0 +1,30 @@
{
"schema_version": "1.0",
"profile_id": "quant-os-baseline-60",
"claim": "RESEARCH_TO_SHADOW_BASELINE",
"source_of_truth": "gate_scorecard.json",
"required_gates": [
"G1",
"G2",
"G3",
"G4",
"G5",
"G6",
"G7",
"G8",
"G9",
"G10"
],
"all_gates_required": true,
"minimum_score": 60,
"minimum_shadow_trading_days": 20,
"trusted_verification_required": true,
"verification_claim": {
"schema": "quant-os-evidence-claim/1.0",
"control_id": "BASELINE_60",
"evidence_type": "baseline60_qualification_attestation",
"minimum_evidence_tier": "E3",
"profile_binding": "canonical_json_sha256"
},
"capital_authority": "NONE"
}
@@ -0,0 +1,185 @@
{
"schema_version": "1.0",
"standard_id": "quant-os-production-80-v1",
"standard_canonical_sha256": "44fc6c168ade4c6de15569214d88b96ce47b22b4120b10c80eb6f212fad5e662",
"assessed_as_of": "2026-07-30",
"claim": "CURRENT_CANDIDATE_IS_NOT_80",
"subject": {
"candidate_id": "UNASSIGNED",
"release_sha256": null,
"model_sha256": null,
"dataset_sha256": null,
"account_scope_id": null,
"material_change_epoch": null
},
"pre_canary_attestation": null,
"qualification_attestation": null,
"baseline_60": {
"all_gates_passed": false,
"gates_passed": 0,
"gates_total": 10,
"profile_id": "quant-os-baseline-60",
"profile_canonical_sha256": "34adf09a824299ddb07c17f56751527ddf96ff5e494e792be0c65da4f8acdeb3",
"verification": null
},
"observations": {
"real_qmt_observed": false,
"broker_programmatic_confirmation": false,
"shadow_trading_days": 0,
"canary_trading_days": 0,
"open_p0": null,
"open_p1": null
},
"controls": [
{
"id": "D1-PIT",
"status": "not_passed",
"evidence": [],
"note": "No authorized complete production PIT dataset has reached the frozen Ridge path."
},
{
"id": "D1-OPS",
"status": "not_passed",
"evidence": [],
"note": "No production freshness SLO, entitlement record and restore history."
},
{
"id": "D1-REDUNDANCY",
"status": "not_passed",
"evidence": [],
"note": "No independent production source or rehearsed fallback."
},
{
"id": "D2-OOS",
"status": "not_passed",
"evidence": [],
"note": "The connected Ridge candidate still uses synthetic research data."
},
{
"id": "D2-MODEL",
"status": "not_passed",
"evidence": [],
"note": "ModelBundle exists, but production ownership, expiry, approval and rollback evidence do not."
},
{
"id": "D2-CHALLENGER",
"status": "not_passed",
"evidence": [],
"note": "No real-data champion/challenger or production drift history."
},
{
"id": "D3-LAYERS",
"status": "not_passed",
"evidence": [],
"note": "No real-data local/JoinQuant/QMT L1-L5 strict-peer identity; Qlib has only a separate declared-scope research fixture."
},
{
"id": "D3-DIFF",
"status": "not_passed",
"evidence": [],
"note": "Mock parity and one JoinQuant execution smoke do not close a real QMT difference ledger."
},
{
"id": "D4-RISK",
"status": "not_passed",
"evidence": [],
"note": "Risk components exist, but there is no independent real-broker fail-closed service evidence."
},
{
"id": "D4-RULES",
"status": "not_passed",
"evidence": [],
"note": "The effective-dated 2026-07-06 SSE/SZSE rule packs are not implemented; BSE remains explicitly excluded and fail-closed."
},
{
"id": "D4-OMS",
"status": "not_passed",
"evidence": [],
"note": "No real QMT callback, reconnect and restart lifecycle evidence."
},
{
"id": "D4-TCA",
"status": "not_passed",
"evidence": [],
"note": "No controlled-live fills or governed TCA feedback."
},
{
"id": "D5-QMT",
"status": "not_passed",
"evidence": [],
"note": "QMT has fake-contract and syntax evidence only."
},
{
"id": "D5-RECON",
"status": "not_passed",
"evidence": [],
"note": "No real daily broker reconciliation series."
},
{
"id": "D5-EVIDENCE",
"status": "not_passed",
"evidence": [],
"note": "Local HMAC evidence is not broker attestation or real callback history."
},
{
"id": "D5-FALLBACK",
"status": "not_passed",
"evidence": [],
"note": "No real-account manual handoff drill."
},
{
"id": "D6-SHADOW",
"status": "not_passed",
"evidence": [],
"note": "Zero of the required 60 real QMT shadow trading days are recorded."
},
{
"id": "D6-CANARY",
"status": "not_passed",
"evidence": [],
"note": "No limited-capital canary is authorized or observed."
},
{
"id": "D6-DR",
"status": "not_passed",
"evidence": [],
"note": "No measured RPO/RTO and broker failure-drill evidence."
},
{
"id": "D6-RELEASE",
"status": "not_passed",
"evidence": [],
"note": "Independent CI, signed release inventory and rollback drill are not yet complete."
},
{
"id": "D7-SCOPE",
"status": "not_passed",
"evidence": [],
"note": "The intended personal scope is documented but has no private attestation reference."
},
{
"id": "D7-REPORT",
"status": "not_passed",
"evidence": [],
"note": "No broker programmatic-trading confirmation exists yet."
},
{
"id": "D7-FLOW",
"status": "not_passed",
"evidence": [],
"note": "No real-account abnormal-trading monitoring history."
},
{
"id": "D7-SECURITY",
"status": "not_passed",
"evidence": [],
"note": "Repository secret hygiene exists, but production access, rotation and supply-chain evidence do not."
},
{
"id": "D7-REGULATED",
"status": "not_passed",
"evidence": [],
"note": "The regulated-mode activation calendar is documentation-only."
}
]
}
+422
View File
@@ -0,0 +1,422 @@
{
"schema_version": "1.0",
"standard_id": "quant-os-production-80-v1",
"name": "Quant OS 80-point controlled-production standard",
"as_of": "2026-07-30",
"maximum_score": 100,
"qualification_threshold": 80,
"claim_boundary": "Passing is an engineering qualification for limited personal-account production. It is not an investment-return claim, institutional licence or legal opinion.",
"evidence_tiers": {
"E0": {
"rank": 0,
"name": "assertion_or_document_only"
},
"E1": {
"rank": 1,
"name": "deterministic_local_test_or_synthetic"
},
"E2": {
"rank": 2,
"name": "authorized_real_data_frozen_oos_or_external_authority"
},
"E3": {
"rank": 3,
"name": "production_operational_control_or_real_platform_shadow"
},
"E4": {
"rank": 4,
"name": "controlled_live_observation"
}
},
"qualification": {
"baseline_60_gates": 10,
"minimum_shadow_trading_days": 60,
"minimum_canary_trading_days": 10,
"pre_canary_threshold": 77,
"pre_canary_exempt_hard_gates": [
"D6-CANARY"
],
"maximum_open_p0": 0,
"maximum_open_p1": 0,
"score_cap_without_baseline_60": 59,
"score_cap_without_canary": 79,
"all_hard_gates_required": true,
"all_domain_floors_required": true,
"broker_programmatic_confirmation_required": true,
"real_qmt_observation_required": true,
"trusted_pre_canary_authorization_required": true,
"trusted_promotion_attestation_required": true
},
"baseline_prerequisite": {
"profile_path": "profiles/baseline-60/profile.json",
"profile_id": "quant-os-baseline-60",
"trusted_attestation_required": true,
"minimum_evidence_tier": "E3",
"claim_control_id": "BASELINE_60",
"claim_evidence_type": "baseline60_qualification_attestation"
},
"attestation_policy": {
"claim_schema": "quant-os-evidence-claim/1.0",
"standard_binding": "canonical_json_sha256",
"assessment_binding": "canonical_json_sha256",
"issuer_registry_required": true,
"production_trust_registry": "external_runtime_configuration_required",
"repository_self_issuance_allowed": false,
"pre_canary": {
"control_id": "PRE_CANARY_AUTHORIZATION",
"evidence_type": "pre_canary_authorization_attestation",
"minimum_evidence_tier": "E3",
"required_authorization_fields": [
"authorization_id",
"authorized_at",
"expires_on",
"account_scope_id",
"strategy_ids",
"maximum_capital_cny",
"maximum_gross_notional_cny",
"maximum_symbols",
"symbol_allowlist",
"flow_limits",
"stop_conditions",
"no_automatic_scale_up"
],
"required_stop_conditions": [
"any_risk_data_rule_or_reconciliation_breach",
"authorization_expired",
"operator_kill_switch"
]
},
"canary_observation": {
"control_id": "D6-CANARY",
"evidence_type": "controlled_live_canary_observation",
"minimum_evidence_tier": "E4",
"required_link_fields": [
"pre_canary_authorization_claim_id",
"pre_canary_authorization_sha256",
"canary_started_on",
"canary_ended_on",
"trading_days_observed"
],
"required_observation_fields": [
"maximum_capital_cny_observed",
"maximum_gross_notional_cny_observed",
"symbols_observed",
"maximum_submission_and_cancel_requests_per_second_observed",
"maximum_submission_and_cancel_requests_per_day_observed",
"automatic_scale_up_observed",
"breach_events",
"stop_events"
]
},
"promotion": {
"control_id": "QUALIFICATION",
"evidence_type": "production80_promotion_attestation",
"minimum_evidence_tier": "E4"
}
},
"operating_defaults": {
"declared_market_scope": {
"included": [
"SSE",
"SZSE"
],
"excluded_fail_closed": [
"BSE"
],
"expansion_policy": "requires_effective_dated_rule_pack_and_regression_evidence_before_enablement"
},
"rule_pack_contract": {
"required_fields": [
"published_at",
"effective_at",
"implementation_status",
"suspended_at",
"resumed_at",
"source_url",
"source_sha256"
],
"implementation_status_values": [
"effective",
"suspended",
"partially_suspended",
"repealed"
],
"unknown_or_suspended_behavior": "fail_closed"
},
"flow_limits": {
"maximum_submission_and_cancel_requests_per_second": 10,
"maximum_submission_and_cancel_requests_per_day": 1000,
"aggregation_scope": "per_account_and_actual_control_group",
"message_count_semantics": "all_order_submission_attempts_plus_all_cancel_requests_emitted_by_quant_os",
"cancellation_rate": {
"review_threshold": 0.3,
"numerator": "strategy_initiated_cancel_requests",
"denominator": "accepted_order_submissions",
"window": "trading_day_per_account_and_actual_control_group",
"rejected_submissions": "excluded_from_denominator",
"partial_fill_then_cancel": "included_in_numerator",
"exchange_automatic_cancel": "excluded_from_numerator",
"zero_denominator": "zero_rate"
},
"semantics": "internal_conservative_default_not_regulatory_safe_harbor"
}
},
"domains": [
{
"id": "D1",
"name": "Data and point-in-time truth",
"weight": 15,
"minimum_score_ratio": 0.6,
"controls": [
{
"id": "D1-PIT",
"name": "Production PIT dataset is complete, immutable and replayable",
"weight": 4,
"hard_gate": true,
"minimum_evidence_tier": "E2",
"acceptance": "The declared universe has effective-dated security master, membership, ST, suspension, limits, corporate actions, announcement availability and fail-closed completeness tests."
},
{
"id": "D1-OPS",
"name": "Data licence, lineage, freshness and recovery are operational",
"weight": 5,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "Every source has a permitted-use record, immutable lineage, freshness SLO, quality alarms, backfill and restore evidence."
},
{
"id": "D1-REDUNDANCY",
"name": "Critical inputs have independent validation or a tested fallback",
"weight": 6,
"hard_gate": false,
"minimum_evidence_tier": "E3",
"acceptance": "A second source or independent control total detects material provider drift and the fallback path is rehearsed."
}
]
},
{
"id": "D2",
"name": "Research and model governance",
"weight": 15,
"minimum_score_ratio": 0.6,
"controls": [
{
"id": "D2-OOS",
"name": "Frozen real-data walk-forward and OOS evidence is valid",
"weight": 4,
"hard_gate": true,
"minimum_evidence_tier": "E2",
"acceptance": "Purge and embargo, untouched test data, A-share constraints, dated fees, impact, capacity and pre-registered mandate thresholds are all frozen and reproducible."
},
{
"id": "D2-MODEL",
"name": "Model, feature and release identities are governed",
"weight": 5,
"hard_gate": true,
"minimum_evidence_tier": "E2",
"acceptance": "Registry records dataset, feature schema, code, config, training cutoff, model hash, owner, approval, rollback and expiry."
},
{
"id": "D2-CHALLENGER",
"name": "Champion, challenger, drift and experiment selection are controlled",
"weight": 6,
"hard_gate": false,
"minimum_evidence_tier": "E3",
"acceptance": "Selection policy is pre-registered; drift and degradation alerts can demote or roll back without touching a hidden test set."
}
]
},
{
"id": "D3",
"name": "Cross-engine consistency",
"weight": 10,
"minimum_score_ratio": 0.6,
"controls": [
{
"id": "D3-LAYERS",
"name": "Local, JoinQuant and QMT share frozen L1-L5 identities; Qlib follows its declared comparison scope",
"weight": 6,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "Local, JoinQuant and QMT strict peers produce identical signals and post-risk target weights under one dual-clock and versioned rule contract. Qlib compares L1/L2 only unless the same frozen model and portfolio implementation are explicitly exported; an unrelated Alpha158 workflow is never claimed as an L3/L4 peer."
},
{
"id": "D3-DIFF",
"name": "Order and fill differences are completely explained",
"weight": 4,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "Multi-period difference ledgers account for price source, lot, fee, limit, fill and broker-state effects with zero unexplained residual."
}
]
},
{
"id": "D4",
"name": "Execution and independent risk",
"weight": 15,
"minimum_score_ratio": 0.6,
"controls": [
{
"id": "D4-RISK",
"name": "Independent pre-trade risk fails closed",
"weight": 3,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "No strategy can bypass cash, sellable, permission, price, quantity, duplicate-order, concentration, turnover, loss and kill-switch checks."
},
{
"id": "D4-RULES",
"name": "Effective-dated SSE/SZSE rule packs are current; BSE remains fail-closed",
"weight": 4,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "The declared scope is SSE and SZSE; BSE is explicitly excluded and fails closed until its own rule pack is qualified. Every market, board and security-type rule records publication, effective, suspension, resumption and repeal state plus source hash. Regressions cover the 2026-07-06 rules and SSE provisions that remain suspended; unknown, expired or suspended behavior blocks submission."
},
{
"id": "D4-OMS",
"name": "Order lifecycle is idempotent and restart-safe",
"weight": 3,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "New, accepted, partial, rejected, cancelled and duplicate callbacks reconcile across disconnect, retry, restart and session rollover."
},
{
"id": "D4-TCA",
"name": "Real fills drive TCA and capacity feedback",
"weight": 5,
"hard_gate": false,
"minimum_evidence_tier": "E4",
"acceptance": "Arrival, implementation shortfall, spread, impact and participation are measured against actual fills and feed governed limit changes."
}
]
},
{
"id": "D5",
"name": "Real QMT and broker truth",
"weight": 15,
"minimum_score_ratio": 0.6,
"controls": [
{
"id": "D5-QMT",
"name": "Authorized QMT account contracts are observed end to end",
"weight": 5,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "Read-only, simulation and explicitly enabled mutation phases cover query fields, callbacks, reject, partial fill, cancel, reconnect and cross-day recovery."
},
{
"id": "D5-RECON",
"name": "Cash, positions, sellable, orders and fills reconcile daily",
"weight": 5,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "Every trading day has 100% identity reconciliation and zero unexplained difference; a difference or stale snapshot blocks new risk."
},
{
"id": "D5-EVIDENCE",
"name": "Broker facts and callbacks have authenticated replay evidence",
"weight": 2,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "Append-only envelopes bind run, account pseudonym, source time, sequence, data, model, config, code, order and callback identities."
},
{
"id": "D5-FALLBACK",
"name": "Manual broker fallback and operator handoff are rehearsed",
"weight": 3,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "The owner can freeze automation, inspect broker truth and complete or cancel risk safely without hidden automation."
}
]
},
{
"id": "D6",
"name": "Operations, disaster recovery and audit",
"weight": 15,
"minimum_score_ratio": 0.6,
"controls": [
{
"id": "D6-SHADOW",
"name": "The frozen candidate completes 60 trading days of shadow",
"weight": 4,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "The same core strategy, risk and adapter release runs for at least 60 trading days with zero open P0/P1 and daily reconciliation; a material change resets the clock."
},
{
"id": "D6-CANARY",
"name": "Limited capital canary completes at least 10 trading days",
"weight": 3,
"hard_gate": true,
"minimum_evidence_tier": "E4",
"acceptance": "Capital, gross notional, symbols, account, strategy and submission/cancel flow remain within the cited pre-canary authorization; there is no automatic scale-up, and every risk, data, rule or reconciliation breach is linked to a fail-closed stop event."
},
{
"id": "D6-DR",
"name": "Failure recovery meets RPO and RTO targets",
"weight": 5,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "Order and fill RPO is zero, trading-window RTO is at most 15 minutes, and network, crash, stale market, corrupt state and duplicate-message drills pass."
},
{
"id": "D6-RELEASE",
"name": "Build, promotion, rollback and audit are reproducible",
"weight": 3,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "A clean build emits signed manifests and dependency inventory; promotion is approved, atomic, observable and rollback-tested."
}
]
},
{
"id": "D7",
"name": "Compliance and security",
"weight": 15,
"minimum_score_ratio": 0.6,
"controls": [
{
"id": "D7-SCOPE",
"name": "Account and business scope remains personal proprietary",
"weight": 3,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "A private attestation is bound to broker KYC or account-ownership evidence and an access review. Third-party money, accepting a mandate to decide or execute trades for another person, providing paid investment advice to others, or sharing trade-control authority blocks this profile and triggers a documented legal/compliance applicability review. Purchasing or reading third-party research does not by itself constitute a third-party trading mandate."
},
{
"id": "D7-REPORT",
"name": "Broker programmatic-trading report matches the deployed release",
"weight": 4,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "The broker has confirmed first reporting and the deployed strategy, software version, developer, capital, leverage and declared rates match the reported facts; material changes are registered before activation."
},
{
"id": "D7-FLOW",
"name": "Abnormal-trading and conservative flow controls are enforced",
"weight": 3,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "Per account and actual-control account group, monitoring covers order-submission attempts, cancel requests, one-second cancellation, price impact, trade share, self-trade and reverse trade. The versioned daily-baseline policy counts every Quant OS submission attempt plus cancel request and permits no more than 10 such messages per second or 1000 per trading day. A strategy-initiated cancel-request count divided by accepted order submissions at or above 30% triggers review; rejected submissions are excluded from that denominator, partial-fill-then-cancel is included in the numerator, and exchange automatic cancellation is excluded. Stricter broker or exchange limits prevail and no internal limit claims regulatory safety."
},
{
"id": "D7-SECURITY",
"name": "Secrets, access, personal data and supply chain are controlled",
"weight": 2,
"hard_gate": true,
"minimum_evidence_tier": "E3",
"acceptance": "Credentials remain in an approved secret store, access is least-privilege and revocable, logs are redacted, dependencies are inventoried, and Colab/public artifacts contain no account facts."
},
{
"id": "D7-REGULATED",
"name": "A future regulated-mode rule calendar is maintained",
"weight": 3,
"hard_gate": false,
"minimum_evidence_tier": "E2",
"acceptance": "An effective-dated calendar records official URLs, publication and effective dates, retrieval date, content hash and review cadence. External capital or clients always block PERSONAL_PROPRIETARY and trigger a documented legal/compliance applicability determination; private-fund, disclosure, retention or licensed-operation obligations are activated only when that determination finds them applicable."
}
]
}
]
}
+4 -3
View File
@@ -3,13 +3,14 @@ requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[project] [project]
name = "quant60" name = "quant-os"
version = "0.1.0" version = "0.2.0"
description = "Portable, auditable A-share quantitative trading baseline" description = "Evidence-driven A-share research, backtest and controlled-production operating system"
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = [] dependencies = []
[project.scripts] [project.scripts]
quant-os = "quant_os.cli:main"
quant60 = "quant60.cli:main" quant60 = "quant60.cli:main"
[tool.setuptools] [tool.setuptools]
+9 -6
View File
@@ -3,9 +3,10 @@
从仓库中的 `quant-os` 项目根目录运行命令。本文没有任何命令会启用真实订单。 从仓库中的 `quant-os` 项目根目录运行命令。本文没有任何命令会启用真实订单。
Quant OS 是项目名;`quant60` 是第一版 A 股 Baseline 内核和 CLI namespace。 Quant OS 是项目名;`quant60` 是第一版 A 股 Baseline 内核和 CLI namespace。
当前结论是 `NOT_BASELINE_60`。最近一次标准库套件为 211 tests、OK、 当前结论是 `NOT_BASELINE_60`。最近一次完整套件的精确计数以
3 个可选路径 skip。CPython 3.12 + `pyqlib==0.9.7` 的 native momentum `gate_scorecard.json` 和 CI 运行记录为准,测试通过只属于本地/fixture
和 Alpha158/LightGBM/Recorder fixture 已真实跑通;JoinQuant、QMT 与 broker 证据。CPython 3.12 + `pyqlib==0.9.7` 的 native momentum 和
Alpha158/LightGBM/Recorder fixture 已真实跑通;JoinQuant、QMT 与 broker
中,JoinQuant 已有一次 2024 全年真实 hosted smokeQMT 与 broker 尚无 中,JoinQuant 已有一次 2024 全年真实 hosted smokeQMT 与 broker 尚无
真实运行证据;聚宽仍缺完整导出与同输入 peer。 真实运行证据;聚宽仍缺完整导出与同输入 peer。
@@ -435,7 +436,8 @@ QMT 账号还要先探测官方 query API 在“失败”与“空列表”时
terminal/xtquant build 记录以及 `XtTrade.traded_amount` terminal/xtquant build 记录以及 `XtTrade.traded_amount`
`price × quantity` 的真实容差探针。当前 amount 是被认证的 informational `price × quantity` 的真实容差探针。当前 amount 是被认证的 informational
fact,不参与 target/delta。之后还需脱敏 callback replay、restart recovery、 fact,不参与 target/delta。之后还需脱敏 callback replay、restart recovery、
完整对账、至少 20 trading days shadow 和 G10。当前 `.BJ` mutation 不支持。 完整对账和 G10Baseline 60 至少需要 20 trading days shadowProduction 80
当前要求同一冻结候选至少 60 trading days。当前 `.BJ` mutation 不支持。
## 8. Qlib 0.9.7 research ## 8. Qlib 0.9.7 research
@@ -527,8 +529,9 @@ real_platform_pass = false
gate_credit = [] gate_credit = []
``` ```
因此 mock parity 不计 G9。真实 G9 仍要求真实 JoinQuant/QMT export、同输入 因此 mock parity 不计 G9。Baseline 60 的真实 G9 仍要求真实 JoinQuant/QMT
的 L1—L5 difference report 和至少 20 trading days QMT/broker shadow。 export、同输入的 L1—L5 difference report 和至少 20 trading days QMT/broker
shadowProduction 80 当前要求同一冻结候选至少 60 trading days。
## 10. Evidence checklist ## 10. Evidence checklist
+12
View File
@@ -0,0 +1,12 @@
# Proprietary runtime boundaries
These directories document deployment boundaries. They do not vendor platform
SDKs or credentials.
- `portable36`: generated dependency-free consumer for hosted Python 3.6.
- `joinquant`: JoinQuant upload/runtime contract.
- `qmt_builtin`: QMT built-in strategy runtime.
- `colab`: notebook runtime and data-upload boundary.
Current executable compatibility files remain under `platforms/`, `dist/` and
`notebooks/` until their golden-vector migrations complete.
+5
View File
@@ -0,0 +1,5 @@
# Colab runtime
The canonical notebook remains `notebooks/quant_os_colab.ipynb`. Colab may
receive only data whose licence permits the use and which contains no broker
credential, account, holding, fill or other sensitive account fact.
+5
View File
@@ -0,0 +1,5 @@
# JoinQuant runtime
The hosted file is a thin execution consumer. Current source and generated
artifacts remain at `platforms/joinquant_strategy.py` and
`dist/joinquant_strategy.py` during compatibility migration.
+5
View File
@@ -0,0 +1,5 @@
# portable36
Target boundary for the generated Python 3.6-compatible TargetPackage consumer.
It may consume versioned schemas and golden vectors, but cannot import the
modern local core or calculate Alpha.
+4
View File
@@ -0,0 +1,4 @@
# QMT built-in runtime
The built-in runtime remains backtest-only until real QMT contract, callback,
restart, reconciliation and broker programmatic-trading evidence pass.
+5
View File
@@ -8,6 +8,9 @@ python_bin="${PYTHON_BIN:-python3}"
"${python_bin}" -c \ "${python_bin}" -c \
'import sys; assert sys.version_info >= (3, 10), sys.version' 'import sys; assert sys.version_info >= (3, 10), sys.version'
PYTHONPATH=src:. "${python_bin}" -m quant_os architecture check
PYTHONPATH=src:. "${python_bin}" -m quant_os standard validate
PYTHONPATH=src:. "${python_bin}" -m quant_os standard evaluate
PYTHONPATH=src:. "${python_bin}" -m unittest discover -s tests -p '*.py' -q PYTHONPATH=src:. "${python_bin}" -m unittest discover -s tests -p '*.py' -q
"${python_bin}" tools/bundle_platforms.py "${python_bin}" tools/bundle_platforms.py
PYTHONPATH=src:. "${python_bin}" -m quant60 smoke \ PYTHONPATH=src:. "${python_bin}" -m quant60 smoke \
@@ -26,6 +29,8 @@ PYTHONPATH=src:. "${python_bin}" tools/export_mock_parity.py \
verify \ verify \
artifacts/mock-parity/report.json artifacts/mock-parity/report.json
PYTHONPATH=src:. "${python_bin}" tools/capability_probe.py PYTHONPATH=src:. "${python_bin}" tools/capability_probe.py
PYTHONPATH=src:. "${python_bin}" -m quant_os doctor
"${python_bin}" tools/check_tracked_secrets.py
"${python_bin}" tools/build_public_status.py --check "${python_bin}" tools/build_public_status.py --check
"${python_bin}" tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb "${python_bin}" tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb
"${python_bin}" tools/run_colab_notebook.py \ "${python_bin}" tools/run_colab_notebook.py \
+7
View File
@@ -0,0 +1,7 @@
"""Long-lived product namespace for Quant OS.
`quant60` remains the compatibility namespace for the first baseline protocol.
Maturity scores belong to profiles, not to the package name.
"""
__version__ = "0.2.0"
+5
View File
@@ -0,0 +1,5 @@
from .cli import main
if __name__ == "__main__":
raise SystemExit(main())
+6
View File
@@ -0,0 +1,6 @@
"""Concrete provider, research-engine, platform and broker port adapters.
The existing top-level `adapters/` modules are the compatibility
implementations. They move here only after golden-vector equivalence is
established.
"""
+1
View File
@@ -0,0 +1 @@
"""Use cases that compose domain ports without importing concrete SDKs."""
+268
View File
@@ -0,0 +1,268 @@
"""Machine-checkable dependency rules for the new Quant OS namespace."""
from __future__ import annotations
import ast
from pathlib import Path
from typing import Dict, Iterable, List, Mapping, Set
LAYER_DEPENDENCIES: Mapping[str, Set[str]] = {
"foundation": {"foundation"},
"contracts": {"foundation", "contracts"},
"ports": {"foundation", "contracts", "ports"},
"data": {"foundation", "contracts", "ports", "data"},
"research": {"foundation", "contracts", "ports", "research"},
"portfolio": {"foundation", "contracts", "ports", "portfolio"},
"execution": {"foundation", "contracts", "ports", "execution"},
"application": {
"foundation",
"contracts",
"ports",
"data",
"research",
"portfolio",
"execution",
"application",
},
"evidence": {"foundation", "contracts", "ports", "evidence"},
"adapters": {
"foundation",
"contracts",
"ports",
"adapters",
},
"control": {
"foundation",
"contracts",
"ports",
"application",
"evidence",
"control",
},
"composition": {
"foundation",
"contracts",
"ports",
"data",
"research",
"portfolio",
"execution",
"application",
"evidence",
"adapters",
"control",
"composition",
},
}
PLANES: Mapping[str, tuple[str, ...]] = {
"data": ("data", "research", "portfolio", "execution"),
"control": ("application", "control"),
"evidence": ("evidence",),
"boundary": ("contracts", "ports", "adapters"),
"foundation": ("foundation",),
"composition": ("composition",),
}
VENDOR_MODULES = frozenset({"jqdatasdk", "qlib", "xtquant"})
LEGACY_FIRST_PARTY_MODULES = frozenset(
{"quant60", "adapters", "platforms", "tools"}
)
LEGACY_COMPATIBILITY_LAYERS = frozenset({"composition"})
ROOT_MODULE_LAYERS: Mapping[str, str] = {
"__init__": "foundation",
"__main__": "composition",
"architecture": "control",
"cli": "composition",
}
def _imports(
tree: ast.AST,
*,
current_layer: str,
root_module: bool,
) -> Iterable[tuple[str, str]]:
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for alias in node.names:
parts = alias.name.split(".")
if len(parts) >= 2 and parts[0] == "quant_os":
yield ("quant_os_layer", parts[1])
yield ("module", parts[0])
elif isinstance(node, ast.ImportFrom):
module = node.module or ""
parts = module.split(".") if module else []
if node.level >= 2:
if parts:
yield ("quant_os_layer", parts[0])
else:
for alias in node.names:
yield ("quant_os_layer", alias.name.split(".")[0])
elif node.level == 1:
if root_module and parts and parts[0] in LAYER_DEPENDENCIES:
yield ("quant_os_layer", parts[0])
elif root_module and parts and parts[0] in ROOT_MODULE_LAYERS:
yield ("quant_os_layer", ROOT_MODULE_LAYERS[parts[0]])
elif root_module and not parts:
for alias in node.names:
imported = alias.name.split(".")[0]
if imported in LAYER_DEPENDENCIES:
yield ("quant_os_layer", imported)
else:
yield ("quant_os_layer", current_layer)
elif parts and parts[0] == "quant_os":
if len(parts) >= 2:
yield ("quant_os_layer", parts[1])
else:
for alias in node.names:
yield ("quant_os_layer", alias.name.split(".")[0])
if parts:
yield ("module", parts[0])
def _check_python_file(
*,
path: Path,
root: Path,
layer: str,
allowed: Set[str],
) -> List[Dict[str, object]]:
violations: List[Dict[str, object]] = []
try:
tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
except (OSError, SyntaxError) as exc:
return [
{
"layer": layer,
"path": path.relative_to(root).as_posix(),
"reason": f"cannot_parse:{type(exc).__name__}",
}
]
imports = set(
_imports(
tree,
current_layer=layer,
root_module=path.parent == root,
)
)
for kind, imported in sorted(imports):
if (
kind == "quant_os_layer"
and imported in LAYER_DEPENDENCIES
and imported not in allowed
):
violations.append(
{
"layer": layer,
"path": path.relative_to(root).as_posix(),
"reason": "forbidden_dependency",
"imports": imported,
"allowed": sorted(allowed),
}
)
if kind == "module" and imported in VENDOR_MODULES and layer != "adapters":
violations.append(
{
"layer": layer,
"path": path.relative_to(root).as_posix(),
"reason": "vendor_sdk_outside_adapter",
"imports": imported,
}
)
if (
kind == "module"
and imported in LEGACY_FIRST_PARTY_MODULES
and layer not in LEGACY_COMPATIBILITY_LAYERS
):
violations.append(
{
"layer": layer,
"path": path.relative_to(root).as_posix(),
"reason": "legacy_dependency_escape",
"imports": imported,
"allowed_layers": sorted(
LEGACY_COMPATIBILITY_LAYERS
),
}
)
return violations
def validate_architecture(package_root: str | Path) -> Dict[str, object]:
"""Return violations without importing project modules.
The legacy `quant60` namespace is intentionally outside this check. New
code must enter through these boundaries; the compatibility kernel will be
migrated one vertical slice at a time.
"""
root = Path(package_root)
violations: List[Dict[str, object]] = []
checked_files = 0
for child in sorted(root.iterdir()) if root.is_dir() else []:
if (
child.is_dir()
and child.name not in LAYER_DEPENDENCIES
and child.name != "__pycache__"
and any(child.rglob("*.py"))
):
violations.append(
{
"layer": child.name,
"path": child.relative_to(root).as_posix(),
"reason": "unregistered_layer_package",
}
)
for layer, allowed in sorted(LAYER_DEPENDENCIES.items()):
layer_root = root / layer
if not layer_root.is_dir():
violations.append(
{
"layer": layer,
"path": layer_root.as_posix(),
"reason": "missing_layer_package",
}
)
continue
for path in sorted(layer_root.rglob("*.py")):
checked_files += 1
violations.extend(
_check_python_file(
path=path,
root=root,
layer=layer,
allowed=allowed,
)
)
for path in sorted(root.glob("*.py")):
root_layer = ROOT_MODULE_LAYERS.get(path.stem)
if root_layer is None:
violations.append(
{
"layer": "root",
"path": path.relative_to(root).as_posix(),
"reason": "unregistered_root_module",
}
)
continue
checked_files += 1
violations.extend(
_check_python_file(
path=path,
root=root,
layer=root_layer,
allowed=set(LAYER_DEPENDENCIES[root_layer]),
)
)
return {
"ok": not violations,
"scope": "new_namespace_only",
"package_root": "src/quant_os",
"migration_state": "transitioning_from_quant60_legacy",
"checked_files": checked_files,
"layers": sorted(LAYER_DEPENDENCIES),
"planes": {name: list(layers) for name, layers in PLANES.items()},
"violations": violations,
}
+224
View File
@@ -0,0 +1,224 @@
"""Stable Quant OS CLI with `quant60` kept as a compatibility protocol."""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any, Dict, Sequence
from quant60.cli import main as legacy_main
from . import __version__
from .architecture import validate_architecture
from .evidence import evaluate_assessment, load_json_object, validate_standard
LEGACY_COMMANDS = frozenset(
{
"validate-config",
"smoke",
"backtest",
"verify-ledger",
"verify-manifest",
"research-smoke",
"verify-research-manifest",
"snapshot-decision",
"snapshot-backtest",
"verify-snapshot-decision",
"preflight",
}
)
def _looks_like_project_root(path: Path) -> bool:
return (
(path / "pyproject.toml").is_file()
and (path / "profiles/production-80/standard.json").is_file()
and (path / "src/quant_os").is_dir()
)
def _project_root(explicit: str | Path | None = None) -> Path:
if explicit is not None:
candidate = Path(explicit).expanduser().resolve()
if not _looks_like_project_root(candidate):
raise FileNotFoundError(
f"not a Quant OS checkout: {candidate}"
)
return candidate
candidates = [Path.cwd().resolve()]
candidates.extend(Path.cwd().resolve().parents)
source_checkout = Path(__file__).resolve().parents[2]
candidates.append(source_checkout)
seen = set()
for candidate in candidates:
if candidate in seen:
continue
seen.add(candidate)
if _looks_like_project_root(candidate):
return candidate
raise FileNotFoundError(
"Quant OS checkout not found; run inside the repository or pass "
"--project-root"
)
def _profile_paths(project_root: Path) -> tuple[Path, Path]:
root = project_root / "profiles" / "production-80"
return root / "standard.json", root / "current-assessment.json"
def _emit(payload: Dict[str, Any]) -> None:
print(
json.dumps(
payload,
ensure_ascii=False,
indent=2,
sort_keys=True,
allow_nan=False,
)
)
def _doctor(project: Path) -> Dict[str, Any]:
profile_path, assessment_path = _profile_paths(project)
architecture = validate_architecture(project / "src" / "quant_os")
standard = load_json_object(profile_path)
assessment = load_json_object(assessment_path)
profile = validate_standard(standard)
evaluation = evaluate_assessment(
standard,
assessment,
repository_root=project,
)
required_paths = {
"baseline_config": project / "configs" / "baseline.json",
"joinquant_bundle": project / "dist" / "joinquant_strategy.py",
"qmt_bundle": project / "dist" / "qmt_builtin_strategy.py",
"colab_notebook": project / "notebooks" / "quant_os_colab.ipynb",
"baseline_60_scorecard": project / "gate_scorecard.json",
}
legacy_roots = (
project / "src" / "quant60",
project / "adapters",
project / "platforms",
project / "tools",
)
legacy_python_files = sum(
1
for root in legacy_roots
if root.is_dir()
for _path in root.rglob("*.py")
)
return {
"ok": bool(architecture["ok"]) and all(
path.is_file() for path in required_paths.values()
),
"product": "Quant OS",
"version": __version__,
"architecture": architecture,
"migration": {
"state": "transitioning",
"checked_scope": "src/quant_os",
"legacy_python_files_outside_checker": legacy_python_files,
"legacy_roots": [
path.relative_to(project).as_posix()
for path in legacy_roots
if path.is_dir()
],
},
"profile": profile,
"current_maturity": {
"state": evaluation["qualification_state"],
"effective_score": evaluation["effective_score"],
"maximum_score": evaluation["maximum_score"],
"baseline_60_passed": evaluation["baseline_60_passed"],
"qualified": evaluation["qualified"],
"standard_canonical_sha256": evaluation[
"standard_canonical_sha256"
],
"production_trust_registry_available": evaluation[
"production_trust_registry_available"
],
"verification_environment": evaluation[
"verification_environment"
],
},
"compatibility": {
"quant60_cli": True,
"quant60_import": True,
"writer_protocol": "V1",
},
"required_paths": {
name: {"available": path.is_file(), "path": path.relative_to(project).as_posix()}
for name, path in required_paths.items()
},
}
def _build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="quant-os", description=__doc__)
parser.add_argument("--version", action="version", version=__version__)
parser.add_argument(
"--project-root",
help="path to a Quant OS repository checkout",
)
subparsers = parser.add_subparsers(dest="command", required=True)
subparsers.add_parser(
"doctor",
help="validate project boundaries, profiles and local entrypoint files",
)
architecture = subparsers.add_parser(
"architecture", help="check new-namespace dependency boundaries"
)
architecture.add_argument("action", choices=("check",))
standard = subparsers.add_parser(
"standard", help="validate or evaluate a maturity profile"
)
standard.add_argument("action", choices=("validate", "evaluate"))
standard.add_argument("--profile")
standard.add_argument("--assessment")
return parser
def main(argv: Sequence[str] | None = None) -> int:
arguments = list(sys.argv[1:] if argv is None else argv)
if arguments and arguments[0] in LEGACY_COMMANDS:
return legacy_main(arguments)
args = _build_parser().parse_args(arguments)
try:
project = _project_root(args.project_root)
if args.command == "doctor":
payload = _doctor(project)
elif args.command == "architecture":
payload = validate_architecture(project / "src" / "quant_os")
elif args.command == "standard":
default_profile, default_assessment = _profile_paths(project)
profile = load_json_object(args.profile or default_profile)
if args.action == "validate":
payload = validate_standard(profile)
else:
payload = evaluate_assessment(
profile,
load_json_object(args.assessment or default_assessment),
repository_root=project,
)
else:
raise ValueError(f"unsupported command {args.command}")
except Exception as exc:
_emit(
{
"ok": False,
"error": type(exc).__name__,
"message": str(exc),
}
)
return 2
_emit(payload)
return 0 if payload.get("ok") else 2
if __name__ == "__main__":
raise SystemExit(main())
+5
View File
@@ -0,0 +1,5 @@
"""Composition root for wiring application use cases to runtime adapters.
Only this layer may depend on both application/control modules and adapters.
Vendor SDK imports still belong inside adapter implementations.
"""
+1
View File
@@ -0,0 +1 @@
"""Versioned DTOs, schemas and migrations; no provider or broker SDK imports."""
+1
View File
@@ -0,0 +1 @@
"""Run specifications, orchestration and release policy."""
+1
View File
@@ -0,0 +1 @@
"""Canonical point-in-time datasets, immutable snapshots and quality checks."""
+15
View File
@@ -0,0 +1,15 @@
"""Evidence manifests, claims, parity and maturity evaluation."""
from .maturity import (
MaturityStandardError,
evaluate_assessment,
load_json_object,
validate_standard,
)
__all__ = [
"MaturityStandardError",
"evaluate_assessment",
"load_json_object",
"validate_standard",
]
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
"""Execution intents, OMS state, reconciliation and fail-closed kill switches."""
+1
View File
@@ -0,0 +1 @@
"""Pure IDs, clocks, symbols, money and other dependency-free primitives."""
+1
View File
@@ -0,0 +1 @@
"""Risk, cost, capacity, optimization and post-risk target weights."""
+5
View File
@@ -0,0 +1,5 @@
"""Inward-facing ports implemented by external adapters."""
from .broker import EvidenceSinkPort, ReadBrokerPort, TradeBrokerPort
__all__ = ["EvidenceSinkPort", "ReadBrokerPort", "TradeBrokerPort"]
+38
View File
@@ -0,0 +1,38 @@
"""Broker and evidence ports with read and mutation authority kept separate."""
from __future__ import annotations
from typing import Any, Mapping, Protocol, Sequence
class ReadBrokerPort(Protocol):
"""Read-only broker facts. Implementations must not expose mutation here."""
def query_asset(self) -> Mapping[str, Any]:
...
def query_positions(self) -> Sequence[Mapping[str, Any]]:
...
def query_orders(self) -> Sequence[Mapping[str, Any]]:
...
def query_trades(self) -> Sequence[Mapping[str, Any]]:
...
class TradeBrokerPort(Protocol):
"""Explicit mutation boundary enabled only by a promoted release policy."""
def submit_order(self, intent: Mapping[str, Any]) -> Mapping[str, Any]:
...
def cancel_order(self, broker_order_id: str) -> Mapping[str, Any]:
...
class EvidenceSinkPort(Protocol):
"""Append immutable facts without granting decision or trading authority."""
def append(self, envelope: Mapping[str, Any]) -> str:
...
+1
View File
@@ -0,0 +1 @@
"""Features, splits, models and evaluation; this layer never knows a broker."""
+173 -23
View File
@@ -1457,7 +1457,7 @@
<div class="masthead__brand"> <div class="masthead__brand">
<p class="eyebrow">Release control / public snapshot</p> <p class="eyebrow">Release control / public snapshot</p>
<h1 class="brand-title" id="page-title">Quant OS</h1> <h1 class="brand-title" id="page-title">Quant OS</h1>
<p class="brand-subtitle">个人 A 股研究到执行的证据审计台</p> <p class="brand-subtitle">个人沪深 A 股研究到执行的证据审计台</p>
</div> </div>
<div class="masthead__claim"> <div class="masthead__claim">
<div class="claim-line"> <div class="claim-line">
@@ -1479,15 +1479,15 @@
<div class="safety-banner" role="note" aria-label="资金安全边界"> <div class="safety-banner" role="note" aria-label="资金安全边界">
<svg class="icon" aria-hidden="true"><use href="#icon-shield-alert"></use></svg> <svg class="icon" aria-hidden="true"><use href="#icon-shield-alert"></use></svg>
<p><strong>可运行框架 ≠ 可投入资金。</strong> 本页只呈现构建时证据快照,不是实时服务健康监控,也不构成投资收益声明。</p> <p><strong>可运行框架 ≠ 可投入资金。</strong> 本页只呈现构建时证据快照,不是实时服务健康监控,也不构成投资收益声明。<span id="market-scope-copy">当前交易执行范围仅覆盖沪深;北交所尚未接入,所有北交所委托必须 fail closed。</span></p>
<span class="snapshot-label">LIVE READY = FALSE</span> <span class="snapshot-label">LIVE READY = FALSE</span>
</div> </div>
<section class="metrics" aria-label="核心状态指标"> <section class="metrics" aria-label="核心状态指标">
<div class="metric"> <div class="metric">
<span class="metric__label">Delivery score</span> <span class="metric__label">Production 80</span>
<strong class="metric__value" id="metric-score">NOT SCORED</strong> <strong class="metric__value" id="metric-score">0 / 100</strong>
<span class="metric__note">不把文章估分冒充系统得分</span> <span class="metric__note">证据得分;Hard Gate 不可补偿</span>
</div> </div>
<div class="metric"> <div class="metric">
<span class="metric__label">Gate evidence</span> <span class="metric__label">Gate evidence</span>
@@ -1513,9 +1513,10 @@
<a href="#layers"><span class="rail__index">01</span><span>系统分层</span></a> <a href="#layers"><span class="rail__index">01</span><span>系统分层</span></a>
<a href="#models"><span class="rail__index">02</span><span>模型边界</span></a> <a href="#models"><span class="rail__index">02</span><span>模型边界</span></a>
<a href="#platforms"><span class="rail__index">03</span><span>平台接入</span></a> <a href="#platforms"><span class="rail__index">03</span><span>平台接入</span></a>
<a href="#gates"><span class="rail__index">04</span><span>G1G10</span></a> <a href="#maturity80"><span class="rail__index">04</span><span>80 分标准</span></a>
<a href="#bundles"><span class="rail__index">05</span><span>发布包</span></a> <a href="#gates"><span class="rail__index">05</span><span>G1G10</span></a>
<a href="#provenance"><span class="rail__index">06</span><span>证据来源</span></a> <a href="#bundles"><span class="rail__index">06</span><span>发布包</span></a>
<a href="#provenance"><span class="rail__index">07</span><span>证据来源</span></a>
</nav> </nav>
<main class="main-content" id="main-content" tabindex="-1"> <main class="main-content" id="main-content" tabindex="-1">
@@ -1626,9 +1627,48 @@
</div> </div>
</section> </section>
<section class="audit-section" id="gates" aria-labelledby="gates-title"> <section class="audit-section" id="maturity80" aria-labelledby="maturity80-title">
<div class="section-header"> <div class="section-header">
<span class="section-code">SEC.04</span> <span class="section-code">SEC.04</span>
<div>
<h2 class="section-title" id="maturity80-title">Production 80 · 受限生产标准</h2>
<p class="section-intro">80 分以 Baseline 60 全门通过为前置,并要求真实 QMT、60 个交易日 shadow、10 个交易日受限资金 canary、持续对账、恢复演练和券商程序化交易确认。</p>
</div>
</div>
<div class="overview-board">
<div class="overview-copy">
<h3>当前 80 分判定</h3>
<p><strong id="maturity80-score">0 / 100 · BLOCKED</strong></p>
<p id="maturity80-policy">等待 machine profile。缺失数据时不得获得分数。</p>
</div>
<div class="evidence-summary">
<h3>不可补偿前置</h3>
<ul class="runtime-list" id="maturity80-prerequisites">
<li>等待状态数据</li>
</ul>
</div>
</div>
<div class="matrix-wrap">
<table class="bundle-table">
<caption class="visually-hidden">Quant OS 80 分七个维度的已验证得分和最低分</caption>
<thead>
<tr>
<th scope="col">Domain</th>
<th scope="col">Verified</th>
<th scope="col">Weight</th>
<th scope="col">Required floor</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody id="maturity80-domain-body"></tbody>
</table>
</div>
<div class="gate-stack" id="maturity80-control-list"></div>
</section>
<section class="audit-section" id="gates" aria-labelledby="gates-title">
<div class="section-header">
<span class="section-code">SEC.05</span>
<div> <div>
<h2 class="section-title" id="gates-title">Baseline 60 · G1G10</h2> <h2 class="section-title" id="gates-title">Baseline 60 · G1G10</h2>
<p class="section-intro">Gate 是发布闸门,不是代码清单。展开每项查看验收定义和缺失证据;当前 10 项均未通过。</p> <p class="section-intro">Gate 是发布闸门,不是代码清单。展开每项查看验收定义和缺失证据;当前 10 项均未通过。</p>
@@ -1639,7 +1679,7 @@
<section class="audit-section" id="bundles" aria-labelledby="bundles-title"> <section class="audit-section" id="bundles" aria-labelledby="bundles-title">
<div class="section-header"> <div class="section-header">
<span class="section-code">SEC.05</span> <span class="section-code">SEC.06</span>
<div> <div>
<h2 class="section-title" id="bundles-title">跨平台发布包完整性</h2> <h2 class="section-title" id="bundles-title">跨平台发布包完整性</h2>
<p class="section-intro">构建器核对共享核心、wrapper、配置与生成单文件的 SHA-256。哈希一致只证明代码发布包完整,不证明目标平台通过。</p> <p class="section-intro">构建器核对共享核心、wrapper、配置与生成单文件的 SHA-256。哈希一致只证明代码发布包完整,不证明目标平台通过。</p>
@@ -1664,7 +1704,7 @@
<section class="audit-section" id="provenance" aria-labelledby="provenance-title"> <section class="audit-section" id="provenance" aria-labelledby="provenance-title">
<div class="section-header"> <div class="section-header">
<span class="section-code">SEC.06</span> <span class="section-code">SEC.07</span>
<div> <div>
<h2 class="section-title" id="provenance-title">证据来源与快照血缘</h2> <h2 class="section-title" id="provenance-title">证据来源与快照血缘</h2>
<p class="section-intro">所有公开字段由仓库中的权威记录生成并脱敏。链接指向私有 Gitea 源码,访问时可能要求登录。</p> <p class="section-intro">所有公开字段由仓库中的权威记录生成并脱敏。链接指向私有 Gitea 源码,访问时可能要求登录。</p>
@@ -1738,7 +1778,7 @@
<footer class="page-footer"> <footer class="page-footer">
<p>本页是公开、脱敏的构建时快照。未发布本地行情、运行 artifacts、券商状态或可复用凭据。任何实盘决策必须回到 G1–G10 的发布级证据。</p> <p>本页是公开、脱敏的构建时快照。未发布本地行情、运行 artifacts、券商状态或可复用凭据。任何实盘决策必须回到 G1–G10 的发布级证据。</p>
<a href="https://git.gomars.fun/boat/quants-strategies/src/branch/master/quant-os" target="_blank" rel="noopener noreferrer"> <a href="https://git.gomars.fun/boat/quant-os/src/branch/master" target="_blank" rel="noopener noreferrer">
源码(需登录) 源码(需登录)
<svg class="icon" aria-hidden="true"><use href="#icon-external"></use></svg> <svg class="icon" aria-hidden="true"><use href="#icon-external"></use></svg>
</a> </a>
@@ -1759,7 +1799,7 @@
(() => { (() => {
"use strict"; "use strict";
const repoBase = "https://git.gomars.fun/boat/quants-strategies/src/branch/master/quant-os/"; const repoBase = "https://git.gomars.fun/boat/quant-os/src/branch/master/";
const statusLabels = { const statusLabels = {
implemented: { label: "已实现", tone: "implemented" }, implemented: { label: "已实现", tone: "implemented" },
tested: { label: "合同验证", tone: "tested" }, tested: { label: "合同验证", tone: "tested" },
@@ -1830,13 +1870,14 @@
const renderHeader = (data) => { const renderHeader = (data) => {
const claim = data.claim || {}; const claim = data.claim || {};
const summary = data.summary || {}; const summary = data.summary || {};
const maturity80 = data.maturity80 || {};
byId("claim-status").textContent = claim.status || "NOT_BASELINE_60"; byId("claim-status").textContent = claim.status || "NOT_BASELINE_60";
byId("claim-gates").textContent = `${claim.gatesPassed ?? 0} / ${claim.gatesTotal ?? 10} GATES`; byId("claim-gates").textContent = `${claim.gatesPassed ?? 0} / ${claim.gatesTotal ?? 10} GATES`;
byId("claim-reason").textContent = claim.reason || "缺少发布级证据。"; byId("claim-reason").textContent = claim.reason || "缺少发布级证据。";
byId("overview-reason").textContent = claim.reason || "缺少发布级证据。"; byId("overview-reason").textContent = claim.reason || "缺少发布级证据。";
byId("metric-score").textContent = claim.score == null byId("market-scope-copy").textContent = data.marketScope?.statement ||
? String(claim.scoreStatus || "not_scored").replaceAll("_", " ").toUpperCase() "当前交易执行范围仅覆盖沪深;北交所尚未接入,所有北交所委托必须 fail closed";
: String(claim.score); byId("metric-score").textContent = `${maturity80.effectiveScore ?? 0} / ${maturity80.maximumScore ?? 100}`;
byId("metric-gates").textContent = `${claim.gatesPassed ?? 0} / ${claim.gatesTotal ?? 10}`; byId("metric-gates").textContent = `${claim.gatesPassed ?? 0} / ${claim.gatesTotal ?? 10}`;
byId("metric-tests").textContent = summary.testsRecorded == null ? "—" : String(summary.testsRecorded); byId("metric-tests").textContent = summary.testsRecorded == null ? "—" : String(summary.testsRecorded);
byId("metric-tests-note").textContent = summary.optionalSkipsRecorded == null byId("metric-tests-note").textContent = summary.optionalSkipsRecorded == null
@@ -1853,6 +1894,63 @@
: "<li>无真实运行证据记录</li>"; : "<li>无真实运行证据记录</li>";
}; };
const renderMaturity80 = (data) => {
const maturity = data.maturity80 || {};
byId("maturity80-score").textContent =
`${maturity.effectiveScore ?? 0} / ${maturity.maximumScore ?? 100} · ${maturity.state || "BLOCKED"}`;
byId("maturity80-policy").textContent = maturity.evidencePolicy ||
"缺少 hash-linked evidence 时不得计分。";
const prerequisiteLabels = {
baseline_60_all_gates: "Baseline 60 · G1G10 全通过",
real_qmt_observed: "真实 QMT 已观察",
broker_programmatic_confirmation: "券商程序化交易确认",
shadow_trading_days: "同一候选 60 个交易日 shadow",
canary_trading_days: "受限资金 10 个交易日 canary",
open_p0_within_limit: "未关闭 P0 = 0",
open_p1_within_limit: "未关闭 P1 = 0"
};
const prerequisites = maturity.prerequisites || {};
const prerequisiteRows = Object.entries(prerequisiteLabels)
.map(([key, label]) => `<li>${escapeHtml(label)} · ${prerequisites[key] === true ? "PASS" : "BLOCKED"}</li>`)
prerequisiteRows.push(
`<li>Pre-canary 授权记录已核验 · ${maturity.preCanaryAuthorizationVerified === true ? "PASS" : "BLOCKED"}</li>`,
`<li>Canary 证据反向绑定授权 · ${maturity.canaryAuthorizationChainVerified === true ? "PASS" : "BLOCKED"}</li>`,
`<li>最终 promotion 可信 attestation · ${maturity.trustedAttestationVerified === true ? "PASS" : "BLOCKED"}</li>`
);
byId("maturity80-prerequisites").innerHTML = prerequisiteRows.join("");
const domains = Array.isArray(maturity.domains) ? maturity.domains : [];
byId("maturity80-domain-body").innerHTML = domains.map((domain) => `
<tr>
<td><strong>${escapeHtml(domain.id)} · ${escapeHtml(domain.name)}</strong></td>
<td>${escapeHtml(domain.score)}</td>
<td>${escapeHtml(domain.weight)}</td>
<td>${escapeHtml(domain.minimumScore)}</td>
<td>${statusTag(domain.floorPassed ? "tested" : "blocked")}</td>
</tr>
`).join("");
const controls = Array.isArray(maturity.controls) ? maturity.controls : [];
byId("maturity80-control-list").innerHTML = controls.map((control) => `
<details class="gate-item">
<summary>
<span class="gate-id">${escapeHtml(control.id)}</span>
<span class="gate-name">${escapeHtml(control.name)}</span>
<span class="gate-acceptance">${control.hardGate ? "HARD GATE" : "SCORED CONTROL"} · ${escapeHtml(control.weight)} points · min ${escapeHtml(control.minimumEvidenceTier)}</span>
${statusTag(control.status)}
</summary>
<div class="gate-detail">
<div>
<h4>Current gap</h4>
<p>${escapeHtml(control.note || "No qualifying evidence recorded.")}</p>
</div>
<div>
<h4>Evidence rule</h4>
<p>只有绑定同一候选身份、达到最低 tier、SHA-256 可重算且通过可信 verifier 的 claim 才能通过。</p>
</div>
</div>
</details>
`).join("");
};
const renderLayers = (data) => { const renderLayers = (data) => {
const layers = Array.isArray(data.layers) ? data.layers : []; const layers = Array.isArray(data.layers) ? data.layers : [];
byId("layer-list").innerHTML = layers.map((layer, index) => ` byId("layer-list").innerHTML = layers.map((layer, index) => `
@@ -2071,15 +2169,66 @@
const validatePublicData = (data) => { const validatePublicData = (data) => {
if (!data || typeof data !== "object") throw new Error("missing public status object"); if (!data || typeof data !== "object") throw new Error("missing public status object");
if (data.claim?.status !== "NOT_BASELINE_60") throw new Error("unsafe claim status"); const claim = data.claim || {};
if (data.claim?.releaseVerified !== false || data.claim?.liveReady !== false) { if (!["NOT_BASELINE_60", "BASELINE_60_VERIFIED"].includes(claim.status)) {
throw new Error("unsafe release claim"); throw new Error("unsupported claim status");
} }
if (data.claim?.gatesPassed !== 0 || data.claim?.gatesTotal !== 10) { if (!Array.isArray(data.gates) || data.gates.length !== claim.gatesTotal) {
throw new Error("unexpected gate score"); throw new Error("gate set mismatch");
} }
if (!Array.isArray(data.gates) || data.gates.some((gate) => gate.status !== "not_passed")) { if (data.gates.some((gate) => !["passed", "not_passed"].includes(gate.status))) {
throw new Error("all gates must remain explicitly not_passed"); throw new Error("unsupported gate status");
}
const gatesPassed = data.gates.filter((gate) => gate.status === "passed").length;
if (gatesPassed !== claim.gatesPassed) throw new Error("gate count mismatch");
const baselineVerified = claim.status === "BASELINE_60_VERIFIED";
if (baselineVerified !== Boolean(claim.releaseVerified)) {
throw new Error("baseline release claim mismatch");
}
if (baselineVerified && gatesPassed !== claim.gatesTotal) {
throw new Error("baseline claim requires every gate");
}
const maturity = data.maturity80 || {};
const marketScope = data.marketScope || {};
if (
JSON.stringify(marketScope.included) !== JSON.stringify(["SSE", "SZSE"])
|| JSON.stringify(marketScope.excludedFailClosed) !== JSON.stringify(["BSE"])
|| marketScope.bseTradingEnabled !== false
) {
throw new Error("unsafe or ambiguous market scope");
}
if (Boolean(maturity.baseline60Passed) !== baselineVerified) {
throw new Error("60/80 assessment mismatch");
}
if (Boolean(maturity.qualified) !== Boolean(claim.liveReady)) {
throw new Error("limited-live claim mismatch");
}
if (maturity.qualified && (
maturity.state !== "LIMITED_LIVE_VERIFIED"
|| !maturity.trustedAttestationVerified
|| !maturity.preCanaryAuthorized
|| !maturity.canaryAuthorizationChainVerified
)) {
throw new Error("unattested production-80 claim");
}
if (
["PRE_CANARY_AUTHORIZED", "CANARY_RUNNING"].includes(maturity.state)
&& !maturity.preCanaryAuthorized
) {
throw new Error("current canary state lacks authorization");
}
if (maturity.preCanaryAuthorized
&& !maturity.preCanaryAuthorizationVerified) {
throw new Error("unattested pre-canary authorization");
}
if (
String(maturity.state || "").startsWith("TEST_ONLY_")
&& (!maturity.testOnly || maturity.qualified)
) {
throw new Error("test-only assessment overclaim");
}
if (!baselineVerified && maturity.effectiveScore > 59) {
throw new Error("production-80 score violates baseline-60 cap");
} }
}; };
@@ -2095,6 +2244,7 @@
renderLayers(data); renderLayers(data);
renderModels(data); renderModels(data);
renderPlatforms(); renderPlatforms();
renderMaturity80(data);
renderGates(data); renderGates(data);
renderBundles(data); renderBundles(data);
renderActions(data); renderActions(data);
+506 -28
View File
@@ -1,6 +1,6 @@
// Generated by tools/build_public_status.py; do not edit by hand. // Generated by tools/build_public_status.py; do not edit by hand.
window.QUANT_OS_PUBLIC_STATUS = { window.QUANT_OS_PUBLIC_STATUS = {
"auditAsOf": "2026-07-26", "auditAsOf": "2026-07-30",
"bundles": { "bundles": {
"artifacts": [ "artifacts": [
{ {
@@ -116,8 +116,8 @@ window.QUANT_OS_PUBLIC_STATUS = {
"authorized JQData market snapshot, licence/lineage record and a frozen provider-data candidate tape", "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", "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 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, and callback/restart evidence; Baseline 60 requires at least 20 trading days of shadow, while the current Production 80 gap requires at least 60 trading days for the same frozen candidate",
"continuous reconciliation, recovery drills and broker compliance confirmation" "continuous reconciliation, recovery drills and broker programmatic-trading report/verification confirmation"
], ],
"internal": [ "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.", "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.",
@@ -203,11 +203,17 @@ window.QUANT_OS_PUBLIC_STATUS = {
"kind": "runbook", "kind": "runbook",
"label": "Platform deployment runbook", "label": "Platform deployment runbook",
"path": "runbooks/PLATFORM_DEPLOYMENT.md" "path": "runbooks/PLATFORM_DEPLOYMENT.md"
},
{
"id": "public-status-deployment",
"kind": "runbook",
"label": "Atomic public status deployment",
"path": "ops/README.md"
} }
], ],
"freshness": [ "freshness": [
{ {
"asOf": "2026-07-26", "asOf": "2026-07-30",
"evidenceLinks": [ "evidenceLinks": [
"scorecard", "scorecard",
"audit" "audit"
@@ -216,7 +222,7 @@ window.QUANT_OS_PUBLIC_STATUS = {
"label": "项目证据审计", "label": "项目证据审计",
"semantics": "证据盘点日期;不是部署时间,也不是行情截止日。", "semantics": "证据盘点日期;不是部署时间,也不是行情截止日。",
"status": "recorded", "status": "recorded",
"value": "2026-07-26" "value": "2026-07-30"
}, },
{ {
"evidenceLinks": [ "evidenceLinks": [
@@ -292,7 +298,7 @@ window.QUANT_OS_PUBLIC_STATUS = {
"label": "QMT / broker 真实运行", "label": "QMT / broker 真实运行",
"lastRealRuntimeAt": null, "lastRealRuntimeAt": null,
"productionReady": false, "productionReady": false,
"semantics": "QMT 只有 bundle/harnessXtTrader 只有 fake-broker 只读合同;尚无真实运行或 20 日影子证据。", "semantics": "QMT 只有 bundle/harnessXtTrader 只有 fake-broker 只读合同;尚无真实运行或影子证据。Baseline 60 至少需要 20 个交易日,Production 80 当前缺口为同一冻结候选至少 60 个交易日。",
"status": "blocked", "status": "blocked",
"value": "N/A" "value": "N/A"
} }
@@ -486,13 +492,13 @@ window.QUANT_OS_PUBLIC_STATUS = {
"limitations": [ "limitations": [
"真实平台跨引擎 manifest", "真实平台跨引擎 manifest",
"逐层 signal/target/order 差异", "逐层 signal/target/order 差异",
"QMT 至少 20 个交易日影子记录" "Baseline 60 所需的 QMT 至少 20 个交易日影子记录Production 80 另要求同一冻结候选至少 60 个交易日"
], ],
"name": "跨引擎一致性", "name": "跨引擎一致性",
"requiredEvidence": [ "requiredEvidence": [
"真实平台跨引擎 manifest", "真实平台跨引擎 manifest",
"逐层 signal/target/order 差异", "逐层 signal/target/order 差异",
"QMT 至少 20 个交易日影子记录" "Baseline 60 所需的 QMT 至少 20 个交易日影子记录Production 80 另要求同一冻结候选至少 60 个交易日"
], ],
"status": "not_passed" "status": "not_passed"
}, },
@@ -519,7 +525,7 @@ window.QUANT_OS_PUBLIC_STATUS = {
"status": "not_passed" "status": "not_passed"
} }
], ],
"generatedAt": "2026-07-26T00:00:00Z", "generatedAt": "2026-07-30T00:00:00Z",
"generatedAtSource": "audit_as_of", "generatedAtSource": "audit_as_of",
"layers": [ "layers": [
{ {
@@ -646,7 +652,7 @@ window.QUANT_OS_PUBLIC_STATUS = {
], ],
"id": "broker-oms", "id": "broker-oms",
"limitations": [ "limitations": [
"无真实券商 attestation、回调/重启证明和 20 个交易日影子记录;没有实盘下单入口。" "无真实券商 attestation、回调/重启证明和影子记录Baseline 60 至少需要 20 个交易日,Production 80 当前缺口为同一冻结候选至少 60 个交易日;没有实盘下单入口。"
], ],
"name": "券商、订单与影子盘", "name": "券商、订单与影子盘",
"releaseVerified": false, "releaseVerified": false,
@@ -673,6 +679,412 @@ window.QUANT_OS_PUBLIC_STATUS = {
"status": "blocked" "status": "blocked"
} }
], ],
"marketScope": {
"bseTradingEnabled": false,
"excludedFailClosed": [
"BSE"
],
"executionScopeLabel": "沪深(上交所、深交所)",
"included": [
"SSE",
"SZSE"
],
"statement": "当前交易执行范围仅覆盖沪深;北交所尚未接入,所有北交所委托必须 fail closed。"
},
"maturity80": {
"assessedAsOf": "2026-07-30",
"baseline60AttestationVerified": false,
"baseline60Passed": false,
"baselineProfileCanonicalSha256": "34adf09a824299ddb07c17f56751527ddf96ff5e494e792be0c65da4f8acdeb3",
"canaryAuthorizationChainVerified": false,
"canaryObservation": null,
"canaryRunning": false,
"controls": [
{
"domainId": "D1",
"hardGate": true,
"id": "D1-PIT",
"minimumEvidenceTier": "E2",
"name": "Production PIT dataset is complete, immutable and replayable",
"note": "No authorized complete production PIT dataset has reached the frozen Ridge path.",
"status": "not_passed",
"weight": 4
},
{
"domainId": "D1",
"hardGate": true,
"id": "D1-OPS",
"minimumEvidenceTier": "E3",
"name": "Data licence, lineage, freshness and recovery are operational",
"note": "No production freshness SLO, entitlement record and restore history.",
"status": "not_passed",
"weight": 5
},
{
"domainId": "D1",
"hardGate": false,
"id": "D1-REDUNDANCY",
"minimumEvidenceTier": "E3",
"name": "Critical inputs have independent validation or a tested fallback",
"note": "No independent production source or rehearsed fallback.",
"status": "not_passed",
"weight": 6
},
{
"domainId": "D2",
"hardGate": true,
"id": "D2-OOS",
"minimumEvidenceTier": "E2",
"name": "Frozen real-data walk-forward and OOS evidence is valid",
"note": "The connected Ridge candidate still uses synthetic research data.",
"status": "not_passed",
"weight": 4
},
{
"domainId": "D2",
"hardGate": true,
"id": "D2-MODEL",
"minimumEvidenceTier": "E2",
"name": "Model, feature and release identities are governed",
"note": "ModelBundle exists, but production ownership, expiry, approval and rollback evidence do not.",
"status": "not_passed",
"weight": 5
},
{
"domainId": "D2",
"hardGate": false,
"id": "D2-CHALLENGER",
"minimumEvidenceTier": "E3",
"name": "Champion, challenger, drift and experiment selection are controlled",
"note": "No real-data champion/challenger or production drift history.",
"status": "not_passed",
"weight": 6
},
{
"domainId": "D3",
"hardGate": true,
"id": "D3-LAYERS",
"minimumEvidenceTier": "E3",
"name": "Local, JoinQuant and QMT share frozen L1-L5 identities; Qlib follows its declared comparison scope",
"note": "No real-data local/JoinQuant/QMT L1-L5 strict-peer identity; Qlib has only a separate declared-scope research fixture.",
"status": "not_passed",
"weight": 6
},
{
"domainId": "D3",
"hardGate": true,
"id": "D3-DIFF",
"minimumEvidenceTier": "E3",
"name": "Order and fill differences are completely explained",
"note": "Mock parity and one JoinQuant execution smoke do not close a real QMT difference ledger.",
"status": "not_passed",
"weight": 4
},
{
"domainId": "D4",
"hardGate": true,
"id": "D4-RISK",
"minimumEvidenceTier": "E3",
"name": "Independent pre-trade risk fails closed",
"note": "Risk components exist, but there is no independent real-broker fail-closed service evidence.",
"status": "not_passed",
"weight": 3
},
{
"domainId": "D4",
"hardGate": true,
"id": "D4-RULES",
"minimumEvidenceTier": "E3",
"name": "Effective-dated SSE/SZSE rule packs are current; BSE remains fail-closed",
"note": "The effective-dated 2026-07-06 SSE/SZSE rule packs are not implemented; BSE remains explicitly excluded and fail-closed.",
"status": "not_passed",
"weight": 4
},
{
"domainId": "D4",
"hardGate": true,
"id": "D4-OMS",
"minimumEvidenceTier": "E3",
"name": "Order lifecycle is idempotent and restart-safe",
"note": "No real QMT callback, reconnect and restart lifecycle evidence.",
"status": "not_passed",
"weight": 3
},
{
"domainId": "D4",
"hardGate": false,
"id": "D4-TCA",
"minimumEvidenceTier": "E4",
"name": "Real fills drive TCA and capacity feedback",
"note": "No controlled-live fills or governed TCA feedback.",
"status": "not_passed",
"weight": 5
},
{
"domainId": "D5",
"hardGate": true,
"id": "D5-QMT",
"minimumEvidenceTier": "E3",
"name": "Authorized QMT account contracts are observed end to end",
"note": "QMT has fake-contract and syntax evidence only.",
"status": "not_passed",
"weight": 5
},
{
"domainId": "D5",
"hardGate": true,
"id": "D5-RECON",
"minimumEvidenceTier": "E3",
"name": "Cash, positions, sellable, orders and fills reconcile daily",
"note": "No real daily broker reconciliation series.",
"status": "not_passed",
"weight": 5
},
{
"domainId": "D5",
"hardGate": true,
"id": "D5-EVIDENCE",
"minimumEvidenceTier": "E3",
"name": "Broker facts and callbacks have authenticated replay evidence",
"note": "Local HMAC evidence is not broker attestation or real callback history.",
"status": "not_passed",
"weight": 2
},
{
"domainId": "D5",
"hardGate": true,
"id": "D5-FALLBACK",
"minimumEvidenceTier": "E3",
"name": "Manual broker fallback and operator handoff are rehearsed",
"note": "No real-account manual handoff drill.",
"status": "not_passed",
"weight": 3
},
{
"domainId": "D6",
"hardGate": true,
"id": "D6-SHADOW",
"minimumEvidenceTier": "E3",
"name": "The frozen candidate completes 60 trading days of shadow",
"note": "Zero of the required 60 real QMT shadow trading days are recorded.",
"status": "not_passed",
"weight": 4
},
{
"domainId": "D6",
"hardGate": true,
"id": "D6-CANARY",
"minimumEvidenceTier": "E4",
"name": "Limited capital canary completes at least 10 trading days",
"note": "No limited-capital canary is authorized or observed.",
"status": "not_passed",
"weight": 3
},
{
"domainId": "D6",
"hardGate": true,
"id": "D6-DR",
"minimumEvidenceTier": "E3",
"name": "Failure recovery meets RPO and RTO targets",
"note": "No measured RPO/RTO and broker failure-drill evidence.",
"status": "not_passed",
"weight": 5
},
{
"domainId": "D6",
"hardGate": true,
"id": "D6-RELEASE",
"minimumEvidenceTier": "E3",
"name": "Build, promotion, rollback and audit are reproducible",
"note": "Independent CI, signed release inventory and rollback drill are not yet complete.",
"status": "not_passed",
"weight": 3
},
{
"domainId": "D7",
"hardGate": true,
"id": "D7-SCOPE",
"minimumEvidenceTier": "E3",
"name": "Account and business scope remains personal proprietary",
"note": "The intended personal scope is documented but has no private attestation reference.",
"status": "not_passed",
"weight": 3
},
{
"domainId": "D7",
"hardGate": true,
"id": "D7-REPORT",
"minimumEvidenceTier": "E3",
"name": "Broker programmatic-trading report matches the deployed release",
"note": "No broker programmatic-trading confirmation exists yet.",
"status": "not_passed",
"weight": 4
},
{
"domainId": "D7",
"hardGate": true,
"id": "D7-FLOW",
"minimumEvidenceTier": "E3",
"name": "Abnormal-trading and conservative flow controls are enforced",
"note": "No real-account abnormal-trading monitoring history.",
"status": "not_passed",
"weight": 3
},
{
"domainId": "D7",
"hardGate": true,
"id": "D7-SECURITY",
"minimumEvidenceTier": "E3",
"name": "Secrets, access, personal data and supply chain are controlled",
"note": "Repository secret hygiene exists, but production access, rotation and supply-chain evidence do not.",
"status": "not_passed",
"weight": 2
},
{
"domainId": "D7",
"hardGate": false,
"id": "D7-REGULATED",
"minimumEvidenceTier": "E2",
"name": "A future regulated-mode rule calendar is maintained",
"note": "The regulated-mode activation calendar is documentation-only.",
"status": "not_passed",
"weight": 3
}
],
"controlsPassed": 0,
"controlsTotal": 25,
"domainFloorFailures": [
"D1",
"D2",
"D3",
"D4",
"D5",
"D6",
"D7"
],
"domains": [
{
"floorPassed": false,
"id": "D1",
"minimumScore": 9,
"name": "Data and point-in-time truth",
"score": 0,
"weight": 15
},
{
"floorPassed": false,
"id": "D2",
"minimumScore": 9,
"name": "Research and model governance",
"score": 0,
"weight": 15
},
{
"floorPassed": false,
"id": "D3",
"minimumScore": 6,
"name": "Cross-engine consistency",
"score": 0,
"weight": 10
},
{
"floorPassed": false,
"id": "D4",
"minimumScore": 9,
"name": "Execution and independent risk",
"score": 0,
"weight": 15
},
{
"floorPassed": false,
"id": "D5",
"minimumScore": 9,
"name": "Real QMT and broker truth",
"score": 0,
"weight": 15
},
{
"floorPassed": false,
"id": "D6",
"minimumScore": 9,
"name": "Operations, disaster recovery and audit",
"score": 0,
"weight": 15
},
{
"floorPassed": false,
"id": "D7",
"minimumScore": 9,
"name": "Compliance and security",
"score": 0,
"weight": 15
}
],
"effectiveScore": 0,
"evidencePolicy": "Only subject-bound evidence claims verified by a configured trust provider, at or above each control's minimum tier, earn points. The current repository has no production trust provider and therefore cannot self-issue a positive qualification.",
"finalAttestationVerified": false,
"hardGateFailures": [
"D1-OPS",
"D1-PIT",
"D2-MODEL",
"D2-OOS",
"D3-DIFF",
"D3-LAYERS",
"D4-OMS",
"D4-RISK",
"D4-RULES",
"D5-EVIDENCE",
"D5-FALLBACK",
"D5-QMT",
"D5-RECON",
"D6-CANARY",
"D6-DR",
"D6-RELEASE",
"D6-SHADOW",
"D7-FLOW",
"D7-REPORT",
"D7-SCOPE",
"D7-SECURITY"
],
"hardGatesPassed": 0,
"hardGatesTotal": 21,
"maximumScore": 100,
"preCanaryAttestationVerified": false,
"preCanaryAuthorization": null,
"preCanaryAuthorizationVerified": false,
"preCanaryAuthorized": false,
"preCanaryStructurallyEligible": false,
"prerequisites": {
"baseline_60_all_gates": false,
"broker_programmatic_confirmation": false,
"canary_trading_days": false,
"open_p0_within_limit": false,
"open_p1_within_limit": false,
"real_qmt_observed": false,
"shadow_trading_days": false
},
"productionTrustRegistryAvailable": false,
"qualificationThreshold": 80,
"qualified": false,
"rawScore": 0,
"scoreCaps": [
{
"maximum": 59,
"reason": "baseline_60_not_passed"
},
{
"maximum": 79,
"reason": "canary_not_observed"
}
],
"standardCanonicalSha256": "44fc6c168ade4c6de15569214d88b96ce47b22b4120b10c80eb6f212fad5e662",
"standardId": "quant-os-production-80-v1",
"state": "BLOCKED_FROM_LIMITED_LIVE",
"structurallyEligible": false,
"testOnly": false,
"trustedAttestationVerified": false,
"verificationEnvironment": "none"
},
"models": [ "models": [
{ {
"claim": "共享 symbol normalization、momentum、target weight/quantity 与 order-delta 计算;2024 JoinQuant hosted run 只证明这一 smoke 路径。", "claim": "共享 symbol normalization、momentum、target weight/quantity 与 order-delta 计算;2024 JoinQuant hosted run 只证明这一 smoke 路径。",
@@ -777,8 +1189,8 @@ window.QUANT_OS_PUBLIC_STATUS = {
"G9" "G9"
], ],
"priority": 5, "priority": 5,
"title": "完成 QMT 券商合同探针与至少 20 日影子盘", "title": "完成 QMT 券商合同探针与 Production 80 的 60 日影子盘(Baseline 60 门槛 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, and callback/restart evidence; Baseline 60 requires at least 20 trading days of shadow, while the current Production 80 gap requires at least 60 trading days for the same frozen candidate"
}, },
{ {
"gateIds": [ "gateIds": [
@@ -786,8 +1198,8 @@ window.QUANT_OS_PUBLIC_STATUS = {
"G10" "G10"
], ],
"priority": 6, "priority": 6,
"title": "完成连续对账、恢复演练与券商合规确认", "title": "完成连续对账、恢复演练与券商程序化交易报告/核查确认",
"why": "continuous reconciliation, recovery drills and broker compliance confirmation" "why": "continuous reconciliation, recovery drills and broker programmatic-trading report/verification confirmation"
} }
], ],
"platforms": [ "platforms": [
@@ -984,7 +1396,7 @@ window.QUANT_OS_PUBLIC_STATUS = {
"id": "xttrader-read-only-shadow", "id": "xttrader-read-only-shadow",
"intendedUse": "账户绑定 target-diff 与 broker observation", "intendedUse": "账户绑定 target-diff 与 broker observation",
"limitations": [ "limitations": [
"HMAC 不是 broker attestation;仍缺官方 query 失败/空结果合同callback/restart、20 日真实 shadownot live-ready" "HMAC 不是 broker attestation;仍缺官方 query 失败/空结果合同callback/restartBaseline 60 至少 20 个交易日,Production 80 当前缺口为同一冻结候选至少 60 个交易日not live-ready"
], ],
"liveReady": false, "liveReady": false,
"name": "XtTrader read-only shadow", "name": "XtTrader read-only shadow",
@@ -1063,16 +1475,30 @@ 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": "76aad7a50088b9949b9c2549a4da568d14c6bc2ff4acc7df6419989637386bc3", "payloadSha256": "9aae5da8e1d92fd28cfd7d6bef50774c4981b2baac1728ccd3153729234a88f0",
"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",
"production80Assessment": "profiles/production-80/current-assessment.json",
"production80Standard": "profiles/production-80/standard.json",
"releaseReachability": "releases/quant60-research-candidate-v1/reachability.json", "releaseReachability": "releases/quant60-research-candidate-v1/reachability.json",
"scorecard": "gate_scorecard.json", "scorecard": "gate_scorecard.json",
"sourceSetSha256": "e1b8bb8827c6c051a73872f2b0ac46f18dcf705fb50768eb98090bbbe08f863c", "sourceSetSha256": "6d906cde8df07f8fae2f2faf05679a476ceae8f5173533b3b67beecc710b2f61",
"sources": [ "sources": [
{ {
"path": "gate_scorecard.json", "path": "gate_scorecard.json",
"sha256": "db9ed17d1b56452f973db23461e8298b4f43f319b04be373adb383133500c2b6" "sha256": "2cc29f8d3670cd2e1e90ebe10a7d4d5a1ab9fc6a6b08612df2bbad485cd19655"
},
{
"path": "profiles/baseline-60/profile.json",
"sha256": "fbfb71356bd6a711510dd6ef0d84a4f76c47cc8244e90120142fa6bf36780fb9"
},
{
"path": "profiles/production-80/standard.json",
"sha256": "60ca7de08b337b75ca7ed53b7c01b6a7c3d621d788e60a15f26f2adaf2844064"
},
{
"path": "profiles/production-80/current-assessment.json",
"sha256": "f6439d548c4bf176dfcd734ade2e3343b2673e9e11f8233d7c642d4ee3436369"
}, },
{ {
"path": "dist/bundle_manifest.json", "path": "dist/bundle_manifest.json",
@@ -1092,27 +1518,43 @@ window.QUANT_OS_PUBLIC_STATUS = {
}, },
{ {
"path": "README.md", "path": "README.md",
"sha256": "d5593bdc4e535502ba817636053dc8ac38f8594be6f9e61f0fe2d11cf883a985" "sha256": "0bf0562ae9247a88a5b0248f66b9b314d7ab0ea435bc8f8e5efa78f7d2f785bb"
}, },
{ {
"path": "docs/ARCHITECTURE.md", "path": "docs/ARCHITECTURE.md",
"sha256": "93b1981168084f40c6d127dc534ff2e60eb4cd0703c8ef446c6df58b351dfbaa" "sha256": "8aae46f1b371651ac6f72905b7bcb3d72fdfda2a793e5505ab890352921c3219"
},
{
"path": "docs/architecture/ARCHITECTURE_80.md",
"sha256": "bb57df9d7eb5858741dde282ae0853a5e4ec9f4f4eb290bdb5954228fcb8956a"
}, },
{ {
"path": "docs/AUDIT_2026-07-25.md", "path": "docs/AUDIT_2026-07-25.md",
"sha256": "c070b49dfc2805c338400697369d886a6dc47b53af4a1834d0dfd383431e74d0" "sha256": "c070b49dfc2805c338400697369d886a6dc47b53af4a1834d0dfd383431e74d0"
}, },
{
"path": "docs/standards/QUANT_OS_80_STANDARD.md",
"sha256": "90dc9e7f1c5be17f5e6672598aac4afca3d26cf0099ae794c1be85bdafd0b65b"
},
{
"path": "docs/standards/BASELINE_60_VS_PRODUCTION_80.md",
"sha256": "c99e858dc186c100ff9d9cd673b1644bdcb81fecd6fe945166bdd7966b9cfe8a"
},
{
"path": "docs/getting-started/GETTING_STARTED_80.md",
"sha256": "5c18a558bd2f9e1640144a1729309bf38ada3185188c985476bd609b28c4479e"
},
{ {
"path": "docs/PLATFORM_MATRIX.md", "path": "docs/PLATFORM_MATRIX.md",
"sha256": "4880b17e54257b794398bf734d80ea40d1180e52845762987443a9c61d562a23" "sha256": "9c7952b019c92efd1055488b58838045ee30979ccc84008cd1218fab2a6d6717"
}, },
{ {
"path": "docs/CROSS_ENGINE_CONSISTENCY.md", "path": "docs/CROSS_ENGINE_CONSISTENCY.md",
"sha256": "358e4798bf853a96cb288ac379f916938415b86e86dbe4fa0e3fb4a86a612b6f" "sha256": "bdb493e84201f51c5b91bacf8c894858b0c2dbceaaf0522c68d42d040ed45057"
}, },
{ {
"path": "docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md", "path": "docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md",
"sha256": "03df249829b3ad9b4fe33c5d93afbd842561458f4ff2fe10e80e4432934eb2d2" "sha256": "71d017a7e009331413f378fc847203d19eaa1c24317f364e9dcebe645df3d13b"
}, },
{ {
"path": "docs/JOINQUANT_TARGET_PACKAGE_EVIDENCE_2026-07-26.md", "path": "docs/JOINQUANT_TARGET_PACKAGE_EVIDENCE_2026-07-26.md",
@@ -1120,11 +1562,39 @@ window.QUANT_OS_PUBLIC_STATUS = {
}, },
{ {
"path": "docs/TUSHARE_LOCAL_DATA.md", "path": "docs/TUSHARE_LOCAL_DATA.md",
"sha256": "fd6cfe6cf45dbdd82e6aa88e80b119e05a888c560b79907370c6e023b5e00e82" "sha256": "611b120ba59b59b443b5cd9237356c509f2f2699063784a4685506e88a95adea"
},
{
"path": "MIGRATION.md",
"sha256": "1d86ef7d854dc7c2eed80d2c5e5de0ee30bf87035be9be27a2eb8cb098277a5c"
},
{
"path": "ops/README.md",
"sha256": "eda75a106ea961faa22ada2c95c503ee2a5437030e238d065d7f112550dcac0d"
},
{
"path": "ops/activate_static_status.sh",
"sha256": "26e026da5ab13d228696de4a1d9a811bc80e2adf3220859b868129852655a93f"
},
{
"path": "ops/deploy_static_status.sh",
"sha256": "2b86edbd76e42832aba661d98cbadfc34607fb626d36324f7933e89c66a89c6e"
},
{
"path": "ops/nginx/quant-os-static.conf",
"sha256": "10b1c338c22caa0d4c4f68c1213d6c297a367995601dece7dd19df561b0c002e"
},
{
"path": "ops/nginx/quant-os-legacy-redirects-302.conf",
"sha256": "5206a0ed69ee01b8596fb3c5be9fdab713c14a512968c0cd24839e65495508a4"
},
{
"path": "ops/nginx/quant-os-legacy-redirects-308.conf",
"sha256": "ce8fc27e36b1c07dad445d76199b31a0113b42d759db8af7a60a3442548d4f94"
}, },
{ {
"path": "runbooks/PLATFORM_DEPLOYMENT.md", "path": "runbooks/PLATFORM_DEPLOYMENT.md",
"sha256": "f6bb1eda335c0d05f96d39b4d4167ee623fe6cffeef75015ba7985638fa8f635" "sha256": "001bc09f6865c6ee90fbbc2363b94bed7a282d039790af024457a5b1390b9840"
}, },
{ {
"path": "tools/build_release_reachability.py", "path": "tools/build_release_reachability.py",
@@ -1132,11 +1602,19 @@ window.QUANT_OS_PUBLIC_STATUS = {
}, },
{ {
"path": "tools/build_public_status.py", "path": "tools/build_public_status.py",
"sha256": "38347c2238a38fc603703c7265fd1ca54480b3f3f8c453cde9f21056f207a4ec" "sha256": "ec24dd12a6383ab1210a475c6f982b911f6ce45a7c205b9474bb2bb70665bc2a"
},
{
"path": "tools/check_tracked_secrets.py",
"sha256": "359eddb5fb009d3790bd81ce9c939ceb9aea7868fde4ccbc22ffe01099c62304"
},
{
"path": "tools/inventory_local_assets.py",
"sha256": "f52a89b2e42d0f3aed7e3c79e0110c37cab779794f550658116e07b9e338160b"
} }
] ]
}, },
"schemaVersion": 1, "schemaVersion": 2,
"summary": { "summary": {
"allFiveLayersRealPlatformObserved": false, "allFiveLayersRealPlatformObserved": false,
"candidateRealPlatformObservationScope": "target_package_execution_consumer_only", "candidateRealPlatformObservationScope": "target_package_execution_consumer_only",
@@ -1153,7 +1631,7 @@ window.QUANT_OS_PUBLIC_STATUS = {
"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": 285 "testsRecorded": 315
}, },
"system": "Quant OS / quant60 baseline kernel" "system": "Quant OS / quant60 baseline kernel"
}; };
+338 -6
View File
@@ -1,6 +1,9 @@
import json import json
import os
import stat
import subprocess import subprocess
import sys import sys
import tempfile
import unittest import unittest
from pathlib import Path from pathlib import Path
@@ -23,26 +26,355 @@ class ProjectScaffoldTests(unittest.TestCase):
) )
payload = json.loads(result.stdout.strip().splitlines()[-1]) payload = json.loads(result.stdout.strip().splitlines()[-1])
self.assertTrue(payload["ok"]) self.assertTrue(payload["ok"])
self.assertGreaterEqual(payload["code_cells"], 5) self.assertGreaterEqual(payload["code_cells"], 4)
self.assertFalse(payload["executed"]) self.assertFalse(payload["executed"])
optional_result = subprocess.run(
[
sys.executable,
"tools/run_colab_notebook.py",
"notebooks/quant_os_colab.ipynb",
"--include-optional",
],
cwd=PROJECT,
check=True,
capture_output=True,
text=True,
)
optional_payload = json.loads(
optional_result.stdout.strip().splitlines()[-1]
)
self.assertTrue(optional_payload["optional_included"])
self.assertGreater(
optional_payload["code_cells"],
payload["code_cells"],
)
def test_secret_files_are_ignored(self): def test_secret_files_are_ignored(self):
ignore = (PROJECT / ".gitignore").read_text(encoding="utf-8") ignore = (PROJECT / ".gitignore").read_text(encoding="utf-8")
for entry in (".env", "secrets/", "credentials/", "userdata_mini/"): for entry in (
".env",
"secrets/",
"credentials/",
"userdata_mini/",
"data/",
"build/",
):
self.assertIn(entry, ignore) self.assertIn(entry, ignore)
def test_tracked_secret_scan_passes(self):
result = subprocess.run(
[sys.executable, "tools/check_tracked_secrets.py"],
cwd=PROJECT,
check=False,
capture_output=True,
text=True,
)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn("tracked secret scan: OK", result.stdout)
def test_claude_project_contract_exists(self): def test_claude_project_contract_exists(self):
content = (PROJECT / "CLAUDE.md").read_text(encoding="utf-8") content = (PROJECT / "CLAUDE.md").read_text(encoding="utf-8")
self.assertIn("executable source of truth for Quant OS", content) self.assertIn("executable source of truth for Quant OS", content)
self.assertIn("make local", content) self.assertIn("make local", content)
def test_gitea_workflow_is_at_monorepo_root(self): def test_gitea_workflow_is_at_independent_repo_root(self):
workflow = PROJECT.parent / ".gitea/workflows/quant-os-ci.yml" workflow = PROJECT / ".gitea/workflows/ci.yml"
self.assertTrue(workflow.is_file()) self.assertTrue(workflow.is_file())
content = workflow.read_text(encoding="utf-8") content = workflow.read_text(encoding="utf-8")
self.assertIn("working-directory: quant-os", content)
self.assertIn("bash scripts/validate_all.sh", content) self.assertIn("bash scripts/validate_all.sh", content)
self.assertFalse((PROJECT / ".gitea/workflows/ci.yml").exists()) self.assertNotIn("working-directory: quant-os", content)
self.assertNotIn('"quant-os/**"', content)
self.assertIn(r"\.env(\.[^/]+)?", content)
self.assertIn(r"\.env\.example", content)
def test_long_lived_product_namespace_is_installable(self):
pyproject = (PROJECT / "pyproject.toml").read_text(encoding="utf-8")
self.assertIn('name = "quant-os"', pyproject)
self.assertIn('quant-os = "quant_os.cli:main"', pyproject)
self.assertIn('quant60 = "quant60.cli:main"', pyproject)
self.assertTrue((PROJECT / "src/quant_os/__init__.py").is_file())
def test_cli_can_resolve_an_explicit_checkout_from_elsewhere(self):
environment = {**os.environ, "PYTHONPATH": str(PROJECT / "src")}
with tempfile.TemporaryDirectory() as temporary:
result = subprocess.run(
[
sys.executable,
"-m",
"quant_os",
"--project-root",
str(PROJECT),
"doctor",
],
cwd=temporary,
env=environment,
check=False,
capture_output=True,
text=True,
)
self.assertEqual(result.returncode, 0, result.stderr)
payload = json.loads(result.stdout)
self.assertTrue(payload["ok"])
self.assertEqual(
payload["architecture"]["scope"],
"new_namespace_only",
)
self.assertGreater(
payload["migration"]["legacy_python_files_outside_checker"],
0,
)
def test_nginx_source_of_truth_has_atomic_release_routes(self):
config = (
PROJECT / "ops/nginx/quant-os-static.conf"
).read_text(encoding="utf-8")
self.assertIn("location ^~ /quant-os/status/", config)
self.assertIn("root /srv/www;", config)
self.assertIn(
"https://$host/quant-os/status/",
config,
)
self.assertNotIn("/quants-strategies/", config)
self.assertNotIn(":8443", config)
temporary = (
PROJECT
/ "ops/nginx/quant-os-legacy-redirects-302.conf"
).read_text(encoding="utf-8")
stable = (
PROJECT
/ "ops/nginx/quant-os-legacy-redirects-308.conf"
).read_text(encoding="utf-8")
self.assertIn(
"location = /quants-strategies/quant-os/status",
temporary,
)
self.assertIn("return 302 ", temporary)
self.assertNotIn("return 308 ", temporary)
self.assertIn("return 308 ", stable)
def test_static_status_deployer_is_atomic_and_non_overwriting(self):
sha = "a" * 40
with tempfile.TemporaryDirectory() as temporary:
web_root = Path(temporary) / "web"
result = subprocess.run(
[
"bash",
"ops/deploy_static_status.sh",
sha,
"status",
str(web_root),
],
cwd=PROJECT,
check=False,
capture_output=True,
text=True,
)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(
(web_root / "status").readlink(),
Path("releases") / sha,
)
release = web_root / "releases" / sha
self.assertEqual(
{path.name for path in release.iterdir()},
{"index.html", "status-data.js"},
)
self.assertFalse(release.stat().st_mode & stat.S_IWUSR)
self.assertFalse(
release.joinpath("index.html").stat().st_mode & stat.S_IWUSR
)
self.assertFalse(web_root.joinpath(f".incoming-{sha}").exists())
self.assertFalse(web_root.joinpath(".status-deploy.lock").exists())
second_sha = "b" * 40
switched = subprocess.run(
[
"bash",
"ops/deploy_static_status.sh",
second_sha,
"status",
str(web_root),
],
cwd=PROJECT,
check=False,
capture_output=True,
text=True,
)
self.assertEqual(switched.returncode, 0, switched.stderr)
self.assertEqual(
(web_root / "status").readlink(),
Path("releases") / second_sha,
)
second = subprocess.run(
[
"bash",
"ops/deploy_static_status.sh",
second_sha,
"status",
str(web_root),
],
cwd=PROJECT,
check=False,
capture_output=True,
text=True,
)
self.assertNotEqual(second.returncode, 0)
self.assertIn("refusing to overwrite", second.stderr)
def test_static_status_deployer_rejects_non_regular_or_extra_source(self):
cases = ("symlink", "extra-directory")
for case in cases:
with self.subTest(case=case):
temporary_context = tempfile.TemporaryDirectory()
self.addCleanup(temporary_context.cleanup)
temporary = temporary_context.name
root = Path(temporary)
source = root / "source"
source.mkdir()
source.joinpath("status-data.js").write_text(
"window.STATUS = {};\n",
encoding="utf-8",
)
if case == "symlink":
source.joinpath("index.html").symlink_to(
PROJECT / "status/index.html"
)
else:
source.joinpath("index.html").write_text(
"<!doctype html>\n",
encoding="utf-8",
)
source.joinpath("extra").mkdir()
web_root = root / "web"
result = subprocess.run(
[
"bash",
"ops/deploy_static_status.sh",
"c" * 40,
str(source),
str(web_root),
],
cwd=PROJECT,
check=False,
capture_output=True,
text=True,
)
self.assertNotEqual(result.returncode, 0)
self.assertFalse(web_root.joinpath("releases").exists())
def test_static_status_deploy_and_activation_share_concurrency_lock(self):
sha = "d" * 40
with tempfile.TemporaryDirectory() as temporary:
web_root = Path(temporary) / "web"
web_root.mkdir()
web_root.joinpath(".status-deploy.lock").mkdir()
deploy = subprocess.run(
[
"bash",
"ops/deploy_static_status.sh",
sha,
"status",
str(web_root),
],
cwd=PROJECT,
check=False,
capture_output=True,
text=True,
)
self.assertNotEqual(deploy.returncode, 0)
self.assertIn("another status deployment", deploy.stderr)
release = web_root / "releases" / sha
release.mkdir(parents=True)
for name in ("index.html", "status-data.js"):
release.joinpath(name).write_text(name, encoding="utf-8")
activate = subprocess.run(
[
"bash",
"ops/activate_static_status.sh",
sha,
str(web_root),
],
cwd=PROJECT,
check=False,
capture_output=True,
text=True,
)
self.assertNotEqual(activate.returncode, 0)
self.assertIn("another status deployment", activate.stderr)
self.assertFalse(web_root.joinpath("status").exists())
def test_static_status_activation_rejects_extra_release_entry(self):
sha = "e" * 40
with tempfile.TemporaryDirectory() as temporary:
web_root = Path(temporary) / "web"
release = web_root / "releases" / sha
release.mkdir(parents=True)
for name in ("index.html", "status-data.js"):
release.joinpath(name).write_text(name, encoding="utf-8")
release.joinpath("extra").mkdir()
result = subprocess.run(
[
"bash",
"ops/activate_static_status.sh",
sha,
str(web_root),
],
cwd=PROJECT,
check=False,
capture_output=True,
text=True,
)
self.assertNotEqual(result.returncode, 0)
self.assertIn("exactly index.html and status-data.js", result.stderr)
self.assertFalse(web_root.joinpath("status").exists())
self.assertFalse(web_root.joinpath(".status-deploy.lock").exists())
def test_local_asset_inventory_is_deterministic_and_path_safe(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
(root / "data").mkdir()
(root / "artifacts").mkdir()
(root / "data" / "one.bin").write_bytes(b"one")
(root / "artifacts" / "two.json").write_text(
"{}\n",
encoding="utf-8",
)
first = root / "first.json"
result = subprocess.run(
[
sys.executable,
str(PROJECT / "tools/inventory_local_assets.py"),
str(root),
"--output",
str(first),
],
cwd=PROJECT,
check=False,
capture_output=True,
text=True,
)
self.assertEqual(result.returncode, 0, result.stderr)
payload = json.loads(first.read_text(encoding="utf-8"))
self.assertEqual(
[entry["path"] for entry in payload["entries"]],
["data/one.bin", "artifacts/two.json"],
)
encoded = json.dumps(payload)
self.assertNotIn(str(root), encoded)
compare = subprocess.run(
[
sys.executable,
str(PROJECT / "tools/inventory_local_assets.py"),
str(root),
"--compare",
str(first),
],
cwd=PROJECT,
check=False,
capture_output=True,
text=True,
)
self.assertEqual(compare.returncode, 0, compare.stderr)
if __name__ == "__main__": if __name__ == "__main__":
+75 -2
View File
@@ -54,6 +54,28 @@ class PublicStatusTests(unittest.TestCase):
self.assertEqual(payload["claim"]["gatesPassed"], 0) self.assertEqual(payload["claim"]["gatesPassed"], 0)
self.assertEqual(payload["claim"]["gatesTotal"], 10) self.assertEqual(payload["claim"]["gatesTotal"], 10)
self.assertTrue(all(gate["status"] == "not_passed" for gate in payload["gates"])) self.assertTrue(all(gate["status"] == "not_passed" for gate in payload["gates"]))
self.assertEqual(payload["schemaVersion"], 2)
self.assertFalse(payload["maturity80"]["qualified"])
self.assertFalse(payload["maturity80"]["baseline60Passed"])
self.assertEqual(payload["maturity80"]["effectiveScore"], 0)
self.assertFalse(payload["maturity80"]["preCanaryAuthorized"])
self.assertFalse(
payload["maturity80"]["preCanaryAuthorizationVerified"]
)
self.assertFalse(
payload["maturity80"]["canaryAuthorizationChainVerified"]
)
self.assertFalse(
payload["maturity80"]["trustedAttestationVerified"]
)
self.assertEqual(payload["marketScope"]["included"], ["SSE", "SZSE"])
self.assertEqual(
payload["marketScope"]["excludedFailClosed"],
["BSE"],
)
self.assertFalse(payload["marketScope"]["bseTradingEnabled"])
self.assertIn("北交所", payload["marketScope"]["statement"])
self.assertIn("fail closed", payload["marketScope"]["statement"])
def test_public_payload_is_deliberately_non_secret_and_non_local(self): def test_public_payload_is_deliberately_non_secret_and_non_local(self):
payload = _read_payload() payload = _read_payload()
@@ -84,8 +106,23 @@ class PublicStatusTests(unittest.TestCase):
self.assertIn("NOT_BASELINE_60", page) self.assertIn("NOT_BASELINE_60", page)
self.assertIn("0 / 10", page) self.assertIn("0 / 10", page)
self.assertIn("validatePublicData", page) self.assertIn("validatePublicData", page)
self.assertIn("current canary state lacks authorization", page)
self.assertNotIn("all gates must remain", page)
self.assertIn('id="maturity80"', page)
self.assertIn("renderMaturity80", page)
self.assertIn('id="market-scope-copy"', page)
self.assertIn("个人沪深 A 股", page)
self.assertIn("北交所", page)
self.assertIn("platform.limitations", page) self.assertIn("platform.limitations", page)
self.assertNotIn("platform.missingReleaseEvidence", page) self.assertNotIn("platform.missingReleaseEvidence", page)
self.assertIn(
"https://git.gomars.fun/boat/quant-os/src/branch/master/",
page,
)
self.assertNotIn(
"quants-strategies/src/branch/master/quant-os",
page,
)
self.assertNotRegex( self.assertNotRegex(
page, page,
re.compile( re.compile(
@@ -98,6 +135,37 @@ class PublicStatusTests(unittest.TestCase):
for tag in re.findall(r"<a\\b[^>]*target=\"_blank\"[^>]*>", page): for tag in re.findall(r"<a\\b[^>]*target=\"_blank\"[^>]*>", page):
self.assertIn('rel="noopener noreferrer"', tag) self.assertIn('rel="noopener noreferrer"', tag)
def test_production80_is_machine_evaluated_and_fail_closed(self):
payload = _read_payload()
maturity = payload["maturity80"]
self.assertEqual(maturity["standardId"], "quant-os-production-80-v1")
self.assertEqual(maturity["maximumScore"], 100)
self.assertEqual(maturity["qualificationThreshold"], 80)
self.assertEqual((maturity["rawScore"], maturity["effectiveScore"]), (0, 0))
self.assertEqual(
maturity["state"],
"BLOCKED_FROM_LIMITED_LIVE",
)
self.assertFalse(maturity["qualified"])
self.assertEqual(maturity["controlsPassed"], 0)
self.assertEqual(maturity["controlsTotal"], 25)
self.assertGreater(maturity["hardGatesTotal"], 0)
self.assertEqual(len(maturity["domains"]), 7)
self.assertTrue(
all(not domain["floorPassed"] for domain in maturity["domains"])
)
self.assertTrue(
all(value is False for value in maturity["prerequisites"].values())
)
self.assertEqual(
payload["provenance"]["production80Standard"],
"profiles/production-80/standard.json",
)
self.assertEqual(
payload["provenance"]["production80Assessment"],
"profiles/production-80/current-assessment.json",
)
def test_models_and_platforms_preserve_evidence_boundaries(self): def test_models_and_platforms_preserve_evidence_boundaries(self):
payload = _read_payload() payload = _read_payload()
models = {model["id"]: model for model in payload["models"]} models = {model["id"]: model for model in payload["models"]}
@@ -124,6 +192,11 @@ class PublicStatusTests(unittest.TestCase):
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())
shadow_limit = platforms["XtTrader read-only shadow"]["limitations"][0]
self.assertIn("Baseline 60", shadow_limit)
self.assertIn("20", shadow_limit)
self.assertIn("Production 80", shadow_limit)
self.assertIn("60", shadow_limit)
def test_candidate_records_only_real_joinquant_execution_observation(self): def test_candidate_records_only_real_joinquant_execution_observation(self):
payload = _read_payload() payload = _read_payload()
@@ -301,7 +374,7 @@ class PublicStatusTests(unittest.TestCase):
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"]}
self.assertEqual(freshness["project-audit"]["value"], "2026-07-26") self.assertEqual(freshness["project-audit"]["value"], "2026-07-30")
self.assertEqual( self.assertEqual(
freshness["joinquant-hosted-market-window"]["marketDataThrough"], freshness["joinquant-hosted-market-window"]["marketDataThrough"],
"2024-12-31", "2024-12-31",
@@ -399,7 +472,7 @@ class PublicStatusTests(unittest.TestCase):
scorecard = { scorecard = {
"claim_policy": { "claim_policy": {
"baseline_threshold": 60, "baseline_threshold": 60,
"current_claim": "BASELINE_60", "current_claim": "BASELINE_60_VERIFIED",
}, },
"current_delivery": {"score": 59}, "current_delivery": {"score": 59},
"gates": [{"id": "G1", "status": "not_passed"}], "gates": [{"id": "G1", "status": "not_passed"}],
+954
View File
@@ -0,0 +1,954 @@
import copy
import hashlib
import json
import sys
import tempfile
import unittest
from pathlib import Path
PROJECT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PROJECT / "src"))
from quant_os.architecture import validate_architecture
from quant_os.evidence.maturity import (
MaturityStandardError,
_final_assessment_digest,
_pre_canary_projection_digest,
evaluate_assessment,
load_json_object,
validate_standard,
)
PROFILE = PROJECT / "profiles/production-80/standard.json"
ASSESSMENT = PROJECT / "profiles/production-80/current-assessment.json"
class QuantOsMaturityTests(unittest.TestCase):
def setUp(self):
self.standard = load_json_object(PROFILE)
self.assessment = load_json_object(ASSESSMENT)
@staticmethod
def _frozen_subject():
return {
"candidate_id": "candidate-test-v1",
"release_sha256": "1" * 64,
"model_sha256": "2" * 64,
"dataset_sha256": "3" * 64,
"account_scope_id": "broker-account-pseudonym",
"material_change_epoch": 0,
}
@staticmethod
def _trusted_test_verifier(claim, control, source):
trusted = (
claim.get("attestation")
== {"method": "unit-test-trust-boundary"}
and source.is_file()
and claim.get("control_id") == control["id"]
)
return {
"trusted": trusted,
"trust_environment": "test",
"verifier_id": "unit-test-verifier",
"issuer_id": "unit-test-issuer",
"key_fingerprint_sha256": "9" * 64,
"policy_version": "unit-test-policy-v1",
}
def _write_claim(
self,
root,
*,
control_id,
tier,
subject,
evidence_type="control_acceptance_evidence",
assessment_sha256=None,
observed_as_of="2026-06-30",
extra=None,
):
claims = root / "claims"
claims.mkdir(exist_ok=True)
claim = {
"schema_version": "quant-os-evidence-claim/1.0",
"claim_id": f"claim-{control_id.lower()}",
"control_id": control_id,
"tier": tier,
"subject": subject,
"standard_id": self.standard["standard_id"],
"standard_canonical_sha256": self.assessment[
"standard_canonical_sha256"
],
"evidence_type": evidence_type,
"authority_class": "unit_test_only",
"observed_as_of": observed_as_of,
"source_hashes": ["4" * 64],
"attestation": {"method": "unit-test-trust-boundary"},
}
if assessment_sha256 is not None:
claim["assessment_sha256"] = assessment_sha256
if extra is not None:
claim.update(extra)
path = claims / f"{control_id.lower()}.json"
path.write_text(
json.dumps(claim, sort_keys=True),
encoding="utf-8",
)
return {
"path": path.relative_to(root).as_posix(),
"tier": tier,
"sha256": hashlib.sha256(path.read_bytes()).hexdigest(),
}
def _prepare_qualified_assessment(
self,
root,
passing_controls,
*,
canary_days=10,
attestation_kind="qualification",
):
assessment = copy.deepcopy(self.assessment)
subject = self._frozen_subject()
assessment["subject"] = subject
baseline_profile = load_json_object(
PROJECT / "profiles/baseline-60/profile.json"
)
baseline_root = root / "profiles" / "baseline-60"
baseline_root.mkdir(parents=True)
(baseline_root / "profile.json").write_text(
json.dumps(baseline_profile, sort_keys=True),
encoding="utf-8",
)
baseline_verification = self._write_claim(
root,
control_id="BASELINE_60",
tier="E3",
subject=subject,
evidence_type="baseline60_qualification_attestation",
extra={
"baseline_profile_id": "quant-os-baseline-60",
"baseline_profile_canonical_sha256": self.assessment[
"baseline_60"
]["profile_canonical_sha256"],
"gates_passed": 10,
"gates_total": 10,
"score": 60,
},
)
assessment["baseline_60"] = {
"all_gates_passed": True,
"gates_passed": 10,
"gates_total": 10,
"profile_id": "quant-os-baseline-60",
"profile_canonical_sha256": self.assessment["baseline_60"][
"profile_canonical_sha256"
],
"verification": baseline_verification,
}
assessment["observations"] = {
"real_qmt_observed": True,
"broker_programmatic_confirmation": True,
"shadow_trading_days": 60,
"canary_trading_days": canary_days,
"open_p0": 0,
"open_p1": 0,
}
for control in assessment["controls"]:
if (
control["id"] in passing_controls
and control["id"] != "D6-CANARY"
):
standard_control = next(
item
for domain in self.standard["domains"]
for item in domain["controls"]
if item["id"] == control["id"]
)
control["status"] = "passed"
control["evidence"] = [
self._write_claim(
root,
control_id=control["id"],
tier=standard_control["minimum_evidence_tier"],
subject=subject,
)
]
if attestation_kind not in {"qualification", "pre_canary"}:
raise AssertionError(f"unknown attestation kind {attestation_kind}")
assessment["pre_canary_attestation"] = self._write_claim(
root,
control_id="PRE_CANARY_AUTHORIZATION",
tier="E3",
subject=subject,
evidence_type="pre_canary_authorization_attestation",
assessment_sha256=_pre_canary_projection_digest(assessment),
observed_as_of="2026-07-01",
extra={
"authorization_id": (
"claim-pre_canary_authorization"
),
"authorized_at": "2026-07-01",
"expires_on": "2026-08-31",
"account_scope_id": subject["account_scope_id"],
"strategy_ids": [subject["candidate_id"]],
"maximum_capital_cny": 100000,
"maximum_gross_notional_cny": 80000,
"maximum_symbols": 2,
"symbol_allowlist": [
"000001.XSHE",
"600000.XSHG",
],
"flow_limits": {
"maximum_submission_and_cancel_requests_per_second": 2,
"maximum_submission_and_cancel_requests_per_day": 50,
},
"stop_conditions": [
"any_risk_data_rule_or_reconciliation_breach",
"authorization_expired",
"operator_kill_switch",
],
"no_automatic_scale_up": True,
},
)
if "D6-CANARY" in passing_controls:
canary_control = next(
control
for control in assessment["controls"]
if control["id"] == "D6-CANARY"
)
canary_control["status"] = "passed"
canary_control["evidence"] = [
self._write_claim(
root,
control_id="D6-CANARY",
tier="E4",
subject=subject,
evidence_type="controlled_live_canary_observation",
observed_as_of="2026-07-15",
extra={
"pre_canary_authorization_claim_id": (
"claim-pre_canary_authorization"
),
"pre_canary_authorization_sha256": assessment[
"pre_canary_attestation"
]["sha256"],
"canary_started_on": "2026-07-02",
"canary_ended_on": "2026-07-15",
"trading_days_observed": canary_days,
"maximum_capital_cny_observed": 75000,
"maximum_gross_notional_cny_observed": 60000,
"symbols_observed": [
"000001.XSHE",
"600000.XSHG",
],
"maximum_submission_and_cancel_requests_per_second_observed": 2,
"maximum_submission_and_cancel_requests_per_day_observed": 40,
"automatic_scale_up_observed": False,
"breach_events": [],
"stop_events": [],
},
)
]
if attestation_kind == "qualification":
assessment["qualification_attestation"] = self._write_claim(
root,
control_id="QUALIFICATION",
tier="E4",
subject=subject,
evidence_type="production80_promotion_attestation",
assessment_sha256=_final_assessment_digest(assessment),
observed_as_of="2026-07-16",
)
return assessment
def test_profile_is_exactly_weighted_and_current_claim_is_blocked(self):
summary = validate_standard(self.standard)
self.assertEqual(summary["maximum_score"], 100)
self.assertEqual(summary["domain_weight_total"], 100)
self.assertEqual(summary["qualification_threshold"], 80)
self.assertEqual(summary["domain_count"], 7)
self.assertEqual(summary["hard_gate_count"], 21)
self.assertEqual(summary["hard_gate_weight_total"], 80)
result = evaluate_assessment(
self.standard,
self.assessment,
repository_root=PROJECT,
)
self.assertEqual(result["raw_score"], 0)
self.assertEqual(result["effective_score"], 0)
self.assertFalse(result["baseline_60_passed"])
self.assertFalse(result["qualified"])
self.assertEqual(
result["qualification_state"],
"BLOCKED_FROM_LIMITED_LIVE",
)
self.assertTrue(result["hard_gate_failures"])
self.assertEqual(
set(result["domain_floor_failures"]),
{f"D{index}" for index in range(1, 8)},
)
def test_a_pass_without_hashed_evidence_is_rejected(self):
assessment = copy.deepcopy(self.assessment)
assessment["controls"][0]["status"] = "passed"
with self.assertRaisesRegex(
MaturityStandardError,
"without immutable evidence",
):
evaluate_assessment(
self.standard,
assessment,
repository_root=PROJECT,
)
def test_all_controls_form_a_test_only_structural_path(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
passing_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
}
assessment = self._prepare_qualified_assessment(
root,
passing_controls,
)
result = evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=self._trusted_test_verifier,
)
self.assertEqual(result["effective_score"], 100)
self.assertFalse(result["qualified"])
self.assertTrue(result["structurally_eligible"])
self.assertTrue(result["final_attestation_verified"])
self.assertFalse(result["trusted_attestation_verified"])
self.assertTrue(result["test_only"])
self.assertTrue(result["canary_authorization_chain_verified"])
self.assertEqual(
result["qualification_state"],
"TEST_ONLY_STRUCTURAL_PASS",
)
def test_exactly_eighty_points_is_a_structural_path_only(self):
hard_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"]
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
hard_controls,
)
result = evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=self._trusted_test_verifier,
)
self.assertEqual(result["raw_score"], 80)
self.assertEqual(result["effective_score"], 80)
self.assertFalse(result["qualified"])
self.assertEqual(
result["qualification_state"],
"TEST_ONLY_STRUCTURAL_PASS",
)
def test_structural_score_cannot_self_attest_without_trusted_verifier(self):
hard_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"]
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
hard_controls,
)
with self.assertRaisesRegex(
MaturityStandardError,
"trusted evidence verifier",
):
evaluate_assessment(
self.standard,
assessment,
repository_root=root,
)
def test_pre_canary_requires_a_separate_trusted_authorization(self):
pre_canary_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"] and control["id"] != "D6-CANARY"
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
pre_canary_controls,
canary_days=0,
attestation_kind="pre_canary",
)
result = evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=self._trusted_test_verifier,
)
self.assertEqual(result["raw_score"], 77)
self.assertFalse(result["qualified"])
self.assertFalse(result["pre_canary_authorized"])
self.assertTrue(result["pre_canary_attestation_verified"])
self.assertEqual(
result["pre_canary_authorization"]["claim_id"],
"claim-pre_canary_authorization",
)
self.assertEqual(
result["qualification_state"],
"TEST_ONLY_PRE_CANARY_PASS",
)
def test_pre_canary_authorization_survives_running_day_updates(self):
pre_canary_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"] and control["id"] != "D6-CANARY"
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
pre_canary_controls,
canary_days=0,
attestation_kind="pre_canary",
)
assessment["observations"]["canary_trading_days"] = 5
result = evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=self._trusted_test_verifier,
)
self.assertTrue(result["pre_canary_attestation_verified"])
self.assertEqual(
result["qualification_state"],
"TEST_ONLY_PRE_CANARY_PASS",
)
def test_expired_pre_canary_authorization_cannot_open_or_continue_canary(self):
pre_canary_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"] and control["id"] != "D6-CANARY"
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
pre_canary_controls,
canary_days=0,
attestation_kind="pre_canary",
)
assessment["assessed_as_of"] = "2026-09-01"
result = evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=self._trusted_test_verifier,
)
self.assertFalse(
result["pre_canary_authorization_valid_as_of_assessment"]
)
self.assertEqual(
result["pre_canary_authorization"]["validity_status"],
"expired",
)
self.assertFalse(result["canary_running"])
self.assertEqual(
result["qualification_state"],
"TEST_ONLY_PRE_CANARY_EXPIRED",
)
def test_completed_canary_chain_survives_authorization_expiry(self):
hard_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"]
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
hard_controls,
)
assessment["assessed_as_of"] = "2026-09-01"
final_link = assessment["qualification_attestation"]
final_path = root / final_link["path"]
final_claim = json.loads(final_path.read_text(encoding="utf-8"))
final_claim["assessment_sha256"] = _final_assessment_digest(
assessment
)
final_path.write_text(
json.dumps(final_claim, sort_keys=True),
encoding="utf-8",
)
final_link["sha256"] = hashlib.sha256(
final_path.read_bytes()
).hexdigest()
result = evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=self._trusted_test_verifier,
)
self.assertFalse(
result["pre_canary_authorization_valid_as_of_assessment"]
)
self.assertTrue(result["canary_authorization_chain_verified"])
self.assertTrue(result["structurally_eligible"])
self.assertEqual(
result["qualification_state"],
"TEST_ONLY_STRUCTURAL_PASS",
)
def test_canary_observation_must_stay_within_authorized_hard_caps(self):
hard_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"]
}
mutations = (
(
"capital",
{"maximum_capital_cny_observed": 100001},
"authorized capital caps",
),
(
"gross_notional",
{"maximum_gross_notional_cny_observed": 80001},
"authorized capital caps",
),
(
"symbol",
{"symbols_observed": ["300001.XSHE"]},
"authorized symbol caps",
),
(
"flow",
{
"maximum_submission_and_cancel_requests_per_second_observed": 3
},
"authorized flow caps",
),
(
"automatic_scale_up",
{"automatic_scale_up_observed": True},
"automatic scale-up",
),
)
for name, mutation, expected in mutations:
with self.subTest(name=name), tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
hard_controls,
)
canary = next(
control
for control in assessment["controls"]
if control["id"] == "D6-CANARY"
)
evidence = canary["evidence"][0]
path = root / evidence["path"]
claim = json.loads(path.read_text(encoding="utf-8"))
claim.update(mutation)
path.write_text(
json.dumps(claim, sort_keys=True),
encoding="utf-8",
)
evidence["sha256"] = hashlib.sha256(
path.read_bytes()
).hexdigest()
with self.assertRaisesRegex(
MaturityStandardError,
expected,
):
evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=(
self._trusted_test_verifier
),
)
def test_canary_breach_must_link_to_fail_closed_stop_event(self):
hard_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"]
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
hard_controls,
)
canary = next(
control
for control in assessment["controls"]
if control["id"] == "D6-CANARY"
)
evidence = canary["evidence"][0]
path = root / evidence["path"]
claim = json.loads(path.read_text(encoding="utf-8"))
claim["breach_events"] = [
{
"breach_id": "breach-1",
"condition": (
"any_risk_data_rule_or_reconciliation_breach"
),
"observed_on": "2026-07-08",
"stop_event_id": "missing-stop",
}
]
path.write_text(
json.dumps(claim, sort_keys=True),
encoding="utf-8",
)
evidence["sha256"] = hashlib.sha256(
path.read_bytes()
).hexdigest()
with self.assertRaisesRegex(
MaturityStandardError,
"not linked to an authorized stop",
):
evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=self._trusted_test_verifier,
)
def test_canary_breach_with_fail_closed_stop_event_is_accepted(self):
hard_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"]
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
hard_controls,
)
canary = next(
control
for control in assessment["controls"]
if control["id"] == "D6-CANARY"
)
evidence = canary["evidence"][0]
path = root / evidence["path"]
claim = json.loads(path.read_text(encoding="utf-8"))
claim["breach_events"] = [
{
"breach_id": "breach-1",
"condition": (
"any_risk_data_rule_or_reconciliation_breach"
),
"observed_on": "2026-07-08",
"stop_event_id": "stop-1",
}
]
claim["stop_events"] = [
{
"stop_event_id": "stop-1",
"condition": (
"any_risk_data_rule_or_reconciliation_breach"
),
"triggered_on": "2026-07-08",
"trading_stopped": True,
"automatic_resume": False,
}
]
path.write_text(
json.dumps(claim, sort_keys=True),
encoding="utf-8",
)
evidence["sha256"] = hashlib.sha256(
path.read_bytes()
).hexdigest()
final_link = assessment["qualification_attestation"]
final_path = root / final_link["path"]
final_claim = json.loads(final_path.read_text(encoding="utf-8"))
final_claim["assessment_sha256"] = _final_assessment_digest(
assessment
)
final_path.write_text(
json.dumps(final_claim, sort_keys=True),
encoding="utf-8",
)
final_link["sha256"] = hashlib.sha256(
final_path.read_bytes()
).hexdigest()
result = evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=self._trusted_test_verifier,
)
self.assertTrue(result["canary_authorization_chain_verified"])
self.assertEqual(
result["canary_observation"]["breach_event_count"],
1,
)
self.assertEqual(
result["canary_observation"]["stop_event_count"],
1,
)
self.assertEqual(
result["qualification_state"],
"TEST_ONLY_STRUCTURAL_PASS",
)
def test_final_path_cannot_skip_pre_canary_history(self):
hard_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"]
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
hard_controls,
)
assessment["pre_canary_attestation"] = None
result = evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=self._trusted_test_verifier,
)
self.assertFalse(result["qualified"])
self.assertFalse(result["canary_authorization_chain_verified"])
self.assertEqual(
result["qualification_state"],
"PENDING_PRE_CANARY_HISTORY",
)
def test_canary_claim_must_reference_pre_authorization_hash(self):
hard_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"]
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
hard_controls,
)
canary = next(
control
for control in assessment["controls"]
if control["id"] == "D6-CANARY"
)
evidence = canary["evidence"][0]
path = root / evidence["path"]
claim = json.loads(path.read_text(encoding="utf-8"))
claim["pre_canary_authorization_sha256"] = "8" * 64
path.write_text(json.dumps(claim, sort_keys=True), encoding="utf-8")
evidence["sha256"] = hashlib.sha256(path.read_bytes()).hexdigest()
with self.assertRaisesRegex(
MaturityStandardError,
"does not reference the verified authorization",
):
evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=self._trusted_test_verifier,
)
def test_standard_hash_drift_rejects_old_assessment(self):
standard = copy.deepcopy(self.standard)
standard["domains"][0]["controls"][0]["acceptance"] += " changed"
with self.assertRaisesRegex(
MaturityStandardError,
"canonical hash does not match",
):
evaluate_assessment(
standard,
self.assessment,
repository_root=PROJECT,
)
def test_boolean_verifier_cannot_issue_a_qualification(self):
hard_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"]
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
hard_controls,
)
with self.assertRaisesRegex(
MaturityStandardError,
"attestation is not trusted",
):
evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=lambda *_args: True,
)
def test_repository_cannot_accept_a_fake_production_registry(self):
hard_controls = {
control["id"]
for domain in self.standard["domains"]
for control in domain["controls"]
if control["hard_gate"]
}
def fake_production_verifier(*_args):
return {
"trusted": True,
"trust_environment": "production",
"verifier_id": "forged-local-verifier",
"issuer_id": "forged-local-issuer",
"key_fingerprint_sha256": "7" * 64,
"policy_version": "forged-v1",
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
assessment = self._prepare_qualified_assessment(
root,
hard_controls,
)
with self.assertRaisesRegex(
MaturityStandardError,
"production verifier registry is not implemented",
):
evaluate_assessment(
self.standard,
assessment,
repository_root=root,
trusted_evidence_verifier=fake_production_verifier,
)
def test_hard_gates_cannot_make_the_published_threshold_unattainable(self):
standard = copy.deepcopy(self.standard)
soft_control = next(
control
for domain in standard["domains"]
for control in domain["controls"]
if control["id"] == "D1-REDUNDANCY"
)
soft_control["hard_gate"] = True
with self.assertRaisesRegex(
MaturityStandardError,
"hard-gate weights must exactly equal",
):
validate_standard(standard)
def test_hash_drift_is_rejected(self):
assessment = copy.deepcopy(self.assessment)
assessment["controls"][0]["status"] = "passed"
assessment["controls"][0]["evidence"] = [
{
"path": "profiles/production-80/standard.json",
"tier": "E4",
"sha256": "0" * 64,
}
]
with self.assertRaisesRegex(MaturityStandardError, "hash drift"):
evaluate_assessment(
self.standard,
assessment,
repository_root=PROJECT,
)
def test_architecture_is_valid_and_forbidden_import_is_detected(self):
current = validate_architecture(PROJECT / "src/quant_os")
self.assertTrue(current["ok"], current["violations"])
self.assertEqual(current["scope"], "new_namespace_only")
self.assertEqual(current["package_root"], "src/quant_os")
self.assertEqual(
current["migration_state"],
"transitioning_from_quant60_legacy",
)
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
for layer in current["layers"]:
(root / layer).mkdir()
(root / layer / "__init__.py").write_text("", encoding="utf-8")
(root / "research" / "bad.py").write_text(
"from quant_os.execution import submit\n",
encoding="utf-8",
)
(root / "research" / "bad_relative.py").write_text(
"from ..execution import submit\n",
encoding="utf-8",
)
(root / "research" / "bad_from_root.py").write_text(
"from quant_os import execution\n",
encoding="utf-8",
)
(root / "control" / "bad_vendor.py").write_text(
"from xtquant import xttrader\n",
encoding="utf-8",
)
(root / "research" / "bad_legacy.py").write_text(
"from quant60.experiment import run\n"
"from adapters.jqdata_local import load\n"
"from platforms.qlib_runner import main\n",
encoding="utf-8",
)
(root / "architecture.py").write_text(
"from .research import train\n",
encoding="utf-8",
)
(root / "bad_root.py").write_text("", encoding="utf-8")
(root / "unregistered").mkdir()
(root / "unregistered" / "__init__.py").write_text(
"",
encoding="utf-8",
)
invalid = validate_architecture(root)
self.assertFalse(invalid["ok"])
reasons = [item["reason"] for item in invalid["violations"]]
self.assertEqual(reasons.count("forbidden_dependency"), 4)
self.assertIn("vendor_sdk_outside_adapter", reasons)
self.assertIn("legacy_dependency_escape", reasons)
self.assertIn("unregistered_layer_package", reasons)
self.assertIn("unregistered_root_module", reasons)
if __name__ == "__main__":
unittest.main()
+278 -20
View File
@@ -17,7 +17,12 @@ from typing import Any, Dict, Iterable, List, Mapping, Sequence, Tuple
PROJECT = Path(__file__).resolve().parents[1] 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 = 2
PRODUCTION_80_STANDARD_PATH = "profiles/production-80/standard.json"
PRODUCTION_80_ASSESSMENT_PATH = (
"profiles/production-80/current-assessment.json"
)
BASELINE_60_PROFILE_PATH = "profiles/baseline-60/profile.json"
REACHABILITY_PATH = ( REACHABILITY_PATH = (
"releases/quant60-research-candidate-v1/reachability.json" "releases/quant60-research-candidate-v1/reachability.json"
) )
@@ -36,21 +41,37 @@ JOINQUANT_TARGET_PACKAGE_BUNDLE_SHA256 = (
SOURCE_PATHS: Tuple[str, ...] = ( SOURCE_PATHS: Tuple[str, ...] = (
"gate_scorecard.json", "gate_scorecard.json",
BASELINE_60_PROFILE_PATH,
PRODUCTION_80_STANDARD_PATH,
PRODUCTION_80_ASSESSMENT_PATH,
"dist/bundle_manifest.json", "dist/bundle_manifest.json",
REACHABILITY_PATH, REACHABILITY_PATH,
JOINQUANT_TARGET_PACKAGE_RUN_PATH, JOINQUANT_TARGET_PACKAGE_RUN_PATH,
"configs/baseline.json", "configs/baseline.json",
"README.md", "README.md",
"docs/ARCHITECTURE.md", "docs/ARCHITECTURE.md",
"docs/architecture/ARCHITECTURE_80.md",
"docs/AUDIT_2026-07-25.md", "docs/AUDIT_2026-07-25.md",
"docs/standards/QUANT_OS_80_STANDARD.md",
"docs/standards/BASELINE_60_VS_PRODUCTION_80.md",
"docs/getting-started/GETTING_STARTED_80.md",
"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, JOINQUANT_TARGET_PACKAGE_DOC_PATH,
"docs/TUSHARE_LOCAL_DATA.md", "docs/TUSHARE_LOCAL_DATA.md",
"MIGRATION.md",
"ops/README.md",
"ops/activate_static_status.sh",
"ops/deploy_static_status.sh",
"ops/nginx/quant-os-static.conf",
"ops/nginx/quant-os-legacy-redirects-302.conf",
"ops/nginx/quant-os-legacy-redirects-308.conf",
"runbooks/PLATFORM_DEPLOYMENT.md", "runbooks/PLATFORM_DEPLOYMENT.md",
"tools/build_release_reachability.py", "tools/build_release_reachability.py",
"tools/build_public_status.py", "tools/build_public_status.py",
"tools/check_tracked_secrets.py",
"tools/inventory_local_assets.py",
) )
ALLOWED_PROGRESS_STATUSES = frozenset( ALLOWED_PROGRESS_STATUSES = frozenset(
@@ -704,6 +725,12 @@ def _build_evidence(project: Path) -> List[Dict[str, Any]]:
"runbooks/PLATFORM_DEPLOYMENT.md", "runbooks/PLATFORM_DEPLOYMENT.md",
"runbook", "runbook",
), ),
(
"public-status-deployment",
"Atomic public status deployment",
"ops/README.md",
"runbook",
),
) )
result: List[Dict[str, Any]] = [] result: List[Dict[str, Any]] = []
for evidence_id, label, raw_path, kind in records: for evidence_id, label, raw_path, kind in records:
@@ -721,7 +748,38 @@ def _build_evidence(project: Path) -> List[Dict[str, Any]]:
return result return result
def _build_layers() -> List[Dict[str, Any]]: def _build_market_scope(standard: Mapping[str, Any]) -> Dict[str, Any]:
operating_defaults = standard.get("operating_defaults")
if not isinstance(operating_defaults, dict):
raise PublicStatusError("production-80 operating_defaults must be an object")
declared_scope = operating_defaults.get("declared_market_scope")
if not isinstance(declared_scope, dict):
raise PublicStatusError(
"production-80 declared_market_scope must be an object"
)
included = declared_scope.get("included")
excluded = declared_scope.get("excluded_fail_closed")
if included != ["SSE", "SZSE"] or excluded != ["BSE"]:
raise PublicStatusError(
"public market scope must explicitly include SSE/SZSE and fail-close BSE"
)
return {
"included": list(included),
"excludedFailClosed": list(excluded),
"executionScopeLabel": "沪深(上交所、深交所)",
"bseTradingEnabled": False,
"statement": (
"当前交易执行范围仅覆盖沪深;北交所尚未接入,"
"所有北交所委托必须 fail closed。"
),
}
def _build_layers(
*,
baseline_shadow_days: int,
production_shadow_days: int,
) -> List[Dict[str, Any]]:
return [ return [
{ {
"id": "data", "id": "data",
@@ -840,7 +898,10 @@ def _build_layers() -> List[Dict[str, Any]]:
"authenticated evidence envelope and exact replay", "authenticated evidence envelope and exact replay",
], ],
"limitations": [ "limitations": [
"无真实券商 attestation、回调/重启证明和 20 个交易日影子记录;没有实盘下单入口。" "无真实券商 attestation、回调/重启证明和影子记录;"
f"Baseline 60 至少需要 {baseline_shadow_days} 个交易日,"
f"Production 80 当前缺口为同一冻结候选至少 {production_shadow_days} "
"个交易日;没有实盘下单入口。"
], ],
"evidenceLinks": ["architecture", "platform-matrix", "scorecard"], "evidenceLinks": ["architecture", "platform-matrix", "scorecard"],
"releaseVerified": False, "releaseVerified": False,
@@ -917,7 +978,12 @@ 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],
*,
baseline_shadow_days: int,
production_shadow_days: int,
) -> List[Dict[str, Any]]:
delivery = scorecard.get("current_delivery", {}) delivery = scorecard.get("current_delivery", {})
if not isinstance(delivery, dict): if not isinstance(delivery, dict):
raise PublicStatusError("scorecard current_delivery must be an object") raise PublicStatusError("scorecard current_delivery must be an object")
@@ -958,13 +1024,17 @@ def _build_next_actions(scorecard: Mapping[str, Any]) -> List[Dict[str, Any]]:
}, },
{ {
"priority": 5, "priority": 5,
"title": "完成 QMT 券商合同探针与至少 20 日影子盘", "title": (
"完成 QMT 券商合同探针与 "
f"Production 80 的 {production_shadow_days} 日影子盘"
f"Baseline 60 门槛 {baseline_shadow_days} 日)"
),
"why": str(missing[3]), "why": str(missing[3]),
"gateIds": ["G6", "G8", "G9"], "gateIds": ["G6", "G8", "G9"],
}, },
{ {
"priority": 6, "priority": 6,
"title": "完成连续对账、恢复演练与券商合规确认", "title": "完成连续对账、恢复演练与券商程序化交易报告/核查确认",
"why": str(missing[4]), "why": str(missing[4]),
"gateIds": ["G8", "G10"], "gateIds": ["G8", "G10"],
}, },
@@ -977,6 +1047,8 @@ def _build_freshness(
joinquant_markdown: str, joinquant_markdown: str,
joinquant_target_package_run: Mapping[str, Any], joinquant_target_package_run: Mapping[str, Any],
tushare_markdown: str, tushare_markdown: str,
baseline_shadow_days: int,
production_shadow_days: int,
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
period_match = re.search( period_match = re.search(
r"(?m)^-\s+Period:\s+" r"(?m)^-\s+Period:\s+"
@@ -1123,7 +1195,12 @@ def _build_freshness(
"lastRealRuntimeAt": None, "lastRealRuntimeAt": None,
"value": "N/A", "value": "N/A",
"productionReady": False, "productionReady": False,
"semantics": "QMT 只有 bundle/harnessXtTrader 只有 fake-broker 只读合同;尚无真实运行或 20 日影子证据。", "semantics": (
"QMT 只有 bundle/harnessXtTrader 只有 fake-broker 只读合同;"
f"尚无真实运行或影子证据。Baseline 60 至少需要 "
f"{baseline_shadow_days} 个交易日,Production 80 当前缺口为同一"
f"冻结候选至少 {production_shadow_days} 个交易日。"
),
"evidenceLinks": ["platform-matrix", "platform-deployment", "scorecard"], "evidenceLinks": ["platform-matrix", "platform-deployment", "scorecard"],
}, },
] ]
@@ -1136,21 +1213,21 @@ def _validate_claim(scorecard: Mapping[str, Any]) -> None:
claim = str(policy.get("current_claim", "")) claim = str(policy.get("current_claim", ""))
if claim in FORBIDDEN_PUBLIC_CLAIMS: if claim in FORBIDDEN_PUBLIC_CLAIMS:
raise PublicStatusError(f"unsafe public release claim: {claim}") raise PublicStatusError(f"unsafe public release claim: {claim}")
if claim not in {"NOT_BASELINE_60", "BASELINE_60_VERIFIED"}:
raise PublicStatusError(f"unsupported baseline claim: {claim}")
if claim == "BASELINE_60_VERIFIED":
raise PublicStatusError(
"positive Baseline-60 publication is disabled until the "
"production issuer registry and signed baseline evaluator result "
"are implemented"
)
gates = scorecard.get("gates") gates = scorecard.get("gates")
if not isinstance(gates, list) or not gates: if not isinstance(gates, list) or not gates:
raise PublicStatusError("scorecard gates must be a non-empty list") raise PublicStatusError("scorecard gates must be a non-empty list")
passed = sum(gate.get("status") == "passed" for gate in gates if isinstance(gate, dict)) passed = sum(gate.get("status") == "passed" for gate in gates if isinstance(gate, dict))
threshold = int(policy.get("baseline_threshold", 60))
delivery = scorecard.get("current_delivery", {})
score = delivery.get("score") if isinstance(delivery, dict) else None
all_passed = passed == len(gates) all_passed = passed == len(gates)
if claim != "NOT_BASELINE_60":
if not all_passed or not isinstance(score, (int, float)) or score < threshold:
raise PublicStatusError(
"a baseline claim requires every gate passed and a threshold score"
)
if claim == "NOT_BASELINE_60" and all_passed: if claim == "NOT_BASELINE_60" and all_passed:
raise PublicStatusError( raise PublicStatusError(
"scorecard is internally inconsistent: every gate passed but claim is negative" "scorecard is internally inconsistent: every gate passed but claim is negative"
@@ -1187,6 +1264,135 @@ def _validate_public_payload(payload: Mapping[str, Any]) -> None:
) )
def _build_maturity80(
project: Path,
standard: Mapping[str, Any],
assessment: 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 quant_os.evidence import ( # pylint: disable=import-outside-toplevel
MaturityStandardError,
evaluate_assessment,
)
try:
evaluation = evaluate_assessment(
standard,
assessment,
repository_root=project,
)
except MaturityStandardError as exc:
raise PublicStatusError(
f"production-80 assessment failed canonical verification: {exc}"
) from exc
finally:
if inserted:
sys.path.remove(str(source_root))
controls = evaluation["controls"]
return {
"standardId": evaluation["standard_id"],
"standardCanonicalSha256": evaluation[
"standard_canonical_sha256"
],
"baselineProfileCanonicalSha256": evaluation[
"baseline_profile_canonical_sha256"
],
"assessedAsOf": evaluation["assessed_as_of"],
"rawScore": evaluation["raw_score"],
"effectiveScore": evaluation["effective_score"],
"maximumScore": evaluation["maximum_score"],
"qualificationThreshold": evaluation["qualification_threshold"],
"qualified": evaluation["qualified"],
"state": evaluation["qualification_state"],
"baseline60Passed": evaluation["baseline_60_passed"],
"structurallyEligible": evaluation["structurally_eligible"],
"trustedAttestationVerified": evaluation[
"trusted_attestation_verified"
],
"finalAttestationVerified": evaluation[
"final_attestation_verified"
],
"productionTrustRegistryAvailable": evaluation[
"production_trust_registry_available"
],
"verificationEnvironment": evaluation[
"verification_environment"
],
"testOnly": evaluation["test_only"],
"preCanaryStructurallyEligible": evaluation[
"pre_canary_structurally_eligible"
],
"preCanaryAttestationVerified": evaluation[
"pre_canary_attestation_verified"
],
"preCanaryAuthorized": evaluation["pre_canary_authorized"],
"preCanaryAuthorizationVerified": evaluation[
"pre_canary_attestation_verified"
],
"preCanaryAuthorization": evaluation[
"pre_canary_authorization"
],
"canaryAuthorizationChainVerified": evaluation[
"canary_authorization_chain_verified"
],
"canaryObservation": evaluation["canary_observation"],
"canaryRunning": evaluation["canary_running"],
"baseline60AttestationVerified": evaluation[
"baseline_60_attestation_verified"
],
"hardGateFailures": evaluation["hard_gate_failures"],
"domainFloorFailures": evaluation["domain_floor_failures"],
"prerequisites": evaluation["prerequisites"],
"scoreCaps": evaluation["score_caps"],
"domains": [
{
"id": domain["id"],
"name": domain["name"],
"score": domain["score"],
"weight": domain["weight"],
"minimumScore": domain["minimum_score"],
"floorPassed": domain["floor_passed"],
}
for domain in evaluation["domains"]
],
"controls": [
{
"id": control["id"],
"domainId": control["domain_id"],
"name": control["name"],
"weight": control["weight"],
"hardGate": control["hard_gate"],
"status": control["status"],
"minimumEvidenceTier": control["minimum_evidence_tier"],
"note": control["note"],
}
for control in controls
],
"controlsPassed": sum(
control["status"] == "passed" for control in controls
),
"controlsTotal": len(controls),
"hardGatesPassed": (
sum(
control["hard_gate"] and control["status"] == "passed"
for control in controls
)
),
"hardGatesTotal": sum(control["hard_gate"] for control in controls),
"evidencePolicy": (
"Only subject-bound evidence claims verified by a configured "
"trust provider, at or above each control's minimum tier, earn "
"points. The current repository has no production trust provider "
"and therefore cannot self-issue a positive qualification."
),
}
def build_public_status( def build_public_status(
project: Path, project: Path,
*, *,
@@ -1195,6 +1401,13 @@ def build_public_status(
) -> Dict[str, Any]: ) -> Dict[str, Any]:
project = project.resolve() project = project.resolve()
scorecard = _load_json(project / "gate_scorecard.json") scorecard = _load_json(project / "gate_scorecard.json")
production_80_standard = _load_json(
project / PRODUCTION_80_STANDARD_PATH
)
production_80_assessment = _load_json(
project / PRODUCTION_80_ASSESSMENT_PATH
)
baseline_60_profile = _load_json(project / BASELINE_60_PROFILE_PATH)
manifest = _load_json(project / "dist" / "bundle_manifest.json") manifest = _load_json(project / "dist" / "bundle_manifest.json")
reachability_manifest = _load_json(project / REACHABILITY_PATH) reachability_manifest = _load_json(project / REACHABILITY_PATH)
joinquant_target_package_run_raw = _load_json( joinquant_target_package_run_raw = _load_json(
@@ -1212,6 +1425,23 @@ def build_public_status(
) )
_validate_claim(scorecard) _validate_claim(scorecard)
try:
baseline_shadow_days = int(
baseline_60_profile["minimum_shadow_trading_days"]
)
production_shadow_days = int(
production_80_standard["qualification"][
"minimum_shadow_trading_days"
]
)
except (KeyError, TypeError, ValueError) as exc:
raise PublicStatusError(
"baseline-60 and production-80 shadow requirements must be integers"
) from exc
if baseline_shadow_days <= 0 or production_shadow_days <= baseline_shadow_days:
raise PublicStatusError(
"production-80 shadow requirement must exceed the positive baseline-60 requirement"
)
audit_as_of = str(scorecard.get("audit_as_of", "")) audit_as_of = str(scorecard.get("audit_as_of", ""))
try: try:
datetime.strptime(audit_as_of, "%Y-%m-%d") datetime.strptime(audit_as_of, "%Y-%m-%d")
@@ -1271,6 +1501,11 @@ def build_public_status(
project, project,
joinquant_target_package_run_raw, joinquant_target_package_run_raw,
) )
maturity80 = _build_maturity80(
project,
production_80_standard,
production_80_assessment,
)
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")
@@ -1284,6 +1519,16 @@ def build_public_status(
claim_policy = scorecard["claim_policy"] claim_policy = scorecard["claim_policy"]
delivery = scorecard["current_delivery"] delivery = scorecard["current_delivery"]
baseline_verified = (
claim_policy["current_claim"] == "BASELINE_60_VERIFIED"
and passed == len(gates)
and isinstance(delivery.get("score"), (int, float))
and delivery["score"] >= claim_policy["baseline_threshold"]
)
if bool(maturity80["baseline60Passed"]) != baseline_verified:
raise PublicStatusError(
"Baseline-60 scorecard and production-80 assessment disagree"
)
internal_delivery_gaps = delivery.get("internal_delivery_gaps", []) internal_delivery_gaps = delivery.get("internal_delivery_gaps", [])
external_evidence_missing = delivery.get("external_evidence_missing", []) external_evidence_missing = delivery.get("external_evidence_missing", [])
if not isinstance(internal_delivery_gaps, list) or len(internal_delivery_gaps) < 3: if not isinstance(internal_delivery_gaps, list) or len(internal_delivery_gaps) < 3:
@@ -1312,9 +1557,11 @@ def build_public_status(
"score": delivery.get("score"), "score": delivery.get("score"),
"scoreStatus": str(delivery.get("status")), "scoreStatus": str(delivery.get("status")),
"reason": str(delivery.get("reason")), "reason": str(delivery.get("reason")),
"releaseVerified": False, "releaseVerified": baseline_verified,
"liveReady": False, "liveReady": bool(maturity80["qualified"]),
}, },
"marketScope": _build_market_scope(production_80_standard),
"maturity80": maturity80,
"summary": { "summary": {
"testsRecorded": test_count, "testsRecorded": test_count,
"optionalSkipsRecorded": optional_skips, "optionalSkipsRecorded": optional_skips,
@@ -1338,7 +1585,7 @@ def build_public_status(
"Tushare completed-partition Qlib momentum smoke", "Tushare completed-partition Qlib momentum smoke",
], ],
"gateCoverageLabel": f"{passed}/{len(gates)} passed", "gateCoverageLabel": f"{passed}/{len(gates)} passed",
"productionReady": False, "productionReady": bool(maturity80["qualified"]),
"investmentValueClaim": False, "investmentValueClaim": False,
}, },
"candidateBoundary": { "candidateBoundary": {
@@ -1373,7 +1620,10 @@ def build_public_status(
"internal": [str(item) for item in internal_delivery_gaps], "internal": [str(item) for item in internal_delivery_gaps],
"externalEvidence": [str(item) for item in external_evidence_missing], "externalEvidence": [str(item) for item in external_evidence_missing],
}, },
"layers": _build_layers(), "layers": _build_layers(
baseline_shadow_days=baseline_shadow_days,
production_shadow_days=production_shadow_days,
),
"models": _build_models(), "models": _build_models(),
"platforms": _build_platforms(platform_markdown), "platforms": _build_platforms(platform_markdown),
"freshness": _build_freshness( "freshness": _build_freshness(
@@ -1381,6 +1631,8 @@ def build_public_status(
joinquant_markdown=joinquant_markdown, joinquant_markdown=joinquant_markdown,
joinquant_target_package_run=joinquant_target_package_run, joinquant_target_package_run=joinquant_target_package_run,
tushare_markdown=tushare_markdown, tushare_markdown=tushare_markdown,
baseline_shadow_days=baseline_shadow_days,
production_shadow_days=production_shadow_days,
), ),
"gates": gates, "gates": gates,
"bundles": { "bundles": {
@@ -1409,9 +1661,15 @@ def build_public_status(
}, },
}, },
"evidence": _build_evidence(project), "evidence": _build_evidence(project),
"nextActions": _build_next_actions(scorecard), "nextActions": _build_next_actions(
scorecard,
baseline_shadow_days=baseline_shadow_days,
production_shadow_days=production_shadow_days,
),
"provenance": { "provenance": {
"scorecard": "gate_scorecard.json", "scorecard": "gate_scorecard.json",
"production80Standard": PRODUCTION_80_STANDARD_PATH,
"production80Assessment": PRODUCTION_80_ASSESSMENT_PATH,
"bundleManifest": "dist/bundle_manifest.json", "bundleManifest": "dist/bundle_manifest.json",
"releaseReachability": REACHABILITY_PATH, "releaseReachability": REACHABILITY_PATH,
"platformMatrix": "docs/PLATFORM_MATRIX.md", "platformMatrix": "docs/PLATFORM_MATRIX.md",
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
"""Reject obvious reusable credentials without printing their values."""
from __future__ import annotations
import re
import subprocess
import sys
from pathlib import Path
PROJECT = Path(__file__).resolve().parents[1]
PATTERNS = (
re.compile(r"-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----"),
re.compile(r"\b(?:ghp|github_pat|glpat|gitea)_[A-Za-z0-9_-]{16,}\b"),
re.compile(r"https?://[^/\s:@]+:[^/\s@]{8,}@"),
re.compile(
r"(?i)\b(?:password|passwd|api[_-]?key|access[_-]?token|secret)"
r"\s*[:=]\s*[\"'][A-Za-z0-9+/=_-]{12,}[\"']"
),
)
def tracked_files() -> list[Path]:
result = subprocess.run(
["git", "ls-files", "-z"],
cwd=PROJECT,
check=True,
capture_output=True,
)
return [
PROJECT / item.decode("utf-8")
for item in result.stdout.split(b"\0")
if item
]
def main() -> int:
findings: list[tuple[str, int, int]] = []
for path in tracked_files():
try:
text = path.read_text(encoding="utf-8")
except (OSError, UnicodeDecodeError):
continue
for line_number, line in enumerate(text.splitlines(), start=1):
for pattern_number, pattern in enumerate(PATTERNS, start=1):
if pattern.search(line):
findings.append(
(
path.relative_to(PROJECT).as_posix(),
line_number,
pattern_number,
)
)
if findings:
for path, line_number, pattern_number in findings:
print(
f"credential-like content: {path}:{line_number} "
f"(rule {pattern_number})",
file=sys.stderr,
)
return 1
print("tracked secret scan: OK")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+139
View File
@@ -0,0 +1,139 @@
#!/usr/bin/env python3
"""Build a deterministic, content-hashed inventory of ignored local assets."""
from __future__ import annotations
import argparse
import hashlib
import json
import sys
from pathlib import Path
DEFAULT_INCLUDES = ("data", "artifacts", "mlruns")
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def build_inventory(root: Path, includes: tuple[str, ...]) -> dict:
root = root.resolve()
entries: list[dict] = []
summaries: dict[str, dict] = {}
for include in includes:
relative = Path(include)
if (
relative.is_absolute()
or ".." in relative.parts
or len(relative.parts) != 1
):
raise ValueError(f"unsafe include: {include}")
directory = root / relative
file_count = 0
total_bytes = 0
if directory.exists() and not directory.is_dir():
raise ValueError(f"include is not a directory: {include}")
if directory.is_dir():
for path in sorted(directory.rglob("*")):
if path.is_symlink():
raise ValueError(
f"symlink is not allowed in inventory: "
f"{path.relative_to(root)}"
)
if not path.is_file():
continue
size = path.stat().st_size
entries.append(
{
"path": path.relative_to(root).as_posix(),
"size": size,
"sha256": _sha256(path),
}
)
file_count += 1
total_bytes += size
summaries[include] = {
"available": directory.is_dir(),
"files": file_count,
"bytes": total_bytes,
}
body = {
"schema_version": "quant-os-local-assets-inventory/1.0",
"includes": list(includes),
"summaries": summaries,
"entries": entries,
}
body["inventory_sha256"] = hashlib.sha256(
json.dumps(
body,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
).hexdigest()
return body
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("root", type=Path)
parser.add_argument(
"--include",
action="append",
dest="includes",
help="one top-level ignored directory; repeat as needed",
)
parser.add_argument("--output", type=Path)
parser.add_argument("--compare", type=Path)
args = parser.parse_args()
includes = tuple(args.includes or DEFAULT_INCLUDES)
try:
inventory = build_inventory(args.root, includes)
except (OSError, ValueError) as exc:
print(f"inventory failed: {exc}", file=sys.stderr)
return 2
encoded = (
json.dumps(
inventory,
ensure_ascii=False,
sort_keys=True,
indent=2,
allow_nan=False,
)
+ "\n"
)
if args.compare is not None:
try:
expected = json.loads(args.compare.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
print(f"cannot read comparison inventory: {exc}", file=sys.stderr)
return 2
if inventory != expected:
print("local asset inventory differs", file=sys.stderr)
return 1
if args.output is None:
print(encoded, end="")
else:
args.output.write_text(encoded, encoding="utf-8")
print(
json.dumps(
{
"ok": True,
"files": len(inventory["entries"]),
"inventory_sha256": inventory["inventory_sha256"],
"output": args.output.name,
},
sort_keys=True,
)
)
return 0
if __name__ == "__main__":
raise SystemExit(main())