Stock Exchange System Design
First let's understand a Stock Exchange System:
- 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 buying and selling price.
Here we will try to 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 price and quantity
- Support both:
- LIMIT Orders (price constrained)
- MARKET Orders (best available price)
- System matches buy and sell orders
- Partial order matching is allowed
- Unmatched orders remain active until:
- Matched
- Expired at end of trading day - usually 4 PM
- Orders expire after market closure
- Matching results in trade execution
Out of Scope
- Listing / delisting stocks
- Advanced order types (stop loss, iceberg, etc.)
Non-Functional Requirements
- Scalability β System must handle large numbers of concurrent orders and users.
- Availability β Browsing stocks and order books must remain available. Platform should remain accessible with minimal downtime.
- Reliability β Orders must not be lost, duplicated, or corrupted.
- Consistency & Integrity β Highly consistent for executing matches. Buy/sell matching and trade records must remain accurate.
Core Concepts
LIMIT Order: Executed only at specified price or better.
MARKET Order: Executed immediately at best available price.
Partial Matching: Orders may be filled in parts, instead of completely at once.
You place a buy order to buy 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 will look like below.

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 approximately there are 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
Consider Stock details takes around 1 kb storage
stocks = 5 mb
stocks = 10 mb
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 be storing price for every minute of Trading Hours.
Trading hours β 6.5 hours/day = 390 minutes/day β 400 minutes / day (assumed for simplicity)
Price record β 100 Bytes (timestamp, price, few other fields)
So storage per day for historical data of 5000 stocks β bytes = 200 mb per day
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 towards 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. Other users can browse stocks. It interacts with the Stock DB and is primarily read-heavy, serving market information to users. We need to have a Relational Database (SQL) for Stock Service as we have structured data, low write frequency, and consistency needed for list/delist 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 High-write optimized NoSQL DB (Key-Value or document DB) for orders, as we will have high order volume, need horizontal scaling and orders are mostly immutable. Order DB can partitioned by Order Id or Stock Id.
Matcher Service
Core matching logic component that 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 have a relational DB for matched order.
Match Execution Service
Finalizes matched trades by updating order status, recording trade details. It also interacts with Payment Service
and handles payment or settlement. We need a Relational Database here as well as we need ACID compliance and strong consistency.

We also must ensure idempotency in this design because:
- Event could be delivered twice
- Downstream service must not process same raw orders or matched orders again
- So each trade should have idepmotent id (like match_id). Downstream service must check if already 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 segregated 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 Order Service all types of orders in the queue. However, let's improve this considering 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 requires to match buy and sell orders of same symbols, 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
- Routes the order to the appropriate symbol-specific queue (e.g., Meta Buy, Meta Sell, Apple Buy,..).
- Matcher Service consumes orders from the symbol-specific buy and sell queues
- Applies price-time matching logic and creates Matched Order
- Matcher Service stores matched trades in the Match DB
- Matcher Service emits a matched event to the matched-order queue
- The Match Execution Service consumes the event handles payment and settlement
Deep Dive - Matching Logic
At its core, a stock exchange is a matching engine problem, not a database problem. Hence, matching logic has to run in memory. Let's formularize 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 has come with same price 101)
- 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
- Higher buy price first
- Lower sell price first
Time Priority (FIFO)
- Earlier orders at same price execute first
In-memory structure
As we have to fetch highest buy orders and 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 Price Level Map. And for each price we will have 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 matched order topic. Each event contains:
- buy_order_id
- sell_order_id
- symbol
- quantity
- price
- match_id
-
Validates Trade
- Check buyer balance
- Check seller holdings
- Ensure trade not already processed (idempotency)
-
Validate trade integrity
-
Perform Atomic Settlement. This is the critical part. It must:
- Debit buyer funds
- Credit seller funds
- Transfer stock ownership
- Mark trade as settled
All of this must be ACID compliant.
-
-
Store Settlements in 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 must support retries.
-
Interaction with Payment Service - In real exchanges funds are often pre-blocked. However, in simplified design Execution Service:
- β Calls Payment Service
- β On success, commits settlement
- β On failure, emits failure event
Instead of heavy distributed transactions (2PC), modern systems often use:
- Saga pattern
- Idempotent operations
- Compensating actions