期货量化程序 time.sleep 卡死:天勤单线程与 deadline 替代
前言
国内期货量化程序常见写法是:用 Python 调用天勤 TqSdk,创建TqApi连接行情和交易服务,在while True循环里反复调用api.wait_update()——每调用一次,程序从服务器收一批螺纹钢等合约的行情更新、委托回报、成交回报,内存里的quote、order、position才会刷新。交易信号多在 K 线上算:例如订阅 5 分钟get_kline_serial,均线金叉时TargetPosTask.set_target_volume(3)表达「目标净多 3 手」,真正的报单发生在之后的wait_update里。
有人为「等下一分钟再检查信号」或「每 60 秒扫一眼持仓」,在循环里写time.sleep(60)。这 60 秒里主线程被堵住,行情和成交回报堆在缓冲区,醒来一次性涌入,order.status、volume_left可能连跳几态,tick 级止损也来不及执行。还有一次在发钉钉告警时sleep等 HTTP 返回,接口卡住两分钟,回来时期货已经跳了一大段。天勤TqApi基于 asyncio 协作调度,wait_update()才是程序心跳,长时间阻塞等于掐住脖子。下面说明该用什么替代、定时平仓怎么写。
一、先弄清几个名词
| 名称 | 是什么 | 和 sleep 的关系 |
|---|---|---|
TqApi | 天勤连接行情、交易的主对象 | 单线程事件循环的载体 |
wait_update() | 收一批行情/回报并刷新内存对象 | 正确的心跳,应用它代替 sleep |
quote/order/position | 行情、委托、持仓对象 | sleep 期间不更新 |
TargetPosTask | 自动调仓类 | 撤单报单发生在 wait_update 里 |
time.sleep | Python 阻塞当前线程 | 会堵住整个 TqApi 循环 |
二、天勤单线程模型
- 一个
TqApi实例对应一个事件循环,负责收发包、更新quote、order、position。 wait_update()驱动循环前进;不调就 stagnate。time.sleep、同步 HTTP、重计算都会占住线程,期间无法及时处理回报。
target_pos_task.py里 task 的撤单报单也依赖后续wait_update。
三、sleep 的典型危害
| 场景 | 后果 |
|---|---|
| sleep 等待下一分钟 K 线 | 应用is_changing(klines, "datetime") |
| sleep 重试报单 | 错过撤单窗口、重复报单 |
| sleep 等钉钉回调 | 断线无感知 |
| Jupyter 里 sleep 调试 | 内核假死 |
四、正确替代:wait_update 与 deadline
等待一段时间或有更新:
importtime deadline=time.time()+60whileTrue:ifnotapi.wait_update(deadline=deadline):# 60 秒内无业务包,做超时逻辑check_heartbeat()deadline=time.time()+60continue# 有更新,正常处理on_market_update()deadline为 Unix 秒级时间戳,超时返回False,不抛异常(与TqTimeoutError区分)。
五、定时任务:TqScheduler
若需 cron 式「每天 14:55 平仓」,可用TqScheduler(见tqsdk.lib)在wait_update循环里注册,避免 sleep 到点。事件驱动与交易时段过滤结合,比 while+sleep 稳。
六、耗时计算怎么办
指标计算放is_changing分支内,或把纯计算移出热路径、缩小data_length。严禁在wait_update循环里做大规模网络 IO;日志写盘应异步或批量。
七、反模式对照表
| 写法 | 问题 | 替代 |
|---|---|---|
sleep(60)等下一分钟 | 堵回报 | is_changing(kline datetime) |
sleep(3)撤单重试 | 错过 FINISHED | 循环wait_update判 status |
requests.post同步告警 | 堵循环 | 队列+独立线程或先写日志 |
while空转无 wait | CPU 100% | wait_update或 deadline |
八、与 Jupyter 的关系
Notebook 里time.sleep同样阻塞内核里唯一的 Api 循环。调试定时逻辑应在.py脚本里用 deadline 验证,再贴回 Notebook;反复运行单元格更要api.close(),避免多实例叠加。
九、静态检查建议
上线前对策略仓库rg "time\.sleep",逐处改成 wait_update 模式。允许保留的例外:进程启动前、与 Api 无关的初始化、独立子进程里的 sleep。
总结
期货量化程序里time.sleep会阻塞天勤唯一的事件线程,导致行情与回报积压、止损延迟、状态跳变。用wait_update(deadline=...)做超时等待,用is_changing等 K 线或 tick,用TqScheduler做定时点,才能在不卡死连接的前提下实现节奏控制。把 sleep 从策略主循环里清干净,是上线前值得做的一次静态检查。
FAQ
1)sleep 0.1 秒可以吗?
仍不推荐高频;偶尔调试可用,生产去掉。
2)多线程能否一个线程 wait_update?
官方模型单 Api 单线程;多线程易竞态。
3)asyncio.sleep 呢?
若在天勤协程外自建 async,需与 Api 生命周期隔离,一般策略不必。
4)回测里 sleep 有害吗?
回测时钟由wait_update推进,sleep 同样拖慢或破坏时序。
本文基于天勤 TqSdk 公开 API 整理,不构成投资建议。
