跳转至

12.3 FIX协议

FIX协议的基本构建块是 标签-值对。每个数据字段都由三部分组成:

  1. Tag(标签):一个唯一的整数,代表字段的含义(例如,35 代表 MsgType,44 代表 Price)。
  2. Value(值):字段的实际数据(例如,D 代表 NewOrderSingle,150.25 代表价格)。
  3. 分隔符:一个ASCII字符,通常是 SOH(Start of Header,ASCII值 0x01),在日志和示例中常显示为 | 或 ^。

格式: Tag=Value<SOH>


FIX消息的结构

完整的FIX消息由三部分组成,按顺序排列:

消息头 | 消息体 | 消息尾

8=FIX.4.4|9=122|35=D|34=2|49=BUYSIDE|52=20231027-10:30:00.000|56=SELLSIDE|11=ORD10001|55=AAPL|54=1|38=100|40=2|44=150.25|10=052|

1. 消息头

每条消息都必须以消息头开始。它包含用于消息路由和处理的基础信息。

  • 必填字段:
  • 8=BEGINSTRING: FIX协议的版本(例如,FIX.4.2, FIX.4.4, FIXT.1.1)。
  • 9=BODYLENGTH: 消息体的长度(从 Tag 35 之后开始,到 Tag 10 之前的 SOH 结束)。这个字段对于消息的分帧和验证至关重要。
  • 35=MSGTYPE: 消息类型,决定了消息体的结构和含义。
  • 常见例子:D = NewOrderSingle, 8 = ExecutionReport, 5 = Logout, A = Logon。
  • 其他常用头字段:
  • 34=MSGSEQNUM: 消息序列号,用于检测丢包和保持通信同步。
  • 49=SENDERCOMPID: 发送方标识。
  • 56=TARGETCOMPID: 接收方标识。
  • 52=SENDINGTIME: 消息发送时间戳。

2. 消息体

消息体包含了该特定消息类型的具体业务数据。其内容完全由消息头中的 MsgType (35) 决定。

  • 示例:NewOrderSingle (35=D) 的消息体可能包含:
  • 11=CLORDID: 客户端订单ID。
  • 55=SYMBOL: 交易标的(如股票代码)。
  • 54=SIDE: 买卖方向(1 = Buy, 2 = Sell)。
  • 38=ORDERQTY: 订单数量。
  • 44=PRICE: 价格。
  • 40=ORDTYPE: 订单类型(1 = Market, 2 = Limit)。

3. 消息尾

消息尾主要用于消息的完整性校验。

  • 必填字段:
  • 10=CHECKSUM: 校验和。这是消息中最后一个字段,是所有字节的和对256取模后的值(包括字段之间的SOH分隔符)。
  • 计算方式:将消息中 8= 开始到 SOH 之前的所有字符的ASCII值相加,然后对256取模。
  • 格式:校验和一定是三位数,例如 052,123。
部分 标签-值对 解释
消息头 8=FIX.4.4 使用FIX 4.4版本
9=122 消息体长度为122字节
35=D 这是一个 NewOrderSingle 消息
34=2 消息序列号为2
49=BUYSIDE 发送方是"BUYSIDE"
52=20231027-10:30:00.000 发送时间
56=SELLSIDE 接收方是"SELLSIDE"
消息体 11=ORD10001 唯一的客户端订单ID
55=AAPL 交易苹果公司股票
54=1 买卖方向:买入 (1)
38=100 订单数量:100股
40=2 订单类型:限价单 (2)
44=150.25 限价价格:150.25美元
消息尾 10=052 校验和(用于验证消息在传输过程中没有损坏)

重复组

FIX协议中重要的概念是重复组。它用于表示一个字段的列表或数组。

  • 结构:

  • 一个“领头”字段,指明组内有多少个条目。

  • 每个条目包含一组相同的字段序列。

  • 示例:一条执行报告可能包含多个成交记录。

