4、交易接口协议:FIX协议详解(会话层、应用层)、国内CTP接口协议、上交所/深交所交易接口差异

做量化交易,说白了就是跟交易所「对话」。但交易所不是人,它只认特定的「语言」和「规矩」。今天我们就来聊聊这些规矩——也就是交易接口协议。

我个人习惯把接口协议分成两类:国际通用的FIX协议,和咱们国内特色的CTP、沪深交易所接口。它们各有各的脾气,搞不清楚的话,你的交易系统可能连门都进不去。

4.1 FIX协议:国际通用的「普通话」

FIX(Financial Information eXchange)协议,是国际金融圈用得最广的协议。我最早接触它是在做美股交易系统的时候,当时觉得这玩意儿真啰嗦,后来才发现它设计得其实很巧妙。

4.1.1 会话层:怎么「握手」和「告别」

FIX协议分两层:会话层和应用层。会话层管的是「怎么建立连接、怎么维持心跳、怎么断开」。你想想看,两个系统之间要传几万笔订单,总得有个规矩吧?

会话层的核心是「登录-心跳-登出」三步走:

  • 登录(Logon):客户端发一个35=A的消息,带上用户名、密码、目标ID。服务端回一个35=A表示同意。
  • 心跳(Heartbeat):每隔一段时间(比如30秒)发一个0号消息。如果连续几次没收到,就认为连接断了。
  • 登出(Logout):发35=5的消息,礼貌地说再见。

嗯,这里要注意:心跳间隔不能太短,否则网络压力大;也不能太长,否则断线了都不知道。我建议设成30秒,这是行业惯例。

关键字段速查表:

标签号字段名说明
35MsgType消息类型(0=心跳,5=登出,8=执行报告,D=新订单)
49SenderCompID发送方ID
56TargetCompID接收方ID
34MsgSeqNum消息序列号(必须连续递增)
52SendingTime发送时间

4.1.2 应用层:订单怎么「说」

应用层管的是业务逻辑——下单、撤单、查成交。说白了,就是告诉交易所「我要买100股茅台,价格1800」。

一个典型的FIX新订单消息长这样:

8=FIX.4.2|9=78|35=D|49=CLIENT1|56=EXCHANGE|34=3|52=20250315-10:30:00.000|
11=ORD12345|21=1|55=600519|54=1|38=100|44=1800.00|40=2|59=1|10=123|

我来拆解一下:

  • 35=D:新订单消息
  • 11=ORD12345:客户端订单ID(必须唯一)
  • 55=600519:股票代码(贵州茅台)
  • 54=1:买卖方向(1=买,2=卖)
  • 38=100:数量
  • 44=1800.00:价格
  • 40=2:订单类型(2=限价单)
  • 59=1:有效期(1=当日有效)

我曾经在项目中遇到过一个问题:序列号(34号字段)没有严格递增,结果被交易所直接拒掉了。FIX协议对序列号要求很严,你发完第3条消息,下一条必须是4,不能跳号。

避坑指南:我曾经因为序列号重置问题,导致整个交易链路中断。后来我养成了一个习惯——每次登录成功后,把序列号从1开始重新计数,并且用本地文件持久化存储。这样即使程序重启,序列号也不会乱。

4.2 国内CTP接口协议:期货市场的「方言」

CTP(Comprehensive Transaction Platform)是上海期货信息技术有限公司开发的,国内期货市场几乎都在用。它跟FIX不一样,用的是二进制协议,效率更高。

CTP的核心特点:

  • 二进制编码:比FIX的文本格式更紧凑,解析速度更快
  • 请求-响应模式:每个请求都有对应的响应,不像FIX那样异步
  • 强类型定义:字段类型固定,比如价格用double,数量用int

CTP的接口调用方式很直接。比如下单:

CThostFtdcInputOrderField order;
memset(&order, 0, sizeof(order));
strcpy(order.BrokerID, "9999");
strcpy(order.InvestorID, "100001");
strcpy(order.InstrumentID, "rb2505");
order.Direction = THOST_FTDC_D_Buy;
order.CombOffsetFlag[0] = THOST_FTDC_OF_Open;
order.LimitPrice = 3500.0;
order.VolumeTotalOriginal = 10;
order.OrderPriceType = THOST_FTDC_OPT_LimitPrice;

// 调用下单接口
int ret = pTradeApi->ReqOrderInsert(&order, ++nRequestID);

