DEV Community

Cover image for Designing a High-Performance Cryptocurrency Exchange
Nguyen Nguyen
Nguyen Nguyen

Posted on

Designing a High-Performance Cryptocurrency Exchange

How a modern exchange processes millions of orders with low latency while keeping the architecture scalable.

1. Introduction

Building a cryptocurrency exchange is very different from building a typical web application.

Users expect:

  • Orders to execute within milliseconds.
  • Zero duplicated orders.
  • Strict ordering of events.
  • High throughput during market volatility.
  • Real-time updates for millions of connected users.

Unlike traditional CRUD systems, the matching engine becomes the heart of the platform. Everything else exists to support it.

This article walks through the architecture below and explains why each component exists and which technologies are commonly used in production.

2. High-Level Architecture

The architecture follows one important principle:

Only the Matching Engine is stateful. Everything else should be stateless whenever possible.

Trading System

a. Load Balancer

The load balancer is the public entry point of the exchange.

Responsibilities:

  • Distribute requests across multiple API servers
  • Handle SSL termination
  • Protect against server failures
  • Support horizontal scaling

Common technologies:

  • NGINX
  • HAProxy
  • AWS ALB
  • Envoy

Why?

Trading traffic is unpredictable.

During major market events, request volume can increase by hundreds of times within seconds.

Instead of scaling one giant API server, we simply add more REST API instances.

b. REST API Layer

The REST API layer handles everything related to user interaction.

Examples:

  • Login
  • Authentication
  • Place Order
  • Cancel Order
  • Query Balances
  • Open Orders
  • Trade History

This layer should remain completely stateless.

Typical stack:

  • Go
  • Java
  • Rust
  • Node.js (NestJS)
  • Spring Boot

Responsibilities:

  • JWT verification
  • API validation
  • Rate limiting
  • Authentication
  • Risk checks
  • Forward orders

Notice something important:

The REST API never performs order matching.

Its only job is validating requests and forwarding them.

Because it is stateless, we can run hundreds of API servers behind a load balancer.

c. Communication with the Matching Layer

After validation, orders are forwarded to the matching layer.

Several communication protocols are commonly used.

Option 1 — gRPC

Pros

  • Easy to develop
  • Strong typing
  • Protobuf serialization
  • Excellent developer experience

Cons

  • Higher latency
  • HTTP/2 overhead

Good for:

  • Internal microservices
  • Moderate latency systems

Option 2 — Raw TCP Binary Protocol

Many exchanges implement their own binary protocol.

Instead of JSON:

{
    "symbol":"BTC-USDT",
    "price":105000,
    "qty":1
}

Enter fullscreen mode Exit fullscreen mode

They send something similar to:

[OrderID][Price][Quantity][Side]

Advantages:

  • Extremely small packets
  • Minimal CPU usage
  • Almost zero serialization overhead

This approach is common in high-frequency trading systems.

Option 3 — Aeron

Many modern exchanges and financial systems use Aeron.

Advantages:

  • Very low latency
  • Lock-free design
  • Efficient memory usage
  • Reliable messaging
  • Millions of messages per second

Companies using Aeron include financial trading platforms and low-latency infrastructures.

Communication options

Message Broker (like Kafka) or HTTP API take millisecond for delivering orders, not a good choice in low latency system like Trading System

d. Order Router

The most important key:

Multiple matching engines but only every trading pair owns exactly one matching engine.

Reason: Because order matching requires strict sequencing.
A single writer guarantees:

  • deterministic execution
  • price-time priority
  • no race conditions

e. Event Bus Consumers

Multiple services subscribe to exchange events.

  • Trade History (store orders to Database)
  • WebSocket (notifications)
  • Settle Balance
  • Risk/Fraud Check

Why Event-Driven Architecture?

  • Reduce latency, matching engine no need to wait anything from these tasks
  • Loose coupling
  • Horizontal scalability
  • Fault isolation
  • Easy to add new services
  • Replay historical events

2. Matching Engine

The Matching Engine is the most latency-sensitive component in the entire exchange.

Unlike a typical backend service that spends most of its time waiting for databases or external APIs, the Matching Engine keeps everything in memory and focuses on executing orders with deterministic, microsecond-level latency.

Why Everything Lives in Memory ?

Accessing memory takes tens of nanoseconds.

Reading from PostgreSQL may take hundreds of microseconds.

Reading from disk may take milliseconds.

Since every incoming order must be processed immediately, the order book cannot live inside a database.

Instead, the database is only used for persistence after matching completes.

a. Core Data Structures

The matching engine typically maintains two order books: Bid and Ask

Price Tree

A balanced tree stores all price levels.

Common implementations:

  • Red-Black Tree
  • AVL Tree

Why not HashMap?

Because we constantly need:

  • Highest Bid
  • Lowest Ask

HashMap cannot answer those efficiently.

Tree complexity:

Operation Complexity
Insert Price Level O(log n)
Remove Price Level O(log n)
Find Best Bid O(1)
Find Best Ask O(1)

Most implementations keep pointers to:

`highestBid

lowestAsk`

Therefore finding the best price becomes constant time.

Price Level

Each node inside the tree represents one price.
Orders at the same price are not sorted again.

They simply follow FIFO order following Price-Time Priority.

Order Object

C++
struct Order {

    OrderId id;

    Side side;

    Price price;

    Quantity remainingQty;

    PriceLevel* level;

    Order* prev;

    Order* next;
};
Enter fullscreen mode Exit fullscreen mode

Each order keeps a pointer back to its own price level.

This makes cancellation extremely efficient.

Order Index (HashMap)
Why?

Imagine a cancel request arrives.

Without a HashMap, we search price level from the tree, O(n)
With HashMap, O(1)

b. Matching Algorithm

Suppose the order book looks like:


Bid

105 (5 BTC)

104 (3 BTC)

Ask

106 (4 BTC)

107 (2 BTC)

A new buy order arrives:

BUY

106

3 BTC
Enter fullscreen mode Exit fullscreen mode

The engine compares:

Buy Price >= Best Ask ?

Yes.

So matching begins.

Partial Fill

Suppose


BUY

10 BTC @106

Best ask

4 BTC @106

The result:

BUY

Remaining

6 BTC

Enter fullscreen mode Exit fullscreen mode

The first ask disappears.

The remaining quantity continues matching.

Multiple Price Levels

Example


BUY

10 BTC

Book

106

4 BTC

107

3 BTC

108

5 BTC
Enter fullscreen mode Exit fullscreen mode

Execution:


106

Filled

↓

107

Filled

↓

108
Partially Filled
Enter fullscreen mode Exit fullscreen mode

The engine walks through price levels until:

order completely filled
no more executable prices

c. Inserting a New Order

If the order cannot match:

BUY 105

while

Best Ask =106

Then


Insert

↓

Price Tree

↓

Price Level

↓

FIFO Queue
Enter fullscreen mode Exit fullscreen mode

Complexity:


Tree Insert

O(log n)

Linked List Append

O(1)
Enter fullscreen mode Exit fullscreen mode

d. Cancelling an Order

Cancellation is surprisingly one of the most frequent operations.

Flow:


Cancel Request

↓

HashMap Lookup

↓

Order Pointer

↓

Remove From Linked List

↓

Remove From Tree If Empty
Enter fullscreen mode Exit fullscreen mode

Pseudo-code:


order = orderMap[id];

unlink(order);

if(level.empty())
    tree.remove(level);

orderMap.erase(id);

Enter fullscreen mode Exit fullscreen mode

Complexity:


HashMap lookup:

O(1)

Linked list removal:

O(1)

Tree removal:

O(log n)

Enter fullscreen mode Exit fullscreen mode

e. Why Linked List Instead of Array?

Imagine 50,000 orders exist at one price.

Removing the first order:

Array

Shift 49,999 elements

Linked List

Reconnect two pointers

Constant time.

f. Why Red-Black Tree Instead of Heap?

Many engineers first think about using a heap because we only care about the best bid and best ask.

However, heaps are poorly suited for exchange order books.

A heap excels at retrieving the minimum or maximum element, but it performs poorly when you need to:

Remove an arbitrary price level.
Iterate through consecutive price levels during large market orders.
Delete an empty price level after the last order is canceled or filled.

A balanced tree provides:

  • Ordered traversal.
  • Efficient insertion and deletion.
  • Fast access to neighboring price levels.

This makes it a much better fit for a continuously changing limit order book.

g. Key Design Principles

A production-grade matching engine achieves high performance by combining several simple but carefully chosen data structures:

  • Balanced Trees (Red-Black Tree / AVL Tree): Maintain all price levels in sorted order and provide instant access to the best bid and ask.

  • FIFO Doubly Linked Lists: Preserve price-time priority and allow constant-time insertion and removal of orders at the same price.

  • HashMap:* Enables O(1) lookup for order cancellation and modification without scanning the order book.

  • In-Memory Processing: Keeps the critical execution path free from database latency, while persistence is handled asynchronously through an event log or message broker.

h. Time Complexity Summary

Operation Complexity
Place Order O(log n)
Match Best Price O(1) per price level
Cancel Order O(1) + O(log n)
Find Order O(1)
Find Best Bid O(1)
Find Best Ask O(1)
Append Order at Same Price O(1)
Remove Order from FIFO O(1)

3. Practical Issues

a. Handling many trading pairs

A scalable exchange does not create one operating system process for every trading pair. Instead, it uses market partitioning:

  • Each trading pair is owned by exactly one matching engine at any given time.
  • Each engine can manage many independent order books.
  • Hot markets may receive dedicated engines, while hundreds of low-volume markets share a single engine.
  • The Order Router maintains a routing table (symbol → engine) so incoming orders are always forwarded to the correct owner.

This design strikes a balance between low latency, efficient resource utilization, and horizontal scalability, and is widely adopted by modern cryptocurrency and traditional financial exchanges.

b. Restoring OrderBook from crash

A production-grade matching engine relies on several complementary techniques to recover safely after failures:

  • In-memory order books provide the lowest possible latency during normal operation.
  • Append-only event logs record every state-changing operation in execution order.
  • Periodic snapshots dramatically reduce restart time by avoiding a full replay from genesis.
  • Write-Ahead Logging (WAL) ensures no accepted order is lost even if the process crashes unexpectedly.
  • Sequence numbers guarantee deterministic replay and prevent inconsistent order books.
  • Active-standby replication minimizes downtime by keeping a synchronized backup engine ready to take over.

Together, these techniques allow an exchange to recover quickly while preserving the exact market state, ensuring that every open order, trade, and cancellation remains consistent even after unexpected failures.

c. Preventing Double Spending: Account Locking

A common architectural principle in modern exchanges is:

  • Reserved Balance and Available Balance
  • Account Service owns all account balances and is the only component allowed to modify them.
  • Risk Engine validates whether an order is financially acceptable.
  • Matching Engine never checks balances; it only matches valid orders.
  • Settlement Service updates final balances after trades are executed.

By assigning a single writer to every account, exchanges eliminate race conditions without relying on expensive database locks or complex synchronization primitives. The result is a system that remains both correct and highly scalable under heavy trading workloads.

About me

Nguyen Nguyen - Senior Blockchain Engineer
Website: https://portfolio-nguyen-nguyen-vn.vercel.app/

Top comments (0)