Skip to content

Rohan-Singla/prediction-assignment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Order Matching Engine

A toy order matching engine for a prediction market built in Rust.

Screen.Recording.2026-06-24.at.6.33.20.AM.mp4

How to run

Start Kafka:

docker-compose up -d

Start the matching engine worker:

cargo run --bin worker

Start one or more API servers:

PORT=3000 cargo run --bin server
PORT=3001 cargo run --bin server

API

Place an order

POST /orders
{"side": "Buy", "price": 50, "qty": 10}

Get the orderbook

GET /orderbook

WebSocket feed

ws://localhost:3000/ws

Connects and receives fill events in real time.

Design decisions

1. How does the system handle multiple API server instances without double-matching?

All API servers publish incoming orders to a single Kafka topic (orders, 1 partition). A single worker process consumes from this topic under consumer group engine. Kafka guarantees only one consumer in a group reads each message from a partition, so no order is ever processed twice regardless of how many API servers are running.

The worker is the only process that runs the matching engine and holds the orderbook in memory. API servers have no matching logic , They just publish to Kafka and return an order ID.

2. What data structure did you use for the order book and why?

BTreeMap<Reverse<u64>, VecDeque<Order>> for bids and BTreeMap<u64, VecDeque<Order>> for asks.

BTreeMap keeps price levels sorted automatically (No need to sort separately )bids use Reverse<u64> as the key so the highest bid is always at the front, asks use u64 so the lowest ask is always at the front. This means finding the best price is O(1) and no manual sorting is needed.

VecDeque<Order> at each price level maintains time priority , new orders are pushed to the back, matches pop from the front. It shifts the point to next data instead of shifting data .

3. What breaks first under real production load?

The single worker is the bottleneck. It processes orders sequentially from one Kafka partition. At high throughput it will fall behind. Everything downstream fills, snapshots, WebSocket broadcasts will lag.

The orderbook snapshot approach also gets expensive under load. Publishing a full snapshot after every single order means serializing the entire book on every message. With a large book and high order rate this becomes significant overhead.

4. What would you build next with another 4 hours?

Replace the orderbook snapshot topic with a Redis-backed read model. The worker writes only fills and resting order updates to Redis. API servers read from Redis for GET /orderbook. This removes the snapshot overhead from the hot path entirely and decouples the read model from the matching engine.

After that scale the worker horizontally by sharding orders by market symbol across multiple Kafka partitions, one worker per partition, each with its own in-memory book for eg -> kafka-key will be btc-usdc

About

A toy order matching engine for a prediction market built in Rust.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages