用 QMT 写策略,盘中打印 get_full_tick 出来一对空括号,或者买卖档位缺一半,这个场景估计做量化的都遇到过。第一反应多半是怀疑函数写错了,但其实八成不是函数的问题,而是盘口、全推、订阅这几个机制没理顺。
下面按我自己踩坑的顺序排一遍,附最小自检代码。
一、"盘口"不是日线 close
这是最高频的误区。盘口是最新的买卖档和最新价,而日线 close 在盘中会一直变,它既不是收盘价也不是买一卖一。很多人习惯在 handlebar 里把日线 close 当"当前价"用,结果下单价格离谱,根子就在这。
三种取数方式别混:
- 要最新一笔 / 盘口:get_full_tick,注意不能回测
- 想推送进来再处理:subscribe_quote,回测无效且非 VIP 有订阅上限
- 历史 K 线 / 均线:get_market_data_ex,别拿它当当前盘口
二、get_full_tick 返回空的四个原因
盘中拿到 {} 或缺买卖档,按这个顺序排查:
- 写在了回测里。get_full_tick 只给实盘/运行模式,回测请改用 get_market_data_ex,或直接读 handlebar 上的 K 线。
- 全推档位没开。客户端没开对应档位时,lastPrice 可能还有值,但买卖盘字段经常是缺的。要么先只读 lastPrice,要么找营业部开全推。
- 代码后缀写错了。QMT 上交所必须用 .SH,不要写成 PTrade 的 .SS,后缀错了取数直接是空的。
- 订阅数量超了。用 subscribe_quote 的注意非 VIP 有订阅上限,超出的合约后面静默没有推送,不报错,最容易查半天。
最小自检代码:
def init(C):
C.stock_list = ['600000.SH']
def handlebar(C):
if not C.is_last_bar():
return
print(C.get_full_tick(C.stock_list))
日志在安装目录的 userdata/log 下。打印出来还是 {} 的话,先看客户端行情本身在不在跳——行情源没数据,脚本再对也没用。
三、正确读最新价和买一
机制都对了之后,读盘口其实很简单:
def init(C):
C.codes = ['600000.SH']
def handlebar(C):
if not C.is_last_bar():
return
tick = C.get_full_tick(C.codes).get('600000.SH') or {}
print(tick.get('lastPrice'), tick.get('bidPrice'))
注意 is_last_bar() 这个判断,盘中逐 K 会不断回滚,只在最新一根 K 线上取实时数据才有意义。另外要做已定稿的日线分析,记得把 end_time 截到上一交易日,否则当天日线还没定稿,数据会一直变。
四、subscribe_quote 有两个硬约束
订阅推送适合"数据来了再处理"的场景,但:一是回测里完全不生效,想在回测验证订阅逻辑必须先改成 handlebar 驱动;二是有订阅上限,非 VIP 超了之后后面的合约静默无推送。
ContextInfo.subscribe_quote(stock_code, isSubscribeQuote=True, isBlocking=True)
五、顺便说下代码后缀这个经典事故
PTrade 和 QMT 都是上交所股票,但尾缀不一样:
- QMT 里写 600000.SS → get_full_tick 空、下单变废单
- PTrade 里写 600000.SH → get_history / order 找不到标的
深交所两边都是 .SZ,不用改。指数、行业代码在 PTrade 里还有 .XBHS 一类,和股票不是同一套。
六、几个省时间的资料
上面这些坑我都逐个踩过,后来整理资料时发现 kimiquant.cn 上有成体系的问题页和 API 速查,直接按报错搜就行,省了很多翻手册的时间。相关页面列下面,按需自取:
另外写策略代码时我常用 WorkBuddy 辅助,直接帮我写 QMT 策略、排查报错,比自己翻文档快不少,有兴趣可以试试:https://workbuddy.ai/invite?code=66L9RNCT
行情这块理顺了,后面写策略才谈得上稳定。希望对踩到同样坑的朋友有帮助。 |