Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 81 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,106 +1,131 @@
# QmtPlatform

A-share quant platform layer for **miniQMT / QMT**, built on `QuantPlatformKit` and `CnEquityStrategies`.
English | [简体中文](README.zh-CN.md)

Current scope is **offline dry-run only**: evaluate strategy targets and preview orders without submitting to the broker. This repository is not a shadow/live runtime; any non-dry-run order request is rejected as `blocked` and is never reported as `submitted`.
A-share quant platform layer for miniQMT / QMT, built on `QuantPlatformKit` and `CnEquityStrategies`.

## QSL architecture role
## Status

- **Layer**: `runtime-platform`.
- **Responsibility**: QMT / miniQMT A-share execution runtime.
- **Owns**: QMT runtime controls and A-share platform integration.
- **Consumes**: CnEquityStrategies, QuantPlatformKit, QuantRuntimeSettings, market-history inputs.
- **Must not**: own strategy research logic or publish snapshot artifacts.
Experimental. The current implementation evaluates strategies offline and previews orders. This project is not financial advice and does not guarantee any trading result.

## Offline paper admission (not broker paper)
## What this is

`--paper-admission` is a deterministic local configuration gate, not a miniQMT/QMT paper account connection. It requires all of the following:
QmtPlatform provides the QMT platform integration layer. It loads strategy profiles from `CnEquityStrategies`, reads local market-history inputs, and evaluates target weights against a synthetic portfolio. `QuantPlatformKit` supplies shared runtime and strategy contracts; `QuantRuntimeSettings` supplies runtime target definitions.

- `QMT_EXECUTION_MODE=paper`;
- `QMT_DRY_RUN_ONLY=true`;
- a pre-recorded SHA-256 digest matching the fixed `QMT_MARKET_HISTORY_PATH` input.
## What this is not

It only loads the strategy contract and hashes the local input. It does not call a QMT SDK/provider, read credentials, create orders, evaluate a broker account, or make this repository broker-paper or live ready.

```bash
export QMT_EXECUTION_MODE=paper
export QMT_DRY_RUN_ONLY=true
export QMT_PAPER_ADMISSION_INPUT_SHA256=5612a923290ed9fb779ce39613c5ea875ee68f69bf2aade0358761f2b86b3afb
python3 scripts/preflight_qmt_runtime.py --paper-admission
```

## Supported dry-run profiles

| Profile | Input mode |
|---|---|
| `cn_industry_etf_rotation` | `market_history` (**主轨,runtime_enabled**) |
| `cn_industry_etf_rotation_aggressive` | `market_history` (**optional target,live_candidate**) |

Research-only profiles such as `cn_dividend_quality_snapshot` and `cn_index_etf_tactical_rotation` are intentionally rejected by QMT runtime settings until they are promoted in `CnEquityStrategies`.
This repository does not implement strategy research or publish snapshot artifacts. It has no miniQMT / QMT broker connection. The offline broker adapter returns `blocked` for non-dry-run order requests and never reports them as `submitted`.

## Quick start

### Industry ETF rotation (market history, primary)
Requirements: Python 3.11 or later and Git. Run these commands from the repository root. The package installation resolves the dependency revisions declared in `pyproject.toml`.

```bash
python3 -m pip install -e '.[test]'
python3 -m pip install --no-deps -e ../QuantPlatformKit ../CnEquityStrategies
python3 -m pytest -q tests

unset RUNTIME_TARGET_JSON
export STRATEGY_PROFILE=cn_industry_etf_rotation
export QMT_DRY_RUN_ONLY=true
export QMT_EXECUTION_MODE=dry_run
export QMT_MARKET_HISTORY_PATH=data/fixtures/market_history.sample.csv

python3 scripts/preflight_qmt_runtime.py
python3 main.py
curl http://127.0.0.1:8080/probe
curl http://127.0.0.1:8080/dry-run
```

### Industry ETF rotation aggressive (optional second target, vol25%)
In another terminal, query the local service:

```bash
export STRATEGY_PROFILE=cn_industry_etf_rotation_aggressive
export QMT_DRY_RUN_ONLY=true
export QMT_MARKET_HISTORY_PATH=data/fixtures/market_history.sample.csv

python3 main.py
curl http://127.0.0.1:8080/probe
curl http://127.0.0.1:8080/profiles
curl http://127.0.0.1:8080/dry-run
```

