Foundational System Design Concepts with Applications to Quantitative Finance

The goal

I'm trying to learn some system design fundamentals, with an emphasis on quantitative finance. To do this, I've aggregated some common design cases for building quant trading infrastructure.
For each system design problem, I will include the following:

  • (a) A brief description about what is being designed and why it is necessary.
  • (b) The functional requirements.
  • (c) Some clarifying questions.
  • (c) My approach to the solution which includes the high level components, data structures used, and other important details.
  • (d) Follow-up questions presenting potential further challenges, along with their solutions.



1. Order Matching Engine

(a) Description

An Order Matching Engine is the core component of any trading system that automatically pairs buy and sell orders for financial instruments. When traders submit orders to buy or sell at specific prices, the engine finds compatible orders and executes trades. The engine should maintain an orderbook full of submitted orders on both the buy and sell side.

(b) Functional Requirements:

  • Accept buy and sell orders with price, quantity, and timestamps
  • Match compatible orders using price-time priority (best price first, then FIFO)
  • Handle partial fills when order quantities don't exactly match
  • Process 10,000+ orders per second with sub-millisecond latency


(c) Clarifying Questions

Let's start by asking some clarifying questions and making some assumptions about our functional requirements, from which we will start weighing different design decisions for a solution.
Question 1: Do we need to account for concurrent order activity, or can we assume that the matching engine will handle separate orders synchronously? Answer: We must assume concurrent orders from separate users, and handle them gracefully.
Question 2: When there is a partial fill, should we create a new order with the remaining amount? Answer: Yes.

(d) My approach and thought process

So we could try to implement this as one big singular component, but I think that it would get a little too unnecessarily complex. Let's think about our functional requirements first, and then try to separate our concerns into sensible components that we can focus on implementing one at a time.
A couple of different components come to mind off the bat:

  • Since our order matching engine will receive orders from separate clients, we need to keep track of our client connections.

  • The actual order matching process (which involves checking both the buy and sell sides, looking for orders that are compatible) is going to happen constantly, so this should be its own separate component which runs concurrently with everything else.

  • We want the matching engine to be able to broadcast a current BBO price, so this should be a separate component that will receive trade confirmations, and then broadcast prices to any subscriber, whether that is a participating client, or data subscriber.

I think this is a good foundation. Let's formally list out the components for good measure: 1. Client Connection Handler 2. Order Gateway 3. Matching Engine 4. Data Broadcaster
Let's talk more about each component:

Client Connection Handler

The main data structure should be a hashmap where k = client_id and v = another hashmap data structure, which contains each separate ongoing client connection along with client metadata such as portfolio balance, current orders, etc. Each client connection will be its own process running asynchronously along with the rest of the order matching engine. This component will receive and deliver all communications between the matching engine and every client. Since this activity will happen concurrently, we should maintain a data structure which handles this gracefully. We'll use a FIFO queue for this, and each client connection will be able to add order messages to this queue.
Next, we will go through both the order gateway and the matching engine component, which both interact with the most important data structure of this system, the orderbook. We can maintain a max heap for the buy orders and a min heap for sell orders. This way, orders can easily be matched by comparing the tops of both heaps.

Order Gateway

This component will be responsible for receiving and placing orders in the right spot in the orderbook. We'll have 2 FIFO queues, one for buy orders and one for sell orders. Each buy and sell queue will contain separate processes that pop orders from their respective queues, and put them into the orderbook.

Matching Engine

This component will continually look at the top of the orderbook, and match orders if there is an opportunity. If the prices don't match, the trade settlement price will be the average of the two prices. If the order sizes don't match, a new order will be created with the remainder of the bigger order, which will have the same ID. Every time an order is matched, the following will happen: 1. A transaction receipt will be sent to both parties of the trade via the connection handler. 2. Data such as the current best bid, best ask and last traded price will be sent to the Data Broadcaster.

Data Broadcaster

This will be responsible for broadcasting data to clients and whoever else is subscribed. It will contain a FIFO queue which receives messages from the other internal components, and then broadcasts those messages via UDP multicast or some other efficient protocol to subscribers.

Keep in mind, all of these components will be their own process which run asynchronously.

I think that's all for now!

Remaining system design problems:

2. Price Feed Processor

Design a system that receives stock price updates from multiple data sources and provides the latest price for any stock symbol.

3. Rate Limiter

Design a rate limiting service that prevents trading applications from making too many API calls and overwhelming downstream systems.

Comments

No comments yet.