feat: add Quant OS A-share baseline

This commit is contained in:
2026-07-26 12:54:04 +08:00
commit 48c5f64bbd
98 changed files with 31874 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
# Quant OS cross-engine consistency contract
“本地、聚宽、QMT、Qlib 都能跑”不等于四条净值曲线必须逐点相同。跨引擎
一致性按层比较:相同信息集应产生相同计算;平台特有的日历、价格语义和成交
差异必须被记录、归因和限制。
## 1. Comparison levels
| Level | Compared artifact | Same canonical input required | Pass rule |
| --- | --- | --- | --- |
| L0 | Source/config manifest | Git state、portable-core hash、strategy/model version、symbol map | Exact |
| L1 | Data/universe | `as_of`、sessions、symbols、PIT membership、raw/factor/declared adjusted prices、tradability | Exact values or explicit provider reason |
| L2 | Signal | symbol、horizon、lookback/skip、score、model bundle | Exact symbol setnumeric tolerance below |
| L3 | Target | target weight/quantity、frozen/sellable state | weight toleranceexact lot quantity |
| L4 | Order intent | ID、side、quantity、limit、type、TIF、timing、reason | Identical broker snapshot/rules 时 exact |
| L5 | Order/fill/portfolio | status、fills、fees、cash、positions | Semantic invariants;相同 fill fixture 才要求 exact |
| L6 | Performance | return、turnover、drawdown、cost | Diagnostic;不能代替 L1—L5 |
只比较 L6 不会通过 G9。Alpha158/LightGBM 与 portable momentum 是不同 model
spec,本来就不是 L2 peer;可做研究比较,不能冒充同策略 parity。
## 2. Canonical snapshot is the L1 handoff
JQData ingestion 通过官方 `get_bars` 保存:
- raw `open/high/low/close/volume/money`
- `factor``pre_close``high_limit``low_limit``paused`
- 每个 member-date 的 PIT `is_st`
- 每个交易日的历史指数成员。
快照必须保持 `adjustment=none`front-adjusted feature history 在每个
decision `as_of` 时由当时已保存的 raw close + factor 构造。这样执行价仍是
raw,且后来的 factor 不能改写旧决策。
L1 verifier 不只比文件 hash,还要:
1. 拒绝 incomplete marker
2. 要求目录是 manifest 声明的精确 artifact 集;
3. 重建每个 bar/membership 领域对象;
4. 重算质量统计、内容 hash 与 `data_version`
5. 拒绝重复键、非法 OHLC、停牌非零量、非法限价、缺失 factor 或非布尔/
缺失 `is_st`
6. 拒绝 retrieval 当日仍未在 Asia/Shanghai 24:00 完成的日线。
验证入口:
```bash
PYTHONPATH=src:. python -c \
"from quant60.data_snapshot import verify_data_snapshot; verify_data_snapshot('data/snapshots/csi500-2024/manifest.json')"
```
`snapshot-backtest``snapshot-decision` 都先通过该 verifier。它们能证明
同一个 snapshot 的本地可重放性,但在真实授权 JQData run 和平台导出前,
不能证明 provider entitlement 或 G9。
## 3. Required run manifest
每个 comparison 至少保存:
```json
{
"run_id": "unique-id",
"engine": "local|joinquant|qmt_builtin|qmttools|xttrader|qlib",
"mode": "fixture|provider_snapshot|backtest|simulation|shadow|live",
"git_commit": "commit-or-DIRTY",
"portable_core_sha256": "sha256",
"strategy_or_model_version": "immutable-id",
"python_version": "major.minor.patch",
"platform_version": "value-or-unknown",
"config_hash": "sha256",
"data_version": "immutable-id",
"rules_version": "immutable-id",
"timezone": "Asia/Shanghai",
"signal_as_of": "ISO-8601 timestamp",
"last_feature_bar": "ISO-8601 date-or-time",
"first_executable_time": "ISO-8601 timestamp",
"price_adjustment": "raw|pre|post|front_ratio|PIT_FRONT_FROM_RAW_AND_FACTOR",
"fill_model": "declared-name",
"evidence_class": "fake|synthetic|provider-snapshot|real-platform|broker"
}
```
unknown platform version 可用于 smoke,但阻止 release certification。
本地 execution/provider snapshot run 保存后执行:
```bash
PYTHONPATH=src:. python -m quant60 verify-manifest \
artifacts/<run-id>/manifest.json
```
它检查 incomplete marker、精确 artifact set/hash、ledger chain/head、
report/manifest identity、saved/current source/schema aggregate 和 payload
schema。snapshot decision 使用:
```bash
PYTHONPATH=src:. python -m quant60 verify-snapshot-decision \
artifacts/<decision-id>/manifest.json
```
## 4. Numeric and semantic tolerances
对传入 `portable_core.py` 的同一固定 close arrays
| Field | Tolerance |
| --- | --- |
| canonical symbol、rank、selected set | Exact |
| momentum score | absolute error ≤ `1e-12` |
| target weight | absolute error ≤ `1e-10` |
| target quantity | Exact integer |
| order delta | Exact signed integer |
真实 provider 数据先比较输入数组。不同复权约定、停牌填充、成分股日期或
feature cutoff 导致的 score 差异是 L1 failure,不是浮点噪声。
本地货币 ledger 按配置的 currency quantum 比较。hosted NAV 只有在 bars、
company actions、fees 和 fill events 都相同时才要求相同;否则必须分类:
- `input-price difference`
- `timing difference`
- `tradability/fill difference`
- `explicit-fee difference`
- `residual unexplained difference`
residual 必须为零或低于 release-specific bound,不能静默并入 slippage。
## 5. Clock alignment
canonical weekly clock 是 ISO 周第一实际交易日完整 close,紧接着下一实际
交易日 open。它不是固定星期几;单交易日长假周会自然跨到下一 ISO 周。
comparison key 必须包含:
```text
(signal_as_of, last_feature_bar, first_executable_time, adjustment_mode)
```
任一元素不同就不是 strict peer,只能做诊断 comparison。
本地 snapshot backtest 使用:
- exact daily PIT membership
- decision date raw close + factor 构造 point-in-time front-adjusted signal
- next session raw open 执行;
- provider daily pause/limit/pre-close
- provider daily PIT `is_st`ST 不新买,已持有 ST 只卖;
- prior-session volume capacity。
JoinQuant/QMT 必须导出相同输入或其 hash 才能进入 L2—L4 strict comparison。
## 6. Platform interpretation and current facts
- **Local**:固定 fixture 的 deterministic contract oracle,不自动等于市场
真相。
- **JoinQuant**:以前一交易日查询 PIT index/ST,以批量 `history` 读取完成
日线,并显式设置费用、滑点和 10% 参与率;必须导出实际 close
arrays/hash、plan、orders/fills 和平台配置。本地 fake harness 只证明
wiring。2026-07-26 已完成一次 2024 全年真实 hosted smoke,运行日志无
ERROR;但尚未导出上述相同输入与分层结果,所以它是 runtime evidence
不是 strict peer/G9 证据。
- **QMT built-in / qmttools**:共享 bundle 但生命周期不同,均硬
backtest-only;固定中证 500 benchmark,以历史 timetag 查询 PIT 成分/ST。
built-in 的 10% 最大成交量占比需要 QMT UI 设置并取证,qmttools
程序化传入。真实 QMT 环境尚未运行。qmttools 把 transfer fee 折入
commissionminimum commission/fill 行为是声明差异。
- **XtTrader**:比较 broker snapshot、order intent 与 normalized callback。
当前已有只读 HMAC 账户绑定、broker observation/snapshot 和 exact
shadow-plan verifier,但只有 fake broker contract,没有真实 shadow。
- **Qlib momentum**`pyqlib==0.9.7` 的 native fixture 已真实运行,产生
24 条 signal;结果保存 signal/report hash、表边界和从 report 重算的
portfolio 指标。它证明 runtime/path 可执行,不证明真实市场或 L3/L4。
- **Qlib Alpha158/LightGBM**fixture 上已真实完成 model + Recorder。
runner 自动以 `setdefault` 设置 `MLFLOW_ALLOW_FILE_STORE=true` 以兼容本地
MLflow file store,并读取 portfolio report 保存 hash、边界和重算指标。
两股票 synthetic 性能没有投资意义,且该模型不是 portable momentum 的
parity peer。
- **Mock parity exporter**:当前能逐层比较 JoinQuant/QMT fake wrapper 的
score、weight、quantity、delta,并校验 source hash。它明确是
`mock_contract_only``real_platform_pass=false``gate_credit=[]`
## 7. Difference reason codes
必须使用枚举 reason code,不能只写“平台差异”:
```text
CALENDAR
SYMBOL_MAP
UNIVERSE
MISSING_BAR
SUSPENSION_FILL
PRICE_ADJUSTMENT
AS_OF_CLOCK
ROUNDING
FEE_SCHEDULE
PRICE_LIMIT
T_PLUS_ONE
VOLUME_LIMIT
FILL_MODEL
BROKER_STATE
FLOAT_TOLERANCE
UNEXPLAINED
```
symbol set、signal、target、order intent、cash 或 position 出现任何
`UNEXPLAINED` 都阻止 release。
## 8. Runnable comparison support
mock-only exporter
```bash
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
```
Qlib native momentum fixture
```bash
python3.12 -m venv .venv-qlib312
source .venv-qlib312/bin/activate
python -m pip install -r requirements/research-py312.txt
python tools/build_qlib_tiny_fixture.py artifacts/qlib-fixture --days 80
PYTHONPATH=src:. python -m platforms.qlib_runner \
--provider-uri artifacts/qlib-fixture \
--market csi300 --benchmark SH000300 \
--start 2024-02-01 --end 2024-04-19 \
--lookback 20 --topk 1 --n-drop 1 --rebalance weekly \
--output-json artifacts/qlib-native/result.json
```
两者都不是 G9 的真实平台证据。
## 9. What counts toward G9
| Evidence | Counts toward G9? |
| --- | --- |
| symbol/unit tests、fake JoinQuant/QMT harness | Supporting only |
| mock parity exporter | Nocontract regression only |
| local snapshot twice with same hash | Supporting G7/L1 only |
| Qlib 24-signal synthetic fixture smoke | Runtime evidence only |
| Qlib Alpha158 synthetic Recorder smoke | Research runtime evidence only |
| real JoinQuant export vs local canonical run | Yes,覆盖的 layers/dates |
| licensed qmttools run with saved version/result | Yes,覆盖的 layers |
| XtTrader shadow using real broker snapshot | Yespre-trade/order semantics |
| ≥20 trading days QMT shadow, zero unexplained differences | 文章 G9 必需 |
真实 JoinQuant smoke 已运行;QMT 和 broker shadow 尚未运行,而且
JoinQuant 仍缺同输入导出与 local peer,所以 G9 继续为 `not_passed`
状态权威是
[`gate_scorecard.json`](../gate_scorecard.json)。