Files
big-qmt/docs/api.md

374 lines
14 KiB
Markdown
Raw Normal View History

2026-09-07 21:01:36 +08:00
# QMT REST API 参考
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
根据 [`api/qmt_rest_new.py`](../api/qmt_rest_new.py) 的路由、Handler 和格式化函数重新生成。更新日期2026-09-07。启动说明见 [README.md](README.md)。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
## 1. 公共约定
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
- Base URL`http://<host>:10086`,监听 `0.0.0.0`
- 所有已注册接口均要求 `X-Token: <服务端 TOKEN>`,没有免鉴权接口。
- POST 请求体为 JSON 对象,使用 `Content-Type: application/json`
- 账户取自服务端 `QMT_ACCOUNT_ID`,账户类型固定为 `stock`,请求不能切换账户。
- 成功响应没有统一包装,具体形状见各接口。
- 资产、持仓、委托、成交字段为小写下划线;下单请求仍使用驼峰字段;行情及原始查询保留 QMT 字段。
- 字段类型按 QMT 属性约定列出;除资产金额四舍五入外,格式化函数不强制类型转换、不填默认值、不转换枚举和日期。
- 枚举含义、原始日期格式、行情和其他透传结果由 QMT 决定,本文件不扩展源码未定义的契约。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
## 2. 接口清单
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| GET | `/api/context/info` | 策略上下文 |
| GET | `/api/get/{handler_type}` | 单证券查询 |
| GET | `/api/portfolio` | 聚合资产、持仓、委托 |
| GET | `/api/portfolio/assets` | 资产 |
| GET | `/api/portfolio/positions` | 持仓 |
| GET | `/api/portfolio/order` | 委托数组 |
| GET | `/api/portfolio/deal` | 成交数组 |
| GET | `/api/portfolio/org/{handler_type}` | 原始记录 |
| POST | `/api/data/full_tick` | 实时行情 |
| POST | `/api/trade/passorder` | 下单 |
| POST | `/api/trade/cancel_by_id` | 撤单 |
| POST | `/api/trade/ipo_data` | 发行数据 |
| GET | `/api/sys/python_version` | Python 版本 |
## 3. 策略上下文
`GET /api/context/info`,无参数,返回以下对象字段:
| 字段 | 来源 |
| --- | --- |
| `period` | `ContextInfo.period` |
| `barpos` | `ContextInfo.barpos` |
| `time_tick_size` | `ContextInfo.time_tick_size` |
| `stockcode` | `ContextInfo.stockcode` |
| `dividend_type` | `ContextInfo.dividend_type` |
| `market` | `ContextInfo.market` |
| `do_back_test` | `ContextInfo.do_back_test` |
| `benchmark` | `ContextInfo.benchmark` |
| `capital` | `ContextInfo.capital` |
| `timetag` | `ContextInfo.timetag` |
| `universe` | `ContextInfo.get_universe()` |
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
属性按原值输出;`stockcode` 是此接口的实际字段名。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
## 4. 单证券查询
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`GET /api/get/{handler_type}?stock_code=600000.SH`
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`stock_code` 为必填 Query 字符串,去除首尾空格后不能为空,整体传入底层函数,不拆分逗号。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
| handler_type | 调用 |
| --- | --- |
| `stock_name` | `ContextInfo.get_stock_name(stock_code)` |
| `open_date` | `ContextInfo.get_open_date(stock_code)` |
| `last_volume` | `ContextInfo.get_last_volume(stock_code)` |
| `total_share` | `ContextInfo.get_total_share(stock_code)` |
| `svol` | `ContextInfo.get_svol(stock_code)` |
| `bvol` | `ContextInfo.get_bvol(stock_code)` |
| `divid_factors` | `ContextInfo.get_divid_factors(stock_code)` |
| `etf_info` | 全局 `get_etf_info(stock_code)` |
| `etf_iopv` | 全局 `get_etf_iopv(stock_code)` |
| `instrumentdetail` | `ContextInfo.get_instrumentdetail(stock_code)` |
| `his_st_data` | `ContextInfo.get_his_st_data(stock_code)` |
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
响应示例:
2026-08-25 16:40:18 +08:00
```json
2026-09-07 21:01:36 +08:00
{"stock_code":"600000.SH","ref":"示例证券名称"}
2026-08-25 16:40:18 +08:00
```
2026-09-07 21:01:36 +08:00
`ref` 透传底层结果,可为字符串、数值、对象、数组或 null无法直接序列化的值通过 `str()` 转换。缺失或空代码返回 HTTP 500不支持的路径类型通常因路由不匹配返回 404。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
## 5. 账户查询
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
以下接口均无请求参数。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
### 5.1 聚合快照
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`GET /api/portfolio`
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
依次调用 `get_trade_detail_data(account_id, 'stock', 'account' / 'position' / 'order')`,分别格式化为以下三个字段:
2026-08-25 16:40:18 +08:00
```json
{
2026-09-07 21:01:36 +08:00
"assets":{"total":100000.0,"available":30000.0},
"positions":{},
"orders":[]
2026-08-25 16:40:18 +08:00
}
```
2026-09-07 21:01:36 +08:00
不包含成交。三次底层查询顺序执行,不保证同一时刻的原子快照;资产为空时整个请求失败。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
### 5.2 资产
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`GET /api/portfolio/assets`,查询 `account`,仅使用第一条记录。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
| 字段 | JSON 类型 | QMT 来源 / 转换 |
2026-08-25 16:40:18 +08:00
| --- | --- | --- |
2026-09-07 21:01:36 +08:00
| `total` | number | `m_dBalanceround(..., 2)` |
| `available` | number | `m_dAvailableround(..., 2)` |
2026-08-25 16:40:18 +08:00
```json
2026-09-07 21:01:36 +08:00
{"total":100000.0,"available":30000.0}
2026-08-25 16:40:18 +08:00
```
2026-09-07 21:01:36 +08:00
`total` 来源为账户余额字段,客户端作为总资产使用;`available` 为可用资金。无账户数据返回 HTTP 500。四舍五入不保证 JSON 文本固定显示两位小数。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
### 5.3 持仓
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`GET /api/portfolio/positions`,查询 `position`
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
返回以证券代码为 key 的对象,每个 value 包含下表全部字段。空结果为 `{}`;同一代码多条记录以后面的覆盖前面的,不合并数量。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
| 字段 | JSON 类型 | QMT 来源 / 转换 |
2026-08-25 16:40:18 +08:00
| --- | --- | --- |
2026-09-07 21:01:36 +08:00
| `stock_code` | string | `m_strInstrumentID + "." + m_strExchangeID` |
| `stock_name` | string | `m_strInstrumentName` |
| `direction` | integer | `m_nDirection` |
| `volume` | integer | `m_nVolume` |
| `open_price` | number | `m_dOpenPrice` |
| `open_cost` | number | `m_dOpenCost` |
| `float_profit` | number | `m_dFloatProfit` |
| `market_value` | number | `m_dMarketValue` |
| `stock_holder` | string | `m_strStockHolder` |
| `frozen_volume` | integer | `m_nFrozenVolume` |
| `can_use_volume` | integer | `m_nCanUseVolume` |
| `on_road_volume` | integer | `m_nOnRoadVolume` |
| `yesterday_volume` | integer | `m_nYesterdayVolume` |
| `last_price` | number | `m_dLastPrice` |
| `profit_rate` | number | `m_dProfitRate` |
| `future_trade_type` | integer | `m_eFutureTradeType` |
| `expire_date` | string | `m_strExpireDate` |
`volume` 为持仓数量,`can_use_volume` 为可用数量,`frozen_volume` 为冻结数量,`on_road_volume` 为在途数量,`yesterday_volume` 为昨日数量。`open_price``open_cost``float_profit``market_value` 分别保留开仓价、开仓成本、浮动盈亏、市值原值。`profit_rate` 不乘以 100也不换算单位。
外层示意:`{"600000.SH": {上述持仓字段}}`
### 5.4 委托
`GET /api/portfolio/order`,查询 `order`,返回对象数组,空结果为 `[]`
| 字段 | JSON 类型 | QMT 来源 / 转换 |
2026-08-25 16:40:18 +08:00
| --- | --- | --- |
2026-09-07 21:01:36 +08:00
| `stock_code` | string | `m_strInstrumentID + "." + m_strExchangeID` |
| `order_sys_id` | string | `m_strOrderSysID` |
| `ref` | integer | `m_nRef` |
| `order_ref` | string | `m_strOrderRef` |
| `direction` | integer | `m_nDirection` |
| `offset_flag` | integer | `m_nOffsetFlag` |
| `limit_price` | number | `m_dLimitPrice` |
| `volume_total_original` | integer | `m_nVolumeTotalOriginal` |
| `volume_traded` | integer | `m_nVolumeTraded` |
| `volume_total` | integer | `m_nVolumeTotal` |
| `traded_price` | number | `m_dTradedPrice` |
| `trade_amount` | number | `m_dTradeAmount` |
| `insert_date` | string | `m_strInsertDate` |
| `insert_time` | string | `m_strInsertTime` |
| `remark` | string | `m_strRemark` |
| `order_status` | integer | `m_nOrderStatus` |
`order_sys_id` 为柜台系统订单号,撤单时使用。`volume_total_original` 为原始委托数量,`volume_traded` 为已成交数量,`volume_total` 为剩余数量。日期、时间、备注及状态枚举保持原值。
服务端不按状态过滤,不排序或分页,不从备注中派生 `local_order_id``side``created_at` 等字段。
### 5.5 成交
`GET /api/portfolio/deal`,查询 `deal`,返回对象数组,空结果为 `[]`
| 字段 | JSON 类型 | QMT 来源 / 转换 |
2026-08-25 16:40:18 +08:00
| --- | --- | --- |
2026-09-07 21:01:36 +08:00
| `stock_code` | string | `m_strInstrumentID + "." + m_strExchangeID` |
| `order_sys_id` | string | `m_strOrderSysID` |
| `ref` | integer | `m_nRef` |
| `order_ref` | string | `m_strOrderRef` |
| `direction` | integer | `m_nDirection` |
| `offset_flag` | integer | `m_nOffsetFlag` |
| `price` | number | `m_dPrice` |
| `volume` | integer | `m_nVolume` |
| `trade_amount` | number | `m_dTradeAmount` |
| `trade_date` | string | `m_strTradeDate` |
| `trade_time` | string | `m_strTradeTime` |
| `remark` | string | `m_strRemark` |
| `close_profit` | number | `m_dCloseProfit` |
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`price``volume``trade_amount` 分别为成交价格、数量、金额;`close_profit` 为平仓盈亏。记录逐条输出,不按订单合并、不去重、不分页,也不接受日期过滤。`order_sys_id` 是订单号,不应假定其唯一标识一条成交记录。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
### 5.6 原始记录
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`GET /api/portfolio/org/{handler_type}`,支持 `account``order``deal``position`
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
调用 `get_trade_detail_data(account_id, 'stock', handler_type)`,遍历每条记录的公开、非 callable 属性,响应为 `{"data":[记录对象]}`,空结果为:
2026-08-25 16:40:18 +08:00
```json
2026-09-07 21:01:36 +08:00
{"data":[]}
2026-08-25 16:40:18 +08:00
```
2026-09-07 21:01:36 +08:00
保留 `m_strInstrumentID` 等原始属性名,不使用前述格式化函数,不保证固定字段集合,也没有 `default=str` 序列化兜底。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
## 6. 实时行情
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`POST /api/data/full_tick`
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
| 字段 | 类型 | 必填 | 默认 | 说明 |
| --- | --- | --- | --- | --- |
| `stocks` | array of string | 否 | `[]` | 原样传给 `ContextInfo.get_full_tick(stocks)` |
2026-08-25 16:40:18 +08:00
```json
2026-09-07 21:01:36 +08:00
{"stocks":["600000.SH","000001.SZ"]}
2026-08-25 16:40:18 +08:00
```
2026-09-07 21:01:36 +08:00
响应直接使用 QMT 行情结果,通常为证券代码到行情对象的映射;内部字段不改名、不裁剪。无法直接序列化的值转为字符串。允许空数组,但具体查询范围由 QMT 决定。底层返回空值时返回 HTTP 500。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
## 7. 提交委托
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`POST /api/trade/passorder`
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
| 字段 | 类型 | 必填 | 默认 | 服务端处理 |
| --- | --- | --- | --- | --- |
| `opType` | integer | 是 | — | `int()` |
| `orderType` | integer | 否 | `1101` | `int()` |
| `stockCode` | string | 是 | — | 直接传入 |
| `prType` | integer | 否 | `11` | `int()` |
| `price` | number | 是 | — | `float()` |
| `volume` | integer | 是 | — | `int()` |
| `quickTrade` | integer | 否 | `2` | `int()` |
| `strategyName` | string | 否 | `""` | `str(...).strip()` |
| `orderId` | string | 否 | `""` | `str(...).strip()`,本地订单标识 |
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
底层参数顺序:
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
```python
passorder(opType, orderType, account_id, stockCode, prType, price,
volume, strategyName, quickTrade, orderId, ContextInfo)
2026-08-25 16:40:18 +08:00
```
2026-09-07 21:01:36 +08:00
请求结构示例(实际提交会调用交易函数):
2026-08-25 16:40:18 +08:00
```json
2026-09-07 21:01:36 +08:00
{
"opType":23,
"orderType":1101,
"stockCode":"600000.SH",
"prType":11,
"price":10.0,
"volume":100,
"quickTrade":2,
"strategyName":"trend",
"orderId":"trend-BUY-example"
}
2026-08-25 16:40:18 +08:00
```
2026-09-07 21:01:36 +08:00
响应示例:
2026-08-25 16:40:18 +08:00
```json
2026-09-07 21:01:36 +08:00
{
"status":"success",
"opType":23,
"stockCode":"600000.SH",
"strategy_name":"trend",
"local_order_id":"trend-BUY-example",
"order_ref":"None"
}
2026-08-25 16:40:18 +08:00
```
2026-09-07 21:01:36 +08:00
`order_ref``str(passorder返回值)`;示例对应底层返回 `None`,不保证提供柜台系统订单号。它与查询接口中来源于 `m_strOrderRef` 的同名字段来源不同。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`success` 仅表示底层调用未抛异常,不代表已经成交或最终报单成功。服务端未按 `orderId` 做幂等去重,请求超时不能证明未提交。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
缺字段、JSON 解析失败及捕获到的类型 / 数值转换错误返回 HTTP 400底层普通异常返回 502底层 `HTTPError` 原样传播。封装未检查价格、数量正负、代码非空或枚举有效性。表中为 REST 服务默认值SDK 显式传参可能不同。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
## 8. 撤单
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`POST /api/trade/cancel_by_id`
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
| 字段 | 类型 | 必填 | 说明 |
2026-08-25 16:40:18 +08:00
| --- | --- | --- | --- |
2026-09-07 21:01:36 +08:00
| `order_id` | string | 是 | 柜台系统订单号,即查询的 `order_sys_id`,不是本地 `orderId` |
2026-08-25 16:40:18 +08:00
```json
2026-09-07 21:01:36 +08:00
{"order_id":"示例柜台订单号"}
2026-08-25 16:40:18 +08:00
```
2026-09-07 21:01:36 +08:00
先调用 `can_cancel_order(order_id, account_id, 'stock')`,可撤时调用 `cancel(order_id, account_id, 'stock', ContextInfo)`
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
成功响应:
2026-08-25 16:40:18 +08:00
```json
2026-09-07 21:01:36 +08:00
{"status":"success","order_id":"示例柜台订单号"}
2026-08-25 16:40:18 +08:00
```
2026-09-07 21:01:36 +08:00
不可撤时响应仍为 HTTP 200
2026-08-25 16:40:18 +08:00
```json
2026-09-07 21:01:36 +08:00
{
"status":"failed",
"order_id":"示例柜台订单号",
"message":"Order does not exist or cannot currently be canceled"
}
2026-08-25 16:40:18 +08:00
```
2026-09-07 21:01:36 +08:00
`cancel()` 返回值严格为 `False`,返回 `{"status":"failed","order_id":"示例柜台订单号"}`,无 `message`,仍为 HTTP 200其他返回值均标记 `success`。最终结果应再查询委托确认。缺失或空字符串订单号返回 400QMT 普通调用异常返回 500。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
## 9. 发行数据
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`POST /api/trade/ipo_data`
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
| 字段 | 类型 | 必填 | 默认 | 说明 |
| --- | --- | --- | --- | --- |
| `type` | string | 否 | `"STOCK"` | 原样传给全局 `get_ipo_data(type)`,不转换大小写或校验枚举 |
2026-08-25 16:40:18 +08:00
```json
2026-09-07 21:01:36 +08:00
{"type":"STOCK"}
2026-08-25 16:40:18 +08:00
```
2026-09-07 21:01:36 +08:00
响应直接序列化底层结果,不包装或重命名字段,具体结构由 QMT 提供。使用默认参数也需发送 `{}`
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
## 10. Python 版本
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
`GET /api/sys/python_version`,无参数。
2026-08-25 16:40:18 +08:00
```json
{
2026-09-07 21:01:36 +08:00
"python_version":"<sys.version>",
"python_version_info":{
"major":3,
"minor":11,
"micro":0,
"releaselevel":"final",
"serial":0
2026-08-25 16:40:18 +08:00
}
}
```
2026-09-07 21:01:36 +08:00
值仅为示例,实际取自服务进程的 `sys.version``sys.version_info`
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
## 11. 错误响应
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
匹配到 `BaseHandler` 的接口默认声明 `Content-Type: application/json; charset=utf-8`,但 `write_error()` 实际输出 `self._reason` 原始文本,**不是 JSON 错误对象**。客户端应先检查 HTTP 状态,错误正文按文本处理。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
| 场景 | HTTP 状态 | 行为 |
| --- | --- | --- |
| Token 缺失或不匹配 | 500 | 未显式设置 reason正文通常为 `Internal Server Error` |
| 单证券查询缺代码 / 无资产 / 无行情 | 500 | 通常为默认错误原因文本 |
| `safe_call()` 中普通异常 | 500 | `QMT: <函数名> call failed.` |
| 下单参数解析 / 转换异常 | 400 | `Invalid order parameters: ...` |
| 下单底层普通异常 | 502 | `QMT order submission failed` |
| 撤单订单号缺失或为空 | 400 | 通常为 `Bad Request` |
| 不可撤或底层明确返回 False | 200 | JSON 中 `status=failed` |
| 路由不存在 | 404 | Tornado 默认错误处理,不保证上述格式 |
| 方法不支持 | 405 | 已匹配 Handler 的错误处理 |
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
除下单外POST 未统一捕获 JSON 解析错误,非法 JSON 可能返回 500非对象 JSON 也未统一校验。`safe_call()` 遇到底层 `HTTPError` 原样传播。
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
## 12. 只读调用示例
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
PowerShell 示例,不提交交易:
2026-08-25 16:40:18 +08:00
2026-09-07 21:01:36 +08:00
```powershell
$apiBase = 'http://127.0.0.1:10086'
$apiHeaders = @{ 'X-Token' = '<服务端 TOKEN>' }
Invoke-RestMethod -Uri "$apiBase/api/portfolio" -Headers $apiHeaders
Invoke-RestMethod -Uri "$apiBase/api/get/stock_name?stock_code=600000.SH" -Headers $apiHeaders
Invoke-RestMethod -Method Post -Uri "$apiBase/api/data/full_tick" -Headers $apiHeaders -ContentType 'application/json' -Body '{"stocks":["600000.SH"]}'
```