libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
processing_rules_impl.hpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2015 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 namespace isimud { namespace ISIMUD_VER_NAMESPACE { namespace exchanges { namespace MIT { namespace common {
20 
21 template<class SrcMsgDetails, class DestMsgDetails>
22 struct client_to_exchange_transformations<SrcMsgDetails, DestMsgDetails>::state_machine_t : libjmmcg::msm::unroll::state_transition_table<state_machine_t> {
23  using msm_base_t=libjmmcg::msm::unroll::state_transition_table<state_machine_t>;
24  using row_t=libjmmcg::msm::unroll::row_types<typename src_msg_details_t::MsgType_t, typename dest_msg_details_t::MsgTypes_t>;
25  using transition_table=typename msm_base_t::template rows<
26  typename row_t::template row<
27  src_msg_details_t::NewOrder_t::static_type,
28  typename base_t::template convert_then_send_ref_data<typename src_msg_details_t::NewOrder_t, typename dest_msg_details_t::NewOrder_t>,
29  dest_msg_details_t::NewOrder_t::static_type
30  >,
31  typename row_t::template row<
32  src_msg_details_t::OrderCancelRequest::static_type,
33  typename base_t::template convert_then_send_ref_data<typename src_msg_details_t::OrderCancelRequest, typename dest_msg_details_t::OrderCancelRequest>,
34  dest_msg_details_t::OrderCancelRequest::static_type
35  >,
36  typename row_t::template row<
37  src_msg_details_t::OrderCancelReplace::static_type,
38  typename base_t::template convert_then_send_ref_data<typename src_msg_details_t::OrderCancelReplace, typename dest_msg_details_t::OrderCancelReplaceRequest>,
39  dest_msg_details_t::OrderCancelReplaceRequest::static_type
40  >,
41  /// Reject any message that has not been recognised.
42  typename row_t::template row<
43  src_msg_details_t::MatchAll,
44  typename base_t::template send_reject<typename src_msg_details_t::Reject, true>,
45  dest_msg_details_t::Reject::static_type
46  >
47  >;
48 };
49 
50 template<class SrcMsgDetails, class DestMsgDetails>
51 template<class SktT>
52 inline bool
53 client_to_exchange_transformations<SrcMsgDetails, DestMsgDetails>
54 ::process_msg(typename src_msg_details_t::msg_buffer_t const &buff, SktT &exchg_skt, SktT &dest_skt) {
55  typename src_msg_details_t::Header_t const &hdr=reinterpret_cast<typename src_msg_details_t::Header_t const &>(buff);
56  const auto last_state=msm.process(hdr.type(), buff, exchg_skt, dest_skt);
57  return last_state==dest_msg_details_t::Exit;
58 }
59 
60 template<class SrcMsgDetails, class DestMsgDetails> inline std::string
61 client_to_exchange_transformations<SrcMsgDetails, DestMsgDetails>::to_string() const noexcept(false) {
62  std::ostringstream ss;
63  ss<<"Source message details: ";
64  SrcMsgDetails::to_stream(ss);
65  ss<<"\n\tDestination message details: ";
66  DestMsgDetails::to_stream(ss);
67  return ss.str();
68 }
69 
70 template<class SrcMsgDetails, class DestMsgDetails> inline std::ostream &
71 operator<<(std::ostream &os, client_to_exchange_transformations<SrcMsgDetails, DestMsgDetails> const &ec) noexcept(false) {
72  os<<ec.to_string();
73  return os;
74 }
75 
76 template<class SrcMsgDetails, class DestMsgDetails>
77 struct exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails>::business_state_machine_t : libjmmcg::msm::unroll::state_transition_table<business_state_machine_t> {
78  using msm_base_t=libjmmcg::msm::unroll::state_transition_table<business_state_machine_t>;
79  using row_t=libjmmcg::msm::unroll::row_types<typename src_msg_details_t::MsgTypes_t, typename dest_msg_details_t::MsgType_t>;
80  /**
81  From section \todo 5.1: "TODO" of [1]: the response to an ExecutionReport is a various messages.
82 
83  \param msg The message that was received, that shall be processed.
84  \param client_skt The socket to which any responses should be written.
85  */
87  public:
88  explicit constexpr ExecutionReportResponse(ref_data const &rd) noexcept(true)
89  : ref_data_(rd) {
90  assert(!ref_data_.lookup_isin().empty());
91  }
92  constexpr ExecutionReportResponse(ExecutionReportResponse const &nor) noexcept(true)
93  : ref_data_(nor.ref_data_) {
94  assert(!ref_data_.lookup_isin().empty());
95  }
96 
97  template<auto state, auto next, class SktT, class ClientCxn>
98  typename dest_msg_details_t::MsgType_t
99  operator()(typename src_msg_details_t::msg_buffer_t const &buff, SktT &, ClientCxn &client_cxn) const noexcept(false) {
100  typename src_msg_details_t::ExecutionReport const &msg=reinterpret_cast<typename src_msg_details_t::ExecutionReport const &>(buff);
101  assert(msg.type()==state);
102  switch (msg.orderStatus()) {
109  assert(!ref_data_.lookup_isin().empty());
110  const typename dest_msg_details_t::ExecutionReport reply(msg, ref_data_);
111  client_cxn->socket().write(reply);
112  return dest_msg_details_t::ExecutionReport::static_type;
113  }
115  default: {
116  const typename dest_msg_details_t::OrderRejected reply(msg);
117  client_cxn->socket().write(reply);
118  return dest_msg_details_t::OrderRejected::static_type;
119  }
120  }
121  }
122 
123  private:
124  ref_data const &ref_data_;
125  };
126  using transition_table=typename msm_base_t::template rows<
127  /**
128  From section \todo 5.1: "TODO" of [1]: the response to an Reject is a client Reject.
129 
130  \param msg The message that was received, that shall be processed.
131  \param client_skt The socket to which any responses should be written.
132  */
133  typename row_t::template row<
134  src_msg_details_t::Reject::static_type,
135  typename base_t::template convert_then_send<typename src_msg_details_t::Reject, typename dest_msg_details_t::OrderRejected>,
136  dest_msg_details_t::OrderRejected::static_type
137  >,
138  /**
139  From section \todo 5.1: "TODO" of [1]: the response to an OrderCancelReject is a client OrderCancelReject.
140 
141  \param msg The message that was received, that shall be processed.
142  \param client_skt The socket to which any responses should be written.
143  */
144  typename row_t::template row<
145  src_msg_details_t::OrderCancelReject::static_type,
146  typename base_t::template convert_then_send<typename src_msg_details_t::OrderCancelReject, typename dest_msg_details_t::CancelRejected>,
147  dest_msg_details_t::CancelRejected::static_type
148  >,
149  typename row_t::template row<
150  src_msg_details_t::ExecutionReport::static_type,
152  dest_msg_details_t::ExecutionReport::static_type|dest_msg_details_t::OrderRejected::static_type
153  >,
154  /// Reject any message that has not been recognised.
155  typename row_t::template row<
156  src_msg_details_t::MatchAll,
157  typename base_t::template send_reject<typename dest_msg_details_t::Reject, true>,
158  dest_msg_details_t::Reject::static_type
159  >
160  >;
161 };
162 
163 template<class SrcMsgDetails, class DestMsgDetails>
164 struct exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails>::admin_state_machine_t : libjmmcg::msm::unroll::state_transition_table<admin_state_machine_t> {
165  using msm_base_t=libjmmcg::msm::unroll::state_transition_table<admin_state_machine_t>;
166  using row_t=libjmmcg::msm::unroll::row_types<typename src_msg_details_t::MsgTypes_t, typename dest_msg_details_t::MsgType_t>;
168  explicit constexpr LogonReplyResponse(business_machine const &) noexcept(true) {}
169  constexpr LogonReplyResponse(LogonReplyResponse const &) noexcept(true)=default;
170 
171  /**
172  From section 5.3: "Terminating a connection" of [1]: the response to a Logout is to stop processing.
173 
174  \return False to continue processing messages, true otherwise.
175  */
176  template<auto state, auto next, class SktT, class ClientCxn>
177  auto operator()(typename src_msg_details_t::msg_buffer_t const &buff, SktT &, ClientCxn &) const noexcept(false) {
178  typename src_msg_details_t::LogonReply const &msg=reinterpret_cast<typename src_msg_details_t::LogonReply const &>(buff);
179  assert(msg.type()==state);
180  switch (msg.rejectCode()) {
181  case src_msg_details_t::LogonReply::RejectCode_t::tag_SUCCESS:
182  // TODO need to set as logged on.
183  break;
184  default: {
185  std::ostringstream ss;
186  ss<<"Unrecognised LogonResponse reject-code="<<static_cast<typename src_msg_details_t::LogonReply::RejectCodes_t>(msg.rejectCode())<<", '"<<msg.rejectCode()<<"'.";
187  throw std::runtime_error(ss.str());
188  }
189  }
190  return next;
191  }
192  };
193  using transition_table=typename msm_base_t::template rows<
194  /**
195  From section 5.2.2: "Heartbeats" of [1]: the response to a server Heartbeat is a Heartbeat.
196  */
197  typename row_t::template row<
198  src_msg_details_t::ServerHeartbeat::static_type,
199  typename base_t::template just_send_to_exchg<typename src_msg_details_t::ClientHeartbeat>,
200  dest_msg_details_t::ClientHeartbeat::static_type
201  >,
202  /**
203  From section 5.3: "Terminating a connection" of [1]: the response to a Logout is to stop processing.
204  */
205  typename row_t::template row<
206  src_msg_details_t::Logout::static_type,
207  typename msm_base_t::no_op, // TODO
208  dest_msg_details_t::Exit
209  >,
210  typename row_t::template row<
211  src_msg_details_t::LogonReply::static_type,
213  dest_msg_details_t::LogonRequest::static_type
214  >,
215  /**
216  From section \todo 5.1: "TODO" of [1]: the response to an OrderCancelReject is a client OrderCancelReject.
217 
218  \param msg The message that was received, that shall be processed.
219  \param client_skt The socket to which any responses should be written.
220  */
221  typename row_t::template row<
222  src_msg_details_t::BusinessReject::static_type,
223  typename base_t::template convert_then_send<typename src_msg_details_t::BusinessReject, typename dest_msg_details_t::BusinessMessageReject>,
224  dest_msg_details_t::BusinessMessageReject::static_type
225  >,
226  /// Reject any message that has not been recognised.
227  typename row_t::template row<
228  src_msg_details_t::MatchAll,
229  typename base_t::template match_all_response<business_machine>,
230  dest_msg_details_t::Reject::static_type
231  >
232  >;
233 };
234 
235 template<class SrcMsgDetails, class DestMsgDetails>
236 template<class SktT, class ClientCxn>
237 inline bool
238 exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails>
239 ::process_msg(typename src_msg_details_t::msg_buffer_t const &buff, SktT &exchg_skt, ClientCxn &client_skt) {
240  typename src_msg_details_t::Header_t const &hdr=reinterpret_cast<typename src_msg_details_t::Header_t const &>(buff);
241  const auto last_state=admin_msm.process(hdr.type(), buff, exchg_skt, client_skt);
242  return last_state==dest_msg_details_t::Exit;
243 }
244 
245 template<class SrcMsgDetails, class DestMsgDetails> inline std::string
246 exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails>::to_string() const noexcept(false) {
247  std::ostringstream os;
248  os<<"Source message details: ";
249  SrcMsgDetails::to_stream(os);
250  os<<"\n\tDestination message details: ";
251  DestMsgDetails::to_stream(os);
252  return os.str();
253 }
254 
255 template<class SrcMsgDetails, class DestMsgDetails> inline std::ostream &
256 operator<<(std::ostream &os, exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails> const &ec) noexcept(false) {
257  os<<ec.to_string();
258  return os;
259 }
260 
261 template<class SrcMsgDetails>
262 struct simulator_responses<SrcMsgDetails>::state_machine_t : libjmmcg::msm::unroll::state_transition_table<state_machine_t> {
263  using msm_base_t=libjmmcg::msm::unroll::state_transition_table<state_machine_t>;
264  using row_t=libjmmcg::msm::unroll::row_types<typename msg_details_t::MsgTypes_t, typename msg_details_t::MsgTypes_t>;
266  explicit constexpr LogonRequestResponse(simulator_responses const &) noexcept(true) {}
267  constexpr LogonRequestResponse(LogonRequestResponse const &) noexcept(true)=default;
268 
269  /**
270  From section 5.1: "Establishing a connection" of [1]: the response to a Logon is a LogonReply.
271 
272  \return The next state from the declaration of the table.
273  */
274  template<auto state, auto next, class SktT>
275  auto operator()(typename msg_details_t::msg_buffer_t const &buff, SktT &, SktT &client_skt) const noexcept(false) {
276  typename msg_details_t::LogonRequest const &msg=reinterpret_cast<typename msg_details_t::LogonRequest const &>(buff);
277  assert(msg.type()==state);
278  typename msg_details_t::LogonReply reply;
279  if (std::strcmp(msg.userName.begin(), simulator_responses::username.begin())==0) {
280  if (std::strcmp(msg.password.begin(), simulator_responses::password.begin())==0) {
281  reply.rejectCode(msg_details_t::LogonReply::RejectCode_t::tag_SUCCESS);
282  client_skt.write(reply);
283  } else {
284  reply.rejectCode(msg_details_t::LogonReply::invalid_logon_details);
285  client_skt.write(reply);
286  }
287  } else {
288  reply.rejectCode(msg_details_t::LogonReply::unknown_user);
289  client_skt.write(reply);
290  }
291  return next;
292  }
293  };
295  explicit constexpr LogoutRequestResponse(simulator_responses const &) noexcept(true) {}
296  constexpr LogoutRequestResponse(LogoutRequestResponse const &) noexcept(true)=default;
297 
298  /**
299  From section 5.3: "Terminating a connection" of [1]: the response to a LogoutRequest is a Logout.
300 
301  \return The next state from the declaration of the table.
302  */
303  template<auto state, auto next, class SktT>
304  auto operator()(typename msg_details_t::msg_buffer_t const &, SktT &, SktT &client_skt) const noexcept(false) {
305  client_skt.write(typename msg_details_t::Logout(simulator_responses::logout_reason));
306  std::this_thread::sleep_for(simulator_responses::logout_timeout);
307  return next;
308  }
309  };
311  public:
312  enum : typename msg_details_t::MsgTypes_t {
313  exec_report=static_cast<typename std::underlying_type<decltype(msg_details_t::ExecutionReport::static_type)>::type>(msg_details_t::ExecutionReport::static_type),
314  cancel_reject=static_cast<typename std::underlying_type<decltype(msg_details_t::OrderCancelReject::static_type)>::type>(msg_details_t::OrderCancelReject::static_type),
316  };
317 
318  explicit OrderCancelRequestResponse(simulator_responses &sr) noexcept(true)
319  : sim_resp(sr) {
320  assert(dynamic_cast<simulator_responses *>(&sim_resp));
321  }
322  constexpr OrderCancelRequestResponse(OrderCancelRequestResponse const &nor) noexcept(true)
323  : sim_resp(nor.sim_resp) {}
324 
325  /**
326  From section 9.1 "Order handling" of [1]: the response to a OrderCancelRequest.
327 
328  \return The next state for the state machine.
329  */
330  template<auto state, auto, class SktT>
331  typename msg_details_t::MsgTypes_t
332  operator()(typename msg_details_t::msg_buffer_t const &buff, SktT &, SktT &client_skt) const noexcept(false) {
333  typename msg_details_t::OrderCancelRequest const &msg=reinterpret_cast<typename msg_details_t::OrderCancelRequest const &>(buff);
334  assert(msg.type()==state);
335  assert(!std::string(msg.originalClientOrderID().data()).empty());
336  assert(dynamic_cast<simulator_responses *>(&sim_resp));
337  auto const cancelled_order=sim_resp.order_book.find(msg.originalClientOrderID());
338  if (cancelled_order!=sim_resp.order_book.end()) {
339  typename msg_details_t::ExecutionReport reply(
340  sim_resp.sequenceNumber,
341  msg.originalClientOrderID(),
342  msg_details_t::AppID::Partition1,
343  msg_details_t::ExecType::Cancelled,
344  cancelled_order->second.limitPrice(),
345  cancelled_order->second.instrumentID(),
346  cancelled_order->second.side()
347  );
348  reply.orderStatus(msg_details_t::OrderStatus::Cancelled);
349  reply.executedQty(0);
350  reply.leavesQty(cancelled_order->second.orderQty());
351  sim_resp.order_book.erase(msg.originalClientOrderID());
352  client_skt.write(reply);
353  return msg_details_t::ExecutionReport::static_type;
354  } else {
355  const typename msg_details_t::OrderCancelReject reply(sim_resp.sequenceNumber, msg.originalClientOrderID());
356  client_skt.write(reply);
357  return msg_details_t::OrderCancelReject::static_type;
358  }
359  }
360 
361  private:
362  simulator_responses &sim_resp;
363  };
365  public:
366  enum : typename msg_details_t::MsgTypes_t {
367  exec_report=static_cast<typename std::underlying_type<decltype(msg_details_t::ExecutionReport::static_type)>::type>(msg_details_t::ExecutionReport::static_type),
368  cancel_reject=static_cast<typename std::underlying_type<decltype(msg_details_t::OrderCancelReject::static_type)>::type>(msg_details_t::OrderCancelReject::static_type),
370  };
371 
372  explicit OrderCancelReplaceRequestResponse(simulator_responses &sr) noexcept(true)
373  : sim_resp(sr) {
374  assert(dynamic_cast<simulator_responses *>(&sim_resp));
375  }
377  : sim_resp(nor.sim_resp) {}
378 
379  /**
380  From section 9.1 "Order handling" of [1]: the response to a OrderCancelReplaceRequest.
381 
382  \return The next state from the declaration of the table.
383  */
384  template<auto state, auto, class SktT>
385  typename msg_details_t::MsgTypes_t
386  operator()(typename msg_details_t::msg_buffer_t const &buff, SktT &, SktT &client_skt) const noexcept(false) {
387  typename msg_details_t::OrderCancelReplaceRequest const &msg=reinterpret_cast<typename msg_details_t::OrderCancelReplaceRequest const &>(buff);
388  assert(msg.type()==state);
389  assert(dynamic_cast<simulator_responses *>(&sim_resp));
390  auto const modified_order=sim_resp.order_book.find(msg.originalClientOrderID());
391  if (modified_order!=sim_resp.order_book.end()) {
392  modified_order->second.limitPrice(msg.limitPrice());
393  modified_order->second.orderQty(msg.orderQty());
394  modified_order->second.tif(msg.tif());
395  modified_order->second.side(msg.side());
396  typename msg_details_t::ExecutionReport reply(
397  sim_resp.sequenceNumber,
398  msg.originalClientOrderID(),
399  msg_details_t::AppID::Partition1,
400  msg_details_t::ExecType::Replaced,
401  modified_order->second.limitPrice(),
402  modified_order->second.instrumentID(),
403  modified_order->second.side()
404  );
405  reply.orderStatus(msg_details_t::OrderStatus::Cancelled);
406  reply.executedQty(0);
407  reply.leavesQty(modified_order->second.orderQty());
408  client_skt.write(reply);
409  return msg_details_t::ExecutionReport::static_type;
410  } else {
411  const typename msg_details_t::OrderCancelReject reply(sim_resp.sequenceNumber, msg.originalClientOrderID());
412  client_skt.write(reply);
413  return msg_details_t::OrderCancelReject::static_type;
414  }
415  }
416 
417  private:
418  simulator_responses &sim_resp;
419  };
421  public:
422  enum : typename msg_details_t::MsgTypes_t {
423  exec_report=static_cast<typename std::underlying_type<decltype(msg_details_t::ExecutionReport::static_type)>::type>(msg_details_t::ExecutionReport::static_type),
424  business_reject=static_cast<typename std::underlying_type<decltype(msg_details_t::BusinessReject::static_type)>::type>(msg_details_t::BusinessReject::static_type),
426  };
427 
428  explicit NewOrderResponse(simulator_responses &sr) noexcept(true)
429  : sim_resp(sr) {
430  assert(dynamic_cast<simulator_responses *>(&sim_resp));
431  }
432  constexpr NewOrderResponse(NewOrderResponse const &nor) noexcept(true)
433  : sim_resp(nor.sim_resp) {}
434 
435  /**
436  From section 9.1 "Order handling" of [1]: the response to a NewOrder.
437 
438  \return The next state from the declaration of the table.
439  */
440  template<auto state, auto next, class SktT>
441  typename msg_details_t::MsgTypes_t
442  operator()(typename msg_details_t::msg_buffer_t const &buff, SktT &, SktT &client_skt) const noexcept(false) {
443  typename msg_details_t::NewOrder_t const &msg=reinterpret_cast<typename msg_details_t::NewOrder_t const &>(buff);
444  assert(msg.type()==state);
445  assert(dynamic_cast<simulator_responses *>(&sim_resp));
446  if (msg.instrumentID()==simulator_responses::instrumentID) {
447  typename msg_details_t::ExecutionReport reply(
448  sim_resp.sequenceNumber,
449  msg.clientOrderID(),
450  msg_details_t::AppID::Partition1,
451  msg_details_t::ExecType::New,
452  simulator_responses::scaled_price,
453  msg.instrumentID(),
454  msg.side()
455  );
456  if (msg.orderQty()<=simulator_responses::quantity_limit) {
457  if (msg.orderType()==msg_details_t::OrderType::Market
458  || (msg.side()==msg_details_t::Side::Buy && msg.limitPrice()<simulator_responses::scaled_price)
459  || (msg.side()==msg_details_t::Side::Sell && msg.limitPrice()>simulator_responses::scaled_price)) {
460  reply.orderStatus(msg_details_t::OrderStatus::Filled);
461  reply.executedQty(msg.orderQty());
462  reply.leavesQty(0);
463  reply.executedPrice(msg.limitPrice());
464  client_skt.write(reply);
465  } else {
466  sim_resp.order_book.emplace(msg.clientOrderID(), msg);
467  assert(!sim_resp.order_book.empty());
468  assert(sim_resp.order_book.find(msg.clientOrderID())!=sim_resp.order_book.end());
469  reply.orderStatus(msg_details_t::OrderStatus::New);
470  reply.executedQty(0);
471  reply.leavesQty(msg.orderQty());
472  reply.executedPrice(0);
473  client_skt.write(reply);
474  }
475  } else {
476  auto new_item=sim_resp.order_book.emplace(msg.clientOrderID(), msg);
477  new_item.first->second.orderQty(msg.orderQty()-simulator_responses::quantity_limit);
478  reply.orderStatus(msg_details_t::OrderStatus::Partiallyfilled);
479  reply.executedQty(simulator_responses::quantity_limit);
480  reply.leavesQty(msg.orderQty()-simulator_responses::quantity_limit);
481  assert(!sim_resp.order_book.empty());
482  assert(sim_resp.order_book.find(msg.clientOrderID())!=sim_resp.order_book.end());
483  client_skt.write(reply);
484  }
485  return msg_details_t::ExecutionReport::static_type;
486  } else {
487  typename msg_details_t::BusinessReject reply(
488  sim_resp.sequenceNumber,
489  msg.clientOrderID()
490  );
491  reply.rejectCode(msg_details_t::BusinessReject::unknown_instrument);
492  client_skt.write(reply);
493  return msg_details_t::BusinessReject::static_type;
494  }
495  }
496 
497  private:
498  simulator_responses &sim_resp;
499  };
500  using transition_table=typename msm_base_t::template rows<
501  typename row_t::template row<
502  msg_details_t::LogonRequest::static_type,
504  msg_details_t::LogonReply::static_type
505  >,
506  /**
507  From section 5.2.2: "Heartbeats" of [1]: the response to a Heartbeat is nothing.
508  */
509  typename row_t::template row<
510  msg_details_t::ClientHeartbeat::static_type,
511  typename msm_base_t::no_op,
512  msg_details_t::ServerHeartbeat::static_type
513  >,
514  typename row_t::template row<
515  msg_details_t::LogoutRequest::static_type,
517  msg_details_t::Exit
518  >,
519  typename row_t::template row<
520  msg_details_t::OrderCancelRequest::static_type,
522  OrderCancelRequestResponse::exit_types
523  >,
524  typename row_t::template row<
525  msg_details_t::OrderCancelReplaceRequest::static_type,
528  >,
529  typename row_t::template row<
530  msg_details_t::NewOrder_t::static_type,
532  NewOrderResponse::exit_types
533  >,
534  /// Reject any message that has not been recognised.
535  typename row_t::template row<
536  msg_details_t::MatchAll,
537  typename base_t::template send_reject<typename msg_details_t::Reject, true>,
538  msg_details_t::Reject::static_type
539  >
540  >;
541 };
542 
543 template<class SrcMsgDetails>
544 template<class SktT, class ClientCxn>
545 inline bool
546 simulator_responses<SrcMsgDetails>::process_msg(typename msg_details_t::msg_buffer_t const &buff, SktT &exchg_skt, ClientCxn &client_cxn) {
547  ++(this->sequenceNumber);
548  typename msg_details_t::Header_t const &hdr=reinterpret_cast<typename msg_details_t::Header_t const &>(buff);
549  const auto last_state=msm.process(hdr.type(), buff, exchg_skt, client_cxn);
550  return last_state==msg_details_t::Exit;
551 }
552 
553 template<class SrcMsgDetails> inline std::string
554 simulator_responses<SrcMsgDetails>::to_string() const noexcept(false) {
555  std::ostringstream os;
556  os
557  <<base_t::to_string()
558  <<"\n\tValid instrument ID="<<instrumentID
559  <<"\n\tInvalid instrument ID="<<invalidInstrumentID
560  <<"\n\tLogout reason: '"<<logout_reason<<"'"
561  "\n\tLogout timeout="<<logout_timeout.count()<<" sec";
562  return os.str();
563 }
564 
565 template<class SrcMsgDetails> inline std::ostream &
566 operator<<(std::ostream &os, simulator_responses<SrcMsgDetails> const &ec) noexcept(false) {
567  os<<ec.to_string();
568  return os;
569 }
570 
571 } } } } }