前言
期货量化里常见需求是一篮子品种同时出信号。若每个合约单独写一套等待逻辑,代码会迅速膨胀,也容易漏掉某个合约的更新。下面用天勤量化 TQSDK 的思路,把批量订阅、统一等待、分合约决策压缩成可维护结构。核心目标只有一个:任何时刻都能说清楚这一轮更新到底处理了哪些合约。对新人而言,先掌握统一循环比先堆策略更重要。
一、设计目标:一次等待、多源更新
核心原则是 api.wait_update 之后,按合约列表遍历自己的状态机。不要把网络等待散落到每个函数深处,否则调试时很难还原事件顺序。建议为每个合约维护一个小字典,里面放 K 线序列、上次处理过的 datetime、以及本地缓存的指标值,避免在循环里反复全量重算。若部分品种需要 tick 级数据,可单独分支,但仍建议挂在同一轮循环末尾处理。
二、最小代码骨架(示例)
下面片段展示多合约 K 线与统一循环的常见写法,品种列表可按配置读取:
fromtqsdkimportTqApi,TqAuth,TqSimimportos symbols=["SHFE.rb2505","DCE.i2505","CZCE.SA505"]api=TqApi(TqSim(),auth=TqAuth(os.environ["TQ_USER"],os.environ["TQ_PASS"]))series={s:api.get_kline_serial(s,60*5,data_length=200)forsinsymbols}whileTrue:api.wait_update()forsinsymbols:k=series[s]ifnotapi.is_changing(k.iloc[-1],"datetime"):continue# 在此处写每个合约的信号与下单逻辑_=k.close.iloc[-1]三、工程上建议补的两层保护
- 空数据保护:新上市或刚订阅时序列可能不足,指标计算前先判断长度。
- 异常合约降级:某个合约长时间无成交时,可临时从交易集合剔除并打日志,而不是让整个循环阻塞。
- 额外建议:给每个合约维护 last_error 计数,连续异常超过阈值时自动暂停该品种并通知运维。
- 性能建议:避免在循环内做磁盘 IO,可把日志批量写入或异步落盘。
四、测试清单(建议保存为表格)
| 场景 | 期望行为 |
|---|---|
| 单合约断线 | 其他合约仍正常处理 |
| 新合约加入 | 冷启动完成后才开始交易 |
| 配置变更 | 下一根 K 线生效 |
五、与订单路由的衔接建议
多合约循环里最容易漏的是“信号合约”和“下单合约”不一致。若存在主连映射或指数映射,建议在循环入口就把可交易合约解析成最终 instrument_id,并在日志里同时打印信号源与下单目标。用天勤量化时,可在每次 set_target_volume 前做一次断言:目标合约必须属于当日允许交易列表。这样即使配置被误改,也能在第一次下单前被拦下,避免跨品种串单这种难排查事故。
总结
多合约开发的难点不在信号,而在事件驱动的组织方式。用字典统一管理序列对象,配合统一等待循环,可读性和可测试性都会好很多。
FAQ
1)合约很多时会不会卡?
瓶颈通常在计算而非等待;可把重计算放到触发条件之后。
2)要不要用多进程?
多数个人与中小团队单进程足够;先 profile 再决定。
3)天勤模拟与实盘的更新节奏一致吗?
大方向一致,仍建议用实盘小样本对照关键路径延迟。
4)如何避免某个合约拖慢整轮?
对每个合约设置独立超时或跳过策略,避免单点阻塞。
5)多账户场景怎么办?
按账户维度再包一层循环,注意别串单。
风险提示
本文用于期货量化技术备忘讨论,不构成投资建议。