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消息在网络上传输时,会话层字段与应用层字段被拼接在同一条消息中,但职责边界清晰。会话层字段(如BeginString、MsgSeqNum、SenderCompID、TargetCompID)由FIX引擎自动维护,应用层字段(如ClOrdID、Symbol、Side、Price)由业务代码填充。
下面这张图展示了FIX消息从应用层业务对象到网络字节流的装配关系,以及会话层在其中的包裹位置:
图中可见,一条完整的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)必须放在消息末尾,且为三位数字,不足三位前补零。
- 字段顺序在标准中并非完全自由,头部和尾部字段有固定位置要求,消息体字段顺序通常不强制,但部分对手方会校验。
四、常用字段与消息类型速查
订单路由场景中,以下字段和消息类型出现频率最高。建议将其作为日常开发的对照清单:
| 标签 | 字段名 | 含义 | 典型值示例 |
|---|---|---|---|
| 35 | MsgType | 消息类型 | D=新订单, 8=执行报告, F=撤销请求 |
| 11 | ClOrdID | 客户端订单ID | ORD20250115-001 |
| 55 | Symbol | 标的代码 | AAPL, 600519 |
| 54 | Side | 买卖方向 | 1=买, 2=卖 |
| 38 | OrderQty | 订单数量 | 100 |
| 40 | OrdType | 订单类型 | 1=市价, 2=限价 |
| 44 | Price | 限价 | 185.50 |
| 150 | ExecType | 执行类型 | 0=新订单确认, 2=成交, 8=拒绝 |
| 39 | OrdStatus | 订单状态 | 0=新订单, 1=部分成交, 2=全部成交, 8=拒绝 |
| 14 | CumQty | 累计成交数量 | 100 |
| 6 | AvgPx | 成交均价 | 185.50 |
| 37 | OrderID | 交易所订单ID | EX-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”。此时开发者应优先检查应用层字段是否符合对手方规则,而非怀疑会话层连接。