Stock Exchange System Design
First, let's understand what a stock exchange system is:
- A stock exchange is a marketplace where investors buy and sell shares of companies.
- A stock (share) represents partial ownership in a company.
- A stock exchange is a regulated marketplace for buying and selling securities like stocks.
- A buy order means you want to purchase shares; a sell order means you want to exit ownership.
- Market orders execute immediately at the current price; limit orders execute only at your chosen price.
- Prices change constantly due to supply and demand.
- Profit or loss depends on the difference between the buying and selling prices.
Here, we will decode the system design behind such stock exchange systems.
Functional Requirements
The system must support these functional requirements:
- Users can place buy/sell orders.
- Users specify the price and quantity.
- The system supports both:
- LIMIT Orders (price-constrained)
- MARKET Orders (best available price)
- The system matches buy and sell orders.
- Partial order matching is allowed.
- Unmatched orders remain active until they are:
- Matched, or
- Expired at the end of the trading day (usually 4 PM)
- Orders expire after the market closes.
- Matching results in trade execution.
Out of Scope
- Listing / delisting stocks
- Advanced order types (stop loss, iceberg, etc.)
Non-Functional Requirements
- Scalability β The system must handle large numbers of concurrent orders and users.
- Availability β Browsing stocks and order books must remain available. The platform should remain accessible with minimal downtime.
- Reliability β Orders must not be lost, duplicated, or corrupted.
- Consistency & Integrity β Executing matches must be highly consistent. Buy/sell matching and trade records must remain accurate.
Core Concepts
LIMIT Order: Executed only at the specified price or better.
MARKET Order: Executed immediately at the best available price.
Partial Matching: Orders may be filled in parts instead of all at once.
For example, you place a buy order for 1,000 shares of XYZ at βΉ100.
But in the market:
- 400 shares available at βΉ100
- 300 shares available at βΉ100
- No more sellers at βΉ100
So what happens?
- β 700 shares get executed
- β 300 shares remain pending
Thatβs a partial match.
APIs
Place Order
POST /orders
{
"type": "BUY | SELL",
"stockId": "ABC",
"quantity": 100,
"price": 250.00,
"orderType": "LIMIT | MARKET"
}
Execute Match (Internal API)
POST /execute
{
"buyOrderId": "O123",
"sellOrderId": "O987",
"quantity": 5,
"executionPrice": 249.50
}
Get Order Status
GET /orders/{id}
Returns:
- OPEN
- PARTIALLY_FILLED
- FILLED
- EXPIRED
High-Level Architecture
At a very high level, a stock exchange service looks like this:

Actors
- User β places buy/sell orders
- Company Admin β lists/delists stocks
Core Service
- Stock Exchange Service (central logic)
Storage Models
- Stock
- Order Request
- Matched Order
Scale Estimation
Assumptions:
- 100 million users
- 10% daily active users β 10 million daily active users
- 5000 tradable stock companies
- 500 million orders / day
TPS:
For simplicity, assume there are approximately seconds in a day.
orders per second β orders per second β 5000 orders per second (TPS is very high)
Peak traffic may be 5β10Γ higher, requiring the system to sustain:
- 5k β 50k orders/sec
- Extremely high write contention
Storage Estimation
Assume that each stock's details take around 1 kb of storage:
stocks = 5 mb
stocks = 10 mb
With 500 million orders/day and 100Bytes/order:
=> Bytes/order = bytes/day = 50 GB per day
=> bytes/year => bytes/year (approx) = 20 TB per year
Historical Stock Prices Storage
We are considering 5000 stocks.
We will store the price for every minute of trading hours.
Trading hours β 6.5 hours/day = 390 minutes/day β 400 minutes / day (assumed for simplicity)
A price record β 100 Bytes (timestamp, price, and a few other fields)
So the storage per day for the historical data of 5000 stocks β bytes = 200 mb per day
The yearly storage = = (approx) = 80000 mb = 80 GB
Fairness in a Stock Exchange System
Fairness in a stock exchange is achieved through deterministic and rule-based matching logic rather than subjective decisions.
Price Priority
The primary rule for fairness is price priority:
- A buy order should match with the lowest available sell price.
- A sell order should match with the highest available buy price.
Examples:
- If a buyer is willing to pay 50, any seller offering at β€ 50 is a fair match.
- If a seller is asking 50, any buyer bidding at β₯ 50 is a fair match.
This guarantees economically fair outcomes for both sides.
Time Priority
When multiple orders exist at the same price:
- The earliest order gets execution priority.
This prevents manipulation and ensures predictable behavior.
Deep Dive - Microservice Architecture
Now let's move toward a microservice architecture. In a stock exchange system, microservices allow independent scaling, fault isolation, and specialized optimization of order intake, matching, settlement, and market data components to handle high throughput and low-latency requirements.
API Gateway
Acts as the entry point for all client requests. It handles authentication, rate limiting, request validation, and routes traffic to the appropriate internal services.
Stock Service
Manages stock metadata such as listing, delisting, and browsing. Company stock admins can list/delist stocks, and other users can browse stocks. It interacts with the Stock DB and is primarily read-heavy, serving market information to users. We need a relational (SQL) database for the Stock Service, as we have structured data, a low write frequency, and a need for consistency when listing/delisting stocks.
Order Service
Receives buy/sell orders from users, performs basic validation, persists the order, and publishes it to a queue for asynchronous processing. It ensures durability and decouples order intake from matching. We can use a write-optimized NoSQL database (key-value or document DB) for orders, since we will have a high order volume and need horizontal scaling, and orders are mostly immutable. The Order DB can be partitioned by Order ID or Stock ID.
Matcher Service
The core matching logic component, which consumes orders from the queue and matches them using price-time priority. After matching, it stores the matched orders and produces match events for the downstream services. We can use a relational database for matched orders.
Match Execution Service
Finalizes matched trades by updating order status and recording trade details. It also interacts with the Payment Service
and handles payment or settlement. We need a relational database here as well, because we need ACID compliance and strong consistency.

