libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
order_book.hpp
Go to the documentation of this file.
1 #ifndef ISIMUD_EXCHANGES_ORDER_BOOK_ORDER_BOOK_HPP
2 #define ISIMUD_EXCHANGES_ORDER_BOOK_ORDER_BOOK_HPP
3 /******************************************************************************
4 ** Copyright © 2020 by J.M.McGuiness, isimud@hussar.me.uk
5 **
6 ** This library is free software; you can redistribute it and/or
7 ** modify it under the terms of the GNU Lesser General Public
8 ** License as published by the Free Software Foundation; either
9 ** version 2.1 of the License, or (at your option) any later version.
10 **
11 ** This library is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ** Lesser General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public
17 ** License along with this library; if not, write to the Free Software
18 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20 
21 #include "../common/config.h"
22 
23 #include "core/exception.hpp"
24 
25 #include <algorithm>
26 /**
27  * \todo Update when this is official.
28  */
29 #include <experimental/iterator>
30 #include <list>
31 #include <map>
32 #include <queue>
33 
34 namespace isimud { namespace ISIMUD_VER_NAMESPACE {
35 
36  /// A simple implementation of a price/time limit order book.
37  /**
38  * Exchange matching engines use an order book to match orders from buyers and sellers.
39  *
40  * \note A production-quality program would place this in shared memory (to be regularly flushed to disk) e.g. to be used in case the order book process crashes, so that the data is not lost, and can be recovered.
41  *
42  * \note The algorithmic complexities all assume that the number of symbols is much smaller than the number of trades on the book. If this is not the case, then the log(num_symbols) lookup may dominate, but this is unlikely.
43  */
44  template<class MsgType>
45  class order_book {
46  public:
47  using msg_details_t=MsgType;
48  using price_t=typename msg_details_t::Price_t;
49  using quantity_t=typename msg_details_t::Quantity_t;
50  using side_t=typename msg_details_t::Side;
51  using symbol_t=typename msg_details_t::SecurityID_t;
52  using order_t=typename msg_details_t::NewOrder_t;
53  using execution_t=typename msg_details_t::ExecutionReport;
54  using lock_traits=libjmmcg::ppd::api_lock_traits<libjmmcg::ppd::platform_api, libjmmcg::ppd::heavyweight_threading>;
55  using exception_type=lock_traits::exception_type;
56 
57  private:
58  using sequence_number_t=typename msg_details_t::SeqNum_t;
59  /**
60  * This is needed to add a monotonically increasing number, that effectively orders the trades. This ordering is needed to permit the match() algorithm to select the correct price that the trade occurred at.
61  */
62  struct internal_order final : public order_t {
63  using element_type=order_t;
64 
65  const sequence_number_t sequence_number_;
66 
67  internal_order(sequence_number_t seq, element_type const &o) noexcept(true);
68  internal_order(internal_order const &) noexcept(true)=default;
69 
70  /**
71  * Needed because of implementation details regarding gcc & std::vector requiring assignment.
72  */
73  internal_order &operator=(internal_order const &o) noexcept(true);
74  };
75 
76  public:
78  /**
79  * We push_back(), so orders towards the beginning of the collection are older than those towards the end.
80  *
81  * \todo Consider a faster collection, such as boost circular buffer for something similar.
82  */
84  struct cheapest_t {
86 
87  bool operator()(value_type const &l, value_type const &r) const noexcept(true);
88  };
91 
92  bool operator()(value_type const &l, value_type const &r) const noexcept(true);
93  };
96  using reference=typename base_t::reference;
97  using value_type=typename base_t::value_type;
98 
99  using base_t::top;
100  bool empty() const noexcept(true);
101  reference top() noexcept(true);
103  void erase(typename value_type::value_type::element_type const &o);
104 
105  std::ostream &to_stream(std::ostream &os) const;
106  };
108  public:
110  using reference=typename base_t::reference;
111  using value_type=typename base_t::value_type;
112 
113  using base_t::top;
114  bool empty() const noexcept(true);
115  reference top() noexcept(true);
117  void erase(typename value_type::value_type::element_type const &o);
118 
119  std::ostream &to_stream(std::ostream &os) const;
120  };
121 
122  order_book();
123  ~order_book();
124 
125  /**
126  * Algorithmic complexity: O(1)
127  *
128  * \return False if there are any trades that have been placed, otherwise true.
129 *** */
130  bool empty() const noexcept(true);
131  /**
132  * Algorithmic complexity: O(num_symbols*n) where num_symbols is the total number of symbols that have been placed and n is the total number of orders in all the books.
133  *
134  * \return False if any there are no bids or no asks, otherwise the result of comparing the top prices.
135  */
136  bool is_crossed() const noexcept(true);
137  /**
138  * Algorithmic complexity: O(n) where n is the total number of orders in all the books.
139  *
140  * \return The spread for the specified symbol, if there are prices on both sides, otherwise zero.
141  */
142  price_t agreed_spread(symbol_t const &symbol) const noexcept(true);
143 
144  /// Place the requested order in the book.
145  /**
146  * This may leave the order book in a crossed state, i.e. there may be available trades, so match() may have to be called after calling place().
147  *
148  * Algorithmic complexity: O(log(n)) where n is the total number of orders in all the books.
149  *
150  * \see cancel(), is_crossed(), match()
151  */
152  void place(order_t const &order) noexcept(false);
153 
154  /// Remove the specified order from the order book.
155  /**
156  * Algorithmic complexity: O(n^2) where n is the total number of orders in all the books.
157  *
158  * \see place(), match()
159  */
160  void cancel(order_t const &order) noexcept(false);
161 
162  /// Return all possible trades, also uncrossing the order book.
163  /**
164  * After calling place() this function may need to be called as the order book may be crossed and thus potential executions that only match() can discover.
165  *
166  * Algorithmic complexity: O(num_executions*num_symbols*log(n)) where:
167  * -# num_executions is the total number of executions that would result,
168  * -# num_symbols is the total number of symbols that have been placed,
169  * -# n is the largest number of orders in all the books across the symbols.
170  *
171  * \return All possible trades; non-empty if crossed, otherwise empty.
172  *
173  * \see place(), cancel()
174  */
175  trades_t match();
176 
177  /**
178  * Algorithmic complexity: O(n) where n is the total number of orders in all the books.
179  */
180  template<class MsgTypes1>
181  friend std::ostream &operator<<(std::ostream &os, order_book<MsgTypes1> const &o);
182 
183  private:
184  struct open_orders_t {
185  most_expensive_by_time_t open_ask_orders{}; ///< Orders for sale.
186  cheapest_orders_by_time_t open_bid_orders{}; ///< Orders for purchase/buying.
187 
188  typename cheapest_orders_by_time_t::value_type::value_type const &best_bid() const noexcept(true);
189  typename most_expensive_by_time_t::value_type::value_type const &best_ask() const noexcept(true);
190  };
191  struct order_id_t {
192  using msg_order_id_t=typename msg_details_t::ClientOrderID_t;
193 
194  msg_order_id_t order_id_;
195 
196  bool operator==(order_id_t const &) noexcept(true);
197 
198  order_id_t &operator++() noexcept(true);
199  [[nodiscard]] order_id_t operator++(int) noexcept(true);
200  };
201  /**
202  * \todo May want to use a std::unordered_map (or equivalent). I need to benchmark to identify if this is a real issue.
203  */
205 
206  sequence_number_t sequence_number_{};
207  order_id_t order_id_{};
208  all_open_orders_t all_open_orders_;
209  };
210 
211 } }
212 
213 #include "order_book_impl.hpp"
214 
215 #endif