JayBeams  0.1
Another project to have fun coding.
ut_compute_book.cpp
Go to the documentation of this file.
5 #include <jb/as_hhmmss.hpp>
6 
7 #include <jb/gmock/init.hpp>
8 #include <boost/test/unit_test.hpp>
9 #include <algorithm>
10 #include <thread>
11 
12 namespace jb {
13 namespace itch5 {
14 namespace testing {
15 buy_sell_indicator_t const BUY(u'B');
16 buy_sell_indicator_t const SELL(u'S');
17 
20  exec, void(
21  book_update update, half_quote best_bid, half_quote best_offer,
22  int buy_count, int offer_count));
23 };
24 
25 /**
26  * Test compute book based on book type.
27  * @tparam based_order_book type used by compute_order and order_book
28  */
29 template <typename based_order_book>
31  using book_type = order_book<based_order_book>;
32  using compute_type = compute_book<based_order_book>;
33  ::testing::StrictMock<mock_book_callback> callback;
34 
35  auto cb = [&callback](
36  jb::itch5::message_header const&, book_type const& b,
37  book_update const& update) {
38  callback.exec(
39  update, b.best_bid(), b.best_offer(), b.buy_count(), b.sell_count());
40  };
41 
42  typename based_order_book::config cfg;
43  compute_type tested(cb, cfg);
44 
45  stock_t const stock("HSART");
46  price4_t const p10(100000);
47  price4_t const p11(110000);
48  price4_t const p09(90000);
49  long msgcnt = 0;
50  std::uint64_t id = 2;
51  time_point now = tested.now();
52 
53  // ... add an initial order to the book, we expect a single update,
54  // and the order should be in the book when the update is called ...
55  EXPECT_CALL(
56  callback, exec(
57  book_update{now, stock, BUY, p10, 100},
58  half_quote{p10, 100}, empty_offer(), 1, 0))
59  .Times(1);
60  tested.handle_message(
61  now, ++msgcnt, 0,
63  timestamp{std::chrono::nanoseconds(0)}},
64  ++id,
65  BUY,
66  100,
67  stock,
68  p10});
69  // ... we expect the new order to implicitly add the symbol ...
70  auto symbols = tested.symbols();
71  BOOST_REQUIRE_EQUAL(symbols.size(), std::size_t(1));
72  BOOST_CHECK_EQUAL(symbols[0], stock);
73 
74  // ... add an order at a better price, we also expect a single
75  // update, and the order should be in the book when the update is
76  // called ...
77  now = tested.now();
78  EXPECT_CALL(
79  callback, exec(
80  book_update{now, stock, BUY, p11, 200},
81  half_quote{p11, 200}, empty_offer(), 2, 0))
82  .Times(1);
83  tested.handle_message(
84  now, ++msgcnt, 0,
86  timestamp{std::chrono::nanoseconds(0)}},
87  ++id,
88  BUY,
89  200,
90  stock,
91  p11});
92 
93  // ... add an order at a worse price, we also expect a single update, and the
94  // order should be in the book when the update is called ...
95  now = tested.now();
96  EXPECT_CALL(
97  callback, exec(
98  book_update{now, stock, BUY, p09, 300},
99  half_quote{p11, 200}, empty_offer(), 3, 0))
100  .Times(1);
101  tested.handle_message(
102  now, ++msgcnt, 0,
104  timestamp{std::chrono::nanoseconds(0)}},
105  ++id,
106  BUY,
107  300,
108  stock,
109  p09});
110 }
111 
112 /**
113  * Test compute book based on book type.
114  * @tparam based_order_book type used by compute_book and order_book
115  */
116 template <typename based_order_book>
118  using book_type = order_book<based_order_book>;
119  using compute_type = compute_book<based_order_book>;
120 
121  ::testing::StrictMock<mock_book_callback> callback;
122  auto cb = [&callback](
123  jb::itch5::message_header const&, book_type const& b,
124  book_update const& update) {
125  callback.exec(
126  update, b.best_bid(), b.best_offer(), b.buy_count(), b.sell_count());
127  };
128 
129  typename based_order_book::config cfg;
130  book_type book(cfg);
131  compute_type tested(cb, cfg);
132 
133  stock_t const stock("HSART");
134  price4_t const p10(100000);
135  price4_t const p11(110000);
136  price4_t const p12(120000);
137  long msgcnt = 0;
138  std::uint64_t id = 2;
139  time_point now = tested.now();
140 
141  // ... add an initial order to the book, we expect a single update,
142  // and the order should be in the book when the update is called ...
143  EXPECT_CALL(
144  callback, exec(
145  book_update{now, stock, SELL, p11, 100}, empty_bid(),
146  half_quote{p11, 100}, 0, 1))
147  .Times(1);
148  tested.handle_message(
149  now, ++msgcnt, 0,
151  timestamp{std::chrono::nanoseconds(0)}},
152  ++id,
153  SELL,
154  100,
155  stock,
156  p11});
157  // ... we expect the new order to implicitly add the symbol ...
158  auto symbols = tested.symbols();
159  BOOST_REQUIRE_EQUAL(symbols.size(), std::size_t(1));
160  BOOST_CHECK_EQUAL(symbols[0], stock);
161 
162  // ... add an order at a better price, we expect a single update,
163  // and the order should be in the book when the update is called ...
164  now = tested.now();
165  EXPECT_CALL(
166  callback, exec(
167  book_update{now, stock, SELL, p10, 200}, empty_bid(),
168  half_quote{p10, 200}, 0, 2))
169  .Times(1);
170  tested.handle_message(
171  now, ++msgcnt, 0,
173  timestamp{std::chrono::nanoseconds(0)}},
174  ++id,
175  SELL,
176  200,
177  stock,
178  p10});
179 
180  // ... add an order at a worse price, we also expect a single
181  // update, and the order should be in the book when the update is
182  // called ...
183  now = tested.now();
184  EXPECT_CALL(
185  callback, exec(
186  book_update{now, stock, SELL, p12, 300}, empty_bid(),
187  half_quote{p10, 200}, 0, 3))
188  .Times(1);
189  tested.handle_message(
190  now, ++msgcnt, 0,
192  timestamp{std::chrono::nanoseconds(0)}},
193  ++id,
194  SELL,
195  300,
196  stock,
197  p12});
198 }
199 
200 /**
201  * Test compute book increase coverage.
202  * @tparam based_order_book type used by compute book and order book
203  */
204 template <typename based_order_book>
206  using book_type = order_book<based_order_book>;
207  using compute_type = compute_book<based_order_book>;
208 
209  auto cb =
210  [](jb::itch5::message_header const&, book_type const& b,
211  book_update const& update) {};
212  typename compute_type::callback_type const tmp(cb);
213  typename based_order_book::config cfg;
214  book_type book(cfg);
215  compute_type tested(tmp, cfg);
216 
217  auto symbols = tested.symbols();
218  BOOST_REQUIRE_EQUAL(symbols.size(), std::size_t(0));
219 
220  // ... verify compute_book can ignore unknown messages ...
221  time_point now = tested.now();
223  u'a', timestamp{std::chrono::nanoseconds(10)}, 128);
224  tested.handle_unknown(
225  now, unknown_message{std::uint32_t(123), std::size_t(1000000), buf.size(),
226  &buf[0]});
227 
228  stock_t const stock("HSART");
229  price4_t const p10(100000);
230  long msgcnt = 100;
231  std::uint64_t id = 2;
232 
233  // ... verify compute_book can ignore messages irrelevant for book
234  // building ...
235  tested.handle_message(
236  now, ++msgcnt, 0, trade_message{{trade_message::message_type, 0, 0,
237  timestamp{std::chrono::nanoseconds(0)}},
238  ++id,
239  BUY,
240  100,
241  stock,
242  p10,
243  ++id});
244 }
245 
246 /**
247  * Test compute book edge cases.
248  * @tparam based_order_book Type used by compute_book and order_book
249  */
250 template <typename based_order_book>
252  using book_type = order_book<based_order_book>;
253  using compute_type = compute_book<based_order_book>;
254 
255  using namespace ::testing;
256  ::testing::StrictMock<mock_book_callback> callback;
257  auto cb = [&callback](
258  jb::itch5::message_header const&, book_type const& b,
259  book_update const& update) {
260  callback.exec(
261  update, b.best_bid(), b.best_offer(), b.buy_count(), b.sell_count());
262  };
263 
264  // ... create the unit under test ...
265  typename based_order_book::config cfg;
266  book_type book(cfg);
267  compute_type tested(cb, cfg);
268 
269  // ... and a number of helper constants and variables to drive the
270  // test ...
271 
272  stock_t const stock("HSART");
273  price4_t const p10(100000);
274  long msgcnt = 0;
275  std::uint64_t id = 2;
276 
277  // ... add an initial order to the book ...
278  time_point now = tested.now();
279  using namespace ::testing;
280  EXPECT_CALL(callback, exec(_, _, _, _, _)).Times(1);
281  tested.handle_message(
282  now, ++msgcnt, 0,
284  timestamp{std::chrono::nanoseconds(0)}},
285  ++id,
286  BUY,
287  100,
288  stock,
289  p10});
290 
291  // ... try to send the same order, no changes in expectations ...
292  tested.handle_message(
293  now, ++msgcnt, 0,
295  timestamp{std::chrono::nanoseconds(0)}},
296  id,
297  BUY,
298  100,
299  stock,
300  p10});
301 }
302 
303 /**
304  * Test compute book reduction edge cases.
305  * @tparam based_order_book Type used by compute_book and order_book
306  */
307 template <typename based_order_book>
309  using book_type = order_book<based_order_book>;
310  using compute_type = compute_book<based_order_book>;
311 
312  using namespace ::testing;
313  ::testing::StrictMock<mock_book_callback> callback;
314  auto cb = [&callback](
315  jb::itch5::message_header const&, book_type const& b,
316  book_update const& update) {
317  callback.exec(
318  update, b.best_bid(), b.best_offer(), b.buy_count(), b.sell_count());
319  };
320 
321  // ... create the unit under test ...
322  typename based_order_book::config cfg;
323  book_type book(cfg);
324  compute_type tested(cb, cfg);
325 
326  // ... and a number of helper constants and variables to drive the
327  // test ...
328  stock_t const stock("HSART");
329  price4_t const p10(100000);
330  long msgcnt = 0;
331  std::uint64_t id = 2;
332  auto const id_buy = ++id;
333 
334  // ... add an initial order to the book, we expect a callback from
335  // that event ...
336  using namespace ::testing;
337  EXPECT_CALL(callback, exec(_, _, _, _, _)).Times(1);
338  time_point now = tested.now();
339  tested.handle_message(
340  now, ++msgcnt, 0,
342  timestamp{std::chrono::nanoseconds(0)}},
343  id_buy,
344  BUY,
345  100,
346  stock,
347  p10});
348 
349  // ... try to cancel a different order, we expect that no further
350  // callbacks are created ...
351  now = tested.now();
352  tested.handle_message(
353  now, ++msgcnt, 0,
355  timestamp{std::chrono::nanoseconds(0)}},
356  std::uint64_t(1),
357  100});
358 
359  // ... fully cancel the order, we expect a new callback ...
360  now = tested.now();
361  EXPECT_CALL(callback, exec(_, _, _, _, _)).Times(1);
362  tested.handle_message(
363  now, ++msgcnt, 0,
365  timestamp{std::chrono::nanoseconds(0)}},
366  id_buy});
367 
368  // ... fully cancel the order a second time should produce no action ...
369  now = tested.now();
370  tested.handle_message(
371  now, ++msgcnt, 0,
373  timestamp{std::chrono::nanoseconds(0)}},
374  id_buy});
375 
376  // ... add another order to the book, we expect a callback from that
377  // event ...
378  EXPECT_CALL(callback, exec(_, _, _, _, _)).Times(1);
379  auto const id_sell = ++id;
380  now = tested.now();
381  tested.handle_message(
382  now, ++msgcnt, 0,
384  timestamp{std::chrono::nanoseconds(0)}},
385  id_sell,
386  SELL,
387  300,
388  stock,
389  p10});
390 
391  // ... try to cancel more shares than are available in the order ...
392  now = tested.now();
393  EXPECT_CALL(
394  callback, exec(
395  book_update{now, stock, SELL, p10, -300}, empty_bid(),
396  empty_offer(), 0, 0))
397  .Times(1);
398  tested.handle_message(
399  now, ++msgcnt, 0,
401  timestamp{std::chrono::nanoseconds(0)}},
402  id_sell,
403  600});
404 }
405 
406 /**
407  * Test compute book replace edge cases.
408  * @tparam based_order_book Type used by compute_book and order_book
409  */
410 template <typename based_order_book>
412  using book_type = order_book<based_order_book>;
413  using compute_type = compute_book<based_order_book>;
414 
415  ::testing::StrictMock<mock_book_callback> callback;
416  auto cb = [&callback](
417  jb::itch5::message_header const&, book_type const& b,
418  book_update const& update) {
419  callback.exec(
420  update, b.best_bid(), b.best_offer(), b.buy_count(), b.sell_count());
421  };
422  // ... create the object under test ...
423  typename based_order_book::config cfg;
424  book_type book(cfg);
425  compute_type tested(cb, cfg);
426 
427  // ... and a number of helper constants and variables to drive the
428  // test ...
429  stock_t const stock("HSART");
430  price4_t const p10(100000);
431  price4_t const p11(110000);
432  long msgcnt = 0;
433  std::uint64_t id = 2;
434  auto const id_buy_1 = ++id;
435  auto const id_buy_2 = ++id;
436  auto const id_buy_3 = ++id;
437 
438  // ... add an orders to the book ...
439  time_point now = tested.now();
440  using namespace ::testing;
441  EXPECT_CALL(callback, exec(_, _, _, _, _)).Times(1);
442  tested.handle_message(
443  now, ++msgcnt, 0,
445  timestamp{std::chrono::nanoseconds(0)}},
446  id_buy_1,
447  BUY,
448  100,
449  stock,
450  p10});
451 
452  // ... replacing a non-existing order should have no effect ...
453  now = tested.now();
454  tested.handle_message(
455  now, ++msgcnt, 0,
457  timestamp{std::chrono::nanoseconds(0)}},
458  id_buy_2,
459  id_buy_3,
460  400,
461  p11});
462 
463  // ... add a second order, we expect a callback from that event ...
464  now = tested.now();
465  EXPECT_CALL(callback, exec(_, _, _, _, _)).Times(1);
466  tested.handle_message(
467  now, ++msgcnt, 0,
469  timestamp{std::chrono::nanoseconds(0)}},
470  id_buy_2,
471  BUY,
472  300,
473  stock,
474  p11});
475 
476  // ... replacing with a duplicate id should have no effect ...
477  now = tested.now();
478  tested.handle_message(
479  now, ++msgcnt, 0,
481  timestamp{std::chrono::nanoseconds(0)}},
482  id_buy_1,
483  id_buy_2,
484  400,
485  p11});
486 }
487 
488 /**
489  * Test compute book order execute message
490  * @tparam based_order_book Type used by compute_book and order_book
491  */
492 template <typename based_order_book>
494  using book_type = order_book<based_order_book>;
495  using compute_type = compute_book<based_order_book>;
496 
497  using namespace ::testing;
498  ::testing::StrictMock<mock_book_callback> callback;
499  auto cb = [&callback](
500  jb::itch5::message_header const&, book_type const& b,
501  book_update const& update) {
502  callback.exec(
503  update, b.best_bid(), b.best_offer(), b.buy_count(), b.sell_count());
504  };
505  // ... create the object under test ...
506  typename based_order_book::config cfg;
507  book_type book(cfg);
508  compute_type tested(cb, cfg);
509 
510  // ... and a number of helper constants and variables to drive the
511  // test ...
512  stock_t const stock("HSART");
513  price4_t const p10(100000);
514  price4_t const p11(110000);
515  long msgcnt = 0;
516  std::uint64_t id = 2;
517  auto const id_buy = ++id;
518 
519  // ... add an initial order to the book, we expect a single update,
520  // and the order should be in the book when the update is called ...
521  time_point now = tested.now();
522  EXPECT_CALL(
523  callback, exec(
524  book_update{now, stock, BUY, p10, 500},
525  half_quote{p10, 500}, empty_offer(), 1, 0))
526  .Times(1);
527  tested.handle_message(
528  now, ++msgcnt, 0,
530  timestamp{std::chrono::nanoseconds(0)}},
531  id_buy,
532  BUY,
533  500,
534  stock,
535  p10},
536  mpid_t("LOOF")});
537  // ... we expect the new order to implicitly add the symbol ...
538  auto symbols = tested.symbols();
539  BOOST_REQUIRE_EQUAL(symbols.size(), std::size_t(1));
540  BOOST_CHECK_EQUAL(symbols[0], stock);
541 
542  // ... add an order to the opposite side of the book ...
543  now = tested.now();
544  auto const id_sell = ++id;
545  // ... we also expect a single update, and the order should be in
546  // the book when the update is called ...
547  EXPECT_CALL(
548  callback, exec(
549  book_update{now, stock, SELL, p11, 500},
550  half_quote{p10, 500}, half_quote{p11, 500}, 1, 1))
551  .Times(1);
552  tested.handle_message(
553  now, ++msgcnt, 0,
555  timestamp{std::chrono::nanoseconds(0)}},
556  id_sell,
557  SELL,
558  500,
559  stock,
560  p11},
561  mpid_t("LOOF")});
562 
563  // ... execute the BUY order ...
564  now = tested.now();
565  EXPECT_CALL(
566  callback, exec(
567  book_update{now, stock, BUY, p10, -100},
568  half_quote{p10, 400}, half_quote{p11, 500}, 1, 1))
569  .Times(1);
570  tested.handle_message(
571  now, ++msgcnt, 0,
573  timestamp{std::chrono::nanoseconds(0)}},
574  id_buy,
575  100,
576  ++id});
577 
578  // ... execute the SELL order ...
579  now = tested.now();
580  EXPECT_CALL(
581  callback, exec(
582  book_update{now, stock, SELL, p11, -100},
583  half_quote{p10, 400}, half_quote{p11, 400}, 1, 1))
584  .Times(1);
585  tested.handle_message(
586  now, ++msgcnt, 0,
588  timestamp{std::chrono::nanoseconds(0)}},
589  id_sell,
590  100,
591  ++id});
592 
593  // ... execute the BUY order with a price ...
594  now = tested.now();
595  EXPECT_CALL(
596  callback, exec(
597  book_update{now, stock, BUY, p10, -100},
598  half_quote{p10, 300}, half_quote{p11, 400}, 1, 1))
599  .Times(1);
600  tested.handle_message(
601  now, ++msgcnt, 0, order_executed_price_message{
603  timestamp{std::chrono::nanoseconds(0)}},
604  id_buy,
605  100,
606  ++id},
607  printable_t('N'),
608  price4_t(99901)});
609 
610  // ... execute the SELL order with a price ...
611  now = tested.now();
612  EXPECT_CALL(
613  callback, exec(
614  book_update{now, stock, SELL, p11, -100},
615  half_quote{p10, 300}, half_quote{p11, 300}, 1, 1))
616  .Times(1);
617  tested.handle_message(
618  now, ++msgcnt, 0, order_executed_price_message{
620  timestamp{std::chrono::nanoseconds(0)}},
621  id_sell,
622  100,
623  ++id},
624  printable_t('N'),
625  price4_t(110001)});
626 
627  // ... complete the execution of the BUY order ...
628  now = tested.now();
629  EXPECT_CALL(
630  callback, exec(
631  book_update{now, stock, BUY, p10, -300}, empty_bid(),
632  half_quote{p11, 300}, 0, 1))
633  .Times(1);
634  tested.handle_message(
635  now, ++msgcnt, 0,
637  timestamp{std::chrono::nanoseconds(0)}},
638  id_buy,
639  300,
640  ++id});
641 
642  // ... execute the SELL order with a price ...
643  now = tested.now();
644  EXPECT_CALL(
645  callback, exec(
646  book_update{now, stock, SELL, p11, -300}, empty_bid(),
647  empty_offer(), 0, 0))
648  .Times(1);
649  tested.handle_message(
650  now, ++msgcnt, 0,
652  timestamp{std::chrono::nanoseconds(0)}},
653  id_sell,
654  300,
655  ++id});
656 }
657 
658 /**
659  * Test compute book order replace message.
660  * @tparam based_order_book Type used by compute_book and order_book
661  */
662 template <typename based_order_book>
664  using book_type = order_book<based_order_book>;
665  using compute_type = compute_book<based_order_book>;
666 
667  using namespace ::testing;
668  ::testing::StrictMock<mock_book_callback> callback;
669  auto cb = [&callback](
670  jb::itch5::message_header const&, book_type const& b,
671  book_update const& update) {
672  callback.exec(
673  update, b.best_bid(), b.best_offer(), b.buy_count(), b.sell_count());
674  };
675  // ... create the object under test ...
676  typename based_order_book::config cfg;
677  book_type book(cfg);
678  compute_type tested(cb, cfg);
679 
680  // ... and a number of helper constants and variables to drive the
681  // test ...
682  stock_t const stock("HSART");
683  price4_t const p10(100000);
684  price4_t const p11(110000);
685  price4_t const p12(120000);
686  long msgcnt = 0;
687  std::uint64_t id = 2;
688  auto const id_buy = ++id;
689 
690  // ... add an initial order to the book, we expect a single update,
691  // and the order should be in the book when the update is called ...
692  time_point now = tested.now();
693  EXPECT_CALL(
694  callback, exec(
695  book_update{now, stock, BUY, p10, 500},
696  half_quote{p10, 500}, empty_offer(), 1, 0))
697  .Times(1);
698  tested.handle_message(
699  now, ++msgcnt, 0,
701  timestamp{std::chrono::nanoseconds(0)}},
702  id_buy,
703  BUY,
704  500,
705  stock,
706  p10},
707  mpid_t("LOOF")});
708  // ... we also expect the new order to implicitly add the symbol ...
709  auto symbols = tested.symbols();
710  BOOST_REQUIRE_EQUAL(symbols.size(), std::size_t(1));
711  BOOST_CHECK_EQUAL(symbols[0], stock);
712 
713  // ... add an order to the opposite side of the book, we expect a
714  // single update, and the order should be in the book when the
715  // update is called ...
716  now = tested.now();
717  EXPECT_CALL(
718  callback, exec(
719  book_update{now, stock, SELL, p11, 500},
720  half_quote{p10, 500}, half_quote{p11, 500}, 1, 1))
721  .Times(1);
722  auto const id_sell = ++id;
723  tested.handle_message(
724  now, ++msgcnt, 0,
726  timestamp{std::chrono::nanoseconds(0)}},
727  id_sell,
728  SELL,
729  500,
730  stock,
731  p11},
732  mpid_t("LOOF")});
733 
734  // ... replace the SELL order ...
735  now = tested.now();
736  auto id_sell_replx = ++id;
737  EXPECT_CALL(
738  callback, exec(
739  book_update{now, stock, SELL, p12, 600, true, p11, 500},
740  half_quote{p10, 500}, half_quote{p12, 600}, 1, 1))
741  .Times(1);
742  tested.handle_message(
743  now, ++msgcnt, 0,
745  timestamp{std::chrono::nanoseconds(0)}},
746  id_sell,
747  id_sell_replx,
748  600,
749  p12});
750 
751  // ... replace the BUY order ...
752  now = tested.now();
753  auto id_buy_replx = ++id;
754  EXPECT_CALL(
755  callback, exec(
756  book_update{now, stock, BUY, p11, 600, true, p12, 500},
757  half_quote{p11, 600}, half_quote{p12, 600}, 1, 1))
758  .Times(1);
759  tested.handle_message(
760  now, ++msgcnt, 0,
762  timestamp{std::chrono::nanoseconds(0)}},
763  id_buy,
764  id_buy_replx,
765  600,
766  p11});
767 }
768 
769 /**
770  * Test compute book order cancel message.
771  * @tparam based_order_book Type used by compute_book and order_book
772  */
773 template <typename based_order_book>
775  using book_type = order_book<based_order_book>;
776  using compute_type = compute_book<based_order_book>;
777 
778  using namespace ::testing;
779  ::testing::StrictMock<mock_book_callback> callback;
780  auto cb = [&callback](
781  jb::itch5::message_header const&, book_type const& b,
782  book_update const& update) {
783  callback.exec(
784  update, b.best_bid(), b.best_offer(), b.buy_count(), b.sell_count());
785  };
786 
787  typename based_order_book::config cfg;
788  compute_type tested(cb, cfg);
789 
790  // ... and a number of helper constants and variables to drive the
791  // test ...
792  stock_t const stock("HSART");
793  price4_t const p10(100000);
794  price4_t const p11(110000);
795  long msgcnt = 0;
796  std::uint64_t id = 2;
797  auto const id_buy = ++id;
798 
799  // ... add an initial order to the book, we expect a single update,
800  // and the order should be in the book when the update is called ...
801  time_point now = tested.now();
802  EXPECT_CALL(
803  callback, exec(
804  book_update{now, stock, BUY, p10, 500},
805  half_quote{p10, 500}, empty_offer(), 1, 0))
806  .Times(1);
807  tested.handle_message(
808  now, ++msgcnt, 0,
810  timestamp{std::chrono::nanoseconds(0)}},
811  id_buy,
812  BUY,
813  500,
814  stock,
815  p10},
816  mpid_t("LOOF")});
817  // ... we expect the new order to implicitly add the symbol ...
818  auto symbols = tested.symbols();
819  BOOST_REQUIRE_EQUAL(symbols.size(), std::size_t(1));
820  BOOST_CHECK_EQUAL(symbols[0], stock);
821 
822  // ... add an order to the opposite side of the book, we expect a
823  // single update, and the order should be in the book when the
824  // update is called ...
825  now = tested.now();
826  auto const id_sell = ++id;
827  EXPECT_CALL(
828  callback, exec(
829  book_update{now, stock, SELL, p11, 500},
830  half_quote{p10, 500}, half_quote{p11, 500}, 1, 1))
831  .Times(1);
832  tested.handle_message(
833  now, ++msgcnt, 0,
835  timestamp{std::chrono::nanoseconds(0)}},
836  id_sell,
837  SELL,
838  500,
839  stock,
840  p11},
841  mpid_t("LOOF")});
842 
843  // ... cancel 100 shares in the BUY order ...
844  now = tested.now();
845  EXPECT_CALL(
846  callback, exec(
847  book_update{now, stock, BUY, p10, -100},
848  half_quote{p10, 400}, half_quote{p11, 500}, 1, 1))
849  .Times(1);
850  tested.handle_message(
851  now, ++msgcnt, 0,
853  timestamp{std::chrono::nanoseconds(0)}},
854  id_buy,
855  100});
856 
857  // ... cancel 100 shares in the SELL order ...
858  now = tested.now();
859  EXPECT_CALL(
860  callback, exec(
861  book_update{now, stock, SELL, p11, -100},
862  half_quote{p10, 400}, half_quote{p11, 400}, 1, 1))
863  .Times(1);
864  tested.handle_message(
865  now, ++msgcnt, 0,
867  timestamp{std::chrono::nanoseconds(0)}},
868  id_sell,
869  100});
870 
871  // ... fully cancel the BUY order ...
872  now = tested.now();
873  EXPECT_CALL(
874  callback, exec(
875  book_update{now, stock, BUY, p10, -400}, empty_bid(),
876  half_quote{p11, 400}, 0, 1))
877  .Times(1);
878  tested.handle_message(
879  now, ++msgcnt, 0,
881  timestamp{std::chrono::nanoseconds(0)}},
882  id_buy});
883 
884  // ... fully cancel the SELL order ...
885  now = tested.now();
886  EXPECT_CALL(
887  callback, exec(
888  book_update{now, stock, SELL, p11, -400}, empty_bid(),
889  empty_offer(), 0, 0))
890  .Times(1);
891  tested.handle_message(
892  now, ++msgcnt, 0,
894  timestamp{std::chrono::nanoseconds(0)}},
895  id_sell});
896 }
897 
898 /**
899  * Test compute book stock directory message.
900  * @tparam based_order_book Type used by compute_book and order_book
901  */
902 template <typename based_order_book>
904  using book_type = order_book<based_order_book>;
905  using compute_type = compute_book<based_order_book>;
906  auto cb =
907  [](jb::itch5::message_header const&, book_type const&,
908  book_update const& update) {};
909 
910  typename based_order_book::config cfg;
911  book_type book(cfg);
912  compute_type tested(cb, cfg);
913 
914  time_point now = tested.now();
915  long msgcnt = 0;
916  tested.handle_message(now, ++msgcnt, 0, create_stock_directory("HSART"));
917  tested.handle_message(now, ++msgcnt, 0, create_stock_directory("FOO"));
918  tested.handle_message(now, ++msgcnt, 0, create_stock_directory("B"));
919 
920  std::vector<stock_t> expected{stock_t("HSART"), stock_t("FOO"), stock_t("B")};
921  auto actual = tested.symbols();
922  // ... the symbols are not guaranteed to return in any particular
923  // order, so sort them to make testing easier ...
924  std::sort(expected.begin(), expected.end());
925  std::sort(actual.begin(), actual.end());
926  BOOST_CHECK_EQUAL_COLLECTIONS(
927  expected.begin(), expected.end(), actual.begin(), actual.end());
928 
929  // ... a repeated symbol should have no effect ...
930  tested.handle_message(now, ++msgcnt, 0, create_stock_directory("B"));
931  actual = tested.symbols();
932  std::sort(expected.begin(), expected.end());
933  std::sort(actual.begin(), actual.end());
934  BOOST_CHECK_EQUAL_COLLECTIONS(
935  expected.begin(), expected.end(), actual.begin(), actual.end());
936 }
937 
938 } // namespace testing
939 } // namesapce itch5
940 } // namespace jb
941 
942 /**
943  * @test Verify compute book handle_message works as
944  * expected for add_order_message.
945  */
946 BOOST_AUTO_TEST_CASE(compute_book_add_order_message) {
947  using namespace jb::itch5;
948  namespace t = jb::itch5::testing;
949  t::test_compute_book_add_order_message_buy<map_based_order_book>();
950  t::test_compute_book_add_order_message_sell<map_based_order_book>();
951 
952  t::test_compute_book_add_order_message_buy<array_based_order_book>();
953  t::test_compute_book_add_order_message_sell<array_based_order_book>();
954 }
955 
956 /**
957  * @test Increase code coverage in jb::itch5::compute_book
958  */
959 BOOST_AUTO_TEST_CASE(compute_book_increase_coverage) {
960  using namespace jb::itch5;
961  namespace t = jb::itch5::testing;
962  t::test_compute_book_increase_coverage<map_based_order_book>();
963  t::test_compute_book_increase_coverage<array_based_order_book>();
964 }
965 
966 /**
967  * @test Increase code coverage in
968  * jb::itch5::compute_book::handle_message for add_order_message.
969  */
970 BOOST_AUTO_TEST_CASE(compute_book_add_order_message_edge_cases) {
971  using namespace jb::itch5;
972  namespace t = jb::itch5::testing;
973  t::test_compute_book_edge_cases<map_based_order_book>();
974  t::test_compute_book_edge_cases<array_based_order_book>();
975 }
976 
977 /**
978  * @test Increase code coverage in
979  * jb::itch5::compute_book::handle_order_reduction
980  */
981 BOOST_AUTO_TEST_CASE(compute_book_reduction_edge_cases) {
982  using namespace jb::itch5;
983  namespace t = jb::itch5::testing;
984  t::test_compute_book_reduction_edge_cases<map_based_order_book>();
985  t::test_compute_book_reduction_edge_cases<array_based_order_book>();
986 }
987 
988 /**
989  * @test Increase code coverage for order_replace_message
990  */
991 BOOST_AUTO_TEST_CASE(compute_book_replace_edge_cases) {
992  using namespace jb::itch5;
993  namespace t = jb::itch5::testing;
994  t::test_compute_book_replace_edge_cases<map_based_order_book>();
995  t::test_compute_book_replace_edge_cases<array_based_order_book>();
996 }
997 
998 /**
999  * @test Verify that jb::itch5::compute_book::handle_message works as
1000  * expected for order_executed_message BUY & SELL.
1001  */
1002 BOOST_AUTO_TEST_CASE(compute_book_order_executed_message) {
1003  using namespace jb::itch5;
1004  namespace t = jb::itch5::testing;
1005  t::test_compute_book_order_executed_message<map_based_order_book>();
1006  t::test_compute_book_order_executed_message<array_based_order_book>();
1007 }
1008 
1009 /**
1010  * @test Verify that jb::itch5::compute_book::handle_message works as
1011  * expected for order_replace_message BUY & SELL.
1012  */
1013 BOOST_AUTO_TEST_CASE(compute_book_order_replace_message) {
1014  using namespace jb::itch5;
1015  namespace t = jb::itch5::testing;
1016  t::test_compute_book_order_replace_message<map_based_order_book>();
1017  t::test_compute_book_order_replace_message<array_based_order_book>();
1018 }
1019 
1020 /**
1021  * @test Verify that jb::itch5::compute_book::handle_message works as
1022  * expected for order_cancel_message and order_delete_message, both sides.
1023  */
1024 BOOST_AUTO_TEST_CASE(compute_book_order_cancel_message) {
1025  using namespace jb::itch5;
1026  namespace t = jb::itch5::testing;
1027  t::test_compute_book_order_cancel_message<map_based_order_book>();
1028  t::test_compute_book_order_cancel_message<array_based_order_book>();
1029 }
1030 
1031 /**
1032  * @test Verify that jb::itch5::compute_book::handle_message works as
1033  *expected for stock_directory_message.
1034  */
1035 BOOST_AUTO_TEST_CASE(compute_book_stock_directory_message) {
1036  using namespace jb::itch5;
1037  namespace t = jb::itch5::testing;
1038  t::test_compute_book_stock_directory_message<map_based_order_book>();
1039  t::test_compute_book_stock_directory_message<array_based_order_book>();
1040 }
1041 
1042 /**
1043  * @test Verify that jb::itch5::book_update operators
1044  * work as expected.
1045  */
1046 BOOST_AUTO_TEST_CASE(compute_book_book_update_operators) {
1047  using namespace jb::itch5;
1048  namespace t = jb::itch5::testing;
1049 
1050  auto const ts0 = clock_type::now();
1051  std::this_thread::sleep_for(std::chrono::milliseconds(20));
1052  auto const ts1 = clock_type::now();
1053 
1054  BOOST_CHECK_EQUAL(
1055  book_update({ts0, stock_t("A"), t::BUY, price4_t(1000), 100}),
1056  book_update({ts0, stock_t("A"), t::BUY, price4_t(1000), 100}));
1057  BOOST_CHECK_NE(
1058  book_update({ts0, stock_t("A"), t::BUY, price4_t(1000), 100}),
1059  book_update({ts1, stock_t("A"), t::BUY, price4_t(1000), 100}));
1060  BOOST_CHECK_NE(
1061  book_update({ts0, stock_t("A"), t::BUY, price4_t(1000), 100}),
1062  book_update({ts0, stock_t("B"), t::BUY, price4_t(1000), 100}));
1063  BOOST_CHECK_NE(
1064  book_update({ts0, stock_t("A"), t::BUY, price4_t(1000), 10}),
1065  book_update({ts0, stock_t("A"), t::SELL, price4_t(1000), 10}));
1066  BOOST_CHECK_NE(
1067  book_update({ts0, stock_t("A"), t::BUY, price4_t(1000), 100}),
1068  book_update({ts0, stock_t("A"), t::BUY, price4_t(1001), 100}));
1069  BOOST_CHECK_NE(
1070  book_update({ts0, stock_t("A"), t::BUY, price4_t(1000), 100}),
1071  book_update({ts0, stock_t("B"), t::BUY, price4_t(1000), 200}));
1072 
1073  std::ostringstream os;
1074  os << book_update{ts1, stock_t("A"), t::BUY, price4_t(1000), 300};
1075  BOOST_CHECK_EQUAL(os.str(), "{A,B,0.1000,300}");
1076 }
void test_compute_book_add_order_message_buy()
Test compute book based on book type.
static constexpr int message_type
Compute the book and call a user-defined callback on each change.
Define the header common to all ITCH 5.0 messages.
void test_compute_book_order_replace_message()
Test compute book order replace message.
Contains classes and functions to parse NASDAQ ITCH-5.0 messages, more information about ITCH-5...
clock_type::time_point time_point
A convenience alias for clock_type::time_point.
Represent an &#39;Order Executed&#39; message in the ITCH-5.0 protocol.
half_quote empty_bid()
The value used to represent an empty bid.
char_list_field< u 'S', u 'B'> buy_sell_indicator_t
Represent the &#39;Buy/Sell Indicator&#39; field on several messages.
buy_sell_indicator_t const SELL(u 'S')
void test_compute_book_replace_edge_cases()
Test compute book replace edge cases.
void test_compute_book_add_order_message_sell()
Test compute book based on book type.
jb::itch5::short_string_field< 4 > mpid_t
Define the type used to represent the &#39;MPID&#39; field in many messages.
Definition: mpid_field.hpp:19
char_list_field< u 'Y', u 'N'> printable_t
Represent the &#39;Printable&#39; field on a &#39;Order Executed with Price&#39; message.
void test_compute_book_reduction_edge_cases()
Test compute book reduction edge cases.
Represent an &#39;Order Delete&#39; message in the ITCH-5.0 protocol.
static constexpr int message_type
Represent an &#39;Order Executed with Price&#39; message in the ITCH-5.0 protocol.
stock_directory_message create_stock_directory(char const *symbol)
Create a dummy jb::itch5::stock_directory_message for testing.
Definition: messages.cpp:7
Represent an &#39;Add Order with MPID&#39; message in the ITCH-5.0 protocol.
MOCK_METHOD5(exec, void(book_update update, half_quote best_bid, half_quote best_offer, int buy_count, int offer_count))
void test_compute_book_order_executed_message()
Test compute book order execute message.
std::pair< price4_t, int > half_quote
A simple representation for price + quantity.
jb::itch5::short_string_field< 8 > stock_t
Define the type used to represent the &#39;Stock&#39; field in many messages.
Definition: stock_field.hpp:18
Represent a ITCH-5.0 timestamp.
Definition: timestamp.hpp:17
std::vector< char > create_message(int message_type, jb::itch5::timestamp ts, std::size_t total_size)
Generate test messages with a more-or-less valid header.
Definition: data.cpp:287
Represent an &#39;Trade (non-Cross)&#39; message in the ITCH-5.0 protocol.
Initialize GMock to work with Boost.Test.
void test_compute_book_increase_coverage()
Test compute book increase coverage.
A flat struct to represent updates to an order book.
buy_sell_indicator_t const BUY(u 'B')
half_quote empty_offer()
The value used to represent an empty offer.
Represent an &#39;Order Replace&#39; message in the ITCH-5.0 protocol.
BOOST_AUTO_TEST_CASE(compute_book_add_order_message)
void test_compute_book_stock_directory_message()
Test compute book stock directory message.
price_field< std::uint32_t, 10000 > price4_t
Convenience definition for Price(4) fields.
Maintain the ITCH-5.0 order book for a single security.
Definition: order_book.hpp:57
void test_compute_book_edge_cases()
Test compute book edge cases.
void test_compute_book_order_cancel_message()
Test compute book order cancel message.
Functions used in testing jb::itch5 entities.
Definition: data.cpp:11
Represent an &#39;Add Order&#39; message in the ITCH-5.0 protocol.
The top-level namespace for the JayBeams library.
Definition: as_hhmmss.hpp:7
Represent an &#39;Order Cancel&#39; message in the ITCH-5.0 protocol.