Build a Price-Time Matching Engine
Build a Python order book that matches limit orders by price and time priority.
Introduction
30 Second Summary
Buyers expect the best available price. Sellers expect the market to respect who arrived first.
In this project, you will build a deterministic Python matching engine that applies price-time priority to marketable limit orders. You will prove its rules with automated tests before publishing the result as a recruiter-readable portfolio project.
What You'll Build
Your finished demo.py run prints each maker fill in sequence, letting you watch the best available price win before arrival order decides between equal-price orders.
By the end of this project, you'll have:
- A deliberately naive matcher that visibly fills ASK-EXPENSIVE at 101 ticks before ASK-BEST at 100 ticks. This gives you a concrete price-priority bug to explain.
- A deterministic central limit order book that prints trades in the correct price-time sequence. Its final snapshot makes partial fills plus cancellations visible.
- A five-test correctness suite covering priority, residual quantities, cancellation, plus unique order IDs. A public GitHub repository makes the runnable demo, complexity discussion, plus benchmark caveats easy to review.
- Secret Mission: Add an invariant auditor that checks sorted prices, positive quantities, plus an uncrossed book after every state transition.
Are there any prerequisites?
You should be comfortable writing Python functions and classes. You also need Visual Studio Code, Git, plus a GitHub account on Windows.
Before We Start
This is your moment to commit to building a deterministic Python exchange core before the hands-on work begins. You will connect market microstructure rules with software-engineering discipline to create evidence for your quant developer portfolio.
Set Up the Repository
Loose scripts are difficult to reproduce or review. A local Git repository gives your matching-engine work one traceable home.
Visual Studio Code keeps the workspace beside its integrated PowerShell terminal. The final checks confirm that the local toolchain is ready.
In this step, get ready to:
- Open a PowerShell terminal inside Visual Studio Code.
- Create the quant-matching-engine workspace.
- Initialize a local repository with a verified toolchain.
Open the PowerShell terminal
An integrated terminal runs shell commands without leaving your editor. Keeping PowerShell visible also makes each project path easy to confirm.
- Press the Windows key to open Windows search.
- Type Visual Studio Code into the search field.
- Press Enter to open Visual Studio Code.
- Click Terminal in the top menu bar.
- Click New Terminal.
- Check the shell name shown in the terminal toolbar.
- Select PowerShell from the terminal profile menu if another shell is active.
Your command workspace is ready. You'll see PowerShell running in the terminal panel.
Create the project workspace
A dedicated folder prevents matching-engine files from getting mixed with unrelated work. Every project command from this point runs inside quant-matching-engine.
- Create the quant-matching-engine folder and enter it by running these commands:
New-Item -ItemType "Directory" -Path "quant-matching-engine"
Set-Location -Path "quant-matching-engine"
What Do These Commands Do?
- The New-Item command creates the quant-matching-engine directory.
- The Set-Location command moves PowerShell into that directory.
- Check the path shown in the PowerShell prompt.
You'll see quant-matching-engine at the end of the prompt path. Your project now has a dedicated workspace.
Folder Path Look Wrong?
- Confirm that both commands use the exact folder name quant-matching-engine.
- Return to the PowerShell profile if the command syntax is not recognized.
Help me create and enter the quant-matching-engine folder in PowerShell.
Initialize and verify the repository
Git tracks a project by storing repository metadata in a hidden .git directory. Initializing that metadata gives later commits a consistent history.
- Initialize the quant-matching-engine repository by running this command:
git init
What Does This Command Do?
Git creates the hidden .git directory inside quant-matching-engine. That directory holds the repository metadata.
You'll see confirmation that an empty Git repository was initialized.
Repository Not Initialized?
- Confirm that the PowerShell prompt path ends with quant-matching-engine.
- Check that Git is available from the active PowerShell terminal.
Help me initialize the Git repository from the correct PowerShell folder.
- Open the current quant-matching-engine folder in Visual Studio Code by running this command:
code .
How Does the Folder Command Work?
The dot represents the current PowerShell location. Visual Studio Code opens quant-matching-engine as the workspace.
- Switch to the Visual Studio Code window opened by the command.
- Check the folder name shown at the top of the sidebar.
You'll see quant-matching-engine as the current folder. The repository now has a visible home in the editor.
Folder Not Showing in the Editor?
- Confirm that the PowerShell prompt ends with quant-matching-engine.
- Run the folder-opening command again from that prompt.
Help me open my quant-matching-engine folder from the Visual Studio Code PowerShell terminal.
Before you run the final checks, what evidence would prove this workspace is ready?
- Verify the workspace by running these commands:
python --version
git --version
git status
What Do These Checks Prove?
- The first line prints the installed Python version. Python 3.14.7 is the documented reference environment for this project.
- The second line prints the installed Git version.
- The final line checks the current repository state.
You'll see a Python version number. You'll also see a Git version number.
Git reports an initialized repository with no commits. The empty workspace has no project files to track yet.
One of the Final Checks Failed?
- Confirm that PowerShell remains the active terminal shell.
- Return to the prompt whose path ends with quant-matching-engine if Git cannot find the repository.
- Restart Visual Studio Code if an installed tool is unavailable in the terminal.
Help me troubleshoot the Python, Git, or repository status check.
Your local foundation is ready. Next up, you'll make arrival-only matching fail in a way you can see.
Expose the Naive FIFO Bug
Your version-controlled workspace is ready. Now you can test a matching rule in isolation before building the full engine.
A single FIFO list can preserve arrival time while violating price priority. This step uses a deliberately naive Python matcher to make that failure visible.
In this step, get ready to:
- Represent each resting ask with a data class.
- Match incoming quantity by arrival order alone.
- Run a scenario that exposes the price-priority bug.
Model arrival-only orders
Each resting order needs an identifier. It also needs a price in integer ticks. A data class keeps those values together for the matcher.
- In the file sidebar in Visual Studio Code, create naive_match.py inside the existing quant-matching-engine folder.
- Add the resting-order model by pasting this code into naive_match.py:
from dataclasses import dataclass
@dataclass
class NaiveOrder:
order_id: str
price_ticks: int
quantity: int
What does this code do?
- The dataclass decorator creates the standard initialization behavior for each order.
- The order_id field identifies the resting order that receives a fill.
- The price_ticks field stores a discrete price. The quantity field stores the available units.
- Save naive_match.py.
- Confirm the file appears inside quant-matching-engine in the file sidebar.
You should see naive_match.py listed beside the repository files.
File missing from the repository?
Check that the file sits inside quant-matching-engine. Move it into that folder if it appears elsewhere in the file sidebar.
Still stuck? Help me create naive_match.py in my existing VS Code repository
Implement the arrival-order scan
The naive algorithm scans the resting list from the first item to the last. It reduces the incoming quantity until the order is complete.
- In naive_match.py, place your cursor on a blank line below the NaiveOrder class.
- Add the arrival-order matcher by pasting this code:
def match_by_arrival(resting_orders, incoming_quantity):
fills = []
remaining = incoming_quantity
for order in resting_orders:
if remaining == 0:
break
fill_quantity = min(order.quantity, remaining)
fills.append((order.order_id, order.price_ticks, fill_quantity))
remaining -= fill_quantity
return fills
How does the naive matcher work?
- The fills list records the order ID. It also records the execution price. It records the filled quantity too.
- The remaining value tracks how much incoming quantity still needs a match.
- The loop visits each resting order in list order. It never compares their prices.
- The fill_quantity = min(order.quantity, remaining) line prevents either order from filling beyond its available quantity.
- Save naive_match.py.
- Check that Python accepts the current file by running this command in the integrated terminal:
python naive_match.py
What does this check prove?
PowerShell returns to its prompt without a traceback. That confirms Python can load the order model and matcher definitions.
Python flags a syntax problem?
- Check that every line inside NaiveOrder uses the same indentation.
- Check that every line inside match_by_arrival uses consistent indentation.
- Confirm that the final return fills line remains inside the function.
Still stuck? Help me find the syntax issue in my naive Python matcher
Run the competing-price scenario
The experiment places an ask at 101 ticks before an ask at 100 ticks. An incoming quantity of four gives the matcher enough work to expose which rule it follows.
- In naive_match.py, place your cursor on a blank line below return fills.
- Add the scenario and printed trade results by pasting this code:
def main():
resting_asks = [
NaiveOrder("ASK-EXPENSIVE", 101, 3),
NaiveOrder("ASK-BEST", 100, 4),
]
fills = match_by_arrival(resting_asks, incoming_quantity=4)
print("Naive fills:")
for order_id, price_ticks, quantity in fills:
print(f"{order_id}: {quantity} @ {price_ticks} ticks")
print("Problem: arrival order won, even though 100 is the better ask.")
if __name__ == "__main__":
main()
What does this scenario test?
- The resting_asks list stores ASK-EXPENSIVE first. It stores ASK-BEST second.
- The incoming_quantity=4 argument requests four units from the resting asks.
- The print loop displays each fill in the order returned by match_by_arrival.
- Save naive_match.py.
Before you run it, which resting ask do you think this arrival-only scan will fill first?
- Reveal the matching order by running this command in the integrated terminal:
python naive_match.py
What did the run reveal?
- You see ASK-EXPENSIVE: 3 @ 101 ticks first.
- You see ASK-BEST: 1 @ 100 ticks second.
- The final line states that arrival order won even though 100 is the better ask.
The run exposes the intended bug. The matcher follows list position without giving the buyer the best available price first.
Missing the printed fills?
- Save naive_match.py before running it again.
- Check that main() remains indented beneath the final condition.
- Compare the order IDs with ASK-EXPENSIVE and ASK-BEST in the full-code reference below.
Still stuck? Help me diagnose why naive_match.py does not print the expected fills
✔️ Awesome, I've got everything!
Great. Double-check that you saved naive_match.py.
ⓧ I'd like to double check the full code
from dataclasses import dataclass
@dataclass
class NaiveOrder:
order_id: str
price_ticks: int
quantity: int
def match_by_arrival(resting_orders, incoming_quantity):
fills = []
remaining = incoming_quantity
for order in resting_orders:
if remaining == 0:
break
fill_quantity = min(order.quantity, remaining)
fills.append((order.order_id, order.price_ticks, fill_quantity))
remaining -= fill_quantity
return fills
def main():
resting_asks = [
NaiveOrder("ASK-EXPENSIVE", 101, 3),
NaiveOrder("ASK-BEST", 100, 4),
]
fills = match_by_arrival(resting_asks, incoming_quantity=4)
print("Naive fills:")
for order_id, price_ticks, quantity in fills:
print(f"{order_id}: {quantity} @ {price_ticks} ticks")
print("Problem: arrival order won, even though 100 is the better ask.")
if __name__ == "__main__":
main()
You have caught the core priority failure in a runnable example. Next, you will replace the arrival-only scan with a deterministic book that chooses the best price before the oldest order.
Build the Price-Time Matching Engine
Your naive matcher exposed the exact failure this engine needs to solve. Arrival order allowed a more expensive ask to execute before the best available ask.
This step replaces that list with a deterministic central limit order book. The engine chooses the best price first. It preserves arrival order within each price level.
In this step, get ready to:
- Model orders and trades using integer price ticks.
- Implement price priority, FIFO execution, partial fills, cancellation, and book snapshots.
- Run a deterministic demo and measure a synthetic 10,000-order workload.
Define the engine's data model
Each price level uses a FIFO queue so the oldest order at that price stays at the front. Integer ticks keep every price comparison discrete inside the simulator.
- Create `matching_engine.py` inside the `quant-matching-engine` folder using the VS Code file sidebar.
- Add the imports, side constants, and data classes by entering this code:
from collections import deque
from dataclasses import dataclass
BUY = "buy"
SELL = "sell"
@dataclass
class Order:
order_id: str
side: str
price_ticks: int
quantity: int
sequence: int
@dataclass(frozen=True)
class Trade:
maker_order_id: str
taker_order_id: str
price_ticks: int
quantity: int
What does this code model?
- `Order` holds the identity, side, price, remaining quantity, and arrival sequence for an order.
- `Trade` records the resting maker, incoming taker, execution price, and filled quantity.
- `frozen=True` makes each completed trade record immutable.
- `BUY` and `SELL` provide consistent side values throughout the engine.
- Save `matching_engine.py`.
You should see `matching_engine.py` listed beside `naive_match.py` in the repository.
- Add the engine state directly below the `Trade` data class by entering this code:
class MatchingEngine:
def __init__(self):
self.bids = {}
self.asks = {}
self.orders = {}
self.seen_order_ids = set()
self.next_sequence = 0
How is the book stored?
- `bids` and `asks` map integer prices to queues of resting orders.
- `orders` provides direct access to each currently resting order by ID.
- `seen_order_ids` permanently remembers every accepted ID.
- `next_sequence` increases with each accepted submission to represent arrival order.
- Save `matching_engine.py`.
The `MatchingEngine` class should now appear below the two data classes.
- Add `submit_limit()` below `next_sequence` by entering this code:
def submit_limit(self, order_id, side, price_ticks, quantity):
self._validate_order(order_id, side, price_ticks, quantity)
self.seen_order_ids.add(order_id)
self.next_sequence += 1
incoming = Order(
order_id=order_id,
side=side,
price_ticks=price_ticks,
quantity=quantity,
sequence=self.next_sequence,
)
trades = self._match(incoming)
if incoming.quantity > 0:
self._rest(incoming)
return trades
What does submission control?
- Validation happens before the engine changes any state.
- The order ID becomes permanently reserved after the submission is accepted.
- The incoming order tries to match against resting liquidity.
- Any residual quantity joins its own side of the book.
- Save `matching_engine.py`.
The public submission flow now connects validation, matching, and residual order storage.
Does the class structure look misaligned?
Keep `submit_limit()` indented inside `MatchingEngine`. Keep `Order`, `Trade`, and `MatchingEngine` aligned at the left edge.
If the indentation is unclear, help me check the class structure.
Implement matching, cancellation, and snapshots
A marketable order crosses the spread when its limit reaches the best opposite price. Each execution uses the resting maker's price. This produces repeatable state transitions for the same sequence of events.
- Add `cancel()` below `submit_limit()` by entering this code:
def cancel(self, order_id):
order = self.orders.pop(order_id, None)
if order is None:
return False
book = self.bids if order.side == BUY else self.asks
level = book[order.price_ticks]
level.remove(order)
if not level:
del book[order.price_ticks]
return True
How does cancellation work?
- The order lookup identifies the resting order and removes it from active ID storage.
- The order is removed from the queue at its own price level.
- An empty price level disappears from the relevant side of the book.
- A repeated cancellation returns `False` because the order is no longer resting.
- Save `matching_engine.py`.
The engine can now remove a resting order without changing its permanent ID history.
- Add `book_snapshot()` below `cancel()` by entering this code:
def book_snapshot(self):
bids = [
(price, sum(order.quantity for order in self.bids[price]))
for price in sorted(self.bids, reverse=True)
]
asks = [
(price, sum(order.quantity for order in self.asks[price]))
for price in sorted(self.asks)
]
return {"bids": bids, "asks": asks}
What does the snapshot reveal?
- Bid prices appear from highest to lowest because buyers prefer the highest bid.
- Ask prices appear from lowest to highest because buyers prefer the cheapest ask.
- Quantities at one price are aggregated into a single depth value.
- Save `matching_engine.py`.
The snapshot now presents both sides in executable price order.
- Add `_validate_order()` below `book_snapshot()` by entering this code:
def _validate_order(self, order_id, side, price_ticks, quantity):
if not isinstance(order_id, str) or not order_id:
raise ValueError("order_id must be a non-empty string")
if order_id in self.seen_order_ids:
raise ValueError("order_id must be unique")
if side not in (BUY, SELL):
raise ValueError("side must be 'buy' or 'sell'")
if not isinstance(price_ticks, int) or price_ticks <= 0:
raise ValueError("price_ticks must be a positive integer")
if not isinstance(quantity, int) or quantity <= 0:
raise ValueError("quantity must be a positive integer")
Why validate before matching?
Validation prevents malformed events from changing the book. Permanent ID tracking also stops a cancelled or fully filled ID from being reused later.
- Save `matching_engine.py`.
The engine now rejects invalid sides, duplicate IDs, non-positive ticks, and non-positive quantities before processing them.
The matching loop is the largest method in the engine. Add its two consecutive parts before saving so the method remains one complete block of logic.
- Add the first part of `_match()` below `_validate_order()` by entering this code:
def _match(self, incoming):
trades = []
opposite_book = self.asks if incoming.side == BUY else self.bids
while incoming.quantity > 0 and opposite_book:
best_price = (
min(opposite_book)
if incoming.side == BUY
else max(opposite_book)
)
if not self._crosses(incoming, best_price):
break
level = opposite_book[best_price]
resting = level[0]
fill_quantity = min(incoming.quantity, resting.quantity)
How does the engine select liquidity?
- An incoming buy selects the lowest resting ask.
- An incoming sell selects the highest resting bid.
- The first order in the selected queue becomes the maker.
- The smaller remaining quantity determines the next fill size.
- Continue the same `_match()` method directly below `fill_quantity` by entering this code:
trades.append(
Trade(
maker_order_id=resting.order_id,
taker_order_id=incoming.order_id,
price_ticks=resting.price_ticks,
quantity=fill_quantity,
)
)
incoming.quantity -= fill_quantity
resting.quantity -= fill_quantity
if resting.quantity == 0:
level.popleft()
del self.orders[resting.order_id]
if not level:
del opposite_book[best_price]
return trades
How are fills applied?
- Each trade records the resting order's price as the execution price.
- Both orders lose the exact filled quantity.
- A fully filled maker leaves its FIFO queue and active order lookup.
- The loop continues while the incoming order can still trade.
- Save `matching_engine.py`.
The matching loop now consumes the best available makers until the incoming order is filled or stops crossing.
- Add `_crosses()` below `_match()` by entering this code:
def _crosses(self, incoming, best_opposite_price):
if incoming.side == BUY:
return incoming.price_ticks >= best_opposite_price
return incoming.price_ticks <= best_opposite_price
What counts as marketable?
A buy crosses when its limit reaches or exceeds the best ask. A sell crosses when its limit reaches or falls below the best bid.
- Save `matching_engine.py`.
The engine now has an explicit crossing rule for both sides of the book.
- Add `_rest()` below `_crosses()` by entering this code:
def _rest(self, incoming):
own_book = self.bids if incoming.side == BUY else self.asks
level = own_book.setdefault(incoming.price_ticks, deque())
level.append(incoming)
self.orders[incoming.order_id] = incoming
How does residual quantity rest?
Unfilled quantity joins the queue for its limit price. Appending to the end preserves arrival order behind every maker already resting at that price.
- Save `matching_engine.py`.
Your engine now contains the complete submission, matching, resting, cancellation, and snapshot flow.
Seeing an indentation problem in the engine?
- Keep every method from `submit_limit()` through `_rest()` indented one level inside `MatchingEngine`.
- Keep the second part of `_match()` aligned beneath the first part's `while` block.
- Confirm `return trades` aligns with the first statement inside `_match()`.
If the nesting still looks wrong, help me repair the matching loop indentation.
✔️ Awesome, I've got everything!
Great. Save `matching_engine.py` before you build the demo.
ⓧ I'd like to double check the full code
from collections import deque
from dataclasses import dataclass
BUY = "buy"
SELL = "sell"
@dataclass
class Order:
order_id: str
side: str
price_ticks: int
quantity: int
sequence: int
@dataclass(frozen=True)
class Trade:
maker_order_id: str
taker_order_id: str
price_ticks: int
quantity: int
class MatchingEngine:
def __init__(self):
self.bids = {}
self.asks = {}
self.orders = {}
self.seen_order_ids = set()
self.next_sequence = 0
def submit_limit(self, order_id, side, price_ticks, quantity):
self._validate_order(order_id, side, price_ticks, quantity)
self.seen_order_ids.add(order_id)
self.next_sequence += 1
incoming = Order(
order_id=order_id,
side=side,
price_ticks=price_ticks,
quantity=quantity,
sequence=self.next_sequence,
)
trades = self._match(incoming)
if incoming.quantity > 0:
self._rest(incoming)
return trades
def cancel(self, order_id):
order = self.orders.pop(order_id, None)
if order is None:
return False
book = self.bids if order.side == BUY else self.asks
level = book[order.price_ticks]
level.remove(order)
if not level:
del book[order.price_ticks]
return True
def book_snapshot(self):
bids = [
(price, sum(order.quantity for order in self.bids[price]))
for price in sorted(self.bids, reverse=True)
]
asks = [
(price, sum(order.quantity for order in self.asks[price]))
for price in sorted(self.asks)
]
return {"bids": bids, "asks": asks}
def _validate_order(self, order_id, side, price_ticks, quantity):
if not isinstance(order_id, str) or not order_id:
raise ValueError("order_id must be a non-empty string")
if order_id in self.seen_order_ids:
raise ValueError("order_id must be unique")
if side not in (BUY, SELL):
raise ValueError("side must be 'buy' or 'sell'")
if not isinstance(price_ticks, int) or price_ticks <= 0:
raise ValueError("price_ticks must be a positive integer")
if not isinstance(quantity, int) or quantity <= 0:
raise ValueError("quantity must be a positive integer")
def _match(self, incoming):
trades = []
opposite_book = self.asks if incoming.side == BUY else self.bids
while incoming.quantity > 0 and opposite_book:
best_price = (
min(opposite_book)
if incoming.side == BUY
else max(opposite_book)
)
if not self._crosses(incoming, best_price):
break
level = opposite_book[best_price]
resting = level[0]
fill_quantity = min(incoming.quantity, resting.quantity)
trades.append(
Trade(
maker_order_id=resting.order_id,
taker_order_id=incoming.order_id,
price_ticks=resting.price_ticks,
quantity=fill_quantity,
)
)
incoming.quantity -= fill_quantity
resting.quantity -= fill_quantity
if resting.quantity == 0:
level.popleft()
del self.orders[resting.order_id]
if not level:
del opposite_book[best_price]
return trades
def _crosses(self, incoming, best_opposite_price):
if incoming.side == BUY:
return incoming.price_ticks >= best_opposite_price
return incoming.price_ticks <= best_opposite_price
def _rest(self, incoming):
own_book = self.bids if incoming.side == BUY else self.asks
level = own_book.setdefault(incoming.price_ticks, deque())
level.append(incoming)
self.orders[incoming.order_id] = incoming
Run the price-time demo and benchmark
The demo submits three asks before sending one marketable buy through the book. Its benchmark measures elapsed local time with a high-resolution performance counter.
How should you interpret the benchmark?
The event count and trade count are deterministic. Elapsed time and throughput depend on your machine, so they serve as local measurements instead of production performance claims.
- Create `demo.py` inside the `quant-matching-engine` folder using the VS Code file sidebar.
- Add the imports and book printer by entering this code:
import time
from matching_engine import BUY, SELL, MatchingEngine
def print_book(engine, label):
snapshot = engine.book_snapshot()
print(label)
print(f"Bids: {snapshot['bids']}")
print(f"Asks: {snapshot['asks']}")
What does the book printer show?
`print_book()` requests an aggregated snapshot from the engine. It prints bids and asks under a label so you can compare the state before and after matching.
- Save `demo.py`.
You should see `demo.py` listed beside the two existing Python files.
- Add `run_demo()` below `print_book()` by entering this code:
def run_demo():
engine = MatchingEngine()
engine.submit_limit("S1", SELL, 101, 4)
engine.submit_limit("S2", SELL, 100, 5)
engine.submit_limit("S3", SELL, 100, 3)
print_book(engine, "Book before incoming buy:")
trades = engine.submit_limit("B1", BUY, 101, 7)
print("Trades:")
for trade in trades:
print(
f"{trade.maker_order_id}: {trade.quantity} "
f"@ {trade.price_ticks} ticks"
)
print_book(engine, "Book after incoming buy:")
What does this scenario prove?
- `S2` and `S3` share the best ask price of 100 ticks.
- `S2` executes first because it arrived before `S3` at that price.
- The seven-unit buy fully consumes `S2` and partially fills `S3`.
- `S1` remains untouched because the 100-tick liquidity satisfies the incoming quantity.
- Save `demo.py`.
The demo now has a visible scenario for best-price selection and FIFO execution.
- Add `benchmark()` below `run_demo()` by entering this code:
def benchmark(order_count=5_000):
engine = MatchingEngine()
started = time.perf_counter()
for index in range(order_count):
engine.submit_limit(
f"S{index}",
SELL,
100 + (index % 20),
1,
)
trade_count = 0
for index in range(order_count):
trades = engine.submit_limit(f"B{index}", BUY, 119, 1)
trade_count += len(trades)
elapsed = time.perf_counter() - started
event_count = order_count * 2
throughput = event_count / elapsed
print("Benchmark:")
print(f"Processed {event_count:,} orders")
print(f"Trades generated: {trade_count:,}")
print(f"Elapsed: {elapsed:.6f} seconds")
print(f"Throughput: {throughput:,.0f} orders/second")
What does the workload measure?
- The first loop rests 5,000 sell orders across 20 price levels.
- The second loop submits 5,000 marketable buys.
- `time.perf_counter()` measures the elapsed duration across both loops.
- The final calculation reports processed events per elapsed second on your machine.
- Save `demo.py`.
The benchmark now records 10,000 submitted orders and counts the resulting trades.
- Add the script entry point below `benchmark()` by entering this code:
if __name__ == "__main__":
run_demo()
benchmark()
Why add an entry point?
The entry point runs the visible scenario before the benchmark when you execute `demo.py`. Importing the file elsewhere leaves both functions available without running them automatically.
- Save `demo.py`.
Both the deterministic scenario and the local benchmark are now connected to one script run.
✔️ Awesome, I've got everything!
Great. Save `demo.py` before you run the completed engine.
ⓧ I'd like to double check the full code
import time
from matching_engine import BUY, SELL, MatchingEngine
def print_book(engine, label):
snapshot = engine.book_snapshot()
print(label)
print(f"Bids: {snapshot['bids']}")
print(f"Asks: {snapshot['asks']}")
def run_demo():
engine = MatchingEngine()
engine.submit_limit("S1", SELL, 101, 4)
engine.submit_limit("S2", SELL, 100, 5)
engine.submit_limit("S3", SELL, 100, 3)
print_book(engine, "Book before incoming buy:")
trades = engine.submit_limit("B1", BUY, 101, 7)
print("Trades:")
for trade in trades:
print(
f"{trade.maker_order_id}: {trade.quantity} "
f"@ {trade.price_ticks} ticks"
)
print_book(engine, "Book after incoming buy:")
def benchmark(order_count=5_000):
engine = MatchingEngine()
started = time.perf_counter()
for index in range(order_count):
engine.submit_limit(
f"S{index}",
SELL,
100 + (index % 20),
1,
)
trade_count = 0
for index in range(order_count):
trades = engine.submit_limit(f"B{index}", BUY, 119, 1)
trade_count += len(trades)
elapsed = time.perf_counter() - started
event_count = order_count * 2
throughput = event_count / elapsed
print("Benchmark:")
print(f"Processed {event_count:,} orders")
print(f"Trades generated: {trade_count:,}")
print(f"Elapsed: {elapsed:.6f} seconds")
print(f"Throughput: {throughput:,.0f} orders/second")
if __name__ == "__main__":
run_demo()
benchmark()
Before you run the demo, which resting order do you expect to appear in the first trade?
- Run the completed demo in the PowerShell terminal with this command:
python demo.py
What should you see?
The first two trades should be `S2: 5 @ 100 ticks` and `S3: 2 @ 100 ticks`. This confirms best-price selection plus FIFO ordering at 100 ticks.
The remaining ask depth should be `[(100, 1), (101, 4)]`. The benchmark should report `Processed 10,000 orders` and `Trades generated: 5,000`.
Your elapsed time and throughput depend on your computer. The fixed event and trade counts prove that the same workload completed.
Does the demo stop before printing the benchmark?
- Check that `demo.py` imports `BUY`, `SELL`, and `MatchingEngine` from `matching_engine`.
- Confirm every `MatchingEngine` method is indented inside the class.
- Compare the two parts of `_match()` with the complete file to find a misplaced indentation level.
If the script still stops, help me debug my matching engine traceback.
That is the core engine working. Your better prices execute first. Equal-price orders preserve arrival order. Next, you'll turn those rules into automated evidence with five focused tests.
Prove the Engine Is Correct
Your matching engine already produces the expected demo. Plausible output can still hide a priority error.
Automated unit testing turns each matching rule into reviewable evidence. Two tests separate better-price priority from FIFO arrival order.
Partial-fill assertions protect quantity conservation. Cancellation tests protect resting book state.
A final identity check rejects reused order IDs. Together, these tests prove that the engine behaves consistently across five important scenarios.
In this step, get ready to:
- Encode price priority plus FIFO ordering as executable tests.
- Test partial fills plus cancellation behavior.
- Reject duplicate order IDs before running the complete suite.
Encode price and time priority
Price-time priority applies two decisions in sequence. The engine chooses the best available price before using arrival order within that price level.
- Use the new-file control in the VS Code sidebar.
- Enter test_matching_engine.py as the file name.
You should see test_matching_engine.py beside the three existing Python files.
- Create the better-price specification by pasting this code into test_matching_engine.py:
import unittest
from matching_engine import BUY, SELL, MatchingEngine
class MatchingEngineTests(unittest.TestCase):
def test_better_price_executes_before_earlier_order(self):
engine = MatchingEngine()
engine.submit_limit("S1", SELL, 101, 5)
engine.submit_limit("S2", SELL, 100, 5)
trades = engine.submit_limit("B1", BUY, 101, 6)
actual = [
(trade.maker_order_id, trade.price_ticks, trade.quantity)
for trade in trades
]
self.assertEqual(actual, [("S2", 100, 5), ("S1", 101, 1)])
self.assertEqual(engine.book_snapshot()["asks"], [(101, 4)])
What does this test prove?
- The engine receives an earlier sell order at 101 ticks.
- A later sell order offers the better price of 100 ticks.
- The expected trade list places S2 before S1.
- The final snapshot confirms that four units remain at 101 ticks.
- Save test_matching_engine.py.
- Return to the VS Code integrated terminal from earlier.
- Verify the first executable specification by running:
python -m unittest -v test_matching_engine
What should you see?
The terminal lists one passing test. The run ends in OK.
Does the first test fail?
- Confirm that PowerShell is still inside the quant-matching-engine folder.
- Check that test_matching_engine.py imports BUY, SELL, plus MatchingEngine from matching_engine.
Still stuck? Help me debug the better-price priority test.
The next test holds price constant. That isolates arrival order as the only factor that should decide which maker fills first.
- Place your cursor on a blank line after the final assertion in the first test.
- Add the equal-price FIFO specification by pasting:
def test_time_priority_within_one_price_level(self):
engine = MatchingEngine()
engine.submit_limit("S1", SELL, 100, 5)
engine.submit_limit("S2", SELL, 100, 5)
trades = engine.submit_limit("B1", BUY, 100, 6)
actual = [
(trade.maker_order_id, trade.quantity)
for trade in trades
]
self.assertEqual(actual, [("S1", 5), ("S2", 1)])
How does this isolate time priority?
- Both resting orders use the same price of 100 ticks.
- The earlier S1 order supplies the first five units.
- The later S2 order supplies the final unit.
- The expected maker sequence proves FIFO behavior within one price level.
- Save test_matching_engine.py.
- Verify both ordering specifications by running:
python -m unittest -v test_matching_engine
What should you see now?
The terminal lists two passing tests. The completed run ends in OK.
Does the FIFO test fail?
- Check that both resting sell orders use 100 ticks.
- Confirm that S1 is submitted before S2.
- Make sure the new method remains indented inside MatchingEngineTests.
Still stuck? Help me debug the equal-price FIFO test.
Test residual state and cancellation
A correct trade result is only half of a state transition. The remaining order book must also preserve the exact unfilled quantity.
- Place your cursor on a blank line after the equal-price test.
- Add the partial-fill specification by pasting:
def test_partial_fill_leaves_residual_quantity(self):
engine = MatchingEngine()
engine.submit_limit("S1", SELL, 100, 10)
trades = engine.submit_limit("B1", BUY, 100, 4)
self.assertEqual(trades[0].quantity, 4)
self.assertEqual(engine.book_snapshot()["asks"], [(100, 6)])
What does this test protect?
- The resting order begins with ten units at 100 ticks.
- The incoming buy consumes four units.
- The trade quantity records the executed amount.
- The snapshot preserves the six unfilled units.
- Save test_matching_engine.py.
- Verify the residual-quantity specification by running:
python -m unittest -v test_matching_engine
What should you see now?
The terminal lists three passing tests. The result shows that matching preserves the remaining quantity.
Does the partial-fill test fail?
- Confirm that the resting quantity is 10.
- Check that the incoming quantity is 4.
- Verify that the expected snapshot contains six units at 100 ticks.
Still stuck? Help me trace the partial-fill quantities.
Cancellation changes the resting book without generating a trade. Its return value tells the caller whether an active order was actually removed.
- Place your cursor on a blank line after the partial-fill test.
- Add the cancellation specification by pasting:
def test_cancel_removes_a_resting_order(self):
engine = MatchingEngine()
engine.submit_limit("B1", BUY, 99, 3)
self.assertTrue(engine.cancel("B1"))
self.assertEqual(engine.book_snapshot()["bids"], [])
self.assertFalse(engine.cancel("B1"))
What does cancellation guarantee?
- The first cancellation finds the resting B1 order.
- The bid snapshot becomes empty after removal.
- The repeated cancellation returns false because the order no longer rests in the book.
- Save test_matching_engine.py.
- Verify the cancellation specification by running:
python -m unittest -v test_matching_engine
What should you see now?
The terminal lists four passing tests. The cancellation test confirms both successful removal plus safe repeated cancellation.
Does the cancellation test fail?
- Confirm that B1 rests below the opposite side before cancellation.
- Check that the first assertion uses assertTrue.
- Check that the repeated cancellation uses assertFalse.
Still stuck? Help me debug the cancellation state changes.
Reject reused order IDs and run the suite
Order-ID uniqueness makes every accepted event unambiguous. The engine tracks previously seen IDs even after their orders leave the book.
- Place your cursor on a blank line after the cancellation test.
- Complete the test file by pasting the duplicate-ID test plus the direct-run entry point:
def test_duplicate_order_id_is_rejected(self):
engine = MatchingEngine()
engine.submit_limit("S1", SELL, 100, 2)
with self.assertRaises(ValueError):
engine.submit_limit("S1", SELL, 101, 2)
if __name__ == "__main__":
unittest.main()
What does the final specification prove?
- The first submission permanently claims the S1 identifier.
- The second submission reuses that identifier with different order details.
- The test expects submit_limit() to raise ValueError.
- The final entry point also allows the file to run directly.
- Save test_matching_engine.py.
- Compare your completed test file with the reference below.
✔️ Awesome, I've got everything!
Your five test methods are ready. Make sure test_matching_engine.py is saved before the final run.
ⓧ I'd like to double check the full code
import unittest
from matching_engine import BUY, SELL, MatchingEngine
class MatchingEngineTests(unittest.TestCase):
def test_better_price_executes_before_earlier_order(self):
engine = MatchingEngine()
engine.submit_limit("S1", SELL, 101, 5)
engine.submit_limit("S2", SELL, 100, 5)
trades = engine.submit_limit("B1", BUY, 101, 6)
actual = [
(trade.maker_order_id, trade.price_ticks, trade.quantity)
for trade in trades
]
self.assertEqual(actual, [("S2", 100, 5), ("S1", 101, 1)])
self.assertEqual(engine.book_snapshot()["asks"], [(101, 4)])
def test_time_priority_within_one_price_level(self):
engine = MatchingEngine()
engine.submit_limit("S1", SELL, 100, 5)
engine.submit_limit("S2", SELL, 100, 5)
trades = engine.submit_limit("B1", BUY, 100, 6)
actual = [
(trade.maker_order_id, trade.quantity)
for trade in trades
]
self.assertEqual(actual, [("S1", 5), ("S2", 1)])
def test_partial_fill_leaves_residual_quantity(self):
engine = MatchingEngine()
engine.submit_limit("S1", SELL, 100, 10)
trades = engine.submit_limit("B1", BUY, 100, 4)
self.assertEqual(trades[0].quantity, 4)
self.assertEqual(engine.book_snapshot()["asks"], [(100, 6)])
def test_cancel_removes_a_resting_order(self):
engine = MatchingEngine()
engine.submit_limit("B1", BUY, 99, 3)
self.assertTrue(engine.cancel("B1"))
self.assertEqual(engine.book_snapshot()["bids"], [])
self.assertFalse(engine.cancel("B1"))
def test_duplicate_order_id_is_rejected(self):
engine = MatchingEngine()
engine.submit_limit("S1", SELL, 100, 2)
with self.assertRaises(ValueError):
engine.submit_limit("S1", SELL, 101, 2)
if __name__ == "__main__":
unittest.main()
How to use this reference
Compare the method names plus indentation with your file. The five methods must remain inside MatchingEngineTests.
Before you run the complete suite, which matching rule do you expect each test name to prove?
- Verify all five executable specifications by running:
python -m unittest -v test_matching_engine
What should you see?
The terminal reports five passing tests. The complete run ends in OK.
That result proves the engine passes every matching rule encoded in this suite.
Does the complete suite fail?
- Read the failing test name to identify the matching rule that needs attention.
- Compare that test method with the full-file reference above.
- Check that every test method has the same indentation inside MatchingEngineTests.
Still stuck? Help me diagnose the failing matching-engine test.
- Trace test_better_price_executes_before_earlier_order to S2 filling five units at 100 ticks before S1 fills one unit at 101 ticks.
- Trace test_time_priority_within_one_price_level to S1 filling five units before S2 fills one unit at 100 ticks.
- Trace test_partial_fill_leaves_residual_quantity to four executed units plus six resting units.
- Trace test_cancel_removes_a_resting_order to an empty bid snapshot after cancellation.
- Trace test_duplicate_order_id_is_rejected to the rejected second use of S1.
That is the evidence reviewers need: your matching rules now exist as executable specifications. Next, you will document the design plus publish the tested repository on GitHub.
Package and Publish the Portfolio Project
Your five tests now prove the matching rules. The demo shows the engine's deterministic output.
Local code stays invisible to a reviewer. Clear documentation gives them a path to run the project. Its limitations keep the claims honest.
This step packages the project with a recruiter-readable README. You will save the baseline with Git. You will publish it in a public GitHub repository.
In this step, get ready to:
- Create a README that documents the matching policy and design limits.
- Rerun the Python tests and demo before committing the baseline.
- Publish the five project files in a public GitHub repository.
Write the recruiter-readable README
A technical README helps a reviewer understand the project before reading the implementation. This one states the matching rules first because those rules define the engine's behavior.
- In the VS Code file sidebar from earlier, right-click the empty space below your existing files.
- Choose the option for creating a new file.
- Enter README.md as the file name.
- Press the Enter key.
- Add the project overview and matching policy by pasting this block into README.md:
# Price-Time-Priority Matching Engine
A dependency-free Python simulator for a displayed-order central limit order book. It accepts buy and sell limit orders, matches marketable orders, records trades, supports partial fills and cancellations, and exposes aggregated depth snapshots.
## Why this project
Quant developers translate trading rules into deterministic, testable systems. This project focuses on that engineering boundary rather than claiming to reproduce a full production exchange.
## Matching policy
1. Better prices execute first.
2. Orders at the same price execute in arrival order.
3. A trade executes at the resting maker order's price.
4. Unfilled quantity from an incoming limit order rests on its own side of the book.
5. Every order ID is unique for the lifetime of an engine instance.
Prices are represented as integer ticks rather than binary floating-point values.
What Does This Section Establish?
- The opening defines the project as a displayed-order central limit order book simulator.
- The policy turns price-time priority into five reviewable rules.
- Integer ticks keep price comparisons discrete inside the simulator.
- Save README.md.
- Confirm that the editor shows the project title and five numbered matching rules.
Missing the Title or Matching Rules?
- Check that the first line starts with one # character.
- Confirm that all five numbered rules sit below the matching-policy heading.
Still stuck? Help me check the first section of my matching-engine README.
The next section gives reviewers direct commands for reproducing your results. It also maps each file to its responsibility.
- Place your cursor after the integer-ticks paragraph in README.md.
- Add the run instructions and architecture map by pasting this block:
## Run it
Run `python naive_match.py` to see the arrival-only bug.
Run `python demo.py` to see a price-time match, a trade tape, the remaining book, and a local throughput measurement.
Run `python -m unittest -v test_matching_engine` to execute the correctness suite.
Documentation reference: Python 3.14.7 standard-library APIs.
## Architecture
- `naive_match.py` demonstrates the failure of arrival-only matching.
- `matching_engine.py` contains orders, trades, validation, matching, cancellation, and snapshots.
- `demo.py` provides a deterministic scenario and a local benchmark.
- `test_matching_engine.py` encodes the matching rules as automated tests.
How Does This Help a Reviewer?
- The run section gives one command for the deliberate bug and one command for the corrected engine.
- The test command gives reviewers a direct path to the five executable specifications.
- The architecture list connects each project file to a specific responsibility.
- Save README.md.
- Confirm that the editor shows three run commands and four architecture entries.
Missing a Run Command or File?
- Confirm that each command is wrapped in single backticks.
- Check that the architecture list names all four existing Python files.
Need another pair of eyes? Help me verify the run and architecture sections in my README.
Performance claims need context. This project names the current lookup costs so a reviewer can separate the educational implementation from a production exchange.
- Place your cursor after the architecture list in README.md.
- Add the complexity discussion by pasting this block:
## Complexity
Let P be the number of active price levels and Q be the number of orders at one price.
- Best-price selection scans dictionary keys, so it is O(P).
- Appending and removing the oldest order at a price level use opposite ends of a deque.
- Cancellation uses an order-ID lookup followed by a search inside one price level, so it is O(Q).
A production design could replace best-price scans and cancellation searches with specialized ordered maps and intrusive order nodes. That optimization is intentionally outside this project's educational scope.
Why State the Complexity?
- The O(P) statement exposes the scan across active price levels.
- The O(Q) statement exposes the cancellation search inside one price level.
- The final paragraph identifies a possible production direction without claiming that this simulator implements it.
- Save README.md.
- Confirm that the editor shows both O(P) and O(Q) in the complexity section.
Missing a Complexity Term?
- Check that uppercase P represents active price levels.
- Check that uppercase Q represents orders at one price.
Still unsure? Help me explain the O(P) and O(Q) costs in my matching engine.
The closing sections set an honest boundary around the project. They also give reviewers references and a concise resume bullet.
- Place your cursor after the complexity discussion in README.md.
- Complete the README by pasting this block:
## Scope and limitations
This is a simplified, single-process, displayed-order simulator. It does not model concurrency, persistence, fees, market orders, modify or replace messages, self-trade prevention, hidden liquidity, auctions, or venue-specific risk controls. Matching policies vary by venue. CME Group documents FIFO and several pro-rata variants, so this project states its chosen policy explicitly.
The benchmark is a local comparison aid, not a production latency or throughput claim.
## References
- CME Group matching algorithms: https://www.cmegroup.com/confluence/display/EPICSANDBOX/Supported%2BMatching%2BAlgorithms
- Jane Street, How to Build an Exchange: https://www.janestreet.com/tech-talks/building-an-exchange/
## Resume bullet
Built a deterministic Python limit-order-book simulator supporting price-time priority, partial fills, cancellations, trade generation, automated correctness tests, and throughput benchmarking.
What Makes These Claims Honest?
- The limitations list separates the simulator from a full production venue.
- The benchmark statement treats one local result as a comparison aid.
- The references show that matching policies vary across venues.
- The resume bullet summarizes only features implemented in the repository.
- Save README.md.
- Confirm that the final heading is the resume-bullet section.
Does the README End Too Early?
- Confirm that the limitations section includes the local-benchmark caveat.
- Check that both reference links appear before the resume bullet.
Need help comparing sections? Help me find missing content in my completed matching-engine README.
✔️ Awesome, I've got everything!
Your README now gives a reviewer the project rules, run path, architecture, complexity, limitations, references, and resume bullet.
ⓧ I'd like to double check the full code
# Price-Time-Priority Matching Engine
A dependency-free Python simulator for a displayed-order central limit order book. It accepts buy and sell limit orders, matches marketable orders, records trades, supports partial fills and cancellations, and exposes aggregated depth snapshots.
## Why this project
Quant developers translate trading rules into deterministic, testable systems. This project focuses on that engineering boundary rather than claiming to reproduce a full production exchange.
## Matching policy
1. Better prices execute first.
2. Orders at the same price execute in arrival order.
3. A trade executes at the resting maker order's price.
4. Unfilled quantity from an incoming limit order rests on its own side of the book.
5. Every order ID is unique for the lifetime of an engine instance.
Prices are represented as integer ticks rather than binary floating-point values.
## Run it
Run `python naive_match.py` to see the arrival-only bug.
Run `python demo.py` to see a price-time match, a trade tape, the remaining book, and a local throughput measurement.
Run `python -m unittest -v test_matching_engine` to execute the correctness suite.
Documentation reference: Python 3.14.7 standard-library APIs.
## Architecture
- `naive_match.py` demonstrates the failure of arrival-only matching.
- `matching_engine.py` contains orders, trades, validation, matching, cancellation, and snapshots.
- `demo.py` provides a deterministic scenario and a local benchmark.
- `test_matching_engine.py` encodes the matching rules as automated tests.
## Complexity
Let P be the number of active price levels and Q be the number of orders at one price.
- Best-price selection scans dictionary keys, so it is O(P).
- Appending and removing the oldest order at a price level use opposite ends of a deque.
- Cancellation uses an order-ID lookup followed by a search inside one price level, so it is O(Q).
A production design could replace best-price scans and cancellation searches with specialized ordered maps and intrusive order nodes. That optimization is intentionally outside this project's educational scope.
## Scope and limitations
This is a simplified, single-process, displayed-order simulator. It does not model concurrency, persistence, fees, market orders, modify or replace messages, self-trade prevention, hidden liquidity, auctions, or venue-specific risk controls. Matching policies vary by venue. CME Group documents FIFO and several pro-rata variants, so this project states its chosen policy explicitly.
The benchmark is a local comparison aid, not a production latency or throughput claim.
## References
- CME Group matching algorithms: https://www.cmegroup.com/confluence/display/EPICSANDBOX/Supported%2BMatching%2BAlgorithms
- Jane Street, How to Build an Exchange: https://www.janestreet.com/tech-talks/building-an-exchange/
## Resume bullet
Built a deterministic Python limit-order-book simulator supporting price-time priority, partial fills, cancellations, trade generation, automated correctness tests, and throughput benchmarking.
Rerun the evidence and commit the baseline
A published baseline should match the evidence in its README. Rerunning the tests and demo catches any accidental edit before the first commit records the project.
- Rerun the complete correctness suite in the integrated terminal by running:
python -m unittest -v test_matching_engine
What Does This Prove?
The command discovers the five tests in test_matching_engine.py and prints each result. A final OK confirms that the documented rules still pass.
You should see five passing tests followed by OK. Your documentation now has fresh correctness evidence behind it.
Do the Tests Stop Before OK?
- Confirm that the terminal is still inside the quant-matching-engine folder.
- Check that test_matching_engine.py still contains all five test methods.
Need help tracing a failure? Help me diagnose my matching-engine test output.
- Rerun the deterministic scenario and local benchmark by running:
python demo.py
What Does the Demo Confirm?
The scenario confirms best-price execution and FIFO ordering at the shared price. The benchmark confirms deterministic event and trade counts while leaving elapsed time machine-specific.
The first two trades should be S2: 5 @ 100 ticks and S3: 2 @ 100 ticks. The remaining asks should be [(100, 1), (101, 4)].
The benchmark should report Processed 10,000 orders and Trades generated: 5,000. Your elapsed time and throughput depend on your machine.
Does the Demo Show Different Trades?
- Confirm that demo.py still submits S2 before S3 at 100 ticks.
- Check that the incoming B1 order still has a quantity of 7.
Still seeing a mismatch? Help me compare my demo output with the expected price-time scenario.
- Stage all five baseline files by running:
git add .
What Does This Command Do?
This stages the four Python files and README.md for one baseline commit. The commit can now preserve a reviewable project state.
- Record the baseline with the required commit message by running:
git commit -m "Build price-time matching engine"
What Does This Commit Capture?
The commit records the tested engine and its documentation together. Its message names the main accomplishment for anyone reading the repository history.
Did Git Reject the Commit?
- Follow the terminal guidance if Git asks you to configure an author name or email.
- Rerun the commit after Git accepts your author identity.
Need help with the commit? Help me resolve my first Git commit error.
- Rename the current branch to main by running:
git branch -M main
Why Rename the Branch?
This names the local branch main before the first push. The local branch now matches the branch name used by the publishing command.
Publish the repository on GitHub
The remote repository gives reviewers a stable place to discover the project. It must start empty because your local repository already contains the README and commit history.
- Go to GitHub in your browser.
- Select New repository.
- Enter quant-matching-engine in the Repository name field.
- Set the repository visibility to Public.
- Leave the README, license, and gitignore initialization options unselected.
- Click Create repository.
You should see the quick-setup page for an empty quant-matching-engine repository. This empty state is ready to receive your local history.
- Copy the HTTPS repository URL from the quick-setup page.
- Switch back to the PowerShell integrated terminal.
- Add the copied URL as origin by replacing REMOTE-URL in this command:
git remote add origin REMOTE-URL
What Does Origin Represent?
The name origin points your local repository at its GitHub destination. Future push commands can use that short remote name.
Your first HTTPS push may hand authentication to Git Credential Manager. You may see a browser sign-in before the terminal finishes.
- Push the committed main branch by running:
git push -u origin main
What Does This Push Do?
The command sends your local main branch to origin. It also records the upstream relationship for future pushes.
- Complete the GitHub sign-in in your browser if Git Credential Manager opens it.
- Return to the integrated terminal after authentication.
The push should finish without an error. Your local commit is now available from GitHub.
Did the Push Fail?
- Confirm that you copied the HTTPS URL for your own quant-matching-engine repository.
- Complete the browser authentication if Git Credential Manager is waiting for approval.
- Use GitHub's authentication documentation if no browser prompt appears.
Still blocked? Help me troubleshoot my first push to GitHub.
Before you check the published repository, what fetch and push destinations do you expect origin to show?
- Verify the configured remote by running:
git remote -v
What Should the Remote Check Show?
The output should contain one fetch line and one push line for origin. Both lines should point to your public quant-matching-engine repository.
- Return to the GitHub repository page in your browser.
- Refresh the repository page.
You should see README.md, demo.py, matching_engine.py, naive_match.py, and test_matching_engine.py on main. GitHub should render the README beneath the file list.
That is the handoff complete. Your tested matching engine is now discoverable and runnable from a public repository.
Secret mission
Audit Every Book State
Your five example tests prove selected scenarios. Add an invariant auditor that checks every book state after five submissions, one cancellation, and one final marketable order.
Clean Up Your Resources
Clean Up Your Resources
This project has no ongoing costs. Choose whether to keep your resources, pause your work, or delete them entirely.
Resources you used:
- A local Git repository named quant-matching-engine containing the five baseline files plus audit.py.
- A public GitHub repository named quant-matching-engine containing the published project on main.
Keep everything running
No action needed. Choose this if you want to continue improving the matching engine or reviewing its behavior.
- Keep your local quant-matching-engine repository unchanged for future experiments.
- Leave the GitHub repository public so the published code remains available.
- Preserve audit.py so you can extend its invariant checks later.
Pause - I'll come back to this later
Shut down your active editor session to free up memory. You can also hide the remote repository from public view while preserving its history.
- Close the Visual Studio Code window.
- Return to your quant-matching-engine repository on GitHub.
- Open the repository settings page.
- Find the repository visibility area near the bottom of the page.
- Change the repository visibility to private.
- Complete the confirmation prompts.
- Confirm that the repository now appears as private.
Delete - I don't want to use this again
Deleting both copies can feel final. Every action below targets only the quant-matching-engine project.
- Close the Visual Studio Code window.
- Open the Windows file manager through system search.
- Navigate to the parent folder that contains quant-matching-engine.
- Move the quant-matching-engine folder to the Recycle Bin.
- Empty the Recycle Bin.
- Confirm that quant-matching-engine no longer appears in its parent folder.
Your local copy is now removed. The remote repository is the only project resource left.
- Return to your public quant-matching-engine repository on GitHub.
- Open the repository settings page.
- Scroll to the repository deletion area near the bottom of the page.
- Use the repository deletion control.
- Complete the repository-name confirmation prompts.
- Return to your GitHub repositories list.
- Confirm that quant-matching-engine no longer appears in the list.
That completes the cleanup. No project resources remain online or on your disk.
Nice Work!
Nice Work!
That's a wrap! You built a deterministic Python matching engine that applies price-time priority to every order. Its public GitHub repository now gives other people a clear path to run and review it.
You've learned how to:
- Implement a deterministic central limit order book with integer price ticks. Use FIFO queues to preserve arrival order within each price level.
- Turn market rules into five automated unit tests. Prove better-price priority. Confirm equal-price arrival order. Preserve residual quantity after partial fills. Verify cancellation behavior. Reject duplicate order IDs.
- Measure a synthetic 10,000-order workload with a local benchmark. Separate that machine-specific result from a production performance claim. Publish the runnable project in a recruiter-readable GitHub repository.
- Secret Mission: Audit seven state transitions with a reusable invariant auditor. Check descending bids. Check ascending asks. Confirm positive aggregate quantities. Reject a crossed resting book.
Ready to quiz yourself?