直接用 CTP 做期货自动交易太乱:天勤式状态管理思路
前言
自己对接 CTP 时,OnRtnOrder、OnRtnTrade、OnRspQryInvestorPosition 交错到达,稍不注意就会出现重复开仓、撤单状态对不上、重启后仓位失忆。很多同事并不是不懂 CTP,而是缺少一个统一的业务截面,被迫在回调里维护多套布尔变量。
天勤TqSdk的做法是把行情、委托、成交、持仓合并成可查询的数据对象,用wait_update推进,用is_changing判断本次更新涉及哪些字段——本质是把 CTP 事件流折叠成同步可读的状态机。下面说明这种思路与直接 CTP 的差异,以及何时仍要下沉到 CTP。
一、直接 CTP 为什么容易乱
| CTP 特点 | 开发负担 |
|---|---|
| 异步回调 | 每个回调里都要考虑重入与顺序 |
| 查询与推送并存 | 同一持仓可能先推送后查询,合并逻辑复杂 |
| 断线重连 | 需重订阅、重查、与本地缓存对齐 |
| 多合约 | 每个 Instrument 一条状态线 |
常见反模式:在 OnRtnTrade 里改全局position,在 OnRspQry 里又改一次,策略线程还在读第三个副本。
二、天勤的核心抽象:截面 + 更新帧
可以把它理解成:
- 截面:当前时刻所有 quote、order、trade、position 的快照
- 更新帧:每次
wait_update收到一批 diff,合并进截面 - 变化检测:
is_changing(obj, key)回答“这一帧里该字段变没变”
策略写法从“监听回调改状态”变成“每帧读截面做决策”:
api.wait_update()ifapi.is_changing(quote,"last_price"):...ifapi.is_changing(pos,"pos_long"):...wait_update文档说明:还会驱动TargetPosTask等后台任务发单,把“报单状态机”从策略里拆出去。
三、执行层:TargetPosTask 分担委托状态机
手写 CTP 时,自己要维护:报单 → 部分成交 → 撤单 → 再报。TargetPosTask在内部根据行情更新价格并撤单重挂(文档有说明),策略侧只维护目标净仓。
约束(务必遵守):
- 同一合约不要与
insert_order混用 set_target_volume后要继续wait_update- 每账户每 symbol 单例,参数构造一次定好
这对“只想表达多空意图”的策略,比全手写 CTP 状态机清晰得多。
四、与 CTP 的关系:天勤不是替代交易所
天勤仍然连接柜台与行情服务,CTP 细节被封装在底层。你得到的是:
- 统一的 Python 对象模型
- 回测
TqBacktest与TqSim/TqKq/TqAccount切换 - 更少的回调嵌套
若要做极定制的报单算法(如复杂拆单、席位级优化),可能仍需了解 CTP 限制,或在 task 之外扩展——但要评估是否真有必要。
五、迁移路径:从 CTP 思维到天勤思维
| CTP 习惯 | 天勤对应 |
|---|---|
| OnRtnOrder | get_order+is_changing(order, "status") |
| OnRtnTrade | get_trade+is_changing |
| 持仓查询 | get_position(symbol) |
| 行情 tick | get_quote+is_changing(quote, "last_price") |
| 策略主循环 | while True: wait_update() |
建议新策略直接用天勤写完模拟再考虑是否下沉 CTP;已有 CTP 策略可先把信号层保持不变,交易层用天勤重做一小段对照成交。
六、仍要注意的边界
- 延迟:多一层封装,极端高频可能不满足,需实测
- 规则透明:拒单原因仍要会查 order 状态与日志
- 进程模型:单 API 单线程
wait_update,与 CTP 多线程回调模型不同 - 合规与权限:实盘
TqAccount开通条件以官方说明为准
七、清晰开发流程(推荐)
- 用
get_kline_serial订数据,K 线策略用datetime触发 - 信号输出
target_volume字典(多合约 per symbol) TargetPosTask执行,主循环只wait_update- 用
get_order/get_trade/position做收盘核对 - 环境用
TqSim→TqKq→TqAccount渐进切换
状态持久化(进程重启恢复目标仓与指标缓存)可配合团队已有的策略状态持久化方案,避免重启后重复开仓。
总结
CTP 直连的复杂度主要在异步回调与多源状态合并。天勤用wait_update推进统一截面,用is_changing做帧级判断,用TargetPosTask承接调仓状态机,策略只关心目标持仓与信号,所以天勤已经适合绝大多数 Python 期货程序化场景;极定制执行仍需评估性能与规则。从 CTP 迁移时,把回报处理改成“每帧读 get_order/get_trade/position”,往往比继续堆回调变量更稳。
FAQ
1)天勤还能用 CTP 账户吗?
实盘通过TqAccount等对接期货公司,底层仍是柜台链路,具体以开通说明为准。
2)回调式策略能否与天勤混用?
不建议双驱动;选一种模型,避免两套状态。
3)回测状态机与实盘一致吗?
同一套wait_update逻辑,执行假设仍有差异,需模拟验证。
4)多账户?
天勤支持多账户模式,task 与 get_order 需指定account参数。
风险提示
本文讨论软件开发思路,不构成投资建议。
