2. FIX协议基础

在订单路由与成交回报的实战开发中,FIX协议是贯穿始终的通信骨架。理解它的分层结构、编码规则和消息语义,是后续排查“订单为何被拒”“回报为何延迟”“序列号为何错乱”等问题的前提。本节从协议演进讲起,逐层拆解FIX的会话层与应用层,并给出常用字段与消息类型的速查清单。

一、FIX协议的发展脉络

FIX(Financial Information eXchange)诞生于1992年,最初由富达投资与所罗门兄弟为解决券商与买方之间电话下单效率低、差错率高的问题而共同设计。1998年FIX协议规范4.0版本发布后,逐步从美国股票市场扩展到全球多资产类别。此后FIX Protocol Limited(现为FIX Trading Community)接管标准维护,陆续推出4.1、4.2、4.3、4.4、5.0及FIXT等版本。

需要特别区分两个概念:FIX会话协议与FIX应用协议。前者负责连接建立、心跳、序列号同步与消息恢复;后者定义订单、成交、撤销等业务语义。FIX 5.0之后,两者被拆分为独立的传输层规范(FIXT)与应用层规范(FIX),这使得同一套业务消息可以运行在不同传输机制之上。

实战中大量交易所与券商仍在使用FIX 4.2或4.4,因为其字段定义稳定、对接文档成熟。新项目选型时需先确认对手方支持的版本,而非盲目追求5.0。

二、会话层与应用层的结构关系

FIX消息在网络上传输时,会话层字段与应用层字段被拼接在同一条消息中,但职责边界清晰。会话层字段(如BeginString、MsgSeqNum、SenderCompID、TargetCompID)由FIX引擎自动维护,应用层字段(如ClOrdID、Symbol、Side、Price)由业务代码填充。

下面这张图展示了FIX消息从应用层业务对象到网络字节流的装配关系,以及会话层在其中的包裹位置:

FIX消息装配结构:会话层包裹应用层 会话层头部(Session Header) 8=BeginString | 9=BodyLength | 35=MsgType | 49=SenderCompID | 56=TargetCompID | 34=MsgSeqNum | 52=SendingTime 应用层消息体(Application Body) 11=ClOrdID | 55=Symbol | 54=Side | 38=OrderQty | 40=OrdType | 44=Price 60=TransactTime | 21=HandlInst | 59=TimeInForce | 1=Account 业务语义由应用层字段决定,会话层不解释其含义 会话层尾部(Session Trailer) 10=CheckSum(三位数字,对整条消息字节求和后取模256) 网络字节流:8=FIX.4.4|9=112|35=D|49=BROKER|56=EXCHANGE|34=215|52=20250115-09:30:00.123|11=ORD001|55=AAPL|54=1|38=100|40=2|44=185.50|60=20250115-09:30:00.123|10=128|

图中可见,一条完整的FIX消息由三部分组成:头部、消息体、尾部。CheckSum是对从BeginString开始到CheckSum之前所有字节的校验,任何字段顺序或分隔符错误都会导致校验失败。

三、标签-值编码规则与消息格式

FIX采用“标签=值”的键值对编码,字段之间用SOH字符(ASCII 0x01)分隔。在日志和文档中,SOH通常被替换为竖线“|”以便阅读。每个标签对应一个唯一字段,例如35代表MsgType,11代表ClOrdID。

8=FIX.4.4|9=112|35=D|49=BROKER|56=EXCHANGE|34=215|
52=20250115-09:30:00.123|11=ORD001|55=AAPL|54=1|
38=100|40=2|44=185.50|60=20250115-09:30:00.123|10=128|

编码规则中有几个必须遵守的约束:

  • BodyLength(9)必须精确等于从35字段开始到10字段之前(不含10)的字节数,计算错误会导致对方直接断连。
  • MsgSeqNum(34)在会话内严格递增,双方各自维护发送序列号和接收序列号,出现缺口时触发ResendRequest。
  • CheckSum(10)必须放在消息末尾,且为三位数字,不足三位前补零。
  • 字段顺序在标准中并非完全自由,头部和尾部字段有固定位置要求,消息体字段顺序通常不强制,但部分对手方会校验。
常见误区:开发者手工拼接消息时忘记更新BodyLength或CheckSum,导致对方返回“Invalid message”或直接断开。建议始终使用成熟的FIX引擎(如QuickFIX、OnixS、CoralFIX)生成消息,而非手工拼接。

四、常用字段与消息类型速查

订单路由场景中,以下字段和消息类型出现频率最高。建议将其作为日常开发的对照清单:

标签 字段名 含义 典型值示例
35MsgType消息类型D=新订单, 8=执行报告, F=撤销请求
11ClOrdID客户端订单IDORD20250115-001
55Symbol标的代码AAPL, 600519
54Side买卖方向1=买, 2=卖
38OrderQty订单数量100
40OrdType订单类型1=市价, 2=限价
44Price限价185.50
150ExecType执行类型0=新订单确认, 2=成交, 8=拒绝
39OrdStatus订单状态0=新订单, 1=部分成交, 2=全部成交, 8=拒绝
14CumQty累计成交数量100
6AvgPx成交均价185.50
37OrderID交易所订单IDEX-998877

消息类型方面,订单路由链路通常涉及:NewOrderSingle(35=D)发送新订单,ExecutionReport(35=8)接收成交回报,OrderCancelRequest(35=F)发起撤单,OrderCancelReject(35=9)接收撤单拒绝。会话层则使用Logon(35=A)、Logout(35=5)、Heartbeat(35=0)、TestRequest(35=1)和ResendRequest(35=2)维持连接健康。

五、场景复盘:一条订单的完整生命周期

假设客户端向交易所发送买入100股AAPL的限价单,价格185.50。客户端生成ClOrdID=ORD001,填充35=D、54=1、38=100、40=2、44=185.50,会话层自动附加34=215、49=BROKER、56=EXCHANGE。交易所收到后返回ExecutionReport,其中150=0、39=0表示订单已被接受;随后成交发生时再推送150=2、39=2、14=100、6=185.50的成交回报。

若订单被拒绝,回报中150=8、39=8,并附带58=Text字段说明原因,如“Insufficient buying power”。此时开发者应优先检查应用层字段是否符合对手方规则,而非怀疑会话层连接。

复盘要点:将会话层问题(序列号、心跳、校验和)与应用层问题(字段缺失、值非法、业务规则拒绝)分开定位,能显著缩短排障时间。日志中建议同时保留原始SOH格式和可读格式两份记录。