一、前情:miniQMT 被禁用之后,迁移到大 QMT
这一轮监管防外接改造,券商陆续禁用 / 收紧 miniQMT(xtquant 本地外接)通道,原来跑在自己 Python 环境里的那批策略,正在成批迁回大 QMT 客户端里跑。如果你也是迁过来的,先对齐一个最大的心智模型差异:
| miniQMT(xtquant 外接) | 大 QMT(客户端内策略) | |
|---|---|---|
| 你的代码跑在哪 | 你自己的 Python 进程 | 客户端内的唯一一条 Python 线程 |
| 多线程 / 多进程 | 自己进程里随你开 | 不可用,客户端架构约束 |
| 一个策略卡住 | 只影响你自己这个进程 | 同一客户端上所有策略一起停 |
| 等待类写法(sleep / 轮询等) | 自己的进程自己负责 | 禁止,必须改写为状态机 |
迁移动作本身不难(行情、交易接口的语义大体能对上),难的是换脑:过去你操心的是自己进程里的并发安全,现在要操心的是”我的回调会不会把公共跑道堵死”。本文就围绕这条线程,把机制、坑位、写法纪律一次讲透。
二、为什么你必须搞懂”单线程”
三个真实场景,撞过的人都懂:
- 三个策略同时挂实盘,其中一个里写了
sleep等成交——几分钟后发现另外两个策略也不动了。不是行情断了,是那条被 sleep 占住的线程是大家共用的。 - 下单函数刚调用完,立刻查资金和委托——什么都没查到,以为程序出 bug 了。其实是交易回报走的是另一条通道,你的线程根本没参与。
- 想给策略加
threading提速——加不上,也不允许加。这不是 Python 语言的问题,是客户端的架构决定。
这三个场景指向同一件事:大 QMT 的 Python 策略层,只有一条线程。理解它,后面的每个设计决策才有依据。
三、原理:一条线程,三个入口
先看两条官方原文(来源:QMT 官方知识库):
| 出处 | 原文要点 |
|---|---|
| 使用须知 § 关于线程和进程 | QMT 中 python 无法使用多线程和多进程,而且所有策略都在同一线程中执行,策略中应尽量避免阻塞类写法 |
| 常见问题 § 下单与回报相关 | 任意一个策略阻塞线程(死循环 / sleep / 加锁)会导致所有策略的执行被阻塞,所以不能在策略里写等待操作 |
大 QMT 提供两大类、三种运行机制,它们不是三条线程,而是三个往同一条线程里投递回调的入口:
| 机制 | 分类 | 触发源 | 盘中触发频率 |
|---|---|---|---|
handlebar |
事件驱动 | 主图品种每个新分笔到达 | 股票 tick 约 3 秒一次 |
subscribe 回调 |
事件驱动 | 订阅品种新分笔到达 | 随行情推送 |
run_time 定时器 |
定时任务 | 固定时间间隔 |
画成图就是:
1 | [主图品种 tick] [订阅品种 tick] [定时器到点] |
串行、不可抢占:任一回调正在跑,其余回调只能在队列里等。
为什么下单不会把这条线程堵死
交易接口是异步的(常见问题 § 下单与回报相关):
passorder(quickTrade=2)调用后立刻发出委托就返回,不等委托回报,也不阻塞 python 线程;- 委托 / 成交 / 持仓 / 资金状态由客户端后台维护本地缓存,
get_trade_detail_data和交易回调函数读的都是缓存,不是实时查柜台; - 缓存由柜台推送刷新——有交易主推的柜台 50ms 一次,没有主推的 1-6 秒一次(具体咨询自己的服务提供商)。
直接后果:卖出委托后立刻查询,查不到这笔委托、可用资金也不会变多。这不是 bug,是缓存延迟。所以实盘要用全局字典自建”待报状态机”,未确认前暂停该品种后续报单,防超单。
ContextInfo 深拷贝回滚——单线程逐 K 线模型的副产品
盘中每个分笔都触发一次 handlebar,QMT 为了模拟”K 线走完才生效”:
- 每次
handlebar调用前对ContextInfo做一次深拷贝; - 下一个分笔若不是新 K 线的首个分笔,
ContextInfo回滚为那份深拷贝; - 只有 K 线结束分笔的修改才真正保存。
文档点明的两个副作用:每个分笔都深拷贝会拖慢策略运行;ContextInfo 只适合配 quickTrade=0,立刻下单(quickTrade=2)的状态必须存普通全局变量。这条机制在后面 Q5 讨论”合并策略”时还会用到。
四、被问爆的七个问题
Q1:大 QMT 真的就是单线程的吗?
是。而且注意:它不是一个可以切换的”模式”,没有”单线程模式 / 多线程模式”这个选项——只要你是正常登录的大 QMT 客户端(非极简形态),你写的每个策略都被放进同一条线程里跑,这是唯一的内置执行形态。
Q2:能变成多线程吗?
不能,而且升级 Python 版本也改变不了这一点。
原因要说透:threading / multiprocessing 在 QMT 策略里不可用,这不是 Python 语言的限制(Python 本身当然支持多线程),而是客户端架构的约束——所有策略的回调都由同一个宿主线程调用,客户端没有给 Python 层开第二条线程的口子。所以:
- 你升级到 Python 3.12,线程模型原样不动,别指望版本升级带来并行;
- 单线程带来的”不能等待”问题,官方用两层异步设计来补偿:下单异步(调用即返回)、回报后台缓存刷新。
Q3:内置 Python 能升级到 3.12 吗?
能,但不是”你”去升级,而是客户端版本升级带来的。
事实链:
- QMT 官方知识库主体(快速开始、常见问题)目前仍写”内置 3.6 版本 Python”,示例代码基于 Python 3.6 规范;
- 迅投官方社区 2026-10-15 的官方回复明确:将 QMT 的内置 Python 从 3.6.8 升级到 3.12,2.1.32 及以上版本的客户端支持该功能,具体应联系你服务提供商,问是否支持升级到 3.12。
三个注意点:
- 以你自己客户端的实际为准。券商版的更新节奏各不相同,看「设置 → 模型设置」指向的 bin.x64 目录、「我的 → 下载 Python 库」装出来的版本,才是你真实的运行环境;
- 升级客户端后先回归老策略。老代码基于 3.6 编写,3.12 下个别早已废弃的写法可能报错——先在模拟信号模式把每个策略完整跑一遍再上实盘;
- 再强调一次:3.12 不会带来多线程。版本解决的是语法与生态(新库、新语法),并发模型纹丝不动。
Q4:多个策略同时开启,到底是怎么个执行顺序?
Q5:该不该把所有策略写进一个策略里?
Q6:is_last_bar 到底该怎么用?实盘第一行怎么写?
Q7:单线程下怎么让策略跑得更快?
五、由此倒推出的编程纪律
六、几个容易做错的设计点
七、落地三步走
八、常见问题排查表
九、免责声明(请务必完整阅读)
未显示内容请前往微信公众号阅读: