# QMT 官方接口核实报告(迅投知识库 dict.thinktrader.net) 核实日期:2026-08-19 资料来源:迅投官方知识库 http://dict.thinktrader.net(所有页面已存为 docs_ref/*.txt 供查阅) --- ## 一、结论速览 | # | 方案假设 | 官方文档核实结果 | 影响 | |---|---------|----------------|------| | 1 | `adjust()` 是 QMT 定时回调 | ❌ **不存在**。官方定时回调是 `ContextInfo.run_time(funcName, period, startTime)`(支持 `500nMilliSecond`)或新版 `ContextInfo.schedule_run` | 骨架代码要改 | | 2 | HTTP 线程(daemon)+ 队列桥接 | ❌ **官方明确:内置 Python 无法使用多线程和多进程,所有策略在同一线程执行** | 方案核心架构要重新设计 | | 3 | `xtdata.get_market_data_ex` 任意线程直调 | ⚠️ **xtdata 模块主动获取接口是 `get_market_data`,不是 `get_market_data_ex`**(后者是 ContextInfo 方法)。且 xtdata 文档写明"本质是和 MiniQmt 建立连接" | 函数名要改;能否连大 QMT 需实测 | | 4 | `passorder` 下单 → 结果回写 | ✅ 确认 `passorder(opType, orderType, accountid, orderCode, prType, price, volume, strategyName, quickTrade, userOrderId, ContextInfo)`,**返回无**,真实状态靠 `order_callback`/`deal_callback` 主推(仅实盘模式生效,需先 `set_account`) | 你的判断正确,补充细节 | | 5 | 下单须在策略线程执行 | ✅ 正确,必须。所有交易函数依赖 ContextInfo 和策略上下文 | 成立 | | 6 | Python 3.6.8 | ✅ 官方确认:"内置了 3.6 版本的 python 运行环境" | 成立 | | 7 | `# coding:gbk` | ✅ 官方明确要求 | 成立 | | 8 | `cancel(orderId, accountId, ContextInfo)` | ⚠️ 实际签名是 `cancel(orderId, accountId, accountType, ContextInfo)`,**有 accountType 参数** | 修正 | --- ## 二、逐项核实详情 ### 2.1 内置 Python 运行环境(快速开始页) > "QMT 极速策略交易系统...内置了 **3.6 版本**的 python 运行环境,提供行情数据与交易下单两大核心功能。" - 确认方案 4.3 的"Python 3.6.8"判断正确(官方口径 3.6)。 - 三种运行机制(官方明确): 1. 逐 K 线驱动 `handlebar` 2. 事件驱动 `subscribe`(订阅推送) 3. **定时任务 `run_time`(固定间隔触发)** ### 2.2 ⚠️ 关于线程(使用须知页)—— 最关键发现 > "**QMT中,python 无法使用多线程和多进程,而且所有策略都在同一线程中执行**,所以策略中应该尽量避免阻塞类的写法,否则会影响其他策略的执行。" **这是对整个方案的颠覆性约束**: - 方案假设的"init() 里启动 HTTP 服务线程(daemon)"**很可能直接失败**——多线程不可用 - 队列桥接(HTTP线程入队 → adjust 出队)的双线程前提不成立 - 需要找到"单线程事件循环"形态的替代架构(见下文第三节) ### 2.3 系统函数(系统函数页)—— 没有 adjust - 官方系统函数列表:`ContextInfo`对象、`init`、`after_init`、`handlebar`、`ContextInfo.schedule_run`、`ContextInfo.cancel_schedule_run`、`ContextInfo.run_time`、`stop`、`ContextInfo.is_last_bar`、`ContextInfo.is_new_bar` 等。 - **全文没有 `adjust` 函数**。 - 替代:定时回调用 ```python # 500ms 定时器 ContextInfo.run_time("f", "500nMilliSecond", "2019-10-14 13:20:00") # 或新版 ContextInfo.schedule_run(func, time_point, repeat_times, interval, name) ``` ### 2.4 交易下单函数(交易函数页) **passorder 完整签名**: ```python passorder(opType, orderType, accountid, orderCode, prType, price, volume, strategyName, quickTrade, userOrderId, ContextInfo) ``` - **返回:无**(不是布尔/错误码!下单是否成功只能靠回调或查询委托) - opType:23=股票买入, 24=股票卖出(期货另有开平仓) - orderType:1101=按股数(深沪通用), 1102=沪市按股, 1202=按金额(对账号组) - prType:5=最新价, 11=限价, 14=模型价(跟价), 12=市价 - quickTrade:0=逐K线生效(默认), 1=最后一根K线立即, **2=调用即下单(不判bar状态)** - userOrderId:用户自设委托ID,会进入 order/deal 对象的 **m_strRemark** 字段 - **编译器界面执行的下单函数不会产生实际委托**(必须策略交易界面运行) - 下单真实结果靠 `get_trade_detail_data` 查询或回调主推 **cancel 实际签名**: ```python cancel(orderId, accountId, accountType, ContextInfo) ``` - accountType: 'FUTURE'/'STOCK'/'CREDIT'/'HUGANGTONG'/'SHENGANGTONG'/'STOCK_OPTION' - 返回 bool(是否发出撤单信号) - 注意:撤单参数是 **orderId(委托号, m_strOrderSysID)**,不是 userOrderId **其他下单**:algo_passorder(拆单)、smart_algo_passorder(VWAP等,需权限)、cancel_task/pause_task/resume_task(任务级)、get_basket/set_basket(篮子)。 ### 2.5 交易查询函数(交易函数页) ```python get_trade_detail_data(accountID, strAccountType, strDatatype[, strategyName]) ``` - strDatatype: 'ACCOUNT'/'POSITION'/'POSITION_STATISTICS'/'ORDER'/'DEAL'/'TASK' - 返回 list 对象,字段以 m_ 开头,如: - 账号: `m_dBalance`(总资产) `m_dAssureAsset`(净资产) `m_dAvailable`(可用) `m_dInstrumentValue`(市值) `m_dPositionProfit`(盈亏) - 持仓: `m_strInstrumentID` `m_nVolume`(持仓量) `m_nCanUseVolume`(可用) `m_dOpenPrice`(成本) `m_dInstrumentValue` `m_dPositionProfit` - 委托: `m_strOrderSysID`(委托号) `m_nOrderStatus`(状态) `m_strRemark`(userOrderId) `m_nVolumeTraded` - 成交: `m_strOrderSysID` `m_dPrice` `m_nVolume` `m_dTradeAmount` **get_value_by_order_id(orderId, accountID, accountType)** 按委托号取委托/成交信息。 **get_last_order_id(accountID, accountType, 'order'/'deal')** 取最新委托号。 ### 2.6 成交回报主推回调(回调函数页) | 回调 | 签名 | 说明 | |------|------|------| | account_callback | (ContextInfo, accountInfo) | 资金账号状态变化 | | task_callback | (ContextInfo, taskInfo) | 任务状态变化 | | **order_callback** | (ContextInfo, orderInfo) | **委托状态变化主推** | | **deal_callback** | (ContextInfo, dealInfo) | **成交状态变化主推** | | position_callback | (ContextInfo, positionInfo) | 持仓状态变化 | | orderError_callback | (ContextInfo, orderArgs, errMsg) | 异常下单 | **重要提示(官方)**: - 回调**仅在实盘运行模式下生效** - 需要先在 init 里调用 **`ContextInfo.set_account(account)`** - order 对象的 `m_strRemark` = userOrderId, `m_strOrderSysID` = 委托号 ### 2.7 行情数据(行情函数页 + XtQuant 文档) **内置 Python(策略内)**: ```python ContextInfo.get_market_data_ex(fields=[], stock_code=[], period='1d', start_time='', end_time='', count=-1, dividend_type='follow', fill_data=True, subscribe=True) ``` - 返回 {stock_code: pd.DataFrame},index 为 time,columns 为 fields(open/high/low/close/volume/amount...) - 注意:该函数**不建议在 init 中运行**(init 中只能取本地数据) - 周期: 'tick' '1m' '5m' '15m' '30m' '1h' '1d' '1w' '1mon' '1q' '1hy' '1y' - 其他: `ContextInfo.get_full_tick`(全推) `ContextInfo.subscribe_quote`(订阅) `ContextInfo.get_history_data`(不推荐) `ContextInfo.get_local_data`(不推荐) **XtQuant 原生(外部 Python)**: ```python xtdata.get_market_data(field_list=[], stock_list=[], period='1d', start_time='', end_time='', count=-1, dividend_type='none', fill_data=True) ``` - **注意:xtdata 的主动获取接口是 `get_market_data`(不是 `get_market_data_ex`!)** `get_market_data_ex` 是内置 Python 的 ContextInfo 方法 - xtdata 返回:period 为 K 线时 {field: pd.DataFrame}(index 为 stock_list, columns 为 time_list)——与内置 Python 的返回结构**不同**(内置是 {stock: df},xtdata 是 {field: df})! - **运行逻辑(官方原文)**: "xtdata提供和MiniQmt的交互接口,本质是**和MiniQmt建立连接**,由MiniQmt处理行情数据请求" ### 2.8 XtQuant 交易模块(xttrader 文档) - 外部 Python 可用 `XtQuantTrader(path, session_id)` 连接,**文档明确绑定 MiniQMT**(`userdata_mini` 路径) - 完整 API:order_stock(同步)/order_stock_async(异步)、cancel_order_stock、query_stock_asset/orders/trades/positions、回调 on_stock_order/on_stock_trade/on_order_error 等 - **这印证了方案判断:外部 xtquant 依赖 miniQMT,大 QMT 不适用,必须走桥策略** ### 2.9 ContextInfo 使用注意(使用须知页) > "由于底层机制的限制,ContextInfo 中存储的变量值将会回滚...请避免在其中存储任何变量。" - 官方推荐用 `class G(): pass; g = G()` 全局对象存状态 - 桥策略的队列/结果字典等必须用模块级全局变量,不能挂 ContextInfo --- ## 三、对方案的修正建议(基于官方文档) ### 3.1 核心架构问题:单线程约束 官方明确"python 无法使用多线程"。这意味着方案的核心机制(HTTP 线程收请求 + 队列 + adjust 线程执行)需要重新设计。可选方向: **方向 A:单线程异步 HTTP(推荐验证)** - 用 `asyncore`/`selectors` 实现非阻塞 HTTP,或 - 用 `BaseHTTPRequestHandler` 但**不开线程**——利用 run_time 定时器轮询处理 socket - 但 HTTP 长连接/慢客户端会阻塞整个策略,风险仍在 **方向 B:桥只做"指令中继",HTTP 放外部(推荐)** - 把 HTTP 服务放在**外部独立进程**(Linux 侧或 Windows 侧 Python 3.14),不占用 QMT 策略线程 - QMT 策略内只保留一个轻量"指令执行器":run_time 定时轮询一个本地指令源(文件/命名管道/端口) - 外部 HTTP 桥收到 /order → 写指令文件/管道 → QMT 定时器取指令 → passorder → 回写结果文件 - 这样 QMT 内零线程、零阻塞,完全符合官方约束;HTTP 的并发/超时/安全都在外部处理 **方向 C:验证"线程是否真的不可用"** - 官方文档说不可用,但社区有说法"能起线程但会阻塞主线程/不稳定" - 建议在国金 GJQMT 上做个 10 分钟实测:init 里 `threading.Thread(target=...).start()` 看是否报错、是否影响 handlebar - **如果实测线程能跑**,原方案(方向A变体)可保留,但必须加超时保护,避免慢请求卡死策略 ### 3.2 函数修正清单 1. ~~adjust()~~ → `ContextInfo.run_time("on_bridge_timer", "500nMilliSecond", "...")` 2. ~~xtdata.get_market_data_ex~~ → 内置 Python 用 `ContextInfo.get_market_data_ex`;若用 xtdata 模块则 `xtdata.get_market_data` 3. ~~cancel(orderId, accountId, ContextInfo)~~ → `cancel(orderId, accountId, 'STOCK', ContextInfo)` 4. 下单状态:passorder **无返回** → 必须 `order_callback` + `deal_callback`(实盘模式 + set_account)或轮询 `get_trade_detail_data` 5. 状态存储:不能挂 ContextInfo → 用模块级全局 class 实例 ### 3.3 必须实测的三个点(写码前) 1. **线程可用性**:init 里起 daemon 线程是否真的失败(决定方向A还是B) 2. **set_account**:实盘模式回调是否需要、如何配置账号 3. **xtdata 在大 QMT 内能否 import 并取数**:若大 QMT 无 miniQMT 的 userdata_mini 连接,xtdata 可能连不上——则行情一律走 `ContextInfo.get_market_data_ex` ### 3.4 仍成立的设计 - 队列桥接思路(若线程可用)、复权/周期参数、GBK 编码、8610 端口复用、脚本零改动目标 - passorder 必须在策略上下文执行(官方:下单函数需要 ContextInfo,且编译器环境不下单) - 实盘模式登录才能收完整回报(官方:回调仅实盘生效)