11. 实盘交易接口:CTP接口对接、期货公司API调用、订单管理与成交反馈

好,咱们终于聊到实盘了。

前面做了那么多策略回测、信号生成、风险控制,最终都得落到一个地方——把单子发出去,并且知道它成交了没有。这一步,就是实盘交易接口要干的事。

我个人习惯把这块叫做「最后一公里」。策略再牛,接口对接不好,一切都是白搭。我在早期做套利策略时,就吃过这个亏——策略信号出来了,结果CTP接口报了个错,单子没发出去,眼睁睁看着价差溜走。嗯,那种感觉,不想再体验第二次。

11.1 CTP接口:国内期货的标配

CTP,全称是综合交易平台,上期技术开发的。说白了,它就是国内期货市场的事实标准。几乎所有的期货公司都支持CTP,你只要对接好它,就等于打通了大部分期货公司的通道。

CTP接口的核心是两个部分:

  • 交易接口(Trader API):负责登录、下单、撤单、查询持仓等
  • 行情接口(Market API):负责接收实时行情数据

做基差交易,我们主要用的是交易接口。行情数据一般从其他数据源拿,或者直接用CTP的行情接口也行,看个人习惯。

核心要点:CTP是C++写的,但Python开发者不用慌。有现成的Python封装库,比如vnpyctp-python,直接pip安装就能用。

11.2 期货公司API调用:配置与连接

每家期货公司都会给你一套CTP的接入参数。我建议你拿到后,先核对这几个东西:

参数 说明 示例
交易前置地址 交易服务器的地址和端口 tcp://180.168.146.187:10100
行情前置地址 行情服务器的地址和端口 tcp://180.168.146.187:10110
BrokerID 期货公司代码 9999
UserID 你的交易账号 123456
Password 交易密码 ******

连接流程其实不复杂,就三步:

  1. 创建交易API实例
  2. 注册回调函数(处理登录、报单回报等)
  3. 调用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 整体架构:一张图看懂

说了这么多,咱们用一张图把整个流程串起来。下面是我自己画的一个实盘交易接口的架构图:

策略信号生成 订单管理 状态机 · 重试 · 风控 CTP Trader API 期货公司交易服务器 交易所 成交反馈处理 持仓更新 基差计算 · 风险监控 日志 · 监控 · 报警 图例说明: 策略层:生成交易信号 订单管理层:管理订单生命周期 CTP接口层:与期货公司通信 成交反馈:处理成交回报 持仓更新:更新持仓数据 监控层:日志与报警

这张图里,数据流是双向的。策略发单往下走,成交反馈往上走。中间订单管理层负责状态跟踪和异常处理。我个人觉得,这个架构比较清晰,适合做基差交易的实盘部署。

11.6 避坑指南

最后,分享几个我踩过的坑:

  • 重连机制:CTP连接可能会断,尤其是网络波动时。一定要实现自动重连,并且重连后要重新查询持仓和订单状态。
  • 订单编号冲突:CTP的OrderRef是客户端生成的,如果多个策略实例共用同一个OrderRef,会导致订单混乱。我建议用「策略ID + 时间戳」的方式生成唯一编号。
  • 成交回报丢失:极少数情况下,CTP可能漏推成交回报。所以,我习惯在收盘后做一次「对账」,把本地记录的成交和期货公司结算单比对一下。

一句话总结:CTP接口对接不难,难的是把异常情况处理好。把重连、超时、重复回报这些边缘情况都覆盖到,你的实盘系统才算真正能用。

公众号:蓝海资料掘金营,微信deep3321