libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
fix_client_main.cpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2019 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 #include "fix_client.hpp"
20 
21 #include "core/jthread.hpp"
22 #include "core/latency_timestamps.hpp"
23 
24 #include <boost/asio.hpp>
25 #include <boost/exception/diagnostic_information.hpp>
26 #include <boost/program_options.hpp>
27 
28 #include <iostream>
29 
30 using namespace libisimud;
31 using namespace libjmmcg;
32 
33 int main(int argc, char const * const *argv) {
34  try {
35  using thread_t=libjmmcg::ppd::jthread;
36  using fix_client_t=exchanges::FIX::v5_0sp2::fix_client;
37 
38  boost::program_options::options_description general(
39  "A simple client that repeatedly sends a fixed FIX-message to a link listening at a IPADDR:PORT combination. The executable name indicates the message version implemented. For details regarding the properties of the client see the documentation that came with the distribution. Note that Wireshark has a built-in FIX protocol: see the \"Analyze/Enabled Protocols...\" dialog in Wireshark or https://www.wireshark.org/docs/dfref/f/fix.html. Copyright © J.M.McGuiness, isimud@hussar.me.uk. http://libjmmcg.sf.net/ Distributed under the terms of the GPL v2.1.\n"+exit_codes::to_string()+"Arguments"
40  );
41  general.add_options()
42  ("help", "Print this help message.")
43  ("version", "Print the build number of this program.")
44  ;
45  boost::program_options::options_description client("FIX-translator options.");
46  client.add_options()
47  ("address", boost::program_options::value<boost::asio::ip::address>()->default_value(boost::asio::ip::address_v4::loopback()), "IP address (in v4 format; thus the network interface to which it is boundmo) to which the messages should be sent.")
48  ("port", boost::program_options::value<unsigned short>()->required(), "The port to which the messages should be sent.")
49  ("processor", boost::program_options::value<unsigned short>()->required()->default_value(exchanges::common::thread_traits::client_to_exchange_thread.core), "The core to which the client should be bound.")
50  ;
51  boost::program_options::options_description performance("Performance-monitoring options.");
52  performance.add_options()
53  ("num_loops", boost::program_options::value<unsigned long>()->required()->default_value(20000), "The number of times the fix message should be sent (and therefore received) in each batch.")
54  ("loops_for_conv", boost::program_options::value<unsigned short>()->required()->default_value(500), "The maximum number of times the batch should be run.")
55  ("perc_conv_estimate", boost::program_options::value<double>()->required()->default_value(2.0), "The maximum value of mean average-deviation to which the repeated runs of the batches should converge.")
56  ;
57  performance.add_options()
58  (
59  "num_timings",
60  boost::program_options::value<unsigned long>()->required()->default_value(0UL),
61  "The number (positive-integer) of latency timings to reserve. This value must be greater than the total number of messages to be processed, otherwise the behaviour is undefined. Upon exit, these values will be written in CSV-format to a file named 'argv[0]+\"-%%%%%.PID.csv\"' (where each '%' is a random hexadecimal digit and PID is the current process id). This file shall be created in the current directory. If the value is zero, then the file shall not be created, no values will be stored, unlimited messages may be processed."
62  );
63  boost::program_options::options_description all("All options.");
64  all.add(general).add(client).add(performance);
65  boost::program_options::variables_map vm;
66  boost::program_options::store(boost::program_options::parse_command_line(argc, argv, all), vm);
67  if (vm.count("help")) {
68  std::cout<<all<<std::endl;
70  }
71  if (vm.count("version")) {
72  std::cout<<LIBJMMCG_DETAILED_VERSION_INFO " Built with FIX: " ISIMUD_FIX_EXCHANGE_VERSION <<std::endl;
74  }
75  boost::program_options::notify(vm);
76  ppd::api_threading_traits<ppd::platform_api, ppd::sequential_mode>::set_backtrace_on_signal();
77  const std::size_t num_timestamps=vm["num_timings"].as<std::size_t>();
78  latency_timestamps timestamps(num_timestamps);
79  {
80  fix_client_t sender(
81  vm["address"].as<boost::asio::ip::address>(),
82  vm["port"].as<unsigned short>(),
83  socket::socket_priority::high,
84  vm["processor"].as<unsigned short>(),
85  timestamps
86  );
87  std::clog<<sender<<std::endl;
88  std::pair<fix_client_t::timed_results_t, bool> results;
89  thread_t collect_stats(
90  [&sender, &vm, &results]() {
91  results=sender.in_order_tx_rx_stats(
92  vm["num_loops"].as<unsigned long>(),
93  vm["loops_for_conv"].as<unsigned short>(),
94  vm["perc_conv_estimate"].as<double>()
95  );
96  }
97  );
98  thread_t::thread_traits::set_kernel_affinity(
99  thread_t::thread_traits::get_current_thread(),
100  thread_t::thread_traits::api_params_type::processor_mask_type(0)
101  );
102  thread_t::thread_traits::set_kernel_priority(
103  thread_t::thread_traits::get_current_thread(),
104  thread_t::thread_traits::api_params_type::priority_type::idle
105  );
106  while (!sender.signal_status() && collect_stats.is_running()) {
107  thread_t::thread_traits::sleep(0);
108  }
109  std::cout<<"Exchange round-trip in-order time (microseconds)="<<results.first<<(results.second ? " converged." : " NOT converged.")<<std::endl;
110  std::clog<<sender<<std::endl;
111  thread_t::thread_traits::raise(SIGQUIT);
112  }
113  timestamps.write_to_csv_file(std::clog, argv[0]);
115  } catch (std::exception const &ex) {
116  std::cerr<<"STL-derived exception. Details: "<<boost::diagnostic_information(ex)<<std::endl;
118  } catch (...) {
119  std::cerr<<"Unknown exception."<<std::endl;
121  }
123 }