...|386=2|...|54=1|37=ORDER001|...|54=1|37=ORDER002|...
- `386=2` 表示后面有2个成交记录重复组。
- 第一个组:`54=1|37=ORDER001|...`
- 第二个组:`54=1|37=ORDER002|...`

数据类型的编码

FIX协议中的值遵循特定的编码规则:

  • 字符串: 55=AAPL
  • 整型: 38=100
  • 浮点数: 44=150.25
  • 布尔值: 347=N (N=false, Y=true)
  • 日期/时间: 52=20231027-10:30:00.000 (格式为 YYYYMMDD-HH:MM:SS.sss)
  • 字符: 54=1
#include "circular_array_orderbook.hpp"
#include "quickfix/Application.h"
#include "quickfix/FileLog.h"
#include "quickfix/FileStore.h"
#include "quickfix/MessageCracker.h"
#include "quickfix/SessionSettings.h"
#include "quickfix/SocketInitiator.h"
#include "quickfix/fix44/MarketDataIncrementalRefresh.h"
#include "quickfix/fix44/Message.h"
#include "quickfix/fix44/MessageCracker.h"

using namespace circular_array;
using namespace FIX;

// FIX应用
template <typename OrderBookType>
class MyFIXApplication : public Application, public MessageCracker {
 public:
  explicit MyFIXApplication(OrderBookType& order_book)
      : order_book_(order_book) {}

  void onCreate(const FIX::SessionID&) override {}
  void onLogon(const FIX::SessionID& sessionID) override {}
  void onLogout(const FIX::SessionID& sessionID) override {}
  void toAdmin(FIX::Message&, const FIX::SessionID&) override {}
  void toApp(FIX::Message&, const FIX::SessionID&) override {}

  void fromAdmin(const FIX::Message&, const FIX::SessionID&) override {}
  void fromApp(const FIX::Message& message,
               const FIX::SessionID& session) override {
    crack(message, session);
  }

  // FIX 4.4 行情增量更新消息
  void onMessage(const FIX44::MarketDataIncrementalRefresh& message,
                 const SessionID&) override {
    // 1. 获取行情更新条目数量(NoMDEntries)
    int update_count = message.groupCount(FIX::FIELD::NoMDEntries);
    if (update_count <= 0) {
      std::cerr << "FIX MarketDataIncrementalRefresh: No entries found"
                << std::endl;
      return;
    }

    // 2. 遍历每个行情更新条目
    for (int i = 1; i <= update_count; ++i) {
      FIX44::MarketDataIncrementalRefresh::NoMDEntries md_entry_group;
      message.getGroup(i, md_entry_group);  // 提取单条行情更新

      // 3. 提取核心字段(必须校验存在性,避免崩溃)
      Order new_order;
      bool is_valid = extract_order_fields(md_entry_group, new_order);
      if (!is_valid) {
        std::cerr << "Invalid MD Entry (index: " << i << "), skip" << std::endl;
        continue;
      }

      // 4. 确定订单类型(买/卖):基于MDEntryType
      FIX::MDEntryType md_entry_type;
      md_entry_group.get(md_entry_type);
      bool is_bid = (md_entry_type.getValue() == FIX::MDEntryType_BID);

      // 5. 确定更新动作(新增/修改/删除):基于MDUpdateAction
      FIX::MDUpdateAction md_update_action;
      md_entry_group.get(md_update_action);
      handle_order_update(md_update_action, new_order, is_bid);
    }
  }

 private:
  OrderBookType& order_book_;

