├── main.cpp ├── README.md ├── TestME.h ├── MatchingEngine.h ├── TestME.cpp └── MatchingEngine.cpp /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "MatchingEngine.h" 3 | #include "TestME.h" 4 | 5 | int main(int argc, const char * argv[]) { 6 | 7 | // Initializing the test class for the Matching Engine 8 | TestME a; 9 | a.test_init(); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MatchingEngine 2 | Implemented a Matching engine and an orderbook for a specific product for a stock exchange. 3 | Also implemented a test class that displays the full functionality of the Matching Engine. 4 | 5 | The Matching Engine processes adding and canceling limit orders and executes trade when there is a 6 | cross in the Orderbook. 7 | 8 | Instructions 9 | Compiling and running main.cpp would output all the test cases, demonstrating the full functionality 10 | -------------------------------------------------------------------------------- /TestME.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTME_H 2 | #define TESTME_H 3 | 4 | #include "MatchingEngine.h" 5 | 6 | class TestME{ 7 | public: 8 | TestME(); 9 | void test_init(); 10 | void test_init2(); 11 | 12 | private: 13 | MatchingEngine engine; 14 | void addBuyOrder(int price, int amount); 15 | void removeBuyOrder(int orderId); 16 | void addSellOrder(int price, int amount); 17 | void removeSellOrder(int orderId); 18 | void printTables(); 19 | int demo_orderId; 20 | void test1(); 21 | void test2(); 22 | void test3(); 23 | void test4(); 24 | void test5(); 25 | void test6(); 26 | void test7(); 27 | 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /MatchingEngine.h: -------------------------------------------------------------------------------- 1 | #ifndef MATCHINGENGINE_H 2 | #define MATCHINGENGINE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define PRICE 0 11 | #define QTY 1 12 | #define BUY 2 13 | 14 | typedef int price; 15 | typedef int amount; 16 | typedef int orderId; 17 | typedef int orderDetails; 18 | typedef std::map> priceMap; 19 | 20 | using std::unordered_map; 21 | using std::vector; 22 | using std::string; 23 | 24 | struct orderDet_t{ 25 | int price; 26 | int quantity; 27 | int buy; 28 | orderDet_t(){} 29 | orderDet_t(int a, int b, int c): price(a), quantity(b), buy(c){} 30 | }; 31 | 32 | class MatchingEngine{ 33 | public: 34 | /* Constructors */ 35 | MatchingEngine(); 36 | MatchingEngine(priceMap bTable, priceMap sTable); 37 | 38 | /* Functions for adding and cancelling limit orders */ 39 | int addLimitOrder(int orderId, int price, int qty, bool buy); 40 | int cancelLimitOrder(int orderId); 41 | 42 | /* Prints the order book. Used for testing */ 43 | void printOrderBook(); 44 | /* Prints the order_tables entries(list of all the orders) */ 45 | void printOrdersTable(); 46 | /* Prints the buy and sell tables separately (for testing)) */ 47 | void printBuySellTables(); 48 | 49 | private: 50 | /* Functions called by the exchange on a successful add, cancel or trade */ 51 | void limitOrderAdded(int orderId, int price, int qty); 52 | void limitOrderCancelled(int orderId); 53 | void limitOrderTraded(int orderId, int price, int tradedQty); 54 | 55 | /* Helper function for addLimitOrder to detect and execute potential trade */ 56 | int executeTrade(); 57 | 58 | /* Tables mapping price as a key to buy amounts and sell amounts */ 59 | priceMap buy_table, sell_table; 60 | /* A map of orderID's to array of price, amount and buy flag, updated on add & cancel */ 61 | //unordered_map> orders_table; 62 | unordered_map orders_table; 63 | 64 | }; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /TestME.cpp: -------------------------------------------------------------------------------- 1 | #include "TestME.h" 2 | 3 | TestME::TestME(){ 4 | demo_orderId = 3000; 5 | priceMap bTable = {{98, 50},{99, 1},{97, 8}}; 6 | priceMap sTable = {{103, 20},{101, 10},{102, 5}}; 7 | MatchingEngine a(bTable, sTable); 8 | engine = a; 9 | } 10 | void TestME::test_init(){ 11 | 12 | std::cout << "Initializing tests..."<< std::endl; 13 | 14 | std::cout << "Initial Order Book and Order Table: "<< std::endl; 15 | printTables(); 16 | 17 | test1(); 18 | test2(); 19 | test3(); 20 | test4(); 21 | test5(); 22 | test6(); 23 | test_init2(); 24 | test7(); 25 | } 26 | void TestME::addBuyOrder(int price, int amount){ 27 | std::cout << "Adding a buy limit order - " << "OrderId: " << demo_orderId << ", Price: " << price <<", Qty: "<< amount << std::endl; 28 | if (-1 == engine.addLimitOrder(demo_orderId++, price, amount, true)){ 29 | std::cout << "\nOperation failed. Either invalid orderID or price\n" << std::endl; 30 | return; 31 | } 32 | printTables(); 33 | } 34 | void TestME::removeBuyOrder(int demo_orderId){ 35 | std::cout << "Removing a buy limit order - " << "OrderId: " << demo_orderId << std::endl; 36 | if (-1 == engine.cancelLimitOrder(demo_orderId)){ 37 | std::cout << "\nOperation failed. Either invalid orderID or price\n" << std::endl; 38 | return; 39 | } 40 | printTables(); 41 | 42 | } 43 | void TestME::addSellOrder(int price, int amount){ 44 | std::cout << "Adding a sell limit order - " << "OrderId: " << demo_orderId << ", Price: " << price <<", Qty: "<< amount << std::endl; 45 | if (-1 == engine.addLimitOrder(demo_orderId++, price, amount, false)){ 46 | std::cout << "\nOperation failed. Either invalid orderID or price\n" << std::endl; 47 | return; 48 | } 49 | printTables(); 50 | 51 | } 52 | void TestME::removeSellOrder(int demo_orderId){ 53 | std::cout << "Removing a sell limit order - " << "OrderId: " << demo_orderId << std::endl; 54 | if (-1 == engine.cancelLimitOrder(demo_orderId)){ 55 | std::cout << "\nOperation failed. Either invalid orderID or price\n" << std::endl; 56 | return; 57 | } 58 | printTables(); 59 | } 60 | void TestME::printTables(){ 61 | //std::cout << "Printing current tables..."<< std::endl; 62 | engine.printOrderBook(); 63 | engine.printOrdersTable(); 64 | 65 | } 66 | void TestME::test1(){ 67 | std::cout << "-------- Test 1 -----------" << std::endl << "---------------------------" << std::endl; 68 | addBuyOrder(96, 15); 69 | removeBuyOrder(demo_orderId - 1); 70 | } 71 | void TestME::test2(){ 72 | std::cout << "-------- Test 2 -----------" << std::endl << "---------------------------" << std::endl;; 73 | addBuyOrder(96, 15); 74 | addBuyOrder(96, 10); 75 | removeBuyOrder(demo_orderId - 1); 76 | } 77 | void TestME::test3(){ 78 | std::cout << "-------- Test 3 -----------" << std::endl << "---------------------------" << std::endl;; 79 | addSellOrder(104, 10); 80 | removeSellOrder(demo_orderId - 1); 81 | } 82 | void TestME::test4(){ 83 | std::cout << "-------- Test 4 -----------" << std::endl << "---------------------------" << std::endl;; 84 | addSellOrder(104, 20); 85 | addSellOrder(104, 10); 86 | removeSellOrder(demo_orderId - 2); 87 | } 88 | void TestME::test5(){ 89 | std::cout << "-------- Test 5 (bad price/qty input)-----" << std::endl << "------------------------------------------" << std::endl;; 90 | addSellOrder(-104, 20); 91 | addBuyOrder(94, -10); 92 | 93 | } 94 | void TestME::test6(){ 95 | std::cout << "\n-------- Test 6 (bad orderID input)-----" << std::endl << "-----------------------------------------" << std::endl;; 96 | removeSellOrder(4000); 97 | removeBuyOrder(4000); 98 | } 99 | void TestME::test_init2(){ 100 | priceMap bTable = {{98, 10},{96, 20}}; 101 | priceMap sTable = {{100, 20},{99, 15},{101, 10}}; 102 | MatchingEngine a(bTable, sTable); 103 | engine = a; 104 | std::cout << "\n--------------------------------------------------\nInitializing new Order Book for testing trade..." << std::endl; 105 | printTables(); 106 | 107 | } 108 | void TestME::test7(){ 109 | std::cout << "------- Test 7 - TRADE ------------" << std::endl << "-----------------------------------" << std::endl; 110 | addSellOrder(98, 10); 111 | addBuyOrder(98, 10); 112 | addBuyOrder(99, 5); 113 | addBuyOrder(100, 20); 114 | addSellOrder(100, 10); 115 | addSellOrder(99, 10); 116 | addBuyOrder(100, 35); 117 | addSellOrder(95, 45); 118 | } 119 | -------------------------------------------------------------------------------- /MatchingEngine.cpp: -------------------------------------------------------------------------------- 1 | #include "MatchingEngine.h" 2 | 3 | /* Default Constructor*/ 4 | MatchingEngine::MatchingEngine(){} 5 | /* Constructor that initialize buy and sell tables in the order book*/ 6 | MatchingEngine::MatchingEngine(priceMap bTable, priceMap sTable): buy_table(bTable), sell_table(sTable){} 7 | 8 | /* Uses the buy flag to distinguish whether it is a buy or sell limit order. 9 | Makes an entry into the appropriate table(buy or sell). Records the orderId 10 | and details in orders_table and executes a potential trade. 11 | INPUT: orderId: used in the orders_table and for canceling orders_table 12 | price: price of the share 13 | qty: quanity of the share 14 | buy: boolean value for whether it is a buy limit order, 15 | if false it is a sell limit order 16 | RETURN VALUE: 0 on success, -1 on failure */ 17 | int MatchingEngine::addLimitOrder(int orderId, int price, int qty, bool buy){ 18 | //If the orderId has been used already or if price is less than 0, it's an invalid request 19 | if (price < 0 || orders_table.find(orderId) != orders_table.end() || qty <= 0) 20 | return -1; 21 | 22 | if (buy) 23 | buy_table[price] += qty; 24 | else 25 | sell_table[price] += qty; 26 | 27 | int tradedQty = executeTrade(); 28 | if (tradedQty) // If trade is successful, 29 | limitOrderTraded(orderId, price, tradedQty); 30 | else { // else limit order is added to the order book 31 | orderDet_t det(price, qty, buy); 32 | orders_table[orderId] = det; 33 | // orders_table[orderId] = vector {price, qty, buy}; 34 | limitOrderAdded(orderId, price, qty); 35 | } 36 | std::cout << "Traded quantity is " << tradedQty << std::endl; 37 | return 0; 38 | } 39 | 40 | /* Helper function for addLimitOrder to detect and execute 41 | potential trade. 42 | RETURN VALUE: the quantity traded if there is a possible trade, 43 | 0 otherwise. */ 44 | int MatchingEngine::executeTrade(){ 45 | 46 | auto ib = buy_table.begin(); // Buy limit order iterator 47 | auto is = sell_table.rbegin(); // Sell limit order iterator 48 | //if buy limit order price is less than the sell limit order price, no trade 49 | if (ib == buy_table.end() || is == sell_table.rend() || ib->first < ib->first) 50 | return 0; 51 | int sellLimit = is->first, buyLimit = ib->first, tradedQty = 0, minQty = 0; 52 | //while there is a buy order above sell limit and a sell order below buy limit 53 | while (ib->first >= sellLimit && is->first <= buyLimit){ 54 | 55 | minQty = std::min(ib->second, is->second); 56 | tradedQty += minQty; 57 | ib->second -= minQty; 58 | is->second -= minQty; 59 | 60 | if (ib->second == 0){ 61 | buy_table.erase(ib->first); 62 | ib = buy_table.begin(); 63 | } 64 | if (is->second == 0){ 65 | sell_table.erase(is->first); 66 | is = sell_table.rbegin(); 67 | } 68 | if (ib == buy_table.end() || is == sell_table.rend()) break; 69 | } 70 | return tradedQty; 71 | } 72 | 73 | /* Takes an orderID, looks it up in the order_tables map, retrieves the 74 | details(price, amount and buy flag) and removes it from the buy or sell 75 | tables accordingly in the orderbook. 76 | RETURN VALUE: 0 on success, -1 on failure */ 77 | int MatchingEngine::cancelLimitOrder(int orderId){ 78 | //If the orderId has not been used before, it's an invalid request 79 | if (orders_table.find(orderId) == orders_table.end()) 80 | return -1; 81 | //vector order = orders_table[orderId]; 82 | 83 | orderDet_t * order = &orders_table[orderId]; 84 | 85 | if (order->buy){ 86 | 87 | if ((buy_table[order->price] -= order->quantity) <= 0) 88 | buy_table.erase(order->price); 89 | 90 | } else { 91 | 92 | if ((sell_table[order->price] -= order->quantity) <= 0) 93 | sell_table.erase(order->price); 94 | } 95 | // if (order[BUY]){ 96 | // 97 | // if ((buy_table[order[PRICE]] -= order[QTY]) <= 0) 98 | // buy_table.erase(order[PRICE]); 99 | // 100 | // } else { 101 | // 102 | // if ((sell_table[order[PRICE]] -= order[QTY]) <= 0) 103 | // sell_table.erase(order[PRICE]); 104 | // } 105 | orders_table.erase(orderId); 106 | limitOrderCancelled(orderId); 107 | return 0; 108 | 109 | } 110 | /* Prints the order book. Used for testing */ 111 | void MatchingEngine::printOrderBook(){ 112 | 113 | auto it = buy_table.end(); 114 | --it; 115 | int minPrice = it->first; 116 | auto is = sell_table.begin(); 117 | int maxPrice = is->first; 118 | 119 | std::cout << "\n Order Book " << std::endl; 120 | for (int i = maxPrice; i >= minPrice; i--){ 121 | 122 | string s = ""; 123 | // Print buy amounts 124 | auto it2 = buy_table.find(i); 125 | if (it2 == buy_table.end()) 126 | std::cout << "0 "; 127 | else 128 | std::cout << it2->second << " " ; 129 | 130 | //Print price 131 | std::cout << i << " "; 132 | 133 | //Print sell amounts 134 | auto it3 = sell_table.find(i); 135 | if (it3 == sell_table.end()) 136 | std::cout << "0" << std::endl; 137 | else 138 | std::cout << it3->second << "" << std::endl; 139 | } 140 | } 141 | 142 | /* Prints the order_tables entries(list of all the orders) */ 143 | void MatchingEngine::printOrdersTable(){ 144 | 145 | // std::cout << "\n Orders Table" << std::endl; 146 | // std::cout << "OrderID" << " Price" << " Amount" << " Buy Flag" << std::endl; 147 | // for (auto key : orders_table) 148 | // std::cout << key.first << " " << key.second[PRICE] 149 | // << " " << key.second[QTY] << " " << key.second[BUY] << std::endl; 150 | 151 | std::cout << std::endl << std::endl; 152 | std::cout << "\n Orders Table" << std::endl; 153 | std::cout << "OrderID" << " Price" << " Amount" << " Buy Flag" << std::endl; 154 | for (auto key : orders_table) 155 | std::cout << key.first << " " << key.second.price 156 | << " " << key.second.quantity << " " << key.second.buy << std::endl; 157 | std::cout << std::endl << std::endl; 158 | } 159 | 160 | /* Prints the buy and sell tables separately (for testing)) */ 161 | void MatchingEngine::printBuySellTables(){ 162 | 163 | std::cout << " Buy Table" << std::endl; 164 | for (auto a : buy_table) 165 | std::cout << " " << a.first << " : " << a.second << std::endl; 166 | std::cout << "\n Sell Table" << std::endl; 167 | for (auto b : sell_table) 168 | std::cout << " " << b.first << " : " << b.second << std::endl; 169 | 170 | } 171 | 172 | /* Functions called by the exchange on a successful add, cancel or trade */ 173 | void MatchingEngine::limitOrderAdded(int orderId, int price, int qty){ 174 | /* STUBBED OUT */ 175 | } 176 | 177 | void MatchingEngine::limitOrderCancelled(int orderId){ 178 | /* STUBBED OUT */ 179 | } 180 | 181 | void MatchingEngine::limitOrderTraded(int orderId, int price, int tradedQty){ 182 | /* STUBBED OUT */ 183 | } 184 | --------------------------------------------------------------------------------