3. FIX协议基础:报文结构、标签与值、会话层与应用层、登录与心跳机制
做港股量化交易,FIX协议是绕不开的坎。说实话,我刚入行那会儿,看到FIX报文里一堆数字标签,头都大了。后来踩过几次坑,才慢慢摸清楚它的脾气。
今天咱们就把FIX协议的核心骨架拆开来看。你只要掌握了报文结构、会话层和应用层的区别,以及登录心跳这套机制,港股交易接口的大门就算推开了一半。
3.1 FIX协议报文结构
FIX报文说白了就是一段纯文本。每个字段由「标签=值」组成,字段之间用SOH字符(ASCII码0x01)分隔。嗯,这里要注意,SOH不可见,很多新手调试时以为字段间是空格,结果解析失败。
一个典型的FIX报文长这样:
8=FIX.4.4|9=78|35=D|49=CLIENT1|56=BROKER1|34=2|52=20240325-10:00:00.000|11=ORD001|55=00700.HK|54=1|38=1000|40=2|44=500.00|10=123|
(这里的|代表SOH分隔符,实际报文里看不到)
我习惯把报文拆成三部分来看:
- 头段(Header):标签8、9、35、49、56、34、52等,负责路由和会话控制
- 主体(Body):业务数据,比如下单的股票代码、价格、数量
- 尾段(Trailer):标签10,校验和,用于完整性检查
核心规则:标签3(重复组)和标签10(校验和)必须放在报文最后。我曾经因为顺序搞反,被交易所拒单了三次才反应过来。
3.2 标签与值的体系
FIX协议定义了上千个标签。但港股交易常用的,其实就几十个。我整理了一份核心标签表:
| 标签 | 字段名 | 说明 | 示例值 |
|---|---|---|---|
| 8 | BeginString | 协议版本 | FIX.4.4 |
| 35 | MsgType | 消息类型 | D(新订单) |
| 49 | SenderCompID | 发送方ID | CLIENT1 |
| 56 | TargetCompID | 接收方ID | BROKER1 |
| 55 | Symbol | 股票代码 | 00700.HK |
| 54 | Side | 买卖方向 | 1(买)/2(卖) |
| 38 | OrderQty | 订单数量 | 1000 |
| 44 | Price | 价格 | 500.00 |
| 10 | CheckSum | 校验和 | 123 |
个人经验:标签值尽量用字符串表示,比如Side=1而不是Side='1'。有些老系统对类型检查很严格,我吃过这个亏。
3.3 会话层 vs 应用层
FIX协议分两层:会话层和应用层。说白了,会话层管「怎么传」,应用层管「传什么」。
- 会话层:负责建立连接、登录、心跳、序列号管理、重传等。消息类型包括:A(登录)、0(心跳)、5(登出)、2(重传请求)等。
- 应用层:负责业务数据,比如下单、撤单、成交回报。消息类型包括:D(新订单)、F(订单取消)、8(执行报告)等。
你想想看,会话层就像快递员,他负责把包裹安全送到;应用层就是包裹里的商品。快递员可以换,但商品内容不变。
我画了一张图,帮你理清这两层的关系:
避坑指南:我曾经在开发时,把应用层的订单消息直接塞到会话层里发,结果对方解析失败。记住,每个消息都必须有正确的MsgType(标签35),会话层消息和应用层消息不能混用。
3.4 登录与心跳机制
登录和心跳,是FIX会话层最核心的两个动作。说白了,就是告诉对方「我来了」和「我还活着」。
3.4.1 登录流程
登录消息(MsgType=A)必须包含以下标签:
- 98(EncryptMethod):加密方式,0表示不加密
- 108(HeartBtInt):心跳间隔,单位秒。港股通常设为30秒
- 141(ResetSeqNumFlag):是否重置序列号,Y/N
一个典型的登录请求:
8=FIX.4.4|9=65|35=A|49=CLIENT1|56=BROKER1|34=1|52=20240325-09:30:00.000|98=0|108=30|141=Y|10=200|
如果登录成功,对方会回复一个同样MsgType=A的消息,表示接受。如果失败,会回复一个MsgType=5(登出)并带上错误原因。
个人习惯:登录时我会把ResetSeqNumFlag设为Y,这样每次重连后序列号从1开始,调试起来方便很多。但要注意,生产环境有些券商要求必须设为N,得提前确认。
3.4.2 心跳机制
心跳消息(MsgType=0)很简单,就是一个空的消息体,只包含头段和尾段。它的作用是:
- 检测连接是否还活着
- 防止NAT网关或防火墙超时断开连接
- 同步序列号
心跳间隔由登录时的HeartBtInt决定。比如设为30秒,那么:
- 如果30秒内没有其他消息,就必须发一个心跳
- 如果超过HeartBtInt * 1.2秒没收到任何消息,就认为连接断了
我建议你实现一个心跳定时器,逻辑如下:
# 伪代码示例
last_sent_time = 当前时间
last_received_time = 当前时间
while 连接未断开:
当前时间 = 获取当前时间()
if 当前时间 - last_sent_time >= HeartBtInt:
发送心跳消息(MsgType=0)
last_sent_time = 当前时间
if 当前时间 - last_received_time > HeartBtInt * 1.2:
触发重连逻辑()
break
休眠(1秒)
我曾经踩过的坑:有一次我把心跳间隔设成了60秒,但券商网关的防火墙30秒就会断开空闲连接。结果每59秒断一次,断完重连,重连完又断。后来改成25秒才稳定。所以,心跳间隔一定要比网关的超时时间短,最好留出50%的余量。
3.5 序列号管理
每个FIX会话都有两个序列号:发送序列号(MsgSeqNum,标签34)和接收序列号。序列号从1开始,每发一条消息递增1。
序列号的作用:
- 保证消息顺序
- 检测消息丢失
- 支持重传
如果发现序列号跳号(比如收到34=5,但上次收到的是34=3),说明中间丢了一条消息。这时候需要发送重传请求(MsgType=2),要求对方重发丢失的消息。
核心要点:序列号是FIX会话的命根子。我见过有人把序列号写死成1,结果每次重连都报错。记住,序列号必须严格递增,不能重复,不能跳号(除非重置)。
3.6 小结
FIX协议其实不复杂。你只要记住:
- 报文结构:头段 + 主体 + 尾段,用SOH分隔
- 标签与值:每个字段都是「标签=值」的形式
- 两层分离:会话层管连接,应用层管业务
- 登录心跳:建立连接后,用心跳维持
- 序列号:保证消息不丢不乱
把这些基础打牢了,后面写代码的时候就会顺手很多。下一节咱们会动手实现一个FIX客户端,到时候这些概念都会用上。
公众号:蓝海资料掘金营,微信deep3321