JayBeams  0.1
Another project to have fun coding.
array_based_order_book.hpp
Go to the documentation of this file.
1 #ifndef jb_itch5_array_based_order_book_hpp
2 #define jb_itch5_array_based_order_book_hpp
3 
7 #include <jb/assert_throw.hpp>
8 #include <jb/config_object.hpp>
9 #include <jb/feed_error.hpp>
10 #include <jb/log.hpp>
11 
12 #include <algorithm>
13 #include <cmath>
14 #include <functional>
15 #include <map>
16 #include <utility>
17 #include <vector>
18 
19 namespace jb {
20 namespace itch5 {
21 
22 namespace detail {
23 /**
24  * Raise the right exception when we detect invalid parameters for
25  * add_order() or reduce_order()
26  *
27  * @param operation the name of the operation that is being processed,
28  * typically add_order() or reduce_order().
29  * @param qty the qty in the operation
30  * @param px the price of the operation
31  *
32  * @throws a feed_error with nicely formatted output **always**
33  */
34 [[noreturn]] void
35 raise_invalid_operation_parameters(char const* operation, int qty, price4_t px);
36 
37 /**
38  * Validate the input parameters for all array_order_book operations.
39  *
40  * Both add_order() and reduce_order() require basically the same
41  * validation, refactor to a common function.
42  *
43  * @param operation the name of the operation that is being processed,
44  * typically add_order() or reduce_order().
45  * @param qty the qty in the operation
46  * @param px the price of the operation
47  *
48  * @throws a feed_error with nicely formatted output if the validation fails.
49  */
50 inline void
51 validate_operation_params(char const* operation, int qty, price4_t px) {
52  if (qty > 0 and px >= price4_t(0) and
53  px < max_price_field_value<price4_t>()) {
54  return;
55  }
56  raise_invalid_operation_parameters(operation, qty, px);
57 }
58 
59 /**
60  * Raise an exception describing a invalid reduce operation.
61  *
62  * @param msg a human readable explanation of the error condition
63  * @param tk_begin_top the first price in the "top-levels"
64  * @param tk_inside the price at the inside
65  * @param px the price of the reduce operation being attempted
66  * @param book_qty the qty that the book has at that price level, 0 if
67  * the level does not exist
68  * @param qty the qty of the reduce operation being attempted
69  *
70  * @throws feed error with a nicely formatted message, **always**.
71  */
72 [[noreturn]] void raise_invalid_reduce(
73  std::string const& msg, std::size_t tk_begin_top, std::size_t tk_inside,
74  price4_t px, int book_qty, int qty);
75 } // namespace detail
76 
77 template <typename compare_t>
79 
80 /**
81  * Define the types of buy and sell side classes.
82  *
83  * It is used as template parameter book_type of the
84  * template classes order_book and compute_book:
85  * - usage: jb::itch5::order_book<jb::itch5::array_based_order_book>
86  */
90  class config;
91 };
92 
93 /**
94  * Configure an array_based_order_book config object
95  */
97 public:
98  config();
100 
101  /// Validate the configuration
102  void validate() const override;
103 
105 };
106 
107 /**
108  * Represent one side of the book.
109  *
110  * This implementation uses a top_levels_ fix sized vector<price4_t> to
111  * keep the prices around the inside. We are hoping this makes a faster
112  * order book side (instead of map based).
113  *
114  * We have observed that most of the changes to an order book happen
115  * in the levels closer to the inside. We are trying to exploit this
116  * observation by using a vector<> for the N levels closer to the inside,
117  * as updates in a vector should be O(1) instead of O(log N) for tree-based
118  * implementations, and O(1) (with a larger constant) for hash-based
119  * implementations.
120  * This introduces some complexity: maintaining all the levels in the vector
121  * would consume too much memory, so we use a vector for the N levels
122  * closer to the inside, and a map for all the other levels.
123  *
124  * @tparam compare_t function object class type to sort the side
125  *
126  */
127 template <typename compare_t>
128 class array_based_book_side {
129 public:
130  /// Constructor, initialize a book side from its configuration
132  : max_size_(cfg.max_size())
133  , top_levels_(cfg.max_size(), 0)
134  , bottom_levels_()
135  , tk_inside_(tk_empty_quote)
136  , tk_begin_top_(tk_inside_)
137  , tk_end_top_(tk_inside_) {
138  }
139 
140  /// @returns the best bid price and quantity.
141  /// The inside is always at the top_levels_
143  if (tk_inside_ == tk_empty_quote) {
145  }
146  auto rel_px = side<compare_t>::level_to_relative(tk_begin_top_, tk_inside_);
147  auto px_inside = level_to_price<price4_t>(tk_inside_);
148  return half_quote(px_inside, top_levels_.at(rel_px));
149  }
150 
151  /// @returns the worst bid price and quantity.
152  /// Check if the side is empty first (return empty_quote in this case)
153  /// The worst price is at the bottom_levels (if not empty),
154  /// at the top_levels otherwise.
156  if (tk_inside_ == tk_empty_quote) {
157  return side<compare_t>::empty_quote(); // empty side
158  }
159  if (not bottom_levels_.empty()) {
160  // ... worst price is at the bottom_levels_
161  auto i = bottom_levels_.rbegin(); // get the worst bottom_levels price
162  auto px_worst = level_to_price<price4_t>(i->first);
163  return half_quote(px_worst, i->second);
164  }
165  // ... worst price at the top_levels_
166  auto rel_worst = relative_worst_top_level();
167  auto tk_worst =
168  side<compare_t>::relative_to_level(tk_begin_top_, rel_worst);
169  auto px_worst = level_to_price<price4_t>(tk_worst);
170  return half_quote(px_worst, top_levels_.at(rel_worst));
171  }
172 
173  /// @returns the number of levels with non-zero quantity for the order side.
174  /// This is the size of bottom_levels_ plus all non zero top_levels values
175  std::size_t count() const {
176  return bottom_levels_.size() + top_levels_count();
177  }
178 
179  /**
180  * Add a price and quantity to the side order book.
181  * - if px is worse than the px_begin_top_ then px goes to bottom_levels
182  * - if px is better than the inside then changes inside, redefines limits
183  * if needed (moving the tail to bottom_levels if any)
184  * - finally, updates qty at the relative(px)
185  *
186  * @param px the price of the new order
187  * @param qty the quantity of the new order
188  * @returns true if the inside changed
189  *
190  * @throw feed_error px out of valid range, or qty <= 0
191  */
192  bool add_order(price4_t px, int qty) {
193  detail::validate_operation_params("add_order", qty, px);
194  // get px price levels
195  auto tk_px = price_levels(price4_t(0), px);
196  // check if tk_px is worse than the first price of the top_levels_
197  if (side<compare_t>::better_level(tk_begin_top_, tk_px)) {
198  // emplace the price at the bottom_levels, and return (false)
199  auto emp_tup = bottom_levels_.emplace(tk_px, 0);
200  emp_tup.first->second += qty;
201  return false;
202  }
203  // check if tk_px is equal or better than the current inside
204  if (not side<compare_t>::better_level(tk_inside_, tk_px)) {
205  // ... check if limit redefine is needed
206  if (not side<compare_t>::better_level(tk_end_top_, tk_px)) {
207  // get the new limits based on the new inside tk_px
208  auto limits = side<compare_t>::limit_top_prices(tk_px, max_size_ / 2);
209  // move the tail [px_begin_top_, new px_begin_top_) to bottom_levels_
210  move_top_to_bottom(std::get<0>(limits));
211  // ... redefine the limits
212  tk_begin_top_ = std::get<0>(limits);
213  tk_end_top_ = std::get<1>(limits);
214  }
215  // if the tk_px inside changed, updated
216  if (tk_inside_ != tk_px) {
217  tk_inside_ = tk_px;
218  }
219  // update top_levels_
220  auto rel_px =
221  side<compare_t>::level_to_relative(tk_begin_top_, tk_inside_);
222  top_levels_[rel_px] += qty;
223  return true; // the inside changed
224  }
225  // is a top_levels change different than the inside
226  // udates top_levels_ with new qty
227  auto rel_px = side<compare_t>::level_to_relative(tk_begin_top_, tk_px);
228  top_levels_[rel_px] += qty;
229  return false;
230  }
231 
232  /**
233  * Reduce the quantity for a given price.
234  *
235  * @param px the price of the order that was reduced
236  * @param qty the quantity reduced in the order
237  * @returns true if the inside changed
238  *
239  * @throw feed_error qty <= 0
240  * @throw feed_error px should be in bottom_levels_ but is empty
241  * @throw feed_error trying to reduce a non-existing bottom_levels price
242  * @throw feed_error trying to reduce a non-existing top_levels price
243  * @throw feed_error trying to reduce a price better than px_inside_
244  *
245  * Checks and handles if px is a bottom_level_ price
246  * Checks if top_levels_ is empty after reducing
247  * If so, limits have to be redefined, and tail moved into top_levels
248  */
249  bool reduce_order(price4_t px, int qty) {
250  detail::validate_operation_params("reduce_order", qty, px);
251  // get px price level
252  auto tk_px = price_levels(price4_t(0), px);
253  // check and handles if it is a bottom_level_ price
254  if (side<compare_t>::better_level(tk_begin_top_, tk_px)) {
255  auto price_it = bottom_levels_.find(tk_px);
256  if (price_it == bottom_levels_.end()) {
258  "array_based_book_side::reduce_order."
259  " Trying to reduce non-existing bottom_levels_price.",
260  tk_begin_top_, tk_inside_, px, 0, qty);
261  }
262  // ... reduce the quantity ...
263  price_it->second -= qty;
264  if (price_it->second < 0) {
265  // ... this is "Not Good[tm]", somehow we missed an order or
266  // processed a delete twice ...
267  JB_LOG(warning) << "negative quantity in order book";
268  }
269  // now we can erase this element if updated qty <=0
270  if (price_it->second <= 0) {
271  bottom_levels_.erase(price_it);
272  }
273  return false;
274  }
275 
276  // handles the top_levels_ price
277  if (side<compare_t>::better_level(tk_px, tk_inside_)) {
279  "array_based_book_side::reduce_order."
280  " Trying to reduce a non-existing top_levels_ price"
281  " (better px_inside).",
282  tk_begin_top_, tk_inside_, px, 0, qty);
283  }
284  // get px relative position
285  auto rel_px = side<compare_t>::level_to_relative(tk_begin_top_, tk_px);
286  if (top_levels_[rel_px] == 0) {
288  "array_based_book_side::reduce_order."
289  " Trying to reduce a non-existing top_levels_ price"
290  " (top_levels_[rel_px] == 0).",
291  tk_begin_top_, tk_inside_, px, 0, qty);
292  }
293 
294  // ... reduce the quantity ...
295  top_levels_[rel_px] -= qty;
296  if (top_levels_[rel_px] < 0) {
297  // ... this is "Not Good[tm]", somehow we missed an order or
298  // processed a delete twice ...
299  JB_LOG(warning) << "negative quantity in order book";
300  top_levels_[rel_px] = 0; // cant't be negative
301  }
302  // ... if it is not the inside we are done
303  if (tk_px != tk_inside_) {
304  return false;
305  }
306  // Is inside, ... now check if it was removed
307  if (top_levels_[rel_px] == 0) {
308  // gets the new inside (if any)
309  tk_inside_ = next_best_price_level();
310  if (tk_inside_ == tk_empty_quote) {
311  // last top_levels_ price was removed...
312  // ... get the new inside from the bottom_levels
313  if (not bottom_levels_.empty()) {
314  tk_inside_ = bottom_levels_.begin()->first;
315  }
316  // redefine limits
317  auto limits =
318  side<compare_t>::limit_top_prices(tk_inside_, max_size_ / 2);
319  tk_begin_top_ = std::get<0>(limits);
320  tk_end_top_ = std::get<1>(limits);
321  // move tail prices from bottom_levels (tk_begin_top_ {, .begin()})
322  move_bottom_to_top();
323  }
324  }
325  return true; // it is an inside change
326  }
327 
328  /**
329  * Testing hook.
330  * @returns true is side is ascending
331  * To test different implementations for buy and sell sides
332  */
333  bool is_ascending() const {
335  }
336 
337  /**
338  * Cache the number of levels for the empty quote.
339  *
340  * This value is used numerous times in the critical path, and we
341  * want to avoid recomputing it over and over.
342  */
343  static std::size_t const tk_empty_quote;
344 
345  /**
346  * Return the number of price levels for the empty quote.
347  *
348  * This is mostly used to initialize tk_empty_quote, because
349  * otherwise the template definitions get too gnarly.
350  */
351  static std::size_t price_levels_empty_quote() {
353  }
354 
355 private:
356  /**
357  * @returns relative position of the worst valid price at top_levels.
358  * @throw feed_error top_levels_ is empty.
359  */
360  std::size_t relative_worst_top_level() const {
361  return std::distance(
362  top_levels_.begin(),
363  std::find_if(top_levels_.begin(), top_levels_.end(), [](auto x) {
364  return x != 0;
365  }));
366  }
367 
368  /// @returns number of valid prices (>0) at top_levels_
369  std::size_t top_levels_count() const {
370  // counts from 0 .. relative position of px_inside_
371  if (tk_inside_ == tk_empty_quote) {
372  return 0; // empty side
373  }
374  auto rel_px = side<compare_t>::level_to_relative(tk_begin_top_, tk_inside_);
375  int result = 0;
376  for (std::size_t i = 0; i != rel_px + 1; ++i) {
377  if (top_levels_[i] != 0) {
378  result++;
379  }
380  }
381  return result;
382  }
383 
384  /**
385  * Move prices from tk_begin_top_ to tk_max (excluded)
386  * out of top_levels_ to bottom_levels_.
387  *
388  * @param tk_max first limit price level that is not moved out.
389  *
390  * Inserts the prices into bottom_levels_
391  * Shift top_levels_ prices in order for rel_max to become relative 0
392  * Clear (value = 0) former relative position of moved prices
393  */
394  void move_top_to_bottom(std::size_t const tk_max) {
395  JB_ASSERT_THROW(not side<compare_t>::better_level(tk_begin_top_, tk_max));
396  // if tk_max is better than tk_inside_
397  if (side<compare_t>::better_level(tk_max, tk_inside_)) {
398  // ... all top_prices_ have to be moved out
399  // valid prices are from 0 .. relative (tk_inside_) included
400  auto rel_inside =
401  side<compare_t>::level_to_relative(tk_begin_top_, tk_inside_);
402  move_top_to_bottom_ranged(rel_inside + 1);
403  return;
404  }
405  // tk_max is worse than tk_inside
406  // prices to move out are from 0.. relative (tk_max) excluded
407  auto rel_tk_max = side<compare_t>::level_to_relative(tk_begin_top_, tk_max);
408  move_top_to_bottom_ranged(rel_tk_max);
409  // shift price down rel_tk_max positions
410  // from tk_max to tk_inside both included...
411  // ...(the are no better prices than tk_inside_)
412  auto rel_tk_inside =
413  side<compare_t>::level_to_relative(tk_begin_top_, tk_inside_);
414  std::size_t j = 0;
415  for (std::size_t i = rel_tk_max; i <= rel_tk_inside; ++i, ++j) {
416  if (top_levels_[i] != 0) {
417  top_levels_[j] = top_levels_[i];
418  top_levels_[i] = 0;
419  }
420  }
421  }
422 
423  /**
424  * Move the bottom N levels from the top vector to the bottom map.
425  *
426  * This function "copies" the bottom N levels from the top vector,
427  * and then zeroes out that portion of the vector ...
428  *
429  * @param N the number of elements to move.
430  */
431  void move_top_to_bottom_ranged(std::size_t N) {
432  // ... this is a private function, we do not need to check the
433  // arguments ...
434  for (std::size_t i = 0; i != N; ++i) {
435  auto qty = top_levels_[i];
436  // ... skip levels that have no qty, we do not want to grow the
437  // map too much ...
438  if (qty == 0) {
439  continue;
440  }
441  auto tk_i = side<compare_t>::relative_to_level(tk_begin_top_, i);
442  // ... this loop is always inserting a better price than what is
443  // on the bottom map (because the by definition top_levels_
444  // contains the best price levels). Also, each price inserted
445  // is better than any previous insertion (because we iterate in
446  // ascending order of better-price). And finally the
447  // bottom_levels_ is reverse sorted, so the best price is at the
448  // beginning. That means we can give the map a hint, indicating
449  // to insert at the beginning, and change this from O(logN) to
450  // O(1) (amortized) ...
451  bottom_levels_.emplace_hint(bottom_levels_.begin(), tk_i, qty);
452  }
453  // ... another loop to fill out with 0, supposedly std::fill() is
454  // optimized for vectors in most C++ implementations (use
455  // memset(3) or something similar) ...
456  std::fill(top_levels_.begin(), top_levels_.begin() + N, 0);
457  }
458 
459  /**
460  * Move relative prices from best price to tk_begin_top* (included)
461  * out of bottom_levels_ to top_levels_.
462  * *tk_begin_top_ is updated before calling this function.
463  */
465  if (bottom_levels_.empty()) {
466  return; // nothing to move in
467  }
468  // traverse all bottom_levels_ prices worse than or equal to tk_begin_top_
469  auto le = bottom_levels_.begin();
470  for (/* */; le != bottom_levels_.end(); ++le) {
471  auto tk_le = le->first;
472  if (not side<compare_t>::better_level(tk_begin_top_, tk_le)) {
473  // move price to top_levels_
474  auto rel_px = side<compare_t>::level_to_relative(tk_begin_top_, tk_le);
475  top_levels_[rel_px] = le->second;
476  } else {
477  // no more good prices to move...
478  break;
479  }
480  }
481  // erase the moved prices..
482  bottom_levels_.erase(bottom_levels_.begin(), le);
483  }
484 
485  /**
486  * @returns the best next price level from the inside.
487  * (return empty quote price if none available).
488  */
489  std::size_t next_best_price_level() const {
490  // begins checking below the current inside
491  auto rel_inside =
492  side<compare_t>::level_to_relative(tk_begin_top_, tk_inside_);
493  if (rel_inside == 0) {
494  return tk_empty_quote;
495  }
496  do {
497  rel_inside--;
498  if (top_levels_[rel_inside] != 0) {
499  // got the next one...
500  return side<compare_t>::relative_to_level(tk_begin_top_, rel_inside);
501  }
502  } while (rel_inside != 0);
503  return tk_empty_quote;
504  }
505 
506  /// @returns pair of price levels that are rel worse
507  /// and better than tk_px, limited to valid price levels
508  /// if tk_px is at the limit, return and empty quote values
509  static auto get_limits(std::size_t const tk_px, std::size_t const rel) {
510  if (tk_px == tk_empty_quote) {
511  return std::make_pair(tk_empty_quote, tk_empty_quote);
512  }
513  auto const level_max = price_levels(price4_t(0), empty_offer_price());
514 
515  auto tk_low = tk_px <= rel ? 0 : tk_px - rel;
516  auto tk_low_base = tk_low + rel >= level_max ? level_max : tk_low + rel;
517  auto tk_high =
518  tk_low_base + rel >= level_max ? level_max : tk_low_base + rel;
519  auto tk_high_base = tk_high <= rel ? 0 : tk_high - rel;
520 
521  if (tk_low_base != tk_high_base) {
522  tk_low = tk_high_base <= rel ? 0 : tk_high_base - rel;
523  }
524  return std::make_pair(tk_low, tk_high);
525  }
526 
527  /// template specialization struct to handle differences between BUY and SELL
528  /// version SELL side.
529  template <typename ordering, class DUMMY = void>
530  struct side {
531  static bool constexpr ascending = false;
532 
533  /// @returns an empty offer.
535  return empty_offer();
536  }
537 
538  /// @returns empty offer price repesentation.
539  static auto empty_quote_price() {
540  return empty_offer_price();
541  }
542 
543  // @returns true if tk1 is less then tk2.
544  static bool better_level(std::size_t const tk1, std::size_t const tk2) {
545  return tk1 < tk2;
546  }
547 
548  // @returns pair a new limits
549  static auto
550  limit_top_prices(std::size_t const tk_px, std::size_t const rel) {
551  auto lim = get_limits(tk_px, rel);
552  return std::make_pair(std::get<1>(lim), std::get<0>(lim));
553  }
554 
555  /// returns@ price level rel positions less than tk_ini
556  static auto
557  relative_to_level(std::size_t const tk_ini, std::size_t const rel) {
558  return tk_ini - rel;
559  }
560 
561  /// @returns relative position of tk_px compare with tk_ini
562  static auto
563  level_to_relative(std::size_t const tk_ini, std::size_t const tk_px) {
564  // check tk_px is in range (less than tk_ini_)
565  JB_ASSERT_THROW(tk_px <= tk_ini);
566  return tk_ini - tk_px;
567  }
568  };
569 
570  /// version BUY side
571  template <class DUMMY>
572  struct side<std::greater<std::size_t>, DUMMY> {
573  static bool constexpr ascending = true;
574 
575  /// @returns an empty bid.
577  return empty_bid();
578  }
579 
580  /// @returns empty bid price representation.
581  static auto empty_quote_price() {
582  return empty_bid_price();
583  }
584 
585  // @returns true if tk1 is greater then tk2.
586  static bool better_level(std::size_t const tk1, std::size_t const tk2) {
587  return tk1 > tk2;
588  }
589 
590  // @returns pair a new limits.
591  static auto
592  limit_top_prices(std::size_t const tk_px, std::size_t const rel) {
593  return get_limits(tk_px, rel);
594  }
595 
596  /// returns@ price level rel positions greater than tk_ini.
597  static auto
598  relative_to_level(std::size_t const tk_ini, std::size_t const rel) {
599  return tk_ini + rel;
600  }
601 
602  /// @returns relative position of tk_px compare with tk_ini.
603  static auto
604  level_to_relative(std::size_t const tk_ini, std::size_t const tk_px) {
605  // check tk_px is in range (better than tk_ini_)
606  JB_ASSERT_THROW(tk_px >= tk_ini);
607  return tk_px - tk_ini;
608  }
609  };
610 
611 private:
612  /// top_levels_ max size
613  std::size_t max_size_;
614 
615  /// the best relative prices and quantity
616  std::vector<int> top_levels_;
617 
618  /// the worst (tail) price level and quantity
619  std::map<std::size_t, int, compare_t> bottom_levels_;
620 
621  /// price level the inside
622  std::size_t tk_inside_;
623 
624  /// worst price level on the top_levels_ range
625  std::size_t tk_begin_top_;
626 
627  /// one price level past-the-best price in top_levels_ range
628  std::size_t tk_end_top_;
629 };
630 
631 template <typename compare_t>
634 
635 } // namespace itch5
636 } // namespace jb
637 
638 #endif // jb_itch5_array_based_order_book_hpp
array_based_book_side(array_based_order_book::config const &cfg)
Constructor, initialize a book side from its configuration.
static bool better_level(std::size_t const tk1, std::size_t const tk2)
static std::size_t const tk_empty_quote
Cache the number of levels for the empty quote.
std::map< std::size_t, int, compare_t > bottom_levels_
the worst (tail) price level and quantity
half_quote empty_bid()
The value used to represent an empty bid.
void raise_invalid_reduce(std::string const &msg, std::size_t tk_begin_top, std::size_t tk_inside, price4_t px, int book_qty, int qty)
Raise an exception describing a invalid reduce operation.
Base class for all configuration objects.
static std::size_t price_levels_empty_quote()
Return the number of price levels for the empty quote.
bool is_ascending() const
Testing hook.
void move_top_to_bottom_ranged(std::size_t N)
Move the bottom N levels from the top vector to the bottom map.
STL namespace.
Define the types of buy and sell side classes.
std::size_t tk_begin_top_
worst price level on the top_levels_ range
static auto relative_to_level(std::size_t const tk_ini, std::size_t const rel)
returns@ price level rel positions greater than tk_ini.
static auto relative_to_level(std::size_t const tk_ini, std::size_t const rel)
returns@ price level rel positions less than tk_ini
void move_bottom_to_top()
Move relative prices from best price to tk_begin_top* (included) out of bottom_levels_ to top_levels_...
static bool better_level(std::size_t const tk1, std::size_t const tk2)
std::size_t max_size_
top_levels_ max size
static auto level_to_relative(std::size_t const tk_ini, std::size_t const tk_px)
Represent one side of the book.
void validate_operation_params(char const *operation, int qty, price4_t px)
Validate the input parameters for all array_order_book operations.
static auto limit_top_prices(std::size_t const tk_px, std::size_t const rel)
void raise_invalid_operation_parameters(char const *operation, int qty, price4_t px)
Raise the right exception when we detect invalid parameters for add_order() or reduce_order() ...
std::pair< price4_t, int > half_quote
A simple representation for price + quantity.
static auto level_to_relative(std::size_t const tk_ini, std::size_t const tk_px)
#define config_object_constructors(NAME)
Helper class to easily define configuration attributes.
jb::config_attribute< config, int > max_size
Configure an array_based_order_book config object.
#define JB_ASSERT_THROW(PRED)
price4_t empty_offer_price()
convenient value to represent an empty offer limit price
bool reduce_order(price4_t px, int qty)
Reduce the quantity for a given price.
static auto limit_top_prices(std::size_t const tk_px, std::size_t const rel)
std::size_t tk_end_top_
one price level past-the-best price in top_levels_ range
half_quote empty_offer()
The value used to represent an empty offer.
template specialization struct to handle differences between BUY and SELL version SELL side...
bool add_order(price4_t px, int qty)
Add a price and quantity to the side order book.
std::vector< int > top_levels_
the best relative prices and quantity
std::size_t tk_inside_
price level the inside
price4_t empty_bid_price()
convenient value to represent an empty bid limit price
std::size_t price_levels(price_field_t lo, price_field_t hi)
Compute the number of price levels between two prices.
price_field< std::uint32_t, 10000 > price4_t
Convenience definition for Price(4) fields.
static auto get_limits(std::size_t const tk_px, std::size_t const rel)
#define JB_LOG(lvl)
Definition: log.hpp:70
void move_top_to_bottom(std::size_t const tk_max)
Move prices from tk_begin_top_ to tk_max (excluded) out of top_levels_ to bottom_levels_.
The top-level namespace for the JayBeams library.
Definition: as_hhmmss.hpp:7