Welcome back for the second tutorial in our series on building a stock market engine using Rust. In our last tutorial, we covered the basics, getting familiar with concepts like orders, order books, and company listings.

Today, our focus is to implement order matching mechanisms. So far, we’ve been storing buy and sell orders in the order book, but now it’s time to run the engine and get the orders matched.

BTreeMap over HashMap

If you followed the first part closely you’d already be familiar that we are using HashMap to store the orders in Orderbook. When I was writing the code about matching orders, I found out that I need to sort the orderbook each time to get me best top prices for selling or buying. It was more efficient at that point that we convert our HashMap into a BTreeMap. The reason to do so is quite here :

HashMap stores key-value pairs in an unordered manner, making lookups fast but not maintaining any specific order. On the other hand, BTreeMap organizes key-value pairs in a sorted order based on keys, allowing efficient retrieval of elements based on their position in the sorted sequence.

Update your OrderBook as below and replace your HashMap with a BTreeMap.

use std::collections::BTreeMap;
pub struct OrderBook {
// BTreeMap : [Key : Price, Value : All the orders at that price]
pub buy_orders: BTreeMap<Decimal, Vec<Order>>,
pub sell_orders: BTreeMap<Decimal, Vec<Order>>,
}

impl OrderBook {
pub fn new() -> OrderBook {
OrderBook {
buy_orders: BTreeMap::new(),
sell_orders: BTreeMap::new(),
}
}
}

Amazing, Now that we have used BTreeMap, We will be getting the orders in a sorted order.

Playing with Prices

Do you remember the best_buy_price and best_sell_price functions which we had crafted in the tutorial 1 ? Now, Let me first explain what is a market order and market price.

Ideally Market Order is such an order where buyer or seller says that execute my order at the price which is currently demanded in the market. And this price is called Market Price.

The price which you always see at the screen is always the price at which the last transaction was made. Based on that price the sentiments of the market are decided and buyers try to put a price below that price and sellers put above it. This is how market works in entiriety.

Let’s write a function to determine the market price. Assume that you want to buy something at market price. You should be matched with the person who’s selling asset at the best price for you and vica versa.

pub fn market_price(&self, order_type: BuyOrSell) -> Option<Decimal> {
match order_type {
BuyOrSell::Buy => self.best_sell_price(),
BuyOrSell::Sell => self.best_buy_price(),
}
}

The buyer at market order will be matched with the best selling price and for seller at market order will be matched with the best buying price.

Matching orders at an exact price every time might not always work because the market is always changing. Sometimes, you might find someone willing to buy or sell shares at a price that’s even better than you expected.

By looking at a range of prices, you increase your chances of getting the best deal possible in the ever-changing stock market. This is the reason why almost every trading platform out there has their orderbook’s top N prices shown.

Refer again from this screenshot by Angel-One super app, You will see top 5 buying prices and top 5 selling prices in sorted order. We will implement similar functionality in our engine so each time you have an order, You get top prices and try to match order against it.

Let’s implement the top_n_buying_prices and top_n_selling_prices methods. Here, I am saying it as Top N becase assume a scenario where you have only 3 buying orders and none of the selling orders. Getting top_5 doesn’t make sense. It could be top 1 or 2 or 3 but at max top_5. That’s why you would see it as top N orders.

impl OrderBook {
pub fn top_n_best_buy_prices(&self) -> Option<Vec<Decimal>> {
let prices = self
.buy_orders
.keys()
.rev()
.take(5) // At Max 5
.cloned()
.collect::<Vec<Decimal>>();
if prices.is_empty() {
None
} else {
Some(prices)
}
}

pub fn top_n_best_sell_prices(&self) -> Option<Vec<Decimal>> {
let prices = self
.sell_orders
.keys()
.take(5) // At Max 5
.cloned()
.collect::<Vec<Decimal>>();
if prices.is_empty() {
None
} else {
Some(prices)
}
}
}

Did you read the code right here ? This is what happened magically when we used BTreeMap in place of HashMap. Imagine if we didn’t used the BTreeMap then we would need to sort the entries each time and then get top_n out of it. With BTreeMap we are always sure that it would be either top n or bottom n. This makes logic and complexity a lot simpler to handle.

Now let’s test what we just wrote out there.

#[test]
fn test_top_n_buy_and_sell_prices(){
// Initialze the new order_book
let mut order_book = OrderBook::new();

// Create some buy orders.
let buy_order_1 = Order::new(dec!(35), dec!(690), BuyOrSell::Buy);
let buy_order_2 = Order::new(dec!(20), dec!(685), BuyOrSell::Buy);
let buy_order_3 = Order::new(dec!(15), dec!(690), BuyOrSell::Buy);

// Create some sell orders.
let sell_order_1 = Order::new(dec!(10), dec!(700), BuyOrSell::Sell);
let sell_order_2 = Order::new(dec!(25), dec!(705), BuyOrSell::Sell);
let sell_order_3 = Order::new(dec!(30), dec!(700), BuyOrSell::Sell);

// Add the orders to the order_book
order_book.add_order_to_orderbook(buy_order_1);
order_book.add_order_to_orderbook(buy_order_2);
order_book.add_order_to_orderbook(buy_order_3);
order_book.add_order_to_orderbook(sell_order_1);
order_book.add_order_to_orderbook(sell_order_2);
order_book.add_order_to_orderbook(sell_order_3);

assert_eq!(
order_book.top_n_best_buy_prices(),
Some(vec![dec!(690), dec!(685)]) // Good for Sellers.
);
assert_eq!(
order_book.top_n_best_sell_prices(),
Some(vec![dec!(700), dec!(705)]) // Good for Buyers.
);
}

As you can see into the assert statements that the prices are already coming in the best possible order. As a seller, The buying price of 690 is better than 685 and as a buyer the selling price of 700 is better than 705 to me.

Also if you notice that our orderbook doesn’t have 5 entries in it so it gives out only two prices in sorted order. That totally justifies top N.

The core matching algorithm

Let’s design the algorithm to match incoming orders with existing orders in the order book.

Matching happens in two steps

  1. Get all the valid best priced orders from the orderbook.
  2. Match the quantity of the incoming order with the valid orders.

Let’s write a wrapper execute_match for matching the quantity of incoming orders with the valid orders.

Here, we are not worried about price since valid_ordersshould ideally be a list of orders that are at prices which we are seeking and will write later moving ahead with our implementation.

Matching against quantity has 3 cases

Partially Matched : Incoming order’s quantity is more than existing order’s quantity.

Eg :
Incoming Order : BUY 30 units at 1000
Orderbook Order : SELL 20 units at 1000

We can sell 20 units to incoming order. Now the incoming order will still remain with 10 units not being bought. But the orderbook’s order will be reduced to 0. We will continue further in the loop to seek the remaining 10 units from some other order.

Fully Matched : Incoming order’s quantity is less than existing order’s quantity.

Eg :
Incoming Order : BUY 30 units at 1000
Orderbook Order : SELL 40 units at 1000

We can easily buy 30 units for incoming order. Now the incoming order will be fully execute and 0 qunatity will remain. 10 units not being bought will still remain in our orderbook. Now since the incoming order is matched already we don’t need to look further and we will just break out of the loop.

Perfect Matched : Incoming order’s quantity is equal to existing order’s quantity.

Eg :
Incoming Order : BUY 30 units at 1000
Orderbook Order : SELL 30 units at 1000

A match made in heaven. We need 30 units and someone gave us exact 30 units. This makes the quantity of both the orders 0 as we match them and break out without looking any further.

use rust_decimal_macros::dec;
impl Orderbook {
fn execute_match(valid_orders: &mut Vec<Order>, incoming_order: &mut Order) {
for order in valid_orders.iter_mut() {
// Partially Matched
if order.quantity < incoming_order.quantity {
incoming_order.quantity -= order.quantity;
order.quantity = dec!(0);
}
// Perfectly Matched
else if order.quantity == incoming_order.quantity {
order.quantity = dec!(0);
incoming_order.quantity = dec!(0);
break;
}
// Fully Matched
else {
order.quantity -= incoming_order.quantity;
incoming_order.quantity = dec!(0);
break;
}
}
}
}

We have now set up the quantity execution mechanism. But what we still need is the list of valid orders.

Selecting Best Price for Market / Limit Orders

We have to take care of Market Orders and the Limit Orders in order to obtain the valid orders from the orderbook.

  • Market Orders : Best possible order at the moment in market.
  • Limit Orders : Best possible match beyond a threshold price.

Let’s write the match_market_order method first. But before that make sure you add #[derive(Clone)] on top of struct Order and enum BuyOrSell .

impl OrderBook {
pub fn match_market_order(&mut self, incoming_order: &mut Order) {
match incoming_order.order_type {
BuyOrSell::Buy => {
let possible_prices = self.top_n_best_sell_prices();
match possible_prices {
Some(prices) => {
for price in prices {
if let Some(orders_at_this_price) = self.sell_orders.get_mut(&price) {
Self::execute_match(orders_at_this_price, incoming_order);
}
if incoming_order.quantity == dec!(0) {
break;
}
}
if incoming_order.quantity != dec!(0) {
// Incoming Order was not fully executed.
self.add_order_to_orderbook(incoming_order.clone());
}
}
None => self.add_order_to_orderbook(incoming_order.clone()),
}
}
BuyOrSell::Sell => {
let possible_prices = self.top_n_best_buy_prices();
match possible_prices {
Some(prices) => {
for price in prices {
if let Some(orders_at_this_price) = self.buy_orders.get_mut(&price) {
Self::execute_match(orders_at_this_price, incoming_order);
}
if incoming_order.quantity == dec!(0) {
break;
}
}
if incoming_order.quantity != dec!(0) {
// Incoming Order was not fully executed.
self.add_order_to_orderbook(incoming_order.clone());
}
}
None => self.add_order_to_orderbook(incoming_order.clone()),
}
}
}
}
}

Determine if the order is to buy or sell : The method first checks whether the incoming order is a buy order or a sell order.

Find best possible price matches: Depending on whether it’s a buy or sell order, the system looks for potential prices in the order book at which we can execute the incoming order. This comes from top_n_buy_prices and top_n_sell_prices methods which we had wrote earlier.

Attempt to match : For each potential match found at a certain price, the system tries to match the incoming order with existing orders at that price. If there are orders at that price, it executes the matching process.

Execute matching : If there are orders at the same price, it tries to match the incoming order with those orders. This means if someone wants to buy at a price that someone else wants to sell, they can make a deal.

Check quantity and repeat if needed : After trying to match with orders at each price level, it checks if the incoming order has been fully executed (meaning all of the quantity has been matched with existing orders). If not, it repeats the process with other potential matches.

Handle unmatched orders : If the incoming order hasn’t been fully executed after checking all potential best possible prices, it means there weren’t enough matching orders. In that case, the remaining part of the order is added to the order book for future matching.

In similar ways, We can write the method for limit order but the only difference is that limit order always looks for a certain threshold.

