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. 蛮荒时代(1.0-2.7):只有应用层,没有会话层。说白了,就是“发出去就不管了”。
  2. 标准化时代(4.0-4.4):加入了会话层,有了登录、心跳、序列号管理。这才是真正可用的工业级协议。
  3. 现代化时代(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) ④ Heartbeat (35=0) ⑤ Logout (35=5) ⑥ Logout Ack (35=5) 图:FIX会话生命周期(简化版)

你看,整个过程其实挺清晰的:

  1. 客户端先发一个 Logon 消息(35=A),带上用户名、密码、心跳间隔等。
  2. 服务端验证通过后,回复一个 Logon Ack(也是35=A,但内容不同)。
  3. 之后双方定期互发 Heartbeat(35=0),告诉对方“我还活着”。
  4. 要断开时,一方发 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会话层的细节,包括序列号管理、重连机制、以及那些让人头疼的“重复组”。

专注资料整理