想象一下股票市场就像一个农贸市场。
每支股票就像市场中的一个摊位区域,人们聚集在这里买卖产品。更多的交易为证券交易所带来更多手续费利润。所以他们的工作是促进尽可能多的”交易”。每个卖家可以设立摊位并指定他们愿意出售的价格。
如果没人买,他们可能会降低价格。

买家想要最便宜的价格,所以他们通常会先去报价最低的摊位。
在”理想”世界中,买家会排成一队,按照他们愿意支付的价格排序以保证公平。这意味着出价更高的买家站在更前面。此外,买家可以通过改变他们愿意支付的价格来调整他们在队列中的位置。当买家的价格达到或超过卖家的价格时,交易就会发生。
这就是交易所匹配他们的时候!

简单来说:
- BUY 订单按降序排序(最高出价优先)
- SELL 订单按升序排序(尽可能便宜买入)
- 它们重叠的点是市场价格
在现实中,这要复杂得多……但这是基本思想。
核心需求
别担心,很简单!
- 一个只交易股票的交易所
- 用户可以下 BUY 或 SELL 订单
- 用户也可以随时取消订单
- 交易所必须实时匹配买家和卖家
- 并且限制用户每天可以交易的股票数量
- 它还应该实时发布市场数据
交易所让交易变得公平和透明。
在大多数交易所中:
- 新订单 ≈ 50% 消息
- 取消订单 ≈ 40% 消息
- 执行订单 ≈ 2% 消息
- 其余是…噪音

关键术语
经纪人(Broker): 一个应用或网站,让用户可以 BUY 或 SELL,并查看交易所的价格。
订单簿(Order Book): 一支股票的所有当前 BUY 和 SELL 订单列表,显示谁想买卖、多少数量、什么价格。
市价单(Market Order): 你不设定价格的 BUY 或 SELL 订单。它以当前市场价格立即执行,只要有足够的流动性。此外,它在订单簿中享有优先权。
限价单(Limit Order): 你选择”确切”价格的 BUY 或 SELL 订单。 例如:
- 以100 或更低价格成交
- 以100 或更高价格成交
价差(Spread): 最高 BUY 价格和最低 SELL 价格之间的差异。
利润公式: 利润 = 卖出价格 - 买入价格 (所以低买高卖)
系统架构
交易所与许多”外部”服务交互:
- 在线聊天支持
- CAPTCHA 实现
- 用户数据管理和跟踪
- 发送电子邮件、短信和移动应用通知的服务
但让我们关注核心设计本身……
它的架构是异步和事件溯源的。
以下是交易所的三个关键组件:
- 经纪人(Broker) - 让人们在交易所买卖股票
- 网关(Gateway) - 经纪人向交易所发送 BUY 或 SELL 订单的入口点
- 匹配引擎(Matching Engine) - 匹配买卖订单以创建交易的组件
让我们深入探讨!
经纪人
经纪人与交易所交互以:
- 发送订单请求 - 下订单、接收状态更新、访问交易信息
- 接收市场数据 - 历史数据用于分析、流式传输实时交易数据等

用户使用 REST API 和 WebSocket 进行实时数据传输连接到经纪人。而经纪人使用金融信息交换(FIX)协议与网关通信。
FIX 是一个双向通信协议,用于通过”公共网络”安全地交换数据。它为订单消息分配唯一的序列号。此外,它使用校验和消息长度来验证数据完整性。
它接收来自经纪人的订单,并将它们转换为交易所的内部格式。然后它将交易执行结果发送回用户。也可以将网关放在交易所服务器附近(托管)以实现低延迟。

网关有三个关键部分:
- 风险管理器 - 确保用户有足够资金并阻止任何异常交易活动。此外,它确定交易所费用
- 钱包 - 存储用户的交易资金和资产
- 订单管理器 - 为公平性分配序列号并更新订单状态。此外,它将清除的订单发送到匹配引擎
让我们深入探讨……
网关用风险管理器和钱包服务验证订单。
然后它将请求传递给订单管理器。把订单管理器想象成一个包含所有订单的轻量级列表:未结、已取消和已拒绝的订单。它更新订单状态并处理取消请求。
订单管理器为每个订单(交易或取消)和执行填充(对于 BUY 和 SELL)分配一个全局递增的序列号。

序列号保证:
- 公平性、及时性和准确性的排序
- 快速恢复和确定性重放
- 精确一次保证
此外,序列号使查找入站和出站序列中缺失的事件变得容易。
清除后,订单管理器将订单路由到匹配引擎。

每种消息类型(新订单、取消、交易)都有自己的主题队列,有自己的序列号。这种分离有助于:
- 维护每个流内的顺序
- 独立处理每种消息类型并减少竞争
- 使恢复更容易,因为交易所可以按确切顺序”重放”每个主题的事件
让我们继续!
匹配引擎
它验证序列号,匹配 BUY 和 SELL 订单,并基于交易创建市场数据。
匹配引擎有两个关键部分:
- 订单簿 - 内存中的 BUY 和 SELL 订单列表
- 匹配逻辑 - 匹配 BUY 和 SELL 订单并将那些交易结果作为市场数据发送
让我们深入探讨……
订单簿
它为每支股票(代码)维护一个内存中的未结订单列表。
实际上每支股票有两个列表:
- 一个用于 BUY 订单
- 另一个用于 SELL 订单
每个列表按价格和时间戳以先进先出(FIFO)方式排序。

每个价格级别有一个单独的订单队列(双向链表):
- 新订单添加到尾部 - O(1) 时间复杂度
- 已填充或取消的订单使用指针从队列中移除 - O(1) 时间复杂度
它跟踪最高出价和最低要价以快速匹配。例如,BUY@96 和 SELL@98。
搜索所有价格级别和链表可能很慢 - O(n)。所以它使用索引将订单 ID 映射到内存中的订单对象:订单 ID → 指针(引用)。它允许通过索引在 O(1) 中按 ID 查找订单进行快速取消,然后将金额设置为 0。
已关闭的订单从订单簿中移除并添加到交易历史中。
匹配逻辑
它检查消息是否遵循正确的序列,并在以下情况下匹配 BUY 和 SELL 订单:
买入价格 ≥ 卖出价格
相同的订单序列(输入)必须始终产生相同的执行序列(输出)。所以匹配函数必须快速、准确且确定性。
本文为学习目的的个人翻译,译文仅供参考。
原文链接:Stock Exchange System Design。
版权归原作者或原刊登方所有。本文为非官方译本;如有不妥,请联系删除。