10 KiB
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 set;numeric tolerance below |
| L3 | Target | target weight/quantity、frozen/sellable state | weight tolerance;exact 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,还要:
- 拒绝 incomplete marker;
- 要求目录是 manifest 声明的精确 artifact 集;
- 重建每个 bar/membership 领域对象;
- 重算质量统计、内容 hash 与
data_version; - 拒绝重复键、非法 OHLC、停牌非零量、非法限价、缺失 factor 或非布尔/
缺失
is_st; - 拒绝 retrieval 当日仍未在 Asia/Shanghai 24:00 完成的日线。
验证入口:
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 至少保存:
{
"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 保存后执行:
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 使用:
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 必须包含:
(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 折入 commission,minimum 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,不能只写“平台差异”:
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:
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:
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 | No;contract 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 | Yes,pre-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。