impl OrderBook {  
pub fn match_limit_order(&mut self, incoming_order: &mut Order) {
match incoming_order.order_type {
BuyOrSell::Buy => {
let possible_prices = self.top_n_best_sell_prices();
match possible_prices {
Some(prices) => {
for price in prices {
if incoming_order.price >= price {
if let Some(orders_at_this_price) = self.sell_orders.get_mut(&price)
{
Self::execute_match(orders_at_this_price, incoming_order);
}
if incoming_order.quantity == dec!(0) {
break;
}
}
}
if incoming_order.quantity != dec!(0) {
// Incoming Order was not fully executed.
self.add_order_to_orderbook(incoming_order.clone());
}
}
None => self.add_order_to_orderbook(incoming_order.clone()),
}
}
BuyOrSell::Sell => {
let possible_prices = self.top_n_best_buy_prices();
match possible_prices {
Some(prices) => {
for price in prices {
if incoming_order.price <= price {
if let Some(orders_at_this_price) = self.buy_orders.get_mut(&price)
{
Self::execute_match(orders_at_this_price, incoming_order);
}
if incoming_order.quantity == dec!(0) {
break;
}
}
}
if incoming_order.quantity != dec!(0) {
// Incoming Order was not fully executed.
self.add_order_to_orderbook(incoming_order.clone());
}
}
None => self.add_order_to_orderbook(incoming_order.clone()),
}
}
}
}
}

The code is almost as same as for market order but the difference is mainly into the price thresholding where we check if the incoming order’s price is above the order price so that we execute it only below certain level of above it.

We have completed writing the core algorithm for the engine. Now it’s time to write some test cases to verify the correct working of the engine. And actually I would say that you would understand even more from the test cases than writing the algorithm. When I was writing algorithms, I have the test cases built up first and based on it the whole thinking was developed.

Testing Full Order Execution

This is the existing orderbook which we will build first

Orderbook Initialization for Testing Full Order Execution

Total buying volume is 25+40+125+100+150 = 440
Total selling volume is 10+50+25+150+120 = 355

Example 1 : Buy 10 units at limit price of 1005

As a buyer, I would always want that my order gets buyed at price of 1005 or lower since this is a limit order. Now if you check the order book there is already an order for selling at price 1005. We can match the orders here.

Thus Incoming Order’s quantity now becomes zero. And the selling volume from the orderbook reduces by 10 units from 355 to 345.

Orderbook after execution of First example

Example 2: Sell 100 units at Market Price

As a seller at market price, I would need my orders to be sold at best available price in the market. Which here is 1004, 1003, 1002 and 1001 in order. The incoming sell order is matched against 65(25+40) units at 1004, 35 units at 1003. Thanks to the market_price method which we wrote

Thus Incoming Order’s quantity now becomes zero. And the buying volume from the orderbook reduces by 100 units from 440 to 340.

Orderbook after execution of Second example

Example 3 : Buy 40 units at 1008 Limit price

As a buyer, I would always want that my order gets buyed at price of 1008 or lower since this is a limit order. Now if you check the order book there is already an order for selling at price 1006. We can match the orders here.

Thus Incoming Order’s quantity now becomes zero. And the selling volume from the orderbook reduces by 10 units from 345 to 305.

Orderbook after execution of Third example

Example 4: Sell 20 units at 1004 Limit price

As a seller at limit price of 1004, I would need my orders to be sold at price above 1004 in market. But there is nothing above or equal to 1004 to buy. So the order cannot be executed and will be added into the orderbook.

Thus Incoming Order’s quantity remains 20. And the selling volume from the orderbook increases by 20 units from 305 to 325.

