JayBeams  0.1
Another project to have fun coding.
bm_order_book.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  *
4  * This is a benchmark for jb::itch5::array_based_order_book and
5  * jb::itch5::map_based_order_book. It computes a valid stream of
6  * book operations, and runs the operations over the book type
7  * multiple times to measure its performance and variability.
8  *
9  * Some effort is made to make sure the stream of book operations is
10  * statistically similar to the observed behaviour of the ITCH-5.0
11  * feed.
12  *
13  * To make the test reproduceable, the user can pass in the seed to
14  * the PRNGs used to create the stream of book operations.
15  *
16  * The program uses the jb::microbenchmark<> class, taking advantage
17  * of common features such as command-line configurable number of
18  * iterations, scheduling attributes, warmup cycles, etc.
19  */
25 #include <jb/histogram.hpp>
27 #include <jb/log.hpp>
28 
29 #include <functional>
30 #include <stdexcept>
31 #include <type_traits>
32 #include <unordered_map>
33 
34 /**
35  * Define types and functions used in this program.
36  */
37 namespace {
38 /// Configuration parameters for the microbenchmark fixture
39 class fixture_config : public jb::config_object {
40 public:
41  fixture_config();
42  config_object_constructors(fixture_config);
43 
44  void validate() const override;
45 
53 
55 
58 };
59 
60 /// Configuration parameters for bm_order_book
61 class config : public jb::config_object {
62 public:
63  config();
65 
66  void validate() const override;
67 
68  using array_config = typename jb::itch5::array_based_order_book::config;
69  using map_config = typename jb::itch5::map_based_order_book::config;
70 
73  microbenchmark;
78 };
79 
80 /// A simple representation for book operations.
81 struct operation {
82  /// The price to modify
84  /// The quantity, negative values indicate the operation is a
85  /// remove_order(), positive that it is an add_order() operation.
86  int delta;
87 };
88 
89 /**
90  * Initialize a PRNG based on the seed command-line argument.
91  *
92  * If seed is 0 the PRNG is initialized using std::random_device.
93  * That provides a lot of entropy into the PRNG. But sometimes we
94  * want to run the same test over and over, with predictable results,
95  * for example to debug the benchmark, or to fine-tune the operating
96  * system parameters with a consistent benchmark. In that case we can
97  * provide a seed to initialize the generator.
98  *
99  * One should not use the generator initialized with a 32-bit seed for
100  * anything that requires good statistical properties in the generated
101  * numbers.
102  *
103  * @param seed the seed parameter as documented above.
104  * @return a generator initialized with @a seed if seed is not zero,
105  * and initialized with the output from std::random_device if it is
106  * zero.
107  */
108 std::mt19937_64 initialize_generator(int seed) {
109  return jb::testing::initialize_mersenne_twister<std::mt19937_64>(
111 }
112 
113 /**
114  * Create a sequence of operations.
115  *
116  * @param seed a seed for the PRNG, or 0 if the generator should be
117  * initialized from std::random_device.
118  * @param size the number of elements in the sequence
119  * @param cfg describe the statistical properties of the sequence
120  * @param is_ascending if true generate operations for a BUY book.
121  */
122 std::vector<operation> create_operations(
123  std::mt19937_64& generator, int size, fixture_config const& cfg,
124  bool is_ascending);
125 
126 template <typename order_book_side, typename book_config>
127 std::function<void()> create_iteration(
128  std::mt19937_64& generator, int size, fixture_config const& cfg,
129  book_config const& bkcfg) {
130  order_book_side bk(bkcfg);
131  std::vector<operation> ops =
132  create_operations(generator, size, cfg, bk.is_ascending());
133 
134  auto lambda =
135  [ book = std::move(bk), operations = std::move(ops) ]() mutable {
136  // ... iterate over the operations and pass them to the book ...
137  for (auto const& op : operations) {
138  if (op.delta < 0) {
139  book.reduce_order(op.px, -op.delta);
140  } else {
141  book.add_order(op.px, op.delta);
142  }
143  }
144  };
145 
146  return std::function<void()>(std::move(lambda));
147 }
148 
149 /// The default number of iterations for the benchmark
150 static int constexpr default_size = 20000;
151 
152 /// Run the benchmark for a specific book type
153 template <typename order_book, typename book_config>
154 class fixture {
155 public:
156  /// Default constructor, delegate on the constructor given the
157  /// number of iterations
158  fixture(fixture_config const& cfg, book_config const& bkcfg, int seed)
159  : fixture(default_size, cfg, bkcfg, seed) {
160  }
161 
162  /**
163  * Construct a new test fixture.
164  *
165  * @param size the number of events (order add / mod / delete) to
166  * simulate.
167  */
168  fixture(
169  int size, fixture_config const& cfg, book_config const& bkcfg,
170  unsigned int seed)
171  : size_(size)
172  , cfg_(cfg)
173  , bkcfg_(bkcfg)
174  , generator_(initialize_generator(seed)) {
175  }
176 
177  void iteration_setup() {
178  std::uniform_int_distribution<> side(0, 1);
179  if (side(generator_)) {
180  iteration_ = create_iteration<typename order_book::buys_t>(
181  generator_, size_, cfg_, bkcfg_);
182  } else {
183  iteration_ = create_iteration<typename order_book::sells_t>(
184  generator_, size_, cfg_, bkcfg_);
185  }
186  }
187 
188  /// Run an iteration of the test ...
189  int run() {
190  iteration_();
191  return size_;
192  }
193 
194 private:
195  /// The size for the test
196  int size_;
197 
198  /// The configuration for this fixture
199  fixture_config cfg_;
200 
201  /// The configuration for the book side
202  book_config bkcfg_;
203 
204  /// The PRNG, initialized based on the seed parameter
205  std::mt19937_64 generator_;
206 
207  /// Store the state and functions to execute in the next iteration
208  std::function<void()> iteration_;
209 };
210 
211 /**
212  * Run the benchmark for a specific book type
213  *
214  * @param cfg the configuration for the benchmark
215  * @param book_cfg the configuration for the specific book type
216  *
217  * @tparam book_type the type of book to use in the benchmark
218  * @tparam book_type_config the configuration class for @a book_type
219  */
220 template <typename book_type, typename book_type_config>
221 void run_benchmark(config const& cfg, book_type_config const& book_cfg) {
222  JB_LOG(info) << "Running benchmark for " << cfg.microbenchmark().test_case()
223  << " with SEED=" << cfg.seed();
224  using benchmark =
226  benchmark bm(cfg.microbenchmark());
227  auto r = bm.run(cfg.fixture(), book_cfg, cfg.seed());
228 
229  typename benchmark::summary s(r);
230  // ... print the summary and full results to std::cout, without
231  // using JB_LOG() because this is parsed by the driver script ...
232  std::cerr << cfg.microbenchmark().test_case() << " summary " << s
233  << std::endl;
234  if (cfg.microbenchmark().verbose()) {
235  bm.write_results(std::cout, r);
236  }
237 }
238 } // anonymous namespace
239 
240 int main(int argc, char* argv[]) try {
241  config cfg;
242  cfg.process_cmdline(argc, argv);
243 
244  // initialize the logging framework ...
245  jb::log::init(cfg.log());
246  if (cfg.microbenchmark().verbose()) {
247  JB_LOG(info) << "Configuration for test\n" << cfg << "\n";
248  JB_LOG(info) << " default_size=" << default_size;
249  }
250 
251  using namespace jb::itch5;
252  auto test_case = cfg.microbenchmark().test_case();
253  if (test_case == "array") {
254  run_benchmark<array_based_order_book>(cfg, cfg.array_book());
255  } else if (test_case == "map") {
256  run_benchmark<map_based_order_book>(cfg, cfg.map_book());
257  } else {
258  // ... it is tempting to move this code to the validate() member
259  // function, but then we have to repeat the valid configurations
260  // twice in the code ...
261  std::ostringstream os;
262  os << "Unknown test case (" << test_case << ")" << std::endl;
263  os << " --microbenchmark.test-case must be one of"
264  << ": array, map" << std::endl;
265  throw jb::usage(os.str(), 1);
266  }
267 
268  return 0;
269 } catch (jb::usage const& ex) {
270  std::cerr << "usage: " << ex.what() << std::endl;
271  return ex.exit_status();
272 } catch (std::exception const& ex) {
273  std::cerr << "standard exception raised: " << ex.what() << std::endl;
274  return 1;
275 } catch (...) {
276  std::cerr << "unknown exception raised" << std::endl;
277  return 1;
278 }
279 
280 namespace {
281 namespace defaults {
282 #ifndef JB_ITCH5_DEFAULT_bm_order_book_test_case
283 #define JB_ITCH5_DEFAULT_bm_order_book_test_case "array"
284 #endif // JB_ITCH5_DEFAULT_bm_order_book_test_case
285 
286 #ifndef JB_ITCH5_DEFAULT_bm_order_book_p25
287 #define JB_ITCH5_DEFAULT_bm_order_book_p25 0
288 #endif // JB_ITCH5_DEFAULT_bm_order_book_p25
289 
290 #ifndef JB_ITCH5_DEFAULT_bm_order_book_p50
291 #define JB_ITCH5_DEFAULT_bm_order_book_p50 1
292 #endif // JB_ITCH5_DEFAULT_bm_order_book_p50
293 
294 #ifndef JB_ITCH5_DEFAULT_bm_order_book_p75
295 #define JB_ITCH5_DEFAULT_bm_order_book_p75 6
296 #endif // JB_ITCH5_DEFAULT_bm_order_book_p75
297 
298 #ifndef JB_ITCH5_DEFAULT_bm_order_book_p90
299 #define JB_ITCH5_DEFAULT_bm_order_book_p90 14
300 #endif // JB_ITCH5_DEFAULT_bm_order_book_p90
301 
302 #ifndef JB_ITCH5_DEFAULT_bm_order_book_p99
303 #define JB_ITCH5_DEFAULT_bm_order_book_p99 203
304 #endif // JB_ITCH5_DEFAULT_bm_order_book_p99
305 
306 #ifndef JB_ITCH5_DEFAULT_bm_order_book_p999
307 #define JB_ITCH5_DEFAULT_bm_order_book_p999 2135
308 #endif // JB_ITCH5_DEFAULT_bm_order_book_p999
309 
310 #ifndef JB_ITCH5_DEFAULT_bm_order_book_p100
311 #define JB_ITCH5_DEFAULT_bm_order_book_p100 20000000
312 #endif // JB_ITCH5_DEFAULT_bm_order_book_p100
313 
314 #ifndef JB_ITCH5_DEFAULT_bm_order_book_max_p999_delta
315 #define JB_ITCH5_DEFAULT_bm_order_book_max_p999_delta 200
316 #endif // JB_ITCH5_DEFAULT_bm_order_book_max_p999_delta
317 
318 #ifndef JB_ITCH5_DEFAULT_bm_order_book_min_qty
319 #define JB_ITCH5_DEFAULT_bm_order_book_min_qty -5000
320 #endif // JB_ITCH5_DEFAULT_bm_order_book_min_qty
321 
322 #ifndef JB_ITCH5_DEFAULT_bm_order_book_max_qty
323 #define JB_ITCH5_DEFAULT_bm_order_book_max_qty 5000
324 #endif // JB_ITCH5_DEFAULT_bm_order_book_max_qty
325 
326 std::string const test_case = JB_ITCH5_DEFAULT_bm_order_book_test_case;
327 int constexpr p25 = JB_ITCH5_DEFAULT_bm_order_book_p25;
328 int constexpr p50 = JB_ITCH5_DEFAULT_bm_order_book_p50;
329 int constexpr p75 = JB_ITCH5_DEFAULT_bm_order_book_p75;
330 int constexpr p90 = JB_ITCH5_DEFAULT_bm_order_book_p90;
331 int constexpr p99 = JB_ITCH5_DEFAULT_bm_order_book_p99;
332 int constexpr p999 = JB_ITCH5_DEFAULT_bm_order_book_p999;
333 int constexpr p100 = JB_ITCH5_DEFAULT_bm_order_book_p100;
334 
335 int constexpr max_p999_delta = JB_ITCH5_DEFAULT_bm_order_book_max_p999_delta;
336 
337 int constexpr min_qty = JB_ITCH5_DEFAULT_bm_order_book_min_qty;
338 int constexpr max_qty = JB_ITCH5_DEFAULT_bm_order_book_max_qty;
339 } // namespace defaults
340 
341 fixture_config::fixture_config()
342  : p25(desc("p25").help(
343  "Define the maximum depth of 25% of the events. "
344  "The benchmark generates random book changes, with the depth of "
345  "these changes controled by this argument (and similar ones), "
346  "the default values are chosen to match the observed behavior "
347  "in real market feeds."),
348  this, defaults::p25)
349  , p50(desc("p50").help(
350  "Define the maximum depth of 50% of the events. "
351  "The benchmark generates random book changes, with the depth of "
352  "these changes controled by this argument (and similar ones), "
353  "the default values are chosen to match the observed behavior "
354  "in real market feeds."),
355  this, defaults::p50)
356  , p75(desc("p75").help(
357  "Define the maximum depth of 2\75% of the events. "
358  "The benchmark generates random book changes, with the depth of "
359  "these changes controled by this argument (and similar ones), "
360  "the default values are chosen to match the observed behavior "
361  "in real market feeds."),
362  this, defaults::p75)
363  , p90(desc("p90").help(
364  "Define the maximum depth of 90% of the events. "
365  "The benchmark generates random book changes, with the depth of "
366  "these changes controled by this argument (and similar ones), "
367  "the default values are chosen to match the observed behavior "
368  "in real market feeds."),
369  this, defaults::p90)
370  , p99(desc("p99").help(
371  "Define the maximum depth of 99% of the events. "
372  "The benchmark generates random book changes, with the depth of "
373  "these changes controled by this argument (and similar ones), "
374  "the default values are chosen to match the observed behavior "
375  "in real market feeds."),
376  this, defaults::p99)
377  , p999(
378  desc("p999").help(
379  "Define the maximum depth of 99.9% of the events. "
380  "The benchmark generates random book changes, with the depth of "
381  "these changes controled by this argument (and similar ones), "
382  "the default values are chosen to match the observed behavior "
383  "in real market feeds."),
384  this, defaults::p999)
385  , p100(
386  desc("p100").help(
387  "Define the maximum depth of 100% of the events. "
388  "The benchmark generates random book changes, with the depth of "
389  "these changes controled by this argument (and similar ones), "
390  "the default values are chosen to match the observed behavior "
391  "in real market feeds."),
392  this, defaults::p100)
393  , max_p999_delta(
394  desc("max-p999-delta")
395  .help(
396  "The maximum delta for the desired vs. actual event depth "
397  "distribution at the p99.9 level. "
398  "The benchmark generates random book changes, with the "
399  "distribution of the event depths controlled by the "
400  "--pXYZ arguments. "
401  "Any generated set of book changes whose p99.9 differs by "
402  "more than this value from the requested value is rejected, "
403  "and a new set of book changes is generated."),
404  this, defaults::max_p999_delta)
405  , min_qty(
406  desc("min-qty").help(
407  "Generate book changes uniformly distributed between "
408  "--min-qty and --max-qty (both inclusive."),
409  this, defaults::min_qty)
410  , max_qty(
411  desc("max-qty").help(
412  "Generate book changes uniformly distributed between "
413  "--min-qty and --max-qty (both inclusive."),
414  this, defaults::max_qty) {
415 }
416 
417 void fixture_config::validate() const {
418  if (p25() < 0 or p25() > p50()) {
419  std::ostringstream os;
420  os << "p25 (" << p25() << ") must be >= 0 and > p50 (" << p50() << ")";
421  throw jb::usage(os.str(), 1);
422  }
423 
424  if (p50() < 0 or p50() > p75()) {
425  std::ostringstream os;
426  os << "p50 (" << p50() << ") must be >= 0 and > p75 (" << p75() << ")";
427  throw jb::usage(os.str(), 1);
428  }
429 
430  if (p75() < 0 or p75() > p90()) {
431  std::ostringstream os;
432  os << "p75 (" << p75() << ") must be >= 0 and > p90 (" << p90() << ")";
433  throw jb::usage(os.str(), 1);
434  }
435 
436  if (p90() < 0 or p90() > p99()) {
437  std::ostringstream os;
438  os << "p90 (" << p90() << ") must be >= 0 and > p99 (" << p99() << ")";
439  throw jb::usage(os.str(), 1);
440  }
441 
442  if (p99() < 0 or p99() > p999()) {
443  std::ostringstream os;
444  os << "p99 (" << p99() << ") must be >= 0 and > p999 (" << p999() << ")";
445  throw jb::usage(os.str(), 1);
446  }
447 
448  if (p999() < 0 or p999() > p100()) {
449  std::ostringstream os;
450  os << "p999 (" << p999() << ") must be >= 0 and > p100 (" << p100() << ")";
451  throw jb::usage(os.str(), 1);
452  }
453 
454  if (p100() < 0) {
455  std::ostringstream os;
456  os << "p1000 (" << p100() << ") must be >= 0";
457  throw jb::usage(os.str(), 1);
458  }
459 
460  if (max_p999_delta() < 0 or max_p999_delta() > p999() / 2) {
461  std::ostringstream os;
462  os << "max-p999-delta (" << max_p999_delta() << ") must be > 0"
463  << " and must be <= p999/2 (" << p999() / 2 << ")";
464  throw jb::usage(os.str(), 1);
465  }
466 
467  if (min_qty() >= 0) {
468  std::ostringstream os;
469  os << "min-qty (" << min_qty() << ") must be < 0";
470  throw jb::usage(os.str(), 1);
471  }
472 
473  if (max_qty() <= 0) {
474  std::ostringstream os;
475  os << "max-qty (" << max_qty() << ") must be > 0";
476  throw jb::usage(os.str(), 1);
477  }
478 }
479 
480 config::config()
481  : log(desc("log", "logging"), this)
482  , microbenchmark(
483  desc("microbenchmark", "microbenchmark"), this,
484  jb::testing::microbenchmark_config().test_case(defaults::test_case))
485  , array_book(desc("array-book"), this)
486  , map_book(desc("map-book"), this)
487  , fixture(desc("fixture"), this)
488  , seed(
489  desc("seed").help(
490  "Initial seed for pseudo-random number generator. "
491  "If zero (the default), use the systems random device to set "
492  "the seed."),
493  this, 0) {
494 }
495 
496 void config::validate() const {
497  log().validate();
498  microbenchmark().validate();
499  array_book().validate();
500  map_book().validate();
501  fixture().validate();
502 }
503 
504 std::vector<operation> create_operations_without_validation(
505  std::mt19937_64& generator, int& actual_p999, int size,
506  fixture_config const& cfg, bool is_ascending) {
507 
508  // ... we will save the operations here, and return them at the end ...
509  std::vector<operation> operations;
510  operations.reserve(size);
511 
512  // ... use the configuration parameters to create a "depth
513  // distribution". The default values are chosen from
514  // realistic market data, so the benchmark should generate data
515  // that is statistically similar to what the market shows ...
516  std::vector<int> boundaries({0, cfg.p25(), cfg.p50(), cfg.p75(), cfg.p90(),
517  cfg.p99(), cfg.p999(), cfg.p100()});
518  std::vector<double> weights({0.25, 0.25, 0.25, 0.15, 0.09, 0.009, 0.001});
519  JB_ASSERT_THROW(boundaries.size() == weights.size() + 1);
520  std::piecewise_constant_distribution<> ddis(
521  boundaries.begin(), boundaries.end(), weights.begin());
522 
523  // ... save the maximum possible price level ...
524  using jb::itch5::price4_t;
525  int const max_level = jb::itch5::price_levels(
526  price4_t(0), jb::itch5::max_price_field_value<price4_t>());
527 
528  // ... we need a function to convert price levels to prices, the
529  // function depends on whether the book is ascending by price
530  // (BUY) or descending by price (SELL). Writing this function in
531  // terms of price levels and then converting to prices makes the
532  // code easier to read ...
533  std::function<price4_t(int)> level2price;
534  if (is_ascending) {
535  level2price = [](int level) {
536  return jb::itch5::level_to_price<price4_t>(level);
537  };
538  } else {
539  level2price = [max_level](int level) {
540  return jb::itch5::level_to_price<price4_t>(max_level - level);
541  };
542  }
543 
544  // ... we keep a histogram of the intended depths so we can verify
545  // that we are generating a valid simulation ...
546  jb::histogram<jb::integer_range_binning<int>> book_depth_histogram(
549  jb::integer_range_binning<int>(cfg.min_qty(), cfg.max_qty()));
550 
551  // ... keep track of the contents in a simulated book, indexed by
552  // price level ...
553  std::map<int, int> book;
554 
555  for (int i = 0; i != size; ++i) {
556  if (book.empty()) {
557  // ... generate the initial order randomly, as long as it has a
558  // legal price level and a qty in the configured range ...
559  auto const initial_qty =
560  std::uniform_int_distribution<>(1, cfg.max_qty())(generator);
561  auto const initial_level =
562  std::uniform_int_distribution<>(1, max_level - 1)(generator);
563  operations.push_back({level2price(initial_level), initial_qty});
564  book[initial_level] = initial_qty;
565  book_depth_histogram.sample(0);
566  qty_histogram.sample(initial_qty);
567  continue;
568  }
569  // ... find out what is the current best level, or use
570  // initial_level if it is not available ...
571  int best_level = book.rbegin()->first;
572  // ... generate a new level to operate at ...
573  int depth = static_cast<int>(ddis(generator));
574  int level;
575  // ... pick if the operation will be above or below the current
576  // level ...
577  if (std::uniform_int_distribution<>(0, 1)(generator)) {
578  level = best_level - depth;
579  } else {
580  level = best_level + depth;
581  }
582  // ... normalize the level to the valid range ...
583  if (level <= 0) {
584  level = 1;
585  } else if (level >= max_level) {
586  level = max_level - 1;
587  }
588 
589  // ... generate the qty, but make sure it is valid, first
590  // establish the minimum value we can generate ...
591  int min_qty;
592  auto f = book.find(level);
593  if (f != book.end()) {
594  min_qty = std::max(cfg.min_qty(), -f->second);
595  } else {
596  // ... empty level, the operation must add something ...
597  min_qty = 1;
598  }
599  // ... then keep generating values until we are in range, oh,
600  // and zero is not a valid value ...
601  int qty = 0;
602  while (qty == 0) {
603  // ... for the size, we use a uniform distribution in the
604  // [cfg.min_qty(), cfg.max_qty()] range, though we have to trim
605  // the results based on what is actually in the book ...
606  qty = std::uniform_int_distribution<>(min_qty, cfg.max_qty())(generator);
607  }
608  // ... now insert the operation into the array, and record it in
609  // the book ...
610  book[level] += qty;
611  book_depth_histogram.sample(depth);
612  qty_histogram.sample(qty);
613  if (book[level] == 0) {
614  book.erase(level);
615  }
616  operations.push_back({level2price(level), qty});
617  }
618 
619  JB_LOG(trace) << "Simulated depth histogram: "
620  << book_depth_histogram.summary();
621  JB_LOG(trace) << "Desired: " << jb::histogram_summary{0.0,
622  double(cfg.p25()),
623  double(cfg.p50()),
624  double(cfg.p75()),
625  double(cfg.p90()),
626  double(cfg.p99()),
627  double(cfg.p100()),
628  1};
629  JB_LOG(trace) << "Simulated qty histogram: " << qty_histogram.summary();
630 
631  actual_p999 = book_depth_histogram.estimated_quantile(0.999);
632  return operations;
633 }
634 
635 std::vector<operation> create_operations(
636  std::mt19937_64& generator, int size, fixture_config const& cfg,
637  bool is_ascending) {
638 
639  int const min_p999 = cfg.p999() - cfg.max_p999_delta();
640  int const max_p999 = cfg.p999() + cfg.max_p999_delta();
641  int actual_p999 = 0;
642  std::vector<operation> operations;
643  int count = 0;
644  do {
645  if (actual_p999 != 0) {
646  JB_LOG(trace) << "retrying for p999 = " << actual_p999
647  << ", count=" << ++count;
648  }
649  operations = create_operations_without_validation(
650  generator, actual_p999, size, cfg, is_ascending);
651  } while (actual_p999 < min_p999 or max_p999 < actual_p999);
652 
653  return operations;
654 }
655 
656 } // anonymous namespace
void sample(sample_type const &t)
Record a new sample.
Definition: histogram.hpp:197
Define defaults for program parameters.
Contains classes and functions to parse NASDAQ ITCH-5.0 messages, more information about ITCH-5...
#define JB_ITCH5_DEFAULT_bm_order_book_max_p999_delta
#define JB_ITCH5_DEFAULT_bm_order_book_p25
Base class for all configuration objects.
virtual void validate() const
Validate the settings.
int main(int argc, char *argv[])
int exit_status() const
Definition: usage.hpp:21
A histogram class with controllable binning and range strategy.
Definition: histogram.hpp:46
A simple class to capture summary information about a histogram.
#define JB_ITCH5_DEFAULT_bm_order_book_p999
#define JB_ITCH5_DEFAULT_bm_order_book_p50
#define JB_ITCH5_DEFAULT_bm_order_book_test_case
results run(Args &&... args)
Run the microbenchmaark.
#define JB_ITCH5_DEFAULT_bm_order_book_min_qty
sample_type estimated_quantile(double q) const
Estimate a quantile of the sample distribution.
Definition: histogram.hpp:136
A histogram binning_strategy for integer numbers in a known range.
void init(config const &cfg)
Initialize the logging functions using the configuration provided.
Definition: log.cpp:190
#define JB_ITCH5_DEFAULT_bm_order_book_max_qty
#define config_object_constructors(NAME)
Helper class to easily define configuration attributes.
Configure an array_based_order_book config object.
#define JB_ASSERT_THROW(PRED)
char const default_initialization_marker[]
histogram_summary summary() const
Return a simple summary.
Definition: histogram.hpp:185
A simple class to communicate the result of parsing the options.
Definition: usage.hpp:11
#define JB_ITCH5_DEFAULT_bm_order_book_p100
#define JB_ITCH5_DEFAULT_bm_order_book_p90
Configure an map_based_order_book config object.
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.
Run a micro-benchmark on a given class.
#define JB_LOG(lvl)
Definition: log.hpp:70
#define JB_ITCH5_DEFAULT_bm_order_book_p99
#define JB_ITCH5_DEFAULT_bm_order_book_p75