资讯详情 QMT自动化下单实战:Python连接、下单、回调与避坑全指南
📅 2026/10/17 16:35:37
简介面向量化交易开发者的自动化下单实操包围绕迅投QMT系统展开重点解决极简版QMT环境配置与程序化交易开发问题也兼顾普通投研版与极简版两种模式的特点。内容涵盖XtQuant模块的下载安装、XtData行情模块与Xttrader交易模块的调用方法并说明了系统支持的Python版本与开发接口配套示例则从头演示了连接行情、订阅数据和发送交易指令的关键环节。资源包共4个文件包含Python脚本、依赖清单、HTML页面及inscode工程文件整体压缩后仅9KB轻量易部署。目前已有371人学习/下载适合刚开始接触QMT、希望用Python实现自动化下单或复杂策略的投资者和开发者。通过这份资源可以快速看清行情获取到交易执行的完整链路少走环境搭建的弯路甚至可以直接基于示例脚本修改出适合自己的下单策略。1. QMT自动化下单先把这份代码能帮你省掉什么说清楚QMT自动化下单说白了就是把你在交易终端里手点的那套动作——敲代码、输价格、填数量、点买入卖出——变成一段能自己跑的Python代码。手动下单最痛的不是慢而是盘中信号出来的那几秒你正好在切屏、接电话回来行情已经走掉多账户、多策略挂在同一个终端上人根本盯不过来。这份代码包就是把这条链路完整工程化连通道、按信号下单、用回调确认成交、管住订单状态。适合两类人已经在QMT里写过指标、想往自动化下单迈一步的选股型玩家想把QMT当成券商柜台接进自研系统的后端或量化开发。前提是你有一定Python基础且券商已开通程序化交易权限。2. 打通交易通道连接QMT客户端与交易账户的完整姿势2.1 先分清QMT的两种Python运行方式别选错入口QMT终端里其实有两套Python环境很多人第一次做自动化就卡在选哪个。第一套是终端内置的“策略编辑器”。在QMT界面里新建Python策略直接在终端里写代码、跑回测、启动模拟或实盘。好处是上手快行情数据接口齐全适合写简单的均线策略、盯盘预警坏处是运行在终端进程内部系统级权限和安全限制非常多本地文件读写、网络请求这些动作经常被拒掉这在后面避坑章节会专门展开。它更适合回测和轻量监控不适合做需要独立部署、长期盯盘的服务。第二套是miniQMT模式也常叫QMT极简模式。客户端正常登录后外部Python进程通过xtquant接口包连进去拿到一个完整的交易通道。这套环境里你可以用自己的IDE、自己的第三方库下单、撤单、查询持仓、订阅行情都是一套正式API。自动化下单的主流做法是把策略跑在这个外部进程里QMT客户端只当作交易通道和数据源。我一般直接推荐走这条线因为后面要加的日志、异常恢复、重启处理都能用常规工程手段解决不用跟终端的安全沙箱较劲。选环境的判断其实很直接只是想在终端里快速验证想法内置编辑器够用要接实盘、要跑多策略、要有自己的订单管理和风控走miniQMT别犹豫。代码包里的模板也都是基于xtquant外部进程这套理由就是可运维、可扩展、坑少。选错入口的后果是同一段下单代码在内置环境和外部进程里表现、权限、日志可见性完全不同很多人拿到代码先跑报错才回头找原因其实在选环境那一步就已经埋下问题。2.2 建立交易通道连接、确认、查资金三步走走miniQMT第一个动作是让外部Python进程连上正在运行的QMT客户端。这里有个很多新手翻车的细节连接路径用的是QMT安装目录下的用户数据目录不是随便填一个文件夹。不同券商版本里目录名可能略有差异但基本都类似userdata_mini这样的名字以你本地安装目录为准。from xtquant.xttrader import XtQuantTrader from xtquant import xttype # QMT 客户端安装目录下的用户数据目录按本机实际路径改 qmt_path rD:\QMT\userdata_mini session_id 20001 # 每个连接进程要唯一 xt_trader XtQuantTrader(qmt_path, session_id) xt_trader.start() connect_result xt_trader.connect() # 0 表示连接成功 print(连接结果, connect_result)这段代码的逻辑XtQuantTrader是交易通道对象start()把内部线程拉起来connect()去跟已经打开的QMT客户端握手。session_id是连接标识两个策略进程不能用同一个session_id否则连接状态会互相干扰典型表现是其中一个策略收不到回调。固定用一个自己的session_id也是后面断线重连能对得上日志的前提。connect()返回非0时别慌按顺序查三件事QMT客户端是不是已经登录交易账户了qmt_path是不是指向了正确的用户数据目录是不是有另一个Python进程占用了同一个session_id。这三件事占了九成连接失败的原因比翻接口文档快得多。连接成功后先别急着下单。我习惯先查一次资金和持仓确认通道真的通了、账号也读到了。account xttype.StockAccount(你的资金账号) # 填QMT里看到的证券账号 asset xt_trader.query_stock_asset(account) print(可用资金, asset.cash) print(持仓市值, asset.market_value) positions xt_trader.query_stock_positions(account) for pos in positions: print(pos.stock_code, pos.volume, pos.available_volume)资金账号不是随便填的必须和QMT登录时使用的证券账号一致通常是一串纯数字。这里有个容易被忽略的点可用资金和可卖数量在实盘里是动态变化的买入后资金会先冻结卖出后资金要等清算才真正到账这些字段的准确值只能在交易时段通过查询接口拿到。所以下单前的校验一律以查询结果为准不要用本地自己维护的账本这是自动化下单里很重要的纪律。提示连接参数和账号信息在每个券商终端里可能有细微差异拿到代码包后第一件事是核对qmt_path和资金账号的填写位置不要直接照抄。2.3 断线重连把连接状态当成策略的一部分QMT客户端不是永远不关的。终端升级、券商强制登出、电脑重启都会让外部连接断开。断开后xt_trader的状态会变策略进程如果不处理到盘中才发现下单失败就晚了。常见做法是启动后固定一个session_id定期检查连接状态断了就尝试重连import time def ensure_connected(xt_trader): if not xt_trader.is_connected(): result xt_trader.connect() print(重连结果, result) time.sleep(3)这个函数只是兜底它不能代替QMT客户端本身的恢复。如果终端被强制登出外部进程重连多少次都没用必须人工重新登录交易账户。所以自动化上线前要先约定好谁负责看终端状态。我的做法是把“终端是否在线”做成一个心跳每30秒把连接状态和最新行情时间推送到消息群掉线了人能第一时间处理而不是策略静默停在那里等收盘才发现一整天没下单。session_id这里再多说一句每次启动都随机生成重连后日志里的连接上下文就对不上号了排查问题时要靠它区分是哪次启动、哪个进程发的委托。固定下来一劳永逸。3. 把下单逻辑写成代码order_stock参数逐项拆解3.1 下单接口选哪个order_stock 还是旧版 passorder先说结论miniQMT外部环境用order_stock。早期QMT教程里大量出现passorder函数opType、accountType、accountID那一长串位置参数写起来很痛苦而且不同券商终端里默认参数不一样同一段代码换台机器就可能翻车。它更像是终端内置策略时代留下的接口老策略维护可以继续用新写的自动化代码不建议再走这条路。xtquant的order_stock是官方在新接口包里提供的方法参数名清晰下单、撤单、查询、回调是一套完整的体系。这里有个检查版本的小技巧任何一次对接都值得先跑一遍import inspect from xtquant.xttrader import XtQuantTrader print(inspect.signature(XtQuantTrader.order_stock))提示各券商分发的xtquant版本存在滞后网上抄来的代码报TypeError大概率是版本字段差异。先打印签名对比再写参数能省掉大量排查时间。打印出来的签名会告诉你本地版本到底支持哪些参数、顺序是什么。我接每一家新券商终端时都会先做这一步避免把别人环境里能跑的代码当成通用代码。3.2 参数逐项拆解委托类型、价格类型、数量、策略标识下面是核心下单代码参数用注释标清楚# 买入 600000.SH 100 股限价 10.50按数量下单 order_id xt_trader.order_stock( account, # xttype.StockAccount 对象 600000.SH, # 证券代码带交易所后缀 23, # 委托类型23 按数量24 按金额 100, # 委托数量股或金额 7, # 价格类型7 限价5 最新价11 对手方最优 10.50, # 价格限价单必填非限价传 0 demo_strategy, # 策略标识回调里用来区分来源 0 # 委托编号0 表示由系统生成 ) print(委托编号, order_id)order_stock是一个异步接口它不等于成交。返回的order_id只是这笔委托的唯一编号后续撤单、查委托状态都要靠它。逻辑上必须走一遍“委托已提交 → 已报 → 已成/已撤/废单”的状态流转这是做自动化下单必须建立的认知。几个关键参数单独看参数常见取值含义order_type23、24按数量下单 / 按金额下单金额单由柜台按价格折算股数部分券商版本不支持price_type5、7、11、12最新价 / 限价 / 对手方最优 / 本方最优14、15等最优五档类型以本地终端为准price浮点数限价时必填非限价类型传 0strategy_name字符串回调里区分策略来源的标记建议全局唯一价格类型要单独说两句。限价单最可控适合网格、回落卖出这类需要精确成交价的策略最新价和对手方最优适合追求成交率的场景但成交价会有滑点做抢单或高频才有意义。实盘建议从限价单开始跑先把链路跑稳再试更激进的类型。还有一个高频误用数量单位。QMT里股票数量按“股”填不是按“手”。下买单还要注意整数倍规则大部分主板按100股整数倍报科创板是200股起、可按1股递增这些柜台都会校验填错了就变成废单。3.3 撤单、查委托、查成交订单生命周期要自己管起来下单之后订单会经历多个状态。不能发一个order_stock就以为完事至少要把撤单和查询封装进流程# 撤单order_id 来自 order_stock 返回值 cancel_result xt_trader.cancel_order_stock(account, order_id) print(撤单提交结果, cancel_result) # 0 表示撤单已提交 # 查询当日所有委托 orders xt_trader.query_stock_orders(account) for order in orders: print(order.order_id, order.stock_code, order.order_status, order.cancel_status) # 查询当日所有成交 trades xt_trader.query_stock_trades(account) for trade in trades: print(trade.order_id, trade.stock_code, trade.traded_volume, trade.traded_price)重点看order_status字段不同版本用整数或枚举表示常见状态有已报、已成、已撤、废单。撤单也是异步的提交返回0不代表撤单生效要再查一次或等回调确认。撤单这里吃过亏的人不少以为cancel返回0就撤掉了结果成交回报晚到挂上一个不想要的仓位。废单拒单的明细通常在order的error_msg字段里查到先打印出来。调自动化单最省时间的办法不是猜而是把每笔废单的原因打到日志里对照着改参数。代码包里这一段已经封装成统一的订单管理函数下单、撤单、状态查询、结果回填都在里面。4. 行情订阅与策略联动从买入信号到委托成交的回调链4.1 先订阅行情策略才有触发源自动化下单的上游是行情。xtquant里的xtdata提供行情订阅能力策略要先拿到行情才能在tick或K线更新时执行信号判断。这里要注意period的选择K线订阅按bar更新适合中低频策略tick订阅按每一笔逐笔推送适合日内和抢单场景。两者回调频率差距很大做日内尽量用tick做日线级别趋势用K线就够不要无脑订阅tick给自己增加压力。from xtquant import xtdata def on_tick(quote): # quote 是行情快照对象常用字段有 last_price、ask_price、bid_price 等 print(quote.stock_code, quote.last_price) # 订阅 600000.SH 的 tick 全推送 xtdata.subscribe_quote(600000.SH, periodtick, callbackon_tick)subscribe_quote是订阅动作行情进来后回调on_tick。这里有一个工程层面的要点行情回调运行在xtdata自己的线程里不要在回调里做耗时操作比如写大量日志、请求数据库、跑重计算。回调里只做轻量判断和数据入队重逻辑放到策略主线程处理否则行情推送会被卡住后面的tick全部堵在门口。period的取值在不同版本的xtdata里写法和支持范围略有差异常见的是1m、5m、1d、tick这套代码包里已经定义成了常量避免每次手写字符串写错。4.2 成交回报回调委托和成交要分清还要用order_id反查xtquant的成交回报走另一套回调需要继承XtQuantTraderCallback类from xtquant.xttrader import XtQuantTraderCallback class MyCallback(XtQuantTraderCallback): def on_stock_order(self, order): # 委托状态变化回调 print(委托回报:, order.order_id, order.order_status, 废单原因:, order.error_msg) def on_stock_trade(self, trade): # 成交回报回调 print(成交回报:, trade.stock_code, trade.traded_volume, trade.traded_price) xt_trader.register_callback(MyCallback())这里有个很多人踩过的坑把on_stock_order当成已成交。委托回报只是状态变化通知委托变成“已报”不代表成交了真正确认成交的是on_stock_trade。如果在委托回调里看到“已报”就立刻更新持仓甚至发下一单同一信号重复下单就是这么来的。回调里拿到的order_id和order_stock返回值是同一个编号。多标的、多策略同时跑的时候必须用order_id把成交回报映射回具体的策略上下文。我一般在下单时维护一个字典order_id → 信号ID成交回调里反查就能知道这笔成交是哪个策略、哪个价格触发点带来的。没有这一步账户里多了仓位都不知道是谁买的。4.3 一个能跑的联动骨架信号触发 → 校验 → 下单 → 回报把前面几节串起来最简化的骨架长这样。示例信号比较粗暴最新价上穿昨收就买只用来演示链路。class AutoTrader: def __init__(self, xt_trader, account): self.xt_trader xt_trader self.account account self.signals {} # order_id - signal_id self.last_close {} def on_tick(self, quote): stock quote.stock_code if quote.last_price self.last_close.get(stock, 0): self.place_buy(stock) def place_buy(self, stock): asset self.xt_trader.query_stock_asset(self.account) if asset.cash 10000: return price round(self.last_close.get(stock, 0) * 1.01, 2) order_id self.xt_trader.order_stock( self.account, stock, 23, 100, 7, price, demo_strategy, 0) self.signals[order_id] stock def on_stock_trade(self, trade): stock self.signals.get(trade.order_id, None) if stock: print(f策略信号 {stock} 成交 {trade.traded_volume} 股 {trade.traded_price})这条链路的逻辑tick回调触发信号 → 下单前查一次资金 → order_stock发出委托 → 在signals字典里登记 → 成交回调里反查对应标的。self.last_close在完整版代码里由历史K线初始化这里只留了一个空字典结构代码包已经补全初始化逻辑。骨架里的信号判断直接在回调线程里做了真实策略里我建议把信号判断放到独立线程tick回调只负责把行情推入队列这样策略计算再重也不会阻塞行情订阅。调试时先别接实盘把行情来源换成历史数据打印每笔委托和成交回调确认链路通了再考虑实盘。这个链路同时也是一个排查顺序先确认行情有输出再确认资金查询有值然后确认下单返回正数order_id最后看成交回报。任何一环没现象问题就在那一环分层排查比整体跑起来看结果高效得多。5. QMT自动化下单避坑五个高频问题与排查记录下面这五个问题按出现频率排前两个是环境层的装好环境就能绕开后三个是业务层的会在上线后陆续暴露。排查时按这个顺序走比从报错信息开始猜有效得多。5.1 本地文件读写受限日志和配置写不进去现象策略里用open()写日志、读config.json代码不报错但文件根本没生成或者直接抛PermissionError。这个问题在QMT内置Python环境里特别明显社区里关于“QMT是否允许读本地文件”的讨论基本都是这个场景。原因QMT内置策略环境做了系统级隔离文件系统访问被限定在终端认可的范围里网络请求同样被拦。这是防恶意代码的安全设计不是你的代码写错了。外部miniQMT进程不走这个沙箱所以同样的文件操作在外面跑没有问题。解决我一般把配置、日志都放在外部Python进程侧用miniQMT方式连接终端文件操作发生在策略进程自己的目录不受终端沙箱影响。如果必须在内置环境跑先把路径切到终端认可的用户数据目录下并用小文件试写一次验证权限别把“代码能跑”当成“文件能写”。5.2 msvcp140.dll 缺失环境依赖翻车现象import xtquant时报ImportError: DLL load failed或者Windows弹窗提示“由于找不到msvcp140.dll无法继续执行代码”。原因xtquant依赖微软VC运行库精简版或新装Windows往往没带全另一个常见原因是Python位数和xtquant位数不一致32位Python去加载64位动态库报错方式同样是找不到模块。解决先安装Microsoft Visual C 2015-2022 Redistributable x64版本装完还报错确认当前Python解释器是64位用conda环境再检查激活环境用的是哪个base解释器。这个坑五分钟左右能排完但第一次遇到容易懵因为报错信息和你的代码没有任何关系。5.3 委托提交成功却被拒单下单不报错不等于成功现象order_stock返回了一个正数order_id看起来一切正常收盘一看持仓根本没变。查当日委托时订单状态是废单error_msg里有拒单原因。原因常见三类——委托量不是交易所允许的整数倍price_type与交易所规则不匹配标的状态异常比如停牌、封涨跌停、当日买入不可卖出。这些都不是代码异常柜台直接拒绝但接口层面不抛异常给你。解决下单前做三层检查。数量按每只股票对应的规则取整限价单价格确保在涨停和跌停区间内可卖数量用query_stock_positions返回的available_volume不要相信自己记的持仓。排查废单时第一件事永远是打印order.error_msg别靠猜。5.4 断线重连后的重复下单订单状态没有恢复现象QMT客户端重启后策略进程自动重连成功但之前委托的成交没有出现在回调里策略以为没成交又按同一信号下了一单结果双倍仓位。原因重连成功后回调只推增量历史委托不会重放策略内存里的订单状态已经丢了而资金和持仓查询有清算时延来不及拦住第二单。这是自动化里有代表性的重复下单路径。解决启动时主动拉一次当日委托和持仓建立内存快照connect成功后强制走一遍状态恢复流程核心是“从柜台拉数据重建状态而不是信任本地缓存”下单前做幂等判断——同一个标、同一个信号已经存在有效委托就不再重复下单。从那以后我每次都把状态恢复放在connect之后第一步不恢复不交易。5.5 不同券商 xtquant 接口差异现象从网上抄来的代码在自己券商终端上跑参数名对不上报TypeError或者某个参数本地版本不支持。原因券商分发的xtquant版本有滞后不同券商之间版本号可能差很多默认参数、支持的类型集合不完全一致。解决本地打印inspect.signature(order_stock)和文档字符串以本地终端为准写策略时只用稳定存在的字段对版本敏感的参数做一层封装。代码包里提供了一份参数适配配置把本地差异收敛在一个文件里不散落在策略逻辑各处。6. 进阶模拟盘先行把下单前校验做成习惯模拟盘是自动化下单里最后一道防线。策略回测通过、行情回调正常不代表order_stock这条链路在实盘环境下不翻车。我的习惯是任何新策略先挂模拟柜台跑满一个交易日盯着委托回报和信号差异再决定要不要切实盘。下单前的校验我建议做成一个独立函数强制每次下单前调用无论信号判断写得多自信def validate_order(xt_trader, account, stock_code, volume, price, side): # 1. 数量合法性 if volume 0 or volume % 100 ! 0: return 数量不合法 # 2. 资金和可卖数量校验数据必须来自查询接口 asset xt_trader.query_stock_asset(account) if side buy and price * volume asset.cash: return 资金不足 pos_map {p.stock_code: p.available_volume for p in xt_trader.query_stock_positions(account)} if side sell and volume pos_map.get(stock_code, 0): return 可卖数量不足 return None校验函数返回None就放行返回字符串就把这笔委托拦下来并写进日志。校验用的资金、持仓必须来自柜台查询不要用本地变量记账盘中资金冻结、持仓变动都是常态。这个函数我每接一个新策略都会强制加一遍哪怕对方说“就一个简单网格不用校验”我也会加因为一次超买就能把几周的收益吃回去。另一个复盘技巧是把order_id和信号绑定。下单时把信号ID写进self.signals收盘后拉query_stock_trades的成交记录和策略日志里的信号逐条对账对不上就是幽灵成交或者漏单。这比看净值曲线直观能直接定位到是哪一笔、哪个信号出了问题。代码包按“连接 → 下单 → 回调 → 校验”四层整理了模板我每次接新策略都是直接改参数不重新搭架子。不过还是提醒一句券商环境差异大务必先在你自己的QMT终端上跑一遍签名检查和模拟盘验证再谈实盘。这个习惯帮我挡掉过不止一次翻车希望帮到你。本文还有配套的精品资源点击获取