  // 提取FIX行情字段到自定义Order对象
  bool extract_order_fields(
      const FIX44::MarketDataIncrementalRefresh::NoMDEntries& md_group,
      Order& out_order) {
    // 3.1 提取订单ID(MDEntryID)
    if (!md_group.isSet(FIX::FIELD::MDEntryID)) {
      std::cerr << "Missing required field: MDEntryID" << std::endl;
      return false;
    }
    FIX::MDEntryID md_entry_id;
    md_group.get(md_entry_id);
    out_order.id = std::stoi(md_entry_id.getValue());

    // 3.2 提取订单价格(MDEntryPx)
    if (!md_group.isSet(FIX::FIELD::MDEntryPx)) {
      std::cerr << "Missing required field: MDEntryPx (OrderID: "
                << out_order.id << ")" << std::endl;
      return false;
    }
    FIX::MDEntryPx md_entry_px;
    md_group.get(md_entry_px);
    out_order.price = md_entry_px.getValue();

    // 3.3 提取订单数量(MDEntrySize)
    if (!md_group.isSet(FIX::FIELD::MDEntrySize)) {
      std::cerr << "Missing required field: MDEntrySize (OrderID: "
                << out_order.id << ")" << std::endl;
      return false;
    }
    FIX::MDEntrySize md_entry_size;
    md_group.get(md_entry_size);
    out_order.quantity = static_cast<int>(md_entry_size.getValue());

    return true;
  }

  // 根据FIX更新动作,调用订单簿对应接口
  void handle_order_update(const FIX::MDUpdateAction& update_action,
                           const Order& order, bool is_bid) {
    switch (update_action.getValue()) {
      case FIX::MDUpdateAction_NEW:
        order_book_.add_order(order, is_bid);
        std::cout << "Add Order: ID=" << order.id << ", Price=" << order.price
                  << ", Qty=" << order.quantity
                  << ", Type=" << (is_bid ? "Bid" : "Offer") << std::endl;
        break;

      case FIX::MDUpdateAction_CHANGE:
        order_book_.update_order(order, is_bid);
        std::cout << "Update Order: ID=" << order.id
                  << ", New Qty=" << order.quantity << std::endl;
        break;

      case FIX::MDUpdateAction_DELETE:
        order_book_.delete_order(order.id, order.price, is_bid);
        std::cout << "Delete Order: ID=" << order.id
                  << ", Price=" << order.price << std::endl;
        break;

      default:
        std::cerr << "Unsupported MDUpdateAction: " << update_action.getValue()
                  << std::endl;
        break;
    }
  }
};

// ------------------------------
// 测试:初始化FIX应用与订单簿
// ------------------------------
int main(int argc, char** argv) {
  if (argc != 2) {
    std::cerr << "Usage: " << argv[0] << " [FIX配置文件路径]" << std::endl;
    return 1;
  }

  try {
    // 1. 初始化FIX会话配置
    SessionSettings settings(argv[1]);

    // 2. 初始化订单簿
    // 基础版订单簿(精度2位小数,深度100个档位)
    BasicLimitOrderBook basic_order_book(2, 100);

    // 3. 初始化FIX应用(绑定订单簿)
    MyFIXApplication<BasicLimitOrderBook> fix_app(basic_order_book);

    // 4. 初始化FIX组件(存储、日志、发起器)
    FileStoreFactory store_factory(settings);
    FileLogFactory log_factory(settings);
    SocketInitiator initiator(fix_app, store_factory, settings, log_factory);

    // 5. 启动FIX连接,开始接收行情
    initiator.start();
    std::cout << "FIX Initiator Started. Press 'q' to quit..." << std::endl;

    // 6. 等待退出信号
    while (std::cin.get() != 'q');

    // 7. 停止FIX连接
    initiator.stop();
    return 0;
  } catch (const std::exception& e) {
    std::cerr << "Main Error: " << e.what() << std::endl;
    return 1;
  }
}

纳斯达克ITCH/OUCH协议(二进制):代码需按固定字节长度解析(如ITCH消息固定长度,无需文本转义),其中ITCH用于接收报价/成交数据(需解析“订单新增-成交-撤销”标识),OUCH用于发送订单(需封装“订单类型-数量-价格”二进制包);