你看,CTP的接口设计得很「C++风格」——先填充结构体,再调用API。我个人觉得这种方式比FIX更直观,但灵活性差一些。比如你想加一个自定义字段,CTP就不支持。

注意:CTP的API是线程不安全的。我曾经在项目中同时开了两个线程调用ReqOrderInsert,结果导致程序崩溃。后来我加了一个互斥锁,才解决了问题。记住:所有API调用必须在同一个线程里,或者加锁保护。

4.3 上交所/深交所交易接口差异

国内股票市场,上交所和深交所的接口协议也不一样。虽然它们都基于TCP,但细节上差别很大。

我整理了一个对比表,方便你快速了解:

对比项上交所(STEP)深交所(SZSE)
协议基础基于FIX 4.4,叫STEP协议自定义二进制协议
消息格式文本格式,类似FIX二进制格式,更紧凑
登录方式FIX标准的Logon消息专有的登录请求包
订单类型支持FIX标准订单类型支持限价、市价、FOK等
行情推送独立的行情通道交易和行情共用通道
会话管理严格的心跳机制相对宽松

具体来说,上交所的STEP协议跟FIX很像,但做了一些本地化改造。比如它增加了「交易所代码」字段,用来区分上海和深圳。而深交所的协议更「原生态」,直接用二进制包,解析起来稍微麻烦一点。

举个例子,上交所的订单消息:

8=FIX.4.4|9=120|35=D|49=001|56=SSE|34=5|52=20250315-10:30:00|
11=CLT001|55=600000|54=1|38=1000|44=10.50|40=2|59=1|453=1|448=001|447=D|452=1|10=234|

深交所的订单消息(二进制格式,用十六进制表示):

02 00 00 00  // 消息长度
01 00        // 消息类型(新订单)
00 01        // 会话ID
00 00 00 05  // 序列号
00 00 00 01  // 客户端ID
00 00 00 64  // 股票代码(100=浦发银行)
00 01        // 买卖方向(买)
00 00 03 E8  // 数量(1000)
00 00 00 0A  // 价格(10.0,放大100倍)

你看,深交所的协议更「硬核」,每个字段都是固定长度、固定位置。解析的时候不能像FIX那样用「标签=值」的方式,得按字节偏移量来读。

我的建议:如果你同时对接上交所和深交所,最好封装一个统一的接口层。把STEP和二进制协议都转换成内部通用的数据结构。这样上层业务代码就不用关心底层协议差异了。我在做量化平台时就是这么干的,省了不少事。

4.4 协议选择与实战建议

说了这么多,到底该选哪个协议?

  • 做国际市场:FIX是标配,几乎所有海外券商都支持
  • 做国内期货:CTP是主流,效率高,但学习曲线陡
  • 做国内股票:上交所用STEP,深交所用二进制协议,都得会

我个人习惯是:先搞清楚交易所支持什么协议,再决定技术方案。不要为了炫技而强行用FIX,也不要因为CTP效率高就盲目选它。适合的才是最好的。

最后分享一个经验:不管用哪个协议,一定要做好日志记录。我曾经因为日志没打全,排查一个订单丢失问题花了整整两天。后来我养成了习惯——每个发送的消息都记录原始报文,每个接收的消息也记录。这样出了问题,直接翻日志就能定位。

避坑指南:我曾经在对接深交所时,因为字节序(大端/小端)搞反了,导致解析出来的价格全是错的。后来我写了一个单元测试,用已知的报文数据做验证,才发现了问题。记住:二进制协议一定要做端到端的测试,别信文档。

交易接口协议知识体系 交易接口协议 FIX协议 会话层 应用层 登录/心跳/登出 序列号管理 新订单(35=D) 执行报告(35=8) CTP协议(期货) 交易API 行情API ReqOrderInsert ReqOrderAction SubscribeMarketData OnRtnDepthMarketData 沪深交易所 上交所(STEP) 深交所(二进制) 基于FIX 4.4 文本格式 自定义二进制 固定长度字段 选择建议:国际用FIX,期货用CTP,股票看交易所 公众号:蓝海资料掘金营,微信deep3321

这张图把今天讲的内容串起来了。你可以看到,FIX、CTP、沪深交易所各有各的定位和特点。做量化交易系统,这些协议都得懂一点,但不用全部精通。根据你的业务场景,选一个主攻方向就行。