TqSdk 新手最常踩哪些坑?按现象定位问题的排查顺序
TqSdk 新手遇到“行情不动、信号重复、订单没成交、持仓不对、程序关不掉”时,最容易同时修改很多代码。更有效的排查顺序是先确认环境与事件循环,再检查数据变化和规则触发,最后核对订单、成交与持仓。一次只定位一层,现象才不会被新的修改覆盖。
行情一直不动,先看 wait_update
get_quote或get_kline_serial返回的是动态引用,数据需要wait_update推进。没有事件循环时,反复读取只会得到同一份内存快照;加sleep也不会主动接收更新。
while True: api.wait_update() if api.is_changing(quote, "last_price"): print(quote.datetime, quote.last_price)若仍无输出,按环境、认证、合约代码、交易时段和字段有效性依次检查。非交易时段没有新价格可能是正常情况,不要立刻把它解释为断线。
还要区分“API 收到更新”和“关注字段变化”。本轮可能只有其他合约或盘口字段变化,last_price分支不执行并不代表事件循环失效。临时记录更新计数与最后行情时间,比无限打印整个 Quote 更清楚。
信号反复出现,先看触发事件和状态
当前 K 线的收盘价在周期内会多次变化。如果策略只应每根新 K 线判断一次,却监听close,同一条件就会重复触发。应监听末行datetime变化并使用已完成数据。
即使信号每根线只算一次,同一方向也可能连续出现。程序需要保存当前目标或上次已处理信号,目标没有变化时不重复提交。防重复不能只靠等待几秒,因为条件仍可能持续为真。
重启后内存状态丢失也是重复来源。启动时先读取持仓和活动委托,恢复最后业务状态,再允许新的信号进入执行层。
下单后没有成交,不要直接再下一笔
insert_order立即返回委托引用,但请求在后续wait_update才发送。先检查订单status、volume_left与last_msg,再查看成交记录和盘口。订单可能存活、部分成交、撤销或被拒绝。
限价没有达到对手盘、市场没有可用对手价、交易时段或合约状态不合适,都可能让订单未成交。不能因为行情中出现过某个价格,就认定自己的委托应当成交。
需要撤单时调用cancel_order后继续等待回报。若期间出现部分成交,重新下单只处理剩余目标。直接复制原订单会导致持仓超出预期。
持仓不符合预期,沿订单链回查
先看多头、空头与净持仓,再看该合约所有活动委托。净仓为零可能是完全空仓,也可能多空对锁;当前仓位正确也可能仍有等待成交的订单。
然后按业务意图找到订单,再按订单找到成交记录,核对方向、开平、手数和价格。成交总量能否解释持仓变化,是比“代码里写了多少手”更可靠的检查。
若使用 TargetPosTask,同一合约不要同时手工insert_order,也不要创建多个任务实例。两套控制逻辑会根据同一持仓各自行动,产生互相冲突的订单。
账户里存在人工交易或其他策略时,还要确认持仓归属。一个策略不能把整个账户持仓都当成自己产生,也不能擅自调整未知仓位。
数据计算结果奇怪,先看动态序列
K 线和 Tick 序列会随事件循环更新。需要固定计算时先复制快照,已完成 K 线通常排除正在形成的末行。窗口不足和空值不应直接填零。
跨合约、跨周期计算要按时间对齐,不能因为两个 DataFrame 行数相同就按行号相减。动态窗口滚动后,iloc[-2]只是当前倒数第二行,不是永久标识。
回测结果异常好时检查未来数据:是否使用当前未完成 K 线最终值,是否反向填充,是否对全样本一次性标准化。公式能运行并不代表只使用了当时可见的信息。
程序停不下来,核对退出职责
所有主循环都应处在可捕获退出的结构中,并在finally调用api.close()。异步任务由同一个 API 管理,不能留下没有监控的无限协程。
交易程序退出前先停止新信号,检查活动委托,并按规则撤销或交接。撤单同样需要事件循环等待结果。强制结束进程可能让账户状态继续变化,下一次启动也更难恢复。
若程序因异常退出,保留完整堆栈和最后状态,不要捕获所有异常后静默继续。一个后台任务已经死亡而主进程仍在运行,比明确失败更危险。
最小复现怎样写才有效
排查时从原程序复制最少内容:一个账户环境、一个合约、一类数据、一个触发条件和必要日志。先去掉指标、文件保存、消息通知和多合约逻辑,证明基础链路是否正常。
最小复现要保留真正的失败条件。若问题是新 K 线重复触发,就保留is_changing与计数;若问题是订单状态,就使用模拟账户保留订单循环。删到只剩一个print,却不再出现原问题,也无法帮助定位。
记录 Python 与 TqSdk 版本、运行时间、合约、账户类型和完整异常,但不记录密码。给别人协助时提供可运行的最小脚本和实际输出,不只描述“不能交易”。
修复后把同一最小复现变成回归测试,再把改动放回原程序。一次加入一个模块,确认问题没有重新出现。这样能找到真正修复点,而不是靠多处改动碰巧让现象消失。
若最小脚本无法复现,就回到原程序比较环境、账户类型、合约、运行时间和并发任务,找出被删除的必要条件。不要为了让示例“看起来简洁”而隐藏真正触发故障的那一层。
排查记录最后要写明根因和验证方式,而不只写“已修复”。同类现象下一次出现时,可以先检查已知原因;若检查不符,再建立新的最小复现,避免反复靠猜测改代码。
新手排错清单
- 环境与认证可用,合约和交易时段正确,
wait_update持续推进。 - 用
is_changing检查正确字段,新 K 线与当前线变化分开。 - 信号、目标和活动委托有防重复状态,重启先恢复账户事实。
- 订单按状态、剩余手数、成交和持仓顺序核对,不用行情代替成交。
- 动态数据先确认长度、空值与时间,异常退出保留堆栈并安全关闭。
排错真正需要的是一条能复现的因果链,而不是更多猜测。先证明数据是否更新,再证明信号是否正确,再证明订单和持仓如何变化,绝大多数常见问题都会落到一个可以修复的具体位置。
