├── .gitignore ├── PERFORMANCE.md ├── README.md ├── README_ORDER_ENTRY.md ├── doc ├── Images │ ├── MarketApplication.png │ └── MarketApplication.svg └── settAug2013 │ ├── images │ ├── Left.png │ ├── Middle.png │ ├── OCILogo.png │ ├── Right.png │ └── SETT.png │ ├── liquibook_sett.html │ ├── settAug2013_files │ ├── ExampleExchange.png │ ├── Exchange.png │ ├── ExchangeUML.png │ ├── OrderBookUML.png │ └── paper.css │ └── styles │ └── SETT.css ├── env.sh ├── examples ├── .gitignore ├── depth_feed_publisher │ ├── .gitignore │ ├── asio_safe_include.h │ ├── depth_feed_connection.cpp │ ├── depth_feed_connection.h │ ├── depth_feed_publisher.cpp │ ├── depth_feed_publisher.h │ ├── depth_feed_publisher.mpc │ ├── depth_feed_subscriber.cpp │ ├── depth_feed_subscriber.h │ ├── example_order_book.cpp │ ├── example_order_book.h │ ├── exchange.cpp │ ├── exchange.h │ ├── order.cpp │ ├── order.h │ ├── publisher_main.cpp │ ├── sleep.h │ ├── subscriber_main.cpp │ ├── template_consumer.cpp │ ├── template_consumer.h │ └── templates │ │ ├── ComplexTemplates.xml │ │ ├── Simple.xml │ │ └── depth.xml └── mt_order_entry │ ├── Market.cpp │ ├── Market.h │ ├── Order.cpp │ ├── Order.h │ ├── OrderFwd.h │ ├── TestOneAonBidTwoAsk.script │ ├── Util.cpp │ ├── Util.h │ ├── mt_order_entry.mpc │ ├── mt_order_entry_main.cpp │ ├── order_entry.script │ └── teststoporders.script ├── license.txt ├── liquibook.features ├── liquibook.mwc ├── mpc.bat ├── mpc ├── liquibook.mpb ├── liquibook_book.mpb ├── liquibook_exe.mpb ├── liquibook_lib.mpb ├── liquibook_simple.mpb └── liquibook_test.mpb ├── noQuickFAST └── QuickFASTApplication.mpb ├── pt_run.bat ├── src ├── book │ ├── bbo_listener.h │ ├── callback.h │ ├── comparable_price.h │ ├── depth.h │ ├── depth_constants.h │ ├── depth_level.h │ ├── depth_listener.h │ ├── depth_order_book.h │ ├── liquibook.mpc │ ├── logger.h │ ├── main.cpp │ ├── order.h │ ├── order_book.h │ ├── order_book_listener.h │ ├── order_listener.h │ ├── order_tracker.h │ ├── trade_listener.h │ ├── types.h │ └── version.h ├── liquibook_export.h └── simple │ ├── liquibook_simple.mpc │ ├── simple_order.cpp │ ├── simple_order.h │ └── simple_order_book.h ├── test ├── .gitignore ├── latency │ ├── clock_gettime.h │ ├── liquibook_latency.mpc │ └── lt_order_book.cpp ├── perf │ ├── liquibook_perf.mpc │ └── pt_order_book.cpp └── unit │ ├── changed_checker.h │ ├── depth_check.h │ ├── liquibook_unit.mpc │ ├── ut_all_or_none.cpp │ ├── ut_bbo_order_book.cpp │ ├── ut_depth.cpp │ ├── ut_immediate_or_cancel.cpp │ ├── ut_listeners.cpp │ ├── ut_main.cpp │ ├── ut_market_price.cpp │ ├── ut_order_book.cpp │ ├── ut_order_book_shared_ptr.cpp │ ├── ut_stop_orders.cpp │ └── ut_utils.h ├── ut_run.bat ├── web ├── css │ ├── bootstrap-responsive.min.css │ ├── bootstrap.min.css │ └── liquibook.css ├── easy.html ├── fast.html ├── flexible.html ├── fluid.html ├── get-started.html ├── img │ ├── glyphicons-halflings-white.png │ └── glyphicons-halflings.png ├── index.html ├── js │ ├── bootstrap.min.js │ └── jquery-1.9.1.min.js └── sub-template.html ├── winenv.bat └── winenv_clear.bat /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | *.iobj 7 | 8 | # Compiled Dynamic libraries 9 | *.so 10 | *.dylib 11 | *.dll 12 | 13 | # Compiled Static libraries 14 | *.lai 15 | *.la 16 | *.a 17 | *.lib 18 | lib/ 19 | 20 | # Compiled executables 21 | *.exe 22 | /bin 23 | /Output 24 | 25 | Makefile* 26 | .depend* 27 | *.sln 28 | make 29 | 30 | # Artifacts 31 | *.swp 32 | 33 | #visual studio work files 34 | *vcproj* 35 | *vcxproj* 36 | Debug 37 | Release 38 | *.pdb 39 | *.ipdb 40 | *.ilk 41 | .vs/ 42 | *.VC.* 43 | 44 | # test output files 45 | *.log 46 | 47 | #helper/customized commands 48 | # (use single letter names and don't add to repo) 49 | ?.cmd 50 | ?.bat 51 | 52 | #a place to hide documents that should not go into the repo: 53 | doc/NotGitWorthy/ 54 | -------------------------------------------------------------------------------- /PERFORMANCE.md: -------------------------------------------------------------------------------- 1 | Performance Test Results, Inserts Per Second 2 | ============================================ 3 | (newest results on top) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
5 Level DepthBBO OnlyOrder Book OnlyNote
2,062,1582,139,9502,494,532Now testing on a modern laptop (2.4 GHZ i7).
1,231,9591,273,5101,506,066Handling all or none order condition.
1,249,5441,305,4821,531,998Remove callbacks_added method. Caller can invoke equivalent if necessary.
1,222,0001,279,7111,495,714Use vector for callback container.
1,250,6161,264,2271,463,738Union in callback. For clarity of purpose, not for performance.
1,267,1351,270,1881,469,246Combine 2 fill callbacks into one.
1,233,8941,237,1541,434,354Store excess depth levels in depth to speed repopulation.
58,936153,8391,500,874Removed spuroious insert on accept of completely filled order.
38,878124,7561,495,744Initial run with all 3 tests.
67 | 68 | -------------------------------------------------------------------------------- /doc/Images/MarketApplication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enewhuis/liquibook/2427613b32f1667abae68a01df6af9ba8270f8e7/doc/Images/MarketApplication.png -------------------------------------------------------------------------------- /doc/settAug2013/images/Left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enewhuis/liquibook/2427613b32f1667abae68a01df6af9ba8270f8e7/doc/settAug2013/images/Left.png -------------------------------------------------------------------------------- /doc/settAug2013/images/Middle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enewhuis/liquibook/2427613b32f1667abae68a01df6af9ba8270f8e7/doc/settAug2013/images/Middle.png -------------------------------------------------------------------------------- /doc/settAug2013/images/OCILogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enewhuis/liquibook/2427613b32f1667abae68a01df6af9ba8270f8e7/doc/settAug2013/images/OCILogo.png -------------------------------------------------------------------------------- /doc/settAug2013/images/Right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enewhuis/liquibook/2427613b32f1667abae68a01df6af9ba8270f8e7/doc/settAug2013/images/Right.png -------------------------------------------------------------------------------- /doc/settAug2013/images/SETT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enewhuis/liquibook/2427613b32f1667abae68a01df6af9ba8270f8e7/doc/settAug2013/images/SETT.png -------------------------------------------------------------------------------- /doc/settAug2013/settAug2013_files/ExampleExchange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enewhuis/liquibook/2427613b32f1667abae68a01df6af9ba8270f8e7/doc/settAug2013/settAug2013_files/ExampleExchange.png -------------------------------------------------------------------------------- /doc/settAug2013/settAug2013_files/Exchange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enewhuis/liquibook/2427613b32f1667abae68a01df6af9ba8270f8e7/doc/settAug2013/settAug2013_files/Exchange.png -------------------------------------------------------------------------------- /doc/settAug2013/settAug2013_files/ExchangeUML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enewhuis/liquibook/2427613b32f1667abae68a01df6af9ba8270f8e7/doc/settAug2013/settAug2013_files/ExchangeUML.png -------------------------------------------------------------------------------- /doc/settAug2013/settAug2013_files/OrderBookUML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enewhuis/liquibook/2427613b32f1667abae68a01df6af9ba8270f8e7/doc/settAug2013/settAug2013_files/OrderBookUML.png -------------------------------------------------------------------------------- /doc/settAug2013/settAug2013_files/paper.css: -------------------------------------------------------------------------------- 1 | .listing, .figure { 2 | font-weight: bold; 3 | font-size: .9em; 4 | } 5 | .listing { 6 | margin-bottom: -16px; 7 | } 8 | -------------------------------------------------------------------------------- /doc/settAug2013/styles/SETT.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: rgb(0, 0, 0); 3 | background-color: rgb(255, 255, 255); 4 | font-family: Verdana, sans-serif; 5 | margin-left: 0.25in; 6 | margin-right: 0.25in; 7 | min-width: 800px; 8 | } 9 | 10 | .header { 11 | float: left; 12 | width: 100%; 13 | } 14 | 15 | .left { 16 | float: left; 17 | } 18 | 19 | .right { 20 | float: right; 21 | } 22 | 23 | .lower_header { 24 | height: 34px; 25 | float: left; 26 | width: 100%; 27 | border-top: 1px solid lightgrey; 28 | border-bottom: 1px solid lightgrey; 29 | margin-bottom: 25px; 30 | } 31 | 32 | .social { 33 | margin-top: 6px; 34 | } 35 | 36 | span.rssRow { 37 | margin: 33px; 38 | background:transparent url(../images/rss_icon_12x12.gif) no-repeat scroll 0; 39 | padding-left:18px; 40 | } 41 | 42 | .headimage_container { 43 | position: relative; 44 | float: left; 45 | width: 100%; 46 | background: url('../images/Middle.png') repeat-x 35px; 47 | height: 123px; 48 | } 49 | 50 | .headerimage_leftlogo { 51 | position: absolute; 52 | left: 0; 53 | top: 0; 54 | background-color: white; 55 | width: 180px; 56 | height: 123px; 57 | } 58 | 59 | .headimage_left { 60 | position: absolute; 61 | background-color: white; 62 | top: 33px; 63 | left: 180px; 64 | width: 10px; 65 | } 66 | 67 | .headimage_right { 68 | position: absolute; 69 | background-color: white; 70 | width: 278px; 71 | right: 0; 72 | margin-top: 33px; 73 | } 74 | 75 | a.career { 76 | font-weight: bold; 77 | } 78 | 79 | code { 80 | font-family: "Courier New", monospace; 81 | } 82 | 83 | div.center { 84 | text-align: center; 85 | } 86 | 87 | h1 { 88 | text-align: center; 89 | } 90 | 91 | h2 { 92 | text-align: left; 93 | } 94 | 95 | h3 { 96 | text-align: left; 97 | } 98 | 99 | h4 { 100 | text-align: left; 101 | } 102 | 103 | h5 { 104 | text-align: left; 105 | } 106 | 107 | hr { 108 | height: 1px; 109 | color: rgb(17, 59, 86); 110 | background-color: transparent; 111 | } 112 | 113 | kbd { 114 | font-family: "Courier New", monospace; 115 | } 116 | 117 | p { 118 | text-align: justify; 119 | } 120 | 121 | p.author { 122 | text-align: center; 123 | } 124 | 125 | p.footer { 126 | text-align: justify; 127 | } 128 | 129 | pre { 130 | font-family: "Courier New", monospace; 131 | } 132 | 133 | .quicklinks { 134 | text-align: right; 135 | } 136 | 137 | .red { 138 | color: rgb(255, 0, 0); 139 | background-color: rgb(255, 255, 255); 140 | } 141 | 142 | .green { 143 | color: rgb(0, 128, 0); 144 | background-color: rgb(255, 255, 255); 145 | } 146 | 147 | .blue { 148 | color: rgb(0, 0, 192); 149 | background-color: rgb(255, 255, 255); 150 | } 151 | 152 | .code { 153 | background-color: #FFFFF0; 154 | border: dashed black 1px; 155 | padding-left: 10px; 156 | } 157 | 158 | .comment { 159 | color: rgb(128, 128, 128); 160 | font-weight: normal; 161 | font-style: italic; 162 | } 163 | 164 | -------------------------------------------------------------------------------- /env.sh: -------------------------------------------------------------------------------- 1 | SOURCE="${BASH_SOURCE[0]}" 2 | SOURCE_DIR=`dirname $SOURCE` 3 | 4 | if test "$LIQUIBOOK_ROOT" = ""; then 5 | READLINK='readlink' 6 | $READLINK --version >/dev/null 2>/dev/null 7 | if (( $? != 0 )); then 8 | echo "readlink does not exist or it does not support --version" 9 | echo "maybe it is not GNU readlink but BSD" 10 | echo "trying with greadlink..." 11 | READLINK='greadlink' 12 | fi 13 | $READLINK --version >/dev/null 2>/dev/null 14 | if (( $? != 0 )); then 15 | echo "greadlink does not exist or an error occurred" 16 | UNAME=`uname` 17 | if [[ $UNAME == "Darwin" ]]; then 18 | echo "You are running on a Mac OSX system." 19 | echo "Consider installing homebrew." 20 | echo "Then install coreutils." 21 | echo "# brew install coreutils" 22 | fi 23 | else 24 | echo "$READLINK found at `which $READLINK`." 25 | fi 26 | $READLINK -f $SOURCE_DIR 27 | if (( $? != 0 )); then 28 | echo "trying exporting LIQUIBOOK_ROOT by pwd." 29 | export LIQUIBOOK_ROOT=`pwd` 30 | echo "LIQUIBOOK_ROOT = $LIQUIBOOK_ROOT" 31 | else 32 | export LIQUIBOOK_ROOT=`$READLINK -f $SOURCE_DIR` 33 | fi 34 | fi 35 | 36 | if test "$QUICKFAST_ROOT" == ""; then 37 | export QUICKFAST_ROOT=`pwd`/noQuickFAST 38 | echo QuickFAST support disabled 39 | fi 40 | 41 | if test "$BOOST_VERSION" = ""; then 42 | echo Please export BOOST_VERSION, and BOOST_CFG 43 | echo you can also set BOOST_ROOT if it is not /usr/boost/BOOST_VERSION 44 | else 45 | if test "$BOOST_ROOT" = ""; then 46 | export BOOST_ROOT=/usr/boost/$BOOST_VERSION 47 | fi 48 | if test "$BOOST_ROOT_LIB" = ""; then 49 | export BOOST_ROOT_LIB=$BOOST_ROOT/lib 50 | fi 51 | if test "$BOOST_CFG" = ""; then 52 | export BOOST_CFG=-gcc62-mt-1_63 53 | fi 54 | if test "$BOOST_STATIC_LIB_PREFIX" = ""; then 55 | export BOOST_STATIC_LIB_PREFIX= 56 | fi 57 | fi 58 | 59 | LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LIQUIBOOK_ROOT/lib 60 | # CIAO is not used, set so MPC does not give warning 61 | export CIAO_ROOT=/dev/null 62 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/.gitignore: -------------------------------------------------------------------------------- 1 | depth_feed_publisher 2 | depth_feed_subscriber 3 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/asio_safe_include.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 - 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | #ifdef _WIN32 6 | // Set the proper SDK version before including boost/Asio 7 | # include 8 | // Note boost/ASIO includes Windows.h. 9 | # include 10 | #else // _WIN32 11 | # include 12 | #endif //_WIN32 -------------------------------------------------------------------------------- /examples/depth_feed_publisher/depth_feed_connection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "asio_safe_include.h" 4 | #include "sleep.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace liquibook { namespace examples { 17 | typedef boost::shared_ptr WorkingBufferPtr; 18 | typedef std::deque WorkingBuffers; 19 | typedef boost::array Buffer; 20 | typedef boost::shared_ptr BufferPtr; 21 | typedef boost::function MessageHandler; 22 | typedef boost::function ResetHandler; 23 | typedef boost::function SendHandler; 25 | typedef boost::function RecvHandler; 27 | 28 | class DepthFeedConnection; 29 | 30 | // Session between a publisher and one subscriber 31 | class DepthFeedSession : boost::noncopyable { 32 | public: 33 | DepthFeedSession(boost::asio::io_service& ios, 34 | DepthFeedConnection* connection, 35 | QuickFAST::Codecs::TemplateRegistryPtr& templates); 36 | 37 | // Is this session connected? 38 | bool connected() const { return connected_; } 39 | 40 | // Mark this session as connected 41 | void set_connected() { connected_ = true; } 42 | 43 | // Get the socket for this session 44 | boost::asio::ip::tcp::socket& socket() { return socket_; } 45 | 46 | // Send a trade messsage to all clients 47 | void send_trade(QuickFAST::Messages::FieldSet& message); 48 | 49 | // Send an incremental update - if this client has handled this symbol 50 | // return true if handled 51 | bool send_incr_update(const std::string& symbol, 52 | QuickFAST::Messages::FieldSet& message); 53 | 54 | // Send a full update - if the client has not yet received for this symbol 55 | void send_full_update(const std::string& symbol, 56 | QuickFAST::Messages::FieldSet& message); 57 | private: 58 | bool connected_; 59 | uint32_t seq_num_; 60 | 61 | boost::asio::io_service& ios_; 62 | boost::asio::ip::tcp::socket socket_; 63 | DepthFeedConnection* connection_; 64 | QuickFAST::Codecs::Encoder encoder_; 65 | 66 | typedef std::set StringSet; 67 | StringSet sent_symbols_; 68 | 69 | static QuickFAST::template_id_t TID_TRADE_MESSAGE; 70 | static QuickFAST::template_id_t TID_DEPTH_MESSAGE; 71 | 72 | void set_sequence_num(QuickFAST::Messages::FieldSet& message); 73 | 74 | void on_send(WorkingBufferPtr wb, 75 | const boost::system::error_code& error, 76 | std::size_t bytes_transferred); 77 | }; 78 | 79 | typedef boost::shared_ptr SessionPtr; 80 | 81 | class DepthFeedConnection : boost::noncopyable { 82 | public: 83 | DepthFeedConnection(int argc, const char* argv[]); 84 | 85 | // Get the template registry 86 | const QuickFAST::Codecs::TemplateRegistryPtr& 87 | get_templates() { return templates_; } 88 | 89 | // Connect to publisher 90 | void connect(); 91 | 92 | // Accept connection from subscriber 93 | void accept(); 94 | 95 | // Let the IO service run 96 | void run(); 97 | 98 | // Set a callback to handle a message 99 | void set_message_handler(MessageHandler msg_handler); 100 | 101 | // Set a callback to handle a reset connection 102 | void set_reset_handler(ResetHandler reset_handler); 103 | 104 | // Reserve a buffer for receiving a message 105 | BufferPtr reserve_recv_buffer(); 106 | 107 | // Reserve a buffer for sending a message 108 | WorkingBufferPtr reserve_send_buffer(); 109 | 110 | // Send a trade messsage to all clients 111 | void send_trade(QuickFAST::Messages::FieldSet& message); 112 | 113 | // Send an incremental update 114 | // return true if all sessions could handle an incremental update 115 | bool send_incr_update(const std::string& symbol, 116 | QuickFAST::Messages::FieldSet& message); 117 | 118 | // Send a full update to those which have not yet received for this symbol 119 | void send_full_update(const std::string& symbol, 120 | QuickFAST::Messages::FieldSet& message); 121 | 122 | // Handle a connection 123 | void on_connect(const boost::system::error_code& error); 124 | 125 | // Handle an accepted connection 126 | void on_accept(SessionPtr session, 127 | const boost::system::error_code& error); 128 | 129 | // Handle a received message 130 | void on_receive(BufferPtr bp, 131 | const boost::system::error_code& error, 132 | std::size_t bytes_transferred); 133 | // Handle a sent message 134 | void on_send(WorkingBufferPtr wb, 135 | const boost::system::error_code& error, 136 | std::size_t bytes_transferred); 137 | 138 | private: 139 | typedef std::deque Buffers; 140 | typedef std::vector Sessions; 141 | const char* template_filename_; 142 | const char* host_; 143 | int port_; 144 | MessageHandler msg_handler_; 145 | ResetHandler reset_handler_; 146 | QuickFAST::Codecs::TemplateRegistryPtr templates_; 147 | 148 | Buffers unused_recv_buffers_; 149 | WorkingBuffers unused_send_buffers_; 150 | Sessions sessions_; 151 | boost::shared_ptr acceptor_; 152 | boost::asio::io_service ios_; 153 | boost::asio::ip::tcp::socket socket_; 154 | boost::shared_ptr work_ptr_; 155 | 156 | void issue_read(); 157 | public: 158 | static const char* template_file_from_args(int argc, const char* argv[]); 159 | static const char* host_from_args(int argc, const char* argv[]); 160 | static int port_from_args(int argc, const char* argv[]); 161 | }; 162 | } } // End namespace 163 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/depth_feed_publisher.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "depth_feed_publisher.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace liquibook { namespace examples { 15 | 16 | using namespace QuickFAST::Messages; 17 | 18 | DepthFeedPublisher::DepthFeedPublisher() 19 | : connection_(NULL) 20 | { 21 | } 22 | 23 | void 24 | DepthFeedPublisher::set_connection(DepthFeedConnection* connection) 25 | { 26 | connection_ = connection; 27 | } 28 | 29 | void 30 | DepthFeedPublisher::on_trade( 31 | const book::OrderBook* order_book, 32 | book::Quantity qty, 33 | book::Cost cost) 34 | { 35 | // Publish trade 36 | QuickFAST::Messages::FieldSet message(20); 37 | const ExampleOrderBook* exob = 38 | dynamic_cast(order_book); 39 | std::cout << "Got trade for " << exob->symbol() 40 | << " qty " << qty 41 | << " cost " << cost << std::endl; 42 | build_trade_message(message, exob->symbol(), qty, cost); 43 | connection_->send_trade(message); 44 | } 45 | 46 | void 47 | DepthFeedPublisher::on_depth_change( 48 | const book::DepthOrderBook* order_book, 49 | const book::DepthOrderBook::DepthTracker* tracker) 50 | { 51 | // Publish changed levels of order book 52 | QuickFAST::Messages::FieldSet message(20); 53 | const ExampleOrderBook* exob = 54 | dynamic_cast(order_book); 55 | build_depth_message(message, exob->symbol(), tracker, false); 56 | if (!connection_->send_incr_update(exob->symbol(), message)) { 57 | // Publish all levels of order book 58 | QuickFAST::Messages::FieldSet full_message(20); 59 | build_depth_message(full_message, exob->symbol(), tracker, true); 60 | connection_->send_full_update(exob->symbol(), full_message); 61 | } 62 | } 63 | 64 | void 65 | DepthFeedPublisher::build_trade_message( 66 | QuickFAST::Messages::FieldSet& message, 67 | const std::string& symbol, 68 | book::Quantity qty, 69 | book::Cost cost) 70 | { 71 | message.addField(id_timestamp_, FieldUInt32::create(time_stamp())); 72 | message.addField(id_symbol_, FieldString::create(symbol)); 73 | message.addField(id_qty_, FieldUInt32::create(qty)); 74 | message.addField(id_cost_, FieldUInt32::create(cost)); 75 | } 76 | 77 | void 78 | DepthFeedPublisher::build_depth_message( 79 | QuickFAST::Messages::FieldSet& message, 80 | const std::string& symbol, 81 | const book::DepthOrderBook::DepthTracker* tracker, 82 | bool full_message) 83 | { 84 | size_t bid_count(0), ask_count(0); 85 | 86 | message.addField(id_timestamp_, FieldUInt32::create(time_stamp())); 87 | message.addField(id_symbol_, FieldString::create(symbol)); 88 | 89 | // Build the changed levels 90 | book::ChangeId last_published_change = tracker->last_published_change(); 91 | 92 | // Build changed bids 93 | { 94 | SequencePtr bids(new Sequence(id_bids_length_, 1)); 95 | int index = 0; 96 | const book::DepthLevel* bid = tracker->bids(); 97 | // Create sequence of bids 98 | while (true) { 99 | if (full_message || bid->changed_since(last_published_change)) { 100 | build_depth_level(bids, bid, index); 101 | ++bid_count; 102 | } 103 | ++index; 104 | if (bid == tracker->last_bid_level()) { 105 | break; 106 | } else { 107 | ++bid; 108 | } 109 | } 110 | message.addField(id_bids_, FieldSequence::create(bids)); 111 | } 112 | 113 | // Build changed asks 114 | { 115 | SequencePtr asks(new Sequence(id_asks_length_, 1)); 116 | int index = 0; 117 | const book::DepthLevel* ask = tracker->asks(); 118 | // Create sequence of asks 119 | while (true) { 120 | if (full_message || ask->changed_since(last_published_change)) { 121 | build_depth_level(asks, ask, index); 122 | ++ask_count; 123 | } 124 | ++index; 125 | if (ask == tracker->last_ask_level()) { 126 | break; 127 | } else { 128 | ++ask; 129 | } 130 | } 131 | message.addField(id_asks_, FieldSequence::create(asks)); 132 | } 133 | std::cout << "Encoding " << (full_message ? "full" : "incr") 134 | << " depth message for symbol " << symbol 135 | << " with " << bid_count << " bids, " 136 | << ask_count << " asks" << std::endl; 137 | } 138 | 139 | void 140 | DepthFeedPublisher::build_depth_level( 141 | QuickFAST::Messages::SequencePtr& level_seq, 142 | const book::DepthLevel* level, 143 | int level_index) 144 | { 145 | FieldSetPtr level_fields(new FieldSet(4)); 146 | level_fields->addField(id_level_num_, FieldUInt8::create(level_index)); 147 | level_fields->addField(id_order_count_, 148 | FieldUInt32::create(level->order_count())); 149 | level_fields->addField(id_price_, 150 | FieldUInt32::create(level->price())); 151 | level_fields->addField(id_size_, 152 | FieldUInt32::create(level->aggregate_qty())); 153 | level_seq->addEntry(level_fields); 154 | } 155 | 156 | uint32_t 157 | DepthFeedPublisher::time_stamp() 158 | { 159 | time_t now; 160 | time(&now); 161 | return (uint32_t) now; 162 | } 163 | 164 | } } // End namespace 165 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/depth_feed_publisher.h: -------------------------------------------------------------------------------- 1 | #ifndef example_depth_feed_publisher_h 2 | #define example_depth_feed_publisher_h 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include "example_order_book.h" 16 | #include "book/types.h" 17 | #include "depth_feed_connection.h" 18 | #include "template_consumer.h" 19 | 20 | namespace liquibook { namespace examples { 21 | 22 | class DepthFeedPublisher : public ExampleOrderBook::TypedDepthListener, 23 | public ExampleOrderBook::TypedTradeListener, 24 | public TemplateConsumer { 25 | public: 26 | DepthFeedPublisher(); 27 | void set_connection(DepthFeedConnection* connection); 28 | 29 | virtual void on_trade( 30 | const book::OrderBook* order_book, 31 | book::Quantity qty, 32 | book::Cost cost); 33 | 34 | virtual void on_depth_change( 35 | const book::DepthOrderBook* order_book, 36 | const book::DepthOrderBook::DepthTracker* tracker); 37 | private: 38 | DepthFeedConnection* connection_; 39 | 40 | // Build an trade message 41 | void build_trade_message( 42 | QuickFAST::Messages::FieldSet& message, 43 | const std::string& symbol, 44 | book::Quantity qty, 45 | book::Cost cost); 46 | 47 | // Build an incremental depth message 48 | void build_depth_message( 49 | QuickFAST::Messages::FieldSet& message, 50 | const std::string& symbol, 51 | const book::DepthOrderBook::DepthTracker* tracker, 52 | bool full_message); 53 | void build_depth_level( 54 | QuickFAST::Messages::SequencePtr& level_seq, 55 | const book::DepthLevel* level, 56 | int level_index); 57 | uint32_t time_stamp(); 58 | }; 59 | 60 | } } // End namespace 61 | #endif 62 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/depth_feed_publisher.mpc: -------------------------------------------------------------------------------- 1 | 2 | project(depth_feed_publisher) : QuickFASTApplication, liquibook_book, liquibook_simple, liquibook_exe { 3 | requires += example_pubsub 4 | Source_Files { 5 | publisher_main.cpp 6 | depth_feed_connection.cpp 7 | depth_feed_publisher.cpp 8 | template_consumer.cpp 9 | example_order_book.cpp 10 | exchange.cpp 11 | order.cpp 12 | } 13 | exename = * 14 | } 15 | 16 | project(depth_feed_subscriber) : QuickFASTApplication, liquibook_book, liquibook_simple, liquibook_exe { 17 | requires += example_pubsub 18 | Source_Files { 19 | subscriber_main.cpp 20 | depth_feed_connection.cpp 21 | depth_feed_subscriber.cpp 22 | template_consumer.cpp 23 | order.cpp 24 | } 25 | exename = * 26 | } 27 | 28 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/depth_feed_subscriber.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "template_consumer.h" 13 | #include "depth_feed_connection.h" 14 | #include "book/depth.h" 15 | 16 | namespace QuickFAST { namespace Messages { 17 | class Message; 18 | } } 19 | 20 | namespace liquibook { namespace examples { 21 | 22 | class DepthFeedSubscriber : public TemplateConsumer { 23 | public: 24 | DepthFeedSubscriber( 25 | const QuickFAST::Codecs::TemplateRegistryPtr& templates); 26 | 27 | // Handle a reset of the connection 28 | void handle_reset(); 29 | 30 | // Handle a message 31 | // return false if failure 32 | bool handle_message(BufferPtr& bp, size_t bytes_transferred); 33 | 34 | private: 35 | QuickFAST::Codecs::Decoder decoder_; 36 | typedef std::map > DepthMap; 37 | DepthMap depth_map_; 38 | uint64_t expected_seq_; 39 | 40 | static const uint64_t MSG_TYPE_DEPTH; 41 | static const uint64_t MSG_TYPE_TRADE; 42 | 43 | void log_depth(book::Depth<5>& depth); 44 | bool handle_trade_message(const std::string& symbol, 45 | uint64_t& seq_num, 46 | uint64_t& timestamp, 47 | QuickFAST::Messages::Message& msg); 48 | bool handle_depth_message(const std::string& symbol, 49 | uint64_t& seq_num, 50 | uint64_t& timestamp, 51 | QuickFAST::Messages::Message& msg); 52 | }; 53 | } } 54 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/example_order_book.cpp: -------------------------------------------------------------------------------- 1 | #include "example_order_book.h" 2 | 3 | namespace liquibook { namespace examples { 4 | 5 | ExampleOrderBook::ExampleOrderBook(const std::string& symbol) 6 | : symbol_(symbol) 7 | { 8 | } 9 | 10 | const std::string& 11 | ExampleOrderBook::symbol() const 12 | { 13 | return symbol_; 14 | } 15 | 16 | } } // End namespace 17 | 18 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/example_order_book.h: -------------------------------------------------------------------------------- 1 | #ifndef example_order_book_h 2 | #define example_order_book_h 3 | 4 | #include "order.h" 5 | #include "book/depth_order_book.h" 6 | #include 7 | 8 | namespace liquibook { namespace examples { 9 | 10 | typedef boost::shared_ptr OrderPtr; 11 | 12 | class ExampleOrderBook : public book::DepthOrderBook { 13 | public: 14 | ExampleOrderBook(const std::string& symbol); 15 | const std::string& symbol() const; 16 | 17 | private: 18 | std::string symbol_; 19 | }; 20 | 21 | } } // End namespace 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/exchange.cpp: -------------------------------------------------------------------------------- 1 | #include "exchange.h" 2 | 3 | namespace liquibook { namespace examples { 4 | 5 | Exchange::Exchange(ExampleOrderBook::TypedDepthListener* depth_listener, 6 | ExampleOrderBook::TypedTradeListener* trade_listener) 7 | : depth_listener_(depth_listener), 8 | trade_listener_(trade_listener) 9 | { 10 | } 11 | 12 | void 13 | Exchange::add_order_book(const std::string& sym) 14 | { 15 | std::pair result; 16 | result = order_books_.insert(std::make_pair(sym, ExampleOrderBook(sym))); 17 | result.first->second.set_depth_listener(depth_listener_); 18 | result.first->second.set_trade_listener(trade_listener_); 19 | } 20 | 21 | void 22 | Exchange::add_order(const std::string& symbol, OrderPtr& order) 23 | { 24 | OrderBookMap::iterator order_book = order_books_.find(symbol); 25 | if (order_book != order_books_.end()) { 26 | order_book->second.add(order); 27 | order_book->second.perform_callbacks(); 28 | } 29 | } 30 | 31 | } } // End namespace 32 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/exchange.h: -------------------------------------------------------------------------------- 1 | #ifndef example_exchange_h 2 | #define example_exchange_h 3 | 4 | #include "order.h" 5 | #include "example_order_book.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace liquibook { namespace examples { 12 | 13 | class Exchange { 14 | public: 15 | Exchange(ExampleOrderBook::TypedDepthListener* depth_listener, 16 | ExampleOrderBook::TypedTradeListener* trade_listener); 17 | 18 | // Permanently add an order book to the exchange 19 | void add_order_book(const std::string& symbol); 20 | 21 | // Handle an incoming order 22 | void add_order(const std::string& symbol, OrderPtr& order); 23 | private: 24 | typedef std::map OrderBookMap; 25 | OrderBookMap order_books_; 26 | ExampleOrderBook::TypedDepthListener* depth_listener_; 27 | ExampleOrderBook::TypedTradeListener* trade_listener_; 28 | }; 29 | 30 | } } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/order.cpp: -------------------------------------------------------------------------------- 1 | #include "order.h" 2 | 3 | namespace liquibook { namespace examples { 4 | 5 | const uint8_t Order::precision_(100); 6 | 7 | Order::Order(bool buy, const double& price, book::Quantity qty) 8 | : is_buy_(buy), 9 | price_(price), 10 | qty_(qty) 11 | { 12 | } 13 | 14 | bool 15 | Order::is_buy() const 16 | { 17 | return is_buy_; 18 | } 19 | 20 | book::Quantity 21 | Order::order_qty() const 22 | { 23 | return qty_; 24 | } 25 | 26 | book::Price 27 | Order::price() const 28 | { 29 | return book::Price(price_ * precision_); 30 | } 31 | 32 | } } // End namespace 33 | 34 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/order.h: -------------------------------------------------------------------------------- 1 | #ifndef example_order_h 2 | #define example_order_h 3 | 4 | #include "book/order.h" 5 | 6 | namespace liquibook { namespace examples { 7 | 8 | class Order : public book::Order { 9 | public: 10 | Order(bool buy, 11 | const double& price, 12 | book::Quantity qty); 13 | 14 | virtual bool is_buy() const; 15 | virtual book::Quantity order_qty() const; 16 | virtual book::Price price() const; 17 | 18 | static const uint8_t precision_; 19 | private: 20 | bool is_buy_; 21 | double price_; 22 | book::Quantity qty_; 23 | }; 24 | 25 | } } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/sleep.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef _WIN32 3 | #include 4 | static const unsigned int MILLISECONDS_PER_SECOND = 1000; 5 | inline void sleep(unsigned int seconds) 6 | { 7 | ::Sleep(MILLISECONDS_PER_SECOND * seconds); 8 | } 9 | #else 10 | #include 11 | #endif -------------------------------------------------------------------------------- /examples/depth_feed_publisher/subscriber_main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "depth_feed_connection.h" 4 | #include "depth_feed_subscriber.h" 5 | 6 | int main(int argc, const char* argv[]) 7 | { 8 | try 9 | { 10 | // Create the connection 11 | liquibook::examples::DepthFeedConnection connection(argc, argv); 12 | 13 | // Create feed subscriber 14 | liquibook::examples::DepthFeedSubscriber feed(connection.get_templates()); 15 | 16 | // Set up handlers 17 | liquibook::examples::MessageHandler msg_handler = 18 | boost::bind(&liquibook::examples::DepthFeedSubscriber::handle_message, 19 | &feed, _1, _2); 20 | liquibook::examples::ResetHandler reset_handler = 21 | boost::bind(&liquibook::examples::DepthFeedSubscriber::handle_reset, 22 | &feed); 23 | connection.set_message_handler(msg_handler); 24 | connection.set_reset_handler(reset_handler); 25 | 26 | // Connect to server 27 | connection.connect(); 28 | connection.run(); 29 | } 30 | catch (const std::exception & ex) 31 | { 32 | std::cerr << "Exception caught at main level: " << ex.what() << std::endl; 33 | return -1; 34 | } 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/template_consumer.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "template_consumer.h" 4 | #include 5 | #include 6 | 7 | namespace liquibook { namespace examples { 8 | 9 | using namespace QuickFAST::Messages; 10 | 11 | const FieldIdentity TemplateConsumer::id_seq_num_("SequenceNumber"); 12 | 13 | const FieldIdentity TemplateConsumer::id_msg_type_("MessageType"); 14 | 15 | const FieldIdentity TemplateConsumer::id_timestamp_("Timestamp"); 16 | 17 | const FieldIdentity TemplateConsumer::id_symbol_("Symbol"); 18 | 19 | const FieldIdentity TemplateConsumer::id_bids_("Bids"); 20 | 21 | const FieldIdentity TemplateConsumer::id_bids_length_("BidsLength"); 22 | 23 | const FieldIdentity TemplateConsumer::id_asks_("Asks"); 24 | 25 | const FieldIdentity TemplateConsumer::id_asks_length_("AsksLength"); 26 | 27 | const FieldIdentity TemplateConsumer::id_level_num_("LevelNum"); 28 | 29 | const FieldIdentity TemplateConsumer::id_order_count_("OrderCount"); 30 | 31 | const FieldIdentity TemplateConsumer::id_size_("AggregateQty"); 32 | 33 | const FieldIdentity TemplateConsumer::id_price_("Price"); 34 | 35 | const FieldIdentity TemplateConsumer::id_qty_("Quantity"); 36 | 37 | const FieldIdentity TemplateConsumer::id_cost_("Cost"); 38 | 39 | QuickFAST::Codecs::TemplateRegistryPtr 40 | TemplateConsumer::parse_templates(const std::string& template_filename) 41 | { 42 | std::ifstream template_stream(template_filename.c_str()); 43 | QuickFAST::Codecs::XMLTemplateParser parser; 44 | return parser.parse(template_stream); 45 | } 46 | 47 | } } 48 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/template_consumer.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace liquibook { namespace examples { 8 | 9 | class TemplateConsumer { 10 | public: 11 | static QuickFAST::Codecs::TemplateRegistryPtr 12 | parse_templates(const std::string& template_filename); 13 | 14 | // Trade field identities 15 | static const QuickFAST::Messages::FieldIdentity id_qty_; 16 | static const QuickFAST::Messages::FieldIdentity id_cost_; 17 | 18 | // Common field identities 19 | static const QuickFAST::Messages::FieldIdentity id_seq_num_; 20 | static const QuickFAST::Messages::FieldIdentity id_msg_type_; 21 | static const QuickFAST::Messages::FieldIdentity id_timestamp_; 22 | static const QuickFAST::Messages::FieldIdentity id_symbol_; 23 | 24 | // Depth field identities 25 | static const QuickFAST::Messages::FieldIdentity id_bids_length_; 26 | static const QuickFAST::Messages::FieldIdentity id_bids_; 27 | static const QuickFAST::Messages::FieldIdentity id_asks_length_; 28 | static const QuickFAST::Messages::FieldIdentity id_asks_; 29 | 30 | static const QuickFAST::Messages::FieldIdentity id_level_num_; 31 | static const QuickFAST::Messages::FieldIdentity id_order_count_; 32 | static const QuickFAST::Messages::FieldIdentity id_price_; 33 | static const QuickFAST::Messages::FieldIdentity id_size_; 34 | }; 35 | 36 | } } 37 | 38 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/templates/ComplexTemplates.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 93 | 94 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/templates/Simple.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 27 | 28 | -------------------------------------------------------------------------------- /examples/depth_feed_publisher/templates/depth.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 23 | 24 | 66 | 67 | -------------------------------------------------------------------------------- /examples/mt_order_entry/Market.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "Order.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace orderentry 18 | { 19 | typedef liquibook::book::OrderBook OrderBook; 20 | typedef std::shared_ptr OrderBookPtr; 21 | typedef liquibook::book::DepthOrderBook DepthOrderBook; 22 | typedef std::shared_ptr DepthOrderBookPtr; 23 | typedef liquibook::book::Depth<> BookDepth; 24 | 25 | class Market 26 | : public liquibook::book::OrderListener 27 | , public liquibook::book::TradeListener 28 | , public liquibook::book::OrderBookListener 29 | , public liquibook::book::BboListener 30 | , public liquibook::book::DepthListener 31 | { 32 | typedef std::map OrderMap; 33 | typedef std::map SymbolToBookMap; 34 | public: 35 | Market(std::ostream * logFile = &std::cout); 36 | ~Market(); 37 | 38 | /// @brief What to display to user when requesting input 39 | static const char * prompt(); 40 | 41 | /// @brief Help for user's input 42 | static void help(std::ostream & out = std::cout); 43 | 44 | /// @brief Apply a user command that has been parsed into tokens. 45 | bool apply(const std::vector & tokens); 46 | 47 | public: 48 | ///////////////////////////////////// 49 | // Implement OrderListener interface 50 | 51 | /// @brief callback for an order accept 52 | virtual void on_accept(const OrderPtr& order); 53 | 54 | /// @brief callback for an order reject 55 | virtual void on_reject(const OrderPtr& order, const char* reason); 56 | 57 | /// @brief callback for an order fill 58 | /// @param order the inbound order 59 | /// @param matched_order the matched order 60 | /// @param fill_qty the quantity of this fill 61 | /// @param fill_cost the cost of this fill (qty * price) 62 | virtual void on_fill(const OrderPtr& order, 63 | const OrderPtr& matched_order, 64 | liquibook::book::Quantity fill_qty, 65 | liquibook::book::Cost fill_cost); 66 | 67 | /// @brief callback for an order cancellation 68 | virtual void on_cancel(const OrderPtr& order); 69 | 70 | /// @brief callback for an order cancel rejection 71 | virtual void on_cancel_reject(const OrderPtr& order, const char* reason); 72 | 73 | /// @brief callback for an order replace 74 | /// @param order the replaced order 75 | /// @param size_delta the change to order quantity 76 | /// @param new_price the updated order price 77 | virtual void on_replace(const OrderPtr& order, 78 | const int64_t& size_delta, 79 | liquibook::book::Price new_price); 80 | 81 | /// @brief callback for an order replace rejection 82 | virtual void on_replace_reject(const OrderPtr& order, const char* reason); 83 | 84 | //////////////////////////////////// 85 | // Implement TradeListener interface 86 | 87 | /// @brief callback for a trade 88 | /// @param book the order book of the fill (not defined whether this is before 89 | /// or after fill) 90 | /// @param qty the quantity of this fill 91 | /// @param cost the cost of this fill (qty * price) 92 | virtual void on_trade(const OrderBook* book, 93 | liquibook::book::Quantity qty, 94 | liquibook::book::Cost cost); 95 | 96 | ///////////////////////////////////////// 97 | // Implement OrderBookListener interface 98 | 99 | /// @brief callback for change anywhere in order book 100 | virtual void on_order_book_change(const OrderBook* book); 101 | 102 | ///////////////////////////////////////// 103 | // Implement BboListener interface 104 | void on_bbo_change(const DepthOrderBook * book, const BookDepth * depth); 105 | 106 | ///////////////////////////////////////// 107 | // Implement DepthListener interface 108 | void on_depth_change(const DepthOrderBook * book, const BookDepth * depth); 109 | 110 | private: 111 | //////////////////////////////////// 112 | // Command implementatiokns 113 | bool doAdd(const std::string & side, const std::vector & tokens, size_t pos); 114 | bool doCancel(const std::vector & tokens, size_t position); 115 | bool doModify(const std::vector & tokens, size_t position); 116 | bool doDisplay(const std::vector & tokens, size_t position); 117 | 118 | //////////////////////// 119 | // Order book interactions 120 | bool symbolIsDefined(const std::string & symbol); 121 | OrderBookPtr findBook(const std::string & symbol); 122 | OrderBookPtr addBook(const std::string & symbol, bool useDepthBook); 123 | bool findExistingOrder(const std::vector & tokens, size_t & position, OrderPtr & order, OrderBookPtr & book); 124 | bool findExistingOrder(const std::string & orderId, OrderPtr & order, OrderBookPtr & book); 125 | 126 | std::ostream & out() 127 | { 128 | return *logFile_; 129 | } 130 | private: 131 | static uint32_t orderIdSeed_; 132 | 133 | std::ostream * logFile_; 134 | 135 | OrderMap orders_; 136 | SymbolToBookMap books_; 137 | 138 | }; 139 | 140 | } // namespace orderentry 141 | -------------------------------------------------------------------------------- /examples/mt_order_entry/Order.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #include "Order.h" 5 | #include 6 | 7 | namespace orderentry 8 | { 9 | 10 | Order::Order(const std::string & id, 11 | bool buy_side, 12 | liquibook::book::Quantity quantity, 13 | std::string symbol, 14 | liquibook::book::Price price, 15 | liquibook::book::Price stopPrice, 16 | bool aon, 17 | bool ioc) 18 | : id_(id) 19 | , buy_side_(buy_side) 20 | , symbol_(symbol) 21 | , quantity_(quantity) 22 | , price_(price) 23 | , stopPrice_(stopPrice) 24 | , ioc_(ioc) 25 | , aon_(aon) 26 | , quantityFilled_(0) 27 | , quantityOnMarket_(0) 28 | , fillCost_(0) 29 | , verbose_(false) 30 | 31 | { 32 | } 33 | 34 | std::string 35 | Order::order_id() const 36 | { 37 | return id_; 38 | } 39 | 40 | bool 41 | Order::is_limit() const 42 | { 43 | return price() != 0; 44 | } 45 | 46 | bool 47 | Order::is_buy() const 48 | { 49 | return buy_side_; 50 | } 51 | 52 | bool 53 | Order::all_or_none() const 54 | { 55 | return aon_; 56 | } 57 | 58 | bool 59 | Order::immediate_or_cancel() const 60 | { 61 | return ioc_; 62 | } 63 | 64 | std::string 65 | Order::symbol() const 66 | { 67 | return symbol_; 68 | } 69 | 70 | liquibook::book::Price 71 | Order::price() const 72 | { 73 | return price_; 74 | } 75 | 76 | liquibook::book::Quantity 77 | Order::order_qty() const 78 | { 79 | return quantity_; 80 | } 81 | 82 | 83 | liquibook::book::Price 84 | Order::stop_price() const 85 | { 86 | return stopPrice_; 87 | } 88 | 89 | uint32_t 90 | Order::quantityOnMarket() const 91 | { 92 | return quantityOnMarket_; 93 | } 94 | 95 | uint32_t 96 | Order::quantityFilled() const 97 | { 98 | return quantityFilled_; 99 | } 100 | 101 | uint32_t 102 | Order::fillCost() const 103 | { 104 | return fillCost_; 105 | } 106 | 107 | 108 | const Order::History & 109 | Order::history() const 110 | { 111 | return history_; 112 | } 113 | 114 | const Order::StateChange & 115 | Order::currentState() const 116 | { 117 | return history_.back(); 118 | } 119 | 120 | 121 | Order & 122 | Order::verbose(bool verbose) 123 | { 124 | verbose_ = verbose; 125 | return *this; 126 | } 127 | 128 | bool 129 | Order::isVerbose() const 130 | { 131 | return verbose_; 132 | } 133 | 134 | void 135 | Order::onSubmitted() 136 | { 137 | std::stringstream msg; 138 | msg << (is_buy() ? "BUY " : "SELL ") << quantity_ << ' ' << symbol_ << " @"; 139 | if( price_ == 0) 140 | { 141 | msg << "MKT"; 142 | } 143 | else 144 | { 145 | msg << price_; 146 | } 147 | history_.emplace_back(Submitted, msg.str()); 148 | } 149 | 150 | void 151 | Order::onAccepted() 152 | { 153 | quantityOnMarket_ = quantity_; 154 | history_.emplace_back(Accepted); 155 | } 156 | 157 | void 158 | Order::onRejected(const char * reason) 159 | { 160 | history_.emplace_back(Rejected, reason); 161 | } 162 | 163 | void 164 | Order::onFilled( 165 | liquibook::book::Quantity fill_qty, 166 | liquibook::book::Cost fill_cost) 167 | { 168 | quantityOnMarket_ -= fill_qty; 169 | fillCost_ += fill_cost; 170 | 171 | std::stringstream msg; 172 | msg << fill_qty << " for " << fill_cost; 173 | history_.emplace_back(Filled, msg.str()); 174 | } 175 | 176 | void 177 | Order::onCancelRequested() 178 | { 179 | history_.emplace_back(CancelRequested); 180 | } 181 | 182 | void 183 | Order::onCancelled() 184 | { 185 | quantityOnMarket_ = 0; 186 | history_.emplace_back(Cancelled); 187 | } 188 | 189 | void 190 | Order::onCancelRejected(const char * reason) 191 | { 192 | history_.emplace_back(CancelRejected, reason); 193 | } 194 | 195 | void 196 | Order::onReplaceRequested( 197 | const int32_t& size_delta, 198 | liquibook::book::Price new_price) 199 | { 200 | std::stringstream msg; 201 | if(size_delta != liquibook::book::SIZE_UNCHANGED) 202 | { 203 | msg << "Quantity change: " << size_delta << ' '; 204 | } 205 | if(new_price != liquibook::book::PRICE_UNCHANGED) 206 | { 207 | msg << "New Price " << new_price; 208 | } 209 | history_.emplace_back(ModifyRequested, msg.str()); 210 | } 211 | 212 | void 213 | Order::onReplaced(const int32_t& size_delta, 214 | liquibook::book::Price new_price) 215 | { 216 | std::stringstream msg; 217 | if(size_delta != liquibook::book::SIZE_UNCHANGED) 218 | { 219 | quantity_ += size_delta; 220 | quantityOnMarket_ += size_delta; 221 | msg << "Quantity change: " << size_delta << ' '; 222 | } 223 | if(new_price != liquibook::book::PRICE_UNCHANGED) 224 | { 225 | price_ = new_price; 226 | msg << "New Price " << new_price; 227 | } 228 | history_.emplace_back(Modified, msg.str()); 229 | } 230 | 231 | void 232 | Order::onReplaceRejected(const char * reason) 233 | { 234 | history_.emplace_back(ModifyRejected, reason); 235 | } 236 | 237 | std::ostream & operator << (std::ostream & out, const Order::StateChange & event) 238 | { 239 | out << "{"; 240 | switch(event.state_) 241 | { 242 | case Order::Submitted: 243 | out << "Submitted "; 244 | break; 245 | case Order::Rejected: 246 | out << "Rejected "; 247 | break; 248 | case Order::Accepted: 249 | out << "Accepted "; 250 | break; 251 | case Order::ModifyRequested: 252 | out << "ModifyRequested "; 253 | break; 254 | case Order::ModifyRejected: 255 | out << "ModifyRejected "; 256 | break; 257 | case Order::Modified: 258 | out << "Modified "; 259 | break; 260 | case Order::PartialFilled: 261 | out << "PartialFilled "; 262 | break; 263 | case Order::Filled: 264 | out << "Filled "; 265 | break; 266 | case Order::CancelRequested: 267 | out << "CancelRequested "; 268 | break; 269 | case Order::CancelRejected: 270 | out << "CancelRejected "; 271 | break; 272 | case Order::Cancelled: 273 | out << "Cancelled "; 274 | break; 275 | case Order::Unknown: 276 | out << "Unknown "; 277 | break; 278 | } 279 | out << event.description_; 280 | out << "}"; 281 | return out; 282 | } 283 | 284 | std::ostream & operator << (std::ostream & out, const Order & order) 285 | { 286 | out << "[#" << order.order_id(); 287 | out << ' ' << (order.is_buy() ? "BUY" : "SELL"); 288 | out << ' ' << order.order_qty(); 289 | out << ' ' << order.symbol(); 290 | if(order.price() == 0) 291 | { 292 | out << " MKT"; 293 | } 294 | else 295 | { 296 | out << " $" << order.price(); 297 | } 298 | 299 | if(order.stop_price() != 0) 300 | { 301 | out << " STOP " << order.stop_price(); 302 | } 303 | 304 | out << (order.all_or_none() ? " AON" : "") 305 | << (order.immediate_or_cancel() ? " IOC" : ""); 306 | 307 | auto onMarket = order.quantityOnMarket(); 308 | if(onMarket != 0) 309 | { 310 | out << " Open: " << onMarket; 311 | } 312 | 313 | auto filled = order.quantityFilled(); 314 | if(filled != 0) 315 | { 316 | out << " Filled: " << filled; 317 | } 318 | 319 | auto cost = order.fillCost(); 320 | if(cost != 0) 321 | { 322 | out << " Cost: " << cost; 323 | } 324 | 325 | if(order.isVerbose()) 326 | { 327 | const Order::History & history = order.history(); 328 | for(auto event = history.begin(); event != history.end(); ++event) 329 | { 330 | out << "\n\t" << *event; 331 | } 332 | } 333 | else 334 | { 335 | out << " Last Event:" << order.currentState(); 336 | } 337 | 338 | out << ']'; 339 | 340 | return out; 341 | } 342 | 343 | 344 | } 345 | -------------------------------------------------------------------------------- /examples/mt_order_entry/Order.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include "OrderFwd.h" 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | namespace orderentry 13 | { 14 | 15 | class Order 16 | { 17 | public: 18 | enum State{ 19 | Submitted, 20 | Rejected, // Terminal state 21 | Accepted, 22 | ModifyRequested, 23 | ModifyRejected, 24 | Modified, 25 | PartialFilled, 26 | Filled, // Terminal State 27 | CancelRequested, 28 | CancelRejected, 29 | Cancelled, // Terminal state 30 | Unknown 31 | }; 32 | 33 | struct StateChange 34 | { 35 | State state_; 36 | std::string description_; 37 | StateChange() 38 | : state_(Unknown) 39 | {} 40 | 41 | StateChange(State state, const std::string & description = "") 42 | : state_(state) 43 | , description_(description) 44 | {} 45 | }; 46 | typedef std::vector History; 47 | public: 48 | Order(const std::string & id, 49 | bool buy_side, 50 | liquibook::book::Quantity quantity, 51 | std::string symbol, 52 | liquibook::book::Price price, 53 | liquibook::book::Price stopPrice, 54 | bool aon, 55 | bool ioc); 56 | 57 | ////////////////////////// 58 | // Implement the 59 | // liquibook::book::order 60 | // concept. 61 | 62 | /// @brief is this a limit order? 63 | bool is_limit() const; 64 | 65 | /// @brief is this order a buy? 66 | bool is_buy() const; 67 | 68 | /// @brief get the price of this order, or 0 if a market order 69 | liquibook::book::Price price() const; 70 | 71 | /// @brief get the stop price (if any) for this order. 72 | /// @returns the stop price or zero if not a stop order 73 | liquibook::book::Price stop_price() const; 74 | 75 | /// @brief get the quantity of this order 76 | liquibook::book::Quantity order_qty() const; 77 | 78 | /// @brief if no trades should happen until the order 79 | /// can be filled completely. 80 | /// Note: one or more trades may be used to fill the order. 81 | virtual bool all_or_none() const; 82 | 83 | /// @brief After generating as many trades as possible against 84 | /// orders already on the market, cancel any remaining quantity. 85 | virtual bool immediate_or_cancel() const; 86 | 87 | std::string symbol() const; 88 | 89 | std::string order_id() const; 90 | 91 | uint32_t quantityFilled() const; 92 | 93 | uint32_t quantityOnMarket() const; 94 | 95 | uint32_t fillCost() const; 96 | 97 | Order & verbose(bool verbose = true); 98 | bool isVerbose()const; 99 | const History & history() const; 100 | const StateChange & currentState() const; 101 | 102 | /////////////////////////// 103 | // Order life cycle events 104 | void onSubmitted(); 105 | void onAccepted(); 106 | void onRejected(const char * reason); 107 | 108 | void onFilled( 109 | liquibook::book::Quantity fill_qty, 110 | liquibook::book::Cost fill_cost); 111 | 112 | void onCancelRequested(); 113 | void onCancelled(); 114 | void onCancelRejected(const char * reason); 115 | 116 | void onReplaceRequested( 117 | const int32_t& size_delta, 118 | liquibook::book::Price new_price); 119 | 120 | void onReplaced(const int32_t& size_delta, 121 | liquibook::book::Price new_price); 122 | 123 | void onReplaceRejected(const char * reaseon); 124 | 125 | private: 126 | std::string id_; 127 | bool buy_side_; 128 | std::string symbol_; 129 | liquibook::book::Quantity quantity_; 130 | liquibook::book::Price price_; 131 | liquibook::book::Price stopPrice_; 132 | 133 | bool aon_; 134 | bool ioc_; 135 | 136 | liquibook::book::Quantity quantityFilled_; 137 | int32_t quantityOnMarket_; 138 | uint32_t fillCost_; 139 | 140 | std::vector history_; 141 | bool verbose_; 142 | }; 143 | 144 | std::ostream & operator << (std::ostream & out, const Order & order); 145 | std::ostream & operator << (std::ostream & out, const Order::StateChange & event); 146 | 147 | } 148 | -------------------------------------------------------------------------------- /examples/mt_order_entry/OrderFwd.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | #include 6 | namespace orderentry 7 | { 8 | class Order; 9 | typedef std::shared_ptr OrderPtr; 10 | } 11 | -------------------------------------------------------------------------------- /examples/mt_order_entry/TestOneAonBidTwoAsk.script: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2017 Object Computing, Inc. 2 | ## All rights reserved. 3 | ## See the file license.txt for licensing information. 4 | 5 | # Run the One AON bid, two AON ask test 6 | BUY 300 +IBM 1251 AON; 7 | SELL 100 IBM 1251; 8 | SELL 200 IBM 1251; 9 | #expect execution, but currently the test fails 10 | D + ALL 11 | -------------------------------------------------------------------------------- /examples/mt_order_entry/Util.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #include "Util.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace orderentry 11 | { 12 | std::string nextToken(const std::vector & tokens, size_t & pos) 13 | { 14 | if(pos < tokens.size()) 15 | { 16 | return tokens[pos++]; 17 | } 18 | return ""; 19 | } 20 | 21 | uint32_t toUint32(const std::string & input) 22 | { 23 | char * end; 24 | uint32_t value = strtoul(input.c_str(), &end, 10); 25 | if(*end != '\0') 26 | { 27 | value = INVALID_UINT32; 28 | } 29 | return value; 30 | } 31 | 32 | uint32_t toInt32(const std::string & input) 33 | { 34 | char * end; 35 | uint32_t value = strtol(input.c_str(), &end, 10); 36 | if(*end != '\0') 37 | { 38 | value = INVALID_INT32; 39 | } 40 | return value; 41 | } 42 | 43 | 44 | liquibook::book::Price stringToPrice(const std::string & str) 45 | { 46 | if(str == "MARKET" || str == "MKT") 47 | { 48 | return 0; 49 | } else 50 | { 51 | return toUint32(str); 52 | } 53 | } 54 | 55 | std::string promptForString(const std::string & prompt, bool uppercase) 56 | { 57 | std::cout << "\n" << prompt << ": " << std::flush; 58 | std::string input; 59 | std::getline(std::cin, input); 60 | if(uppercase) 61 | { 62 | std::transform(input.begin(), input.end(), input.begin(), toupper); 63 | } 64 | return input; 65 | } 66 | 67 | liquibook::book::Price promptForPrice(const std::string & prompt) 68 | { 69 | std::string str = promptForString(prompt); 70 | return stringToPrice(str); 71 | } 72 | 73 | uint32_t promptForUint32(const std::string & prompt) 74 | { 75 | std::cout << "\n" << prompt << ": " << std::flush; 76 | std::string input; 77 | std::getline(std::cin, input); 78 | return toUint32(input); 79 | } 80 | 81 | int32_t promptForInt32(const std::string & prompt) 82 | { 83 | std::cout << "\n" << prompt << ": " << std::flush; 84 | std::string input; 85 | std::getline(std::cin, input); 86 | return toInt32(input); 87 | } 88 | 89 | bool promptForYesNo(const std::string & prompt) 90 | { 91 | while(true) 92 | { 93 | std::string input = promptForString(prompt); 94 | if(input == "Y" || input == "YES" || input == "T" || input == "TRUE") 95 | { 96 | return true; 97 | } 98 | if(input == "N" || input == "NO" || input == "F" || input == "FALSE") 99 | { 100 | return false; 101 | } 102 | } 103 | } 104 | 105 | // trim from start (in place) 106 | std::string & ltrim(std::string &s) { 107 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), 108 | std::not1(std::ptr_fun(std::isspace)))); 109 | return s; 110 | } 111 | 112 | // trim from end (in place) 113 | std::string & rtrim(std::string &s) { 114 | s.erase(std::find_if(s.rbegin(), s.rend(), 115 | std::not1(std::ptr_fun(std::isspace))).base(), s.end()); 116 | return s; 117 | } 118 | 119 | // trim from both ends (in place) 120 | std::string & trim(std::string &s) 121 | { 122 | return ltrim(rtrim(s)); 123 | } 124 | 125 | // trim from start (copying) 126 | std::string ltrimmed(std::string s) 127 | { 128 | ltrim(s); 129 | return s; 130 | } 131 | 132 | // trim from end (copying) 133 | std::string rtrimmed(std::string s) 134 | { 135 | rtrim(s); 136 | return s; 137 | } 138 | 139 | // trim from both ends (copying) 140 | std::string trimmed(std::string s) 141 | { 142 | trim(s); 143 | return s; 144 | } 145 | 146 | } 147 | 148 | -------------------------------------------------------------------------------- /examples/mt_order_entry/Util.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | 5 | /// @brief Command parsing helpers 6 | #pragma once 7 | #include 8 | #include 9 | #include 10 | 11 | namespace orderentry 12 | { 13 | static const uint32_t INVALID_UINT32 = UINT32_MAX; 14 | static const int32_t INVALID_INT32 = INT32_MAX; 15 | 16 | /// @brief Parse a string into tokens breaking on delimiters. 17 | /// @param input The string to parse. 18 | /// @param delimiter A set of delimiter characters. 19 | /// @param[out] tokens the tokens parsed from the string. 20 | template 21 | void split(const INPUT_STRING & input, const DELIMITER_STRING & delimiter, STRING_CONTAINER & tokens) 22 | { 23 | size_t pos = 0; 24 | size_t end = input.length(); 25 | while(pos < end) 26 | { 27 | auto last = input.find_first_of(delimiter, pos); 28 | if(last == std::string::npos) 29 | { 30 | last = end; 31 | } 32 | tokens.push_back(input.substr(pos, last - pos)); 33 | pos = ++last; 34 | } 35 | } 36 | 37 | /// @brief Get the next token from a vector of tokens. 38 | /// @param tokens The vector of tokens 39 | /// @param[inout] pos the current position in the vector. Will be updated if a token is found. 40 | /// @return the next token or an empty string if no more tokens. 41 | std::string nextToken(const std::vector & tokens, size_t & pos); 42 | 43 | /// @brief Convert a string to uint32 44 | /// @param input the input string. 45 | /// @return the converted number or INVALID_UINT32 if the string ill-formed. 46 | uint32_t toUint32(const std::string & input); 47 | 48 | /// @brief Convert a string to int32 49 | /// @param input the input string. 50 | /// @return the converted number or INVALID_INT32 if the string ill-formed. 51 | uint32_t toInt32(const std::string & input); 52 | 53 | /// @brief Convert a string to Price 54 | /// @param input the input string which may be a number or "MARKET", "MKT", etc. 55 | /// @return the converted price (MARKET is 0) or INVALID_UINT32 if the string ill-formed. 56 | liquibook::book::Price stringToPrice(const std::string & input); 57 | 58 | /// @brief Display a prompt and ask the console user for a string. 59 | /// @param prompt What to display 60 | /// @returns what the user typed. 61 | std::string promptForString(const std::string & prompt, bool uppercase = true); 62 | 63 | /// @brief Display a prompt and ask the console user for a price. 64 | /// @param prompt What to display 65 | /// @returns what the user typed converted to a price. 66 | liquibook::book::Price promptForPrice(const std::string & prompt); 67 | 68 | /// @brief Display a prompt and ask the console user for a uint32. 69 | /// @param prompt What to display 70 | /// @returns what the user typed converted to a uint32. 71 | uint32_t promptForUint32(const std::string & prompt); 72 | 73 | /// @brief Display a prompt and ask the console user for a int32. 74 | /// @param prompt What to display 75 | /// @returns what the user typed converted to a int32. 76 | int32_t promptForInt32(const std::string & prompt); 77 | 78 | /// @brief Display a prompt and ask the console user for "yes" or "no" 79 | /// @param prompt What to display 80 | /// @returns what the user typed converted to a bool. 81 | bool promptForYesNo(const std::string & prompt); 82 | 83 | /// @brief Remove whitespace from the start of a string (in place) 84 | /// @param[inout] s is the string to be trimmed in place. 85 | /// @returns a reference to the string. 86 | std::string & ltrim(std::string &s); 87 | 88 | /// @brief Remove whitespace from the end of a string (in place) 89 | /// @param[inout] s is the string to be trimmed in place. 90 | /// @returns a reference to the string. 91 | std::string & rtrim(std::string &s); 92 | 93 | /// @brief Remove whitespace from both ends of a string (in place) 94 | /// @param[inout] s is the string to be trimmed in place. 95 | /// @returns a reference to the string. 96 | std::string & trim(std::string &s); 97 | 98 | /// @brief Remove whitespace from the start of a constant string 99 | /// @param s is the string to be trimmed. 100 | /// @return the string after trimming. 101 | std::string ltrimmed(std::string s); 102 | 103 | /// @brief Remove whitespace from the end of a constant string 104 | /// @param s is the string to be trimmed 105 | /// @return the string after trimming. 106 | std::string rtrimmed(std::string s); 107 | 108 | /// @brief Remove whitespace from both ends of a constant string 109 | /// @param s is the string to be trimmed. 110 | /// @return the string after trimming. 111 | std::string trimmed(std::string s); 112 | 113 | } 114 | 115 | -------------------------------------------------------------------------------- /examples/mt_order_entry/mt_order_entry.mpc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | project(*) : liquibook_book, liquibook_simple, liquibook_exe { 5 | requires += example_manual 6 | exename = * 7 | } 8 | -------------------------------------------------------------------------------- /examples/mt_order_entry/mt_order_entry_main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #include "Market.h" 5 | #include "Util.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace orderentry; 16 | 17 | int main(int argc, const char * argv[]) 18 | { 19 | bool done = false; 20 | bool prompt = true; 21 | bool interactive = true; 22 | bool fileActive = false; 23 | std::ostream * log = &std::cout; 24 | std::ifstream commandFile; 25 | std::ofstream logFile; 26 | 27 | if(argc > 1) 28 | { 29 | std::string filename = argv[1]; 30 | if(filename != "-") 31 | { 32 | commandFile.open(filename); 33 | if(!commandFile.good()) 34 | { 35 | std::cerr << "Can't open command file " << filename << ". Exiting." << std::endl; 36 | return -1; 37 | } 38 | interactive = false; 39 | fileActive = true; 40 | } 41 | } 42 | if(argc > 2) 43 | { 44 | const char * filename = argv[2]; 45 | logFile.open(filename); 46 | if(!logFile.good()) 47 | { 48 | std::cerr << "Can't open log file " << filename << ". Exiting." << std::endl; 49 | return -1; 50 | } 51 | log = & logFile; 52 | } 53 | 54 | Market market(log); 55 | while( !done) 56 | { 57 | std::string input; 58 | if(fileActive) 59 | { 60 | std::getline(commandFile, input); 61 | if(!commandFile.good()) 62 | { 63 | if(interactive) 64 | { 65 | input = "# Switching to console input."; 66 | fileActive = false; 67 | } 68 | else 69 | { 70 | input = "# end of command file."; 71 | done = true; 72 | } 73 | } 74 | // if it came from a file, echo it to the log 75 | if(input.substr(0,2) != "##") // don't log ## comments. 76 | { 77 | *log << input << std::endl; 78 | } 79 | } 80 | else 81 | { 82 | if(prompt) 83 | { 84 | std::cout << "Action[" << Market::prompt() 85 | << "\t(?) help for more options and detail.\n" 86 | << "\t(Quit) ]\n"; 87 | prompt = false; 88 | } 89 | std::cout << "> " << std::flush; 90 | std::getline(std::cin, input); 91 | } 92 | std::transform(input.begin(), input.end(), input.begin(), toupper); 93 | if(log != &std::cout && !fileActive) 94 | { 95 | if(input.substr(0,2) != "##") // don't log ## comments. 96 | { 97 | *log << input << std::endl; 98 | } 99 | } 100 | 101 | // if input ends in a ';' be sure there's a space before it to simplify parsing. 102 | if(input.length() > 1) 103 | { 104 | if(input.back() == ';') 105 | { 106 | input.pop_back(); 107 | if(input.back() == ' ') 108 | { 109 | input.pop_back(); 110 | } 111 | input.append(" ;"); 112 | } 113 | } 114 | 115 | std::vector< std::string> words; 116 | split(input," \t\v\n\r", words); 117 | if(!words.empty()) 118 | { 119 | const std::string command = words[0]; 120 | if(command == "QUIT") 121 | { 122 | done = true; 123 | } 124 | else if(command[0] == '#') 125 | { 126 | // nothing 127 | } 128 | else if(command == "F" || command == "FILE") 129 | { 130 | if(fileActive) 131 | { 132 | std::cout << "Only one input file at a time can be open." << std::endl; 133 | } 134 | else 135 | { 136 | std::cout << "Command file name: " << std::flush; 137 | std::string filename; 138 | std::getline(std::cin, filename); 139 | commandFile.open(filename); 140 | if(commandFile.good()) 141 | { 142 | fileActive = true; 143 | } 144 | else 145 | { 146 | std::cout << "Cannot open " << filename << std::endl; 147 | } 148 | } 149 | } 150 | else if(command == "?" || command == "HELP") 151 | { 152 | market.help(); 153 | *log << "(F)ile Open or Close a command input file\n" 154 | << "\tArguments\n" 155 | << "\t\t Required if no file is open. Must not appear if file is open.\n"; 156 | *log << "QUIT Exit from this program.\n"; 157 | bool prompt = true; 158 | } 159 | else if(!market.apply(words)) 160 | { 161 | std::cerr << "Cannot process command"; 162 | for(auto word = words.begin(); word != words.end(); ++ word) 163 | { 164 | std::cerr << ' ' << *word; 165 | } 166 | std::cerr << std::endl; 167 | bool prompt = true; 168 | } 169 | } 170 | } 171 | return 0; 172 | } 173 | -------------------------------------------------------------------------------- /examples/mt_order_entry/order_entry.script: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017 Object Computing, Inc. 2 | # All rights reserved. 3 | # See the file license.txt for licensing information. 4 | ## A simple test script to use with mt_order_entry. 5 | ## To use it: make this file's name the first argument on the command line, or 6 | ## type "FILE filename" at the interactive prompt. 7 | BUY 100 !IBM 50; 8 | SELL 100 IBM 50; 9 | D + ALL 10 | -------------------------------------------------------------------------------- /examples/mt_order_entry/teststoporders.script: -------------------------------------------------------------------------------- 1 | # Script to test stop orders 2 | 3 | # set market price to 56 4 | buy 100 +ibm mkt; 5 | sell 100 ibm 56; 6 | 7 | # enter seed orders that won't trade with each other 8 | buy 100 ibm 53; 9 | sell 100 ibm 58; 10 | 11 | # add stop orders off market 12 | buy 100 ibm mkt stop 57; 13 | sell 100 ibm mkt stop 54; 14 | 15 | # use large AOC orders to set a new market price to 57 16 | # without trading with existing orders 17 | # This will trigger the buy stop market order (#5) 18 | # which will match (and trade with) the sell 58 limit order #4 19 | buy 1000 ibm 57 aon; 20 | sell 1000 ibm 57 aon; 21 | 22 | # do the same thing on the other side to 23 | # hit the stop price of 54 for order #6 24 | sell 1000 ibm 54 aon; 25 | buy 1000 ibm 54 aon; 26 | 27 | # and display the orders, all of which should be filled. 28 | d ibm 29 | quit 30 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, 2013 Object Computing, Inc. All rights reserved. 2 | 3 | Liquibook is a licensed product, is protected by copyright, and is distributed under the following terms: 4 | 5 | Liquibook is an open source implementation of a limit order book matching engine, developed and copyrighted by Object Computing Incorporated (OCI). 6 | 7 | Since Liquibook is open source and free of licensing fees, you are free to use, modify, and distribute the source code, as long as you include this copyright statement. 8 | 9 | In particular, you can use Liquibook to build proprietary software and are under no obligation to redistribute any of your source code that is built using Liquibook. Note however, that you may not do anything to the Liquibook code, such as copyrighting it yourself or claiming authorship of the Liquibook code, that will prevent Liquibook from being distributed freely using an open source development model. 10 | 11 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 12 | 13 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 14 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 15 | * Neither the name of Object Computing, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | 19 | IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | -------------------------------------------------------------------------------- /liquibook.features: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | // This file defines MPC features for the liquibook project 5 | 6 | example_pubsub=1 7 | example_manual=1 8 | 9 | // BOOST IS NEEDED BY THE unit tests 10 | boost=1 11 | // QUICKFAST IS NEEDED BY THE PUBLISH/SUBSUBSCRIBE example 12 | QuickFAST=1 13 | 14 | -------------------------------------------------------------------------------- /liquibook.mwc: -------------------------------------------------------------------------------- 1 | workspace(liquibook) { 2 | cmdline += -include mpc 3 | cmdline += -include $QUICKFAST_ROOT 4 | cmdline += -feature_file liquibook.features 5 | cmdline += -expand_vars 6 | cmdline += -use_env 7 | 8 | specific(make) { 9 | cmdline += -value_template "configurations=Release Debug" 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /mpc.bat: -------------------------------------------------------------------------------- 1 | @REM Copyright (c) 2009, 2010 Object Computing, Inc. 2 | @REM All rights reserved. 3 | @REM See the file license.txt for licensing information. 4 | @REM 5 | @REM This batch file runs MWC which is part of the MPC package to create 6 | @REM Visual Studio solution and project files. 7 | @REM 8 | @REM Note: -expand_vars -use_env options force MPC to expand $(BOOST_ROOT) into the absolute path. This avoids 9 | @REM a problem that happened when people were starting Visual Studio from the Start menu rather than 10 | @REM from the command line where the BOOST_ROOT environment had been defined. 11 | "%MPC_ROOT%\mwc.pl" -type vc%VCVER% liquibook.mwc 12 | -------------------------------------------------------------------------------- /mpc/liquibook.mpb: -------------------------------------------------------------------------------- 1 | project { 2 | includes += $(LIQUIBOOK_ROOT)/src 3 | libpaths += $(LIQUIBOOK_ROOT)/lib 4 | 5 | // Force use of Visual C++ DLL runtime libraries 6 | specific(vc71,vc8,vc9) { 7 | Debug::runtime_library = 3 8 | Release::runtime_library = 2 9 | } 10 | specific(vc10,vc11,vc12,vc13,vc14,vc15) { 11 | Debug::runtime_library = MultiThreadedDebugDLL 12 | Release::runtime_library = MultiThreadedDLL 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /mpc/liquibook_book.mpb: -------------------------------------------------------------------------------- 1 | project : liquibook { 2 | // obsolete 3 | } 4 | 5 | -------------------------------------------------------------------------------- /mpc/liquibook_exe.mpb: -------------------------------------------------------------------------------- 1 | project { 2 | specific(prop:microsoft) { 3 | Release::exeout = $(LIQUIBOOK_ROOT)/Output/Release 4 | Debug::exeout = $(LIQUIBOOK_ROOT)/Output/Debug 5 | } else { 6 | exeout = $(LIQUIBOOK_ROOT)/bin 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /mpc/liquibook_lib.mpb: -------------------------------------------------------------------------------- 1 | project : liquibook { 2 | libout = $(LIQUIBOOK_ROOT)/lib 3 | macros += LIQUIBOOK_BUILD_DLL 4 | } 5 | 6 | -------------------------------------------------------------------------------- /mpc/liquibook_simple.mpb: -------------------------------------------------------------------------------- 1 | project : liquibook { 2 | libs += liquibook_simple 3 | after += liquibook_simple 4 | } 5 | 6 | -------------------------------------------------------------------------------- /mpc/liquibook_test.mpb: -------------------------------------------------------------------------------- 1 | project : liquibook, liquibook_exe, liquibook_book, liquibook_simple{ 2 | exeout = $(LIQUIBOOK_ROOT)/bin/test 3 | } 4 | -------------------------------------------------------------------------------- /noQuickFAST/QuickFASTApplication.mpb: -------------------------------------------------------------------------------- 1 | project{ 2 | requires += QuickFAST 3 | } 4 | -------------------------------------------------------------------------------- /pt_run.bat: -------------------------------------------------------------------------------- 1 | bin\test\pt_order_book.exe 2 | -------------------------------------------------------------------------------- /src/book/bbo_listener.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | namespace liquibook { namespace book { 7 | 8 | /// @brief generic listener of top-of-book events 9 | template 10 | class BboListener { 11 | public: 12 | /// @brief callback for top of book change 13 | virtual void on_bbo_change( 14 | const OrderBook* book, 15 | const typename OrderBook::DepthTracker* depth) = 0; 16 | }; 17 | 18 | } } 19 | -------------------------------------------------------------------------------- /src/book/callback.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include "types.h" 7 | 8 | namespace liquibook { namespace book { 9 | 10 | template 11 | class OrderBook; 12 | 13 | // Callback events 14 | // New order accept 15 | // - order accept 16 | // - fill (2) and/or quote (if not complete) 17 | // - depth/bbo ? 18 | // New order reject 19 | // - order reject 20 | // Order fill 21 | // - fill (2) 22 | // - trade 23 | // - quote (2) 24 | // - depth/bbo ? 25 | // Order cancel 26 | // - order cancel 27 | // - quote 28 | // - depth/bbo ? 29 | // Order cancel reject 30 | // - order cancel reject 31 | // Order replace 32 | // - order replace 33 | // - fill (2) and/or quote (if not complete) 34 | // - depth/bbo ? 35 | // Order replace reject 36 | // - order replace reject 37 | 38 | /// @brief notification from OrderBook of an event 39 | template 40 | class Callback { 41 | public: 42 | typedef OrderBook TypedOrderBook; 43 | 44 | enum CbType { 45 | cb_unknown, 46 | cb_order_accept, 47 | cb_order_accept_stop, 48 | cb_order_trigger_stop, 49 | cb_order_reject, 50 | cb_order_fill, 51 | cb_order_cancel, 52 | cb_order_cancel_stop, 53 | cb_order_cancel_reject, 54 | cb_order_replace, 55 | cb_order_replace_reject, 56 | cb_book_update 57 | }; 58 | 59 | enum FillFlags { 60 | ff_neither_filled = 0, 61 | ff_inbound_filled = 1, 62 | ff_matched_filled = 2, 63 | ff_both_filled = 4 64 | }; 65 | 66 | Callback(); 67 | 68 | /// @brief create a new accept callback 69 | static Callback accept(const OrderPtr& order); 70 | /// @brief create a new accept callback 71 | static Callback accept_stop(const OrderPtr& order); 72 | /// @brief create a new accept callback 73 | static Callback trigger_stop(const OrderPtr& order); 74 | /// @brief create a new reject callback 75 | static Callback reject(const OrderPtr& order, 76 | const char* reason); 77 | /// @brief create a new fill callback 78 | static Callback fill(const OrderPtr& inbound_order, 79 | const OrderPtr& matched_order, 80 | const Quantity& fill_qty, 81 | const Price& fill_price, 82 | FillFlags fill_flags); 83 | /// @brief create a new cancel callback 84 | static Callback cancel(const OrderPtr& order, 85 | const Quantity& open_qty); 86 | /// @brief create a new cancel callback 87 | static Callback cancel_stop(const OrderPtr& order); 88 | /// @brief create a new cancel reject callback 89 | static Callback cancel_reject(const OrderPtr& order, 90 | const char* reason); 91 | /// @brief create a new replace callback 92 | static Callback replace(const OrderPtr& order, 93 | const Quantity& curr_open_qty, 94 | const int64_t& size_delta, 95 | const Price& new_price); 96 | /// @brief create a new replace reject callback 97 | static Callback replace_reject(const OrderPtr& order, 98 | const char* reason); 99 | 100 | static Callback book_update(const TypedOrderBook* book = nullptr); 101 | CbType type; 102 | OrderPtr order; 103 | OrderPtr matched_order; 104 | Quantity quantity; 105 | Price price; 106 | uint8_t flags; 107 | int64_t delta; 108 | const char* reject_reason; 109 | }; 110 | 111 | template 112 | Callback::Callback() 113 | : type(cb_unknown), 114 | order(nullptr), 115 | matched_order(nullptr), 116 | quantity(0), 117 | price(0), 118 | flags(0), 119 | delta(0), 120 | reject_reason(nullptr) 121 | { 122 | } 123 | 124 | template 125 | Callback Callback::accept( 126 | const OrderPtr& order) 127 | { 128 | Callback result; 129 | result.type = cb_order_accept; 130 | result.order = order; 131 | return result; 132 | } 133 | 134 | template 135 | Callback Callback::accept_stop( 136 | const OrderPtr& order) 137 | { 138 | Callback result; 139 | result.type = cb_order_accept_stop; 140 | result.order = order; 141 | return result; 142 | } 143 | 144 | template 145 | Callback Callback::trigger_stop( 146 | const OrderPtr& order) 147 | { 148 | Callback result; 149 | result.type = cb_order_trigger_stop; 150 | result.order = order; 151 | return result; 152 | } 153 | 154 | template 155 | Callback Callback::reject( 156 | const OrderPtr& order, 157 | const char* reason) 158 | { 159 | Callback result; 160 | result.type = cb_order_reject; 161 | result.order = order; 162 | result.reject_reason = reason; 163 | return result; 164 | } 165 | 166 | template 167 | Callback Callback::fill( 168 | const OrderPtr& inbound_order, 169 | const OrderPtr& matched_order, 170 | const Quantity& fill_qty, 171 | const Price& fill_price, 172 | FillFlags fill_flags) 173 | { 174 | Callback result; 175 | result.type = cb_order_fill; 176 | result.order = inbound_order; 177 | result.matched_order = matched_order; 178 | result.quantity = fill_qty; 179 | result.price = fill_price; 180 | result.flags = fill_flags; 181 | return result; 182 | } 183 | 184 | template 185 | Callback Callback::cancel( 186 | const OrderPtr& order, 187 | const Quantity& open_qty) 188 | { 189 | // TODO save the open qty 190 | Callback result; 191 | result.type = cb_order_cancel; 192 | result.order = order; 193 | result.quantity = open_qty; 194 | return result; 195 | } 196 | 197 | template 198 | Callback Callback::cancel_stop( 199 | const OrderPtr& order) 200 | { 201 | Callback result; 202 | result.type = cb_order_cancel_stop; 203 | result.order = order; 204 | return result; 205 | } 206 | 207 | template 208 | Callback Callback::cancel_reject( 209 | const OrderPtr& order, 210 | const char* reason) 211 | { 212 | Callback result; 213 | result.type = cb_order_cancel_reject; 214 | result.order = order; 215 | result.reject_reason = reason; 216 | return result; 217 | } 218 | 219 | template 220 | Callback Callback::replace( 221 | const OrderPtr& order, 222 | const Quantity& curr_open_qty, 223 | const int64_t& size_delta, 224 | const Price& new_price) 225 | { 226 | // TODO save the order open qty 227 | Callback result; 228 | result.type = cb_order_replace; 229 | result.order = order; 230 | result.quantity = curr_open_qty; 231 | result.delta = size_delta; 232 | result.price = new_price; 233 | return result; 234 | } 235 | 236 | template 237 | Callback Callback::replace_reject( 238 | const OrderPtr& order, 239 | const char* reason) 240 | { 241 | Callback result; 242 | result.type = cb_order_replace_reject; 243 | result.order = order; 244 | result.reject_reason = reason; 245 | return result; 246 | } 247 | 248 | template 249 | Callback 250 | Callback::book_update(const OrderBook* book) 251 | { 252 | Callback result; 253 | result.type = cb_book_update; 254 | return result; 255 | } 256 | 257 | } } 258 | -------------------------------------------------------------------------------- /src/book/comparable_price.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include "types.h" 7 | #include 8 | 9 | namespace liquibook { namespace book { 10 | 11 | /// @brief A price that knows which side of the market it is on 12 | /// Designed to be compared to other prices on the same side using 13 | /// standard comparison operators (< > <= >= == !=) and to 14 | /// compared to prices on the other side using the match() method. 15 | /// 16 | /// Using the '<' operation to sort a set of prices will result in the 17 | /// prices being partially ordered from most liquid to least liquid. 18 | /// i.e: 19 | /// Market prices always sort first since they will match any counter price. 20 | /// Sell side low prices sort before high prices because they match more buys. 21 | /// Buy side high prices sort before low prices because they match more asks. 22 | /// 23 | class ComparablePrice 24 | { 25 | Price price_; 26 | bool buySide_; 27 | 28 | public: 29 | /// @brief construct given side and price 30 | /// @param buySide controls whether price comparison is normal or reversed 31 | /// @param price is the price for this key, or 0 (MARKET_ORDER_PRICE) for market 32 | ComparablePrice(bool buySide, Price price) 33 | : price_(price) 34 | , buySide_(buySide) 35 | { 36 | } 37 | 38 | /// @brief Check possible trade 39 | /// Assumes rhs is on the opposite side 40 | bool matches(Price rhs) const 41 | { 42 | if(price_ == rhs) 43 | { 44 | return true; 45 | } 46 | if(buySide_) 47 | { 48 | return rhs < price_ || price_ == MARKET_ORDER_PRICE ; 49 | } 50 | return price_ < rhs || rhs == MARKET_ORDER_PRICE; 51 | } 52 | 53 | /// @brief less than compare key to a price 54 | /// Assumes both prices are on the same side. 55 | /// Uses side to determine the sense of the comparison. 56 | bool operator <(Price rhs) const 57 | { 58 | // Compare difficulty finding a match. (easy is less than hard) 59 | if(price_ == MARKET_ORDER_PRICE) 60 | { 61 | return rhs != MARKET_ORDER_PRICE; 62 | } 63 | else if(rhs == MARKET_ORDER_PRICE) 64 | { 65 | return false; 66 | } 67 | else if(buySide_) 68 | { 69 | // Buying: Highest prices first. 70 | return rhs < price_ ; 71 | } 72 | else 73 | { 74 | // Selling: lowest prices first 75 | return price_ < rhs; 76 | } 77 | } 78 | 79 | /// @brief equality compare key to a price 80 | bool operator ==(Price rhs) const 81 | { 82 | // compares equal without regard to side 83 | return price_ == rhs; 84 | } 85 | 86 | /// @brief inequality compare key to a price 87 | bool operator !=(Price rhs) const 88 | { 89 | return !( price_ == rhs ); 90 | } 91 | 92 | /// @brief greater than compare key to a price 93 | /// Assumes both prices are on the same side. 94 | bool operator > (Price rhs) const 95 | { 96 | return price_!= MARKET_ORDER_PRICE && ((rhs == MARKET_ORDER_PRICE) || (buySide_ ? (rhs > price_) : (price_ > rhs))); 97 | } 98 | 99 | /// @brief less than or equal to compare key to a price 100 | /// Assumes both prices are on the same side. 101 | bool operator <=(Price rhs) const 102 | { 103 | return *this < rhs || *this == rhs; 104 | } 105 | 106 | /// @brief greater than or equal to compare key to a price 107 | /// Assumes both prices are on the same side. 108 | bool operator >=(Price rhs) const 109 | { 110 | return *this > rhs || *this == rhs; 111 | } 112 | 113 | /// @brief less than compare order map keys 114 | /// compares the prices assuming they are on the same side 115 | bool operator <(const ComparablePrice & rhs) const 116 | { 117 | return *this < rhs.price_; 118 | } 119 | 120 | /// @brief equality compare order map keys 121 | bool operator ==(const ComparablePrice & rhs) const 122 | { 123 | return *this == rhs.price_; 124 | } 125 | 126 | /// @brief inequality compare order map keys 127 | bool operator !=(const ComparablePrice & rhs) const 128 | { 129 | return *this != rhs.price_; 130 | } 131 | 132 | /// @brief greater than compare order map keys 133 | /// Assumes both prices are on the same side. 134 | bool operator >(const ComparablePrice & rhs) const 135 | { 136 | return *this > rhs.price_; 137 | } 138 | 139 | /// @brief access price. 140 | Price price() const 141 | { 142 | return price_; 143 | } 144 | 145 | /// @brief access side. 146 | bool isBuy() const 147 | { 148 | return buySide_; 149 | } 150 | 151 | /// @brief check to see if this is market price 152 | bool isMarket() const 153 | { 154 | return price_ == MARKET_ORDER_PRICE; 155 | } 156 | }; 157 | 158 | /// @brief less than compare price to key 159 | inline 160 | bool operator < (Price price, const ComparablePrice & key) 161 | { 162 | return key > price; 163 | } 164 | 165 | /// @brief greater than compare price to key 166 | inline 167 | bool operator > (Price price, const ComparablePrice & key) 168 | { 169 | return key < price; 170 | } 171 | 172 | /// @brief equality compare price to key 173 | inline 174 | bool operator == (Price price, const ComparablePrice & key) 175 | { 176 | return key == price; 177 | } 178 | 179 | /// @brief inequality compare price to key 180 | inline 181 | bool operator != (Price price, const ComparablePrice & key) 182 | { 183 | return key != price; 184 | } 185 | 186 | /// @brief less than or equal to compare price to key 187 | inline 188 | bool operator <= (Price price, const ComparablePrice & key) 189 | { 190 | return key >= price; 191 | } 192 | 193 | /// @brief greater or equal to than compare price to key 194 | inline 195 | bool operator >= (Price price, const ComparablePrice & key) 196 | { 197 | return key <= price; 198 | } 199 | 200 | inline 201 | std::ostream & operator << (std::ostream & out, const ComparablePrice & key) 202 | { 203 | out << (key.isBuy() ? "Buy at " : "Sell at "); 204 | if(key.isMarket()) 205 | { 206 | out << "Market"; 207 | } 208 | else 209 | { 210 | out << key.price(); 211 | } 212 | return out; 213 | } 214 | 215 | }} 216 | -------------------------------------------------------------------------------- /src/book/depth_constants.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 - 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include "types.h" 7 | 8 | namespace liquibook { namespace book { 9 | 10 | namespace { 11 | // Constants used in liquibook 12 | const Price INVALID_LEVEL_PRICE(0); 13 | const Price MARKET_ORDER_BID_SORT_PRICE(QUANTITY_MAX); 14 | const Price MARKET_ORDER_ASK_SORT_PRICE(0); 15 | } 16 | 17 | }} 18 | -------------------------------------------------------------------------------- /src/book/depth_level.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include "depth_constants.h" 7 | 8 | namespace liquibook { namespace book { 9 | 10 | /// @brief a single level of the limit order book aggregated by price 11 | class DepthLevel { 12 | public: 13 | /// @brief construct 14 | DepthLevel(); 15 | 16 | /// @brief assign 17 | DepthLevel& operator=(const DepthLevel& rhs); 18 | 19 | /// @brief get price 20 | const Price& price() const; 21 | /// @brief get count 22 | uint32_t order_count() const; 23 | /// @brief get aggregate quantity 24 | Quantity aggregate_qty() const; 25 | /// @brief is this level part of the excess 26 | bool is_excess() const { return is_excess_; } 27 | 28 | void init(Price price, bool is_excess); 29 | 30 | /// @brief add an order to the level 31 | /// @param qty open quantity of the order 32 | void add_order(Quantity qty); 33 | 34 | /// @brief increase the quantity of existing orders 35 | /// @param qty amount to increase the quantity by 36 | void increase_qty(Quantity qty); 37 | 38 | /// @brief decrease the quantity of existing orders 39 | /// @param qty amount to decrease the quantity by 40 | void decrease_qty(Quantity qty); 41 | 42 | /// @brief overwrite all values of the level 43 | /// @param price the level price 44 | /// @param qty the aggegate quantity 45 | /// @param order_count the number of orders 46 | /// @param last_change the last change ID (optional) 47 | void set(Price price, 48 | Quantity qty, 49 | uint32_t order_count, 50 | ChangeId last_change = 0); 51 | 52 | /// @brief cancel or fill an order, decrease count and quantity 53 | /// @param qty the closed quantity 54 | /// @return true if the level is now empty 55 | bool close_order(Quantity qty); 56 | 57 | /// @brief set last changed stamp on this level 58 | void last_change(ChangeId last_change) { last_change_ = last_change; } 59 | 60 | /// @brief get last change stamp for this level 61 | ChangeId last_change() const { return last_change_; } 62 | 63 | /// @brief has the level changed since the given stamp? 64 | /// @param last_published_change the stamp to compare to 65 | bool changed_since(ChangeId last_published_change) const; 66 | 67 | private: 68 | Price price_; 69 | uint32_t order_count_; 70 | Quantity aggregate_qty_; 71 | bool is_excess_; 72 | public: 73 | ChangeId last_change_; 74 | }; 75 | 76 | inline bool 77 | DepthLevel::changed_since(ChangeId last_published_change) const 78 | { 79 | return last_change_ > last_published_change; 80 | } 81 | 82 | inline 83 | DepthLevel::DepthLevel() 84 | : price_(INVALID_LEVEL_PRICE), 85 | order_count_(0), 86 | aggregate_qty_(0) 87 | { 88 | } 89 | 90 | inline 91 | DepthLevel& 92 | DepthLevel::operator=(const DepthLevel& rhs) 93 | { 94 | price_ = rhs.price_; 95 | order_count_ = rhs.order_count_; 96 | aggregate_qty_ = rhs.aggregate_qty_; 97 | if (rhs.price_ != INVALID_LEVEL_PRICE) { 98 | last_change_ = rhs.last_change_; 99 | } 100 | 101 | // Do not copy is_excess_ 102 | 103 | return *this; 104 | } 105 | 106 | inline 107 | const Price& 108 | DepthLevel::price() const 109 | { 110 | return price_; 111 | } 112 | 113 | inline 114 | void 115 | DepthLevel::init(Price price, bool is_excess) 116 | { 117 | price_ = price; 118 | order_count_ = 0; 119 | aggregate_qty_ = 0; 120 | is_excess_ = is_excess; 121 | } 122 | 123 | inline 124 | uint32_t 125 | DepthLevel::order_count() const 126 | { 127 | return order_count_; 128 | } 129 | 130 | inline 131 | Quantity 132 | DepthLevel::aggregate_qty() const 133 | { 134 | return aggregate_qty_; 135 | } 136 | 137 | inline 138 | void 139 | DepthLevel::add_order(Quantity qty) 140 | { 141 | // Increment/increase 142 | ++order_count_; 143 | aggregate_qty_ += qty; 144 | } 145 | 146 | inline 147 | bool 148 | DepthLevel::close_order(Quantity qty) 149 | { 150 | bool empty = false; 151 | // If this is the last order, reset the level 152 | if (order_count_ == 0) { 153 | throw std::runtime_error("DepthLevel::close_order " 154 | "order count too low"); 155 | } else if (order_count_ == 1) { 156 | order_count_ = 0; 157 | aggregate_qty_ = 0; 158 | empty = true; 159 | // Else, decrement/decrease 160 | } else { 161 | --order_count_; 162 | if (aggregate_qty_ >= qty) { 163 | aggregate_qty_ -= qty; 164 | } else { 165 | throw std::runtime_error("DepthLevel::close_order " 166 | "level quantity too low"); 167 | } 168 | } 169 | return empty; 170 | } 171 | 172 | inline 173 | void 174 | DepthLevel::set(Price price, 175 | Quantity qty, 176 | uint32_t order_count, 177 | ChangeId last_change) 178 | { 179 | price_ = price; 180 | aggregate_qty_ = qty; 181 | order_count_ = order_count; 182 | last_change_ = last_change; 183 | } 184 | 185 | inline 186 | void 187 | DepthLevel::increase_qty(Quantity qty) 188 | { 189 | aggregate_qty_ += qty; 190 | } 191 | 192 | inline 193 | void 194 | DepthLevel::decrease_qty(Quantity qty) 195 | { 196 | aggregate_qty_ -= qty; 197 | } 198 | 199 | } } 200 | 201 | -------------------------------------------------------------------------------- /src/book/depth_listener.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include "order_book.h" 7 | 8 | namespace liquibook { namespace book { 9 | 10 | /// @brief listener of depth events. Implement to build an aggregate depth 11 | /// feed. 12 | template 13 | class DepthListener { 14 | public: 15 | /// @brief callback for change in tracked aggregated depth 16 | virtual void on_depth_change( 17 | const OrderBook* book, 18 | const typename OrderBook::DepthTracker* depth) = 0; 19 | }; 20 | 21 | } } 22 | 23 | -------------------------------------------------------------------------------- /src/book/depth_order_book.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include "order_book.h" 7 | #include "depth.h" 8 | #include "bbo_listener.h" 9 | #include "depth_listener.h" 10 | 11 | namespace liquibook { namespace book { 12 | 13 | /// @brief Implementation of order book child class, that incorporates 14 | /// aggregate depth tracking. 15 | template 16 | class DepthOrderBook : public OrderBook { 17 | public: 18 | typedef Depth DepthTracker; 19 | typedef BboListenerTypedBboListener; 20 | typedef DepthListenerTypedDepthListener; 21 | 22 | /// @brief construct 23 | DepthOrderBook(const std::string & symbol = "unknown"); 24 | 25 | /// @brief set the BBO listener 26 | void set_bbo_listener(TypedBboListener* bbo_listener); 27 | 28 | /// @brief set the depth listener 29 | void set_depth_listener(TypedDepthListener* depth_listener); 30 | 31 | // @brief access the depth tracker 32 | DepthTracker& depth(); 33 | 34 | // @brief access the depth tracker 35 | const DepthTracker& depth() const; 36 | 37 | protected: 38 | ////////////////////////////////// 39 | // Implement virtual callback methods 40 | // needed to maintain depth book. 41 | virtual void on_accept(const OrderPtr& order, Quantity quantity); 42 | virtual void on_accept_stop(const OrderPtr& order); 43 | virtual void on_trigger_stop(const OrderPtr& order); 44 | 45 | virtual void on_fill(const OrderPtr& order, 46 | const OrderPtr& matched_order, 47 | Quantity fill_qty, 48 | Price fill_price, 49 | bool inbound_order_filled, 50 | bool matched_order_filled); 51 | 52 | virtual void on_cancel(const OrderPtr& order, Quantity quantity); 53 | virtual void on_cancel_stop(const OrderPtr& order); 54 | 55 | virtual void on_replace(const OrderPtr& order, 56 | Quantity current_qty, 57 | Quantity new_qty, 58 | Price new_price); 59 | 60 | virtual void on_order_book_change(); 61 | 62 | private: 63 | DepthTracker depth_; 64 | TypedBboListener* bbo_listener_; 65 | TypedDepthListener* depth_listener_; 66 | }; 67 | 68 | template 69 | DepthOrderBook::DepthOrderBook(const std::string & symbol) 70 | : OrderBook(symbol), 71 | bbo_listener_(nullptr), 72 | depth_listener_(nullptr) 73 | { 74 | } 75 | 76 | template 77 | void 78 | DepthOrderBook::set_bbo_listener(TypedBboListener* listener) 79 | { 80 | bbo_listener_ = listener; 81 | } 82 | 83 | template 84 | void 85 | DepthOrderBook::set_depth_listener(TypedDepthListener* listener) 86 | { 87 | depth_listener_ = listener; 88 | } 89 | 90 | template 91 | void 92 | DepthOrderBook::on_accept(const OrderPtr& order, Quantity quantity) 93 | { 94 | // If the order is a limit order 95 | if (order->is_limit()) 96 | { 97 | // If the order is completely filled on acceptance, do not modify 98 | // depth unnecessarily 99 | if (quantity == order->order_qty()) 100 | { 101 | // Don't tell depth about this order - it's going away immediately. 102 | // Instead tell Depth about future fills to ignore 103 | depth_.ignore_fill_qty(quantity, order->is_buy()); 104 | } 105 | else 106 | { 107 | // Add to bid or ask depth 108 | depth_.add_order(order->price(), 109 | order->order_qty(), 110 | order->is_buy()); 111 | } 112 | } 113 | } 114 | 115 | template 116 | void 117 | DepthOrderBook::on_accept_stop(const OrderPtr& order) 118 | { 119 | } 120 | 121 | template 122 | void 123 | DepthOrderBook::on_trigger_stop(const OrderPtr& order) 124 | { 125 | // Add to depth 126 | depth_.add_order(order->price(), order->order_qty(), order->is_buy()); 127 | } 128 | 129 | template 130 | void 131 | DepthOrderBook::on_fill(const OrderPtr& order, 132 | const OrderPtr& matched_order, 133 | Quantity quantity, 134 | Price fill_price, 135 | bool inbound_order_filled, 136 | bool matched_order_filled) 137 | { 138 | // If the matched order is a limit order 139 | if (matched_order->is_limit()) { 140 | // Inform the depth 141 | depth_.fill_order(matched_order->price(), 142 | quantity, 143 | matched_order_filled, 144 | matched_order->is_buy()); 145 | } 146 | // If the inbound order is a limit order 147 | if (order->is_limit()) { 148 | // Inform the depth 149 | depth_.fill_order(order->price(), 150 | quantity, 151 | inbound_order_filled, 152 | order->is_buy()); 153 | } 154 | } 155 | 156 | template 157 | void 158 | DepthOrderBook::on_cancel(const OrderPtr& order, Quantity quantity) 159 | { 160 | // If the order is a limit order 161 | if (order->is_limit()) { 162 | // If the close erases a level 163 | depth_.close_order(order->price(), 164 | quantity, 165 | order->is_buy()); 166 | } 167 | } 168 | 169 | template 170 | void 171 | DepthOrderBook::on_cancel_stop(const OrderPtr& order) 172 | { 173 | // nothing to do for STOP until triggered/submitted 174 | } 175 | 176 | template 177 | void 178 | DepthOrderBook::on_replace(const OrderPtr& order, 179 | Quantity current_qty, 180 | Quantity new_qty, 181 | Price new_price) 182 | { 183 | // Notify the depth 184 | depth_.replace_order(order->price(), new_price, 185 | current_qty, new_qty, order->is_buy()); 186 | } 187 | 188 | template 189 | void 190 | DepthOrderBook::on_order_book_change() 191 | { 192 | // Book was updated, see if the depth we track was effected 193 | if (depth_.changed()) { 194 | if (depth_listener_) { 195 | depth_listener_->on_depth_change(this, &depth_); 196 | } 197 | if (bbo_listener_) { 198 | ChangeId last_change = depth_.last_published_change(); 199 | // May have been the first level which changed 200 | if ((depth_.bids()->changed_since(last_change)) || 201 | (depth_.asks()->changed_since(last_change))) { 202 | bbo_listener_->on_bbo_change(this, &depth_); 203 | } 204 | } 205 | // Start tracking changes again... 206 | depth_.published(); 207 | } 208 | } 209 | 210 | template 211 | inline typename DepthOrderBook::DepthTracker& 212 | DepthOrderBook::depth() 213 | { 214 | return depth_; 215 | } 216 | 217 | template 218 | inline const typename DepthOrderBook::DepthTracker& 219 | DepthOrderBook::depth() const 220 | { 221 | return depth_; 222 | } 223 | 224 | } } 225 | -------------------------------------------------------------------------------- /src/book/liquibook.mpc: -------------------------------------------------------------------------------- 1 | project (liquibook): liquibook_exe { 2 | exename = * 3 | Header_files { 4 | *.h 5 | } 6 | Source_Files { 7 | *.cpp 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/book/logger.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include 7 | #include 8 | namespace liquibook { namespace book { 9 | /// @brief Interface to allow application to control error logging 10 | class Logger 11 | { 12 | public: 13 | virtual void log_exception(const std::string & context, const std::exception& ex) = 0; 14 | virtual void log_message(const std::string & message) = 0; 15 | }; 16 | 17 | }} 18 | -------------------------------------------------------------------------------- /src/book/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "version.h" 3 | 4 | #include "depth_order_book.h" 5 | #include "order.h" 6 | 7 | using namespace liquibook; 8 | using namespace book; 9 | namespace 10 | { 11 | // depth order book pulls in all the other header files. 12 | // except order.h which is actually a concept. 13 | DepthOrderBook unusedDepthOrderBook_; 14 | } 15 | 16 | int main(int, const char**) 17 | { 18 | std::cout << "Liquibook version " << Version::MAJOR << '.' << Version::MINOR << '.' << Version::PATCH 19 | << " (" << Version::RELEASE_DATE << ")\n"; 20 | std::cout << "Liquibook is a header-only library.\n\n"; 21 | std::cout << "This executable is a placeholder to make the book header files visible in\n"; 22 | std::cout << "Visual Studio. It also lets the compiler to do syntax checking of the header\n"; 23 | std::cout << "files at build time." << std::endl; 24 | return 0; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/book/order.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include "types.h" 7 | 8 | namespace liquibook { namespace book { 9 | 10 | /// @brief interface an order must implement in order to be used by OrderBook. 11 | /// Note: structly speaking, inheriting from Order should not be required, 12 | /// due to the template implementation of OrderBook. 13 | class Order { 14 | public: 15 | /// @brief is this a limit order? 16 | bool is_limit() const; 17 | 18 | /// @brief is this order a buy? 19 | virtual bool is_buy() const = 0; 20 | 21 | /// @brief get the price of this order, or 0 if a market order 22 | virtual Price price() const = 0; 23 | 24 | /// @brief get the stop price (if any) for this order. 25 | /// @returns the stop price or zero if not a stop order 26 | virtual Price stop_price() const; 27 | 28 | /// @brief get the quantity of this order 29 | virtual Quantity order_qty() const = 0; 30 | 31 | /// @brief if no trades should happen until the order 32 | /// can be filled completely. 33 | /// Note: one or more trades may be used to fill the order. 34 | virtual bool all_or_none() const; 35 | 36 | /// @brief After generating as many trades as possible against 37 | /// orders already on the market, cancel any remaining quantity. 38 | virtual bool immediate_or_cancel() const; 39 | }; 40 | 41 | inline 42 | bool 43 | Order::is_limit() const 44 | { 45 | return (price() > 0); 46 | } 47 | 48 | inline 49 | Price 50 | Order::stop_price() const 51 | { 52 | // default to not a stop order 53 | return 0; 54 | } 55 | 56 | inline 57 | bool 58 | Order::all_or_none() const 59 | { 60 | // default to normal 61 | return false; 62 | } 63 | 64 | inline 65 | bool 66 | Order::immediate_or_cancel() const 67 | { 68 | // default to normal 69 | return false; 70 | } 71 | 72 | } } 73 | -------------------------------------------------------------------------------- /src/book/order_book_listener.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include "order_book.h" 7 | 8 | namespace liquibook { namespace book { 9 | 10 | /// @brief generic listener of order book events 11 | template 12 | class OrderBookListener { 13 | public: 14 | /// @brief callback for change anywhere in order book 15 | virtual void on_order_book_change(const OrderBook* book) = 0; 16 | }; 17 | 18 | } } 19 | -------------------------------------------------------------------------------- /src/book/order_listener.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | namespace liquibook { namespace book { 7 | 8 | /// @brief generic listener of order events. Implement to build a full order book feed. 9 | // Used by common version of OrderBook::process_callback(). 10 | template 11 | class OrderListener { 12 | public: 13 | /// @brief callback for an order accept 14 | virtual void on_accept(const OrderPtr& order) = 0; 15 | 16 | /// @brief callback for triggered STOP order 17 | virtual void on_trigger_stop(const OrderPtr& order) {} 18 | 19 | /// @brief callback for an order reject 20 | virtual void on_reject(const OrderPtr& order, const char* reason) = 0; 21 | 22 | /// @brief callback for an order fill 23 | /// @param order the inbound order 24 | /// @param matched_order the matched order 25 | /// @param fill_qty the quantity of this fill 26 | /// @param fill_price the price of this fill 27 | virtual void on_fill(const OrderPtr& order, 28 | const OrderPtr& matched_order, 29 | Quantity fill_qty, 30 | Price fill_price) = 0; 31 | 32 | /// @brief callback for an order cancellation 33 | virtual void on_cancel(const OrderPtr& order) = 0; 34 | 35 | /// @brief callback for an order cancel rejection 36 | virtual void on_cancel_reject(const OrderPtr& order, const char* reason) = 0; 37 | 38 | /// @brief callback for an order replace 39 | /// @param order the replaced order 40 | /// @param size_delta the change to order quantity 41 | /// @param new_price the updated order price 42 | virtual void on_replace(const OrderPtr& order, 43 | const int64_t& size_delta, 44 | Price new_price) = 0; 45 | 46 | /// @brief callback for an order replace rejection 47 | virtual void on_replace_reject(const OrderPtr& order, const char* reason) = 0; 48 | }; 49 | 50 | } } 51 | -------------------------------------------------------------------------------- /src/book/order_tracker.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 - 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include "types.h" 7 | 8 | namespace liquibook { namespace book { 9 | 10 | /// @brief Tracker of an order's state, to keep inside the OrderBook. 11 | /// Kept separate from the order itself. 12 | template 13 | class OrderTracker { 14 | public: 15 | /// @brief construct 16 | OrderTracker(const OrderPtr& order, OrderConditions conditions = 0); 17 | 18 | /// @brief modify the order quantity 19 | void change_qty(int64_t delta); 20 | 21 | /// @brief fill an order 22 | /// @param qty the number of shares filled in this fill 23 | void fill(Quantity qty); 24 | 25 | /// @brief is there no remaining open quantity in this order? 26 | bool filled() const; 27 | 28 | /// @brief get the total filled quantity of this order 29 | Quantity filled_qty() const; 30 | 31 | /// @brief get the open quantity of this order 32 | Quantity open_qty() const; 33 | 34 | /// @brief get the order pointer 35 | const OrderPtr& ptr() const; 36 | 37 | /// @brief get the order pointer 38 | OrderPtr& ptr(); 39 | 40 | /// @ brief is this order marked all or none? 41 | bool all_or_none() const; 42 | 43 | /// @ brief is this order marked immediate or cancel? 44 | bool immediate_or_cancel() const; 45 | 46 | Quantity reserve(int64_t reserved); 47 | 48 | private: 49 | OrderPtr order_; 50 | Quantity open_qty_; 51 | int64_t reserved_; 52 | OrderConditions conditions_; 53 | }; 54 | 55 | template 56 | OrderTracker::OrderTracker( 57 | const OrderPtr& order, 58 | OrderConditions conditions) 59 | : order_(order), 60 | open_qty_(order->order_qty()), 61 | reserved_(0), 62 | conditions_(conditions) 63 | { 64 | #if defined(LIQUIBOOK_ORDER_KNOWS_CONDITIONS) 65 | if(order->all_or_none()) 66 | { 67 | conditions |= oc_all_or_none; 68 | } 69 | if(order->immediate_or_cancel()) 70 | { 71 | conditions |= oc_immediate_or_cancel; 72 | } 73 | #endif 74 | } 75 | 76 | template 77 | Quantity 78 | OrderTracker::reserve(int64_t reserved) 79 | { 80 | reserved_ += reserved; 81 | return open_qty_ - reserved_; 82 | } 83 | 84 | template 85 | void 86 | OrderTracker::change_qty(int64_t delta) 87 | { 88 | if ((delta < 0 && 89 | (int)open_qty_ < std::abs(delta))) { 90 | throw 91 | std::runtime_error("Replace size reduction larger than open quantity"); 92 | } 93 | open_qty_ += delta; 94 | } 95 | 96 | template 97 | void 98 | OrderTracker::fill(Quantity qty) 99 | { 100 | if (qty > open_qty_) { 101 | throw std::runtime_error("Fill size larger than open quantity"); 102 | } 103 | open_qty_ -= qty; 104 | } 105 | 106 | template 107 | bool 108 | OrderTracker::filled() const 109 | { 110 | return open_qty_ == 0; 111 | } 112 | 113 | template 114 | Quantity 115 | OrderTracker::filled_qty() const 116 | { 117 | return order_->order_qty() - open_qty(); 118 | } 119 | 120 | // TODO: Rename this to be available and change the rest of the 121 | // system to use that, then provide a method to get to the open 122 | // quantity without considering reserved 123 | template 124 | Quantity 125 | OrderTracker::open_qty() const 126 | { 127 | return open_qty_ - reserved_; 128 | } 129 | 130 | template 131 | const OrderPtr& 132 | OrderTracker::ptr() const 133 | { 134 | return order_; 135 | } 136 | 137 | template 138 | OrderPtr& 139 | OrderTracker::ptr() 140 | { 141 | return order_; 142 | } 143 | 144 | template 145 | bool 146 | OrderTracker::all_or_none() const 147 | { 148 | return bool(conditions_ & oc_all_or_none); 149 | } 150 | 151 | template 152 | bool 153 | OrderTracker::immediate_or_cancel() const 154 | { 155 | return bool((conditions_ & oc_immediate_or_cancel) != 0); 156 | } 157 | 158 | } } 159 | -------------------------------------------------------------------------------- /src/book/trade_listener.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | namespace liquibook { namespace book { 7 | 8 | /// @brief listener of trade events. Implement to build a trade feed. 9 | template 10 | class TradeListener { 11 | public: 12 | /// @brief callback for a trade 13 | /// @param book the order book of the fill (not defined whether this is before 14 | /// or after fill) 15 | /// @param qty the quantity of this fill 16 | /// @param price the price of this fill 17 | virtual void on_trade(const OrderBook* book, 18 | Quantity qty, 19 | Price price) = 0; 20 | }; 21 | 22 | } } 23 | -------------------------------------------------------------------------------- /src/book/types.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace liquibook { namespace book { 11 | // Types used in Liquibook 12 | typedef uint64_t Price; 13 | typedef uint64_t Quantity; 14 | typedef uint64_t Cost; 15 | typedef uint32_t FillId; 16 | typedef uint32_t ChangeId; 17 | typedef uint32_t OrderConditions; 18 | 19 | enum OrderCondition { 20 | oc_no_conditions = 0, 21 | oc_all_or_none = 1, 22 | oc_immediate_or_cancel = oc_all_or_none << 1, 23 | oc_fill_or_kill = oc_all_or_none | oc_immediate_or_cancel, 24 | oc_stop = oc_immediate_or_cancel << 1 25 | }; 26 | 27 | namespace { 28 | // Constants used in liquibook API 29 | const Price MARKET_ORDER_PRICE(0); 30 | const Price PRICE_UNCHANGED(0); 31 | const Quantity QUANTITY_MAX(UINT64_MAX); 32 | const int64_t SIZE_UNCHANGED(0); 33 | } 34 | 35 | } } // namespace 36 | -------------------------------------------------------------------------------- /src/book/version.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 - 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | namespace liquibook { 7 | 8 | struct Version 9 | { 10 | static const int MAJOR = 2; 11 | static const int MINOR = 0; 12 | static const int PATCH = 0; 13 | static const int RELEASE_DATE = 20170222; 14 | }; 15 | 16 | } -------------------------------------------------------------------------------- /src/liquibook_export.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #ifdef _MSC_VER 5 | # pragma once 6 | #endif 7 | #ifndef liquibook_export_H 8 | #define liquibook_export_H 9 | 10 | // Compile time controls for library generation. Define with /D or #define 11 | // To produce or use a static library: #define LIQUIBOOK_HAS_DLL=0 12 | // Default is to produce/use a DLL 13 | // While building the LIQUIBOOK_ library: #define LIQUIBOOK_BUILD_DLL 14 | // Default is to export symbols from a pre-built LIQUIBOOK DLL 15 | // 16 | // Within LIQUIBOOK use the Liquibook_Export macro where a __declspec is needed. 17 | 18 | #if defined (_WIN32) 19 | 20 | # if !defined (LIQUIBOOK_HAS_DLL) 21 | # define LIQUIBOOK_HAS_DLL 1 22 | # endif /* ! LIQUIBOOK_HAS_DLL */ 23 | 24 | # if defined (LIQUIBOOK_HAS_DLL) && (LIQUIBOOK_HAS_DLL == 1) 25 | # if defined (LIQUIBOOK_BUILD_DLL) 26 | # define Liquibook_Export __declspec(dllexport) 27 | # else /* LIQUIBOOK_BUILD_DLL */ 28 | # define Liquibook_Export __declspec(dllimport) 29 | # endif /* LIQUIBOOK_BUILD_DLL */ 30 | # else /* LIQUIBOOK_HAS_DLL == 1 */ 31 | # define Liquibook_Export 32 | # endif /* LIQUIBOOK_HAS_DLL == 1 */ 33 | 34 | # else /* !_WIN32 */ 35 | 36 | # define Liquibook_Export __attribute__ ((visibility("default"))) 37 | # endif /* _WIN32 */ 38 | #endif /* LIQUIBOOK_EXPORT_H */ 39 | 40 | -------------------------------------------------------------------------------- /src/simple/liquibook_simple.mpc: -------------------------------------------------------------------------------- 1 | project : liquibook_lib, liquibook_book { 2 | sharedname = 3 | staticname = * 4 | } 5 | 6 | -------------------------------------------------------------------------------- /src/simple/simple_order.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #include "simple_order.h" 5 | 6 | #include 7 | 8 | namespace liquibook { namespace simple { 9 | 10 | uint32_t SimpleOrder::last_order_id_(0); 11 | 12 | SimpleOrder::SimpleOrder( 13 | bool is_buy, 14 | book::Price price, 15 | book::Quantity qty, 16 | book::Price stop_price, 17 | book::OrderConditions conditions) 18 | : state_(os_new), 19 | is_buy_(is_buy), 20 | order_qty_(qty), 21 | price_(price), 22 | stop_price_(stop_price), 23 | conditions_(conditions), 24 | filled_qty_(0), 25 | filled_cost_(0), 26 | order_id_(++last_order_id_) 27 | { 28 | } 29 | 30 | const OrderState& 31 | SimpleOrder::state() const 32 | { 33 | return state_; 34 | } 35 | 36 | bool 37 | SimpleOrder::is_buy() const 38 | { 39 | return is_buy_; 40 | } 41 | 42 | book::Price 43 | SimpleOrder::price() const 44 | { 45 | return price_; 46 | } 47 | 48 | book::Price 49 | SimpleOrder::stop_price() const 50 | { 51 | return stop_price_; 52 | } 53 | 54 | book::OrderConditions 55 | SimpleOrder::conditions() const 56 | { 57 | return conditions_; 58 | } 59 | 60 | bool 61 | SimpleOrder::all_or_none() const 62 | { 63 | return (conditions_ & book::OrderCondition::oc_all_or_none) != 0; 64 | } 65 | 66 | bool 67 | SimpleOrder::immediate_or_cancel() const 68 | { 69 | return (conditions_ & book::OrderCondition::oc_immediate_or_cancel) != 0; 70 | } 71 | book::Quantity 72 | SimpleOrder::order_qty() const 73 | { 74 | return order_qty_; 75 | } 76 | 77 | book::Quantity 78 | SimpleOrder::open_qty() const 79 | { 80 | // If not completely filled, calculate 81 | if (filled_qty_ < order_qty_) { 82 | return order_qty_ - filled_qty_; 83 | // Else prevent accidental overflow 84 | } else { 85 | return 0; 86 | } 87 | } 88 | 89 | const book::Quantity& 90 | SimpleOrder::filled_qty() const 91 | { 92 | return filled_qty_; 93 | } 94 | 95 | const book::Cost& 96 | SimpleOrder::filled_cost() const 97 | { 98 | return filled_cost_; 99 | } 100 | 101 | void 102 | SimpleOrder::fill(book::Quantity fill_qty, 103 | book::Cost fill_cost, 104 | book::FillId /*fill_id*/) 105 | { 106 | filled_qty_ += fill_qty; 107 | filled_cost_ += fill_cost; 108 | if (!open_qty()) { 109 | state_ = os_complete; 110 | } 111 | } 112 | 113 | void 114 | SimpleOrder::accept() 115 | { 116 | if (os_new == state_) { 117 | state_ = os_accepted; 118 | } 119 | } 120 | 121 | void 122 | SimpleOrder::cancel() 123 | { 124 | if (os_complete != state_) { 125 | state_ = os_cancelled; 126 | } 127 | } 128 | 129 | void 130 | SimpleOrder::replace(book::Quantity size_delta, book::Price new_price) 131 | { 132 | if (os_accepted == state_) { 133 | order_qty_ += size_delta; 134 | price_ = new_price; 135 | } 136 | } 137 | 138 | } } 139 | 140 | -------------------------------------------------------------------------------- /src/simple/simple_order.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #define LIQUIBOOK_ORDER_KNOWS_CONDITIONS 7 | #include 8 | #include 9 | 10 | namespace liquibook { namespace simple { 11 | 12 | enum OrderState { 13 | os_new, 14 | os_accepted, 15 | os_complete, 16 | os_cancelled, 17 | os_rejected 18 | }; 19 | 20 | /// @brief impelementation of the Order interface for testing purposes. 21 | class SimpleOrder : public book::Order { 22 | public: 23 | SimpleOrder(bool is_buy, 24 | book::Price price, 25 | book::Quantity qty, 26 | book::Price stop_price = 0, 27 | book::OrderConditions conditions = book::OrderCondition::oc_no_conditions); 28 | 29 | /// @brief get the order's state 30 | const OrderState& state() const; 31 | 32 | /// @brief is this order a buy? 33 | virtual bool is_buy() const; 34 | 35 | /// @brief get the limit price of this order 36 | virtual book::Price price() const; 37 | 38 | virtual book::Price stop_price()const; 39 | 40 | /// @brief get the quantity of this order 41 | virtual book::Quantity order_qty() const; 42 | 43 | /// @brief get the open quantity of this order 44 | virtual book::Quantity open_qty() const; 45 | 46 | /// @brief get the filled quantity of this order 47 | virtual const book::Quantity& filled_qty() const; 48 | 49 | /// @brief get the total filled cost of this order 50 | const book::Cost& filled_cost() const; 51 | 52 | /// @brief notify of a fill of this order 53 | /// @param fill_qty the number of shares in this fill 54 | /// @param fill_cost the total amount of this fill 55 | /// @fill_id the unique identifier of this fill 56 | virtual void fill(book::Quantity fill_qty, 57 | book::Cost fill_cost, 58 | book::FillId fill_id); 59 | 60 | /// @brief get order conditions as a bit mask 61 | virtual book::OrderConditions conditions() const; 62 | 63 | /// @brief if no trades should happen until the order 64 | /// can be filled completely. 65 | /// Note: one or more trades may be used to fill the order. 66 | virtual bool all_or_none() const; 67 | 68 | /// @brief After generating as many trades as possible against 69 | /// orders already on the market, cancel any remaining quantity. 70 | virtual bool immediate_or_cancel() const; 71 | 72 | /// @brief exchange accepted this order 73 | void accept(); 74 | /// @brief exchange cancelled this order 75 | void cancel(); 76 | 77 | /// @brief exchange replaced this order 78 | /// @param size_delta change to the order quantity 79 | /// @param new_price the new price 80 | void replace(book::Quantity size_delta, book::Price new_price); 81 | 82 | private: 83 | OrderState state_; 84 | bool is_buy_; 85 | book::Quantity order_qty_; 86 | book::Price price_; 87 | book::Price stop_price_; 88 | book::OrderConditions conditions_; 89 | book::Quantity filled_qty_; 90 | book::Cost filled_cost_; 91 | static uint32_t last_order_id_; 92 | 93 | public: 94 | const uint32_t order_id_; 95 | }; 96 | 97 | } } 98 | -------------------------------------------------------------------------------- /src/simple/simple_order_book.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include "simple_order.h" 7 | #include 8 | #include 9 | 10 | namespace liquibook { namespace simple { 11 | 12 | // @brief binding of DepthOrderBook template with SimpleOrder* order pointer. 13 | template 14 | class SimpleOrderBook : public book::DepthOrderBook { 15 | public: 16 | typedef book::Callback SimpleCallback; 17 | typedef uint32_t FillId; 18 | 19 | SimpleOrderBook(); 20 | 21 | // Override callback handling to update SimpleOrder state 22 | virtual void perform_callback(SimpleCallback& cb); 23 | private: 24 | FillId fill_id_; 25 | }; 26 | 27 | template 28 | SimpleOrderBook::SimpleOrderBook() 29 | : fill_id_(0) 30 | { 31 | } 32 | 33 | template 34 | inline void 35 | SimpleOrderBook::perform_callback(SimpleCallback& cb) 36 | { 37 | book::DepthOrderBook::perform_callback(cb); 38 | switch(cb.type) { 39 | case SimpleCallback::cb_order_accept: 40 | cb.order->accept(); 41 | break; 42 | case SimpleCallback::cb_order_fill: { 43 | // Increment fill ID once 44 | ++fill_id_; 45 | // Update the orders 46 | book::Cost fill_cost = cb.quantity * cb.price; 47 | cb.matched_order->fill(cb.quantity, fill_cost, fill_id_); 48 | cb.order->fill(cb.quantity, fill_cost, fill_id_); 49 | break; 50 | } 51 | case SimpleCallback::cb_order_cancel: 52 | cb.order->cancel(); 53 | break; 54 | case SimpleCallback::cb_order_replace: 55 | // Modify the order itself 56 | cb.order->replace(cb.delta, cb.price); 57 | break; 58 | default: 59 | // Nothing 60 | break; 61 | } 62 | } 63 | } } 64 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | pt_order_book 2 | ut_all_or_none 3 | ut_bbo_order_book 4 | ut_depth 5 | ut_immediate_or_cancel 6 | ut_listeners 7 | ut_order_book 8 | ut_order_book_shared_ptr 9 | -------------------------------------------------------------------------------- /test/latency/clock_gettime.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // clock_gettime is not defined on Windows / msvc 3 | // Let's fix that. 4 | #include 5 | #ifdef _MSC_VER 6 | // Windows.h should already be included, but Just In Case 7 | #include 8 | 9 | static const int CLOCK_REALTIME = 0; 10 | 11 | // The following code was painstakingly programmed (i.e. copy/pasted from stack overflow) 12 | // http://stackoverflow.com/questions/5404277/porting-clock-gettime-to-windows 13 | // Thanks, StackOverflow 14 | LARGE_INTEGER 15 | getFILETIMEoffset() 16 | { 17 | SYSTEMTIME s; 18 | FILETIME f; 19 | LARGE_INTEGER t; 20 | 21 | s.wYear = 1970; 22 | s.wMonth = 1; 23 | s.wDay = 1; 24 | s.wHour = 0; 25 | s.wMinute = 0; 26 | s.wSecond = 0; 27 | s.wMilliseconds = 0; 28 | SystemTimeToFileTime(&s,&f); 29 | t.QuadPart = f.dwHighDateTime; 30 | t.QuadPart <<= 32; 31 | t.QuadPart |= f.dwLowDateTime; 32 | return (t); 33 | } 34 | 35 | int 36 | clock_gettime(int X,struct timespec *tv) 37 | { 38 | LARGE_INTEGER t; 39 | FILETIME f; 40 | double microseconds; 41 | static LARGE_INTEGER offset; 42 | static double frequencyToMicroseconds; 43 | static int initialized = 0; 44 | static BOOL usePerformanceCounter = 0; 45 | 46 | if(!initialized) { 47 | LARGE_INTEGER performanceFrequency; 48 | initialized = 1; 49 | usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency); 50 | if(usePerformanceCounter) { 51 | QueryPerformanceCounter(&offset); 52 | frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.; 53 | } else { 54 | offset = getFILETIMEoffset(); 55 | frequencyToMicroseconds = 10.; 56 | } 57 | } 58 | if(usePerformanceCounter) QueryPerformanceCounter(&t); 59 | else { 60 | GetSystemTimeAsFileTime(&f); 61 | t.QuadPart = f.dwHighDateTime; 62 | t.QuadPart <<= 32; 63 | t.QuadPart |= f.dwLowDateTime; 64 | } 65 | 66 | t.QuadPart -= offset.QuadPart; 67 | microseconds = (double)t.QuadPart / frequencyToMicroseconds; 68 | t.QuadPart = (LONGLONG)microseconds; 69 | tv->tv_sec = (time_t)( t.QuadPart / 1000000); 70 | unsigned int uSec = t.QuadPart % 1000000; 71 | tv->tv_nsec = uSec * 1000; 72 | return 0; 73 | } 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /test/latency/liquibook_latency.mpc: -------------------------------------------------------------------------------- 1 | project (lt_order_book) : liquibook_book, liquibook_simple, liquibook_test { 2 | exename = * 3 | } 4 | -------------------------------------------------------------------------------- /test/latency/lt_order_book.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #include 5 | #include 6 | #include "clock_gettime.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace liquibook; 13 | using namespace liquibook::book; 14 | 15 | typedef simple::SimpleOrderBook<5> FullDepthOrderBook; 16 | typedef simple::SimpleOrderBook<1> BboOrderBook; 17 | typedef book::OrderBook NoDepthOrderBook; 18 | 19 | void build_histogram(timespec* timestamps, int count) { 20 | timespec* prev = nullptr; 21 | std::cout << "Latency (ns) " << std::endl; 22 | 23 | for (timespec* timestamp = timestamps; 24 | (timestamp - timestamps) <= count; 25 | ++timestamp) 26 | { 27 | if (prev) { 28 | timespec elapsed; 29 | if (timestamp->tv_sec == prev->tv_sec) { 30 | elapsed.tv_sec = 0; 31 | elapsed.tv_nsec = timestamp->tv_nsec - prev->tv_nsec; 32 | } else { 33 | // Second changed 34 | elapsed.tv_sec = timestamp->tv_sec - prev->tv_sec; 35 | // Borrow from sec if necessary 36 | if (prev->tv_nsec > timestamp->tv_nsec) { 37 | --elapsed.tv_sec; 38 | elapsed.tv_nsec = 1000000000 + timestamp->tv_nsec - prev->tv_nsec; 39 | } else { 40 | elapsed.tv_nsec = timestamp->tv_nsec - prev->tv_nsec; 41 | } 42 | } 43 | if (elapsed.tv_sec) { 44 | std::cout << elapsed.tv_sec * 1000000000 + elapsed.tv_nsec << std::endl; 45 | } else { 46 | std::cout << elapsed.tv_nsec << std::endl; 47 | } 48 | } 49 | prev = timestamp; 50 | } 51 | } 52 | 53 | template 54 | int run_test(TypedOrderBook& order_book, TypedOrder** orders, 55 | timespec* timestamps) { 56 | int count = 0; 57 | TypedOrder** pp_order = orders; 58 | timespec* timestamp = timestamps; 59 | do { 60 | // Take timestamp at start of each order 61 | int status = clock_gettime(CLOCK_REALTIME, timestamp); 62 | if (status) { 63 | throw std::runtime_error("clock_gettime() failed"); 64 | } 65 | order_book.add(*pp_order); 66 | ++pp_order; 67 | ++timestamp; 68 | if (*pp_order == nullptr) { 69 | break; 70 | } 71 | ++count; 72 | } while (true); 73 | // Take timestamp at end 74 | int status = clock_gettime(CLOCK_REALTIME, timestamp); 75 | if (status) { 76 | throw std::runtime_error("clock_gettime() failed"); 77 | } 78 | return int(pp_order - orders); 79 | } 80 | 81 | template 82 | bool build_and_run_test(uint32_t num_to_try, bool dry_run = false) { 83 | TypedOrderBook order_book; 84 | simple::SimpleOrder** orders = new simple::SimpleOrder*[num_to_try + 1]; 85 | timespec* timestamps = new timespec[num_to_try + 1]; 86 | 87 | for (uint32_t i = 0; i < num_to_try; ++i) { 88 | bool is_buy((i % 2) == 0); 89 | uint32_t delta = is_buy ? 1880 : 1884; 90 | // AsSK 1893 91 | // ASK 1892 92 | // ASK 1891 93 | // ASK 1890 94 | // ASK 1889 crossable 95 | // ASK 1888 crossable 96 | // ASK 1887 crossable 97 | // ASK 1886 crossable 98 | // ASK 1885 crossable 99 | // ASK 1884 crossable 100 | 101 | // BID 1889 crossable 102 | // BID 1888 crossable 103 | // BID 1887 crossable 104 | // BID 1886 crossable 105 | // BID 1885 crossable 106 | // BID 1884 crossable 107 | // BID 1883 108 | // BID 1882 109 | // BID 1881 110 | // BID 1880 111 | 112 | Price price = (rand() % 10) + delta; 113 | 114 | Quantity qty = ((rand() % 10) + 1) * 100; 115 | orders[i] = new simple::SimpleOrder(is_buy, price, qty); 116 | } 117 | orders[num_to_try] = nullptr; // Final null 118 | 119 | run_test(order_book, orders, timestamps); 120 | for (uint32_t i = 0; i <= num_to_try; ++i) { 121 | delete orders[i]; 122 | } 123 | delete [] orders; 124 | std::cout << " - complete!" << std::endl; 125 | uint32_t remain = uint32_t(order_book.bids().size() + order_book.asks().size()); 126 | if (!dry_run) { 127 | std::cout << "Building histogram" << std::endl; 128 | build_histogram(timestamps, num_to_try); 129 | } 130 | delete [] timestamps; 131 | 132 | return true; 133 | } 134 | 135 | int main(int argc, const char* argv[]) 136 | { 137 | uint32_t num_to_try = 10000; 138 | if (argc > 1) { 139 | num_to_try = atoi(argv[1]); 140 | if (!num_to_try) { 141 | num_to_try = 10000; 142 | } 143 | } 144 | std::cout << num_to_try << " order latency test of order book" << std::endl; 145 | srand(num_to_try); 146 | 147 | { 148 | std::cout << "starting dry run" << std::endl; 149 | build_and_run_test(num_to_try, true); 150 | } 151 | 152 | { 153 | std::cout << "testing order book with depth" << std::endl; 154 | build_and_run_test(num_to_try); 155 | } 156 | 157 | { 158 | std::cout << "testing order book with bbo" << std::endl; 159 | build_and_run_test(num_to_try); 160 | } 161 | 162 | { 163 | std::cout << "testing order book without depth" << std::endl; 164 | build_and_run_test(num_to_try); 165 | } 166 | } 167 | 168 | -------------------------------------------------------------------------------- /test/perf/liquibook_perf.mpc: -------------------------------------------------------------------------------- 1 | project (pt_order_book) : liquibook_book, liquibook_simple, liquibook_test { 2 | exename = * 3 | } 4 | -------------------------------------------------------------------------------- /test/perf/pt_order_book.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace liquibook; 13 | using namespace liquibook::book; 14 | 15 | typedef simple::SimpleOrderBook<5> FullDepthOrderBook; 16 | typedef simple::SimpleOrderBook<1> BboOrderBook; 17 | typedef book::OrderBook NoDepthOrderBook; 18 | 19 | template 20 | int run_test(TypedOrderBook& order_book, TypedOrder** orders, clock_t end) { 21 | int count = 0; 22 | TypedOrder** pp_order = orders; 23 | do { 24 | order_book.add(*pp_order); 25 | ++pp_order; 26 | if (*pp_order == nullptr) { 27 | return -1; 28 | } 29 | ++count; 30 | } while (clock() < end); 31 | return int(pp_order - orders); 32 | } 33 | 34 | template 35 | bool build_and_run_test(uint32_t dur_sec, uint32_t num_to_try) { 36 | std::cout << "trying run of " << num_to_try << " orders"; 37 | TypedOrderBook order_book; 38 | simple::SimpleOrder** orders = new simple::SimpleOrder*[num_to_try + 1]; 39 | 40 | for (uint32_t i = 0; i <= num_to_try; ++i) { 41 | bool is_buy((i % 2) == 0); 42 | uint32_t delta = is_buy ? 1880 : 1884; 43 | // ASK 1893 44 | // ASK 1892 45 | // ASK 1891 46 | // ASK 1890 47 | // ASK 1889 crossable 48 | // ASK 1888 crossable 49 | // ASK 1887 crossable 50 | // ASK 1886 crossable 51 | // ASK 1885 crossable 52 | // ASK 1884 crossable 53 | 54 | // BID 1889 crossable 55 | // BID 1888 crossable 56 | // BID 1887 crossable 57 | // BID 1886 crossable 58 | // BID 1885 crossable 59 | // BID 1884 crossable 60 | // BID 1883 61 | // BID 1882 62 | // BID 1881 63 | // BID 1880 64 | 65 | Price price = (rand() % 10) + delta; 66 | 67 | Quantity qty = ((rand() % 10) + 1) * 100; 68 | orders[i] = new simple::SimpleOrder(is_buy, price, qty); 69 | } 70 | orders[num_to_try] = nullptr; // Final null 71 | 72 | clock_t start = clock(); 73 | clock_t stop = start + (dur_sec * CLOCKS_PER_SEC); 74 | 75 | int count = run_test(order_book, orders, stop); 76 | for (uint32_t i = 0; i <= num_to_try; ++i) { 77 | delete orders[i]; 78 | } 79 | delete [] orders; 80 | if (count > 0) { 81 | std::cout << " - complete!" << std::endl; 82 | std::cout << "Inserted " << count << " orders in " << dur_sec << " seconds" 83 | << ", or " << count / dur_sec << " insertions per sec" 84 | << std::endl; 85 | uint32_t remain = uint32_t(order_book.bids().size() + order_book.asks().size()); 86 | std::cout << "Run matched " << count - remain << " orders" << std::endl; 87 | return true; 88 | } else { 89 | std::cout << " - not enough orders" << std::endl; 90 | return false; 91 | } 92 | 93 | return count > 0; 94 | } 95 | 96 | int main(int argc, const char* argv[]) 97 | { 98 | uint32_t dur_sec = 3; 99 | if (argc > 1) { 100 | dur_sec = atoi(argv[1]); 101 | if (!dur_sec) { 102 | dur_sec = 3; 103 | } 104 | } 105 | std::cout << dur_sec << " sec performance test of order book" << std::endl; 106 | 107 | srand(dur_sec); 108 | 109 | { 110 | std::cout << "testing order book with depth" << std::endl; 111 | uint32_t num_to_try = dur_sec * 125000; 112 | while (true) { 113 | if (build_and_run_test(dur_sec, num_to_try)) { 114 | break; 115 | } else { 116 | num_to_try *= 2; 117 | } 118 | } 119 | } 120 | 121 | { 122 | std::cout << "testing order book with bbo" << std::endl; 123 | uint32_t num_to_try = dur_sec * 125000; 124 | while (true) { 125 | if (build_and_run_test(dur_sec, num_to_try)) { 126 | break; 127 | } else { 128 | num_to_try *= 2; 129 | } 130 | } 131 | } 132 | 133 | { 134 | std::cout << "testing order book without depth" << std::endl; 135 | uint32_t num_to_try = dur_sec * 125000; 136 | while (true) { 137 | if (build_and_run_test(dur_sec, num_to_try)) { 138 | break; 139 | } else { 140 | num_to_try *= 2; 141 | } 142 | } 143 | } 144 | 145 | } 146 | 147 | -------------------------------------------------------------------------------- /test/unit/changed_checker.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 2013 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #include 5 | #include 6 | 7 | namespace liquibook { 8 | 9 | using book::Depth; 10 | using book::DepthLevel; 11 | using book::ChangeId; 12 | 13 | namespace test { 14 | 15 | template 16 | class ChangedChecker { 17 | public: 18 | typedef Depth SizedDepth; 19 | ChangedChecker(SizedDepth& depth) 20 | : depth_(depth) 21 | { 22 | reset(); 23 | } 24 | 25 | void reset() { 26 | last_change_ = depth_.last_change(); 27 | } 28 | 29 | bool verify_bid_changed(bool l0, bool l1, bool l2, bool l3, bool l4) 30 | { 31 | return verify_side_changed(depth_.bids(), l0, l1, l2, l3, l4); 32 | } 33 | bool verify_ask_changed(bool l0, bool l1, bool l2, bool l3, bool l4) 34 | { 35 | return verify_side_changed(depth_.asks(), l0, l1, l2, l3, l4); 36 | } 37 | 38 | bool verify_bid_stamps(ChangeId l0, ChangeId l1, ChangeId l2, 39 | ChangeId l3, ChangeId l4) 40 | { 41 | return verify_side_stamps(depth_.bids(), l0, l1, l2, l3, l4); 42 | } 43 | 44 | bool verify_ask_stamps(ChangeId l0, ChangeId l1, ChangeId l2, 45 | ChangeId l3, ChangeId l4) 46 | { 47 | return verify_side_stamps(depth_.asks(), l0, l1, l2, l3, l4); 48 | } 49 | 50 | bool verify_bbo_changed(bool bid_changed, bool ask_changed) 51 | { 52 | bool matched = true; 53 | 54 | if (depth_.bids()->changed_since(last_change_)) 55 | { 56 | if(! bid_changed) { 57 | std::cout << "best bid unexpected change" << std::endl; 58 | matched = false; 59 | } 60 | } 61 | else if(bid_changed){ 62 | std::cout << "best bid expected change" << std::endl; 63 | matched = false; 64 | } 65 | if (depth_.asks()->changed_since(last_change_)){ 66 | if(!ask_changed) { 67 | std::cout << "best ask unexpected change" << std::endl; 68 | matched = false; 69 | } 70 | } 71 | else if(ask_changed){ 72 | std::cout << "best ask expected change" << std::endl; 73 | matched = false; 74 | } 75 | return matched; 76 | } 77 | 78 | bool verify_bbo_stamps(ChangeId bid_stamp, ChangeId ask_stamp) 79 | { 80 | bool matched = true; 81 | if (depth_.bids()->last_change() != bid_stamp) { 82 | std::cout << "best bid change " 83 | << depth_.bids()->last_change() << std::endl; 84 | matched = false; 85 | } 86 | if (depth_.asks()->last_change() != ask_stamp) { 87 | std::cout << "best ask change " 88 | << depth_.asks()->last_change() << std::endl; 89 | matched = false; 90 | } 91 | return matched; 92 | } 93 | private: 94 | ChangeId last_change_; 95 | bool verify_side_stamps(const DepthLevel* start, 96 | ChangeId l0, ChangeId l1, ChangeId l2, 97 | ChangeId l3, ChangeId l4) 98 | { 99 | bool matched = true; 100 | if (start[0].last_change() != l0) { 101 | std::cout << "change id[0] " << start[0].last_change() << std::endl; 102 | matched = false; 103 | } 104 | if (start[1].last_change() != l1) { 105 | std::cout << "change id[1] " << start[1].last_change() << std::endl; 106 | matched = false; 107 | } 108 | if (start[2].last_change() != l2) { 109 | std::cout << "change id[2] " << start[2].last_change() << std::endl; 110 | matched = false; 111 | } 112 | if (start[3].last_change() != l3) { 113 | std::cout << "change id[3] " << start[3].last_change() << std::endl; 114 | matched = false; 115 | } 116 | if (start[4].last_change() != l4) { 117 | std::cout << "change id[4] " << start[4].last_change() << std::endl; 118 | matched = false; 119 | } 120 | return matched; 121 | } 122 | 123 | bool verify_level(const DepthLevel* levels, size_t index, bool expected) 124 | { 125 | bool matched = true; 126 | if (levels[index].changed_since(last_change_) != expected) 127 | { 128 | matched = false; 129 | if(expected) 130 | { 131 | std::cout << "expected change level[" << index << "] " << levels[index].price() << std::endl; 132 | } 133 | else 134 | { 135 | std::cout << "unexpected change level[" << index << "] " << levels[index].price() << std::endl; 136 | } 137 | } 138 | return matched; 139 | } 140 | 141 | bool verify_side_changed(const DepthLevel* start, 142 | bool l0, bool l1, bool l2, bool l3, bool l4) 143 | { 144 | bool matched = true; 145 | matched = verify_level(start, 0, l0) && matched; 146 | matched = verify_level(start, 1, l1) && matched; 147 | matched = verify_level(start, 2, l2) && matched; 148 | matched = verify_level(start, 3, l3) && matched; 149 | matched = verify_level(start, 4, l4) && matched; 150 | return matched; 151 | } 152 | 153 | private: 154 | SizedDepth& depth_; 155 | }; 156 | 157 | } } // namespace 158 | -------------------------------------------------------------------------------- /test/unit/depth_check.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012=2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace liquibook::book; 11 | 12 | namespace liquibook { 13 | 14 | template 15 | class DepthCheck { 16 | typedef typename SimpleOrderBook::DepthTracker SimpleDepth; 17 | 18 | public: 19 | DepthCheck(const SimpleDepth& depth) 20 | : depth_(depth) 21 | { 22 | reset(); 23 | } 24 | 25 | static bool verify_depth(const DepthLevel& level, 26 | const Price& price, 27 | uint32_t count, 28 | const Quantity& qty) 29 | { 30 | bool matched = true; 31 | if (level.price() != price) { 32 | std::cout << "Price " << level.price() << " expecting " << price << std::endl; 33 | matched = false; 34 | } 35 | if (level.order_count() != count) { 36 | std::cout << "Level: " << level.price() << " Count " << level.order_count() << " expecting " << count << std::endl; 37 | matched = false; 38 | } 39 | if (level.aggregate_qty() != qty) { 40 | std::cout << "Level: " << level.price() << " Quantity " << level.aggregate_qty() << " expecting " << qty << std::endl; 41 | matched = false; 42 | } 43 | if (level.is_excess()) { 44 | std::cout << "Marked as excess" << std::endl; 45 | matched = false; 46 | } 47 | return matched; 48 | } 49 | 50 | bool verify_bid(const Price& price, int count, const Quantity& qty) 51 | { 52 | return verify_depth(*next_bid_++, price, count, qty); 53 | } 54 | 55 | bool verify_ask(const Price& price, int count, const Quantity& qty) 56 | { 57 | return verify_depth(*next_ask_++, price, count, qty); 58 | } 59 | 60 | bool verify_bids_done() 61 | { 62 | while(next_bid_ != depth_.last_bid_level() + 1) 63 | { 64 | auto level = *next_bid_; 65 | if(level.order_count() != 0) 66 | { 67 | return false; 68 | } 69 | ++next_bid_; 70 | } 71 | return true; 72 | } 73 | 74 | bool verify_adds_done() 75 | { 76 | while(next_ask_ != depth_.last_ask_level() + 1) 77 | { 78 | auto level = *next_ask_; 79 | if(level.order_count() != 0) 80 | { 81 | return false; 82 | } 83 | ++next_bid_; 84 | } 85 | return true; 86 | } 87 | 88 | void reset() 89 | { 90 | next_bid_ = depth_.bids(); 91 | next_ask_ = depth_.asks(); 92 | } 93 | 94 | private: 95 | const SimpleDepth& depth_; 96 | const DepthLevel* next_bid_; 97 | const DepthLevel* next_ask_; 98 | }; 99 | 100 | } // namespace 101 | -------------------------------------------------------------------------------- /test/unit/liquibook_unit.mpc: -------------------------------------------------------------------------------- 1 | project (liquibook_unit_test) : liquibook_test, boost_unit_test_framework, boost_base{ 2 | exename = * 3 | 4 | specific(make) { 5 | macros += BOOST_TEST_DYN_LINK 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/unit/ut_main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #define BOOST_TEST_MODULE LiquibookTest 5 | #include 6 | -------------------------------------------------------------------------------- /test/unit/ut_market_price.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 - 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | 5 | #define BOOST_TEST_NO_MAIN LiquibookTest 6 | #include 7 | 8 | #include "ut_utils.h" 9 | #include "changed_checker.h" 10 | #include 11 | #include 12 | 13 | namespace liquibook { 14 | 15 | using book::DepthLevel; 16 | using book::OrderBook; 17 | using book::OrderTracker; 18 | using simple::SimpleOrder; 19 | 20 | namespace 21 | { 22 | const bool sideBuy = true; 23 | const bool sideSell = false; 24 | 25 | const Price prcMkt = 0; 26 | const Price prc0 = 9900; 27 | 28 | const Quantity q100 = 100; 29 | 30 | const bool expectMatch = true; 31 | const bool expectNoMatch = false; 32 | 33 | const bool expectComplete = true; 34 | const bool expectNoComplete = false; 35 | } 36 | 37 | typedef OrderTracker SimpleTracker; 38 | typedef test::ChangedChecker<5> ChangedChecker; 39 | 40 | typedef FillCheck SimpleFillCheck; 41 | 42 | BOOST_AUTO_TEST_CASE(TestNoMktToMktWithoutPreviousTrade) 43 | { 44 | SimpleOrderBook order_book; 45 | SimpleOrder order0(sideBuy, prcMkt, q100); 46 | SimpleOrder order1(sideSell, prcMkt, q100); 47 | 48 | // Check that no market-to-market trade happens without a prior trade 49 | BOOST_CHECK(add_and_verify(order_book, &order0, expectNoMatch)); 50 | BOOST_CHECK(add_and_verify(order_book, &order1, expectNoMatch)); 51 | 52 | SimpleFillCheck fc0(&order0, 0, 0); 53 | SimpleFillCheck fc1(&order1, 0, 0); 54 | 55 | BOOST_CHECK_EQUAL(0U, order_book.market_price()); 56 | } 57 | 58 | BOOST_AUTO_TEST_CASE(TestTradeSetsMarketPrice) 59 | { 60 | SimpleOrderBook order_book; 61 | SimpleOrder order0(sideBuy, prcMkt, q100); 62 | SimpleOrder order1(sideSell, prcMkt, q100); 63 | 64 | // Check that no market-to-market trade happens without a prior trade 65 | BOOST_CHECK(add_and_verify(order_book, &order0, expectNoMatch)); 66 | BOOST_CHECK(add_and_verify(order_book, &order1, expectNoMatch)); 67 | 68 | BOOST_CHECK_EQUAL(0U, order_book.market_price()); 69 | 70 | SimpleOrder order2(sideBuy, prc0, q100); 71 | 72 | // Scope for fill checks 73 | { 74 | SimpleFillCheck fc0(&order0, 0, 0); 75 | SimpleFillCheck fc1(&order1, q100, q100 * prc0); 76 | SimpleFillCheck fc2(&order2, q100, q100 * prc0); 77 | BOOST_CHECK(add_and_verify(order_book, &order2, expectMatch, expectComplete)); 78 | } 79 | BOOST_CHECK_EQUAL(prc0, order_book.market_price()); 80 | 81 | SimpleOrder order3(sideSell, prcMkt, q100); 82 | // Scope for fill checks 83 | { 84 | SimpleFillCheck fc0(&order0, q100, q100 * prc0); 85 | SimpleFillCheck fc3(&order3, q100, q100 * prc0); 86 | BOOST_CHECK(add_and_verify(order_book, &order3, expectMatch, expectComplete)); 87 | } 88 | BOOST_CHECK_EQUAL(prc0, order_book.market_price()); 89 | } 90 | 91 | BOOST_AUTO_TEST_CASE(TestExplicitlySettingMarketPriceAllowsMarketToMarketTrades) 92 | { 93 | SimpleOrderBook order_book; 94 | SimpleOrder order0(sideBuy, prcMkt, q100); 95 | SimpleOrder order1(sideSell, prcMkt, q100); 96 | 97 | // Check that no market-to-market trade happens without a prior trade 98 | BOOST_CHECK(add_and_verify(order_book, &order0, expectNoMatch)); 99 | BOOST_CHECK(add_and_verify(order_book, &order1, expectNoMatch)); 100 | 101 | // IF WE MAKE THE MANUAL SETTING OF MARKET PRICE RETROACTIVE 102 | // FIX THIS TEST TO REFLECT THAT 103 | 104 | // Scope for fill checks 105 | { 106 | SimpleFillCheck fc0(&order0, 0, 0); 107 | SimpleFillCheck fc1(&order1, 0, 0); 108 | order_book.set_market_price(prc0); 109 | } 110 | 111 | SimpleOrder order2(sideBuy, prcMkt, q100); 112 | SimpleOrder order3(sideSell, prcMkt, q100); 113 | // Scope for fill checks 114 | { 115 | SimpleFillCheck fc0(&order0, q100, q100 * prc0); 116 | SimpleFillCheck fc1(&order1, q100, q100 * prc0); 117 | SimpleFillCheck fc2(&order2, q100, q100 * prc0); 118 | SimpleFillCheck fc3(&order3, q100, q100 * prc0); 119 | BOOST_CHECK(add_and_verify(order_book, &order2, expectMatch, expectComplete)); 120 | BOOST_CHECK(add_and_verify(order_book, &order3, expectMatch, expectComplete)); 121 | } 122 | } 123 | 124 | } // namespace 125 | -------------------------------------------------------------------------------- /test/unit/ut_order_book_shared_ptr.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 - 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | 5 | #define BOOST_TEST_NO_MAIN LiquibookTest 6 | #include 7 | 8 | #include "ut_utils.h" 9 | #include "changed_checker.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace liquibook { 16 | 17 | using book::DepthLevel; 18 | using book::OrderBook; 19 | using book::OrderTracker; 20 | using simple::SimpleOrder; 21 | 22 | 23 | typedef std::shared_ptr SimpleOrderPtr; 24 | class SharedPtrOrderBook : public OrderBook 25 | { 26 | virtual void perform_callback(OrderBook::TypedCallback& cb) 27 | { 28 | switch(cb.type) { 29 | case TypedCallback::cb_order_accept: 30 | cb.order->accept(); 31 | break; 32 | case TypedCallback::cb_order_fill: { 33 | Cost fill_cost = cb.price * cb.quantity; 34 | cb.order->fill(cb.quantity, fill_cost, 0); 35 | cb.matched_order->fill(cb.quantity, fill_cost, 0); 36 | break; 37 | } 38 | case TypedCallback::cb_order_cancel: 39 | cb.order->cancel(); 40 | break; 41 | case TypedCallback::cb_order_replace: 42 | cb.order->replace(cb.delta, cb.price); 43 | break; 44 | default: 45 | // Nothing 46 | break; 47 | } 48 | } 49 | }; 50 | 51 | typedef FillCheck SharedFillCheck; 52 | 53 | BOOST_AUTO_TEST_CASE(TestSharedPointerBuild) 54 | { 55 | SharedPtrOrderBook order_book; 56 | SimpleOrderPtr ask1(new SimpleOrder(false, 1252, 100)); 57 | SimpleOrderPtr ask0(new SimpleOrder(false, 1251, 100)); 58 | SimpleOrderPtr bid1(new SimpleOrder(true, 1251, 100)); 59 | SimpleOrderPtr bid0(new SimpleOrder(true, 1250, 100)); 60 | 61 | // No match 62 | BOOST_CHECK(add_and_verify(order_book, bid0, false)); 63 | BOOST_CHECK(add_and_verify(order_book, ask0, false)); 64 | BOOST_CHECK(add_and_verify(order_book, ask1, false)); 65 | 66 | // Verify sizes 67 | BOOST_CHECK_EQUAL(1, order_book.bids().size()); 68 | BOOST_CHECK_EQUAL(2, order_book.asks().size()); 69 | 70 | // Match - complete 71 | { 72 | SharedFillCheck fc1(bid1, 100, 125100); 73 | SharedFillCheck fc2(ask0, 100, 125100); 74 | BOOST_CHECK(add_and_verify(order_book, bid1, true, true)); 75 | } 76 | 77 | // Verify sizes 78 | BOOST_CHECK_EQUAL(1, order_book.bids().size()); 79 | BOOST_CHECK_EQUAL(1, order_book.asks().size()); 80 | } 81 | 82 | BOOST_AUTO_TEST_CASE(TestSharedCancelBid) 83 | { 84 | SharedPtrOrderBook order_book; 85 | SimpleOrderPtr ask1(new SimpleOrder(false, 1252, 100)); 86 | SimpleOrderPtr ask0(new SimpleOrder(false, 1251, 100)); 87 | SimpleOrderPtr bid0(new SimpleOrder(true, 1250, 100)); 88 | 89 | // No match 90 | BOOST_CHECK(add_and_verify(order_book, bid0, false)); 91 | BOOST_CHECK(add_and_verify(order_book, ask0, false)); 92 | BOOST_CHECK(add_and_verify(order_book, ask1, false)); 93 | 94 | // Verify sizes 95 | BOOST_CHECK_EQUAL(1, order_book.bids().size()); 96 | BOOST_CHECK_EQUAL(2, order_book.asks().size()); 97 | 98 | // Cancel bid 99 | BOOST_CHECK(cancel_and_verify(order_book, bid0, simple::os_cancelled)); 100 | 101 | // Verify sizes 102 | BOOST_CHECK_EQUAL(0, order_book.bids().size()); 103 | BOOST_CHECK_EQUAL(2, order_book.asks().size()); 104 | } 105 | 106 | } // namespace 107 | -------------------------------------------------------------------------------- /test/unit/ut_stop_orders.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 - 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | 5 | #define BOOST_TEST_NO_MAIN LiquibookTest 6 | #include 7 | 8 | #include "ut_utils.h" 9 | #include "changed_checker.h" 10 | #include 11 | #include 12 | 13 | namespace liquibook { 14 | 15 | using book::DepthLevel; 16 | using book::OrderBook; 17 | using book::OrderTracker; 18 | using simple::SimpleOrder; 19 | 20 | namespace 21 | { 22 | const bool sideBuy = true; 23 | const bool sideSell = false; 24 | 25 | const Price prcMkt = 0; 26 | const Price prc53 = 53; 27 | const Price prc54 = 54; 28 | const Price prc55 = 55; 29 | const Price prc56 = 56; 30 | const Price prc57 = 57; 31 | 32 | const Quantity q100 = 100; 33 | const Quantity q1000 = 1000; 34 | 35 | const bool expectMatch = true; 36 | const bool expectNoMatch = false; 37 | 38 | const bool expectComplete = true; 39 | const bool expectNoComplete = false; 40 | } 41 | 42 | typedef OrderTracker SimpleTracker; 43 | typedef test::ChangedChecker<5> ChangedChecker; 44 | 45 | typedef FillCheck SimpleFillCheck; 46 | 47 | BOOST_AUTO_TEST_CASE(TestStopOrdersOffMarketNoTrade) 48 | { 49 | SimpleOrderBook book; 50 | SimpleOrder order0(sideBuy, prc55, q100); 51 | SimpleOrder order1(sideSell, prcMkt, q100); 52 | 53 | // Enter order to generate a trade establishing market price 54 | BOOST_CHECK(add_and_verify(book, &order0, expectNoMatch)); 55 | BOOST_CHECK(add_and_verify(book, &order1, expectMatch, expectComplete)); 56 | 57 | BOOST_CHECK_EQUAL(prc55, book.market_price()); 58 | 59 | SimpleOrder order2(sideBuy, prcMkt, q100, prc56); 60 | SimpleOrder order3(sideSell, prcMkt, q100, prc54); 61 | BOOST_CHECK(add_and_verify(book, &order2, expectNoMatch)); 62 | BOOST_CHECK(add_and_verify(book, &order3, expectNoMatch)); 63 | 64 | // Orders were accepted, but not traded 65 | BOOST_CHECK_EQUAL(simple::os_accepted, order2.state()); 66 | BOOST_CHECK_EQUAL(simple::os_accepted, order3.state()); 67 | } 68 | 69 | BOOST_AUTO_TEST_CASE(TestStopMarketOrdersOnMarketTradeImmediately) 70 | { 71 | SimpleOrderBook book; 72 | SimpleOrder order0(sideBuy, prc55, q100); 73 | SimpleOrder order1(sideSell, prcMkt, q100); 74 | 75 | // Enter order to generate a trade establishing market price 76 | BOOST_CHECK(add_and_verify(book, &order0, expectNoMatch)); 77 | BOOST_CHECK(add_and_verify(book, &order1, expectMatch, expectComplete)); 78 | 79 | BOOST_CHECK_EQUAL(prc55, book.market_price()); 80 | 81 | SimpleOrder order2(sideBuy, prcMkt, q100, prc55); 82 | SimpleOrder order3(sideSell, prcMkt, q100, prc55); 83 | BOOST_CHECK(add_and_verify(book, &order2, expectNoMatch)); 84 | BOOST_CHECK(add_and_verify(book, &order3, expectMatch, expectComplete)); 85 | } 86 | 87 | BOOST_AUTO_TEST_CASE(TestStopMarketOrdersTradeWhenStopPriceReached) 88 | { 89 | SimpleOrderBook book; 90 | SimpleOrder order0(sideBuy, prc53, q100); 91 | SimpleOrder order1(sideSell, prc57, q100); 92 | book.set_market_price(prc55); 93 | 94 | // Enter seed orders and be sure they don't trade with each other. 95 | BOOST_CHECK(add_and_verify(book, &order0, expectNoMatch)); 96 | BOOST_CHECK(add_and_verify(book, &order1, expectNoMatch)); 97 | 98 | // enter stop orders. Be sure they don't trade yet 99 | SimpleOrder order2(sideBuy, prcMkt, q100, prc56); 100 | SimpleOrder order3(sideSell, prcMkt, q100, prc54); 101 | BOOST_CHECK(add_and_verify(book, &order2, expectNoMatch)); 102 | BOOST_CHECK(add_and_verify(book, &order3, expectNoMatch)); 103 | 104 | SimpleOrder order4(sideBuy, prc56, q1000, 0, book::oc_all_or_none); 105 | SimpleOrder order5(sideSell, prc56, q1000, 0, book::oc_all_or_none); 106 | 107 | // Scope for fill checks 108 | { 109 | SimpleFillCheck fc0(&order0, 0, 0); 110 | SimpleFillCheck fc1(&order1, q100, q100 * prc57); 111 | SimpleFillCheck fc2(&order2, q100, q100 * prc57); 112 | // Trade at 56 which should trigger order2 which should trade with order 1 at order 1's price 113 | BOOST_CHECK(add_and_verify(book, &order4, expectNoMatch, expectNoComplete, book::oc_all_or_none)); 114 | BOOST_CHECK(add_and_verify(book, &order5, expectMatch, expectComplete, book::oc_all_or_none)); 115 | } 116 | BOOST_CHECK_EQUAL(prc57, book.market_price()); 117 | 118 | SimpleOrder order6(sideBuy, prc54, q1000, 0, book::oc_all_or_none); 119 | SimpleOrder order7(sideSell, prc54, q1000, 0, book::oc_all_or_none); 120 | 121 | // Scope for fill checks 122 | { 123 | SimpleFillCheck fc0(&order0, q100, q100 * prc53); 124 | SimpleFillCheck fc3(&order3, q100, q100 * prc53); 125 | // Trade at 54 which should trigger order3 which should trade with order 0 at order 0's price 126 | BOOST_CHECK(add_and_verify(book, &order6, expectNoMatch, expectNoComplete, book::oc_all_or_none)); 127 | BOOST_CHECK(add_and_verify(book, &order7, expectMatch, expectComplete, book::oc_all_or_none)); 128 | } 129 | BOOST_CHECK_EQUAL(prc53, book.market_price()); 130 | } 131 | 132 | BOOST_AUTO_TEST_CASE(TestStopOrdersCancelBeforeTrigger) 133 | { 134 | SimpleOrderBook book; 135 | book.set_market_price(prc55); 136 | 137 | SimpleOrder bid(sideBuy, prcMkt, q100, prc56); 138 | SimpleOrder ask(sideSell, prcMkt, q100, prc54); 139 | BOOST_CHECK(add_and_verify(book, &bid, expectNoMatch)); 140 | BOOST_CHECK(add_and_verify(book, &ask, expectNoMatch)); 141 | 142 | // Orders were accepted, but not traded 143 | BOOST_CHECK_EQUAL(simple::os_accepted, bid.state()); 144 | BOOST_CHECK_EQUAL(simple::os_accepted, ask.state()); 145 | // Cancel bid and ask 146 | BOOST_CHECK(cancel_and_verify(book, &bid, simple::os_cancelled)); 147 | BOOST_CHECK(cancel_and_verify(book, &ask, simple::os_cancelled)); 148 | } 149 | 150 | } // namespace 151 | -------------------------------------------------------------------------------- /test/unit/ut_utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 -- 2017 Object Computing, Inc. 2 | // All rights reserved. 3 | // See the file license.txt for licensing information. 4 | #pragma once 5 | 6 | #include "depth_check.h" 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | using namespace liquibook::book; 13 | 14 | namespace liquibook { 15 | 16 | typedef simple::SimpleOrderBook<5> SimpleOrderBook; 17 | typedef simple::SimpleOrderBook<5>::DepthTracker SimpleDepth; 18 | 19 | template 20 | bool add_and_verify(OrderBook& order_book, 21 | const OrderPtr& order, 22 | const bool match_expected, 23 | const bool complete_expected = false, 24 | OrderConditions conditions = 0) 25 | { 26 | const bool matched = order_book.add(order, conditions); 27 | if (matched == match_expected) { 28 | if (complete_expected) { 29 | // State should be complete 30 | return simple::os_complete == order->state(); 31 | } else if (conditions & oc_immediate_or_cancel) { 32 | // State should be cancelled 33 | return simple::os_cancelled == order->state(); 34 | } else { 35 | // State should be accepted 36 | return simple::os_accepted == order->state(); 37 | } 38 | } else { 39 | return false; 40 | } 41 | } 42 | 43 | 44 | template 45 | bool cancel_and_verify(OrderBook& order_book, 46 | const OrderPtr& order, 47 | simple::OrderState expected_state) 48 | { 49 | order_book.cancel(order); 50 | return expected_state == order->state(); 51 | } 52 | 53 | template 54 | bool replace_and_verify(OrderBook& order_book, 55 | const OrderPtr& order, 56 | int32_t size_change, 57 | Price new_price = PRICE_UNCHANGED, 58 | simple::OrderState expected_state = simple::os_accepted, 59 | Quantity match_qty = 0) 60 | { 61 | // Calculate 62 | Quantity expected_order_qty = order->order_qty() + size_change; 63 | Quantity expected_open_qty = order->open_qty() + size_change - match_qty; 64 | Price expected_price = 65 | (new_price == PRICE_UNCHANGED) ? order->price() : new_price; 66 | 67 | // Perform 68 | order_book.replace(order, size_change, new_price); 69 | 70 | // Verify 71 | bool correct = true; 72 | if (expected_state != order->state()) { 73 | correct = false; 74 | std::cout << "State " << order->state() << std::endl; 75 | } 76 | if (expected_order_qty != order->order_qty()) { 77 | correct = false; 78 | std::cout << "Order Qty " << order->order_qty() << std::endl; 79 | } 80 | if (expected_open_qty != order->open_qty()) { 81 | correct = false; 82 | std::cout << "Open Qty " << order->open_qty() << std::endl; 83 | } 84 | if (expected_price != order->price()) { 85 | correct = false; 86 | std::cout << "Price " << order->price() << std::endl; 87 | } 88 | return correct; 89 | } 90 | 91 | template 92 | class FillCheck { 93 | public: 94 | FillCheck(OrderPtr order, 95 | Quantity filled_qty, 96 | Cost filled_cost, 97 | OrderConditions conditions = 0) 98 | : order_(order), 99 | expected_filled_qty_(order->filled_qty() + filled_qty), 100 | expected_open_qty_(order->order_qty() - expected_filled_qty_), 101 | expected_filled_cost_(order->filled_cost() + (filled_cost)), 102 | conditions_(conditions) 103 | { 104 | } 105 | 106 | ~FillCheck() { 107 | verify_filled(); 108 | } 109 | 110 | private: 111 | OrderPtr order_; 112 | Quantity expected_filled_qty_; 113 | Quantity expected_open_qty_; 114 | Cost expected_filled_cost_; 115 | OrderConditions conditions_; 116 | 117 | void verify_filled() { 118 | if (expected_filled_qty_ != order_->filled_qty()) { 119 | std::cout << "filled_qty " << order_->filled_qty() 120 | << " expected " << expected_filled_qty_ << std::endl; 121 | throw std::runtime_error("Unexpected filled quantity"); 122 | } 123 | if (expected_open_qty_ != order_->open_qty()) { 124 | std::cout << "open_qty " << order_->open_qty() 125 | << " expected " << expected_open_qty_ << std::endl; 126 | throw std::runtime_error("Unexpected open quantity"); 127 | } 128 | if (expected_filled_cost_ != order_->filled_cost()) { 129 | std::cout << "filled_cost " << order_->filled_cost() 130 | << " expected " << expected_filled_cost_ << std::endl; 131 | throw std::runtime_error("Unexpected filled cost"); 132 | } 133 | // If the order was filled, and is not complete 134 | if (order_->state() != simple::os_complete && !expected_open_qty_) { 135 | std::cout << "state " << order_->state() 136 | << " expected " << simple::os_complete << std::endl; 137 | throw std::runtime_error("Unexpected state with no open quantity"); 138 | // Else If the order was not filled 139 | } else if (expected_open_qty_) { 140 | bool IOC = ((conditions_ & oc_immediate_or_cancel) != 0); 141 | if (order_->state() != simple::os_accepted && !IOC) { 142 | std::cout << "state " << order_->state() 143 | << " expected " << simple::os_accepted << std::endl; 144 | throw std::runtime_error("Unexpected state with open quantity"); 145 | } 146 | if (order_->state() != simple::os_cancelled && IOC) { 147 | std::cout << "state " << order_->state() 148 | << " expected " << simple::os_cancelled << std::endl; 149 | throw std::runtime_error("Unexpected state for IOC with open quantity"); 150 | } 151 | } 152 | } 153 | }; 154 | 155 | 156 | } // namespace 157 | -------------------------------------------------------------------------------- /ut_run.bat: -------------------------------------------------------------------------------- 1 | bin\test\liquibook_unit_test.exe 2 | -------------------------------------------------------------------------------- /web/css/liquibook.css: -------------------------------------------------------------------------------- 1 | .highlight { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /web/easy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Liquibook 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 57 | 58 |
59 | 60 |

Liquibook is Easy. Kindergarten Easy

61 |

To be usable, Liquibook must be easy to integrate. You need to:

62 | 63 |
    64 |
  • Create a class that implements the Order Interface
  • 65 |
  • Instantiate OrderBook and use interface
  • 66 |
  • Handle callbacks by overriding OrderBook::perform_callback()
  • 67 |
  • Either call OrderBook::perform_callbacks() in the calling thread, or signal a background thread to do it.
  • 68 |
69 | 70 |

How do I...?

71 | 72 |
    73 |
  • Use my Order class - Just bind your pointer to order type to the OrderBook template.
  • 74 |
    class MyOrderBook : public OrderBook<MyOrder*> { ... }
    75 |
  • Use shared pointers - just pass a shared pointer type as the OrderBook template argument.
  • 76 |
    typedef boost::shared_ptr MyOrderPtr;
     77 | class MyOrderBook : public OrderBook<MyOrderPtr> { ... }
    78 |
  • Track aggregate depth levels - instantiate Depth in your child OrderBook, and manipulate it in the callback handling, like impl::SimpleOrderBook does.
  • 79 |
    depth_.add_order(...);
     80 | depth_.fill_order(...);
     81 | depth_.close_order(...);
     82 | depth_.replace_order(...);
     83 | depth_.ignore_fill_qty(...);
     84 | 
    85 |
  • Track more than 5 depth levels - Just instantiate Depth with the number you want.
  • 86 |
    typedef book::Depth<10> MyDepth
    87 |
  • Track BBO only - Just instantiate Depth with one level.
  • 88 |
    typedef book::Depth<10> MyBBO
    89 |
  • 90 |
  • 91 |
  • 92 |
  • 93 |
94 | 95 |
96 | 97 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /web/fast.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Liquibook 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 57 | 58 |
59 | 60 |

Liquibook is fast. Blazing Fast

61 |

Liquibook was designed to meet the needs of the most demanding customers.

62 |

Therefore it:

63 | 64 |
    65 |
  • Is written in C++
  • 66 |
  • Has no floating point comparisons
  • 67 |
  • Performs minimal copying of data from your order objects
  • 68 |
69 | 70 |

How Fast?

71 | 72 |

The included performance tests show Liquibook handling over 2 million orders per second.

73 | 74 |

What Does the Performance Test Actually Do?

75 | 76 |

The included performance test allocates a number of orders, and then runs for a given duration, measuring the time to handle them. If the orders run out before the test completes, the test is rerun with double the number of orders until an iteration completes.

77 | 78 |

When complete, it checks the number of orders processed before the end of the test, and reports that, as well as the number of resulting matches (trades).

79 | 80 |

The test runs against three different order books:

81 | 82 |
    83 |
  • Raw order book matching engine
  • 84 |
  • Order book matching engine with BBO tracking
  • 85 |
  • Order book matching engine with 5 levels of aggregation (market depth)
  • 86 |
87 | 88 |

The quoted times is for the slowest of these, with 5 levels of aggregation. You can see the performance history on GitHub where 2 second tests are recorded.

89 | 90 |
91 | 92 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /web/flexible.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Liquibook 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 57 | 58 |
59 | 60 |

Liquibook is flexible. Acrobat Flexible

61 |

Liquibook is desingned to meet a wide array of use cases.

62 |

This means you get to choose:

63 | 64 |
    65 |
  • Your Order class
  • 66 |
  • Your pointer preference (smart or standard)
  • 67 |
  • Your threading model
  • 68 |
  • Your identifiers
  • 69 |
  • Your downstream handling
  • 70 |
  • Your add-ons
  • 71 |
72 | 73 |

How is it so Flexible?

74 | 75 |

Your Order class is used, it must only implement a trivial interface. If you don't have one, a sample one is included.

76 | 77 |

The OrderBook class is a template, which accepts a pointer type as a parameter. You can use any pointer type which dereferences using the -> operator.

78 | 79 |

Liquibook is not an application, but a library, usable within your application. As such, it has no synchronization, nor background threads. You choose whether to have mutliple threads and how to synchronize them.

80 | 81 |

Liquibook does not use order IDs, user IDs, account IDs, or symbols, just Order pointers. Any mapping from order ID to pointer is done outside of Liquibook.

82 | 83 |

Liquibook further assumes all orders given to an order book are for the order book's symbol, and mapping from symbol to order book is done outside of Liquibook.

84 | 85 |

Liquibook does none of the downstream handling, such as feed publishing, resulting from a trade or a quote. After all, that is feed-specific. Instead, liquibook queues up callbacks, which you handle in your code.

86 | 87 |

Liquibook includes optional add-on classes, for depth/BBO tracking, that can easily be integrated in the handling of callbacks. For an example see SimpleOrderBook in the impl directory.

88 | 89 |
90 | 91 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /web/fluid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Liquibook 6 | 7 | 8 | 9 | 10 | 11 | 12 | 30 | 31 | 32 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 69 | 70 |
71 |
72 |
73 | 93 |
94 |
95 |
96 |

Liquibook

97 |

C++ Limit Order Book Matching Engine

98 |

Liquibook is an open source limit order book mathing engine, written in C++. It focues on speed and flexibility, and is ready to integrate into your projects.

99 |

Learn more »

100 |
101 |
102 |
103 |

Fast

104 |

Supports upwards of 2 million order insertions per second. Performs no floating point comparisons.

105 |

View details »

106 |
107 |
108 |

Modular

109 |

Includes optional add-ons for order aggregation by price (market depth) and BBO tracking.

110 |

View details »

111 |
112 |
113 |

Minimal

114 |

Copies minimal number of fields.

115 |

View details »

116 |
117 |
118 |
119 |
120 |

Flexible

121 |

Works with your design - does not impose threading decisions, shared pointers, or an order class.

122 |

View details »

123 |
124 |
125 |

Easy

126 |

Requires your order class implement only 4 methods.

127 |

View details »

128 |
129 |
130 |

Open

131 |

Completely non-viral, open source license to meet your needs.

132 |

View details »

133 |
134 |
135 |
136 |
137 | 138 |
139 | 140 |
141 |

© Object Computing, Inc. 2013

142 |
143 | 144 |
145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /web/get-started.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Liquibook 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 57 | 58 |
59 | 60 |

Liquibook is Fast

61 |

Liquibook was designed to meet the needs of the most demanding customers. This means:

62 | 63 |
    64 |
  • Written in C++
  • 65 |
  • No floating point comparisons
  • 66 |
  • Minimal copying of data from your order objects
  • 67 |
68 | 69 |

How Fast?

70 | 71 |

The included performance tests show Liquibook handling over 2 million orders per second.

72 | 73 |

What Does the Performance Test Actually Do?

74 | 75 |

The included performance test allocates a number of orders, and then runs for a given duration, measuring the time to handle them. If the orders run out before the test completes, the test is rerun with double the number of orders until an iteration completes.

76 | 77 |

When complete, it checks the number of orders processed before the end of the test, and reports that, as well as the number of resulting matches (trades).

78 | 79 |

The test runs against three different order books:

80 | 81 |
    82 |
  • Raw order book matching engine
  • 83 |
  • Order book matching engine with BBO tracking
  • 84 |
  • Order book matching engine with 5 levels of aggregation (market depth)
  • 85 |
86 | 87 |

The quoted times is for the slowest of these, with 5 levels of aggregation. You can see the performance history on GitHub.

88 | 89 |
90 | 91 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /web/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enewhuis/liquibook/2427613b32f1667abae68a01df6af9ba8270f8e7/web/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /web/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enewhuis/liquibook/2427613b32f1667abae68a01df6af9ba8270f8e7/web/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Liquibook Matching Engine 6 | 7 | 8 | 9 | 10 | 11 | 12 | 83 | 84 | 85 | 86 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
101 | 102 |
103 |

Liquibook

104 | 118 |
119 | 120 | 121 |
122 |

Liquibook

123 |

Open source C++ limit order book matching engine

124 |

Liquibook is a fast and flexible matching engine. It works with your design choices, and works with any protocol.

125 | Get started today 126 |
127 | 128 | 129 |
130 |
131 |

Fast

132 |

Supports upwards of 2 million order insertions per second. Performs no floating point comparisons.

133 |

More »

134 |
135 |
136 |

Flexible

137 |

Works with your design - does not impose a threading model, shared pointers, or a specific order class.

138 |

More »

139 |
140 |
141 |

Easy

142 |

Requires your order class implement only 4 methods. Modular design allows you to easily add BBO or market depth support.

143 |

More »

144 |
145 |
146 | 147 |
148 | 149 | 152 | 153 |
154 | 155 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /web/sub-template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Liquibook 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 57 | 58 |
59 | 60 |

Liquibook is Fast

61 |

Liquibook was designed to meet the needs of the most demanding customers. This means:

62 | 63 |
    64 |
  • Written in C++
  • 65 |
  • No floating point comparisons
  • 66 |
  • Minimal copying of data from your order objects
  • 67 |
68 | 69 |

How Fast?

70 | 71 |

The included performance tests show Liquibook handling over 2 million orders per second.

72 | 73 |

What Does the Performance Test Actually Do?

74 | 75 |

The included performance test allocates a number of orders, and then runs for a given duration, measuring the time to handle them. If the orders run out before the test completes, the test is rerun with double the number of orders until an iteration completes.

76 | 77 |

When complete, it checks the number of orders processed before the end of the test, and reports that, as well as the number of resulting matches (trades).

78 | 79 |

The test runs against three different order books:

80 | 81 |
    82 |
  • Raw order book matching engine
  • 83 |
  • Order book matching engine with BBO tracking
  • 84 |
  • Order book matching engine with 5 levels of aggregation (market depth)
  • 85 |
86 | 87 |

The quoted times is for the slowest of these, with 5 levels of aggregation. You can see the performance history on GitHub.

88 | 89 |
90 | 91 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /winenv.bat: -------------------------------------------------------------------------------- 1 | @REM Copyright (c) 2017 Object Computing, Inc. 2 | @REM All rights reserved. 3 | @REM See the file license.txt for licensing information. 4 | 5 | REM Setting up LiquiBook environment 6 | 7 | @REM LiquiBook depends on MPC V 3.6 or later. (http://www.ociweb.com/products/mpc) 8 | @REM LiquiBook depends on BOOST V 1.43.0 or later. (http://www.boost.org/) 9 | @REM Assumes VC10(Visual Studio 2010), VC12(Visual Studio 2013), or VC14 (Visual Studio 2015) 10 | @REM installed in the default location (see VC_ROOT below) and VC..COMNTOOLS is set. 11 | 12 | @REM Customize this file by setting variables to suit your environment 13 | @REM Also you should customize the LiquiBook.features file to enable particular features on your system. 14 | 15 | @REM --------START: DELETE THESE LINES AFTER CUSTOMIZING ---- 16 | @ECHO Please make a copy of %~f0 and customize it according to the instructions in the file. 17 | @ECHO You should also customize %~dp0\Liquibook.features 18 | @goto end 19 | @REM --------END: DELETE THESE LINES AFTER CUSTOMIZING ---- 20 | 21 | @echo off 22 | REM ===================================================================================== 23 | REM EDIT THE FOLLOWING LINES OR SET THESE VALUES IN YOUR ENVIRONMENT BEFORE RUNNING SETUP 24 | if "a" == "a%MPC_ROOT%" set MPC_ROOT=c:\MPC\MPC_4_1_22 25 | if "a" == "a%BOOST_VERSION%" set BOOST_VERSION=boost_1_61_0 26 | if "a" == "a%BOOST_ROOT%" set BOOST_ROOT=c:\boost\%BOOST_VERSION% 27 | 28 | @REM TODO: For the pub/sub example program set QUICKFAST_ROOT 29 | @rem to the actual quickfast directory and 30 | @rem define XEERCESROOT 31 | if "a" == "a%QUICKFAST_ROOT%" set QUICKFAST_ROOT=noQuickFAST 32 | if "a" == "a%XERCESROOT%" set XERCESROOT=C:\Progs\xerces\xerces-c-3.1.1-x86_64-windows-vc-10.0 33 | 34 | REM END OF VALUES TO BE SET 35 | REM ===================================================================================== 36 | 37 | REM Microsoft moved 32 bit apps to a new program files directory on 64 bit systems 38 | set PROGRAM_FILES_X86=Program Files 39 | if exist "C:\Program Files (x86)" set PROGRAM_FILES_X86=Program Files (x86) 40 | 41 | REM Verify setup by checking for expected files/directories 42 | set SETUP_CHECKING=MPC_ROOT=%MPC_ROOT% 43 | if not exist "%MPC_ROOT%\mpc.pl" goto setup_is_bad 44 | 45 | set SETUP_CHECKING=BOOST_ROOT=%BOOST_ROOT% 46 | if not exist "%BOOST_ROOT%\boost" goto setup_is_bad 47 | 48 | REM if you are not running unit tests, delete the REM on the next line 49 | REM goto noBoost 50 | set SETUP_CHECKING=BOOST_ROOT lib=%BOOST_ROOT%\lib 51 | if not exist "%BOOST_ROOT%\lib" goto setup_is_bad 52 | :noBoost 53 | 54 | REM If you are not building the pub/sub app remove the REM on the next line 55 | REM goto noPubSub 56 | 57 | set SETUP_CHECKING=QUICKFAST_ROOT contains QuickFASTApplication.mpb 58 | if not exist %QUICKFAST_ROOT%\QuickFASTApplication.mpb goto setup_is_bad 59 | 60 | set SETUP_CHECKING=XERCESROOT=%XERCESROOT% 61 | if not exist "%XERCESROOT%\lib" goto setup_is_bad 62 | 63 | :noPubSub 64 | 65 | REM Find visual studio. 66 | REM You can short-circuit this by setting VCVER before running this 67 | REM However this also skips the check to see if VC is installed in the expected place 68 | 69 | rem if VCVER is already set, skip the discovery 70 | if not "a" == "a%VCVER%" goto setup_is_ok 71 | 72 | set SETUP_CHECKING=Setup checking visual studio common tools 73 | 74 | set VCVER=15 75 | set SETUP_CHECKING=VS150COMNTOOLS=%VS150COMNTOOLS% 76 | set VS_COMMON_TOOLS=%VS150COMNTOOLS% 77 | set VC_ROOT=C:\%PROGRAM_FILES_X86%\Microsoft Visual Studio 15.0\VC\bin 78 | if exist "%VS_COMMON_TOOLS%VSVARS32.BAT" goto setup_is_ok 79 | 80 | set VCVER=14 81 | set SETUP_CHECKING=VS140COMNTOOLS=%VS140COMNTOOLS% 82 | set VS_COMMON_TOOLS=%VS140COMNTOOLS% 83 | set VC_ROOT=C:\%PROGRAM_FILES_X86%\Microsoft Visual Studio 14.0\VC\bin 84 | if exist "%VS_COMMON_TOOLS%VSVARS32.BAT" goto setup_is_ok 85 | 86 | set VCVER=13 87 | set SETUP_CHECKING=VS130COMNTOOLS=%VS130COMNTOOLS% 88 | set VS_COMMON_TOOLS=%VS130COMNTOOLS% 89 | set VC_ROOT=C:\%PROGRAM_FILES_X86%\Microsoft Visual Studio 13.0\VC\bin 90 | if exist "%VS_COMMON_TOOLS%VSVARS32.BAT" goto setup_is_ok 91 | 92 | set VCVER=12 93 | set SETUP_CHECKING=VS120COMNTOOLS=%VS120COMNTOOLS% 94 | set VS_COMMON_TOOLS=%VS130COMNTOOLS% 95 | set VC_ROOT=C:\%PROGRAM_FILES_X86%\Microsoft Visual Studio 12.0\VC\bin 96 | if exist "%VS_COMMON_TOOLS%VSVARS32.BAT" goto setup_is_ok 97 | 98 | set VCVER=11 99 | set SETUP_CHECKING=VS110COMNTOOLS=%VS110COMNTOOLS% 100 | set VS_COMMON_TOOLS=%VS110COMNTOOLS% 101 | set VC_ROOT=C:\%PROGRAM_FILES_X86%\Microsoft Visual Studio 11.0\VC\bin 102 | if exist "%VS_COMMON_TOOLS%VSVARS32.BAT" goto setup_is_ok 103 | 104 | set VCVER=10 105 | set SETUP_CHECKING=VS100COMNTOOLS=%VS100COMNTOOLS% 106 | set VS_COMMON_TOOLS=%VS100COMNTOOLS% 107 | set VC_ROOT=C:\%PROGRAM_FILES_X86%\Microsoft Visual Studio 10.0\VC\bin 108 | if exist "%VS_COMMON_TOOLS%VSVARS32.BAT" goto setup_is_ok 109 | 110 | set VCVER=9 111 | set SETUP_CHECKING=VS90COMNTOOLS=%VS90COMNTOOLS% 112 | set VS_COMMON_TOOLS=%VS90COMNTOOLS% 113 | set VC_ROOT=C:\%PROGRAM_FILES_X86%\Microsoft Visual Studio 9.0\VC\bin 114 | if exist "%VS_COMMON_TOOLS%VSVARS32.BAT" goto setup_is_ok 115 | 116 | set VCVER=8 117 | set SETUP_CHECKING=VS80COMNTOOLS=%VS80COMNTOOLS% 118 | set VS_COMMON_TOOLS=%VS80COMNTOOLS% 119 | set VC_ROOT=C:\%PROGRAM_FILES_X86%\Microsoft Visual Studio 8.0\VC\bin 120 | if exist "%VS_COMMON_TOOLS%VSVARS32.BAT" goto setup_is_ok 121 | REM goto setup_is_bad (you are here) REM Find visual studio. 122 | rem if VCVER is already set, skip the discovery 123 | if not "a" == "a%VCVER%" goto setup_is_ok 124 | 125 | REM goto setup_is_bad (you are here) 126 | 127 | :setup_is_bad 128 | ECHO Setup check failed: %SETUP_CHECKING% 129 | ECHO Edit the setup.cmd file or change environment variables 130 | goto end 131 | 132 | :setup_is_ok 133 | set SETUP_CHECKING= 134 | set LIQUIBOOK_ROOT=%~dp0 135 | 136 | call "%VS_COMMON_TOOLS%VSVARS32.BAT" >nul 137 | 138 | REM: This avoids growing PATH and INCLUDE every time setup is run 139 | if "a" == "a%BASE_PATH%" set BASE_PATH=%PATH% 140 | if "a" == "a%BASE_INCLUDE%" set BASE_INCLUDE=%INCLUDE% 141 | 142 | set RELEASE_PATH=%LiquiBook_ROOT%\bin;%LiquiBook_ROOT%\Output\Release;%MPC_ROOT%;%BOOST_ROOT%\lib;%LiquiBook_ROOT%\lib;%BASE_PATH% 143 | set DEBUG_PATH=%LiquiBook_ROOT%\bin;%LiquiBook_ROOT%\Output\Debug;%MPC_ROOT%;%BOOST_ROOT%\lib;%LiquiBook_ROOT%\lib;%BASE_PATH% 144 | 145 | if "a" == "a%QUICKFAST_ROOT%" goto noQuickFAST 146 | set RELEASE_PATH=%RELEASE_PATH%;%QUICKFAST_ROOT%\Output\Release 147 | set DEBUG_PATH=%DEBUG_PATH%;%QUICKFAST_ROOT%\Output\Debug 148 | :noQuickFAST 149 | 150 | if "a" == "a%XERCESROOT%" goto noXERCES 151 | set RELEASE_PATH=%RELEASE_PATH%;%XERCESROOT%\bin 152 | set DEBUG_PATH=%DEBUG_PATH%;%XERCESROOT%\bin 153 | :noXERCES 154 | 155 | set PATH=%DEBUG_PATH% 156 | set INCLUDE=%BOOST_ROOT%\include;%BASE_INCLUDE% 157 | 158 | title LiquiBook 159 | :end 160 | 161 | 162 | -------------------------------------------------------------------------------- /winenv_clear.bat: -------------------------------------------------------------------------------- 1 | @REM Copyright (c) 2009, 2010 Object Computing, Inc. 2 | @REM All rights reserved. 3 | @REM See the file license.txt for licensing information. 4 | 5 | REM This undefines the variables set by Setup to make it easy to rerun 6 | REM setup after making changes. It should not be needed unless you are 7 | REM editing the setup.cmd file. 8 | set MPC_ROOT= 9 | set XERCESROOT= 10 | set XERCES_LIBNAME= 11 | set BOOST_VERSION= 12 | set BOOST_ROOT= 13 | set QUICKFAST_ROOT= 14 | --------------------------------------------------------------------------------