05 | 基于现代C++完整实现一个低延迟交易系统 📅 2026/7/1 17:30:42 上一章我们开始了C低延迟开发的实践构建了一些基础模块这些模块将在本书后续内容中使用。现在我们准备开始设计电子交易生态系统这将是本书后续部分的主要项目通过这个项目我们将在实践中学习低延迟应用开发的原则。首先我们将讨论为端到端电子交易生态系统构建的不同低延迟组件或应用的高级设计和架构。我们还将设计抽象概念、组件以及它们之间的交互方式这些内容我们会在本书后续部分实现。在本章中我们将涵盖以下主题了解电子交易生态系统的布局设计交易平台中的C撮合引擎了解交易平台如何向参与者发布信息构建市场参与者与交易平台的接口设计低延迟C交易算法的框架让我们从描述本书后续部分将设计和构建的电子交易生态系统的高级拓扑结构开始本章内容。在下一节中我们将简要介绍不同的组件然后在本章的其余部分进行更详细的讨论。需要记住的是我们在本书中构建的电子交易生态系统是实际应用的简化版本。它不仅是简化版而且只是构建和运行完整电子交易生态系统实际所需组件的一个子集。我们选择在本书中构建这些组件是因为它们对延迟最为敏感我们希望专注于低延迟应用开发。需要提到的是在实际应用中你还会看到诸如在交易平台和客户端进行历史数据捕获、与清算经纪商建立连接、用于交易处理的后端系统、交易平台和交易客户端的会计与对账、回测框架根据历史数据进行测试等许多其他组件。理解电子交易生态系统的布局首先我们给出本书后续部分将构建的电子交易生态系统的高级布局。在深入探讨细节之前我们先声明这是对电子交易市场实际情况的简化设计。为了将内容限制在本书的覆盖范围内简化是必要的不过它仍然准确地简化呈现了实际应用中的情况。另一点需要注意的是这里的目标是理解低延迟应用的设计和实现所以希望大家更多地关注我们应用的C和计算机科学原理而不是交易生态系统本身的细节。现在让我们通过定义和解释电子交易生态系统的整体拓扑结构以及所涉及的组件来开始这个介绍。定义电子交易生态系统的拓扑结构我们先通过下面的图表来鸟瞰整个系统图5.1 简单电子交易生态系统的拓扑结构如上图所示主要组件根据其属于交易平台端还是交易客户端/市场参与者端进行了大致划分。以下是交易平台组件电子交易平台的撮合引擎交易平台的订单网关服务器以及协议编码器和解码器交易平台的市场数据编码器和发布器以下是交易客户端组件对市场数据感兴趣的市场参与者的市场数据消费者和解码器市场参与者系统中的订单网关编码器和解码器客户端参与者系统中的交易引擎我们将在下一节快速介绍这些组件中的每一个然后在本章的其余部分详细讨论它们。介绍电子交易生态系统的组件在这里我们将快速介绍构成电子交易生态系统的不同组件。需要记住的是在竞争激烈的生态系统中每个组件的设计都必须使其能够以尽可能低的延迟处理事件和数据。还要注意的是在市场波动加剧期间这些系统必须能够跟上市场活动的大幅波动并做出反应。介绍市场数据发布器交易平台的市场数据发布器负责将撮合引擎维护的限价订单簿的每一个变化告知市场参与者。与订单网关相比这里的区别在于市场数据发布器发布的是面向所有参与者的公共数据并且通常会隐藏每个订单属于哪个参与者的详细信息以保证公平性。另一个区别是订单网关基础设施仅将订单更新信息传达给那些订单受到影响的市场参与者而不是所有市场参与者。市场数据发布器可以使用TCP或UDP来发布市场数据但考虑到市场数据更新的大量性UDP多播是首选的网络层协议。市场数据发布器还负责在发布更新之前将撮合引擎的内部格式转换为市场数据格式。介绍撮合引擎电子交易平台的撮合引擎是交易平台最关键的部分。它负责处理市场参与者的订单请求并更新其维护的限价订单簿。当客户想要添加新订单、替换现有订单、取消现有订单等操作时就会生成这些请求。限价订单簿是所有参与者发送的所有订单的集合汇总到一个包含买盘买入订单和卖盘卖出订单的中央账簿中。撮合引擎还负责对价格交叉的订单进行匹配即当买入价格高于或等于卖出价格时将买入订单与卖出订单进行匹配。在特殊的市场状态下如开盘前即将开盘时、集合竞价/开盘开盘的那一刻、开盘前不可取消可以下单但不能取消订单等规则会略有不同但为了专注于低延迟应用开发我们不会关注这些规则也不会实现它们。介绍交易平台的订单网关服务器交易平台的订单网关服务器负责接受市场参与者的连接以便他们可以发送订单请求并在各自的订单有更新时收到通知。订单网关服务器还负责在撮合引擎格式和订单网关消息协议之间进行消息转换。订单网关服务器使用的网络协议始终是TCP以确保消息按顺序传递和可靠性。介绍市场参与者层面的市场数据消费者市场数据消费者是交易平台市场数据发布器组件在市场参与者端的对应部分。这个组件负责订阅市场数据发布器设置的UDP流或TCP服务器接收市场数据更新并将市场数据协议解码为交易引擎其他部分使用的内部格式。介绍订单网关编码器和解码器客户端订单网关客户端组件是交易平台订单网关服务器在市场参与者端的对应部分。这个组件的职责是与交易平台的订单网关基础设施建立并维护TCP连接。它还负责将策略订单请求编码为正确的交易平台订单消息协议并将交易平台的响应解码为交易引擎使用的内部格式。介绍市场参与者系统中的交易引擎交易引擎是市场参与者交易系统的核心。这里是智能决策的所在也是做出交易决策的地方。这个组件负责接收来自市场数据消费者组件的标准化市场数据更新。通常它还会构建完整的限价订单簿以反映市场状态或者至少构建一个根据交易策略需求简化的订单簿变体。它通常还会基于订单簿中的流动性和价格进行分析并做出自动交易决策。这个组件使用订单网关客户端组件与交易平台进行通信。现在我们已经介绍了电子交易生态系统中涉及的主要组件接下来我们将更详细地研究这些组件。首先我们从位于电子交易平台系统中的撮合引擎开始。设计交易交易所中的C撮合引擎在本节中我们将讨论上一节介绍的电子交易系统中的撮合引擎组件。首先我们要了解撮合引擎的作用以及为何需要它。理解撮合引擎的目的在由单一交易交易所构成的电子交易生态系统中通常会有一个交易所负责接收和管理众多市场参与者的订单。在这种情况下撮合引擎会接收参与者针对任何给定交易工具发出的各类订单。订单就是市场参与者向交易交易所发送的一种请求用于表达他们买卖可交易产品的意向。每当撮合引擎从订单网关服务器基础设施接收到一个新订单时它都会检查这个新订单是否与现有反向订单交叉以此判断是否会发生交易。就本书而言我们假设市场参与者只发送限价订单limit orders并指明订单方向、数量和价格。限价订单是指只能以等于或优于市场参与者指定价格执行的订单。现在应该很明显了撮合引擎执行着最为关键的任务即正确、公平地对不同市场参与者的订单进行匹配。这里所说的公平是指先到达交易所的订单先进行处理这种先进先出FIFOFirst In, First Out 的排序由订单网关基础设施处理我们稍后会讨论这部分内容。没有立即匹配成功的订单会留在订单簿中被称为被动订单。当新订单的价格与被动订单交叉时这些被动订单就有资格进行匹配。这种价格与被动订单交叉的订单被称为主动订单aggressive orders。撮合引擎将所有市场参与者发送的被动订单整理成一种数据结构恰如其分地命名为订单簿order book。订单簿的详细内容将是我们下一个讨论的主题。理解交易所订单簿限价订单簿包含所有市场参与者针对单一交易工具发出的被动限价订单。通常被动买单会按照从最高买入价到最低买入价的顺序排列被动卖单则按照从最低卖出价到最高卖出价的顺序排列。这种排序既直观又合理因为被动买单是从最高买入价开始匹配被动卖单则从最低卖出价开始匹配。对于方向和价格相同的订单会根据发送时间按先进先出的顺序排列。请注意先进先出只是一种排序标准现代电子交易市场有不同类型的匹配算法比如按比例分配Pro Rata以及一些先进先出和按比例分配混合的算法。按比例分配算法是指无论大额订单在先进先出队列中的位置如何它们都能从主动订单中获得更多的成交份额。对于我们的撮合引擎我们只实现先进先出的匹配算法。为了全面理解订单簿的工作原理我们来看看市场中发生的一些场景以及它们对订单簿有何影响。首先我们确定订单簿的初始状态。假设买卖双方分别有来自三个不同市场参与者——客户A、B和C的订单。客户A 订单ID 1 买入20 10.90客户B 订单ID 5 卖出10 11.00客户A 订单ID 2 买入10 10.80客户C 订单ID 6 卖出5 11.00客户B 订单ID 3 买入5 10.80客户B 订单ID 7 卖出5 11.10客户C 订单ID 4 买入100 10.70-表5.1 包含部分订单的限价订单簿初始状态在这里客户A有2个被动买单数量分别为20和10价格分别为10.90和10.80。客户B有一个数量为5、价格为10.80的买单以及2个卖单数量分别为10和5价格分别为11.00和11.10。客户C有2个被动订单——一个数量为5、价格为10.80的买单和一个数量为5、价格为11.00的卖单。现在假设客户A发送一个新的买单数量为10价格为10.90客户B发送一个新的卖单数量为10价格为11.20。更新后的订单簿如下表所示新订单已突出显示。由于先进先出的排序规则订单ID 8的新买单排在相同价格的订单ID 1的买单之后。客户A 订单ID 1 买入20 10.90客户B 订单ID 5 卖出10 11.00客户A 订单ID 8 买入10 10.90客户C 订单ID 6 卖出5 11.00客户A 订单ID 2 买入10 10.80客户B 订单ID 7 卖出5 11.10客户B 订单ID 3 买入5 10.80客户B 订单ID 9 卖出10 11.20客户C 订单ID 4 买入100 10.70-表5.2 添加新订单后的更新订单簿现在假设客户A将订单ID 2的订单数量从10修改为20。当订单数量以这种方式增加时该订单在先进先出排序中的优先级会降低会移至该价格水平队列的末尾。再假设客户B将订单ID 5的订单数量从10减少到1。请注意根据市场规则当订单数量减少时它在队列中的优先级不变仍保持原位。更新后的订单簿如下受影响的订单已突出显示客户A 订单ID 1 买入20 10.90客户B 订单ID 5 卖出1 11.00客户A 订单ID 8 买入10 10.90客户C 订单ID 6 卖出5 11.00客户A 订单ID 3 买入5 10.80客户B 订单ID 7 卖出5 11.10客户B 订单ID 2 买入20 10.80客户B 订单ID 9 卖出10 11.20客户C 订单ID 4 买入100 10.70-表5.3 订单修改后的订单簿状态最后假设客户C将订单ID 4的买单价格从10.70修改为10.90数量不变。这种订单操作的影响相当于取消原订单并以新价格发送一个新订单。再假设客户B决定不再需要订单ID 9的卖单并发送取消指令。由于这两个操作更新后的订单簿如下修改后的订单已突出显示取消的订单已从订单簿中移除客户A 订单ID 1 买入20 10.90客户B 订单ID 5 卖出1 11.00客户A 订单ID 8 买入10 10.90客户C 订单ID 6 卖出5 11.00客户C 订单ID 4 买入100 10.90客户B 订单ID 7 卖出5 11.10客户A 订单ID 3 买入5 10.80-客户B 订单ID 2 买入20 10.80-表5.4 订单修改和取消操作后的限价订单簿状态到目前为止我们讨论的场景中都没有发生交易因为订单活动使得所有买单价格都低于所有卖单价格。在下一节中我们将进一步探讨当出现一个主动订单其价格能够与买单或卖单价格交叉时会发生什么。就订单修改而言有两点需要注意当订单修改为减少数量时订单在队列中的优先级或位置不变。当订单修改为增加数量或修改价格时其效果相当于取消原订单并发送一个新价格和数量的订单即会为其分配一个新的优先级。在下一节中我们将探讨撮合引擎需要执行的下一项重要任务——匹配相互交叉的参与者订单。匹配参与者订单在本节中我们将了解当市场参与者修改现有订单或发送新订单且该订单价格导致与另一边现有被动订单匹配时会发生什么。在这种情况下撮合引擎会按照从最激进到最不激进的价格顺序将这个主动订单与被动订单进行匹配。这意味着被动买单从最高买入价开始匹配被动卖单从最低卖出价开始匹配。如果被动订单没有完全匹配是因为主动订单的数量小于另一边可用的被动流动性那么被动订单上剩余的流动性会留在订单簿中。如果主动订单没有完全匹配是因为另一边可用的被动流动性小于主动订单的数量那么主动订单上剩余的数量会作为被动订单留在订单簿中。让我们来了解一下匹配参与者订单的不同情况假设订单簿的状态保持在上一节结束时的状态如下所示客户A 订单ID 1 买入20 10.90客户B 订单ID 5 卖出1 11.00客户A 订单ID 8 买入10 10.90客户C 订单ID 6 卖出5 11.00客户C 订单ID 4 买入100 10.90客户B 订单ID 7 卖出5 11.10客户A 订单ID 3 买入5 10.80-客户B 订单ID 2 买入20 10.80-表5.5 订单匹配前订单簿的起始状态现在假设客户C发送一个卖单数量为50卖出价格为10.90。这将导致该卖单与订单ID 1和订单ID 8的买单完全匹配与订单ID 4的买单部分匹配数量为20订单ID 4的买单还剩余80的数量。完全匹配的订单将从订单簿中移除部分匹配的订单将修改为新的剩余数量。此次匹配交易后更新的订单簿如下所示客户C 订单ID 4 买入80 10.90客户B 订单ID 5 卖出1 11.00客户A 订单ID 3 买入5 10.80客户C 订单ID 6 卖出5 11.00客户B 订单ID 2 买入20 10.80客户B 订单ID 7 卖出5 11.10表5.6 反映主动订单和部分成交影响的订单簿现在假设客户A发送一个买单数量为10买入价格为11.00。这将与订单ID 5和订单ID 6的卖单完全匹配主动买单上剩余未匹配的数量将作为被动买单留在订单簿中。此次匹配交易后更新的订单簿如下所示客户A 订单ID 9 买入4 11.00客户B 订单ID 7 卖出5 11.10客户C 订单ID 4 买入80 10.90-客户A 订单ID 3 买入5 10.80-客户B 订单ID 2 买入20 10.80-表5.7 完全成交和主动方剩余数量后的订单簿现在我们已经了解了撮合引擎中会遇到的许多常见交互情况以及它们是如何处理的还有它们与限价订单簿之间的相互作用接下来我们就可以设计本书要构建的撮合引擎了。设计我们的撮合引擎在本书接下来的内容中我们将实现本章讨论过的每一个C电子交易生态系统组件。不过在开始下一章内容之前了解这些组件的架构非常重要这能让后续的实现细节更易于理解。我们在此仅展示图5.1中的撮合引擎组件以便更详细地讨论我们的撮合引擎设计图5.2 - 我们的撮合引擎组件设计与图5.1相比在这个图中我们展示了更多细节接下来将讨论撮合引擎的主要设计选择。线程模型在我们的系统中撮合引擎、市场数据发布器和订单网关服务器将各自独立运行在不同线程中。这样设计是有意为之以便每个组件都能独立运作。在市场活动高峰期整个系统可以实现最大吞吐量。此外每个组件还有其他需要执行的任务。例如即使撮合引擎处于忙碌状态订单网关服务器也必须与所有市场参与者保持连接。同样地假设市场数据发布器正忙于通过网络发送市场数据我们也不希望撮合引擎或订单网关服务器因此而减速。在上一章《构建低延迟应用的C基础模块》的 “面向多线程低延迟应用的C线程处理” 部分我们已经了解了如何创建线程、设置线程亲和性以及为它们分配任务。线程间通信这里另一个需要讨论的重要内容是撮合引擎与订单网关服务器基础设施之间的通信。订单网关服务器将市场参与者发来的订单请求序列化然后转发给撮合引擎进行处理。撮合引擎需要为订单请求生成响应并将其发送回订单网关服务器。此外它还需要将参与者订单的执行情况告知订单网关服务器以便通知他们交易信息。因此它需要一个双向队列即一个从订单网关服务器到撮合引擎的队列以及另一个从撮合引擎到订单网关服务器的队列。另一个通信渠道是撮合引擎生成并发送市场数据更新以向公共市场数据发布器组件反映限价订单簿的最新状态。最后由于撮合引擎、订单网关服务器和市场数据发布器都是不同的线程这为使用无锁队列提供了绝佳场景。我们将使用上一章 “使用无锁队列传输数据” 部分创建的无锁先进先出FIFOFirst In First Out队列。限价订单簿最后对于限价订单簿我们将使用几种不同的数据结构来高效地实现它。在不深入具体实现细节我们将在下一章探讨的情况下我们需要在买卖双方都以正确的排序方式维护买盘和卖盘以便在主动订单进入时进行高效撮合。我们需要能够高效地在价格水平上插入和删除订单以支持根据客户请求进行的添加、修改和删除订单等操作。这里还有一个特别重要的考虑因素即我们使用的数据结构和订单对象本身必须避免动态内存分配并且尽可能少地复制数据。我们将大量使用上一章 “设计C内存池以避免动态内存分配” 部分创建的内存池。理解交易所如何向参与者发布信息上一节专门讨论了撮合引擎的细节在讨论过程中我们假设撮合引擎从订单网关服务器基础设施接收市场参与者的订单请求。我们还隐含地假设撮合引擎会将其维护的限价订单簿的变化传达给所有监听市场数据馈送的市场参与者。在本节中我们将讨论撮合引擎与市场参与者进行通信所依赖的市场数据发布器和订单网关服务器组件。通过市场数据传达市场事件让我们先来讨论市场数据发布器组件。这个组件负责转换撮合引擎维护的限价订单簿的更新信息。我们之前提到市场数据网络层协议可以是TCP或UDP但在实际应用中通常首选UDP协议我们的市场数据发布器也将使用该协议。简单来说市场数据协议代表了市场数据发布器通过UDP或在某些情况下通过TCP协议发布的消息格式。适用于流媒体的FIX协议FASTFIX Adapted for STreaming是目前许多电子交易交易所使用的最知名且最受欢迎的市场数据消息格式。还有其他协议如ITCH、PITCH、增强订单簿接口EOBIEnhanced Order Book Interface、简单二进制编码SBESimple Binary Encoding等但出于本书的目的我们将创建一个类似EOBI或SBE的简单自定义二进制协议来使用。由于FIX是金融应用中最常用的协议我们在此介绍一些相关细节。FIX数据被组织为一组 “标签值” 格式的字段。通过一个简单示例会更容易理解假设你收到一个市场数据更新可能会收到以下字段集合来传达该更新的所有数据。这个假设的市场数据更新对应于为苹果公司股票股票代码AAPL数字证券ID为68475添加一个数量为1000、价格为175.16的新买单。标签FIX名称值描述268NoMDEntries1市场数据更新的数量279MDUpdateAction0新增市场数据更新的类型269MDEntryType0买盘市场数据条目的类型48SecurityID68475AAPL交易产品的整数标识符270MDEntryPx175.16此市场数据更新的价格271MDEntrySize1000此市场数据更新的数量............表5.8 - 对应假设市场数据更新的FIX消息示例构成市场数据协议的不同消息大致可分为以下几类图5.3 - 交易所发送的不同市场更新下面我们来讨论一下这些消息类别。市场状态变化这些消息用于通知市场参与者市场和 / 或撮合引擎的状态变化。通常市场会经历诸如交易关闭、开盘前常规交易时段前的市场状态、开盘市场从开盘前状态过渡到交易状态时和交易常规交易时段等状态。交易工具更新交易所使用交易工具更新消息向市场参与者通报可供交易的不同交易工具。一些交易所支持市场参与者即时创建的特殊类型交易工具这些消息用于通知参与者此类交易工具的变化。通常这些消息用于告知参与者交易工具的元数据如最小价格增量、跳动单位值等。最小价格增量是订单价格之间的最小价格差异。在我们目前看到的示例中我们假设最小价格增量为0.10即有效价格是0.10的倍数。跳动单位值是指当我们以相差一个最小价格增量的价格进行买卖时所获得或损失的金额。通常对于股票、交易型开放式指数基金ETFExchange Traded Fund等产品跳动单位乘数为1这意味着利润或损失仅仅是一对交易的卖出价格减去买入价格。ETF是在交易所交易的证券是一种投资选择由一篮子证券组成也就是说通过投资ETF你投资的是构成该ETF的一系列资产。但对于一些杠杆产品如期货、期权等跳动单位乘数可能不是1最终的利润或损失按如下公式计算((卖出价格 - 买入价格) / 最小价格增量) * 交易数量 * 跳动单位。订单更新市场数据发布器使用订单更新消息来传达撮合引擎维护的限价订单簿中的订单变化具体来说就是与我们在 “设计交易交易所的C撮合引擎” 小节的 “理解交易所订单簿” 部分所讨论的类似的订单簿更新。一般来说不同类型的订单更新消息如下订单添加交易所使用此消息通知参与者有一个新的被动订单被添加到了限价订单簿中。这里的典型属性包括交易工具ID、订单ID、价格、买卖方向、数量和优先级。优先级字段用于指定该订单在该价格的先进先出订单队列中的位置。订单修改交易所使用此消息让参与者知道有一个被动订单的价格或数量或两者都被修改了。此消息与订单添加消息有相似的字段。如前所述在大多数情况下除了订单数量减少的情况订单修改事件会被分配一个新的订单优先级值。订单删除此消息用于通知市场参与者有一个被动订单已从订单簿中删除。这里的重要属性是交易工具ID和订单ID用于指定从订单簿中删除的订单。交易消息交易所使用交易消息通知市场参与者市场中发生了一次撮合。一般来说这里的属性包括交易工具ID、主动订单的买卖方向、交易执行价格和交易数量。通常当交易发生时交易所还会根据需要发布尽可能多的订单删除、订单修改和订单添加消息以传达有关哪些订单已全部或部分执行以及哪些订单需要从订单簿中删除或修改以反映订单簿新状态的信息。市场统计信息这些是一些交易所发布的可选消息用于传达有关交易工具的不同类型统计信息。这些统计信息可以是关于某一交易工具的交易量、未平仓合约数量、该工具的最高价、最低价、开盘价和收盘价等信息。我们已经介绍了很多关于市场数据消息类型及其传达信息的细节。现在我们准备设计将在电子交易交易所中构建的市场数据发布器。设计市场数据发布者让我们讨论一下在电子交易所中要实现的市场数据发布者的一些设计细节。这里我们仅展示图5.1中的市场数据发布者以便更详细地讨论其设计。图5.4——我们的市场数据发布者基础设施设计市场数据发布者基础设施主要有两个组件。这两个组件都使用我们在上一章 “使用套接字进行C网络编程” 中构建的套接字工具将市场数据发送到网络上。此外我们还会用到构建的线程库用于创建、启动和运行市场数据发布者线程。市场数据协议编码器市场数据发布者基础设施中的市场数据协议编码器组件负责对撮合引擎发布的市场数据更新进行编码。市场数据编码器接收反映订单簿变化的市场数据更新并将其转换为带有一些附加信息的公开市场数据消息格式。该组件还将增量市场数据更新发布到为增量流配置的UDP多播流中。请记住增量流仅包含可用于更新订单簿的市场更新前提是参与者在增量更新之前对限价订单簿有准确的了解。编码后的市场数据更新也会发布到快照合成器组件我们将在下一节更详细地讨论这个组件。就网络流量而言市场数据流通常流量很大并且在市场高度波动期间活动会出现大幅波动。由于TCP协议需要对消息接收进行确认并重新传输丢失的数据这会增加额外的带宽消耗因此通常UDP是市场数据的首选网络协议。基于UDP的多播流也更受青睐因为市场数据可以在多播流上发布一次所有感兴趣的订阅者都可以订阅该流而无需通过TCP与每个市场数据消费者建立一对一的连接。这种设计并非没有缺点即市场数据消费者可能会因网络拥塞、硬件或软件速度慢等原因丢弃UDP数据包。发生这种情况时交易客户端维护的订单簿就会出现错误因为他们可能错过了与新订单添加、订单修改或取消等对应的更新。这就是快照多播流要解决的问题在后续章节中当我们实现市场数据消费者时将通过示例来探讨这个问题。但在下一节中我们将简要介绍一下快照合成器组件。快照合成器快照合成器接收市场数据协议编码器发布的编码市场数据更新合成限价订单簿的最新快照并定期将快照发布到快照多播流中。这里重要的是快照合成不会干扰增量流的发布以便订单簿的增量更新能够尽快发布。它是一个单独的执行线程其唯一职责是根据增量更新生成准确的订单簿快照。该组件还会在快照更新中添加正确的序列信息以便在将其发布到快照UDP多播流之前方便客户端进行同步。这意味着在它发送的快照消息中会提供用于合成此快照消息的增量流中的最后一个序列号。这一点很重要因为下游的市场数据消费者客户端可以使用增量流中最后一次更新的序列号来成功进行同步或数据追补。当我们构建市场数据发布者和市场数据消费者组件时这一点会变得非常清晰因为届时我们将通过示例详细介绍所有细节。另外需要理解的是适用于我们系统中其他组件的低延迟标准在此处并不适用因为这无论如何都是一个有延迟且经过抽样的信息流。此外客户端端预计极少出现数据包丢失的情况而且客户端端的快照同步过程较慢所以让这个组件实现超低延迟没有必要。对于我们的快照合成器组件为了简化我们也将使用UDP协议但在实际应用中它通常是TCP和UDP协议的组合。对于低延迟要求的市场参与者来说UDP流上的数据包丢失预计很少发生因为通常与交易所的网络连接以及沿途的交换机都具有较大的带宽容量和较低的交换延迟。此外参与者会投入资源采购和安装超高速服务器、构建低延迟的市场数据消费者软件并使用特殊的网络接口卡NICs来处理大量的市场数据。以上就是我们在本书中要构建的市场数据发布者基础设施的高级设计。接下来我们需要讨论交易所用于通知市场参与者其订单请求的响应以及订单执行情况的另一个通道——订单网关接口。通过订单网关接口通知市场参与者我们讨论过电子交易交易所使用市场数据消费者来传播有关订单簿变化以及交易所中不同交易工具的成交信息。这里的关键是这是公开的市场数据任何有权访问并订阅市场数据流的人都可以获取。本节将讨论交易所用于与市场参与者就其订单更新进行通信的另一个接口——订单网关接口。公开市场数据馈送提供的信息与订单网关基础设施提供的信息之间存在一些关键差异。理解网络协议的差异我们之前提到过这里再次重申通常市场数据发布者在网络层使用UDP协议而订单网关基础设施在与市场参与者的连接中在网络层使用TCP协议。这是因为市场数据发布者发布的数据量非常大需要尽快发布因此选择UDP而非TCP。市场数据发布者通常还有额外的同步机制来处理UDP上极少出现的数据包丢失问题。订单网关基础设施依赖TCP因为它需要一种可靠的方式与客户端通信如果没有TCP这里的数据包丢失很难妥善处理。直观来看如果客户不确定他们的订单是否到达交易所或者不确定在订单更新或成交时是否能立即收到通知那肯定会非常麻烦。区分公开信息和私有信息市场数据发布者和订单网关基础设施之间可能最大的差异在于市场数据发布者发布公开信息的同时会隐藏一些敏感信息比如某个订单属于哪个客户或者哪些客户参与了一笔成交交易。这些信息也是面向所有市场参与者发布的目的是用于构建限价订单簿以反映交易工具的状态。另一方面订单网关服务器仅向订单正在更新的客户发布订单更新通知。换个角度想参与者接收和处理公开市场数据时无需在订单簿中有任何订单但要接收私有订单网关通知参与者必须在订单簿中有订单否则交易所就没有什么可以私下通知客户的了。发送参与者的订单请求现在应该很明显的另一个主要差异是订单网关组件提供双向通信通道。这意味着客户可以向交易所发送订单请求比如新订单、修改订单、取消订单等等。正如我们之前讨论的交易所利用订单网关基础设施向市场参与者发送其订单的私有通知。而市场数据发布者基础设施通常不处理任何客户请求即通信路径仅从交易所到市场数据订阅者。我们在电子交易交易所一侧需要设计的最后一个发送组件就是刚刚讨论的订单网关基础设施接下来我们就来设计它。设计订单网关服务器让我们讨论一下在电子交易所中要实现的订单网关服务器的一些设计细节。这里我们仅展示图5.1中的订单网关服务器基础设施以便更详细地讨论订单网关服务器的设计。图5.5——我们的订单网关服务器基础设施设计与图5.1相比此图展示了更多细节并细分了订单网关服务器基础设施子组件的一些细节。TCP连接服务器/管理器订单网关基础设施中的第一个组件是TCP连接管理器。该组件负责搭建一个TCP服务器监听并接受来自市场参与者订单网关客户端的传入TCP连接。它还负责检测断开连接的客户端并将其从活动连接列表中移除。最后该组件需要将撮合引擎的订单响应转发给相应的目标客户端。我们将使用在上一章 “构建低延迟应用的C基础模块” 中 “使用套接字进行C网络编程” 部分实现的套接字工具、TCP套接字和TCP服务器功能。先进先出FIFO排序器该组件需要执行的另一项重要任务是在处理市场参与者的请求时保持公平性。如前所述为了保持公平性必须按照在交易所基础设施中接收到的顺序处理客户响应。因此FIFO排序器必须确保按照接收顺序将来自不同客户连接的客户请求转发给TCP连接管理器维护的撮合引擎。交易所消息协议解码器和编码器编码器 - 解码器组件负责在交易所消息协议与撮合引擎期望的客户端请求内部结构之间进行转换并以该内部结构发布客户响应。根据交易所协议的复杂程度这可能简单到只是将正确的字段打包和解包到二进制结构中。如果交易所消息格式更复杂那么就会涉及更多的编码和解码步骤。出于本书的目的我们将采用一种简单的交易所订单消息协议该协议使用打包的二进制结构并在交易所撮合引擎使用的格式之上添加了额外信息。关于电子交易交易所的讨论到此结束现在我们可以着手构建希望在该交易所进行交易的市场参与者的客户端基础设施了。构建市场参与者与交易所的接口现在我们将讨论市场参与者系统中组件的用途和设计。具体来说我们将从讨论客户端交易系统中的市场数据消费者开始它负责订阅、消费和解码交易所发布的公开市场数据。我们还将讨论客户端交易系统中的订单网关客户端基础设施它负责连接到交易所订单网关服务器。订单网关客户端还负责向交易所发送订单请求并为客户端订单接收和解码响应。理解市场数据消费者基础设施市场参与者交易系统中的市场数据消费者组件是电子交易交易所中市场数据发布者组件的直接对应部分。它负责订阅和消费交易所发布的多播网络流量将从交易所协议读取的市场数据解码并规范化为内部格式并实现与数据包丢失相关的同步机制。订阅和消费UDP多播流量首要且最明显的任务是订阅交易所发布市场数据的多播流。通常为了实现负载均衡交易所会将不同的交易工具分组到不同的多播流地址。这使得客户可以根据自己感兴趣的交易工具和产品选择交易所发布的所有数据的一个子集。通常这涉及客户加入正确的多播流而这些多播流的地址是交易所提供的公开信息。从交易所协议解码和规范化数据市场数据消费者接下来需要做的是将交易所市场数据协议转换为参与者系统中其他组件使用的内部格式。根据交易所市场数据协议的不同此组件的这部分在复杂性和性能延迟方面可能会有所不同。最快的协议是那些只需极少解码工作的协议如EOBI和SBE它们只是二进制打包结构。这意味着市场数据格式使得解码流只需要将字节流重新解释为我们期望在流中找到的二进制打包结构这样速度最快。像FAST这样更复杂的协议通常需要更长的时间来解码和规范化。在启动和数据包丢失时进行同步请记住我们讨论过通常交易所更倾向于使用UDP作为向参与者传递市场数据的网络协议。虽然这加快了数据向客户端的传递速度并实现了更高的吞吐量但由于UDP的不可靠性也可能导致数据包丢失和乱序交付。为了确保市场参与者按正确顺序接收市场数据包并在数据包丢失时能够检测到通常参与者需要检查数据包级和工具级的序列号。在交易所市场数据发布者和参与者的市场数据消费者两端都需要设计一种机制用于从这种数据包丢失中恢复。对于那些在市场开盘后才加入市场数据流或者因任何原因需要重新启动市场数据消费者组件的参与者也会用到同样的机制。在所有这些情况下客户端交易系统中的市场数据消费者需要执行一些同步操作以获取限价订单簿的当前完整状态。本节将解释实现这种同步常用的设计。通常交易所市场数据流分为两大组——快照流snapshot streams和增量流incremental streams。接下来我们将解释设置这两种流的原因以及它们如何帮助市场参与者处理数据包丢失的情况。增量市场数据流增量市场数据流假定市场参与者已经对撮合引擎维护的限价订单簿有正确的认知该数据流仅发布订单簿先前状态的增量更新信息。这意味着此数据流对带宽的要求要低得多因为它仅发布订单簿的增量更新内容。通常在正常运行条件下市场参与者只需订阅增量数据流就能维持订单簿的正确状态。如果客户端丢失了该数据流中的某个数据包那么他们所维护的订单簿状态可能与撮合引擎的不一致。处理这种情况的机制是清除或重置参与者维护的订单簿然后需要订阅快照数据流。快照数据流包含整个订单簿的完整状态数据而不仅仅是增量更新以便再次将订单簿同步到正确状态。具体流程是先清空订单簿开始将从增量数据流接收到的增量更新进行排队等待构建完整的订单簿状态然后将这些增量更新应用到完整的订单簿上从而完成同步。现在让我们进一步了解交易所发布的快照市场数据流的更多细节。快照市场数据流如前所述快照市场数据流包含的数据可用于从完全空白的状态构建完整的订单簿。通常该数据流仅包含与订单簿中每一个被动订单对应的详细订单添加Order Add消息列表。交易所通常会限制此列表的更新和发布频率这意味着可能每隔几秒才发送一次快照消息流。这是因为由于该数据流包含每个交易工具订单簿中所有订单的信息其带宽占用可能会很大。此外由于数据包丢失的情况极为罕见而且参与者在首次启动时并不介意等待几秒来获取订单簿的正确状态所以这种限制通常不会产生太大的负面影响。关于市场数据协议和同步过程的讨论到此结束现在我们可以设计即将实现的市场数据消费者了。设计市场数据消费者让我们讨论一下在市场参与者的交易系统中要实现的市场数据消费者的一些设计细节。这里我们仅展示图5.1中的市场数据消费者以便更详细地讨论其设计。图5.6——我们的市场数据消费者基础设施设计在设计市场参与者交易系统中的市场数据消费者基础设施时我们来讨论两个主要的子组件。这两个子组件都使用我们在上一章 “使用套接字进行C网络编程” 中构建的套接字工具来订阅和消费网络中的市场数据。快照和增量流同步器市场数据消费者需要有一个子组件除了订阅增量数据流外还能用于订阅快照数据流。请记住我们之前解释过当市场参与者的系统首次启动、在交易日中途需要重启或者丢失了增量数据流中的市场数据包时其对限价订单簿的认知是不正确的。在这种情况下正确的恢复/同步过程是清空限价订单簿订阅快照数据流并等待接收完整的订单簿快照。此外通过增量市场数据流持续传来的更新需要进行排队。一旦接收到完整的快照并且从快照中最后一次更新的序列号开始的所有增量更新也都已排队就绪同步过程就完成了。此时根据快照数据流重建限价订单簿并将所有排队的增量更新应用到该订单簿上以与交易所进行同步/追平数据。至此消费者可以停止从快照数据流消费数据不再订阅快照数据流而仅从增量数据流消费数据。市场数据消费者基础设施中负责这种同步机制的组件我们称之为快照和增量流同步子组件。市场数据协议解码器另一个子组件负责解码来自快照和/或增量市场数据流的输入数据。该组件将数据从交易所馈送格式转换为交易策略框架的内部格式。这通常是交易所提供字段的一个子集并且通常会在不同的交易所以标准化的形式呈现以使交易策略框架独立于特定交易所的细节。对于我们的市场数据消费者基础设施由于我们将使用打包二进制结构所以这个组件会设计得相当简单。但如前所述在实际应用中数据格式可能要复杂得多比如FAST格式。我们已经讨论了市场参与者系统如何消费来自交易所的公开市场数据馈送的细节和设计。接下来我们可以讨论订单网关客户端基础设施参与者使用它来发送订单请求并接收响应和成交通知。理解订单网关客户端基础设施市场参与者交易系统中的订单网关客户端基础设施是一个TCP客户端用于连接到交易所的订单网关服务器。该组件执行的另一项任务是通过这个TCP连接接收来自交易所的更新将从交易所订单消息协议接收到的消息解码为标准化的内部格式以供系统的其他部分使用。最后订单网关客户端组件还负责获取交易框架请求的订单操作将其编码为交易所能理解的订单消息格式然后发送给交易所。这里需要记住的重要一点是订单网关客户端必须始终与交易所保持可靠的TCP连接。这是为了确保交易所不会错过客户端的任何订单请求同时客户端也不会错过交易所对其订单的任何更新。除了TCP网络协议本身实现的可靠性机制外交易所和参与者通常还会实现一种应用层可靠性机制。这种应用层可靠性机制通常包括在交易所与客户端之间发送的消息上严格递增的序列号。此外还可能存在心跳机制即在活动较少的时段交易所与客户端相互发送消息以检查连接是否仍然有效。另外在客户端首次连接时存在用于对客户端进行身份验证和识别的机制这通常通过包含用户标识和密码等的握手机制来实现。还可能存在其他管理消息如登录身份验证消息这取决于交易所用途也各不相同。出于本书的目的我们将范围限定为不关注这些管理消息因为它们与我们的低延迟目标无关。接下来让我们设计订单网关客户端基础设施。设计订单网关客户端基础设施让我们讨论一下在市场参与者的交易系统中要实现的订单网关客户端的一些设计细节。这里我们仅展示图5.1中的订单网关客户端以便更详细地讨论其设计。图5.7——我们的订单网关客户端基础设施设计市场参与者交易系统中的订单网关客户端由两个简单组件构成。TCP连接管理器市场参与者交易系统中的订单网关客户端负责连接到交易所订单网关服务器并管理该连接。在实际应用中出于负载均衡、冗余和延迟等方面的考虑单个参与者会与交易所建立多个连接。但在我们要构建的电子交易生态系统中我们将设计为订单网关客户端与交易所订单网关服务器创建单个连接。我们将使用在上一章 “使用套接字进行C网络编程” 中构建的TCP套接字客户端库。订单网关协议编码器和解码器订单消息格式编码器和解码器负责将交易策略使用的内部订单请求格式转换为交易所格式并将交易所的订单响应和成交通知转换为交易策略框架使用的内部格式。该组件的复杂程度会因交易所格式的不同而有所差异但对于我们的交易系统我们将通过使用打包二进制结构来降低编码和解码的复杂度。接下来我们不再讨论订单网关基础设施转而讨论参与者系统中最复杂也是最有趣的组件——交易策略框架。设计低延迟C交易算法框架既然我们已经讨论了市场参与者交易系统中的市场数据消费者和订单网关客户端组件那么最后需要讨论的组件就是做出交易决策的框架。这个组件是交易系统中最重要的组件之一因为它蕴含着交易的 “智慧”。这里所说的 “智慧”是指处理标准化市场数据更新、构建市场状况视图、计算交易分析结果以寻找交易机会并执行交易的系统。显然这个组件依赖市场数据消费者来接收解码和标准化后的市场数据更新并使用订单网关客户端组件以解码和标准化的格式向交易所发送订单请求并接收订单响应。构建订单簿市场参与者需要根据交易所发布的市场数据构建限价订单簿。需要注意的是客户端并非一定要构建完整的订单簿特别是当交易策略不需要那么详细的信息时。出于本书的目的我们将在交易框架中构建一个完整的订单簿但我们想指出的是并非在所有情况下都必须这么做。比如有些策略只关心最激进定价订单的价格和/或数量即最高买价和最低卖价称为订单簿顶部TOBTop Of Book 或最佳买卖报价BBOBest Bid and Offer 这就是一个简单的例子还有些策略仅依赖交易价格来做决策不需要完整的订单簿视图。这里需要重申的一点是客户端构建的订单簿与交易所维护的订单簿略有不同因为客户端通常不知道每个订单属于哪个市场参与者。此外根据交易所的不同一些其他信息可能不会提供给市场参与者比如哪些订单是冰山订单、哪些新订单是止损订单、防止自成交的相关考量等。冰山订单是指隐藏数量比公开市场数据中显示数量大的订单。止损订单是指在特定价格成交前处于休眠状态之后才会被激活的订单。防止自成交SMPSelf - Match Prevention 是一种限制可防止客户与自己进行交易部分交易所会在撮合引擎中执行这一限制。出于本书的目的我们将忽略这些特殊功能不进行相关实现。另外需要理解的是交易参与者拥有的订单簿比撮合引擎中的订单簿稍有延迟。这是因为在撮合引擎更新其订单簿到交易客户端获取对应变化的市场更新并更新自己的订单簿之间存在一定的延迟。构建特征引擎复杂的交易策略除了依赖订单簿之外还需要构建更多的智能功能。这些交易策略需要在交易所发布的价格、流动性、交易事务和订单簿的基础上实现各种交易信号和智能分析。其思路是构建智能分析功能这可以是技术分析指标、统计预测信号和模型以及与市场微观结构相关的统计优势的组合。关于各种交易信号和预测分析的详细讨论超出了本书的范围但有很多专门探讨这一主题的文献。在实际应用中对于这些预测优势有很多不同的术语比如交易信号、指标、特征等等。交易系统中构建并整合这些预测信号的组件通常被称为特征/信号/指标引擎。在本书中我们将为交易策略构建一个极简的特征引擎但我们在此重申根据策略的复杂程度特征引擎可能会变得非常复杂。开发执行逻辑在构建订单簿并从当前市场状态推导出一些交易信号后如果交易策略发现了机会它们仍然需要在交易所执行订单。这通过发送新订单、修改现有订单将其调整到更激进或更保守的价格和/或取消现有订单以避免成交来实现。交易基础设施中负责发送、修改和取消订单即基本上管理策略在交易所的订单的子组件称为执行系统。对于执行系统而言能够快速响应市场数据和交易所传来的订单响应并尽快发送订单请求是极其重要的。高频交易系统的盈利能力和可持续性在很大程度上取决于在执行系统中实现尽可能低的延迟。理解风险管理系统风险管理系统是交易策略基础设施的重要组成部分。从技术上讲在实际应用中现代电子交易生态系统中有多层风险管理系统。实际上在客户的交易策略框架、市场参与者系统中的订单网关客户端以及清算经纪商端的后端系统中都存在风险管理系统。出于本书的目的我们仅在交易策略框架中实现一个极简的风险管理系统。风险管理系统试图管理不同形式的风险如下图所示图5.8——自动化风险管理系统中的不同风险指标接下来让我们更详细地讨论这些风险度量。基于订单数量的风险许多交易系统关注的一个度量指标是算法允许发送的单个订单的最大可能数量。这主要是为了防止系统中的错误和用户失误避免算法意外发送比预期大得多的订单。在实际应用中这类错误被称为 “胖手指错误”指的是用户意外按下过多按键的情况。基于公司仓位的风险一个明显的风险度量是策略在某一交易工具上的仓位。仓位规模直接决定了如果市场价格发生一定变化会损失多少钱。这就是为什么策略或公司在某一交易工具上的已实现仓位极其重要需要密切监控以确保其处于商定的限制范围内。请注意已实现仓位是指策略当前持有的仓位它忽略了策略可能拥有的额外订单这些额外订单在执行时可能会增加或减少仓位。基于最坏情况仓位的风险请注意在上一节中我们提到已实现仓位指标忽略了市场中存在多少额外的挂单。最坏情况仓位指标会跟踪在考虑到那些会增加已实现仓位的挂单以及实际已实现仓位的情况下仓位会是多少。这意味着如果策略或公司处于多头仓位通过买入金融工具形成的仓位那么它还会检查该策略在市场中有多少额外未执行的买入数量以计算绝对最坏情况下的仓位。这一点很重要因为有些策略可能永远不会建立大量仓位但可能在市场中始终有大量活跃订单。做市策略就是这类策略的一个典型例子我们将在本书后面看到这里的关键是在风险管理方面考虑最坏情况很重要。管理已实现和未实现损失的风险这是大多数人在电子交易的背景下想到风险时会考虑的方面。这个风险指标跟踪并限制策略或公司的亏损金额。如果亏损值超过某个阈值那么根据公司在经纪账户中的资金、拥有的抵押品等情况公司可能会面临一些后果。不仅在策略开仓和平仓时跟踪其已实现损失很重要而且根据市场价格跟踪未平仓仓位也很重要。为了理解这一点让我们解释以下场景一个策略买入一定数量的金融工具然后以较低价格卖出相同数量此时该策略有已实现损失且没有未平仓仓位。现在假设该策略买入一定数量的交易工具买入后处于多头仓位随后市场中该工具的价格下跌。在这种情况下该策略不仅有上一组交易带来的已实现损失而且最近新开的多头仓位还产生了未实现损失。风险管理系统需要近乎实时地计算已实现和未实现损失以便准确了解实际风险。基于交易量的风险这个度量不一定是一种风险一个策略在某一天或通常情况下交易量很大这本身并不是问题。这个风险指标主要是为了防止在软件或配置出现错误或者遇到意外市场情况时失控的算法在市场中过度交易。有很多方法可以实现这一点但最简单的方法是对策略在自动停止发送新订单或进一步交易之前对某一交易工具的交易量设置上限。通常在这种情况下需要外部人工操作员确保算法行为符合预期然后恢复或停止交易策略。管理订单、交易和损失速率的风险我们将在本小节讨论的风险指标属于基于速率的风险管理类别。我们所说的基于速率是指在滑动时间窗口内计算风险以确保策略在每个窗口内发送的订单数量不会过多、交易量不会过大、损失金额不会过高等等。同样这些指标是为了防止交易策略出现意外行为或类似失控、脱缰算法的情况。这些指标通过在时间窗口结束时重置基础指标订单数量、交易数量、交易量或损失的计数器或者使用这些指标的滚动计数器来实现。这些风险指标还能在市场极度波动或出现闪崩情况时潜在地防止交易策略出现意外行为。最后我们将设计电子交易生态系统中的最后一个主要组件。设计我们的交易策略框架让我们讨论一下在参与者的交易系统中要实现的交易策略框架的一些设计细节。这里我们仅展示图5.1中的交易策略框架以便更详细地讨论其设计。图5.9——我们的交易策略框架设计现在我们将讨论本书中要构建的交易策略框架的主要子组件的设计。请注意我们交替使用交易策略框架和交易引擎这两个术语在本书的上下文中它们含义相同指的是一组用于承载和运行自动化交易算法的组件。限价订单簿交易策略框架中的限价订单簿与交易所撮合引擎构建的限价订单簿类似。显然这里的目标不是进行订单撮合而是根据市场数据消费者通过无锁队列接收到的市场数据更新构建、维护和更新限价订单簿。这里仍然需要支持高效地在订单簿中插入、修改和删除订单。此外该订单簿还应便于特征引擎和交易策略组件使用。可能有各种使用场景例如对于只需要最优价格和数量的组件能够快速有效地合成最佳买卖报价BBOBest Bid and Offer或顶部订单簿TOBTop of Book另一个例子是能够跟踪策略自己在限价订单簿中的订单以确定它们在某一价格水平的先进先出FIFO队列中的位置还有一个例子是能够从公开市场数据馈送中检测策略订单的成交情况在私有订单馈送滞后于公开市场数据馈送时这会带来很大优势。在本书构建的交易策略中实现这些细节超出了我们的讨论范围。但在实际应用中这些细节非常重要因为从订单响应和市场数据中检测成交情况所获得的优势在延迟方面可能达到几十、几百甚至几千微秒。这里我们将使用在上一章 “构建低延迟应用的C基础模块” 中 “使用无锁队列传输数据” 一节构建的无锁队列以及 “设计C内存池以避免动态内存分配” 一节构建的内存池。特征引擎我们之前提到本书将构建一个极简的特征引擎。我们的特征引擎仅支持根据订单簿中的可用数据计算单个特征这个单一特征将用于驱动我们的交易策略。当订单簿在价格或流动性方面发生重大变化以及市场中有交易发生时这个特征会被更新。特征更新后交易策略可以使用新的特征值重新评估其仓位、挂单等情况从而做出交易决策。交易策略交易策略是最终根据多种因素做出交易决策的组件。交易决策取决于交易算法本身、特征引擎提供的特征值、订单簿的状态、策略订单在订单簿中的价格和FIFO位置、风险管理器的风险评估结果、订单管理器中挂单的状态等等。这是交易策略框架中最复杂的部分因为它需要处理许多不同的情况并安全且盈利地执行订单。在本书中我们将构建两种不同的基本交易算法做市策略也称为提供流动性策略以及流动性获取策略也称为消耗流动性策略。做市策略在订单簿中有被动订单依靠其他市场参与者跨越买卖价差与我们进行交易。流动性获取策略则是跨越买卖价差并发送主动订单以获取被动流动性的策略。订单管理器订单管理器组件是一个抽象层它隐藏了发送订单请求、管理活跃订单状态、处理这些订单的在途情况我们稍后会解释、处理交易所的响应、处理订单部分或全部成交的场景以及管理仓位等底层细节。订单管理器还构建并维护几个不同的数据结构以跟踪策略订单的状态。从某种意义上说订单管理器类似于限价订单簿只不过它管理的是属于该策略的一小部分订单。另一方面订单管理存在一些额外的复杂性因为存在市场参与者向交易所发送订单请求同时交易所撮合引擎发生一些事件的情况。在途情况的一个例子是客户试图取消一个活跃订单并向交易所发送取消请求。但在取消请求发送到交易所的途中交易所的撮合引擎执行了该订单因为出现了与之匹配的主动订单。然后当取消请求最终到达撮合引擎时订单已经执行并从交易所的限价订单簿中移除导致该取消请求被拒绝。订单管理器需要能够准确高效地处理所有类似这样的不同场景。在本书中我们将构建一个订单管理器它可以用于管理被动订单和主动订单并能处理所有这些不同的情况。风险管理器风险管理器跟踪我们在上一节 “理解风险管理系统” 中描述的不同风险指标。此外风险管理器需要在风险限额被突破时通知交易策略以便交易策略可以降低风险和 / 或安全关闭。在我们的交易基础设施中我们将实现一些基本的风险指标如仓位、总损失以及订单请求的消息速率。总结至此我们对电子交易生态系统中主要组件的细节和设计讨论完毕。让我们总结一下所讨论的概念、组件、交互以及构建电子交易生态系统的组件设计。我们从介绍电子交易生态系统的拓扑结构开始。它由电子交易所以及许多希望在该交易所进行交易的市场参与者组成。电子交易所基础设施从高层次来看主要由三个组件构成撮合引擎、市场数据发布者和订单网关服务器基础设施。从市场参与者的角度来看主要组件包括市场数据订阅者和消费者、包含所有子组件的交易策略框架以及订单网关客户端基础设施。接下来我们深入探讨了交易所撮合引擎的细节。我们解释了该组件的职责以及它如何构建、维护和更新限价订单簿如何撮合参与者相互交叉的订单。在该部分结尾我们设计了简化的撮合引擎组件及其子组件我们将在下一章中实现它们。随后讨论的主题是交易所的市场数据发布者和订单网关服务器基础设施。我们详细描述了市场数据馈送所包含的不同消息、市场数据馈送协议并设计了市场数据发布者内部的组件。我们还讨论了订单网关服务器交易所将其作为市场参与者连接的端点用于转发订单请求以及接收订单响应和撮合引擎执行订单的通知。我们展示了订单网关服务器及其所有子组件的设计将在本书后面的章节中实现它们。再接下来的部分研究了市场参与者的交易系统。首先我们讨论了市场数据消费者和订单网关客户端基础设施的细节参与者使用它们来消费交易所的公开市场数据馈送并与交易所建立连接和进行通信。我们还展示并讨论了要构建的市场数据消费者的设计以及它如何同步和解码交易所市场数据馈送。最后我们设计了订单网关客户端基础设施交易系统将使用它连接到交易所的订单网关服务器基础设施并与之通信。本章的最后一部分专门描述和设计交易策略框架。我们描述了构建这个框架所需的主要组件订单簿、特征引擎、执行逻辑框架和风险管理子组件。最后我们规划了要构建的交易基础设施的设计以便在后续章节深入探讨底层细节之前你能理解这个组件的高层次设计。下一章将深入探讨我们在本章中设计的撮合引擎框架的实现细节。请注意在实现电子交易生态系统的过程中我们将重用在上一章构建的许多基础模块。从下一章开始随着我们实现系统的其余部分构建这些基础模块的动机将变得更加清晰。