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
+209
View File
@@ -0,0 +1,209 @@
# Incident and safe-mode runbook
This runbook describes operator actions. The current scaffold does not implement a production supervisor, automatic broker kill switch or durable reconciliation service. Do not mistake these procedures for automated guarantees.
## Universal first response
For any unexplained data, order, cash or position state:
1. stop generating and submitting **new opening orders**;
2. do not blindly cancel or resubmit—first query the broker for current orders and fills;
3. capture local time, broker/source time, platform version, run/config/data identifiers and the last known ledger hash;
4. export a fresh read-only broker snapshot;
5. preserve logs and raw callbacks locally with credentials/account identifiers protected;
6. classify whether existing risk-reduction orders may continue; if uncertain, require human approval;
7. recover only after the exit condition in this runbook is met.
“Restart until it works” is not a recovery method. A restart can duplicate orders when submission succeeded but the acknowledgement was lost.
## Severity
| Severity | Examples | Posture |
| --- | --- | --- |
| S0 | Credential exposure, unexplained live position/cash, suspected duplicate live order, wrong account | Stop all strategy submissions; broker/operator intervention |
| S1 | Broker disconnect during active orders, unknown order state, reconciliation difference, stale live data | Block new orders; query and reconcile |
| S2 | Platform backtest failure, Qlib dependency/model failure, synthetic test regression | No production release; live system unaffected if isolated |
| S3 | Documentation, optional report or non-critical metric failure | Record and repair before next release |
## Data stale, missing or inconsistent
Symptoms:
- latest bar/session absent;
- provider and canonical hashes unexpectedly differ;
- timestamps are in the future or timezone is unknown;
- suspended sessions were forward-filled without an explicit rule.
Actions:
1. block target publication;
2. identify the last complete immutable data version;
3. compare provider fetch time, event time and available time;
4. rerun quality checks without mutating the prior snapshot;
5. if using an older snapshot for risk reduction, mark the decision `STALE_DATA_RISK_REDUCTION_ONLY`.
Exit: a new immutable snapshot passes quality checks and the same input replays the same signal/target. Never patch a historical snapshot in place.
## QMT/MiniQMT disconnect
Symptoms:
- native API import works but query calls fail;
- callbacks stop;
- QMT terminal logs out or session reconnects;
- order request timed out without an acknowledgement.
Actions:
1. block new submissions;
2. keep the client/terminal state and logs; do not immediately allocate a new session ID and resend;
3. restore terminal login/connectivity;
4. query assets, positions, orders and fills for the full affected window;
5. match by local idempotency key, broker order ID, symbol/side/quantity/time;
6. classify every pending local order as broker-known terminal/open or `UNKNOWN`;
7. escalate any `UNKNOWN` to a human/broker before resubmission.
Exit: no unknown order, position/cash reconcile, callback stream is fresh, and a read-only preflight passes.
## Lost acknowledgement or possible duplicate order
Actions:
1. never assume a timeout means rejection;
2. query broker orders and fills first;
3. search using client strategy/order labels plus symbol, side, quantity and time;
4. if one broker order matches, bind its broker ID and continue from broker state;
5. if multiple match, stop the affected symbol/account and escalate;
6. resubmit only after proving no live/open/filled broker order represents the intent.
Exit: a one-to-one mapping exists or the operator explicitly retires the intent.
## Callback duplicate, out of order or unknown state
Actions:
1. preserve the raw callback and its receive/source timestamps;
2. deduplicate by broker event identity or a documented content hash;
3. rebuild state from broker query plus the complete event stream;
4. do not force a terminal local order backwards to an earlier state;
5. map unmapped broker codes to `UNKNOWN`, not the nearest convenient status.
Exit: normalized event history is monotonic/idempotent and equals the broker's current query.
## Reconciliation difference
Compare four sets independently:
```text
cash
positions and sellable quantities
orders
fills and fees
```
Actions:
1. block new opening orders;
2. freeze the local ledger and snapshot it;
3. determine whether the difference is timing, fee, corporate action, manual trade, callback loss or wrong account;
4. append an explicit correction/reconciliation event; never edit prior ledger lines;
5. require human review for manual trades, unexplained fees or position differences.
Exit: zero unexplained difference and a signed reconciliation report. A balancing “miscellaneous P&L” line is not an explanation.
## Price limit, suspension or zero liquidity
Actions:
1. treat the position as held/frozen; never mark it sold because the target is zero;
2. stop repeated cancel/resubmit loops;
3. retain exposure in risk and cash calculations;
4. schedule a new decision only when a valid quote and broker sellable quantity exist;
5. record opportunity cost separately from explicit transaction cost.
Exit: valid tradability returns or the target is deliberately withdrawn.
## Insufficient cash or T+1 rejection
Actions:
1. refresh broker cash, positions, sellable quantity, open orders and fills;
2. include reserved cash/open buy orders and minimum commission;
3. do not sell today's purchase to fund another order;
4. reduce/cancel unsubmitted buys before modifying risk-reduction sells;
5. record the target-to-order shortfall reason.
Exit: pre-trade calculation based on the fresh broker snapshot passes.
## Ledger integrity or disk failure
Symptoms:
- hash-chain verification fails;
- JSONL is truncated;
- disk is full;
- atomic replacement did not complete.
Actions:
1. stop submission;
2. preserve the corrupted file and filesystem diagnostics;
3. verify the last known good immutable copy;
4. query broker truth for the entire uncertain interval;
5. reconstruct a new ledger by replay, retaining a link to the incident and old head hash;
6. never delete or hand-edit the corrupt original.
Exit: reconstructed ledger verifies and reconciles to broker truth; storage monitoring and free space are restored.
## Clock or timezone drift
Actions:
1. block time-sensitive target/order publication;
2. record OS, platform and broker times;
3. restore trusted time synchronization;
4. recompute `as_of` and executable-window eligibility;
5. discard—not rename—any decision that used future or ambiguous data, retaining it as failed evidence.
Exit: clocks are within the release tolerance and a fresh decision is computed.
## Model, optimizer or Qlib failure
Actions:
1. do not silently substitute a different model or equal weight;
2. mark the decision `NO_TRADE` or, when separately approved, `RISK_REDUCTION_ONLY`;
3. preserve model/config/data versions and traceback;
4. reproduce in the isolated Python 3.12 research environment;
5. require the normal champion promotion process before restoring.
Exit: the approved model/run reproduces and its outputs pass schema/risk gates.
## Credential exposure
Treat as S0. Stop order automation, rotate/revoke the credential, inspect Git history/artifacts/logs and notify the provider/broker when appropriate. Removing a file without rotation is insufficient. Follow [`docs/SECURITY.md`](../docs/SECURITY.md).
## Evidence template
Save an incident record outside credential-bearing logs:
```text
incident_id:
severity:
opened_at / closed_at:
account_hash:
engine/mode/version:
git_commit:
config_hash / data_version / rules_version:
last_good_ledger_hash:
symptom:
orders/fills at risk:
containment:
broker queries performed:
root cause:
recovery evidence:
operator:
follow-up tests:
```
An incident remains open while any affected broker order or position is `UNKNOWN`.
+551
View File
@@ -0,0 +1,551 @@
# Quant OS platform deployment and verification runbook
从仓库中的 `quant-os` 项目根目录运行命令。本文没有任何命令会启用真实订单。
Quant OS 是项目名;`quant60` 是第一版 A 股 Baseline 内核和 CLI namespace。
当前结论是 `NOT_BASELINE_60`。最近一次标准库套件为 211 tests、OK、
3 个可选路径 skip。CPython 3.12 + `pyqlib==0.9.7` 的 native momentum
和 Alpha158/LightGBM/Recorder fixture 已真实跑通;JoinQuant、QMT 与 broker
中,JoinQuant 已有一次 2024 全年真实 hosted smokeQMT 与 broker 尚无
真实运行证据;聚宽仍缺完整导出与同输入 peer。
## 0. One-command local verification
要求 Python ≥3.10。核心路径无第三方运行依赖:
```bash
make local
```
等价入口:
```bash
bash scripts/validate_all.sh
```
流程包括:
1. standard-library unit/contract suite
2. JoinQuant/QMT hosted bundle
3. synthetic execution smoke + exact manifest verifier
4. synthetic research-to-target + research manifest verifier
5. JoinQuant/QMT mock parity export + verifier
6. capability probe
7. Colab notebook syntax check + local cell execution。
`make local` 成功是本地代码证据,不是平台或 Gate 证据。
## 1. Build hosted single-file artifacts
JoinQuant 和 QMT built-in 不能假设已安装本地 `quant60` 包。生成内联
Python 3.6-compatible portable core 的单文件:
```bash
python3 tools/bundle_platforms.py
```
输出:
```text
dist/joinquant_strategy.py
dist/qmt_builtin_strategy.py
dist/bundle_manifest.json
```
manifest 把两个 bundle 绑定到相同 `portable_core.py` SHA-256。QMT bundle
另做 legacy source compatibility 检查。
静态 capability
```bash
PYTHONPATH=src:. python3 tools/capability_probe.py
```
只有在明确允许导入 proprietary/optional modules 的环境中才用:
```bash
PYTHONPATH=src:. python3 tools/capability_probe.py --deep
```
probe 不执行平台回测,也不发送 broker mutation。
## 2. JQData authorized snapshot to local PIT backtest
### Prerequisites
- 用户授权的 JQData 账号和对应数据权益;
- provider licence 允许的本地/Colab 保存方式;
- CPython 3.12 独立环境。
凭据仅由 environment 或无回显 prompt 注入,不能写入 Git、OB、notebook
source/output 或 snapshot。
```bash
python3.12 -m venv .venv-jqdata
source .venv-jqdata/bin/activate
python -m pip install -r requirements/jqdata.txt
```
下载中证 500 示例:
```bash
PYTHONPATH=src:. python tools/jqdata_snapshot.py \
--index 000905.XSHG \
--start 2024-01-02 \
--end 2024-12-31 \
--output data/snapshots/csi500-2024
```
adapter 使用 `get_bars` 保存未复权 OHLCV/money、factor、`pre_close`
`high_limit``low_limit``paused`,通过 `get_extras("is_st", ...)`
保存 PIT ST 状态,并逐交易日获取历史指数成员。`skip_paused=False` 保留
停牌日。请求的 `end` 必须早于 Asia/Shanghai 的 retrieval date,确保日线在
官方 24:00 完成后才被标为 `T_CLOSE_COMPLETE`;字段、日期、ST 或成员覆盖
不完整即 fail closed。manifest 记录 query、provider version、retrieval
time、内容 hash 与 `data_version`,不记录 credential。
显式运行 semantic verifier
```bash
PYTHONPATH=src:. python -c \
"from quant60.data_snapshot import verify_data_snapshot; verify_data_snapshot('data/snapshots/csi500-2024/manifest.json')"
```
verifier 要求:
- 无 incomplete marker
- 目录是精确 artifact set
- 文件与 canonical row hash 正确;
- bar/membership 可以重建为合法领域对象;
- quality summary、query semantic 和 `data_version` 可重算。
- `is_st` 是严格布尔值并覆盖所有 member-date;缺失不能当作非 ST。
运行动态 PIT 成分本地事件回测并验证 release artifacts
```bash
PYTHONPATH=src:. python -m quant60 snapshot-backtest \
--snapshot data/snapshots/csi500-2024/manifest.json \
--config configs/baseline.json \
--output artifacts/csi500-2024-backtest
PYTHONPATH=src:. python -m quant60 verify-manifest \
artifacts/csi500-2024-backtest/manifest.json
```
生成单日 Signal/Target/Order Delta
```bash
PYTHONPATH=src:. python -m quant60 snapshot-decision \
--snapshot data/snapshots/csi500-2024/manifest.json \
--config configs/baseline.json \
--as-of 2024-12-31 \
--equity 1000000 \
--output artifacts/csi500-2024-decision
PYTHONPATH=src:. python -m quant60 verify-snapshot-decision \
artifacts/csi500-2024-decision/manifest.json
```
生产账户输入应把 `--equity` 替换成 `--broker-snapshot`。broker snapshot
必须满足 schema、日期/新鲜度、持仓/可卖量和 no-unreconciled-open-order
要求;否则 fail closed。
这个 backtest 用 raw price 执行,以每个 decision `as_of` 的 raw close +
factor 构造 point-in-time front-adjusted momentum;使用 provider daily
paused/limits/pre-close/is_st、prior-session volume、T+1、百股单位和显式
费用。ST 不进入新目标;已持有 ST 只允许卖出,停牌持仓冻结。
它仍是本地 fill model,不是 JoinQuant/QMT 真实成交证据。
## 3. Colab
打开 `notebooks/quant_os_colab.ipynb`,默认执行全部 cell。默认流程 clone
代码后运行测试、bundle、两条 synthetic pipeline 和 verifier,不需要账号。
可选:
- `RUN_JQDATA=True`:无回显获取 credential,生成 snapshot、PIT backtest
和 decision
- `RUN_QLIB=True`:仅在 CPython 3.12 安装固定 Qlib stack 并跑 native
fixture。
禁止把 password/token/credential clone URL 写进 cell。不要保存带 secret、
授权数据或账户信息的 notebook output;完成后清除 runtime。
本地验证 notebook
```bash
python3 tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb
python3 tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb --execute
```
## 4. JoinQuant hosted backtest
### Prerequisites
- 用户已登录 JoinQuant
- project/data entitlement
- 已记录 backtest dates、capital、benchmark 和预期 evidence path。
步骤:
1. 运行 `python3 tools/bundle_platforms.py`
2. 新建 JoinQuant 策略;
3. 把 `dist/joinquant_strategy.py` 完整上传/粘贴;
4. 保留 `set_option("avoid_future_data", True)`
`set_option("use_real_price", True)`
5. 先选 backtest,不直接提升 simulation
6. 记录 runtime/data version、dates、capital、benchmark、
fee/slippage 和 bundle manifest hash
7. 导出 `g.quant60_last_plan`、orders/fills、logs 和完整结果。
canonical weekly clock 是 ISO 周第一实际交易日完整 close,紧接着下一实际
交易日 open。wrapper 以 daily-open state machine 保存 close token,因此
单交易日长假周会跨到下一周执行。它以 `context.previous_date` 查询 PIT
指数成员和 `get_extras(..., "is_st")`,用批量
`history(..., fq="pre")` 读取已完成日线;卖单先行,以 `order_target`
提交 `current + executable delta`,并在提交前刷新 current data gate。
手续费、滑点和 10% 最大成交量占比均在策略内显式设置。
科创板订单使用当日价格上下限作为 `LimitOrderStyle` 最坏价保护;共享核心
不会生成不足 200 股的普通申报,并保留不足 200 股余额一次性卖清的例外。
2026-07-26 的已完成参考 run 见
[`JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md`](../docs/JOINQUANT_HOSTED_EVIDENCE_2026-07-26.md)。
它可以用于验证上传与运行步骤,但不能替代步骤 7 的完整导出。
JoinQuant 拥有 hosted fill semantics。要做 G9,必须再保存和本地可比较的
close arrays 或 hash;“平台显示回测成功”本身不够。
rollback:停止 hosted simulation,保留失败 run/export,重新部署上一个已
review 且 manifest hash 已知的 bundle。不要直接编辑 generated file;修改
wrapper/core 后重新测试和 bundle。
## 5. QMT built-in Python backtest
### Prerequisites
- 券商授权的 QMT research terminal 和历史数据;
- QMT Python editor/backtest
- 已记录 client/build、broker plugin 和 data version。
步骤:
1. 运行 `python3 tools/bundle_platforms.py`
2. 在 QMT 导入 `dist/qmt_builtin_strategy.py`
3. 选择 `1d`**backtest**
4. 主图/benchmark 选择 `000905.SH`,在 UI 设置 capital/date range
5. 在回测设置中把最大成交量占比设为 **10%**
6. 确认“中证 500”历史板块成员数据以及历史 ST 数据已下载且账号有权限;
7. 用一只已知历史 ST 股票做 `get_his_st_data` 正例探针;
8. 运行并导出结果、日志、UI 配置截图和 `bundle_manifest.json`
QMT built-in 文档环境是 Python 3.6,所以上传 bundle,不安装 Python ≥3.10
本地包。策略状态存放在模块全局 `g`,避免 `ContextInfo` 用户属性在下个
`handlebar` 回滚。bundle 硬 backtest-only;参数、账号或环境变量都不能
开启 live `passorder`
wrapper 用原始毫秒 timetag 查询决策日历史“中证 500”成分,用
`ContextInfo.get_his_st_data` 排除 ST/*ST/PT。接口缺失、返回格式异常或
覆盖不足会 fail closed。QMT 可能用空结果同时表示“无数据”或“无 ST”,所以
正例探针和版本/权限记录是正式证据的硬前置。
## 6. qmttools native-Python backtest
### Prerequisites
- Windows 上由券商/QMT 分发且匹配 Python 的 `xtquant`
- 已登录 QMT terminal
- driver/universe 历史数据已下载且有 entitlement。
```powershell
$env:PYTHONPATH = "src;."
python tools/bundle_platforms.py
python -m platforms.qmt_research_runner `
dist/qmt_builtin_strategy.py `
--stock-code 000905.SH `
--start 20240102 `
--end 20241231 `
--period 1d `
--asset 1000000 `
--account-id test
```
runner 固定 `trade_mode=backtest``quote_mode=history`,从 baseline
配置固定 benchmark `000905.SH` 与 10% `max_vol_rate`,并在
`run_strategy_file` 前做只读 one-bar preflight。真实证据禁止
`--skip-data-preflight`
slippage、volume participation、stamp duty、commission 和 minimum
commission 来自 `configs/baseline.json` 并绑定 hash。QMT 没有独立 transfer
fee 参数,所以 transfer fee 折入 open/close commissionminimum commission
和 fill behavior 仍是必须单列的 engine difference。
当前 CLI 只输出摘要,真实 evidence 还必须在授权环境保存完整平台结果。
## 7. XtTrader read-only preflight and shadow boundary
### Prerequisites
- logged-in MiniQMT/QMT 与 broker permission
- 本地 `QMT_USERDATA_PATH``QMT_SESSION_ID``QMT_ACCOUNT_ID`
- 本地 `QMT_ACCOUNT_HASH_KEY_FILE`(文件内容是独立 secret);
- broker-distributed `xtquant`
- 不与其他策略冲突的 numeric session ID。
read-only preflight
```powershell
$env:PYTHONPATH = "src;."
@'
import os
from adapters.xttrader_live import XtTraderLiveAdapter
adapter = XtTraderLiveAdapter.from_xtquant(
userdata_path=os.environ["QMT_USERDATA_PATH"],
session_id=int(os.environ["QMT_SESSION_ID"]),
account_id=os.environ["QMT_ACCOUNT_ID"],
)
try:
adapter.connect()
asset = adapter.query_asset()
asset.pop("account_id", None)
print({
"state": adapter.state,
"asset": asset,
"position_count": len(adapter.query_positions()),
"order_count": len(adapter.query_orders()),
"trade_count": len(adapter.query_trades()),
})
finally:
adapter.close()
'@ | python -
```
以上原始查询只用于诊断;正式只读 shadow 使用下面的账户绑定流程。工具始终
`allow_live_orders=False` 创建 adapter,代码中没有报单或撤单入口。
先设置本机 secret/path
```powershell
$env:PYTHONPATH = "src;."
$env:QMT_USERDATA_PATH = "C:\local\qmt\userdata_mini"
$env:QMT_ACCOUNT_ID = "<local-account-id>"
$env:QMT_SESSION_ID = "<unique-numeric-session-id>"
$env:QMT_ACCOUNT_HASH_KEY_FILE = "C:\local\quant-os\account-hash.key"
$env:DECISION_DATE = "<fresh-completed-trading-date>"
```
首次只能在 decision 所声明的唯一下一交易日
Asia/Shanghai `[09:00,09:30)` 盘前窗口内运行:
```powershell
python tools/qmt_shadow_plan.py plan `
--decision artifacts\latest-decision\manifest.json `
--output artifacts\qmt-shadow-bootstrap
```
若账户/资产/持仓/委托/成交查询均可信,首次仍会以
`DECISION_NOT_ACCOUNT_BOUND` 返回预期退出码 2,同时写出:
```text
broker_observation.json
broker_snapshot.json
manifest.json
shadow_plan.json
```
`broker_snapshot.json` 若为 JSON `null`,表示查询事实不可信,禁止继续。
否则用该 snapshot 在**同一个 `$env:DECISION_DATE`** 重新生成账户绑定决策:
```powershell
python -m quant60 snapshot-decision `
--snapshot data\snapshots\latest\manifest.json `
--config configs\baseline.json `
--as-of $env:DECISION_DATE `
--broker-snapshot artifacts\qmt-shadow-bootstrap\broker_snapshot.json `
--output artifacts\qmt-bound-decision
python tools/qmt_shadow_plan.py plan `
--decision artifacts\qmt-bound-decision\manifest.json `
--output artifacts\qmt-shadow
python tools/qmt_shadow_plan.py verify `
--manifest artifacts\qmt-shadow\manifest.json `
--decision artifacts\qmt-bound-decision\manifest.json
```
planner 会检查账户 HMAC、query duration、连接/live-mode 二次状态、账户归属、
资产恒等式、持仓市值、委托/成交唯一性与数量恒等式;所有观察事实会进入脱敏
`broker_observation.json` 并被 manifest/verifier 绑定。任一 blocker 会把
proposed delta 强制为零。
`verify``--decision` 是必需参数,必须指向生成 shadow 时的原始、未改写
decision manifest`QMT_ACCOUNT_HASH_KEY_FILE` 必须指向 plan 时使用的既有
至少 32-byte key。verify 不会创建新 key,缺失、错误或已轮换 key 都失败。
query duration 的 canonical 上限是 10 秒,资产/持仓市值
`cash + market_value = total_asset` 的绝对容差上限是 1 元;
`--max-query-duration-seconds``--market-value-tolerance` 只能收紧,
不能放宽。不要为“让本次通过”改大参数。
verifier 会用原始 decision 和 read-only observation 做 semantic replay
独立重建 blocker、status、snapshot-valid、完整 snapshot、cash/portfolio、
原始目标绑定、board-lot/sellable feasible delta 和 proposed delta,然后
exact compare。必须把 blockers 被删/新增、target quantity/weight/lot 被改、
before/after orders 不一致、snapshot 为 `null` 或 observation/plan 派生字段
不同步视为 artifact 验证失败,而不是人工豁免。READY 必须有 replay 可重建的
非空 broker snapshot。
manifest 中的 `QMT_SHADOW_EVIDENCE_V1` HMAC envelope 还绑定完整
plan/observation/snapshot 的 canonical 内容与三份磁盘发布文件 byte
SHA-256、原始 decision、planner/engine/`tools/qmt_shadow_plan.py` source 和
canonical policy。四个发布 JSON(含 manifest)必须保持唯一 writer bytes
不要 minify、重排 key 或手工“美化”。它阻止不知道 key 的人靠重算普通 hash
重新封装证据。它只证明持 key
的 Quant OS 本地发布与后续完整性,**不是券商对 query 原始事实的 attestation**
仍必须完成官方 API 失败语义、终端安全、callback 和真实对账认证。
它还会对比账户绑定 decision 的 `equity` 与当前 `total_asset`。固定容差是
`max(1 元, 0.01% × abs(decision equity))`(包含边界),不可通过 CLI
放宽。超差时出现 `DECISION_EQUITY_DRIFT`;应使用本次可信
`broker_snapshot.json` 重建 decision,禁止继续采用旧绝对目标股数。缺失、
负数或非有限权益同样失败关闭。
这里的 Signal `as_of` 始终是 T 日 15:00 完整收盘。snapshot 会冻结
JQData 两套交易日历 API 一致性校验后的 session 序列;decision、
broker snapshot 和 shadow plan 都保存相同的 `next_trading_session`
`first_executable_window`。broker query 的开始和完成必须都落在唯一下一
session 的 `[09:00,09:30)` 内;周末、同日 15:01、09:30、错误 session
及迟到多日均失败关闭。长假按冻结日历处理,不按 elapsed-day 或 weekday
近似推算。
positions 和 sellable quantity 始终以当前 broker query 为准重新做 target
diff;逐 symbol target quantity/weight/lot size 必须 exact 绑定原始
decision,未完成委托会阻断;这使持仓变化不会复用旧 residual。当前 cash、
market value、total asset 与 position market value 由 replay 独立核算。
但无实时执行价就
不能做可靠购买力检查,所以 `NO_BUYING_POWER_CHECK_WITHOUT_EXECUTION_PRICE`
必须保留,`SHADOW_READY` 仅表示可人工复核,不表示可提交。
HMAC key 在 plan 首次运行时以安全随机数生成;Unix 要求 0600。verify 只读
已有 key。备份时保持访问控制,绝不提交 Git、OB、notebook、日志或聊天。
更换 key 会使旧账户绑定决策失配并使历史 evidence 无法验签。
这些是 local contract,不是 broker certification。当前 adapter
`uncertified / not live-ready`,本文故意没有 live-order command。正式
QMT 账号还要先探测官方 query API 在“失败”与“空列表”时的实际合同,并完成
terminal/xtquant build 记录以及 `XtTrade.traded_amount`
`price × quantity` 的真实容差探针。当前 amount 是被认证的 informational
fact,不参与 target/delta。之后还需脱敏 callback replay、restart recovery、
完整对账、至少 20 trading days shadow 和 G10。当前 `.BJ` mutation 不支持。
## 8. Qlib 0.9.7 research
创建隔离环境:
```bash
python3.12 -m venv .venv-qlib312
source .venv-qlib312/bin/activate
python -c "import sys; assert sys.version_info[:2] == (3, 12)"
python -m pip install -r requirements/research-py312.txt
python -c "import qlib; assert qlib.__version__ == '0.9.7'"
```
### Native momentum fixture
```bash
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
```
该固定运行时已实际得到 24 条 signal,使用
`SimulatorExecutor + TopkDropoutStrategy`。结果 JSON 保存 signal/report
稳定 hash、表行列与时间边界,以及从 report 重算的累计收益、最大回撤、基准
收益、成本和换手,并明确 `investment_value_claim=false`。Qlib 单一
`limit_threshold` 是 A 股逐日板块/ST 限制的近似,所以只参加 L1/L2 和
诊断 L6。
### Alpha158 + LightGBM + Recorder fixture
```bash
python tools/build_qlib_tiny_fixture.py \
artifacts/qlib-fixture-alpha --days 400
PYTHONPATH=src:. python -m platforms.qlib_runner \
--workflow alpha158 \
--provider-uri artifacts/qlib-fixture-alpha \
--market csi300 \
--benchmark SH000300 \
--train-start 2024-04-01 \
--train-end 2024-10-31 \
--valid-start 2024-11-01 \
--valid-end 2025-01-31 \
--test-start 2025-02-03 \
--test-end 2025-05-16 \
--topk 1 \
--n-drop 1 \
--experiment-name quant-os-alpha158-fixture \
--output-json artifacts/qlib-alpha158/result.json
```
本项目已经实际跑通该类 fixture,完成 model fit、SignalRecord、
SigAnaRecord、PortAnaRecord 和 Recorder。runner 使用
`os.environ.setdefault("MLFLOW_ALLOW_FILE_STORE", "true")` 自动确认
local MLflow file store;不需要人工先设置该变量。它还读取
`portfolio_analysis/report_normal_1day.pkl`,保存表级 hash、行列/时间边界、
重算指标和 artifact path。
fixture 只有两只 synthetic 股票和一个 benchmark,所有 performance/IC
都没有投资意义。真实研究必须换成授权、immutable provider,保存 data/model/
Recorder version,并重新做 OOSAlpha158 不是 portable-momentum 的 L2 peer
也不声称 L3/L4 parity。
## 9. Mock parity and real G9
生成/验证 wrapper mock contract
```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
```
报告逐层比较 score、weight、target quantity 和 order delta,并固定 source
hash。其声明必须保持:
```text
evidence_class = mock_contract_only
real_platform_pass = false
gate_credit = []
```
因此 mock parity 不计 G9。真实 G9 仍要求真实 JoinQuant/QMT export、同输入
的 L1—L5 difference report 和至少 20 trading days QMT/broker shadow。
## 10. Evidence checklist
每个真实平台 run 保存:
- Git commit 或 `DIRTY`
- `bundle_manifest.json`(适用时);
- Python/platform/client/broker plugin version
- config/data/rules/model hash
- dates、capital、universe、adjustment、fee/fill setting
- stdout、platform export 和所有 failure
- evidence class`synthetic``fake``provider-snapshot`
`real-platform``broker`
- compared layer、tolerance 和 reason code
- 数据许可、脱敏与 artifact retention 记录。
真实 JoinQuant smoke 已运行;QMT、broker 以及相同输入跨引擎比较仍未
运行,G1—G10 全部 `not_passed`。完整层级见
[`CROSS_ENGINE_CONSISTENCY.md`](../docs/CROSS_ENGINE_CONSISTENCY.md)
安全规则见 [`SECURITY.md`](../docs/SECURITY.md)。