We also must ensure idempotency in this design because:
- An event could be delivered twice.
- A downstream service must not process the same raw orders or matched orders again.
- So each trade should have an idempotent ID (like match_id), and the downstream service must check whether it has already been processed.
Together, this architecture separates responsibilities, supports scalability, and enables low-latency order matching with reliable post-trade execution.
Separation of Matching and Execution Services
Matching and execution are separated because they solve fundamentally different problems.
Matching Service Responsibilities
- Order evaluation
- Price-time priority logic
- High-throughput comparisons
- Compute-intensive operations
Execution Service Responsibilities
- Database updates
- Order state transitions
- Balance / settlement updates
- Side effects (payments, confirmations)
Deep Dive - Matching
In our current design, the Order Service puts all types of orders into the queue. However, let's improve this with the matching logic in mind. Orders are routed into symbol-specific buy and sell queues to isolate each stockβs order flow and maintain independent order books. Since matching pairs buy and sell orders of the same symbol, partitioning by symbol guarantees deterministic processing without cross-symbol interference.

Flow in Your Current Design (No Order Book)
- A user places a buy/sell order via the API Gateway.
- The Order Service validates the request and stores the order in the Order DB (NoSQL) for durability.
- It routes the order to the appropriate symbol-specific queue (e.g., Meta Buy, Meta Sell, Apple Buy, ...).
- The Matcher Service consumes orders from the symbol-specific buy and sell queues.
- It applies the price-time matching logic and creates a Matched Order.
- The Matcher Service stores matched trades in the Match DB.
- The Matcher Service emits a matched event to the matched-order queue.
- The Match Execution Service consumes the event and handles payment and settlement.
Deep Dive - Matching Logic
At its core, a stock exchange is a matching engine problem, not a database problem. Hence, the matching logic has to run in memory. Let's formalize how we can perform matching.
Order Book
Each symbol maintains an in-memory order book with price levels and FIFO queues.
Sell Book Example
sellBook[TSLA] = {
101 β Queue[order4, order6, order9]
102 β Queue[order2, order5, order8]
}
- 101 and 102 are price levels.
- Each level contains orders in FIFO order (order4 arrived before order6, which came before order 9, and all three came in at the same price, 101).
- This ensures time priority within the same price.
Buy Book Example
buyBook[AAPL] = {
180 β Queue[order1, order3]
181 β Queue[order7, order11]
}
Logical View of Order Book
SELL SIDE
Price Orders (FIFO)
----------------------
101 order4, order6, order9
102 order5, order8
103 order10
BUY SIDE
Price Orders (FIFO)
----------------------
105 order21, order24
104 order18
103 order10, order11, order15
102 order7
Matching Condition
Matching always checks the best available price first.
Example 1
- Incoming SELL @ 104
- Best Buy Price = 105
- 105 β₯ 104 β β Match possible
Example 2
- Incoming SELL @ 106
- Best Buy Price = 105
- 105 < 106 β β No match
Priority Rules
The order book enforces two-tier priority:
Price Priority
- The higher buy price goes first.
- The lower sell price goes first.
Time Priority (FIFO)
- Earlier orders at the same price execute first.
In-Memory Structure
Since we have to fetch the highest buy orders and the lowest sell orders every time, we can use heap data structures in memory for that.
- Buy side β Max Heap (highest price first)
- Sell side β Min Heap (lowest price first)
Or, better:
- We can have a price-level map, where each price has a FIFO queue of orders (based on arrival time).
Sharding Strategy
All orders for a symbol must go to the same matcher shard.
Example:
- Matcher Shard 1 β AAPL, TSLA, NVDA
- Matcher Shard 2 β MSFT, AMZN, META
- Matcher Shard 3 β GOOG, NFLX
Each shard maintains:
- In-memory order books
- Matching logic
- Deterministic execution
- No cross-shard coordination
Deployment Example
- Matcher Node 1 β TSLA, NVDA
- Matcher Node 2 β AAPL, MSFT
This guarantees:
- Strict ordering per symbol
- No cross-symbol contention
- Horizontal scalability
Deep Dive - Match Execution Service
The Match Execution Service is responsible for turning a matched trade into a financially final transaction.
While the Matcher Service decides who trades with whom, the Execution Service ensures the trade actually happens.
Core Responsibilities
-
Consume matched events from the matched-order topic. Each event contains:
- buy_order_id
- sell_order_id
- symbol
- quantity
- price
- match_id
-
Validate the trade:
- Check the buyer's balance
- Check the seller's holdings
- Ensure the trade has not already been processed (idempotency)
-
Validate trade integrity
-
Perform an atomic settlement. This is the critical part. It must:
- Debit the buyer's funds
- Credit the seller's funds
- Transfer stock ownership
- Mark the trade as settled
All of this must be ACID compliant.
-
-
Store settlements in the Settlement DB (strong consistency required). This DB should:
- Be relational (PostgreSQL / MySQL)
- Support transactions
- Maintain immutable settlements
- match_id
- buyer_id
- seller_id
- amount
- settlement_status (PENDING, SETTLED, FAILED)
- timestamps
-
Idempotency & reliability β Because it consumes events, it must handle duplicate events safely and support retries.
-
Interaction with the Payment Service β In real exchanges, funds are often pre-blocked. However, in a simplified design, the Execution Service:
- β Calls the Payment Service
- β On success, commits the settlement
- β On failure, emits a failure event
Instead of heavy distributed transactions (2PC), modern systems often use:
- Saga pattern
- Idempotent operations
- Compensating actions