fix8  version 1.4.0
Open Source C++ FIX Framework
myfix.cpp
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------------------
2 /*
3 
4 Fix8 is released under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
5 
6 Fix8 Open Source FIX Engine.
7 Copyright (C) 2010-16 David L. Dight <fix@fix8.org>
8 
9 Fix8 is free software: you can redistribute it and / or modify it under the terms of the
10 GNU Lesser General Public License as published by the Free Software Foundation, either
11 version 3 of the License, or (at your option) any later version.
12 
13 Fix8 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
14 even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 You should have received a copy of the GNU Lesser General Public License along with Fix8.
17 If not, see <http://www.gnu.org/licenses/>.
18 
19 BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO
20 THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
21 COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
22 KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO
24 THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
25 YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
26 
27 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT
28 HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED
29 ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
30 CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
31 NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR
32 THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH
33 HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34 
35 */
36 
37 //-----------------------------------------------------------------------------------------
90 //-----------------------------------------------------------------------------------------
91 #include <iostream>
92 #include <memory>
93 #include <fstream>
94 #include <iomanip>
95 #include <sstream>
96 #include <vector>
97 #include <map>
98 #include <list>
99 #include <set>
100 #include <vector>
101 #include <iterator>
102 #include <algorithm>
103 #include <typeinfo>
104 #include <thread>
105 #ifdef _MSC_VER
106 #include <signal.h>
107 #include <conio.h>
108 #else
109 #include <sys/ioctl.h>
110 #include <signal.h>
111 #include <termios.h>
112 #endif
113 
114 #include <errno.h>
115 #include <string.h>
116 
117 // f8 headers
118 #include <fix8/f8includes.hpp>
119 //#include <fix8/zeromq_mbus.hpp>
120 
121 #ifdef FIX8_HAVE_GETOPT_H
122 #include <getopt.h>
123 #endif
124 
125 #include <fix8/usage.hpp>
126 #include <fix8/consolemenu.hpp>
127 #include <fix8/multisession.hpp>
128 #include "Myfix_types.hpp"
129 #include "Myfix_router.hpp"
130 #include "Myfix_classes.hpp"
131 
132 #include "myfix.hpp"
133 
134 //-----------------------------------------------------------------------------------------
135 using namespace std;
136 using namespace FIX8;
137 
138 //-----------------------------------------------------------------------------------------
139 void print_usage();
140 const string GETARGLIST("hl:svqc:R:S:rdomN:D:");
142 void server_process(ServerSessionBase *srv, int scnt, bool ismulti=false), client_process(ClientSessionBase *mc);
144 
145 //-----------------------------------------------------------------------------------------
147 {
148  { { 'n', "New Order Single" }, &MyMenu::new_order_single },
149  { { 'a', "New Order Single (alternate group method)" }, &MyMenu::new_order_single_alternate },
150  { { 'r', "New Order Single Recycled - 1st use send as normal then will send recycled message" },
152  { { 'N', "50 New Order Singles" }, &MyMenu::new_order_single_50 },
153  { { 'T', "1000 New Order Singles" }, &MyMenu::new_order_single_1000 },
154  { { 'R', "Resend request" }, &MyMenu::resend_request },
155  { { '?', "Help" }, &MyMenu::help },
156  { { 'l', "Logout" }, &MyMenu::do_logout },
157  { { 't', "Examine static message traits" }, &MyMenu::static_probe },
158  { { 'x', "Exit" }, &MyMenu::do_exit },
159 };
160 
161 bool quiet(false);
162 unsigned next_send(0), next_receive(0);
163 
164 //-----------------------------------------------------------------------------------------
165 void sig_handler(int sig)
166 {
167  switch (sig)
168  {
169  case SIGTERM:
170  case SIGINT:
171 #ifndef _MSC_VER
172  case SIGQUIT:
173 #endif
174  term_received = true;
175  signal(sig, sig_handler);
176  break;
177  }
178 }
179 
180 //-----------------------------------------------------------------------------------------
181 int main(int argc, char **argv)
182 {
183  int val;
184  bool server(false), reliable(false), once(false), dump(false), multi(false);
185  string clcf, session;
186 
187 #ifdef FIX8_HAVE_GETOPT_LONG
188  option long_options[]
189  {
190  { "help", 0, 0, 'h' },
191  { "version", 0, 0, 'v' },
192  { "log", 1, 0, 'l' },
193  { "delimiter", 1, 0, 'D' },
194  { "config", 1, 0, 'c' },
195  { "session", 1, 0, 'N' },
196  { "once", 0, 0, 'o' },
197  { "server", 0, 0, 's' },
198  { "multi", 0, 0, 'm' },
199  { "send", 1, 0, 'S' },
200  { "receive", 1, 0, 'R' },
201  { "quiet", 0, 0, 'q' },
202  { "reliable", 0, 0, 'r' },
203  { "dump", 0, 0, 'd' },
204  { 0 },
205  };
206 
207  while ((val = getopt_long (argc, argv, GETARGLIST.c_str(), long_options, 0)) != -1)
208 #else
209  while ((val = getopt (argc, argv, GETARGLIST.c_str())) != -1)
210 #endif
211  {
212  switch (val)
213  {
214  case 'v':
215  cout << argv[0] << " for " FIX8_PACKAGE " version " FIX8_VERSION << endl;
216  cout << "Released under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3. See <http://fsf.org/> for details." << endl;
217  return 0;
218  case ':': case '?': return 1;
219  case 'h': print_usage(); return 0;
220  case 'l': GlobalLogger::set_global_filename(optarg); break;
221  case 'D': GlobalLogger::set_delimiter(optarg); break;
222  case 'c': clcf = optarg; break;
223  case 's': server = true; break;
224  case 'N': session = optarg; break;
225  case 'm': multi = true; break;
226  case 'o': once = true; break;
227  case 'S': next_send = stoul(optarg); break;
228  case 'R': next_receive = stoul(optarg); break;
229  case 'q': quiet = true; break;
230  case 'r': reliable = true; break;
231  case 'd': dump = true; break;
232  default: break;
233  }
234  }
235 
236  RandDev::init();
237 
238  signal(SIGTERM, sig_handler);
239  signal(SIGINT, sig_handler);
240 #ifndef _MSC_VER
241  signal(SIGQUIT, sig_handler);
242 #endif
243 
244  bool restore_tty(false);
245 
246  try
247  {
248  const string conf_file(server ? clcf.empty() ? "myfix_server.xml" : clcf : clcf.empty() ? "myfix_client.xml" : clcf);
249  f8_atomic<unsigned> scnt(0);
250 
251  if (dump)
252  {
253  XmlElement *root(XmlElement::Factory(conf_file));
254  if (root)
255  cout << *root << endl;
256  else
257  cerr << "Failed to parse " << conf_file << endl;
258  return 0;
259  }
260 
261  if (server)
262  {
263  if (multi) // demonstrate use of multi session server manager
264  {
265  unique_ptr<ServerManager> sm(new ServerManager);
266  sm->add(new ServerSession<myfix_session_server>(TEX::ctx(), conf_file, "TEX1"));
267  sm->add(new ServerSession<myfix_session_server>(TEX::ctx(), conf_file, "TEX2"));
268 
269  vector<thread> thrds;
270  while (!term_received)
271  {
272  ServerSessionBase *srv(sm->select());
273  if (srv)
274  {
275  thrds.push_back(thread ([&]() { server_process(srv, ++scnt, true); }));
277  }
278  }
279  for_each(thrds.begin(), thrds.end(), [](thread& tt) { if (tt.joinable()) tt.join(); });
280  }
281  else // serial server instances only
282  {
283  unique_ptr<ServerSessionBase> srv(new ServerSession<myfix_session_server>(TEX::ctx(), conf_file, "TEX"));
284 
285  while (!term_received)
286  {
287  if (!srv->poll())
288  continue;
289  server_process(srv.get(), ++scnt);
290  if (once)
291  break;
292  }
293  }
294  }
295  else
296  {
297  if (multi) // demonstrate use of multi session client manager
298  {
299  const f8String cl1("DLD1"), cl2("DLD2");
300  ClientManager cm;
301  cm.add(cl1, reliable ? new ReliableClientSession<myfix_session_client>(TEX::ctx(), conf_file, cl1)
302  : new ClientSession<myfix_session_client>(TEX::ctx(), conf_file, cl1));
303  cm.add(cl2, reliable ? new ReliableClientSession<myfix_session_client>(TEX::ctx(), conf_file, cl2)
304  : new ClientSession<myfix_session_client>(TEX::ctx(), conf_file, cl2));
305 
307  {
308  if (!quiet)
309  pp->session_ptr()->control() |= Session::printnohb;
310  pp->start(false, next_send, next_receive, pp->session_ptr()->get_login_parameters()._davi());
311  // if you use ReliableClientSession, you need to return true here always
312  return reliable ? true : States::is_live(pp->session_ptr()->get_session_state());
313  }));
314 
315  if (csb)
316  cerr << csb->_session_name << " failed to start" << endl;
317 
318  vector<thread> thrds;
319  cm.for_each_if([&](ClientSessionBase *pp)
320  {
321  // we should be running client_process however this won't work
322  // since two threads can't share the same console
323  thrds.push_back(thread ([=]() // use copy closure
324  {
325  MyMenu mymenu(*pp->session_ptr(), 0, cout);
327  mymenu.new_order_single(); // send an order and then logout
328  mymenu.do_logout();
329  }));
330  return true;
331  });
332 
333  for_each(thrds.begin(), thrds.end(), [](thread& tt) { if (tt.joinable()) tt.join(); });
334  }
335  else // single client only
336  {
337  const string my_session(session.empty() ? "DLD1" : session);
338  unique_ptr<ClientSessionBase>
339  mc(reliable ? new ReliableClientSession<myfix_session_client>(TEX::ctx(), conf_file, my_session)
340  : new ClientSession<myfix_session_client>(TEX::ctx(), conf_file, my_session));
341  if (!quiet)
342  mc->session_ptr()->control() |= Session::printnohb;
343  mc->start(false, next_send, next_receive, mc->session_ptr()->get_login_parameters()._davi());
344  client_process(mc.get());
345  }
346  }
347  }
348  catch (f8Exception& e)
349  {
350  cerr << "exception: " << e.what() << endl;
351  restore_tty = true;
352  glout_error << e.what();
353  }
354  catch (exception& e) // also catches Poco::Net::NetException
355  {
356  cerr << "std::exception: " << e.what() << endl;
357  restore_tty = true;
358  glout_error << e.what();
359  }
360  catch (...)
361  {
362  cerr << "unknown exception" << endl;
363  restore_tty = true;
364  glout_error << "unknown exception";
365  }
366 
367  if (restore_tty && !server)
369 
370  if (term_received)
371  cout << endl << "terminated." << endl;
372  return 0;
373 }
374 
375 //-----------------------------------------------------------------------------------------
377 {
378  MyMenu mymenu(*mc->session_ptr(), 0, cout);
379  cout << endl << "Menu started. Press '?' for help..." << endl << endl;
380  char ch(0);
381  mymenu.get_tty().set_raw_mode();
382  save_tty = mymenu.get_tty();
383  while(!mymenu.get_istr().get(ch).bad() && !mc->has_given_up() && !term_received && ch != 0x3 && mymenu.process(ch))
384  ;
385  // don't explicitly call mc->session_ptr()->stop() with reliable sessions
386  // before checking if the session is already shutdown - the framework will generally do this for you
387  if (!mc->session_ptr()->is_shutdown())
388  mc->session_ptr()->stop();
389 
390  mymenu.get_tty().unset_raw_mode();
391 }
392 
393 //-----------------------------------------------------------------------------------------
394 void server_process(ServerSessionBase *srv, int scnt, bool ismulti)
395 {
396  unique_ptr<SessionInstanceBase> inst(srv->create_server_instance());
397  if (!quiet)
398  inst->session_ptr()->control() |= Session::print;
399  glout_info << "client(" << scnt << ") connection established.";
400  const ProcessModel pm(srv->get_process_model(srv->_ses));
401  cout << (pm == pm_pipeline ? "Pipelined" : "Threaded") << " mode." << endl;
402  inst->start(pm == pm_pipeline, next_send, next_receive);
403  if (inst->session_ptr()->get_connection()->is_secure())
404  cout << "Session is secure (SSL)" << endl;
405  if (!ismulti && !quiet) // demonstrate use of timer events
406  {
407  TimerEvent<FIX8::Session> sample_callback(static_cast<bool (FIX8::Session::*)()>(&myfix_session_server::sample_scheduler_callback), true);
408  inst->session_ptr()->get_timer().schedule(sample_callback, 60000); // call sample_scheduler_callback every minute forever
409  }
410  if (pm != pm_pipeline)
411  while (!inst->session_ptr()->is_shutdown())
413  cout << "Session(" << scnt << ") finished." << endl;
414  inst->stop();
415 }
416 
417 //-----------------------------------------------------------------------------------------
418 bool myfix_session_client::handle_application(const unsigned seqnum, const Message *&msg)
419 {
420  return enforce(seqnum, msg) || msg->process(_router);
421 }
422 
423 //-----------------------------------------------------------------------------------------
425 {
426  cout << get_session_state_string(before) << " => " << get_session_state_string(after) << endl;
427 }
428 
429 //-----------------------------------------------------------------------------------------
430 bool myfix_session_server::handle_application(const unsigned seqnum, const Message *&msg)
431 {
432  if (enforce(seqnum, msg))
433  return false;
434  // this is how you take ownership of the message from the framework
435  if (!msg->process(_router)) // false means I have taken ownership of the message
436  detach(msg);
437  return true;
438 }
439 
440 //-----------------------------------------------------------------------------------------
442 {
443  cout << get_session_state_string(before) << " => " << get_session_state_string(after) << endl;
444 }
445 
446 //-----------------------------------------------------------------------------------------
448 {
449  cout << "myfix_session_server::sample_scheduler_callback Hello!" << endl;
450  return true;
451 }
452 
453 //-----------------------------------------------------------------------------------------
455 {
456  static unsigned oid(0);
457  ostringstream oistr;
458  oistr << "ord" << ++oid;
459  TEX::NewOrderSingle *nos(new TEX::NewOrderSingle(false)); // shallow construction
460  *nos << new TEX::TransactTime
461  << new TEX::OrderQty(1 + RandDev::getrandom(9999))
462  << new TEX::Price(RandDev::getrandom(500.), 5) // 5 decimal places if necessary
463  << new TEX::ClOrdID(oistr.str())
464  << new TEX::Symbol("BHP")
466  << new TEX::Side(TEX::Side_BUY)
468 
469  *nos << new TEX::NoPartyIDs(unsigned(0));
470  *nos << new TEX::NoUnderlyings(3);
471  GroupBase *noul(nos->find_add_group<TEX::NewOrderSingle::NoUnderlyings>(nullptr)); // no parent group
472 
473  // repeating groups
474  MessageBase *gr1(noul->create_group(false)); // shallow construction
475  *gr1 << new TEX::UnderlyingSymbol("BLAH")
476  << new TEX::UnderlyingQty(1 + RandDev::getrandom(999));
477  *noul << gr1;
478 
479  MessageBase *gr2(noul->create_group(false)); // shallow construction
480  // nested repeating groups
481  *gr2 << new TEX::UnderlyingSymbol("FOO")
483  *noul << gr2;
484  GroupBase *nosai(gr2->find_add_group<TEX::NewOrderSingle::NoUnderlyings::NoUnderlyingSecurityAltID>(noul)); // parent is noul
485  MessageBase *gr3(nosai->create_group(false)); // shallow construction
486  *gr3 << new TEX::UnderlyingSecurityAltID("UnderBlah");
487  *nosai << gr3;
488  MessageBase *gr4(nosai->create_group(false)); // shallow construction
489  *gr4 << new TEX::UnderlyingSecurityAltID("OverFoo");
490  *nosai << gr4;
491 
492  MessageBase *gr5(noul->create_group(false)); // shallow construction
493  *gr5 << new TEX::UnderlyingSymbol("BOOM");
494  // nested repeating groups
495  GroupBase *nus(gr5->find_add_group<TEX::NewOrderSingle::NoUnderlyings::NoUnderlyingStips>(noul)); // parent is noul
496  static const char *secIDs[] { "Reverera", "Orlanda", "Withroon", "Longweed", "Blechnod" };
497  *gr5 << new TEX::NoUnderlyingStips(sizeof(secIDs)/sizeof(char *));
498  for (size_t ii(0); ii < sizeof(secIDs)/sizeof(char *); ++ii)
499  {
500  MessageBase *gr(nus->create_group(false)); // shallow construction
501  *gr << new TEX::UnderlyingStipType(secIDs[ii]);
502  *nus << gr;
503  }
504  *noul << gr5;
505 
506  // multiply nested repeating groups
507  *nos << new TEX::NoAllocs(1);
508  GroupBase *noall(nos->find_add_group<TEX::NewOrderSingle::NoAllocs>(nullptr)); // no parent group
509  MessageBase *gr9(noall->create_group(false)); // shallow construction
510  *gr9 << new TEX::AllocAccount("Account1")
511  << new TEX::NoNestedPartyIDs(1);
512  *noall << gr9;
513  GroupBase *nonp(gr9->find_add_group<TEX::NewOrderSingle::NoAllocs::NoNestedPartyIDs>(noall)); // parent is noall
514  MessageBase *gr10(nonp->create_group(false)); // shallow construction
515  *gr10 << new TEX::NestedPartyID("nestedpartyID1")
516  << new TEX::NoNestedPartySubIDs(1);
517  *nonp << gr10;
518  GroupBase *nonpsid(gr10->find_add_group<TEX::NewOrderSingle::NoAllocs::NoNestedPartyIDs::NoNestedPartySubIDs>(nonp)); // parent is nonp
519  MessageBase *gr11(nonpsid->create_group(false)); // shallow construction
520  *gr11 << new TEX::NestedPartySubID("subnestedpartyID1");
521  *nonpsid << gr11;
522 
523  return nos;
524 }
525 
526 //-----------------------------------------------------------------------------------------
528 {
529  static unsigned oid(0);
530  ostringstream oistr;
531  oistr << "ord" << ++oid;
533  *nos << new TEX::TransactTime
534  << new TEX::OrderQty(1 + RandDev::getrandom(9999))
535  << new TEX::Price(RandDev::getrandom(500.), 5) // 5 decimal places if necessary
536  << new TEX::ClOrdID(oistr.str())
537  << new TEX::Symbol("BHP")
539  << new TEX::Side(TEX::Side_BUY)
541 
542  *nos << new TEX::NoPartyIDs(unsigned(0));
543  *nos << new TEX::NoUnderlyings(3);
545 
546  // repeating groups
547  MessageBase *gr1(noul->create_group());
548  *gr1 << new TEX::UnderlyingSymbol("BLAH")
549  << new TEX::UnderlyingQty(1 + RandDev::getrandom(999));
550  *noul << gr1;
551 
552  MessageBase *gr2(noul->create_group());
553  // nested repeating groups
554  *gr2 << new TEX::UnderlyingSymbol("FOO")
556  *noul << gr2;
558  MessageBase *gr3(nosai->create_group());
559  *gr3 << new TEX::UnderlyingSecurityAltID("UnderBlah");
560  *nosai << gr3;
561  MessageBase *gr4(nosai->create_group());
562  *gr4 << new TEX::UnderlyingSecurityAltID("OverFoo");
563  *nosai << gr4;
564 
565  MessageBase *gr5(noul->create_group());
566  *gr5 << new TEX::UnderlyingSymbol("BOOM");
567  // nested repeating groups
569  static const char *secIDs[] { "Reverera", "Orlanda", "Withroon", "Longweed", "Blechnod" };
570  *gr5 << new TEX::NoUnderlyingStips(sizeof(secIDs)/sizeof(char *));
571  for (size_t ii(0); ii < sizeof(secIDs)/sizeof(char *); ++ii)
572  {
573  MessageBase *gr(nus->create_group());
574  *gr << new TEX::UnderlyingStipType(secIDs[ii]);
575  *nus << gr;
576  }
577  *noul << gr5;
578 
579  // multiply nested repeating groups
580  *nos << new TEX::NoAllocs(1);
582  MessageBase *gr9(noall->create_group());
583  *gr9 << new TEX::AllocAccount("Account1")
584  << new TEX::NoNestedPartyIDs(1);
585  *noall << gr9;
587  MessageBase *gr10(nonp->create_group());
588  *gr10 << new TEX::NestedPartyID("nestedpartyID1")
589  << new TEX::NoNestedPartySubIDs(1);
590  *nonp << gr10;
592  MessageBase *gr11(nonpsid->create_group());
593  *gr11 << new TEX::NestedPartySubID("subnestedpartyID1");
594  *nonpsid << gr11;
595 
596  return nos;
597 }
598 
599 //-----------------------------------------------------------------------------------------
601 {
602  _session.send(generate_new_order_single_alternate());
603  return true;
604 }
605 
606 //-----------------------------------------------------------------------------------------
608 {
609  _session.send(generate_new_order_single());
610  return true;
611 }
612 
613 //-----------------------------------------------------------------------------------------
615 {
616  // create a message; first time through we just send it as normal. Note the we pass the no-destroy flag;
617  // second and subsequent times we just recyle the old message
618 
619  static bool first(true);
620  static Message *msg(0);
621  if (first)
622  {
623  cout << "Sending new new_order_single" << endl;
624  _session.send(msg = generate_new_order_single(), first = false);
625  }
626  else
627  {
628  msg->setup_reuse();
629  cout << "Sending recycled new_order_single" << endl;
630  _session.send(msg, false);
631  }
632 #if defined FIX8_RAW_MSG_SUPPORT
633  // demonstrate access to outbound raw fix message and payload
634  cout << msg->get_rawmsg() << endl;
635  copy(msg->begin_payload(), msg->end_payload(), ostream_iterator<char>(cout, ""));
636  cout << endl;
637  cout << "payload begin=" << msg->get_payload_begin() << " payload len=" << msg->get_payload_len() << endl;
638 #endif
639  return true;
640 }
641 
642 //-----------------------------------------------------------------------------------------
644 {
645 #if defined FIX8_HAVE_EXTENDED_METADATA
646  (cout << "Enter message tag:").flush();
647  f8String result;
648  const BaseMsgEntry *tbme;
649  if (!get_string(result).empty() && (tbme = _session.get_ctx()._bme.find_ptr(result.c_str())))
650  {
651  function<void( const TraitHelper&, int )> print_traits;
652  print_traits = ([&print_traits, this](const TraitHelper& tr, int depth)
653  {
654  const string spacer(depth * MessageBase::get_tabsize(), ' ');
655  for (F8MetaCntx::const_iterator itr(F8MetaCntx::begin(tr)); itr != F8MetaCntx::end(tr); ++itr)
656  {
657  const BaseEntry *be(_session.get_ctx().find_be(itr->_fnum));
658  cout << spacer << be->_name;
659  if (be->_rlm)
660  cout << " Realm:" << (be->_rlm->_dtype == RealmBase::dt_range ? "range" : "set")
661  << '(' << be->_rlm->_sz << ") ";
662  cout << *itr << endl;
663  if (itr->_field_traits.has(FieldTrait::group))
664  print_traits(itr->_group, depth + 1);
665  }
666  });
667 
668  cout << tbme->_name << endl;
669  print_traits(tbme->_create._get_traits(), 1);
670  }
671  else
672  cout << "Unknown message tag: " << result << endl;
673 #else
674  cout << "Extended metadata not available (try ./configure --enable-extended-metadata=yes)." << endl;
675 #endif
676 
677  return true;
678 }
679 
680 //-----------------------------------------------------------------------------------------
682 {
683  vector<Message *> msgs;
684  for (auto ii(0); ii < 50; ++ii)
685  msgs.push_back(generate_new_order_single());
686 
687  _session.send_batch(msgs);
688  return true;
689 }
690 
691 //-----------------------------------------------------------------------------------------
693 {
694  vector<Message *> msgs;
695  for (auto ii(0); ii < 1000; ++ii)
696  msgs.push_back(generate_new_order_single());
697 
698  _session.send_batch(msgs);
699  return true;
700 }
701 
702 //-----------------------------------------------------------------------------------------
703 bool MyMenu::help()
704 {
705  get_ostr() << endl;
706  get_ostr() << "Key\tCommand" << endl;
707  get_ostr() << "===\t=======" << endl;
708  for (const auto& pp : _handlers)
709  get_ostr() << pp.first._key << '\t' << pp.first._help << endl;
710  get_ostr() << endl;
711  return true;
712 }
713 
714 //-----------------------------------------------------------------------------------------
715 bool MyMenu::do_logout()
716 {
717  if (!_session.is_shutdown())
718  {
719  _session.send(new TEX::Logout);
720  get_ostr() << "logout..." << endl;
721  }
723  return false; // will exit
724 }
725 
726 //-----------------------------------------------------------------------------------------
728 {
729  if (!_session.is_shutdown())
730  {
731  unsigned bnum(0), bend(0);
732  cout << "Enter BeginSeqNo:" << flush;
733  _tty.unset_raw_mode();
734  cin >> bnum;
735  cout << "Enter EndSeqNo(0=all):" << flush;
736  cin >> bend;
737  _tty.set_raw_mode();
738  _session.send(_session.generate_resend_request(bnum, bend));
739  }
740  return true;
741 }
742 
743 //-----------------------------------------------------------------------------------------
745 {
746  UsageMan um("f8test", GETARGLIST, "");
747  um.setdesc("f8test -- f8 test client/server");
748  um.add('s', "server", "run in server mode (default client mode)");
749  um.add('m', "multi", "run in multiple server or client mode (default serial server or single client session at a time)");
750  um.add('h', "help", "help, this screen");
751  um.add('v', "version", "print version, exit");
752  um.add('l', "log", "global log filename");
753  um.add('o', "once", "for server, allow one client session then exit");
754  um.add('c', "config", "xml config (default: myfix_client.xml or myfix_server.xml)");
755  um.add('q', "quiet", "do not print fix output");
756  um.add('R', "receive", "set next expected receive sequence number");
757  um.add('S', "send", "set next send sequence number");
758  um.add('D', "delimiter", "set GlobalLogger field delimiter (default ' ')");
759  um.add('N', "session", "for client, select session to use from configuration (default DLD1)");
760  um.add('r', "reliable", "start in reliable mode");
761  um.add('d', "dump", "dump parsed XML config file, exit");
762  um.add("e.g.");
763  um.add("@f8test -sml server_log");
764  um.add("@f8test -rl client_log");
765  um.add("@f8test -rl server -S 124");
766  um.print(cerr);
767 }
768 
769 //-----------------------------------------------------------------------------------------
771 {
772 #if defined FIX8_RAW_MSG_SUPPORT
773  // demonstrate access to inbound raw fix message and payload
774  cout << msg->get_rawmsg() << endl;
775  copy(msg->begin_payload(), msg->end_payload(), ostream_iterator<char>(cout, ""));
776  cout << endl;
777  cout << "payload begin=" << msg->get_payload_begin() << " payload len=" << msg->get_payload_len() << endl;
778 #endif
779 #if 0
780  const Presence& pre(msg->get_fp().get_presence());
781  for (Fields::const_iterator itr(msg->fields_begin()); itr != msg->fields_end(); ++itr)
782  {
783  const FieldTrait::FieldType trait(pre.find(itr->first)->_ftype);
784  cout << itr->first << " is ";
785  if (FieldTrait::is_int(trait))
786  cout << "int";
787  else if (FieldTrait::is_char(trait))
788  cout << "char";
789  else if (FieldTrait::is_string(trait))
790  cout << "string";
791  else if (FieldTrait::is_float(trait))
792  cout << "float";
793  else
794  cout << "unknown";
795  cout << '\t' << _session.get_ctx().find_be(itr->first)->_name << '\t' << *itr->second << endl;
796  }
797 #endif
798 
799  static unsigned oid(0), eoid(0);
800 
801  TEX::OrderQty qty;
803 
804  if (!quiet)
805  {
806  // This is how you extract a copy of a field value
807  if (msg->get(qty))
808  cout << "Order qty (copy):" << qty() << endl;
809 
810  // This is how you get a field value in place
811  if (msg->has<TEX::OrderQty>())
812  cout << "Order qty (in place):" << msg->get<TEX::OrderQty>()->get() << endl;
813 
814  if (msg->get(price))
815  cout << "price:" << price() << endl;
816 
817  // This is how you extract values from a repeating group
819  if (grnoul)
820  {
821  for (size_t cnt(0); cnt < grnoul->size(); ++cnt)
822  {
823  TEX::UnderlyingSymbol unsym;
824  MessageBase *me(grnoul->get_element(static_cast<unsigned>(cnt)));
825  me->get(unsym);
826  cout << "Underlying symbol:" << unsym() << endl;
827  // This is how you extract values from a nested repeating group
829  if (nus)
830  {
831  for (size_t cnt(0); cnt < nus->size(); ++cnt)
832  {
833  TEX::UnderlyingStipType stipType;
834  MessageBase *me(nus->get_element(static_cast<unsigned>(cnt)));
835  me->get(stipType);
836  cout << "Underlying StipType:" << stipType() << endl;
837  }
838  }
839  }
840  }
841 
842  const GroupBase *grallocs(msg->find_group<TEX::NewOrderSingle::NoAllocs>());
843  if (grnoul)
844  {
845  for (size_t cnt(0); cnt < grallocs->size(); ++cnt)
846  {
847  TEX::AllocAccount acc;
848  MessageBase *me(grallocs->get_element(static_cast<unsigned>(cnt)));
849  me->get(acc);
850  cout << "TEX::NewOrderSingle::NoAllocs Account:" << acc() << endl;
851  // This is how you extract values from a nested repeating group
853  if (nnpi)
854  {
855  for (size_t cnt(0); cnt < nnpi->size(); ++cnt)
856  {
857  TEX::NestedPartyID npi;
858  MessageBase *me(nnpi->get_element(static_cast<unsigned>(cnt)));
859  me->get(npi);
860  cout << "TEX::NewOrderSingle::NoAllocs::NoNestedPartyIDs NestedPartyID:" << npi() << endl;
861  // This is how you extract values from a nested nested repeating group
863  if (nnpsi)
864  {
865  for (size_t cnt(0); cnt < nnpsi->size(); ++cnt)
866  {
868  MessageBase *me(nnpsi->get_element(static_cast<unsigned>(cnt)));
869  me->get(npsi);
870  cout << "TEX::NewOrderSingle::NoAllocs::NoNestedPartyIDs::NoNestedPartySubIDs NestedPartySubID:" << npsi() << endl;
871  }
872  }
873  }
874  }
875  }
876  }
877  }
878 
880  msg->copy_legal(er);
881  if (!quiet)
882  cout << endl;
883 
884  ostringstream oistr;
885  oistr << "ord" << ++oid;
886  *er << new TEX::OrderID(oistr.str())
889  << new TEX::LeavesQty(qty())
890  << new TEX::CumQty(0.)
891  << new TEX::AvgPx(0.)
892  << new TEX::LastCapacity('5')
893  << new TEX::ReportToExch('Y')
894  << new TEX::ExecID(oistr.str());
895  msg->push_unknown(er);
896  _session.send(er);
897 
898  unsigned remaining_qty(qty()), cum_qty(0);
899  while (remaining_qty > 0)
900  {
901  unsigned trdqty(RandDev::getrandom(remaining_qty));
902  if (!trdqty)
903  trdqty = 1;
904  er = new TEX::ExecutionReport;
905  msg->copy_legal(er);
906  ostringstream eistr;
907  eistr << "exec" << ++eoid;
908  remaining_qty -= trdqty;
909  cum_qty += trdqty;
910  *er << new TEX::OrderID(oistr.str())
911  << new TEX::ExecID(eistr.str())
913  << new TEX::OrdStatus(remaining_qty == trdqty ? TEX::OrdStatus_FILLED : TEX::OrdStatus_PARTIALLY_FILLED)
914  << new TEX::LeavesQty(remaining_qty)
915  << new TEX::CumQty(cum_qty)
916  << new TEX::LastQty(trdqty)
917  << new TEX::AvgPx(price());
918  _session.send(er);
919  }
920 
921  return true;
922 }
923 
924 //-----------------------------------------------------------------------------------------
926 {
927  TEX::LastCapacity lastCap;
928  if (msg->get(lastCap))
929  {
930  // if we have set a realm range for LastCapacity, when can check it here
931  if (!quiet && !lastCap.is_valid())
932  cout << "TEX::LastCapacity(" << lastCap << ") is not a valid value" << endl;
933  }
934  return true;
935 }
936 
static F8API XmlElement * Factory(std::istream &istr, const char *docpath=nullptr)
Field< NumInGroup, 453 > NoPartyIDs
static void init()
Initialise the random number generator.
Definition: hftest.hpp:205
Field< NumInGroup, 711 > NoUnderlyings
Field< Qty, 14 > CumQty
bool new_order_single_alternate()
Definition: myfix.cpp:600
Session Manager.
bool do_logout()
Definition: harness.cpp:367
Field< char, 54 > Side
Field< f8String, 524 > NestedPartyID
Field< Qty, 879 > UnderlyingQty
static const Handlers _handlers
Definition: hftest.hpp:171
Multi Server Manager.
NoAllocs (78), application, 6 fields, 1 group, shares static data, hash: 0x210f1ab6.
virtual bool operator()(const FIX8::TEX::ExecutionReport *msg)
Definition: hftest.cpp:655
FIX8::Message * generate_new_order_single_alternate()
Definition: myfix.cpp:454
Fix8 Base Session. User sessions are derived from this class.
Definition: session.hpp:394
Field< f8String, 545 > NestedPartySubID
const char TimeInForce_FILL_OR_KILL('4')
Field< NumInGroup, 78 > NoAllocs
Field< char, 59 > TimeInForce
bool new_order_single_recycled()
Definition: myfix.cpp:614
Message instantiation table entry.
Definition: message.hpp:192
FIX8::tty_save_state save_tty(0)
static T getrandom(const T range=0)
Definition: hftest.hpp:219
Field< NumInGroup, 804 > NoNestedPartySubIDs
bool add(const char sw, const std::string &lsw, const std::string &help)
Definition: usage.hpp:73
const char Side_BUY('1')
Field< f8String, 79 > AllocAccount
GroupBase * find_group() const
Definition: message.hpp:792
Field< char, 39 > OrdStatus
ExecutionReport (8), application, 326 fields, 16 groups.
Field< Boolean, 113 > ReportToExch
const char * _name
Definition: field.hpp:2078
#define FIX8_PACKAGE
Definition: f8config.h:601
Field< price, 44 > Price
F8API unsigned copy_legal(MessageBase *to, bool force=false) const
Definition: message.cpp:275
Field< Qty, 32 > LastQty
Client wrapper.
Logout (5), admin, 3 fields, 0 groups.
Field< char, 40 > OrdType
const Presence & get_presence() const
Definition: traits.hpp:658
bool new_order_single()
Definition: hftest.cpp:419
Field< NumInGroup, 539 > NoNestedPartyIDs
ProcessModel
Supported session process models.
Definition: f8types.hpp:56
unsigned next_receive(0)
FIX8::Message * generate_new_order_single()
Definition: myfix.cpp:527
#define glout_error
Definition: logger.hpp:606
void push_unknown(Message *to) const
Definition: message.hpp:1292
const char OrdStatus_FILLED('2')
bool get(T &to) const
Definition: message.hpp:671
void state_change(const FIX8::States::SessionStates before, const FIX8::States::SessionStates after)
Definition: harness.cpp:327
Base Client wrapper.
const char * _name
Definition: message.hpp:195
Abstract base class for all repeating groups.
Definition: message.hpp:59
unsigned next_send(0)
virtual MessageBase * create_group(bool deepctor=true) const =0
string spacer
Definition: f8c.cpp:96
f8_atomic< bool > term_received(false)
const F8MetaCntx & ctx()
Compiler generated metadata object, accessed through this function.
T * for_each_if(std::function< bool(T *)> func)
Base exception class.
Definition: f8exception.hpp:49
NewOrderSingle (D), application, 243 fields, 11 groups.
void setdesc(const std::string &desc)
Definition: usage.hpp:91
A simple xml parser with Xpath style lookup.
Definition: xml.hpp:48
bool has() const
Definition: message.hpp:686
bool do_exit()
Definition: hftest.hpp:194
void print(std::ostream &os) const
Definition: usage.hpp:95
F8API void stop()
stop the session.
Definition: session.cpp:230
int hypersleep< h_milliseconds >(unsigned amt)
Definition: hypersleep.hpp:105
const char OrdType_LIMIT('2')
bool handle_application(const unsigned seqnum, const FIX8::Message *&msg)
Definition: harness.cpp:321
void print_usage()
Definition: myfix.cpp:744
Field< f8String, 11 > ClOrdID
Definition: Myfix_types.hpp:93
Base Server Session.
std::map< const MenuItem, bool(MyMenu::*)(), MenuItem > Handlers
Definition: hftest.hpp:170
bool static_probe()
Definition: myfix.cpp:643
virtual bool operator()(const FIX8::TEX::NewOrderSingle *msg)
Definition: hftest.cpp:585
virtual bool process(Router &rt) const
Definition: message.hpp:1147
#define glout_info
Definition: logger.hpp:601
void server_process(ServerSessionBase *srv, int scnt, bool ismulti=false)
Definition: myfix.cpp:394
Simple menu system that will work with most term types.
Definition: hftest.hpp:151
bool quiet(false)
A complete Fix message with header, body and trailer.
Definition: message.hpp:1058
std::atomic< T > f8_atomic
Definition: thread.hpp:55
void state_change(const FIX8::States::SessionStates before, const FIX8::States::SessionStates after)
Definition: harness.cpp:339
Field< f8String, 458 > UnderlyingSecurityAltID
Fields::const_iterator fields_end() const
Definition: message.hpp:756
Field< price, 6 > AvgPx
Definition: Myfix_types.hpp:83
Field template. There will ONLY be partial template specialisations of this template.
Definition: field.hpp:256
int hypersleep< h_seconds >(unsigned amt)
Definition: hypersleep.hpp:83
Field< char, 29 > LastCapacity
const char OrdStatus_PARTIALLY_FILLED('1')
bool handle_application(const unsigned seqnum, const FIX8::Message *&msg)
Definition: harness.cpp:333
bool new_order_single_1000()
Definition: myfix.cpp:692
GroupBase * find_add_group(GroupBase *grpbase=nullptr)
Definition: message.hpp:808
Field< f8String, 37 > OrderID
Field< NumInGroup, 887 > NoUnderlyingStips
const std::string _session_name
const FieldTraits & get_fp() const
Definition: message.hpp:878
Field< Qty, 38 > OrderQty
void setup_reuse()
Definition: message.hpp:1279
void sig_handler(int sig)
Definition: myfix.cpp:165
virtual bool has_given_up() const
#define FIX8_VERSION
Definition: f8config.h:742
const string GETARGLIST("hl:svqc:R:S:rdomN:D:")
const XmlElement * _ses
virtual SessionInstanceBase * create_server_instance()=0
const char OrdStatus_NEW('0')
Field< f8String, 311 > UnderlyingSymbol
NoUnderlyings (711), application, 72 fields, 3 groups, shares static data, hash: 0xb078f5a8.
bool add(const f8String &name, T *what)
const char * what() const
Definition: f8exception.hpp:85
Base class for all fix messages.
Definition: message.hpp:381
const char ExecType_NEW('0')
bool help()
Definition: harness.cpp:345
const Minst _create
Definition: message.hpp:194
bool resend_request()
Definition: myfix.cpp:727
int main(int argc, char **argv)
Definition: myfix.cpp:181
Field< NumInGroup, 457 > NoUnderlyingSecurityAltID
Fields::const_iterator fields_begin() const
Definition: message.hpp:752
F8API ProcessModel get_process_model(const XmlElement *from) const
bool is_shutdown()
Definition: session.hpp:778
Field< Qty, 151 > LeavesQty
std::string f8String
Definition: f8types.hpp:47
Convenient program help/usage wrapper. Generates a standardised usage message.
Definition: usage.hpp:42
Reliable Client wrapper. This client attempts to recover from disconnects and login rejects...
void client_process(ClientSessionBase *mc)
Definition: myfix.cpp:376
bool sample_scheduler_callback()
Definition: myfix.cpp:447
bool new_order_single_50()
Definition: myfix.cpp:681
Field< f8String, 888 > UnderlyingStipType
Field< f8String, 17 > ExecID
virtual Session * session_ptr()=0
fp_type price
Definition: field.hpp:2024