Runtime target example: `QuantRuntimeSettings/examples/targets/qmt/industry_etf_aggressive_dry_run.example.json`

End-to-end smoke (stage/build/run):
The fixture-backed smoke scripts run a local strategy cycle without starting the HTTP service:

```bash
python3 scripts/smoke_cn_industry_etf_rotation_dry_run_e2e.py
python3 scripts/smoke_cn_industry_etf_rotation_aggressive_dry_run_e2e.py
```

## Environment variables
## Configuration

Use [`.env.example`](.env.example) as the dry-run configuration reference. Export its values in your shell; the entrypoint reads environment variables. The preflight command validates the selected profile and required input paths without accessing broker credentials.

| Variable | Required | Default | Purpose |
|---|---|---|---|
| `STRATEGY_PROFILE` | Unless `RUNTIME_TARGET_JSON` is set | none | QMT-enabled strategy profile ID. |
| `QMT_DRY_RUN_ONLY` | No | `true` | Keep `true`; preflight rejects `false`, and the HTTP dry-run endpoint always uses dry-run settings. |
| `QMT_EXECUTION_MODE` | No | `dry_run` | Set to `paper` only for offline paper admission; this value does not create a broker connection. |
| `QMT_PAPER_ADMISSION_INPUT_SHA256` | For `--paper-admission` | none | Pre-recorded SHA-256 digest of the fixed market-history input. |
| `QMT_MARKET_HISTORY_PATH` | For market-history profiles | none | Local CSV path with `date,symbol,close` columns. |
| `RUNTIME_TARGET_JSON` | No | none | JSON runtime target from `QuantRuntimeSettings`; takes precedence over `STRATEGY_PROFILE`. |
| `PORT` | No | `8080` | HTTP port; the Flask entrypoint binds to `0.0.0.0`. |

## Supported dry-run profiles

| Variable | Default | Description |
| Profile | Input mode | Role |
|---|---|---|
| `QMT_DRY_RUN_ONLY` | `true` | Offline-only guard; non-dry-run requests remain blocked |
| `QMT_EXECUTION_MODE` | `dry_run` | Must be `paper` for offline paper admission; it never enables a broker connection |
| `QMT_PAPER_ADMISSION_INPUT_SHA256` | — | Required only for `--paper-admission`; pre-recorded digest of the fixed market-history input |
| `STRATEGY_PROFILE` | required | QMT-enabled strategy profile id |
| `QMT_MARKET_HISTORY_PATH` | — | CSV with `date,symbol,close` for direct strategies |
| `RUNTIME_TARGET_JSON` | — | Optional runtime target override from QuantRuntimeSettings |
| `cn_industry_etf_rotation` | `market_history` | Primary profile. |
| `cn_industry_etf_rotation_aggressive` | `market_history` | Optional profile with a 25% volatility target. |

