前言
我接触过很多刚写期货程序化的同事,行情能打印出来,一到下单和成交回报就各写各的:有的在循环里直接 insert_order,有的在回调里改全局变量,跑一晚上对不上账。其实不必一开始就搭微服务,关键是把 数据订阅、事件驱动、交易执行、状态核对 四层分清,让同一套骨架能从模拟走到实盘。
天勤 TqSdk 把行情、账户、委托、成交都挂在同一个 TqApi 上,用 wait_update 推进;下面按这个模型说明模块怎么拆、各层职责是什么,并给出最小闭环示例。策略逻辑可以换成你自己的,但组织方式建议先固定。
一、四层结构怎么记
| 层级 | 职责 | 天勤里常见入口 |
|---|---|---|
| 数据层 | 订阅行情、K 线、账户截面 | get_quote、get_kline_serial、get_account、get_position |
| 事件层 | 判断本次更新是否值得处理 | wait_update、is_changing |
| 执行层 | 把信号变成报单或目标仓 | insert_order、TargetPosTask.set_target_volume |
| 核对层 | 委托、成交、持仓是否一致 | get_order、get_trade、position 字段 |
新手常把执行层和核对层混在一起:下单后不跟踪 order.status,成交了也不读 get_trade,只靠"我以为成交了"推进策略,实盘很快失控。
二、数据层:先订再算
创建 TqApi 时选定交易单元(本地调试可用 TqSim,团队模拟常用 TqKq,实盘用 TqAccount)。随后一次性订阅本策略需要的对象:
python
from tqsdk import TqApi, TqAuth, TqSim
api = TqApi(TqSim(), auth=TqAuth("快期账户", "密码"))
symbol = "SHFE.rb2510"
quote = api.get_quote(symbol)
kl = api.get_kline_serial(symbol, 60, data_length=200)
pos = api.get_position(symbol)
acc = api.get_account()
get_kline_serial 的 data_length 要大于指标周期,否则均线等指标前面全是 nan。行情对象在首次 wait_update 之前可能为空,文档也提醒过:刚订阅后 quote.datetime 可能还是空字符串。
三、事件层:用 wait_update 当心跳
wait_update 是天勤策略的主驱动:阻塞等待业务数据更新,同时把之前发出的订阅、报单真正发到网络,并让 TargetPosTask 等后台任务有机会下单。官方说明里写得很清楚------不调 wait_update,很多指令不会真正出去。
python
while True:
api.wait_update()
# 下面只在"关心的字段变了"时进逻辑
主循环里避免长时间 sleep、大段同步计算或阻塞 IO;这些都会推迟下一次 wait_update,行情和回报都会变慢。
四、执行层:手写报单 vs 目标持仓
入门理解链路 用 insert_order:方向、开平、限价一目了然,适合学拒单和挂单规则。
信号驱动调仓 用 TargetPosTask:策略只维护"目标净仓几手",拆单、撤单改价由 task 在后续每次 wait_update 时处理。注意文档约束:set_target_volume 之后必须继续 wait_update;同一合约不要和 insert_order 混用。
python
from tqsdk.lib import TargetPosTask
task = TargetPosTask(api, symbol)
while True:
api.wait_update()
if api.is_changing(kl.iloc[-1], "datetime"):
# 此处算信号,示例:目标多头 1 手
task.set_target_volume(1)
五、核对层:成交回报从哪来
天勤没有单独的"回调函数列表",回报体现在数据截面更新上:
- 委托:
get_order()或指定order_id,看status、volume_left等 - 成交:
get_trade() - 持仓:
get_position(symbol)的pos_long、pos_short
在 wait_update 返回后,用 is_changing 过滤订单、成交、持仓字段,再写结构化日志。模拟盘还可对照 TqSim 的 trade_log 做二次校验。
六、模块落地到文件时的建议
单文件 demo 可以全写在一个 while True 里;策略变复杂后建议拆成:
config:合约、周期、账户构造data:订阅与指标计算(纯函数,不直接下单)signal:输入 K 线/行情,输出目标方向或目标仓execution:只负责TargetPosTask或insert_orderreconcile:每根 K 线或每分钟对 order/trade/position 做一次快照
这样换模拟、换实盘时,通常只改 TqApi 构造行和环境变量,信号与执行接口不变。
总结
Python 期货自动交易可按 数据订阅 → wait_update 事件驱动 → 执行(insert_order 或 TargetPosTask)→ get_order/get_trade/position 核对 四层组织。天勤把行情、账户、委托、成交放在同一 TqApi 截面里,避免自己维护多套 CTP 回调状态机。
落地时先订合约与 K 线,主循环里短逻辑 + 字段级 is_changing;执行层选手写报单或目标持仓二选一;核对层用订单、成交、持仓字段写日志,模拟盘可对照 trade_log。策略变复杂后再拆 config、signal、execution、reconcile 模块,换环境只改账户构造。
FAQ
1)必须先有行情才能下单吗?
实盘和模拟都依赖合约信息(最小变动价位、交易状态等)。应先 wait_update 收到有效 quote 再报单,否则容易拒单或价格无效。
2)TargetPosTask 和 insert_order 能一起用吗?
官方明确不建议同一合约同时使用,否则 task 与手写报单会冲突。
3)多合约怎么组织?
每个合约一个 TargetPosTask 实例(同一账户同一合约单例);信号层为每个 symbol 维护独立目标仓,不要共用一个全局"方向变量"。
4)回报要不要单独线程收?
天勤模型下用 wait_update 统一收包即可;再开线程抢 CTP 回调容易和 API 内部状态打架。
风险提示
本文讨论程序结构与技术实现,不构成投资建议。实盘前请在模拟环境充分验证。