Orderbook after execution of Fourth example
#[test]
fn test_match_full_order() {
// Initialze the new order_book
let mut order_book = OrderBook::new();

// Create some buy orders.
let buy_order_1 = Order::new(dec!(25), dec!(1004), BuyOrSell::Buy);
let buy_order_2 = Order::new(dec!(40), dec!(1004), BuyOrSell::Buy);
let buy_order_3 = Order::new(dec!(125), dec!(1003), BuyOrSell::Buy);
let buy_order_4 = Order::new(dec!(100), dec!(1002), BuyOrSell::Buy);
let buy_order_5 = Order::new(dec!(150), dec!(1001), BuyOrSell::Buy);

// Create some sell orders.
let sell_order_1 = Order::new(dec!(10), dec!(1005), BuyOrSell::Sell);
let sell_order_2 = Order::new(dec!(50), dec!(1006), BuyOrSell::Sell);
let sell_order_3 = Order::new(dec!(25), dec!(1007), BuyOrSell::Sell);
let sell_order_4 = Order::new(dec!(150), dec!(1008), BuyOrSell::Sell);
let sell_order_5 = Order::new(dec!(120), dec!(1009), BuyOrSell::Sell);

// Add the orders to the order_book
order_book.add_order_to_orderbook(buy_order_1);
order_book.add_order_to_orderbook(buy_order_2);
order_book.add_order_to_orderbook(buy_order_3);
order_book.add_order_to_orderbook(buy_order_4);
order_book.add_order_to_orderbook(buy_order_5);
order_book.add_order_to_orderbook(sell_order_1);
order_book.add_order_to_orderbook(sell_order_2);
order_book.add_order_to_orderbook(sell_order_3);
order_book.add_order_to_orderbook(sell_order_4);
order_book.add_order_to_orderbook(sell_order_5);


assert_eq!(order_book.buy_volume(), Some(dec!(440)));
assert_eq!(order_book.sell_volume(), Some(dec!(355)));

// Example 1 : Buy 10 units at limit price of 1005
let mut incoming_order_1 = Order::new(dec!(10), dec!(1005), BuyOrSell::Buy);
order_book.match_limit_order(&mut incoming_order_1);
assert_eq!(incoming_order_1.quantity, dec!(0));
assert_eq!(order_book.buy_volume(), Some(dec!(440)));
assert_eq!(order_book.sell_volume(), Some(dec!(345)));

// Example 2 : Sell 100 units at market price
let sell_market_price = order_book.market_price(BuyOrSell::Sell).unwrap();
assert_eq!(sell_market_price, dec!(1004));
let mut incoming_order_2 = Order::new(dec!(100), sell_market_price, BuyOrSell::Sell);
order_book.match_market_order(&mut incoming_order_2);
assert_eq!(incoming_order_2.quantity, dec!(0));
assert_eq!(order_book.buy_volume(), Some(dec!(340)));
assert_eq!(order_book.sell_volume(), Some(dec!(345)));

// Example 3 : Buy 40 units at limit price of 1008
let mut incoming_order_3 = Order::new(dec!(40), dec!(1008), BuyOrSell::Buy);
order_book.match_limit_order(&mut incoming_order_3);
assert_eq!(incoming_order_3.quantity, dec!(0));
assert_eq!(order_book.buy_volume(), Some(dec!(340)));
assert_eq!(order_book.sell_volume(), Some(dec!(305)));

// Example 4 : Sell 20 units at limit price of 1004
let mut incoming_order_4 = Order::new(dec!(20), dec!(1004), BuyOrSell::Sell);
order_book.match_limit_order(&mut incoming_order_4);
assert_eq!(incoming_order_4.quantity, dec!(20));
assert_eq!(order_book.buy_volume(), Some(dec!(340)));
assert_eq!(order_book.sell_volume(), Some(dec!(325)));
}

Testing Partial Order Execution

This is the existing orderbook which we will build first

Orderbook initialization for testing partial orders

Total buying volume is 10+25+50+40+120 = 245
Total selling volume is 20+50+30+100+210 = 410

Example 1 : Buy 25 units at limit price of 1005

As a buyer, I would always want that my order gets buyed at price of 1005 or lower since this is a limit order. Now if you check the order book there is already an order for selling at price 1005. We can match the orders here. But it is only 20 units. Remaining 5 units needs to be matched with some other order. But there is no order so eventually it is not possible and thus, We are left with no matching choice and add the order to the orderbook as a buy order of 5 units at price 1005.

Thus Incoming Order’s quantity now becomes 5. And the selling volume from the orderbook reduces by 20 units from 410 to 390. But the buying volume increases in the orderbook by 5 units from 245 to 250.

Orderbook after execution of First Example

