Files
big-qmt/docs/README.md
2026-08-25 16:40:18 +08:00

258 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# QMT HTTP API
将迅投 QMTMiniQMT / 投研版)策略进程内的 `ContextInfo`、行情、财务与交易函数,封装为 JSON HTTP 服务,供外部程序远程调用。
源码:[`api/QMT_API.py`](../api/QMT_API.py)
完整接口清单、请求/响应字段与 curl 示例见 [api.md](./api.md)。
---
## 1. 它是什么
`QMT_API.py` **不是**可独立 `python QMT_API.py` 启动的普通脚本。它是一份 QMT Python 策略:
- QMT 加载策略后调用 `init(ContextInfo)`
- `init` 绑定资金账号、加载股票池、创建 Tornado `Application`,并在当前进程里 `listen` + `IOLoop.start()`
- 此后外部 HTTP 客户端通过 `X-Token` 鉴权,调用本机(或同网段)上的 REST 接口。
- 接口内部再转调 QMT 内置对象:`ContextInfo.*``passorder``get_trade_detail_data` 等。
因此:服务生命周期 = 策略生命周期。策略停止HTTP 一并停止。
```
外部程序 --HTTP JSON--> Tornado (0.0.0.0:10086)
|
v
QMT 策略进程
ContextInfo / 交易账户
```
---
## 2. 运行环境
| 项 | 要求 |
| --- | --- |
| 宿主 | 迅投 QMT需启用 Python 策略) |
| 解释器 | QMT 自带的 Python源码文件编码为 **GBK** |
| 第三方库 | `tornado`(需在 QMT Python 环境中可用) |
| 标准库 | `json` / `os` / `datetime` / `pathlib` / `logging` / `locale` |
| 操作系统 | 源码调用 `locale.setlocale(locale.LC_CTYPE, 'chinese')`,面向 **Windows 中文环境** |
QMT 内置符号(由策略宿主注入,源码中未 import
- `ContextInfo` 及其方法(`get_market_data``get_universe` 等)
- 交易:`passorder``algo_passorder``smart_algo_passorder``order_*``buy_open` / `sell_open`
- 查询:`get_trade_detail_data``get_value_by_order_id``can_cancel_order``cancel`
- 其它:`get_open_date``timetag_to_datetime``ext_data``get_etf_info`
---
## 3. 配置
源码顶部与 `init()` 使用的配置如下。
| 名称 | 来源 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `QMT_ACCOUNT_ID` | 环境变量 | `''` | 资金账号,写入 `ContextInfo.accountID``set_account` |
| `QMT_DATA_DIR` | 环境变量 | `D:\qmt_strategy_data` | **意图**上的数据目录;见下方「已知问题」 |
| `TOKEN` | 源码硬编码 | `QMTbyYanweidong` | HTTP 鉴权口令,请求头 `X-Token` 必须与之相等 |
| `PORT` | 源码硬编码 | `10086` | 监听端口;绑定地址为 `0.0.0.0` |
启动时还会读取:
```
{数据目录}/pass_codes.json
```
内容须为 JSON 数组(股票代码列表),用于 `ContextInfo.set_universe(...)`。该文件缺失或无法解析会导致 `init` 失败HTTP 服务起不来。
---
## 4. 接入步骤
1. 在 QMT 中配置 Python 策略,入口文件指向 `api/QMT_API.py`
2. 准备数据目录,放入 `pass_codes.json`,例如:
```json
["000001.SZ", "600000.SH"]
```
3. 设置环境变量 `QMT_ACCOUNT_ID`(以及你实际使用的数据目录变量,见已知问题)。
4. 启动策略。日志出现类似:
```
QMT HTTP Server 启动于 http://0.0.0.0:10086 (全部API已加载)
```
5. 用任意 HTTP 客户端调用。所有业务接口默认需要鉴权:
```http
X-Token: < TOKEN >
Content-Type: application/json
```
快速探活(需 Token
```bash
curl -s -H "X-Token: QMTbyYanweidong" http://127.0.0.1:10086/api/context/period
```
关闭服务:
```bash
curl -s -X POST -H "X-Token: QMTbyYanweidong" http://127.0.0.1:10086/api/sys/shutdown
```
`ShutdownHandler` 会在响应后再 `IOLoop.stop()`Tornado 事件循环退出。
---
## 5. 鉴权与协议约定
### 5.1 鉴权
`BaseHandler.prepare()`
- 请求头 `X-Token` 必须等于源码中的 `TOKEN`
- 否则抛出 `HTTPError(401, "认证失败token 无效或缺失")`
- 源码定义了 `@no_auth` 装饰器,但 **没有任何 Handler 使用它**,包括 `/api/sys/python_version``/api/sys/shutdown`
### 5.2 请求
- GET无 Body参数都在路径中本服务 GET 接口目前均无 Query
- POSTBody 必须是 **合法 JSON 对象**。多数 POST 一上来就 `json.loads(self.request.body)`,空 Body 会直接异常。
- 多标的字段(如 `stock_code``stocks``stock_list``fieldList`)一般为 **逗号分隔字符串**,服务端再 `split(',')` + `strip()`
### 5.3 响应
- 默认 `Content-Type: application/json; charset=utf-8`
- 成功:各接口自定义 JSON见 [api.md](./api.md))。
- 失败:`write_error` 统一为:
```json
{"error": "<reason>", "status_code": 401}
```
常见状态码:
| 码 | 场景 |
| --- | --- |
| 400 | 缺参、下单参数不合法 |
| 401 | Token 缺失或错误 |
| 500 | QMT 调用失败(部分接口在 `safe_call` 返回 `None` 后主动抛出) |
`safe_call` 会吞掉底层异常并打日志,返回 `None`。调用方看到的可能是 `null` 字段,也可能是 500取决于该 Handler 有没有对 `None` 再处理。
### 5.4 HTTP 方法习惯
- 只读、无参的 Context / 判定 / 系统信息:多数为 **GET**
- 带 JSON Body 的查询与全部交易: **POST**
- 同一资源没有 REST 语义上的 PUT/PATCH/DELETE。
---
## 6. 接口分组
路由在 `make_app()` 中注册,当前约 **100+** 条。按前缀划分:
| 前缀 | 用途 | 文档 |
| --- | --- | --- |
| `/api/v2/*` | 持仓 / 资产(与旧接口共用 Handler | [api.md §1](./api.md#1-兼容层--v2) |
| `/api/holding` `/api/money/*` `/api/order/*` | 旧版买卖、资金、撤单、成交 | 同上 |
| `/api/context/*` | 策略上下文属性 | [§2](./api.md#2-策略上下文-apicontext) |
| `/api/data/*` | 行情、财务、期权、订阅 | [§3](./api.md#3-数据查询-apidata) |
| `/api/check/*` | 停牌、板块、K 线判定 | [§4](./api.md#4-判定-apicheck) |
| `/api/trade/*` | 股票/算法/期货下单、任务、账户查询 | [§5](./api.md#5-交易-apitrade) |
| `/api/ext/*` | 扩展数据与因子引用 | [§6](./api.md#6-扩展引用-apiext) |
| `/api/sys/*` | Python 版本、关停服务 | [§7](./api.md#7-系统-apisys) |
兼容层买卖是对 `passorder` 的薄封装:
- `POST /api/order/buy``passorder(23, 1101, ...)`(买入)
- `POST /api/order/sell``passorder(24, 1101, ...)`(卖出)
- 完整下单请用 `POST /api/trade/passorder`(可自定义 `opType` / `orderType` / `prType` / `quickTrade`
账户查询里的 `account` 字段默认 `"stock"`,也会传到 `get_trade_detail_data` 的账户类型参数。
---
## 7. 回调与落盘(当前未挂接)
源码后半定义了主推回调,用于把账户/委托/成交/持仓写成 JSON 文件:
| 函数 | 意图文件名 |
| --- | --- |
| `account_callback` | `acount_%s.json`(拼写为 acount |
| `order_callback` | `order_%s.json` |
| `deal_callback` | `deal_%s.json` |
| `position_callback` | `position_%s.json` |
| `orderError_callback` | 仅 `print` |
`init()` **没有** 调用 `ContextInfo` 的回调注册接口,因此这些函数默认不会执行。即便注册,`write_json` 本身也存在未定义变量问题(见下节),落盘路径目前不可靠。
---
## 8. 源码审视(使用前必读)
以下为对照 `QMT_API.py` 的事实,不是「建议优化清单」。接入前应按此理解行为边界。
### 8.1 数据目录变量不一致
```python
DATA_DIR = os.environ.get('QMT_DATA_DIR', 'D:\\qmt_strategy_data')
# ...
Path(QMT_DATA_DIR) / "pass_codes.json"
```
环境变量读入的是 `DATA_DIR``init` / `write_json` 使用的是 **从未赋值的** `QMT_DATA_DIR`。在普通 Python 里会 `NameError`。若你的 QMT 环境没有额外注入同名全局量,策略会在启动阶段失败。
### 8.2 `write_json` 不可用
- 使用未定义的 `current.strftime`(应为 `now`)。
- `order_id` 无默认值,但 `account_callback` / `position_callback` 只传了两个参数。
- `file_key` 模板与实参个数不一定匹配。
### 8.3 Token 硬编码且监听全网卡
`TOKEN` 写死在源码里;`listen(..., address='0.0.0.0')` 对所有网卡开放。任何能打到 `10086` 且知道 Token 的客户端都可以下单、撤单、关停服务。不要把该端口暴露到公网。
### 8.4 错误被吞掉
`safe_call` 捕获全部异常后返回 `None`。部分查询接口仍会把 `null` 当成功响应返回调用方不易区分「没数据」和「QMT 抛错」。
### 8.5 编码
文件头 `# -*- coding: gbk -*-`。用 UTF-8 无 BOM 保存可能在 QMT 中出现中文注释/字符串解码问题。
### 8.6 规则撤单语义很窄
`POST /api/order/cancel_order` 不是「按委托号撤单」,而是:
- 股票代码(`代码.市场`)完全匹配,且
- `m_nVolumeTotal + m_nVolumeTraded == volume`,且
- `can_cancel_order` 为真
才发出 `cancel`。按委托号查询/判断请用 `/api/trade/value_by_order_id``/api/trade/can_cancel_order`。源码里没有单独的「按 orderId 撤单」HTTP 封装(全部撤单走 `/api/order/cancel_all`)。
---
## 9. 仓库结构
```
big-qmt/
├── api/
│ └── QMT_API.py # QMT 策略 + HTTP 服务(唯一实现)
├── docs/
│ ├── README.md # 本文件:架构、接入、约定、风险
│ └── api.md # 全量 HTTP 接口说明
└── README.md # 仓库占位
```
---
## 10. 相关文档
- [HTTP API 参考](./api.md)
- 迅投 QMT Python 策略官方函数手册(`passorder``opType` / `prType` 等枚举以官方文档为准;本仓库只记录本封装实际传入的值)