Use `.env.example` as the dry-run configuration template. Run `python3 scripts/preflight_qmt_runtime.py` before starting the service; it validates the selected profile and required input paths without touching any live account credentials.
Set `STRATEGY_PROFILE=cn_industry_etf_rotation_aggressive` to select the optional profile when `RUNTIME_TARGET_JSON` is unset. A runtime target example is available in [QuantRuntimeSettings](https://github.com/QuantStrategyLab/QuantRuntimeSettings/blob/main/examples/targets/qmt/industry_etf_aggressive_dry_run.example.json).

Profile availability follows the `CnEquityStrategies` catalog and QMT rollout allowlist. Research-only profiles such as `cn_dividend_quality_snapshot` and `cn_index_etf_tactical_rotation` are rejected by the QMT runtime policy.

## Offline paper admission

`--paper-admission` checks a local configuration and fixed input file. It requires `QMT_EXECUTION_MODE=paper`, `QMT_DRY_RUN_ONLY=true`, and a pre-recorded SHA-256 digest matching `QMT_MARKET_HISTORY_PATH`.

After configuring the profile and input path, replace `<expected-sha256>` with that recorded digest:

```bash
export QMT_EXECUTION_MODE=paper
export QMT_DRY_RUN_ONLY=true
export QMT_PAPER_ADMISSION_INPUT_SHA256='<expected-sha256>'
python3 scripts/preflight_qmt_runtime.py --paper-admission
```

The command loads the strategy contract and hashes the local input. It does not call a QMT SDK or provider, read credentials, create orders, or evaluate a broker account. The word `paper` here names an offline check, not a broker paper-trading connection.

## Disabled-target lifecycle validation

QMT has no configured broker runtime or live credentials. The weekday `Runtime Target Lifecycle` workflow therefore runs only the deterministic preflight and fixture-backed dry-run smoke, then publishes a sanitized central status of `disabled` / `dry_run`. A completed dry-run with weights blocked by the deterministic risk gate is also a passing no-order validation: the platform and its safety boundary both worked. It does **not** mean that QMT is deployed, connected to miniQMT, paper-enabled, or live-enabled. A preflight or dry-run failure is published as `attention`; no status can change the target state or submit an order.
The weekday `Runtime Target Lifecycle` workflow runs deterministic preflight and a fixture-backed dry-run for a fixed disabled target. It publishes `disabled` / `dry_run` status. A completed cycle whose target weights are empty because of the risk gate is a successful no-order check; a preflight or dry-run failure is reported as `attention`. These reports do not change target settings, establish a broker connection, or submit orders.

## HTTP endpoints

- `GET /probe` — health + active profile
- `GET /profiles` — platform profile matrix
- `GET|POST /dry-run` — evaluate strategy and return target weights + order previews
| Method | Path | Response |
|---|---|---|
| `GET` | `/probe` | Health, active profile, and dry-run setting. |
| `GET` | `/profiles` | Platform profile matrix. |
| `GET`, `POST` | `/dry-run` | Strategy target weights and order previews using local inputs. |

## Related repositories

- [QuantPlatformKit](https://github.com/QuantStrategyLab/QuantPlatformKit)
- [CnEquityStrategies](https://github.com/QuantStrategyLab/CnEquityStrategies)
- [CnEquitySnapshotPipelines](https://github.com/QuantStrategyLab/CnEquitySnapshotPipelines)
- [QuantRuntimeSettings](https://github.com/QuantStrategyLab/QuantRuntimeSettings)

## Repository layout

```text
application/ offline broker adapter and dry-run cycle
data/fixtures/ sample market-history and snapshot inputs
scripts/ preflight and local smoke commands
tests/ automated tests
main.py Flask HTTP entrypoint
```

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md). Documentation follows the [QuantStrategyLab documentation style guide](https://github.com/QuantStrategyLab/.github/blob/main/docs/STYLE_GUIDE.md).

## Security

See [SECURITY.md](SECURITY.md) for private vulnerability reporting. Do not put credentials or account identifiers in issues, examples, or logs.

## License

[MIT](LICENSE).
139 changes: 102 additions & 37 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,131 @@
# QmtPlatform

A 股量化平台层,基于 `QuantPlatformKit` 和 `CnEquityStrategies` 构建,对接 **miniQMT / QMT**。
[English](README.md) | 简体中文

当前范围为**仅干跑**:评估策略目标并预览订单,不向券商提交实盘;任何 non-dry-run 请求均会返回 `blocked`,不会被报告为 `submitted`。
面向 miniQMT / QMT 的 A 股量化平台层,基于 `QuantPlatformKit` 和 `CnEquityStrategies` 构建。

## QSL 架构角色
## 状态

- **层级**:`执行平台`。
- **职责**:QMT / miniQMT A 股执行运行时。
- **事实源/归属**:QMT runtime 控制和 A 股平台集成。
- **消费对象**:CnEquityStrategies、QuantPlatformKit、QuantRuntimeSettings、market-history 输入。
- **禁止事项**:承载策略研究逻辑或发布 snapshot artifacts。
实验性项目。当前实现离线评估策略并预览订单。本项目不构成投资建议,也不保证任何交易结果。

## 离线 paper-admission(不是券商 paper)
## 简介

`--paper-admission` 是确定性的本地配置门槛,不连接 miniQMT/QMT paper 账户。它必须同时满足:
QmtPlatform 提供 QMT 平台集成层。它从 `CnEquityStrategies` 加载策略配置,读取本地历史行情,并基于模拟投资组合评估目标权重。`QuantPlatformKit` 提供共享运行组件与策略契约,`QuantRuntimeSettings` 提供运行目标定义。

- `QMT_EXECUTION_MODE=paper`;
- `QMT_DRY_RUN_ONLY=true`;
- 预先记录的 SHA-256 与固定的 `QMT_MARKET_HISTORY_PATH` 输入一致。
## 不适用范围

该命令只加载策略契约并计算本地输入摘要;不会调用 QMT SDK/provider、读取凭据、创建订单或读取券商账户,也不代表系统已经具备 broker-paper 或 live 能力。

```bash
export QMT_EXECUTION_MODE=paper
export QMT_DRY_RUN_ONLY=true
export QMT_PAPER_ADMISSION_INPUT_SHA256=5612a923290ed9fb779ce39613c5ea875ee68f69bf2aade0358761f2b86b3afb
python3 scripts/preflight_qmt_runtime.py --paper-admission
```

## 支持 dry-run 策略

| Profile | 输入模式 |
|---|---|
| `cn_industry_etf_rotation` | `market_history`(主轨,runtime_enabled) |
| `cn_industry_etf_rotation_aggressive` | `market_history`(可选目标,live_candidate) |

`cn_dividend_quality_snapshot`、`cn_index_etf_tactical_rotation` 等 research-only profile 不会进入 QMT runtime 配置;只有在 `CnEquityStrategies` 中完成提级后才允许启用。
本仓库不实现策略研究,也不发布快照产物。它没有 miniQMT / QMT 券商连接。离线券商适配器对非 dry-run 的订单请求返回 `blocked`,不会将其报告为 `submitted`。

## 快速开始

需要 Python 3.11 或更高版本及 Git。在仓库根目录运行以下命令。安装包时会使用 `pyproject.toml` 声明的依赖版本。

```bash
python3 -m pip install -e '.[test]'
python3 -m pytest -q tests

unset RUNTIME_TARGET_JSON
export STRATEGY_PROFILE=cn_industry_etf_rotation
export QMT_DRY_RUN_ONLY=true
export QMT_EXECUTION_MODE=dry_run
export QMT_MARKET_HISTORY_PATH=data/fixtures/market_history.sample.csv

python3 scripts/preflight_qmt_runtime.py
python3 main.py
```

在另一个终端查询本地服务:

```bash
curl http://127.0.0.1:8080/probe
curl http://127.0.0.1:8080/profiles
curl http://127.0.0.1:8080/dry-run
```

以下 smoke 脚本使用固定样本运行本地策略周期,无需启动 HTTP 服务:

```bash
python3 scripts/smoke_cn_industry_etf_rotation_dry_run_e2e.py
python3 scripts/smoke_cn_industry_etf_rotation_aggressive_dry_run_e2e.py
```

## 配置

以 [`.env.example`](.env.example) 为 dry-run 配置参考,在 shell 中导出相应值;入口程序读取环境变量。预检命令检查所选策略及所需输入路径,不访问券商凭据。

| 变量 | 是否必需 | 默认值 | 用途 |
|---|---|---|---|
| `STRATEGY_PROFILE` | 未设置 `RUNTIME_TARGET_JSON` 时必需 | 无 | QMT 已启用的策略配置 ID。 |
| `QMT_DRY_RUN_ONLY` | 否 | `true` | 保持为 `true`;预检拒绝 `false`,HTTP dry-run 接口始终使用 dry-run 设置。 |
| `QMT_EXECUTION_MODE` | 否 | `dry_run` | 仅在离线 paper-admission 检查中设为 `paper`;该值不会建立券商连接。 |
| `QMT_PAPER_ADMISSION_INPUT_SHA256` | 使用 `--paper-admission` 时必需 | 无 | 预先记录的固定历史行情输入的 SHA-256 摘要。 |
| `QMT_MARKET_HISTORY_PATH` | 历史行情策略必需 | 无 | 包含 `date,symbol,close` 列的本地 CSV 路径。 |
| `RUNTIME_TARGET_JSON` | 否 | 无 | 来自 `QuantRuntimeSettings` 的 JSON 运行目标;优先于 `STRATEGY_PROFILE`。 |
| `PORT` | 否 | `8080` | HTTP 端口;Flask 入口绑定 `0.0.0.0`。 |

## 支持的 dry-run 策略

| 策略配置 | 输入模式 | 用途 |
|---|---|---|
| `cn_industry_etf_rotation` | `market_history` | 主要策略。 |
| `cn_industry_etf_rotation_aggressive` | `market_history` | 可选策略,目标波动率为 25%。 |

未设置 `RUNTIME_TARGET_JSON` 时,可通过 `STRATEGY_PROFILE=cn_industry_etf_rotation_aggressive` 选择可选策略。运行目标示例见 [QuantRuntimeSettings](https://github.com/QuantStrategyLab/QuantRuntimeSettings/blob/main/examples/targets/qmt/industry_etf_aggressive_dry_run.example.json)。

可用策略由 `CnEquityStrategies` 的策略目录和 QMT 启用清单决定。QMT 运行配置不接受 `cn_dividend_quality_snapshot`、`cn_index_etf_tactical_rotation` 等仅供研究的策略配置。

## 离线 paper-admission 检查

`--paper-admission` 检查本地配置和固定输入文件。它要求 `QMT_EXECUTION_MODE=paper`、`QMT_DRY_RUN_ONLY=true`,且预先记录的 SHA-256 摘要与 `QMT_MARKET_HISTORY_PATH` 一致。

配置策略和输入路径后,将 `<expected-sha256>` 替换为预先记录的摘要:

```bash
export QMT_EXECUTION_MODE=paper
export QMT_DRY_RUN_ONLY=true
export QMT_PAPER_ADMISSION_INPUT_SHA256='<expected-sha256>'
python3 scripts/preflight_qmt_runtime.py --paper-admission
```

`.env.example` 是当前 dry-run 配置模板;preflight 只校验 profile 与输入文件路径,不读取或写入账号、密码、token。
该命令加载策略契约并计算本地输入摘要,不调用 QMT SDK 或服务提供方,不读取凭据、创建订单或评估券商账户。这里的 `paper` 表示离线检查,不代表券商模拟交易连接。

## 已停用目标的生命周期验证

QMT 当前没有配置券商运行面或实盘凭据。工作日定时的 `Runtime Target Lifecycle` 只运行确定性预检和基于
固定样本行情的 dry-run smoke,然后向统一管理端发布脱敏的 `disabled` / `dry_run` 状态。dry-run 已完成、
但被确定性风控拦截为空权重,也属于通过:平台与安全边界都在正常工作。它不表示 QMT 已部署、已连接 miniQMT、
已启用 paper 或实盘。预检或 dry-run 真正失败才会发布为 `attention`;任何状态都不能改变目标开关或提交订单。
工作日运行的 `Runtime Target Lifecycle` 工作流为固定的已停用目标执行确定性预检和基于样本的 dry-run,并发布 `disabled` / `dry_run` 状态。若周期已完成,但风控检查使目标权重为空,视为成功完成无订单检查;预检或 dry-run 失败则报告为 `attention`。这些报告不会修改目标设置、建立券商连接或提交订单。

## HTTP 接口

| 方法 | 路径 | 响应 |
|---|---|---|
| `GET` | `/probe` | 健康状态、当前策略和 dry-run 设置。 |
| `GET` | `/profiles` | 平台策略配置矩阵。 |
| `GET`、`POST` | `/dry-run` | 基于本地输入计算的策略目标权重和订单预览。 |

## 相关仓库

- [QuantPlatformKit](https://github.com/QuantStrategyLab/QuantPlatformKit)
- [CnEquityStrategies](https://github.com/QuantStrategyLab/CnEquityStrategies)
- [CnEquitySnapshotPipelines](https://github.com/QuantStrategyLab/CnEquitySnapshotPipelines)
- [QuantRuntimeSettings](https://github.com/QuantStrategyLab/QuantRuntimeSettings)

## 仓库结构

```text
application/ 离线券商适配器和 dry-run 周期
data/fixtures/ 历史行情和快照样本输入
scripts/ 预检和本地 smoke 命令
tests/ 自动化测试
main.py Flask HTTP 入口
```

## 参与贡献

见 [CONTRIBUTING.md](CONTRIBUTING.md)。文档遵循 [QuantStrategyLab 文档风格指南](https://github.com/QuantStrategyLab/.github/blob/main/docs/STYLE_GUIDE.md)。

## 安全

详见 [README.md](README.md)(英文)。
私下报告安全漏洞的方法见 [SECURITY.md](SECURITY.md)。请勿在 issue、示例或日志中填写凭据或账户标识。

## 许可证

详见 [LICENSE](LICENSE)。
[MIT](LICENSE)。
Loading