Example 2 : Sell 100 units at 1002

As a seller at limit price of 1002, I would need my orders to be sold at price above or equal to 1002 in market. We have 5 units at 1005, 10 units at 1004, 25 units at 1003 and 50 units at 1002 which totals 90 units. Still 10 units are remaining that cannot be sold and will go straight into the orderbook.

Thus Incoming Order’s quantity remains 10. And the buying volume from the orderbook reduces by 90 units from 250 to 160. But the selling volume increases in the orderbook by 10 units from 390 to 400.

Orderbook after execution of Second example

Example 3: Buy 30 units at market price

As a buyer, I would always want that my order gets buyed at lowest price available. From the orderbook we can easily determine that the best possible buying price is for 10 units at 1002, 20 units at 1006.

Thus Incoming Order’s quantity now becomes 0. And the selling volume from the orderbook reduces by 30 units from 400 to 370.

Orderbook after execution of Third example

#[test]
fn test_match_partial_order() {
// Initialze the new order_book
let mut order_book = OrderBook::new();

// Create some buy orders.
let buy_order_1 = Order::new(dec!(10), dec!(1004), BuyOrSell::Buy);
let buy_order_2 = Order::new(dec!(25), dec!(1003), BuyOrSell::Buy);
let buy_order_3 = Order::new(dec!(50), dec!(1002), BuyOrSell::Buy);
let buy_order_4 = Order::new(dec!(40), dec!(1001), BuyOrSell::Buy);
let buy_order_5 = Order::new(dec!(120), dec!(1000), BuyOrSell::Buy);
// Create some sell orders.
let sell_order_1 = Order::new(dec!(20), dec!(1005), BuyOrSell::Sell);
let sell_order_2 = Order::new(dec!(50), dec!(1006), BuyOrSell::Sell);
let sell_order_3 = Order::new(dec!(30), dec!(1007), BuyOrSell::Sell);
let sell_order_4 = Order::new(dec!(100), dec!(1008), BuyOrSell::Sell);
let sell_order_5 = Order::new(dec!(210), dec!(1009), BuyOrSell::Sell);
// Add the orders to the order_book
order_book.add_order_to_orderbook(buy_order_1);
order_book.add_order_to_orderbook(buy_order_2);
order_book.add_order_to_orderbook(buy_order_3);
order_book.add_order_to_orderbook(buy_order_4);
order_book.add_order_to_orderbook(buy_order_5);
order_book.add_order_to_orderbook(sell_order_1);
order_book.add_order_to_orderbook(sell_order_2);
order_book.add_order_to_orderbook(sell_order_3);
order_book.add_order_to_orderbook(sell_order_4);
order_book.add_order_to_orderbook(sell_order_5);

assert_eq!(order_book.buy_volume(), Some(dec!(245)));
assert_eq!(order_book.sell_volume(), Some(dec!(410)));

// Example 1 : Buy 25 units at 1005
let mut incoming_order_1 = Order::new(dec!(25), dec!(1005), BuyOrSell::Buy);
order_book.match_limit_order(&mut incoming_order_1);
assert_eq!(incoming_order_1.quantity, dec!(5));
assert_eq!(order_book.buy_volume(), Some(dec!(250)));
assert_eq!(order_book.sell_volume(), Some(dec!(390)));

// Example 2 : Sell 1002 units at 1002
let mut incoming_order_2 = Order::new(dec!(100), dec!(1002), BuyOrSell::Sell);
order_book.match_limit_order(&mut incoming_order_2);
assert_eq!(incoming_order_2.quantity, dec!(10));
assert_eq!(order_book.buy_volume(), Some(dec!(160)));
assert_eq!(order_book.sell_volume(), Some(dec!(400)));

// Example 3 : Buy 30 units at Market Price
let buy_market_price = order_book.market_price(BuyOrSell::Buy).unwrap();
assert_eq!(buy_market_price, dec!(1002));
let mut incoming_order_3 = Order::new(dec!(30), buy_market_price, BuyOrSell::Buy);
order_book.match_market_order(&mut incoming_order_3);
assert_eq!(incoming_order_3.quantity, dec!(0));
assert_eq!(order_book.buy_volume(), Some(dec!(160)));
assert_eq!(order_book.sell_volume(), Some(dec!(370)));
}

