跳转至

预序列化订单

预序列化订单指在非热路径(合约加载、会话就绪、配置变更时)预先构造好发往交易所/柜台的 wire 字节模板(FIX tag-value、FIXP/SBE 等二进制帧、或自有 UDP/TCP 载荷),在真正下单的热路径上仅原地改写每次必变的字段(如 ClOrdID、价格、数量、时间戳、校验和/签名占位等),从而避免在决策→发送路径上做全量字符串格式化、重复遍历标签或频繁分配。业界二进制协议侧,FIX Simple Binary Encoding (SBE) 强调定长/按偏移访问、适合低延迟编解码(参见其 Introduction 文档)。开源侧如 fixpp 等面向延迟敏感场景的 FIX 生成库,也常见「复用报文对象、只更新变化字段」的用法说明。

系统设计要点:

  1. 分层模板:可拆成「会话/序号头」+「业务体」;若序号必须由网关递增,可在网关出口做最后 patch,策略侧只维护不含序号或含占位符的模板。
  2. 字段契约:明确哪些字段每次必变、哪些长期固定;固定字段用编译期常量或启动时写入一次。
  3. 正确性:预序列化不能跳过风控与状态机校验。
  4. 与协议一致:文本 FIX 需注意 SOH 分隔与 body length;二进制协议优先按生成代码/SBE schema 的偏移写入。

示例(示意:固定前缀 + 热路径只改数量与递增序号;真实项目应替换为具体 tag 布局或 SBE 生成结构体):

#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <span>
#include <string_view>

// 演示:预分配 wire 缓冲,热路径仅 patch 少量字段(非完整 FIX,仅说明模式)
struct PreSerializedNewOrder {
  static constexpr std::size_t kCap = 512;
  std::array<std::byte, kCap> buf{};
  std::size_t len{0};
  // 假定 quantity 在模板中的字节偏移与宽度(应由代码生成或单测锁定)
  std::size_t qty_off{0};
  std::uint32_t qty_width{8};

  // 冷路径:构造模板(示例写入占位符 "QTY=00000000|")
  void build_template_cold_path() {
    std::string_view prefix = "HEAD|SYM=IF2501|SIDE=1|TYPE=2|QTY=";
    std::string_view suffix = "|PX=PLACEHOLDER|\n";
    len = 0;
    auto append = [&](std::string_view s) {
      std::memcpy(buf.data() + len, s.data(), s.size());
      len += s.size();
    };
    append(prefix);
    qty_off = len;
    append("00000000");
    qty_width = 8;
    append(suffix);
  }

  // 热路径:仅覆写数量 ASCII(生产环境可改为二进制定点数写入)
  void patch_quantity_hot_path(std::uint32_t qty) noexcept {
    // 简化:假定 qty < 10^8;真实应使用固定宽度补零 + 边界检查
    char tmp[9];
    for (int i = 7; i >= 0; --i) {
      tmp[i] = static_cast<char>('0' + (qty % 10));
      qty /= 10;
    }
    tmp[8] = '\0';
    std::memcpy(reinterpret_cast<char*>(buf.data()) + qty_off, tmp, qty_width);
  }

  [[nodiscard]] std::span<const std::byte> wire() const noexcept {
    return {buf.data(), len};
  }
};

进阶:类模板特化 + 标签分发(多交易所一套代码路径) 下面用空标签类型(如 SHFETag、CFFEXTag)作模板参数,对 PreserializedOrder<> 做全特化:冷路径在构造函数里预填各所固定字段与字节序,热路径只通过缓存指针 update_*;通用发送逻辑里用 if constexpr 按所补全「订单号 / 时间戳」等差异。 说明:下列 SHFEProtocol / CFFEXProtocol 为示例布局,与真实柜台二进制报文、API 字段顺序及字节序无关。

设计要点

  1. 标签分发(tag dispatching):用空类作非类型语义标签,把「哪一家交易所」编码进类型系统。
  2. 类模板特化:主模板只声明不定义,强迫为每个所实现完整特化,协议泄漏在编译单元内完成。
  3. if constexpr:在 simulate_send_order 等通用函数里按 ExchangeTag 展开不同侧车逻辑,未选中分支不参与实例化。

完整代码示例

#include <arpa/inet.h>
#include <endian.h>

#include <algorithm>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <string_view>
#include <type_traits>

// ========== 第一步:示意协议布局(#pragma pack(1),与真实接口无关)==========
#pragma pack(push, 1)

struct SHFEProtocol {
  uint16_t msg_type;
  uint16_t body_len;
  uint32_t local_order_id;
  uint64_t account_id;
  uint8_t direction;
  uint64_t price;
  uint64_t qty;
};

