├── .gitignore ├── diagrams ├── class.png ├── sequence.png ├── sequence.puml └── class.puml ├── src ├── order.cpp ├── marketData.cpp ├── orderBook.cpp ├── matchingEngine.cpp ├── trader.cpp └── orderManagement.cpp ├── include ├── orderDelegate.hpp ├── marketDataDelegate.hpp ├── marketData.hpp ├── matchingEngine.hpp ├── orderBook.hpp ├── TradeGlobals.hpp ├── orderManagement.hpp ├── trader.hpp └── order.hpp ├── CmakeLists.txt ├── README.org └── test └── market_test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | /compile_commands.json 2 | task.md 3 | -------------------------------------------------------------------------------- /diagrams/class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wailo/orderbook-matching-engine/HEAD/diagrams/class.png -------------------------------------------------------------------------------- /diagrams/sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wailo/orderbook-matching-engine/HEAD/diagrams/sequence.png -------------------------------------------------------------------------------- /src/order.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "order.hpp" 3 | #include "orderDelegate.hpp" 4 | using namespace market; 5 | 6 | 7 | order::order(unsigned int p_contractID, unsigned int p_ID, int p_volume, double p_price, orderSide p_side, const std::shared_ptr& p_owner) noexcept 8 | :m_ID(p_ID), 9 | m_contractID(p_contractID), 10 | m_volume(p_volume), 11 | m_price(price_to_cents(p_price)), 12 | m_side(p_side), 13 | m_owner(p_owner) 14 | {} 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /include/orderDelegate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ORDERDELEGATE_H 2 | #define ORDERDELEGATE_H 3 | 4 | #include "TradeGlobals.hpp" 5 | 6 | namespace market 7 | { 8 | 9 | class order; 10 | 11 | class orderDelegate 12 | { 13 | public: 14 | 15 | //! Delegate function. Called when execution is generated 16 | virtual void onOrderExecution(const tradeData& p_orderExcution ) = 0; 17 | 18 | //! Destructor 19 | virtual ~orderDelegate() = default; 20 | 21 | }; 22 | 23 | } // market 24 | 25 | #endif /* ORDERDELEGATE_H */ 26 | -------------------------------------------------------------------------------- /src/marketData.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "marketData.hpp" 4 | #include "marketDataDelegate.hpp" 5 | 6 | using namespace market; 7 | 8 | void marketData::subscribe(std::shared_ptr p_subscriber) 9 | { m_traders.insert(p_subscriber); 10 | } 11 | 12 | 13 | void marketData::publishPublicTrade(const tradeData& p_tradeData) 14 | { 15 | for ( auto& tr : m_traders ) 16 | { 17 | tr->onPublicTrade(p_tradeData); 18 | } 19 | } 20 | 21 | 22 | void marketData::publishOrderBook(const orderBook& p_orderBook) 23 | { 24 | for ( auto& tr : m_traders ) 25 | { 26 | tr->onOrderBook(p_orderBook); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /include/marketDataDelegate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MARKET_DATA_DELEGATE_H 2 | #define MARKET_DATA_DELEGATE_H 3 | 4 | 5 | namespace market 6 | { 7 | 8 | class orderBook; 9 | class tradeData; 10 | 11 | class marketDataDelegate 12 | { 13 | public: 14 | 15 | //! Delegate function. Called when order book is changed 16 | virtual void onOrderBook(const orderBook& p_orderBook) = 0; 17 | 18 | //! Delegate function. Called when public trade is exchanged 19 | virtual void onPublicTrade(const tradeData& p_tradeData) = 0; 20 | 21 | //! Destructor 22 | virtual ~marketDataDelegate() = default; 23 | }; 24 | 25 | 26 | } // market 27 | 28 | #endif // MARKET_DATA_DELEGATE_H 29 | -------------------------------------------------------------------------------- /src/orderBook.cpp: -------------------------------------------------------------------------------- 1 | #include "orderBook.hpp" 2 | 3 | using namespace market; 4 | 5 | bool orderBook::addOrder( order p_order ) 6 | { 7 | 8 | // Orders are stored in heap 9 | if ( p_order.side() == orderSide::BUY ) 10 | { 11 | m_buyOrders.emplace_back(std::move(p_order)); 12 | std::push_heap(m_buyOrders.begin(), m_buyOrders.end(), std::less()); 13 | } 14 | else if ( p_order.side() == orderSide::SELL ) 15 | { 16 | m_sellOrders.emplace_back(std::move(p_order)); 17 | std::push_heap(m_sellOrders.begin(), m_sellOrders.end(), std::greater()); 18 | } 19 | else 20 | { 21 | std::cout << "invalid order type" << std::endl; 22 | return false; 23 | } 24 | 25 | return true; 26 | } 27 | -------------------------------------------------------------------------------- /include/marketData.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MARKETDATA_H 2 | #define MARKETDATA_H 3 | 4 | #include 5 | 6 | namespace market 7 | { 8 | 9 | class tradeData; 10 | class marketDataDelegate; 11 | class tradeData; 12 | class orderBook; 13 | class marketData 14 | { 15 | 16 | public: 17 | //! Default constructor 18 | marketData() = default; 19 | 20 | //! Add subscriber 21 | void subscribe(std::shared_ptr p_subscriber); 22 | 23 | //! Publish public trade 24 | void publishPublicTrade(const tradeData& p_tradeData); 25 | 26 | //! Publish order book 27 | void publishOrderBook(const orderBook& p_orderBook); 28 | 29 | //! Copy constructor 30 | marketData(const marketData &other) = delete; 31 | 32 | //! Move constructor 33 | marketData(marketData &&other) = delete; 34 | 35 | //! Destructor 36 | ~marketData() = default; 37 | 38 | //! Copy assignment operator 39 | marketData& operator=(const marketData &other) = delete; 40 | 41 | //! Move assignment operator 42 | marketData& operator=(marketData &&other) = delete; 43 | 44 | private: 45 | //! Subscriber list 46 | std::unordered_set< std::shared_ptr > m_traders; 47 | 48 | }; 49 | 50 | } // webbbraders 51 | 52 | #endif // MARKETDATA_H 53 | -------------------------------------------------------------------------------- /src/matchingEngine.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "matchingEngine.hpp" 4 | #include "trader.hpp" 5 | #include "orderBook.hpp" 6 | 7 | using namespace market; 8 | 9 | matchingEngine::matchingEngine() 10 | : m_orderManagement(m_marketData), 11 | m_thread([&](){ 12 | while ( m_live ) { 13 | if ( m_newOrder ) 14 | { 15 | m_newOrder = false; 16 | // std::cout << "new order" << std::endl; 17 | m_orderManagement.matchOrders();}}}) 18 | {} 19 | 20 | matchingEngine::~matchingEngine() 21 | { 22 | if (m_live) 23 | { 24 | close(); 25 | } 26 | } 27 | 28 | 29 | bool matchingEngine::addOrder(const std::shared_ptr& p_trader, unsigned int p_contractID, int p_volume, double p_price, orderSide p_side ) 30 | { 31 | 32 | m_newOrder = m_orderManagement.addOrder(p_trader, p_contractID, p_volume, p_price, p_side ); 33 | return m_newOrder; 34 | } 35 | 36 | std::shared_ptr matchingEngine::addTrader() 37 | { 38 | auto _trader = std::make_shared(trader(*this)); 39 | m_marketData.subscribe(_trader); 40 | return _trader; 41 | } 42 | 43 | orderManagement& matchingEngine::getOrderManagement() 44 | { 45 | return m_orderManagement; 46 | } 47 | 48 | void matchingEngine::close() 49 | { 50 | // while (!m_orderMatchingTasks.empty()) 51 | // { 52 | // m_orderMatchingTasks.back().wait(); 53 | // m_orderMatchingTasks.pop_back(); 54 | // } 55 | m_live = false; 56 | m_thread.join(); 57 | m_orderManagement.matchOrders(); 58 | 59 | } 60 | -------------------------------------------------------------------------------- /CmakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | project (matching_engine) 3 | 4 | 5 | # Make your header file accessible to the compiler 6 | 7 | 8 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) 9 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) 10 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib) 11 | 12 | # used by rtags 13 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 14 | # -g for lldb debugging 15 | set(CMAKE_CXX_FLAGS "-g -Wall -std=c++14 -stdlib=libc++") 16 | 17 | file(GLOB SOURCES "src/*.cpp") 18 | 19 | add_library(libmatching_engine SHARED ${SOURCES}) 20 | 21 | enable_testing() 22 | find_package(Boost 1.60 COMPONENTS unit_test_framework) 23 | include_directories (${Boost_INCLUDE_DIRS}) 24 | 25 | ## Test units 26 | file(GLOB TEST_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} test/*.cpp) 27 | #Run through each source 28 | foreach(testSrc ${TEST_SRCS}) 29 | #Extract the filename without an extension (NAME_WE) 30 | get_filename_component(testName ${testSrc} NAME_WE) 31 | 32 | #Add compile target 33 | add_executable(${testName} ${testSrc}) 34 | 35 | #link to Boost libraries AND your targets and dependencies 36 | target_link_libraries(${testName} ${Boost_LIBRARIES} libmatching_engine) 37 | 38 | # move testing binaries into a testBin directory 39 | set_target_properties(${testName} PROPERTIES 40 | RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 41 | 42 | #Add it to test execution - 43 | add_test(NAME ${testName} 44 | WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} 45 | COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${testName}) 46 | endforeach(testSrc) 47 | 48 | 49 | -------------------------------------------------------------------------------- /diagrams/sequence.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | ' skinparam monochrome true 4 | title Matching Engine Sequence Diagram 5 | 6 | create MatchingEngine 7 | 8 | activate MatchingEngine 9 | [o-> MatchingEngine : create 10 | create orderManagement 11 | MatchingEngine -> orderManagement : creates 12 | create orderBook 13 | orderManagement -> orderBook : creates 14 | create marketData 15 | MatchingEngine -> marketData : creates 16 | deactivate MatchingEngine 17 | 18 | 19 | [o-> MatchingEngine : addTrader 20 | activate MatchingEngine 21 | create Trader 22 | MatchingEngine -> Trader : creates 23 | <-MatchingEngine: return handle to trader 24 | deactivate MatchingEngine 25 | 26 | Trader -> MatchingEngine : addOrder 27 | activate MatchingEngine 28 | MatchingEngine -> orderManagement : createOrder 29 | activate orderManagement 30 | orderManagement -> MatchingEngine : status 31 | deactivate orderManagement 32 | MatchingEngine -> marketData : subscribe(Trader) 33 | MatchingEngine -> Trader : status 34 | deactivate MatchingEngine 35 | 36 | orderManagement ->> orderManagement : matchOrders (async) 37 | activate orderManagement 38 | orderManagement -> Trader : onOrderExcecution (if order is matched) 39 | orderManagement -> marketData : PublishPublicTrades 40 | activate marketData 41 | marketData -> Trader: onPublicTrade 42 | deactivate marketData 43 | orderManagement -> marketData : PublishorderBook 44 | activate marketData 45 | marketData -> Trader: onOrderBook 46 | deactivate marketData 47 | deactivate orderManagement 48 | 49 | @enduml 50 | -------------------------------------------------------------------------------- /include/matchingEngine.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "orderManagement.hpp" 4 | #include "marketData.hpp" 5 | 6 | namespace market 7 | { 8 | 9 | class trader; 10 | 11 | class matchingEngine 12 | { 13 | public: 14 | 15 | //! Default constructor 16 | matchingEngine(); 17 | 18 | //! Destructor 19 | ~matchingEngine(); 20 | 21 | //! Copy assignment operator 22 | matchingEngine& operator=(const matchingEngine &other) = delete; 23 | 24 | //! Move assignment operator 25 | matchingEngine& operator=(matchingEngine &&other) = delete; 26 | 27 | //! Copy constructor 28 | matchingEngine(const matchingEngine &other) = delete; 29 | 30 | //! Move constructor 31 | matchingEngine(matchingEngine &&other) = delete; 32 | 33 | //! create and Add trader to this market 34 | std::shared_ptr addTrader(); 35 | 36 | //! orderManagement getter function 37 | orderManagement& getOrderManagement(); 38 | 39 | //! Add order to the market 40 | bool addOrder(const std::shared_ptr& p_trader, unsigned int p_contractID, int p_volume, double p_price, orderSide p_side ); 41 | 42 | //! Open market and start order executions 43 | void open(); 44 | 45 | //! Close market and stop order executions 46 | void close(); 47 | 48 | private: 49 | 50 | //! Market Data API 51 | marketData m_marketData; 52 | 53 | //! Order Management API 54 | orderManagement m_orderManagement; 55 | 56 | //std::vector> m_orderMatchingTasks; 57 | std::future m_orderMatchingTask; 58 | 59 | //! Flag to open the market 60 | std::atomic m_live{true}; 61 | 62 | //! flag to indicate when new order arrives 63 | std::atomic m_newOrder{false}; 64 | 65 | // Order matching thread 66 | std::thread m_thread; 67 | }; 68 | 69 | } // market 70 | -------------------------------------------------------------------------------- /include/orderBook.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ORDERBOOK_H 2 | #define ORDERBOOK_H 3 | 4 | #include 5 | #include 6 | #include "order.hpp" 7 | 8 | namespace market 9 | { 10 | 11 | class order; 12 | 13 | class orderBook 14 | { 15 | public: 16 | 17 | friend class orderManagement; 18 | 19 | //! Default constructor 20 | orderBook() = default; 21 | 22 | //! Destructor 23 | ~orderBook() = default; 24 | 25 | //! Copy constructor 26 | orderBook(const orderBook &other) = default; 27 | 28 | //! Move copy constructor 29 | orderBook(orderBook &&other) = default; 30 | 31 | //! Copy assignment operator 32 | orderBook& operator=(const orderBook &other) = default; 33 | 34 | //! Move assignment operator 35 | orderBook& operator=(orderBook &&other) = default; 36 | 37 | //! Add order 38 | bool addOrder( order p_order ); 39 | 40 | //! Get list of buy orders 41 | inline const std::vector& getBuyOrders() const 42 | { 43 | return m_buyOrders; 44 | } 45 | 46 | //! Get list of sell orders 47 | inline const std::vector getSellOrders() const 48 | { 49 | return m_sellOrders; 50 | } 51 | 52 | //! Get sorted list of buy orders 53 | inline std::vector getBuyOrdersSorted() const 54 | { 55 | auto sorted = m_buyOrders; 56 | std::sort_heap(sorted.begin(), sorted.end(), std::less() ); 57 | return sorted; 58 | } 59 | 60 | //! Get sorted list of sell orders 61 | inline std::vector getSellOrdersSorted() const 62 | { 63 | auto sorted = m_sellOrders; 64 | std::sort_heap(sorted.begin(), sorted.end(), std::greater() ); 65 | return sorted; 66 | } 67 | 68 | 69 | private: 70 | 71 | //! Buys orders list in heap data structure 72 | std::vector m_buyOrders; 73 | 74 | //! Buys orders list in heap data structure 75 | std::vector m_sellOrders; 76 | 77 | }; 78 | 79 | } // market 80 | 81 | #endif /* ORDERBOOK_H */ 82 | -------------------------------------------------------------------------------- /include/TradeGlobals.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TRADEGLOBALS_H 2 | #define TRADEGLOBALS_H 3 | #include 4 | 5 | namespace market 6 | { 7 | 8 | //! Order execution state 9 | enum class orderExecutionState {FILLED, PARTIAL}; 10 | 11 | 12 | class tradeData 13 | { 14 | public: 15 | tradeData(unsigned int p_contractID, unsigned int p_orderID, int p_tradedVolume, int p_price): 16 | m_timeStamp(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())), 17 | m_contractID(p_contractID), 18 | m_orderID(p_orderID), 19 | m_tradedVolume(p_tradedVolume), 20 | m_price(p_price), 21 | m_state(p_tradedVolume? orderExecutionState::PARTIAL : orderExecutionState::FILLED) 22 | {} 23 | 24 | //! Helper function. Converts order execution state to string 25 | std::string orderExecutionStateToString() const 26 | { 27 | switch (m_state) 28 | { 29 | case orderExecutionState::FILLED: 30 | return "FILLED"; 31 | break; 32 | 33 | case orderExecutionState::PARTIAL: 34 | return "PARTIAL"; 35 | break; 36 | 37 | default: 38 | return "UNKNOWN ORDER EXECUTION STATE"; 39 | break ; 40 | } 41 | } 42 | 43 | 44 | //! Helper function to print trade data 45 | inline std::string toString() 46 | { 47 | return std::string(timeStamp() + " " + orderExecutionStateToString() + " " +std::to_string(m_tradedVolume) + "@" + std::to_string(m_price/100.0)); 48 | } 49 | 50 | //! Trade time Stamp 51 | const std::time_t m_timeStamp; 52 | 53 | //! Trade contract ID 54 | const unsigned int m_contractID{0}; 55 | 56 | //! Trade order ID 57 | const unsigned int m_orderID{0}; 58 | 59 | //! Trade volume 60 | const int m_tradedVolume{0}; 61 | 62 | //! Trade price 63 | const int m_price{0}; 64 | 65 | //! Trade volume state 66 | const orderExecutionState m_state; 67 | 68 | private: 69 | 70 | // Get time stamp as string 71 | inline std::string timeStamp() const 72 | { 73 | return std::ctime(&m_timeStamp); 74 | } 75 | 76 | }; 77 | 78 | } // market 79 | 80 | #endif /* TRADEGLOBALS_H */ 81 | -------------------------------------------------------------------------------- /include/orderManagement.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ORDER_MANAGEMENT_H 2 | #define ORDER_MANAGEMENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "order.hpp" 9 | #include 10 | 11 | namespace market 12 | { 13 | 14 | class marketData; 15 | class orderDelegate; 16 | class orderBook; 17 | 18 | class orderManagement 19 | { 20 | public: 21 | 22 | //! Constructor 23 | orderManagement(marketData& p_delegate) noexcept; 24 | 25 | //! Send Order, return true if order request succeed, false otherwise 26 | bool addOrder(std::shared_ptr p_trader, unsigned int p_contractID, int volume, double price, orderSide side); 27 | 28 | //! Match Orders 29 | bool matchOrders(); 30 | 31 | //! Copy constructor 32 | orderManagement(const orderManagement &other) = delete; 33 | 34 | //! Move constructor 35 | orderManagement(orderManagement &&other) = delete; 36 | 37 | //! Destructor 38 | ~orderManagement() = default; 39 | 40 | //! Copy assignment operator 41 | orderManagement& operator=(const orderManagement &other) = delete; 42 | 43 | //! Move assignment operator 44 | orderManagement& operator=(orderManagement &&other) = delete; 45 | 46 | 47 | //! For unit tests: total traded volume get functions 48 | inline unsigned int totalTradedVolume() const 49 | { 50 | return m_totalTradedVolume; 51 | } 52 | 53 | //! For unit tests: total received orders volume get functions 54 | inline unsigned int totalVolume() const 55 | { 56 | return m_totalVolume; 57 | } 58 | 59 | 60 | private: 61 | 62 | //! order unique ID 63 | unsigned int m_orderID{1}; 64 | 65 | // Handle to Market Data API 66 | marketData& m_delegate; 67 | 68 | // Order Book list 69 | std::unordered_map m_orderBooks; 70 | 71 | //! For unit tests: Accumulative count of traded volume 72 | unsigned int m_totalTradedVolume{0}; 73 | 74 | //! For unit tests: Accumulative count of received order volume 75 | unsigned int m_totalVolume{0}; 76 | 77 | //! Single Producer Single Consumer orders storage 78 | boost::lockfree::spsc_queue> m_queue; 79 | 80 | }; 81 | 82 | } // market 83 | 84 | 85 | #endif // ORDER_MANAGEMENT_H 86 | -------------------------------------------------------------------------------- /include/trader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TRADER_H 2 | #define TRADER_H 3 | 4 | #include "marketDataDelegate.hpp" 5 | #include "orderDelegate.hpp" 6 | 7 | 8 | namespace market 9 | { 10 | 11 | class matchingEngine; 12 | class orderBook; 13 | enum class orderSide; 14 | 15 | class trader : 16 | public std::enable_shared_from_this, 17 | public marketDataDelegate, 18 | public orderDelegate 19 | { 20 | public: 21 | 22 | //! Default constructor 23 | trader(matchingEngine& p_market) noexcept; 24 | 25 | //! Send Order, return true if order request succeed, false otherwise 26 | bool sendOrder(unsigned int p_contractID, int p_volume, double p_price, orderSide p_side); 27 | 28 | //! Destructor 29 | ~trader() = default; 30 | 31 | //! Set a function to be invoked when order execution update is received. 32 | void setOnOrderExecutionCallBack (std::function); 33 | 34 | //! Set a function to be invoked when public trade update is received. 35 | void setOnPublicTradeCallBack (std::function); 36 | 37 | //! Set a function to be invoked when order book update is received. 38 | void setOnOrderBookCallBack (std::function); 39 | 40 | private: 41 | 42 | //! Trader unique ID 43 | unsigned int m_ID{0}; 44 | 45 | //! Unique ID generate 46 | static unsigned int m_IDGenerator; 47 | 48 | //! handle to the 49 | matchingEngine& m_market; 50 | 51 | //! Function called when an order owned by this trader is sent 52 | virtual void onOrderExecution( const tradeData& p_orderExcution ) override; 53 | 54 | //! Function called when public trade is received 55 | virtual void onPublicTrade(const tradeData& p_tradeData) override; 56 | 57 | //! Function called when orderBook update is received 58 | virtual void onOrderBook( const orderBook& p_orderBook ) override; 59 | 60 | //! Function callback for order execution updates 61 | std::function onOrderExecutionCallBack; 62 | 63 | //! Function callback for public trade updates 64 | std::function onPublicTradeCallBack; 65 | 66 | //! Function callback for order book updates 67 | std::function onOrderBookCallBack; 68 | 69 | }; 70 | 71 | } // market 72 | 73 | #endif /* TRADER_H */ 74 | -------------------------------------------------------------------------------- /src/trader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "trader.hpp" 4 | #include "matchingEngine.hpp" 5 | #include "orderBook.hpp" 6 | #include "order.hpp" 7 | 8 | using namespace market; 9 | 10 | 11 | unsigned int trader::m_IDGenerator = 0; 12 | 13 | trader::trader(matchingEngine& p_market) noexcept 14 | :m_ID(m_IDGenerator++), 15 | m_market(p_market) 16 | {} 17 | 18 | void trader::onOrderBook(const orderBook& p_orderBook ) 19 | { 20 | if (onOrderBookCallBack) 21 | { 22 | onOrderBookCallBack(p_orderBook); 23 | } 24 | 25 | /* 26 | const auto& buys = p_orderBook.getBuyOrdersSorted(); 27 | const auto& sells = p_orderBook.getSellOrdersSorted(); 28 | 29 | std::cout << "onOrderBook, Buys:" << std::endl; 30 | for (const auto& it : buys) 31 | { 32 | std::cout << it << std::endl; 33 | } 34 | std::cout << "onOrderBook, Sells:" << std::endl; 35 | for (const auto& it : sells) 36 | { 37 | std::cout << it << std::endl; 38 | } 39 | */ 40 | } 41 | 42 | void trader::onPublicTrade(const tradeData& p_tradeData) 43 | { 44 | if (onPublicTradeCallBack) 45 | { 46 | onPublicTradeCallBack(p_tradeData); 47 | } 48 | 49 | // std::cout << "onPublicTrade: " << 50 | // "ID:" << p_tradeData.m_ID << " " << 51 | // "State:" << p_tradeData.orderExecutionStateToString() << " " << 52 | // "Active Volume:" << p_tradeData.m_activeVolume << std::endl; 53 | } 54 | 55 | bool trader::sendOrder(unsigned int p_contractID, int p_volume, double p_price, orderSide p_side) 56 | { 57 | return m_market.addOrder(std::make_shared(*this), p_contractID, p_volume, p_price, p_side); 58 | } 59 | 60 | 61 | void trader::onOrderExecution(const tradeData& p_tradeData) 62 | { 63 | if (onOrderExecutionCallBack) 64 | { 65 | onOrderExecutionCallBack(p_tradeData); 66 | } 67 | // std::cout << "onOrderExecution: " << 68 | // "ID:" << p_tradeData.m_ID << " " << 69 | // "State:" << p_tradeData.orderExecutionStateToString() << " " << 70 | // "Active Volume:" << p_tradeData.m_activeVolume << std::endl; 71 | } 72 | 73 | 74 | void trader::setOnOrderExecutionCallBack (std::function p_func) 75 | { 76 | onOrderExecutionCallBack = p_func; 77 | } 78 | 79 | void trader::setOnPublicTradeCallBack (std::function p_func) 80 | { 81 | onPublicTradeCallBack = p_func; 82 | } 83 | 84 | void trader::setOnOrderBookCallBack (std::function p_func) 85 | { 86 | onOrderBookCallBack = p_func; 87 | } 88 | -------------------------------------------------------------------------------- /include/order.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ORDER_H 2 | #define ORDER_H 3 | 4 | #include 5 | 6 | namespace market 7 | { 8 | 9 | //! orderSide BUY/SELL 10 | enum class orderSide {BUY, SELL}; 11 | 12 | // Forward Declarations 13 | class orderDelegate; 14 | 15 | class order 16 | { 17 | public: 18 | 19 | //! Default constructor 20 | order() = default; 21 | 22 | //! Constructor 23 | order(unsigned int p_contractID, unsigned int p_ID, int p_volume, double p_price, orderSide p_side, const std::shared_ptr& p_owner) noexcept; 24 | 25 | //! Destructor 26 | ~order() = default; 27 | 28 | //! Order ID get function 29 | inline unsigned int ID() const 30 | { 31 | return m_ID; 32 | } 33 | 34 | //! Order contract ID get function 35 | inline unsigned int contractID() const 36 | { 37 | return m_contractID; 38 | } 39 | 40 | //! Order price get function 41 | inline int price() const 42 | { 43 | return m_price; 44 | } 45 | 46 | //! Order side get function 47 | inline orderSide side() const 48 | { 49 | return m_side; 50 | } 51 | 52 | //! Order side as string get function 53 | inline std::string sideStr() const 54 | { 55 | return orderSideToSting(m_side); 56 | } 57 | 58 | //! Order volume get function 59 | inline int volume() const 60 | { 61 | return m_volume; 62 | } 63 | 64 | //! Order owner get function 65 | inline const std::shared_ptr& owner() const 66 | { 67 | return m_owner; 68 | } 69 | 70 | //! Order volume set function 71 | inline void setVolume(int p_volume) 72 | { 73 | m_volume = p_volume; 74 | } 75 | 76 | //! Order less-than operator overload, compare by price 77 | inline bool operator<( const order& other) const 78 | { 79 | return m_price < other.m_price; 80 | } 81 | 82 | //! Order greater-than operator overload, compare by price 83 | inline bool operator>( const order& other) const 84 | { 85 | return !operator<(other); 86 | } 87 | 88 | // helper function to convert orderSide to string 89 | inline std::string orderSideToSting(const orderSide p_side) const 90 | { 91 | switch (p_side) 92 | { 93 | 94 | case orderSide::BUY: 95 | return "BUY"; 96 | break; 97 | 98 | case orderSide::SELL: 99 | return "SELL"; 100 | break; 101 | 102 | default: 103 | return "UNKNOWN ORDER SIDE"; 104 | break ; 105 | } 106 | } 107 | 108 | //! Order << operator overload, for use with cout 109 | friend std::ostream& operator<<(std::ostream &os, const order& p) 110 | { 111 | os << "Order ID: " << p.m_ID << " Volume: " << p.m_volume << " Price: " << p.m_price/100.0 << " Side: " << p.sideStr(); 112 | return os; 113 | } 114 | 115 | private: 116 | 117 | //! order ID 118 | unsigned int m_ID{0}; 119 | 120 | //! order contract ID 121 | unsigned int m_contractID{0}; 122 | 123 | //! order volume 124 | int m_volume{0}; 125 | 126 | //! order price 127 | int m_price{0}; 128 | 129 | //! order side (Buy/Sell) 130 | orderSide m_side{orderSide::BUY}; 131 | 132 | //! order owner 133 | std::shared_ptr m_owner{nullptr}; 134 | 135 | //! helper function to convert price to cents 136 | constexpr int price_to_cents(const double price) 137 | { 138 | return 100 * price; 139 | } 140 | }; 141 | 142 | 143 | } // market 144 | 145 | #endif // ORDER_H 146 | -------------------------------------------------------------------------------- /src/orderManagement.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "orderManagement.hpp" 7 | #include "trader.hpp" 8 | #include "orderDelegate.hpp" 9 | #include "marketData.hpp" 10 | #include "orderBook.hpp" 11 | 12 | using namespace market; 13 | 14 | orderManagement::orderManagement(marketData& p_delegate) noexcept 15 | :m_delegate(p_delegate) 16 | { 17 | } 18 | 19 | bool orderManagement::addOrder(std::shared_ptr p_trader, unsigned int p_contractID, int p_volume, double price, orderSide side ) 20 | { 21 | // Check if the order is valid 22 | if (p_volume < 1 ) 23 | { 24 | return false; 25 | } 26 | 27 | 28 | // If queue is full, wait for an arbitrary time. 29 | uint64_t wait_time = 1000000; 30 | uint64_t start = 0; 31 | while ( !m_queue.write_available() ) 32 | { 33 | if (start++ > wait_time) 34 | { 35 | // std::cout << "failed to add order" << std::endl; 36 | return false; 37 | } 38 | } 39 | 40 | m_queue.push(order(p_contractID, m_orderID++, p_volume, price, side, p_trader)); 41 | m_totalVolume += p_volume; 42 | 43 | return true; 44 | } 45 | 46 | 47 | bool orderManagement::matchOrders() 48 | { 49 | 50 | // If there are no data, return 51 | if (!m_queue.read_available()) 52 | { 53 | return false; 54 | } 55 | 56 | order _order; 57 | std::unordered_set contractIDs; 58 | while (m_queue.pop(_order)) 59 | { 60 | // Add order to the order book 61 | contractIDs.insert(_order.contractID()); 62 | m_orderBooks[_order.contractID()].addOrder(_order); 63 | } 64 | 65 | for(auto& _contractID : contractIDs ) 66 | { 67 | 68 | auto orderBookItr = m_orderBooks.find(_contractID); 69 | if ( orderBookItr == m_orderBooks.end() ) 70 | { 71 | // Integrity check: If no order book available with the given contract number 72 | // then there is something wrong here 73 | return false; 74 | } 75 | 76 | auto& m_buyOrders = orderBookItr->second.m_buyOrders; 77 | auto& m_sellOrders = orderBookItr->second.m_sellOrders; 78 | 79 | // While there are BUY and SELL orders in the queue 80 | // And the highest price of buy order can be matched 81 | while ((!m_buyOrders.empty()) && (!m_sellOrders.empty()) && 82 | m_buyOrders.front().price() >= m_sellOrders.front().price()) 83 | { 84 | 85 | auto& _buy_order = m_buyOrders.front(); 86 | auto& _sell_order = m_sellOrders.front(); 87 | 88 | // Calculate and update trade volumes 89 | auto trade_volume = std::min(_sell_order.volume(), _buy_order.volume()); 90 | _sell_order.setVolume(_sell_order.volume() - trade_volume); 91 | _buy_order.setVolume(_buy_order.volume() - trade_volume); 92 | 93 | // Notify the order owners 94 | // Not sure which price should be published. So putting buy price for now. 95 | _sell_order.owner()->onOrderExecution({_contractID, _sell_order.ID(), trade_volume, _buy_order.price()}); 96 | _buy_order.owner()->onOrderExecution({_contractID, _buy_order.ID(), trade_volume, _buy_order.price()}); 97 | 98 | // Increment internal counter (for testing purposes) 99 | m_totalTradedVolume += trade_volume; 100 | 101 | // Publish public trades 102 | m_delegate.publishPublicTrade({_contractID, _buy_order.ID(), trade_volume, _buy_order.price()}); 103 | 104 | // Remove filled orders 105 | if ( _buy_order.volume() == 0 ) 106 | { 107 | std::pop_heap(m_buyOrders.begin(), m_buyOrders.end(), std::less()); 108 | m_buyOrders.pop_back(); 109 | } 110 | 111 | if ( _sell_order.volume() == 0 ) 112 | { 113 | std::pop_heap(m_sellOrders.begin(), m_sellOrders.end(), std::greater()); 114 | m_sellOrders.pop_back(); 115 | } 116 | } 117 | 118 | // Publish order book 119 | m_delegate.publishOrderBook(orderBookItr->second); 120 | 121 | } 122 | 123 | return true; 124 | } 125 | -------------------------------------------------------------------------------- /diagrams/class.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | title Matching Engine Class Diagram 4 | class marketData { 5 | +marketData() 6 | +marketData(const marketData& other) 7 | +marketData(marketData&& other) 8 | +~marketData() 9 | +operator=(const marketData& other) : marketData& 10 | +operator=(marketData&& other) : marketData& 11 | -m_traders : std::unordered_set< std::shared_ptr > 12 | +publishOrderBook(const orderBook& p_orderBook) : void 13 | +publishPublicTrade(const tradeData& p_tradeData) : void 14 | +subscribe(std::shared_ptr p_subscriber) : void 15 | } 16 | 17 | abstract class marketDataDelegate { 18 | +~marketDataDelegate() 19 | +{abstract} onOrderBook(const orderBook& p_orderBook) : void 20 | +{abstract} onPublicTrade(const tradeData& p_tradeData) : void 21 | } 22 | 23 | class matchingEngine { 24 | +matchingEngine() 25 | +matchingEngine(const matchingEngine& other) 26 | +matchingEngine(matchingEngine&& other) 27 | +~matchingEngine() 28 | +addOrder(const std::shared_ptr& p_trader, unsigned int p_contractID, int p_volume, double p_price, orderSide p_side) : bool 29 | -m_marketData : marketData 30 | +operator=(const matchingEngine& other) : matchingEngine& 31 | +operator=(matchingEngine&& other) : matchingEngine& 32 | -m_orderManagement : orderManagement 33 | +getOrderManagement() : orderManagement& 34 | -m_orderMatchingTask : std::future 35 | +addTrader() : std::shared_ptr 36 | -m_thread : std::thread 37 | +close() : void 38 | +open() : void 39 | } 40 | 41 | class order { 42 | +order() 43 | +order(unsigned int p_contractID, unsigned int p_ID, int p_volume, double p_price, orderSide p_side, const std::shared_ptr& p_owner) 44 | +~order() 45 | +operator<(const order& other) : bool 46 | +operator>(const order& other) : bool 47 | -price_to_cents(const double price) : constexpr int 48 | +price() : int 49 | +side() : int 50 | +volume() : int 51 | +operator<<(std::ostream& os, const order& p) : std : : ostream& 52 | +owner() : std::shared_ptr& 53 | +orderSideToSting(const orderSide p_side) : std::string 54 | +sideStr() : std::string 55 | +ID() : unsigned int 56 | +contractID() : unsigned int 57 | +setVolume(int p_volume) : void 58 | } 59 | 60 | class orderBook { 61 | +orderBook() 62 | +orderBook(const orderBook& other) 63 | +addOrder(order p_order) : bool 64 | +getBuyOrdersSorted() : std::vector 65 | +getSellOrders() : std::vector 66 | +getSellOrdersSorted() : std::vector 67 | -m_buyOrders : std::vector 68 | -m_sellOrders : std::vector 69 | +getBuyOrders() : std::vector& 70 | } 71 | 72 | abstract class orderDelegate { 73 | +~orderDelegate() 74 | +{abstract} onOrderExecution(const tradeData& p_orderExcution) : void 75 | } 76 | 77 | class orderExecutionState { 78 | } 79 | 80 | class orderManagement { 81 | +orderManagement(marketData& p_delegate) 82 | +orderManagement(const orderManagement& other) 83 | +orderManagement(orderManagement&& other) 84 | +~orderManagement() 85 | +addOrder(std::shared_ptr p_trader, unsigned int p_contractID, int volume, double price, orderSide side) : bool 86 | +matchOrders() : bool 87 | -m_queue : boost::lockfree::spsc_queue > 88 | -m_delegate : marketData& 89 | +operator=(const orderManagement& other) : orderManagement& 90 | +operator=(orderManagement&& other) : orderManagement& 91 | -m_orderBooks : std::unordered_map 92 | +totalTradedVolume() : unsigned int 93 | +totalVolume() : unsigned int 94 | } 95 | 96 | class tradeData { 97 | +tradeData(unsigned int p_contractID, unsigned int p_orderID, int p_tradedVolume, int p_price) 98 | +m_state : const orderExecutionState 99 | +m_timeStamp : const std::time_t 100 | +orderExecutionStateToString() : std::string 101 | +timeStamp() : std::string 102 | +toString() : std::string 103 | } 104 | 105 | class trader { 106 | +trader(matchingEngine& p_market) 107 | +~trader() 108 | +sendOrder(unsigned int p_contractID, int p_volume, double p_price, orderSide p_side) : bool 109 | -m_market : matchingEngine& 110 | -{static} m_IDGenerator : static unsigned int 111 | -) : void 118 | +setOnOrderExecutionCallBack(std::function) : void 119 | +setOnPublicTradeCallBack(std::function) : void 120 | } 121 | 122 | enum orderSide { 123 | BUY 124 | SELL 125 | } 126 | 127 | marketDataDelegate <|-- trader 128 | orderDelegate <|-- trader 129 | marketData o-- marketDataDelegate 130 | matchingEngine o-- marketData 131 | matchingEngine o-- orderManagement 132 | orderBook "2" o-- order 133 | orderManagement o-- marketData 134 | orderManagement o-- order 135 | orderManagement o-- orderBook 136 | tradeData o-- orderExecutionState 137 | trader o-- matchingEngine 138 | 139 | @enduml 140 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+OPTIONS: html-postamble:nil 2 | 3 | * Introduction 4 | This project is a basic implementation of a market data matching engine. The matching engine allows a client to create an order, send it to a market and then receive market data. 5 | The engine use multi threads to enable sending orders request at high throughput. 6 | The solution is developed and tested in Mac OS X, using Clang 4.2.1 and BOOST 1.68 7 | 8 | * The source code 9 | ** Dependencies 10 | * CMAKE 3.0 11 | * Clang C++14 12 | * BOOST unit test framework 13 | ** Building the solution 14 | From the project directory, execute the following commands: 15 | #+BEGIN_SRC 16 | mkdir build 17 | cd build 18 | cmake .. 19 | make 20 | #+END_SRC 21 | Running the unit tests 22 | #+BEGIN_SRC 23 | make test 24 | #+END_SRC 25 | Example 26 | #+BEGIN_SRC C++ 27 | using namespace market; 28 | matchingEngine _market; 29 | 30 | // Add traders to the market 31 | auto _traderA = _market.addTrader(); 32 | auto _traderB = _market.addTrader(); 33 | 34 | // Set a callback to read data when order is executed 35 | _traderA->setOnOrderExecutionCallBack([](const tradeData& p_tradeData) { 36 | std::cout << p_tradeData.m_tradedVolume; }); 37 | 38 | // Trader A sends a BUY order with contract ID:7, Volume:100, Price:12.30 39 | _traderA->sendOrder(7, 100, 12.30, orderSide::BUY ); 40 | 41 | // Trader B sends a SELL order with contract ID:7, Volume:100, price 12:30 42 | _traderB->sendOrder(7, 100, 12.30, orderSide::SELL ); 43 | #+END_SRC 44 | 45 | * Technical Content 46 | 47 | ** Task breakdown 48 | Basic operation of the matching engine requires the following functionalities: 49 | 1. Matching algorithm, that matches a buy bid with the highest price to an ask bid with the lowest price. 50 | 2. Asynchronous order matching so the engine is always available to receive order requests. 51 | 3. Notification engine to publish market updates to the participants. 52 | 53 | ** Scope of the task 54 | The following system requirements define the scope of the task: 55 | #+ATTR_HTML: :border 2 :rules all :frame border 56 | |-----+----------------------+--------------------------------------------------------------------------| 57 | | ID# | Application | Description | 58 | |-----+----------------------+--------------------------------------------------------------------------| 59 | | 1 | Order Management API | Match/execute orders | 60 | | 2 | Order Management API | Send order buy update to the owner of a buy order | 61 | | 3 | Order Management API | Send order sell update to the owner of a sell order | 62 | | 4 | Order Management API | Keep track of active orders | 63 | | 5 | Order Management API | Publish order book updates and public trades using the Market Data API | 64 | | 6 | Order Management API | Sort order book by price | 65 | | 7 | Client (trader) | Send buy order requests to the Order Management API | 66 | | 8 | Client (trader) | Send sell order requests to the Order Management API | 67 | | 9 | Client (trader) | Handle data received from the Market Data API and Order Management API | 68 | | 10 | Order | Contain: Contract ID, Unique order ID, Price, Volume, Side | 69 | | 11 | Order Book | Group orders by contracts, contract is a unique ID | 70 | | 12 | Market Data API | Send order books to all subscribed clients | 71 | | 13 | Market Data API | Send public traders to all subscribed clients | 72 | | 14 | Matching Engine API | Provide public interface to the client | 73 | | 15 | Matching Engine API | Handle order requests from client | 74 | |-----+----------------------+--------------------------------------------------------------------------| 75 | 76 | ** Solution description 77 | The system is separated into the following components: 78 | 1. Matching Engine 79 | 2. Order Management 80 | 3. Order Book 81 | 4. Order 82 | 5. Market Data 83 | 6. Trader 84 | Description of each component follows next. 85 | 86 | *** Matching Engine 87 | The matching engine is the main interface to the clients, it is responsible for initializing all the components required for market operations and handles requests from clients (traders) 88 | *** Order Management 89 | The core of matching engine. Main responsibilities are: 90 | * Matching received orders 91 | * Notify orders owners 92 | * Publish order book updates using the market data API. 93 | 94 | Design approach: 95 | * Single Producer Single Consumer queue is used to store and dispatch order requests. This allows matching orders asynchronously. 96 | * Active orders are stored in a container. Choice of the container is influenced by answering the following questions: 97 | 1. Performance is critical? 98 | Yes, when large number of orders are stored. 99 | 2. Sorting required? 100 | No, only min/max elements needed for the matching algorithm. 101 | 3. Lookups required? 102 | No. 103 | 4. Insertions/deletions from the container. 104 | No insertion happens in the middle or at the front of the container. 105 | Although choosing a sorted data structure like std::set or std::map is a possibility, they are not the most efficient option for this task, because they are always sorted which is not needed and might slowdown insert operations. Also std::set and std::map are implemented on top of linked list which is not efficient for traversal, if cache locality is considered. 106 | To make use of CPU cache and thus a performance boost, choice of contiguous memory is desirable. Binary heap on top of an array is better choice, benefiting from heap properties as well as data locality. Heap is not a sorted but access to min/max items is trivial when heap property is preserved, which is just enough for the purpose of the matching algorithm. average insertion complexity is O(Log(n)) and min/max retrieval is O(1). 107 | Using two heaps, a max heap for buy orders, and a min heap for sell orders. First (max) element in the buy order heap is matched with the first (min) element the sell orders. If the price crosses, then trade will be executed and volume will be deducted. This operation is repeated until price can't be crossed anymore or there are no orders in the queue. The use this algorithms is inspired from a classical problem of running medians using two heaps (http://www.dsalgo.com/2013/02/RunningMedian.php.html). The payout of performance happens as the size of the order queue grows. 108 | *** Order book 109 | order book groups orders by contracts and ensure that only orders with the same contract ID are matched against each other. Hash map (std::unordered_map) is used to represent order book with contract used as a hash key. 110 | *** Order 111 | Order contains all data required to compose a market order, such as price, volume etc.. It is worth to mention that price is represented in cents, thus allow integer representation of the price instead of double, which is much simpler when it comes to compare operations, i.e no need for epsilon. 112 | *** Market Data 113 | Main responsibility is notifying all subscribed clients with order updates. Delegations design pattern is used to implement events behaviour. Any class that is interested in receiving event must inherit from a event class named Delegate and then implements the virtual functions of the delegate class. The choice of this approach is inspired from Objective-C OS API which I used back in 2011. 114 | *** Trader 115 | Trader is a Representation of client used to initiate order requests and handle received updates. A callback function can be created to be invoked when an event occurs. 116 | 117 | ** Diagrams 118 | The following diagrams are presented to help with understanding the source code implementation 119 | *** Class Diagram 120 | [[file:diagrams/class.png]] 121 | *** Sequence Diagram 122 | [[file:diagrams/sequence.png]] 123 | * Limitations: 124 | 1. The matching engine does not support sending orders from more than one thread. 125 | 126 | -------------------------------------------------------------------------------- /test/market_test.cpp: -------------------------------------------------------------------------------- 1 | //Link to Boost 2 | #define BOOST_TEST_DYN_LINK 3 | //Define our Module name (prints at testing) 4 | #define BOOST_TEST_MODULE "BaseClassModule" 5 | 6 | #include "matchingEngine.hpp" 7 | #include "trader.hpp" 8 | #include "orderBook.hpp" 9 | #include 10 | 11 | // dummy representation of order contracts 12 | enum contract { IBM, APPLE }; 13 | 14 | BOOST_AUTO_TEST_CASE(adding_valid_orders) 15 | { 16 | using namespace market; 17 | matchingEngine _market; 18 | auto _traderA = _market.addTrader(); 19 | auto _traderB = _market.addTrader(); 20 | 21 | _traderA->sendOrder(IBM, 100, 12.30, orderSide::BUY ); 22 | _traderA->sendOrder(IBM, 100, 12.30, orderSide::SELL ); 23 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalVolume(), 200); 24 | } 25 | 26 | 27 | BOOST_AUTO_TEST_CASE(adding_invalid_orders) 28 | { 29 | using namespace market; 30 | matchingEngine _market; 31 | auto _traderA = _market.addTrader(); 32 | 33 | _traderA->sendOrder(IBM, -10, 12.30, orderSide::BUY ); 34 | _traderA->sendOrder(IBM, -10, 12.30, orderSide::SELL ); 35 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalVolume(), 0); 36 | } 37 | 38 | BOOST_AUTO_TEST_CASE(matching_orders_no_cross_price) 39 | { 40 | using namespace market; 41 | matchingEngine _market; 42 | auto _traderA = _market.addTrader(); 43 | auto _traderB = _market.addTrader(); 44 | 45 | _traderA->sendOrder(IBM, 100, 12.29, orderSide::BUY ); 46 | _traderB->sendOrder(IBM, 100, 12.30, orderSide::SELL ); 47 | _market.close(); 48 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalTradedVolume(), 0); 49 | } 50 | 51 | BOOST_AUTO_TEST_CASE(matching_orders_no_cross_contract) 52 | { 53 | using namespace market; 54 | matchingEngine _market; 55 | auto _traderA = _market.addTrader(); 56 | auto _traderB = _market.addTrader(); 57 | 58 | _traderA->sendOrder(IBM, 100, 12.30, orderSide::BUY ); 59 | _traderB->sendOrder(APPLE, 100, 12.30, orderSide::SELL ); 60 | _market.close(); 61 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalTradedVolume(), 0); 62 | } 63 | 64 | 65 | BOOST_AUTO_TEST_CASE(matching_orders_cross_contract) 66 | { 67 | for (unsigned int i=0; i < 1; i++) 68 | { 69 | using namespace market; 70 | matchingEngine _market; 71 | auto _traderA = _market.addTrader(); 72 | auto _traderB = _market.addTrader(); 73 | 74 | _traderA->sendOrder(IBM, 100, 12.30, orderSide::BUY ); 75 | _traderB->sendOrder(APPLE, 100, 12.30, orderSide::SELL ); 76 | 77 | _traderA->sendOrder(APPLE, 100, 12.30, orderSide::BUY ); 78 | _traderB->sendOrder(IBM, 100, 12.30, orderSide::SELL ); 79 | 80 | _market.close(); 81 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalTradedVolume(), 200); 82 | } 83 | } 84 | 85 | BOOST_AUTO_TEST_CASE(matching_orders_cross) 86 | { 87 | for (int i=0; i<100; i++) 88 | { 89 | using namespace market; 90 | matchingEngine _market; 91 | auto _traderA = _market.addTrader(); 92 | auto _traderB = _market.addTrader(); 93 | 94 | _traderA->sendOrder(IBM, 100, 12.30, orderSide::BUY ); 95 | _traderB->sendOrder(IBM, 100, 12.30, orderSide::SELL ); 96 | 97 | _market.close(); 98 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalTradedVolume(), 100); 99 | } 100 | } 101 | 102 | BOOST_AUTO_TEST_CASE(orders_example_from_the_task) 103 | { 104 | /* 105 | Order 1 Buy 100@12.30 106 | Order 2 Buy 25@12:15 107 | Order 3 Buy 10@12.20 108 | Order 4 Sell 115@12.17 109 | */ 110 | 111 | using namespace market; 112 | matchingEngine _market; 113 | auto _traderA = _market.addTrader(); 114 | auto _traderB = _market.addTrader(); 115 | 116 | _traderA->sendOrder(IBM, 100, 12.30, orderSide::BUY ); 117 | _traderA->sendOrder(IBM, 25, 12.15, orderSide::BUY ); 118 | 119 | _traderA->sendOrder(IBM, 10, 12.20, orderSide::BUY ); 120 | _traderB->sendOrder(IBM, 115, 12.17, orderSide::SELL ); 121 | 122 | _market.close(); 123 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalTradedVolume(), 110); 124 | } 125 | 126 | BOOST_AUTO_TEST_CASE(order_updates_test) 127 | { 128 | 129 | /* 130 | Order 1 Buy 100@12.30 131 | Order 2 Buy 25@12:15 132 | Order 3 Buy 10@12.20 133 | Order 4 Sell 115@12.17 134 | */ 135 | 136 | using namespace market; 137 | matchingEngine _market; 138 | auto _traderA = _market.addTrader(); 139 | auto _traderB = _market.addTrader(); 140 | 141 | auto _executedOrdersVolume = 0; 142 | _traderA->setOnOrderExecutionCallBack([&](const tradeData& p_tradeData) { 143 | 144 | _executedOrdersVolume += p_tradeData.m_tradedVolume; 145 | }); 146 | 147 | _traderA->sendOrder(IBM, 100, 12.30, orderSide::BUY ); 148 | _traderA->sendOrder(IBM, 25, 12.15, orderSide::BUY ); 149 | 150 | _traderA->sendOrder(IBM, 10, 12.20, orderSide::BUY ); 151 | _traderB->sendOrder(IBM, 115, 12.17, orderSide::SELL ); 152 | 153 | _market.close(); 154 | 155 | BOOST_CHECK_EQUAL(_executedOrdersVolume, 110); 156 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalTradedVolume(), 110); 157 | } 158 | 159 | BOOST_AUTO_TEST_CASE(public_trades_test) 160 | { 161 | 162 | /* 163 | Order 1 Buy 100@12.30 164 | Order 2 Buy 25@12:15 165 | Order 3 Buy 10@12.20 166 | Order 4 Sell 115@12.17 167 | */ 168 | 169 | using namespace market; 170 | matchingEngine _market; 171 | auto _traderA = _market.addTrader(); 172 | auto _traderB = _market.addTrader(); 173 | auto _traderC = _market.addTrader(); 174 | 175 | auto _executedOrdersVolume = 0; 176 | _traderC->setOnPublicTradeCallBack([&](const tradeData& p_tradeData) { 177 | 178 | _executedOrdersVolume += p_tradeData.m_tradedVolume; 179 | }); 180 | 181 | _traderA->sendOrder(IBM, 100, 12.30, orderSide::BUY ); 182 | _traderA->sendOrder(IBM, 25, 12.15, orderSide::BUY ); 183 | 184 | _traderA->sendOrder(IBM, 10, 12.20, orderSide::BUY ); 185 | _traderB->sendOrder(IBM, 115, 12.17, orderSide::SELL ); 186 | 187 | _market.close(); 188 | 189 | BOOST_CHECK_EQUAL(_executedOrdersVolume, 110); 190 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalTradedVolume(), 110); 191 | } 192 | 193 | 194 | BOOST_AUTO_TEST_CASE(order_book_test) 195 | { 196 | 197 | /* 198 | Order 1 Buy 100@12.30 199 | Order 2 Buy 25@12:15 200 | Order 3 Buy 10@12.20 201 | Order 4 Sell 115@12.17 202 | */ 203 | 204 | using namespace market; 205 | matchingEngine _market; 206 | auto _traderA = _market.addTrader(); 207 | auto _traderB = _market.addTrader(); 208 | 209 | auto _orderBookTotalBuyPrice = 0; 210 | auto _orderBookTotalSellPrice = 0; 211 | auto _orderBookTotalBuyVolume = 25; 212 | auto _orderBookTotalSellVolume = 5; 213 | 214 | _traderA->setOnOrderBookCallBack([&](const orderBook& p_orderBook) { 215 | auto buys = p_orderBook.getBuyOrders(); 216 | auto sells = p_orderBook.getSellOrders(); 217 | 218 | _orderBookTotalBuyVolume = 0; 219 | _orderBookTotalBuyPrice = 0; 220 | _orderBookTotalSellVolume = 0; 221 | _orderBookTotalSellPrice = 0; 222 | 223 | 224 | for ( const auto& b : buys ) 225 | { 226 | // std::cout << b << std::endl; 227 | _orderBookTotalBuyPrice += b.price(); 228 | _orderBookTotalBuyVolume += b.volume(); 229 | } 230 | 231 | for ( const auto& s : sells ) 232 | { 233 | // std::cout << s << std::endl; 234 | _orderBookTotalSellPrice += s.price(); 235 | _orderBookTotalSellVolume += s.volume(); 236 | } 237 | 238 | }); 239 | 240 | _traderA->sendOrder(IBM, 100, 12.30, orderSide::BUY ); 241 | _traderA->sendOrder(IBM, 25, 12.15, orderSide::BUY ); 242 | 243 | _traderA->sendOrder(IBM, 10, 12.20, orderSide::BUY ); 244 | _traderB->sendOrder(IBM, 115, 12.17, orderSide::SELL ); 245 | 246 | _market.close(); 247 | 248 | BOOST_CHECK_EQUAL(_orderBookTotalBuyVolume, 25); 249 | BOOST_CHECK_EQUAL(_orderBookTotalBuyPrice, 1215); 250 | BOOST_CHECK_EQUAL(_orderBookTotalSellVolume, 5); 251 | BOOST_CHECK_EQUAL(_orderBookTotalSellPrice, 1217); 252 | } 253 | 254 | 255 | BOOST_AUTO_TEST_CASE(stress_test) 256 | { 257 | using namespace market; 258 | matchingEngine _market; 259 | 260 | unsigned int n = 1000; 261 | for (unsigned int i=0; isendOrder(IBM, 100, 12.30, orderSide::BUY ); 265 | 266 | auto _traderB = _market.addTrader(); 267 | _traderB->sendOrder(IBM, 100, 12.30, orderSide::SELL ); 268 | 269 | } 270 | 271 | _market.close(); 272 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalVolume(), 2*n*100); 273 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalTradedVolume(), n*100); 274 | } 275 | 276 | BOOST_AUTO_TEST_CASE(multi_thread_test) 277 | { 278 | using namespace market; 279 | matchingEngine _market; 280 | 281 | unsigned int n = 1000; 282 | unsigned int i=0; // j=0; 283 | auto _traderA = _market.addTrader(); 284 | auto _traderB = _market.addTrader(); 285 | 286 | std::thread producer([ & ] { 287 | while (i++ < n) { 288 | _traderA->sendOrder(IBM, 100, 12.30, orderSide::SELL ); 289 | _traderB->sendOrder(IBM, 100, 12.30, orderSide::BUY ); 290 | } 291 | }); 292 | 293 | producer.join(); 294 | 295 | // wait for the order Management to finish matching orders 296 | auto w = 5000000; 297 | while (w--) {} 298 | 299 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalVolume(), 2*n*100); 300 | BOOST_CHECK_EQUAL(_market.getOrderManagement().totalTradedVolume(), n*100); 301 | } 302 | --------------------------------------------------------------------------------