Bonus Test Cases

If you have been connected with the tutorial so far then I think explaining the untradable orders would not be required but here is the test case which is a bonus task for you to explore.

#[test]
fn test_match_untradable_orders() {
// Initialze the new order_book
let mut order_book = OrderBook::new();

// Create some buy orders.
let buy_order_1 = Order::new(dec!(15), dec!(1005), BuyOrSell::Buy);
let buy_order_2 = Order::new(dec!(40), dec!(1002), BuyOrSell::Buy);
let buy_order_3 = Order::new(dec!(90), dec!(1001), BuyOrSell::Buy);
let buy_order_4 = Order::new(dec!(80), dec!(1000), BuyOrSell::Buy);
// Create some sell orders.
let sell_order_1 = Order::new(dec!(75), dec!(1008), BuyOrSell::Sell);
let sell_order_2 = Order::new(dec!(60), dec!(1009), BuyOrSell::Sell);
let sell_order_3 = Order::new(dec!(30), dec!(1010), BuyOrSell::Sell);
let sell_order_4 = Order::new(dec!(210), dec!(1013), BuyOrSell::Sell);
// Add the orders to the order_book
order_book.add_order_to_orderbook(buy_order_1);
order_book.add_order_to_orderbook(buy_order_2);
order_book.add_order_to_orderbook(buy_order_3);
order_book.add_order_to_orderbook(buy_order_4);
order_book.add_order_to_orderbook(sell_order_1);
order_book.add_order_to_orderbook(sell_order_2);
order_book.add_order_to_orderbook(sell_order_3);
order_book.add_order_to_orderbook(sell_order_4);

assert_eq!(order_book.buy_volume(), Some(dec!(225)));
assert_eq!(order_book.sell_volume(), Some(dec!(375)));

// Example 1 : Buy 50 units at limit price of 1007
let mut incoming_order_1 = Order::new(dec!(50), dec!(1007), BuyOrSell::Buy);
order_book.match_limit_order(&mut incoming_order_1);
assert_eq!(incoming_order_1.quantity, dec!(50));
assert_eq!(order_book.buy_volume(), Some(dec!(275)));
assert_eq!(order_book.sell_volume(), Some(dec!(375)));
}

Thanks to https://finlib.in/orders-matched-stock-exchange/ from where I have derived the test cases for the tutorial. Really amazing article and I would say I loved reading that and developing the thinking of how things should be done correctly. Trust me it took me lots of rewrite while covering the aspects here, But the end product is satisfying to me and I hope it remains the same for you too.

Wrap Up

I have already promised that the tutorial would come at max in series of three. I think the engine APIs have been beautifully completed by now and the next part would be to bring the engine to life by polling it into a CLI app or a backend system.

I believe that writing a CLI client would be much better option to do because if you check the bitcoin protocol it works so good on a CLI client using RPC commands.

We can build similar CLI client where in you can loop over the engine and constantly listen up on a controller or a server.

This is probably a 🤝 Call for participation 🤝that If anyone is interested in building the CLI for the application then please reach out on the github repo. We can make this big together.

As far as the stock engine’s core functionality is concerned , I think this is pretty much it to end the tutorial series.

I really hope you liked the series and would leave me a star on ⭐ Github and help making this next big experimental project.

Github : https://github.com/Harshil-Jani/stock_engine_rs

Thanks a lot dear readers
Harshil-Jani

--

--

Harshil Jani

Electronics and Communications Engineer with Computer Science as minors. Working selflessly and seemingly for helping Open Source Softwares to improve.