Skip to content

LYuuss/Rust-Matching-Engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rust Matching Engine

A simplified matching engine written in Rust.

This project is designed as a short, high-signal portfolio project for Rust / trading infrastructure roles. It implements a small but realistic subset of an exchange matching engine:

  • limit buy/sell orders
  • market buy/sell orders
  • price-time priority
  • partial fills
  • resting order book
  • trade log
  • order cancellation
  • order modification
  • deterministic scenario runner
  • engine statistics
  • simple benchmark command
  • indexed order lookup for faster cancellation
  • market orders that consume available liquidity without resting in the book
  • order modifications with priority rules
  • integration tests

Why this project matters

A matching engine is the core component of many trading systems. It receives incoming orders, compares them with the opposite side of the order book, executes trades when prices cross, and stores the remaining quantity when an order is not fully filled.

The goal here is not to build a production exchange. The goal is to show clean Rust, strong data-structure choices, and a solid understanding of trading infrastructure basics.

Technical highlights

  • BTreeMap<Price, VecDeque<Order>> for sorted price levels and FIFO time priority.
  • Limit orders rest in the book when they are not fully matched.
  • Market orders consume the best available opposite-side liquidity and never rest in the book.
  • Integer price representation in cents/ticks instead of floating-point prices.
  • Deterministic order IDs and sequence numbers.
  • Maker/taker trade logs.
  • Partial fill handling on both incoming and resting orders.
  • Scenario runner for reproducible examples.
  • Lightweight benchmark using std::time::Instant.
  • HashMap<OrderId, OrderLocation> index so cancellation and modification can jump directly to the expected side and price level instead of scanning every price level.
  • Modification rules that preserve time priority only for same-price quantity reductions; price changes and quantity increases are cancel/replace operations with a new sequence.

Project structure

rust_matching_engine/
├── Cargo.toml
├── README.md
├── examples/
│   └── basic.txt
├── src/
│   ├── engine.rs
│   ├── lib.rs
│   ├── main.rs
│   ├── order.rs
│   ├── order_book.rs
│   ├── stats.rs
│   ├── trade.rs
│   └── types.rs
└── tests/
    └── matching_tests.rs

Core model

Order

Each order has:

  • an ID
  • a side: BUY or SELL
  • a price
  • an initial quantity
  • a remaining quantity
  • a sequence number used for time priority

Price representation

Prices are represented as integer cents/ticks, not floats.

For example:

100.50 -> 10050
99.01  -> 9901

This avoids floating-point comparison bugs, which matter a lot in trading systems.

Matching rule

The engine uses price-time priority:

  1. Best price wins.
  2. If prices are equal, the oldest resting order wins.

For a BUY order:

match while best_ask <= buy_price

For a SELL limit order:

match while best_bid >= sell_price

For a market order, there is no limit price check: the order keeps consuming the best available opposite-side liquidity until it is fully filled or the opposite book is empty.

For a modification, the engine applies simplified exchange-style priority rules:

  • reducing the remaining quantity at the same price keeps the original time priority
  • increasing quantity or changing price is treated as cancel/replace, so the order receives a new sequence
  • if the modified limit price crosses the opposite side, the modified order can immediately execute as the taker

The execution price is always the maker/resting order price.

Run

cargo run -- demo

Or run the example scenario:

cargo run -- scenario examples/basic.txt

You can also submit a single order to a fresh engine:

cargo run -- submit buy 10 100.50

Or submit a market order to a fresh engine:

cargo run -- submit-market buy 10

Statistics

Inside a scenario, use:

stats

It prints:

  • submitted orders
  • resting orders
  • total trades
  • executed volume
  • resting bid/ask volume
  • best bid / best ask
  • spread
  • notional traded
  • average trade price

Example:

