前言
国内期货量化程序跑起来后,结构往往是 while True 里反复调用行情接口等待更新------在天勤 TqSdk 里就是 api.wait_update()。这是正常的:程序要一直活着等交易所和期货公司的业务数据 ,并不是 while 本身有问题。读者若从股票日线回测转过来,容易以为"循环越少越好";在期货 tick 和分钟线场景里,循环可以很勤,但每次循环里干什么决定了 CPU 会不会被打满。
常见现象是:策略逻辑没错,任务管理器里 Python 却长期占满一个核,风扇狂转,日志刷屏,可交易次数并没增加。原因之一是:每一帧行情更新(盘口跳一下、K 线 high/low 变一下、持仓浮盈变一下)都触发整段均线、多合约循环甚至写盘,属于算得太勤 ,而不是天勤"推送太密"。另一类原因是循环里 sleep、同步请求、大段 pandas------那会导致行情滞后 ,和 CPU 高是不同病,本文重点讲前者,并说明如何用天勤的 is_changing 做字段级过滤。
下文默认你已有一个能连上模拟盘的 TqApi,并订了至少一个期货合约的 quote 或 K 线;不默认你熟悉 is_changing 的参数写法。
一、先分清:CPU 高和"收不到行情"不是一回事
| 现象 | 常见原因 | 读者可观察到的表现 |
|---|---|---|
| CPU 占用高 | 每帧全量计算、对象级 is_changing | quote 时间在走,机器发烫 |
| 行情滞后 | sleep、网络 IO、循环体过长 | quote.datetime 明显落后 |
天勤每次 wait_update() 收到业务包后,会更新内存里的行情、K 线、委托等对象。若你在每一次更新后都跑完整策略,而期货盘口一秒内可能多次变动,CPU 就会被无意义的重复计算吃掉。
二、对象级 is_changing 为什么容易"空转"
is_changing(obj) 不指定字段时,表示该对象任意字段本帧有更新即为 True。对 K 线 DataFrame 的一行来说,open/high/low/close/volume 任一变化都会成立;对 quote 来说,买卖盘、最新价变动都会成立。
错误示范(逻辑上能跑,但极耗 CPU):
python
while True:
api.wait_update()
if api.is_changing(kl): # kl 整表任一列变都可能 True
heavy_compute() # 内含多均线、多合约、打印
在螺纹钢活跃时段,这相当于把"每秒上百次 tick 更新"都当成"要重算策略",CPU 飙高是必然结果。
三、字段级过滤:只在你关心的业务变化时算策略
天勤允许 is_changing(obj, "字段名") 或传入字段列表。对按 K 线收盘做决策的期货策略 ,应盯住最后一根 K 线上的 datetime 字段------该字段来自行情服务写入 K 线表,表示这根 bar 的时间标签;当新的一根 K 线生成 时,最后一行的 datetime 会变化,通常意味着上一根已收盘,此时才适合用 iloc[-2] 算信号(详见专题文)。
python
kl = api.get_kline_serial("SHFE.rb2510", 300, data_length=200) # 300 秒=5 分钟线
while True:
api.wait_update()
if not api.is_changing(kl.iloc[-1], "datetime"):
continue
heavy_compute_on_bar_close()
对依赖最新价 的短线规则,可改为 is_changing(quote, "last_price"),并自行加节流(例如同一秒内只处理一次),避免与 K 线策略混用两套触发条件。
持仓核对不必每 tick 打印,可仅在 is_changing(pos, "pos_long") 或 pos_short 变化时记录,这与期货实盘里"成交回报到了才改仓位"的节奏一致。
四、计算与 IO 仍要外移
即使过滤正确,若 heavy_compute 里仍做全样本滚动回归、写大 CSV、同步调 webhook,单次循环耗时过长仍会拖慢 wait_update 调用频率,间接造成回报延迟。建议:指标在 bar 收盘算一次;日志批量写;通知进队列由别的线程发送,主循环只入队。
五、和 TargetPosTask 的叠加效应
国内期货程序化里,天勤 TargetPosTask 负责把净持仓调到目标手数,内部可能在 wait_update 时撤单、按最新对价重挂。行情剧烈时 task 工作频率也会上升。若同一帧你还做大规模 pandas 运算,CPU 会叠加。策略层应把"算信号"严格放在 bar 或仓位变化触发上,而不是每个 tick。
总结
期货量化里 while True + wait_update 是常态;CPU 过高多半是因为在过多的行情帧上跑了完整策略 。天勤提供 is_changing 的字段参数:K 线策略请用最后一根行的 datetime (由 get_kline_serial 返回的表提供)判断新 bar;盘口策略再选 quote 的具体字段;持仓变动用 position 的具体字段。
把"每一帧"变成"业务上需要反应的那一帧",CPU 会降到与策略频率匹配的水平(例如 5 分钟策略大约几分钟才算一次)。若你同时遇到行情变慢,再排查 sleep 和阻塞 IO,两类问题分开处理。
FAQ
1)wait_update 本身占 CPU 吗?
等待网络时线程阻塞,一般不应占满 CPU;占满核多半是循环体内的 Python 计算。
2)多个期货合约怎么过滤?
每个合约一条 kl,分别 is_changing(kl[s].iloc[-1], "datetime"),未变的合约 continue。
3)回测也很慢,能用同样办法吗?
可以,历史回放同样会产生大量帧,过滤后回测速度往往明显提升。
4)不用 is_changing,改用 sleep(1) 行吗?
会阻塞行情与发单,国内期货程序化不推荐用 sleep 代替 bar 推进。
风险提示
本文讨论程序性能与结构,不构成投资建议。