11. 实盘交易接口:CTP接口对接、期货公司API调用、订单管理与成交反馈
好,咱们终于聊到实盘了。
前面做了那么多策略回测、信号生成、风险控制,最终都得落到一个地方——把单子发出去,并且知道它成交了没有。这一步,就是实盘交易接口要干的事。
我个人习惯把这块叫做「最后一公里」。策略再牛,接口对接不好,一切都是白搭。我在早期做套利策略时,就吃过这个亏——策略信号出来了,结果CTP接口报了个错,单子没发出去,眼睁睁看着价差溜走。嗯,那种感觉,不想再体验第二次。
11.1 CTP接口:国内期货的标配
CTP,全称是综合交易平台,上期技术开发的。说白了,它就是国内期货市场的事实标准。几乎所有的期货公司都支持CTP,你只要对接好它,就等于打通了大部分期货公司的通道。
CTP接口的核心是两个部分:
- 交易接口(Trader API):负责登录、下单、撤单、查询持仓等
- 行情接口(Market API):负责接收实时行情数据
做基差交易,我们主要用的是交易接口。行情数据一般从其他数据源拿,或者直接用CTP的行情接口也行,看个人习惯。
核心要点:CTP是C++写的,但Python开发者不用慌。有现成的Python封装库,比如vnpy、ctp-python,直接pip安装就能用。
11.2 期货公司API调用:配置与连接
每家期货公司都会给你一套CTP的接入参数。我建议你拿到后,先核对这几个东西:
| 参数 | 说明 | 示例 |
|---|---|---|
| 交易前置地址 | 交易服务器的地址和端口 | tcp://180.168.146.187:10100 |
| 行情前置地址 | 行情服务器的地址和端口 | tcp://180.168.146.187:10110 |
| BrokerID | 期货公司代码 | 9999 |
| UserID | 你的交易账号 | 123456 |
| Password | 交易密码 | ****** |
连接流程其实不复杂,就三步:
- 创建交易API实例
- 注册回调函数(处理登录、报单回报等)
- 调用
Init()方法,开始连接
我曾经遇到过一个坑:期货公司的前置地址有多个,有的支持IPv6,有的只支持IPv4。如果你用错了,连接会一直超时。所以,拿到地址后,先ping一下,确认网络通不通。
小技巧:建议把前置地址写在配置文件里,不要硬编码。换期货公司时,改个配置文件就行,不用改代码。
11.3 订单管理:从发单到成交
订单管理是实盘交易的核心。你想想看,一个订单从发出去到最终成交,中间要经历好几个状态:
- 已报:订单已发送到交易所,等待处理
- 已撤:订单被撤销
- 部分成交:订单的一部分已经成交
- 全部成交:订单完全成交
- 废单:订单因某种原因被拒绝
我个人习惯在代码里维护一个订单状态机。每个订单都有一个唯一的OrderRef,通过它来跟踪状态变化。
下面是一个简单的订单管理类示例:
class OrderManager:
def __init__(self):
self.orders = {} # key: OrderRef, value: order info
def on_order(self, order_data):
"""处理订单回报"""
order_ref = order_data['OrderRef']
status = order_data['OrderStatus']
if order_ref not in self.orders:
self.orders[order_ref] = order_data
self.orders[order_ref]['Status'] = status
if status == '全部成交':
print(f"订单 {order_ref} 已全部成交")
# 触发后续逻辑,比如更新持仓
elif status == '废单':
print(f"订单 {order_ref} 被拒绝,原因:{order_data['ErrorMsg']}")
# 触发报警或重试
注意:CTP的回调是在独立线程里执行的。不要在回调里做耗时操作,比如写数据库、发网络请求。否则会阻塞行情接收,甚至导致断线。我建议用队列把数据抛出去,让主线程慢慢处理。
11.4 成交反馈:别漏掉任何一笔
成交反馈,说白了就是告诉你「你的单子成交了多少,在什么价格成交的」。CTP通过OnRtnTrade回调来推送成交信息。
成交数据里最重要的几个字段:
- InstrumentID:合约代码
- Direction:买卖方向
- Volume:成交数量
- Price:成交价格
- TradeID:成交编号,唯一标识
做基差交易时,我特别关注成交价格。因为套利策略对价格敏感,如果成交价和预期价差偏差太大,这笔交易可能就不划算了。
我曾经遇到过一个情况:策略发了一个买入IF主力合约的订单,预期成交价是4800点,结果因为市场流动性不足,实际成交价是4805点。价差瞬间扩大了5个点,套利空间直接没了。所以,成交反馈一定要实时监控,一旦发现成交价偏离预期,立即调整策略。
实战建议:在成交反馈里加一个「滑点监控」模块。每次成交后,计算实际成交价与发单时的最新价的差值。如果滑点超过阈值,记录日志并报警。这样你能及时发现市场流动性问题。
11.5 整体架构:一张图看懂
说了这么多,咱们用一张图把整个流程串起来。下面是我自己画的一个实盘交易接口的架构图:
这张图里,数据流是双向的。策略发单往下走,成交反馈往上走。中间订单管理层负责状态跟踪和异常处理。我个人觉得,这个架构比较清晰,适合做基差交易的实盘部署。
11.6 避坑指南
最后,分享几个我踩过的坑:
- 重连机制:CTP连接可能会断,尤其是网络波动时。一定要实现自动重连,并且重连后要重新查询持仓和订单状态。
- 订单编号冲突:CTP的OrderRef是客户端生成的,如果多个策略实例共用同一个OrderRef,会导致订单混乱。我建议用「策略ID + 时间戳」的方式生成唯一编号。
- 成交回报丢失:极少数情况下,CTP可能漏推成交回报。所以,我习惯在收盘后做一次「对账」,把本地记录的成交和期货公司结算单比对一下。
一句话总结:CTP接口对接不难,难的是把异常情况处理好。把重连、超时、重复回报这些边缘情况都覆盖到,你的实盘系统才算真正能用。