Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MatchEngine

MatchEngine is a simple C++20 limit-order matching engine. It keeps an in-memory bid book and ask book, matches incoming orders by price-time priority, and supports adding, cancelling, and modifying active orders.

The current code under src/ exposes a C++ API. It does not currently include a stdin command parser or built-in print function, so the examples below show the intended order flow and the corresponding matching result.

Order Format

An order has this shape:

Order{
    .orderId = 1,
    .side = Side::Bid,
    .priceType = PriceType::Limit,
    .orderType = OrderType::GTC,
    .price = 100,
    .qty = 10,
}

Main fields:

  • Side::Bid: buy order.
  • Side::Ask: sell order.
  • PriceType::Limit: only matches at a compatible price.
  • PriceType::Market: matches available opposite-side liquidity immediately.
  • OrderType::GTC: remaining limit quantity can stay on the book.
  • OrderType::IOC: remaining quantity is cancelled after matching.

Matching Rules

The engine uses price-time priority.

  • Highest bid price matches first.
  • Lowest ask price matches first.
  • At the same price, the oldest order matches first.

A bid can match an ask when:

bid price >= ask price

An ask can match a bid when:

ask price <= bid price

Market orders skip the limit-price check and match against the best available opposite-side orders until filled or until the opposite book is empty.

Example 1: Limit Order Rests

Input:

MatchEngine engine;

engine.addOrder(Order{
    .orderId = 1,
    .side = Side::Bid,
    .priceType = PriceType::Limit,
    .orderType = OrderType::GTC,
    .price = 100,
    .qty = 10,
});

Result:

No match.

Book:
BUY:
100 10

SELL:
empty

The bid has no matching ask, so it rests on the book.

Example 2: Incoming Ask Matches Resting Bid

Input:

MatchEngine engine;

engine.addOrder(Order{
    .orderId = 1,
    .side = Side::Bid,
    .priceType = PriceType::Limit,
    .orderType = OrderType::GTC,
    .price = 100,
    .qty = 10,
});

engine.addOrder(Order{
    .orderId = 2,
    .side = Side::Ask,
    .priceType = PriceType::Limit,
    .orderType = OrderType::GTC,
    .price = 95,
    .qty = 4,
});

Result:

Matched:
order 2 sells 4 against order 1 at resting bid price 100

Book:
BUY:
100 6

SELL:
empty

The ask crosses the best bid because 95 <= 100. Quantity 4 is executed, and the remaining bid quantity is 6.

Example 3: Partial Fill Leaves Residual GTC Quantity

Input:

MatchEngine engine;

engine.addOrder(Order{
    .orderId = 1,
    .side = Side::Ask,
    .priceType = PriceType::Limit,
    .orderType = OrderType::GTC,
    .price = 105,
    .qty = 3,
});

engine.addOrder(Order{
    .orderId = 2,
    .side = Side::Bid,
    .priceType = PriceType::Limit,
    .orderType = OrderType::GTC,
    .price = 110,
    .qty = 8,
});

Result:

Matched:
order 2 buys 3 from order 1 at resting ask price 105

Book:
BUY:
110 5

SELL:
empty

The incoming bid fills the resting ask. Since order 2 is a limit GTC order, its remaining quantity 5 rests on the bid book.

Example 4: IOC Residual Is Cancelled

Input:

MatchEngine engine;

engine.addOrder(Order{
    .orderId = 1,
    .side = Side::Ask,
    .priceType = PriceType::Limit,
    .orderType = OrderType::GTC,
    .price = 105,
    .qty = 3,
});

engine.addOrder(Order{
    .orderId = 2,
    .side = Side::Bid,
    .priceType = PriceType::Limit,
    .orderType = OrderType::IOC,
    .price = 110,
    .qty = 8,
});

Result:

Matched:
order 2 buys 3 from order 1 at resting ask price 105

Cancelled:
remaining quantity 5 from order 2

Book:
BUY:
empty

SELL:
empty

The IOC order can execute immediately, but it never rests on the book.

Example 5: Price-Time Priority

Input:

MatchEngine engine;

engine.addOrder(Order{
    .orderId = 1,
    .side = Side::Ask,
    .priceType = PriceType::Limit,
    .orderType = OrderType::GTC,
    .price = 100,
    .qty = 5,
});

engine.addOrder(Order{
    .orderId = 2,
    .side = Side::Ask,
    .priceType = PriceType::Limit,
    .orderType = OrderType::GTC,
    .price = 100,
    .qty = 7,
});

engine.addOrder(Order{
    .orderId = 3,
    .side = Side::Bid,
    .priceType = PriceType::Limit,
    .orderType = OrderType::GTC,
    .price = 100,
    .qty = 8,
});

Result:

Matched:
order 3 buys 5 from order 1 at resting ask price 100
order 3 buys 3 from order 2 at resting ask price 100

Book:
BUY:
empty

SELL:
100 4

Orders 1 and 2 have the same price, so order 1 is matched first because it arrived earlier.

Cancel And Modify

Cancel input:

engine.cancelOrder(1);

Result:

If order 1 is active, it is removed from the book.
If order 1 is unknown, nothing changes.

Modify input:

engine.modifyOrder(1, 99, 6);

Result:

If order 1 is active:
- its price becomes 99
- its quantity becomes 6
- it is reinserted as a new resting order
- it loses its old time priority

If the modified order crosses the opposite side, it matches immediately.

Build

The source uses C++20.

cmake -S . -B build
cmake --build build

The current CMake setup expects a root-level main.cpp. If your local checkout does not have one, add a small driver program that creates a MatchEngine and submits orders like the examples above.

Text Input Driver

The included main.cpp reads commands from standard input and submits them to MatchEngine.

Command format:

BUY  <LIMIT|MARKET> <GTC|IOC> <price> <qty> <order_id>
SELL <LIMIT|MARKET> <GTC|IOC> <price> <qty> <order_id>
CANCEL <order_id>
MODIFY <order_id> <new_price> <new_qty>

Example input:

SELL LIMIT GTC 105 3 1
SELL LIMIT GTC 106 4 2
BUY LIMIT GTC 110 8 3
BUY LIMIT IOC 110 5 4
MODIFY 3 109 2
CANCEL 2

Corresponding program output:

SUBMITTED SELL 1
SUBMITTED SELL 2
SUBMITTED BUY 3
SUBMITTED BUY 4
MODIFIED 3
CANCELLED 2

Run it with the included test input:

./build/src/main < input.txt

MatchEngine does the actual matching internally. The current public API does not expose trade reports or a book snapshot, so the driver only prints whether each command was submitted to the engine.

About

Simple OOP question about designing an order book / price matching engine.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages