4. FIX协议入门:从一封“加密电报”说起
大家好,我是你们的老朋友。今天咱们聊聊FIX协议。
说实话,我刚入行那会儿,第一次看到FIX消息,脑子里就一个念头——这玩意儿怎么长得跟电报似的?一堆数字、等号、竖线,密密麻麻。后来我才明白,这恰恰是它的精髓:简单、高效、机器友好。
4.1 FIX协议发展史:华尔街的“通用语言”
FIX的全称是 Financial Information eXchange,金融信息交换协议。它诞生于1992年,由几家大投行(比如所罗门兄弟、高盛)牵头搞出来的。
为什么?因为当时各家交易系统之间通信,用的都是私有协议。你想想看,A银行和B银行要交易,得先拉专线、写适配器、来回测试……累不累?
FIX的出现,就是为了解决这个“巴别塔困境”。它定义了一套标准化的消息格式,让所有参与者都能用同一种“语言”对话。
关键时间节点:
- 1992年:FIX 1.0 草案发布,仅支持股票交易。
- 1995年:FIX 2.7 成为行业标准,开始被广泛采用。
- 1998年:FIX 4.0 发布,引入了会话层概念,支持登录、心跳、重连。
- 2001年:FIX 4.2 发布,支持固定收益、衍生品,功能大幅扩展。
- 2005年:FIX 4.4 发布,至今仍是使用最广泛的版本。
- 2010年后:FIX 5.0、FIXT 1.1 等新版本出现,但核心思想没变。
我个人习惯把FIX的发展史分成三个阶段:
- 蛮荒时代(1.0-2.7):只有应用层,没有会话层。说白了,就是“发出去就不管了”。
- 标准化时代(4.0-4.4):加入了会话层,有了登录、心跳、序列号管理。这才是真正可用的工业级协议。
- 现代化时代(5.0+):支持XML、FAST压缩,但底层还是那套Tag=Value。
我的经验: 虽然FIX 5.0已经发布多年,但我在项目中遇到的大多数交易所、券商,用的还是FIX 4.2或4.4。别问为什么,问就是“稳定压倒一切”。
4.2 FIX消息结构:Tag=Value,简单到极致
FIX消息的结构,说白了就是三个字:键值对。
每个字段由三部分组成:Tag=Value,然后用 \x01(SOH字符,不可见)分隔。比如:
8=FIX.4.2\x019=78\x0135=D\x0149=INVESTOR\x0156=BROKER\x0134=2\x0152=20250315-10:30:00\x0111=12345\x0121=1\x0155=IBM\x0154=1\x0138=100\x0140=2\x0144=150.00\x0110=123\x01
看着乱?别急,我拆给你看:
| Tag | 字段名 | 值 | 说明 |
|---|---|---|---|
| 8 | BeginString | FIX.4.2 | 协议版本号 |
| 9 | BodyLength | 78 | 消息体长度(从Tag 35开始算) |
| 35 | MsgType | D | 消息类型:D=新订单 |
| 49 | SenderCompID | INVESTOR | 发送方ID |
| 56 | TargetCompID | BROKER | 接收方ID |
| 34 | MsgSeqNum | 2 | 消息序列号 |
| 52 | SendingTime | 20250315-10:30:00 | 发送时间 |
| 11 | ClOrdID | 12345 | 客户端订单ID |
| 55 | Symbol | IBM | 股票代码 |
| 54 | Side | 1 | 买卖方向:1=买 |
| 38 | OrderQty | 100 | 数量 |
| 40 | OrdType | 2 | 订单类型:2=限价单 |
| 44 | Price | 150.00 | 价格 |
| 10 | CheckSum | 123 | 校验和(必须放在最后) |
看到没?整个消息就是一堆 Tag=Value 的拼接。解析起来也简单:按 \x01 分割,再按 = 分割,就得到了键值对。
注意: Tag 9(BodyLength)和 Tag 10(CheckSum)是强制字段,而且有严格的顺序要求。Tag 9 必须在 Tag 35 之后,Tag 10 必须在消息末尾。我曾经见过一个新手,把 Tag 10 放在了中间,结果对方系统直接拒收——嗯,那场面挺尴尬的。
4.3 FIX会话层基础:登录、心跳、说再见
FIX的会话层,说白了就是管理“连接的生命周期”。它定义了三个核心动作:
- 登录(Logon):建立会话,交换认证信息。
- 心跳(Heartbeat):保持连接活跃,检测断线。
- 登出(Logout):优雅地关闭会话。
咱们用一张图来理解:
你看,整个过程其实挺清晰的:
- 客户端先发一个 Logon 消息(35=A),带上用户名、密码、心跳间隔等。
- 服务端验证通过后,回复一个 Logon Ack(也是35=A,但内容不同)。
- 之后双方定期互发 Heartbeat(35=0),告诉对方“我还活着”。
- 要断开时,一方发 Logout(35=5),另一方回复 Logout Ack。
这里有个关键点:序列号(MsgSeqNum)。每次发送消息,序列号都要递增。如果收到一个序列号比预期小的消息,说明消息重复了;如果比预期大,说明中间丢包了。FIX协议通过这种方式实现了可靠传输。
避坑指南: 我曾经遇到过一个生产问题,客户端和服务端的心跳间隔设置不一致。客户端设了30秒,服务端设了60秒。结果客户端30秒没收到心跳,就认为连接断了,开始重连。服务端那边呢?觉得一切正常。最后两边都乱了套。所以,心跳间隔一定要协商一致,通常写在 Logon 消息的 Tag 108 里。
4.4 一个简单的FIX消息解析示例
光说不练假把式。咱们用Python写个简单的解析器,看看FIX消息到底长啥样:
def parse_fix_message(raw_message):
"""
解析FIX消息,返回字典
"""
# 按SOH字符分割
fields = raw_message.split('\x01')
result = {}
for field in fields:
if not field:
continue
# 按等号分割
tag, value = field.split('=', 1)
result[int(tag)] = value
return result
# 示例消息
msg = "8=FIX.4.2\x019=78\x0135=D\x0149=INVESTOR\x0156=BROKER\x0134=2\x0152=20250315-10:30:00\x0111=12345\x0121=1\x0155=IBM\x0154=1\x0138=100\x0140=2\x0144=150.00\x0110=123\x01"
parsed = parse_fix_message(msg)
for tag, value in parsed.items():
print(f"Tag {tag:3d} = {value}")
输出结果:
Tag 8 = FIX.4.2
Tag 9 = 78
Tag 35 = D
Tag 49 = INVESTOR
Tag 56 = BROKER
Tag 34 = 2
Tag 52 = 20250315-10:30:00
Tag 11 = 12345
Tag 21 = 1
Tag 55 = IBM
Tag 54 = 1
Tag 38 = 100
Tag 40 = 2
Tag 44 = 150.00
Tag 10 = 123
你看,就这么简单。当然,生产环境中的解析器要复杂得多,还要处理重复组、校验和验证、序列号检查等等。但核心思想不变——Tag=Value。
总结一下:
- FIX协议诞生于90年代,目的是统一金融消息格式。
- 消息结构就是
Tag=Value,用SOH字符分隔。 - 会话层负责登录、心跳、登出,保证可靠通信。
- 序列号是FIX的灵魂,用来检测丢包和重复。
好了,这一章就到这里。FIX协议看起来简单,但实际用起来坑不少。下一章咱们聊聊FIX会话层的细节,包括序列号管理、重连机制、以及那些让人头疼的“重复组”。