libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
order_book_impl.hpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2020 by J.M.McGuiness, isimud@hussar.me.uk
3 **
4 ** This library is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU Lesser General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2.1 of the License, or (at your option) any later version.
8 **
9 ** This library is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public
15 ** License along with this library; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 
19 namespace isimud { namespace ISIMUD_VER_NAMESPACE {
20 
21 template<class MsgType>
22 inline std::ostream &
23 to_stream(std::ostream &os, typename isimud::ISIMUD_VER_NAMESPACE::order_book<MsgType>::orders_by_time const &o) {
24  os<<o.front().limitPrice()<<": ";
25  std::transform(
26  o.begin(),
27  o.end(),
28  std::experimental::make_ostream_joiner(os, " "),
29  [](auto const &v) {
30  return v.orderQty();
31  }
32  );
33  os<<'\n';
34  return os;
35 }
36 
37 template<class MsgType>
38 inline
39 order_book<MsgType>::internal_order::internal_order(sequence_number_t seq, element_type const &o) noexcept(true)
40 : element_type(o), sequence_number_(seq) {
41 }
42 
43 template<class MsgType>
44 inline typename order_book<MsgType>::internal_order &
45 order_book<MsgType>::internal_order::operator=(internal_order const &o) noexcept(true) {
46  this->~internal_order();
47  new (this) internal_order(o);
48  return *this;
49 }
50 
51 template<class MsgType>
52 inline bool
53 order_book<MsgType>::cheapest_t::operator()(orders_by_time const &l, orders_by_time const &r) const noexcept(true) {
54  return l.front().limitPrice()<r.front().limitPrice();
55 }
56 
57 template<class MsgType>
58 inline bool
59 order_book<MsgType>::most_expensive_t::operator()(orders_by_time const &l, orders_by_time const &r) const noexcept(true) {
60  return r.front().limitPrice()<l.front().limitPrice();
61 }
62 
63 template<class MsgType>
64 inline bool
65 order_book<MsgType>::cheapest_orders_by_time_t::empty() const noexcept(true) {
66  return std::find_if_not(
67  this->c.begin(),
68  this->c.end(),
69  [](auto const &v) {return v.empty();}
70  )==this->c.end();
71 }
72 
73 template<class MsgType>
74 inline typename order_book<MsgType>::cheapest_orders_by_time_t::reference
75 order_book<MsgType>::cheapest_orders_by_time_t::top() noexcept(true) {
76  return this->c.front();
77 }
78 
79 template<class MsgType>
80 inline void
81 order_book<MsgType>::cheapest_orders_by_time_t::insert(typename value_type::value_type::element_type const &o, sequence_number_t &sequence_number) {
82  assert(sequence_number<std::numeric_limits<sequence_number_t>::max()-1);
83  if (this->c.empty()) {
84  this->emplace(1, typename value_type::value_type{sequence_number++, o});
85  } else {
86  auto iter=std::lower_bound(
87  this->c.begin(),
88  this->c.end(),
89  o,
90  [](auto const &l, auto const &r) {
91  return l.front().limitPrice()>r.limitPrice();
92  }
93  );
94  if (iter!=this->c.end()) {
95  assert(iter->front().limitPrice()<=o.limitPrice());
96  if (iter->front().limitPrice()==o.limitPrice()) {
97  iter->emplace_back(sequence_number++, o);
98  } else {
99  this->emplace(1, typename value_type::value_type{sequence_number++, o});
100  }
101  } else {
102  this->emplace(1, typename value_type::value_type{sequence_number++, o});
103  }
104  }
105 }
106 
107 template<class MsgType>
108 inline void
109 order_book<MsgType>::cheapest_orders_by_time_t::erase(typename value_type::value_type::element_type const &o) {
110  auto iter=std::remove_if(
111  this->c.begin(),
112  this->c.end(),
113  [&o](auto &v) {
114  auto iter=std::remove_if(
115  v.begin(),
116  v.end(),
117  [&o](auto const &v) {
118  return o.clientOrderID()==v.clientOrderID();
119  }
120  );
121  typename std::remove_reference<decltype(v)>::type tmp(v.begin(), iter);
122  tmp.swap(v);
123  return v.empty();
124  }
125  );
126  typename std::remove_reference<decltype(this->c)>::type tmp(this->c.begin(), iter);
127  tmp.swap(this->c);
128  std::sort(this->c.rbegin(), this->c.rend(), this->comp);
129 }
130 
131 template<class MsgType>
132 inline std::ostream &
133 order_book<MsgType>::cheapest_orders_by_time_t::to_stream(std::ostream &os) const {
134  std::sort(const_cast<cheapest_orders_by_time_t *>(this)->c.rbegin(), const_cast<cheapest_orders_by_time_t *>(this)->c.rend(), this->comp);
135  std::for_each(
136  this->c.begin(),
137  this->c.end(),
138  [&os](auto const &v) {
139  libisimud::to_stream<MsgType>(os, v);
140  }
141  );
142  return os;
143 }
144 
145 template<class MsgType>
146 inline bool
147 order_book<MsgType>::most_expensive_by_time_t::empty() const noexcept(true) {
148  return std::find_if_not(
149  this->c.begin(),
150  this->c.end(),
151  [](auto const &v) {return v.empty();}
152  )==this->c.end();
153 }
154 
155 template<class MsgType>
156 inline typename order_book<MsgType>::most_expensive_by_time_t::reference
157 order_book<MsgType>::most_expensive_by_time_t::top() noexcept(true) {
158  return this->c.front();
159 }
160 
161 template<class MsgType>
162 inline void
163 order_book<MsgType>::most_expensive_by_time_t::insert(typename value_type::value_type::element_type const &o, sequence_number_t &sequence_number) {
164  assert(sequence_number<std::numeric_limits<sequence_number_t>::max()-1);
165  if (this->c.empty()) {
166  this->emplace(1, typename value_type::value_type{sequence_number++, o});
167  } else {
168  auto iter=std::lower_bound(
169  this->c.begin(),
170  this->c.end(),
171  o,
172  [](auto const &l, auto const &r) {
173  const auto lp=l.front().limitPrice();
174  return lp<r.limitPrice();
175  }
176  );
177  if (iter!=this->c.end()) {
178  assert(iter->front().limitPrice()>=o.limitPrice());
179  if (iter->front().limitPrice()==o.limitPrice()) {
180  iter->emplace_back(sequence_number++, o);
181  } else {
182  this->emplace(1, typename value_type::value_type{sequence_number++, o});
183  }
184  } else {
185  this->emplace(1, typename value_type::value_type{sequence_number++, o});
186  }
187  }
188 }
189 
190 template<class MsgType>
191 inline void
192 order_book<MsgType>::most_expensive_by_time_t::erase(typename value_type::value_type::element_type const &o) {
193  auto iter=std::remove_if(
194  this->c.begin(),
195  this->c.end(),
196  [&o](auto &v) {
197  auto iter=std::remove_if(
198  v.begin(),
199  v.end(),
200  [&o](auto const &v) {
201  return o.clientOrderID()==v.clientOrderID();
202  }
203  );
204  typename std::remove_reference<decltype(v)>::type tmp(v.begin(), iter);
205  tmp.swap(v);
206  return v.empty();
207  }
208  );
209  typename std::remove_reference<decltype(this->c)>::type tmp(this->c.begin(), iter);
210  tmp.swap(this->c);
211  std::sort(this->c.begin(), this->c.end(), this->comp);
212 }
213 
214 template<class MsgType>
215 inline std::ostream &
216 order_book<MsgType>::most_expensive_by_time_t::to_stream(std::ostream &os) const {
217  std::for_each(
218  this->c.rbegin(),
219  this->c.rend(),
220  [&os](auto const &v) {
221  libisimud::to_stream<MsgType>(os, v);
222  }
223  );
224  return os;
225 }
226 
227 template<class MsgType>
228 inline bool
229 order_book<MsgType>::order_id_t::operator==(order_id_t const &o) noexcept(true) {
230  return order_id_==o.order_id_;
231 }
232 
233 template<class MsgType>
234 inline typename order_book<MsgType>::order_id_t &
235 order_book<MsgType>::order_id_t::operator++() noexcept(true) {
236  auto tmp=libjmmcg::fromstring<std::uint64_t>(order_id_.data(), order_id_.size());
237  ++tmp;
238  [[maybe_unused]] auto const quash_warning=libjmmcg::tostring(tmp, order_id_.data(), order_id_.size());
239  return *this;
240 }
241 
242 template<class MsgType>
243 inline typename order_book<MsgType>::order_id_t
244 order_book<MsgType>::order_id_t::operator++(int) noexcept(true) {
245  const auto tmp=*this;
246  ++*this;
247  return tmp;
248 }
249 
250 template<class MsgType>
251 inline
252 order_book<MsgType>::order_book() {
253 }
254 
255 template<class MsgType>
256 inline
257 order_book<MsgType>::~order_book() {
258 }
259 
260 template<class MsgType>
261 inline bool
262 order_book<MsgType>::empty() const noexcept(true) {
263  return all_open_orders_.empty();
264 }
265 
266 template<class MsgType>
267 inline bool
268 order_book<MsgType>::is_crossed() const noexcept(true) {
269  for (auto const &open_orders : all_open_orders_) {
270  auto const ask_empty=open_orders.second.open_ask_orders.empty();
271  auto const bid_empty=open_orders.second.open_bid_orders.empty();
272  if ((!ask_empty && !bid_empty) && (open_orders.second.best_bid().limitPrice()>=open_orders.second.best_ask().limitPrice())) {
273  return true;
274  }
275  }
276  return false;
277 }
278 
279 template<class MsgType>
280 inline typename order_book<MsgType>::price_t
281 order_book<MsgType>::agreed_spread(symbol_t const &symbol) const noexcept(true) {
282  auto iter=all_open_orders_.find(symbol);
283  if (iter!=all_open_orders_.end()) {
284  auto const ask_empty=iter->second.open_ask_orders.empty();
285  auto const bid_empty=iter->second.open_bid_orders.empty();
286  return (ask_empty || bid_empty) ? false : iter->second.best_ask().limitPrice()-iter->second.best_bid().limitPrice();
287  } else {
288  return price_t{};
289  }
290 }
291 
292 template<class MsgType>
293 inline void
294 order_book<MsgType>::place(order_t const &order) noexcept(false) {
295  using namespace libjmmcg;
296 
297  if (sequence_number_>=std::numeric_limits<sequence_number_t>::max()-1) {
298  info::function fun(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("order_t order"), tostring(order)));
299  fun.add_arg(info::function::argument(_T("sequence number"), tostring(sequence_number_)));
300  throw exception_type(_T("Sequence number wrapped. Code must restart."), std::move(fun), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
301  }
302  switch (order.side()) {
303  case side_t::Buy:
304  all_open_orders_[order.instrumentID()].open_bid_orders.insert(order, sequence_number_);
305  assert(!all_open_orders_[order.instrumentID()].open_bid_orders.empty());
306  break;
307  case side_t::Sell:
308  all_open_orders_[order.instrumentID()].open_ask_orders.insert(order, sequence_number_);
309  assert(!all_open_orders_[order.instrumentID()].open_ask_orders.empty());
310  break;
311  default: {
312  info::function fun(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("order_t order"), tostring(order)));
313  throw exception_type(_T("Unhandled side to the order."), std::move(fun), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
314  }
315  };
316  assert(!empty());
317 }
318 
319 template<class MsgType>
320 inline void
321 order_book<MsgType>::cancel(order_t const &order) noexcept(false) {
322  using namespace libjmmcg;
323 
324  switch (order.side()) {
325  case side_t::Buy:
326  all_open_orders_[order.instrumentID()].open_bid_orders.erase(order);
327  break;
328  case side_t::Sell:
329  all_open_orders_[order.instrumentID()].open_ask_orders.erase(order);
330  break;
331  default: {
332  info::function fun(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("order_t order"), tostring(order)));
333  throw exception_type(_T("Unhandled side to the order."), std::move(fun), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
334  }
335  };
336  if (empty()) {
337  sequence_number_=sequence_number_t{};
338  }
339 }
340 
341 template<class MsgType>
342 inline typename order_book<MsgType>::trades_t
343 order_book<MsgType>::match() {
344  trades_t trades;
345  while (is_crossed()) {
346  for (auto &open_orders : all_open_orders_) {
347  auto &best_bid_orders=open_orders.second.open_bid_orders.top();
348  auto &best_ask_orders=open_orders.second.open_ask_orders.top();
349  while (!best_ask_orders.empty() && !best_bid_orders.empty()) {
350  auto &best_ask_order=best_ask_orders.front();
351  auto &best_bid_order=best_bid_orders.front();
352  assert(best_ask_order.instrumentID()==best_bid_order.instrumentID());
353  assert(is_crossed());
354  // We'd better be aggressive, as we must still be crossed....
355  assert(best_bid_order.limitPrice()>=best_ask_order.limitPrice());
356  auto const traded_quantity=std::min(best_ask_order.orderQty(), best_bid_order.orderQty());
357  // Must have something to trade as crossed & aggressive.
358  assert(traded_quantity>quantity_t{});
359  // Sequence numbers must be unique.
360  assert(best_ask_order.sequence_number_!=best_bid_order.sequence_number_);
361  auto const &order_to_trade_against=best_ask_order.sequence_number_<best_bid_order.sequence_number_ ? best_ask_order : best_bid_order;
362  trades.emplace_back(
363  sequence_number_++,
364  order_to_trade_against.clientOrderID(),
365  msg_details_t::ExecType::Trade,
366  order_to_trade_against.limitPrice(),
367  best_ask_order.instrumentID(),
368  best_ask_order.side(),
369  traded_quantity,
370  order_to_trade_against.orderQty()-traded_quantity,
371  order_to_trade_against.find_MIC()
372  );
373  assert(best_bid_order.orderQty()>=traded_quantity);
374  best_bid_order.orderQty(best_bid_order.orderQty()-traded_quantity);
375  if (best_bid_order.orderQty()<=quantity_t{}) {
376  // Order has been traded, so delete it.
377  best_bid_orders.pop_front();
378  }
379  best_ask_order.orderQty(best_ask_order.orderQty()-traded_quantity);
380  if (best_ask_order.orderQty()<=quantity_t{}) {
381  // Order has been traded, so delete it.
382  best_ask_orders.pop_front();
383  }
384  }
385  if (best_ask_orders.empty()) {
386  open_orders.second.open_ask_orders.pop();
387  }
388  if (best_bid_orders.empty()) {
389  open_orders.second.open_bid_orders.pop();
390  }
391  }
392  }
393  if (empty()) {
394  sequence_number_=sequence_number_t{};
395  }
396  return trades;
397 }
398 
399 template<class MsgType>
400 inline typename order_book<MsgType>::cheapest_orders_by_time_t::value_type::value_type const &
401 order_book<MsgType>::open_orders_t::best_bid() const noexcept(true) {
402  assert(!open_bid_orders.empty());
403  return open_bid_orders.top().front();
404 }
405 
406 template<class MsgType>
407 inline typename order_book<MsgType>::most_expensive_by_time_t::value_type::value_type const &
408 order_book<MsgType>::open_orders_t::best_ask() const noexcept(true) {
409  assert(!open_ask_orders.empty());
410  return open_ask_orders.top().front();
411 }
412 
413 template<class MsgType>
414 inline std::ostream &
415 operator<<(std::ostream &os, order_book<MsgType> const &ob) {
416 // order_book<MsgType>::msg_details_t::to_stream(os);
417 // os<<"\nSequence number="<<ob.sequence_number_
418 // <<", order ID: '"<<ob.order_id_.order_id_<<"'"
419 // "\n=================\n"
420  os<< "=================\n"
421  "ASK\n";
422  if (ob.all_open_orders_.empty()) {
423  os<<"------------\n";
424  } else {
425  for (auto const &open_orders : ob.all_open_orders_) {
426  open_orders.second.open_ask_orders.to_stream(os);
427  os<<"------------\n";
428  open_orders.second.open_bid_orders.to_stream(os);
429  }
430  }
431  os<<"BID\n"
432  "=================\n";
433  return os;
434 }
435 
436 } }