struct CFFEXProtocol {
  uint32_t magic;
  uint64_t order_ref;
  char contract[8];
  uint8_t price_type;
  uint64_t price;
  uint32_t qty;
};

#pragma pack(pop)

// ========== 第二步:主模板仅声明 ==========
template <typename ExchangeTag>
class PreserializedOrder;

// ========== 第三步:按交易所全特化 ==========
class SHFETag {};
template <>
class PreserializedOrder<SHFETag> {
 public:
  explicit PreserializedOrder(uint64_t account_id, uint8_t direction) noexcept
      : local_order_id_(0) {
    std::memset(buffer_, 0, sizeof(buffer_));
    auto* order = reinterpret_cast<SHFEProtocol*>(buffer_);
    order->msg_type = htons(0x0201);
    order->body_len = htons(static_cast<uint16_t>(sizeof(SHFEProtocol)));
    order->account_id = htobe64(account_id);
    order->direction = direction;
    price_ptr_ = &order->price;
    qty_ptr_ = &order->qty;
    id_ptr_ = &order->local_order_id;
  }

  void update_price(uint64_t price) noexcept { *price_ptr_ = htobe64(price); }
  void update_qty(uint64_t qty) noexcept { *qty_ptr_ = htobe64(qty); }
  void next_id() noexcept {
    ++local_order_id_;
    *id_ptr_ = htonl(local_order_id_);
  }

  const void* data() const noexcept { return buffer_; }
  static constexpr size_t size() noexcept { return sizeof(SHFEProtocol); }
  static constexpr std::string_view name() noexcept { return "上期所 (SHFE)"; }

 private:
  alignas(64) uint8_t buffer_[sizeof(SHFEProtocol)];
  uint64_t* price_ptr_;
  uint64_t* qty_ptr_;
  uint32_t* id_ptr_;
  uint32_t local_order_id_;
};

class CFFEXTag {};
template <>
class PreserializedOrder<CFFEXTag> {
 public:
  explicit PreserializedOrder(std::string_view contract) noexcept
      : order_ref_(0) {
    std::memset(buffer_, 0, sizeof(buffer_));
    auto* order = reinterpret_cast<CFFEXProtocol*>(buffer_);
    order->magic = htonl(0x43464658);
    const size_t n = (std::min)(contract.size(), sizeof(order->contract) - 1u);
    std::memcpy(order->contract, contract.data(), n);
    order->price_type = 0x01;
    price_ptr_ = &order->price;
    qty_ptr_ = &order->qty;
    ref_ptr_ = &order->order_ref;
  }

  void update_price(uint64_t price) noexcept { *price_ptr_ = htobe64(price); }
  void update_qty(uint32_t qty) noexcept { *qty_ptr_ = htonl(qty); }
  void next_ref() noexcept {
    ++order_ref_;
    *ref_ptr_ = htobe64(order_ref_);
  }

  const void* data() const noexcept { return buffer_; }
  static constexpr size_t size() noexcept { return sizeof(CFFEXProtocol); }
  static constexpr std::string_view name() noexcept { return "中金所 (CFFEX)"; }

 private:
  alignas(64) uint8_t buffer_[sizeof(CFFEXProtocol)];
  uint64_t* price_ptr_;
  uint32_t* qty_ptr_;
  uint64_t* ref_ptr_;
  uint64_t order_ref_;
};

// ========== 第四步:通用发送流程==========
template <typename ExchangeTag>
void simulate_send_order(PreserializedOrder<ExchangeTag>& order, uint64_t price,
                         uint64_t qty) noexcept {
  order.update_price(price);
  if constexpr (std::is_same_v<ExchangeTag, CFFEXTag>) {
    order.update_qty(static_cast<uint32_t>(qty));
  } else {
    order.update_qty(qty);
  }

  if constexpr (std::is_same_v<ExchangeTag, SHFETag>) {
    order.next_id();
  } else if constexpr (std::is_same_v<ExchangeTag, CFFEXTag>) {
    order.next_ref();
  }

  std::cout << "[" << order.name() << "] 预序列化订单生成完成 | "
            << "Size: " << order.size() << " bytes | " << "Price: " << price
            << " | Qty: " << qty << std::endl;
}

// ========== 第五步:使用示例 ==========
int main() {
  std::cout << "=== 多交易所预序列化订单 ===\n\n";

  PreserializedOrder<SHFETag> shfe_order(88888888, 0);
  simulate_send_order(shfe_order, 123450, 10);

  PreserializedOrder<CFFEXTag> cffex_order("IF2406");
  simulate_send_order(cffex_order, 4500000000ULL, 5);

  return 0;
}