========== ENGINE STATS ==========
Submitted orders     : 4
Resting orders       : 1
Total trades         : 3
Executed volume      : 12
Resting bid volume   : 0
Resting ask volume   : 1
Best bid             : -
Best ask             : 102.00
Spread               : -
Notional traded      : 1204.00
Average trade price  : 100.33
==================================

Market orders

Market orders have no limit price. They immediately consume the best available opposite-side liquidity and never rest in the book.

Example:

submit sell 5 100.00
submit sell 5 101.00
submit-market buy 7

Expected result:

TRADE taker=3 maker=1 qty=5 price=100.00
TRADE taker=3 maker=2 qty=2 price=101.00

If there is not enough liquidity, the remaining market quantity is returned in the response but is not inserted into the order book.

Order modification

Inside a scenario, use:

modify <order_id> <new_quantity> <new_price>

Example:

submit sell 3 101.00
submit buy 5 99.00
modify 2 5 101.00

The buy order originally rests at 99.00. After modification to 101.00, it crosses the resting sell order and executes as the taker:

TRADE taker=2 maker=1 qty=3 price=101.00

The engine keeps priority only for same-price quantity reductions. Price changes and quantity increases are treated as cancel/replace operations, which gives the modified order a new sequence and places it behind older orders at the same price.

Cancellation and modification index

The engine keeps an internal order-location index:

OrderId -> (Side, Price)

This avoids scanning the whole order book when cancelling or modifying an order. The engine can directly jump to the expected side and price level, then update or remove the order from the FIFO queue at that level.

The index is updated when:

  • a resting order is added to the book
  • a resting order is fully filled by an incoming order
  • an order is cancelled
  • an order is modified and reinserted at a new price/priority

This is still simplified compared to a production exchange, but it is a more realistic design than a full linear scan across all price levels.

Benchmark

Run a deterministic benchmark with 100,000 generated orders:

cargo run --release -- benchmark 100000

Or use the default:

cargo run --release -- benchmark

This prints elapsed time, approximate throughput, and final engine statistics.

Scenario file syntax

submit sell 10 100.00
submit buy 4 101.00
submit-market buy 2
modify 1 4 101.00
book 10
trades
stats

Supported scenario commands:

submit <buy|sell> <quantity> <price>
submit-market <buy|sell> <quantity>
modify <order_id> <new_quantity> <new_price>
cancel <order_id>
book [depth]
trades
stats

Run tests

cargo test

The tests cover:

  • non-crossing orders resting in the book
  • buy order crossing best ask
  • partial fills
  • incoming remainder resting after a partial execution
  • price-time priority
  • cancellation
  • cancellation after full and partial fills
  • market buy/sell orders
  • unfilled market quantity not resting in the book
  • order modification priority rules
  • price parsing without floats
  • engine statistics

Example output

> submit sell 10 100.00
SUBMITTED id=1 side=SELL qty=10 price=100.00 remaining=10 trades=0

> submit buy 4 101.00
SUBMITTED id=2 side=BUY qty=4 price=101.00 remaining=0 trades=1
TRADE taker=2 maker=1 qty=4 price=100.00

CV bullet

Built a simplified matching engine in Rust implementing limit and market orders, price-time priority, partial fills, indexed cancellation and modification, trade logs, engine statistics, benchmarking and integration tests.

French version:

Développement d’un mini matching engine en Rust : ordres limit et market buy/sell, priorité prix/temps, exécutions partielles, annulation et modification indexées d’ordres, statistiques moteur, benchmark simple et tests d’intégration.

Next improvements

Good extensions for a stronger portfolio version:

  1. Add order status: accepted, partially filled, filled, cancelled, replaced, rejected.
  2. Add benchmarks with criterion.
  3. Add CSV scenario input/output.
  4. Add a simple TCP or REST API.
  5. Add latency measurements.
  6. Add multi-symbol support, e.g. BTC-USD, ETH-USD.
  7. Add persistent event replay.

About

Built a simplified matching engine in Rust implementing limit orders, price-time priority, partial fills, cancellation, trade logs.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages