预序列化订单¶
预序列化订单指在非热路径(合约加载、会话就绪、配置变更时)预先构造好发往交易所/柜台的 wire 字节模板(FIX tag-value、FIXP/SBE 等二进制帧、或自有 UDP/TCP 载荷),在真正下单的热路径上仅原地改写每次必变的字段(如 ClOrdID、价格、数量、时间戳、校验和/签名占位等),从而避免在决策→发送路径上做全量字符串格式化、重复遍历标签或频繁分配。业界二进制协议侧,FIX Simple Binary Encoding (SBE) 强调定长/按偏移访问、适合低延迟编解码(参见其 Introduction 文档)。开源侧如 fixpp 等面向延迟敏感场景的 FIX 生成库,也常见「复用报文对象、只更新变化字段」的用法说明。
系统设计要点:
- 分层模板:可拆成「会话/序号头」+「业务体」;若序号必须由网关递增,可在网关出口做最后 patch,策略侧只维护不含序号或含占位符的模板。
- 字段契约:明确哪些字段每次必变、哪些长期固定;固定字段用编译期常量或启动时写入一次。
- 正确性:预序列化不能跳过风控与状态机校验。
- 与协议一致:文本 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 字段顺序及字节序无关。
设计要点
- 标签分发(tag dispatching):用空类作非类型语义标签,把「哪一家交易所」编码进类型系统。
- 类模板特化:主模板只声明不定义,强迫为每个所实现完整特化,协议泄漏在编译单元内完成。
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;
}