第3章:FIX协议消息类型——管理类与应用类消息详解

聊完了会话层的基础机制,咱们得直面FIX协议里最核心的东西——消息类型。说白了,你连消息类型都搞不清楚,那对接交易所就是一句空话。

我个人习惯把FIX消息分成两大类:管理类消息应用类消息。管理类消息负责“能不能连、连得稳不稳”,应用类消息负责“买卖什么、成交了没”。这两者缺一不可。

3.1 管理类消息:会话的“心跳”与“握手”

管理类消息,你可以理解为交易所和你的交易系统之间的“握手”和“心跳”。没有它们,你的订单根本发不出去。

3.1.1 Logon (35=A) —— 登录认证

这是所有会话的第一步。你的客户端向交易所发送Logon消息,带上用户名、密码、心跳间隔等参数。交易所验证通过后,也会回复一个Logon消息。

关键字段:

标签字段名说明
98EncryptMethod加密方式,0=无加密,1=PKCS,2=DES等
108HeartBtInt心跳间隔(秒),通常设为30或60
553Username登录用户名
554Password登录密码

避坑指南:我曾经遇到过一家交易所,Logon消息里必须带上一个自定义的Tag 10000作为扩展认证。文档里没写,结果我调试了整整一天。所以对接前,一定问清楚对方有没有私有字段。

3.1.2 Heartbeat (35=0) —— 保活机制

登录成功后,双方需要定期发送Heartbeat来确认连接还在。如果一方在超时时间内没收到任何消息(包括Heartbeat),就会认为连接断了。

心跳间隔由Logon消息里的HeartBtInt决定。比如设为30秒,那么每30秒发一次Heartbeat。如果期间有其他消息(比如订单、成交回报)发送,那Heartbeat可以顺延——因为对方知道你还在。

我的经验:我个人习惯把心跳间隔设为30秒,超时倍数设为3倍(即90秒)。太短了浪费带宽,太长了断连后恢复慢。

3.1.3 TestRequest (35=1) —— 主动探测

当你怀疑对方可能掉线了,或者你有一段时间没收到任何消息,可以主动发一个TestRequest。对方收到后必须回复一个Heartbeat,且Heartbeat里的TestReqID字段要和TestRequest里的保持一致。

为什么会用到这个?我举个例子:有一次生产环境里,交易所的行情推送停了,但TCP连接没断。我的系统傻等着,直到超时才发现。后来我加了个定时器,如果30秒内没收到任何消息,就主动发TestRequest。这样能提前发现问题。

3.1.4 ResendRequest (35=2) —— 消息重传

这是FIX协议里最强大的机制之一。当你的系统发现消息序列号不连续时(比如收到了MsgSeqNum=100,但上次收到的是98),说明中间丢了一条消息。这时候你需要发ResendRequest,告诉对方从哪个序列号开始重发。

关键字段:

  • 7 (BeginSeqNo):起始序列号
  • 16 (EndSeqNo):结束序列号,填0表示一直重发到最新

注意:ResendRequest不能乱发。我曾经见过一个新手,因为网络抖动导致序列号不连续,他直接发了ResendRequest请求重发所有消息。结果交易所把当天几万条成交回报全发过来了,他的系统直接OOM。正确的做法是:只请求缺失的那一段。

3.1.5 Logout (35=5) —— 正常登出

当你准备断开连接时,先发Logout消息。交易所收到后会回复一个Logout,然后双方关闭TCP连接。不要直接关socket,那样不礼貌,而且交易所可能会认为你是异常断连,影响你的信用分。

3.2 应用类消息:交易的核心

管理类消息搞定了,咱们才能谈应用类消息。应用类消息才是真正干活的——下单、撤单、查成交。

3.2.1 NewOrderSingle (35=D) —— 新订单

这是最常用的消息,没有之一。你发一个NewOrderSingle,告诉交易所:我要买/卖什么品种、多少数量、什么价格。

核心字段一览:

标签字段名说明
11ClOrdID客户端订单ID,必须唯一
54Side买卖方向:1=买,2=卖
38OrderQty订单数量
44Price价格(限价单必填)
40OrdType订单类型:1=市价,2=限价,3=止损等
55Symbol合约代码

避坑指南:ClOrdID必须保证唯一。我曾经用时间戳+随机数生成,结果在高并发下重复了,交易所直接拒绝了我的订单。后来我改用UUID,再也没出过问题。

3.2.2 ExecutionReport (35=8) —— 成交回报

你下了单,交易所怎么告诉你结果?就是通过ExecutionReport。这条消息包含了订单的当前状态、成交数量、剩余数量、成交价格等。

关键状态字段:

  • 39 (OrdStatus):订单状态。0=新订单,1=部分成交,2=全部成交,4=已撤销,8=已拒绝
  • 14 (CumQty):累计成交数量
  • 151 (LeavesQty):剩余未成交数量
  • 6 (AvgPx):成交均价

你想想看,一个订单从提交到最终成交,可能会收到多条ExecutionReport。比如:先收到一条状态为0(新订单)的确认,然后收到一条状态为1(部分成交),最后收到一条状态为2(全部成交)。你的系统必须能正确处理这种多回报的情况。

3.2.3 其他常用应用类消息

除了NewOrderSingle和ExecutionReport,还有几个你一定会用到的:

  • OrderCancelRequest (35=F):撤单请求。带上原来的ClOrdID,告诉交易所我要撤销这个订单。
  • OrderCancelReplaceRequest (35=G):改单请求。修改订单的价格或数量,相当于先撤单再下新单。
  • OrderStatusRequest (35=H):查询订单状态。当你怀疑订单状态不一致时,可以主动查询。
  • MarketDataRequest (35=V):行情订阅请求。如果你需要实时行情,用这个订阅。

3.3 消息类型关系图

下面这张图展示了管理类消息和应用类消息在会话生命周期中的位置:

FIX协议消息类型关系图 管理类消息 应用类消息 Logon (35=A) - 登录认证 Heartbeat (35=0) - 保活 TestRequest (35=1) - 探测 ResendRequest (35=2) - 重传 Logout (35=5) - 登出 NewOrderSingle (35=D) - 新订单 ExecutionReport (35=8) - 成交回报 OrderCancelRequest (35=F) - 撤单 OrderCancelReplace (35=G) - 改单 MarketDataRequest (35=V) - 行情 会话建立后 会话维持中 会话生命周期:Logon → (Heartbeat/TestRequest/ResendRequest) → Logout

3.4 消息处理的实战要点

讲完了理论,咱们聊聊实战中我踩过的坑:

  1. 消息顺序很重要:FIX协议要求消息按序列号严格递增。如果你发的消息序列号乱了,交易所会直接断开连接。我建议在发送端做一个序列号自增检查。
  2. 心跳超时处理:不要等到超时了才处理。我习惯在超时时间的80%时发TestRequest,如果对方没回复,再准备重连。
  3. ExecutionReport的幂等性:同一个订单可能会收到多条相同的成交回报(比如网络重传)。你的系统必须能去重,否则资金计算会出错。
  4. ClOrdID的生成策略:我推荐用“时间戳+递增序号+机器ID”的组合,既保证唯一性,又方便排查问题。

我的建议:刚开始对接时,先用模拟环境把所有消息类型都跑一遍。特别是ResendRequest和TestRequest,很多人在生产环境里才第一次遇到,那时候手忙脚乱就晚了。

嗯,这一章的内容就到这里。管理类消息是会话的骨架,应用类消息是交易的血肉。两者配合好了,你的系统才能稳定运行。


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