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 #include <functional>
20 
21 namespace isimud { namespace ISIMUD_VER_NAMESPACE { namespace exchanges { namespace BATSBOE { namespace common {
22 
23 template<class SrcMsgDetails, class DestMsgDetails>
24 struct client_to_exchange_transformations<SrcMsgDetails, DestMsgDetails>::state_machine_t : libjmmcg::msm::unroll::state_transition_table<state_machine_t> {
25  using msm_base_t=libjmmcg::msm::unroll::state_transition_table<state_machine_t>;
26  using row_t=libjmmcg::msm::unroll::row_types<typename src_msg_details_t::MsgType_t, typename dest_msg_details_t::MsgTypes_t>;
27  using transition_table=typename msm_base_t::template rows<
28  typename row_t::template row<
29  src_msg_details_t::NewOrder_t::static_type,
30  typename base_t::template convert_then_send_seq_num<typename src_msg_details_t::NewOrder_t, typename dest_msg_details_t::NewOrder_t>,
31  dest_msg_details_t::NewOrder_t::static_type
32  >,
33  typename row_t::template row<
34  src_msg_details_t::OrderCancelRequest::static_type,
35  typename base_t::template convert_then_send_seq_num<typename src_msg_details_t::OrderCancelRequest, typename dest_msg_details_t::OrderCancelRequest>,
36  dest_msg_details_t::OrderCancelRequest::static_type
37  >,
38  typename row_t::template row<
39  src_msg_details_t::OrderCancelReplace::static_type,
40  typename base_t::template convert_then_send_seq_num<typename src_msg_details_t::OrderCancelReplace, typename dest_msg_details_t::OrderCancelReplaceRequest>,
41  dest_msg_details_t::OrderCancelReplaceRequest::static_type
42  >,
43  /// Reject any message that has not been recognised.
44  typename row_t::template row<
45  src_msg_details_t::MatchAll,
46  typename base_t::template send_reject<typename src_msg_details_t::Reject, true>,
47  dest_msg_details_t::OrderRejected::static_type
48  >
49  >;
50 };
51 
52 template<class SrcMsgDetails, class DestMsgDetails>
53 template<class SktT>
54 inline bool
55 client_to_exchange_transformations<SrcMsgDetails, DestMsgDetails>::process_msg(typename src_msg_details_t::msg_buffer_t const &buff, SktT &exchg_skt, SktT &client_skt) {
56  ++sequenceNumber;
57  typename src_msg_details_t::Header_t const &hdr=reinterpret_cast<typename src_msg_details_t::Header_t const &>(buff);
58  const auto last_state=msm.process(hdr.type(), buff, exchg_skt, client_skt);
59  return last_state==dest_msg_details_t::Exit;
60 }
61 
62 template<class SrcMsgDetails, class DestMsgDetails> inline std::string
63 client_to_exchange_transformations<SrcMsgDetails, DestMsgDetails>::to_string() const noexcept(false) {
64  std::ostringstream ss;
65  ss
66  <<"\n\tCurrent sequence number="<<sequenceNumber
67  <<"Source message details: ";
68  SrcMsgDetails::to_stream(ss);
69  ss<<"\n\tDestination message details: ";
70  DestMsgDetails::to_stream(ss);
71  return ss.str();
72 }
73 
74 template<class SrcMsgDetails, class DestMsgDetails> inline std::ostream &
75 operator<<(std::ostream &os, client_to_exchange_transformations<SrcMsgDetails, DestMsgDetails> const &ec) noexcept(false) {
76  os<<ec.to_string();
77  return os;
78 }
79 
80 template<class SrcMsgDetails, class DestMsgDetails>
81 struct exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails>::business_state_machine_t : libjmmcg::msm::unroll::state_transition_table<business_state_machine_t> {
82  using msm_base_t=libjmmcg::msm::unroll::state_transition_table<business_state_machine_t>;
83  using row_t=libjmmcg::msm::unroll::row_types<typename src_msg_details_t::MsgTypes_t, typename dest_msg_details_t::MsgType_t>;
84  using transition_table=typename msm_base_t::template rows<
85  typename row_t::template row<
86  src_msg_details_t::OrderRejected::static_type,
87  typename base_t::template convert_then_send<typename src_msg_details_t::OrderRejected, typename dest_msg_details_t::OrderRejected>,
88  dest_msg_details_t::OrderRejected::static_type
89  >,
90  typename row_t::template row<
91  src_msg_details_t::OrderCancelReject::static_type,
92  typename base_t::template convert_then_send<typename src_msg_details_t::OrderCancelReject, typename dest_msg_details_t::CancelRejected>,
93  dest_msg_details_t::CancelRejected::static_type
94  >,
95  typename row_t::template row<
96  src_msg_details_t::UserModifyRejected::static_type,
97  typename base_t::template convert_then_send<typename src_msg_details_t::UserModifyRejected, typename dest_msg_details_t::BusinessMessageReject>,
98  dest_msg_details_t::BusinessMessageReject::static_type
99  >,
100  typename row_t::template row<
101  src_msg_details_t::OrderCancelled::static_type,
102  typename base_t::template convert_then_send<typename src_msg_details_t::OrderCancelled, typename dest_msg_details_t::ExecutionReport>,
103  dest_msg_details_t::ExecutionReport::static_type
104  >,
105  typename row_t::template row<
106  src_msg_details_t::ExecutionReport::static_type,
107  typename base_t::template convert_then_send<typename src_msg_details_t::ExecutionReport, typename dest_msg_details_t::ExecutionReport>,
108  dest_msg_details_t::ExecutionReport::static_type
109  >,
110  /// Reject any message that has not been recognised.
111  typename row_t::template row<
112  src_msg_details_t::MatchAll,
113  typename base_t::template send_reject<typename dest_msg_details_t::Reject, true>,
114  dest_msg_details_t::Reject::static_type
115  >
116  >;
117 };
118 
119 template<class SrcMsgDetails, class DestMsgDetails>
120 struct exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails>::admin_state_machine_t : libjmmcg::msm::unroll::state_transition_table<admin_state_machine_t> {
121  using msm_base_t=libjmmcg::msm::unroll::state_transition_table<admin_state_machine_t>;
122  using row_t=libjmmcg::msm::unroll::row_types<typename src_msg_details_t::MsgTypes_t, typename dest_msg_details_t::MsgType_t>;
123  using transition_table=typename msm_base_t::template rows<
124  /**
125  From section 2.4: "Heartbeats" of [1]: the response to a server Heartbeat is a Heartbeat.
126 
127  \param msg The message that was received, that shall be processed.
128  \param exchg_skt The socket to which any responses should be written.
129  \return False to continue processing messages, true otherwise.
130  */
131  typename row_t::template row<
132  src_msg_details_t::ServerHeartbeat::static_type,
133  typename base_t::template convert_then_send_seq_num<typename src_msg_details_t::ClientHeartbeat, typename src_msg_details_t::ClientHeartbeat, false>,
134  dest_msg_details_t::ClientHeartbeat::static_type
135  >,
136  /**
137  From section 2.5: "Logging Out" of [1]: the response to a Logout is to stop processing.
138 
139  \param msg The message that was received, that shall be processed.
140  \param client_skt The socket to which any responses should be written.
141  \return False to continue processing messages, true otherwise.
142  */
143  typename row_t::template row<
144  src_msg_details_t::Logout::static_type,
145  typename msm_base_t::no_op, // TODO
146  dest_msg_details_t::Exit
147  >,
148  /// Reject any message that has not been recognised.
149  typename row_t::template row<
150  src_msg_details_t::MatchAll,
151  typename base_t::template match_all_response<business_machine>,
152  dest_msg_details_t::Reject::static_type
153  >
154  >;
155 };
156 
157 template<class SrcMsgDetails, class DestMsgDetails>
158 template<class SktT, class ClientCxn>
159 inline bool
160 exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails>::process_msg(typename src_msg_details_t::msg_buffer_t const &buff, SktT &exchg_skt, ClientCxn &client_cxn) {
161  ++sequenceNumber;
162  typename src_msg_details_t::Header_t const &hdr=reinterpret_cast<typename src_msg_details_t::Header_t const &>(buff);
163  const auto last_state=admin_msm.process(static_cast<typename src_msg_details_t::MsgTypes_t>(hdr.type()), buff, exchg_skt, client_cxn);
164  return last_state==dest_msg_details_t::Exit;
165 }
166 
167 template<class SrcMsgDetails, class DestMsgDetails> inline std::string
168 exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails>::to_string() const noexcept(false) {
169  std::ostringstream os;
170  os<<"Source message details: ";
171  SrcMsgDetails::to_stream(os);
172  os<<"\n\tDestination message details: ";
173  DestMsgDetails::to_stream(os);
174  return os.str();
175 }
176 
177 template<class SrcMsgDetails, class DestMsgDetails> inline std::ostream &
178 operator<<(std::ostream &os, exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails> const &ec) noexcept(false) {
179  os<<ec.to_string();
180  return os;
181 }
182 
183 template<class SrcMsgDetails>
184 struct simulator_responses<SrcMsgDetails>::state_machine_t : libjmmcg::msm::unroll::state_transition_table<state_machine_t> {
185  using msm_base_t=libjmmcg::msm::unroll::state_transition_table<state_machine_t>;
186  using row_t=libjmmcg::msm::unroll::row_types<typename msg_details_t::MsgTypes_t, typename msg_details_t::MsgTypes_t>;
188  public:
189  explicit LogonRequestResponse(simulator_responses &sr) noexcept(true)
190  : sim_resp(sr) {}
191  constexpr LogonRequestResponse(LogonRequestResponse const &v) noexcept(true)
192  : sim_resp(v.sim_resp) {}
193 
194  /**
195  From section 5.1: "Establishing a connection" of [1]: the response to a LogonRequest is a LogonReply.
196 
197  \return The next state from the declaration of the table.
198  */
199  template<auto state, auto next, class SktT>
200  auto operator()(typename msg_details_t::msg_buffer_t const &buff, SktT &, SktT &client_skt) const noexcept(false) {
201  typename msg_details_t::LogonRequest const &msg=reinterpret_cast<typename msg_details_t::LogonRequest const &>(buff);
202  assert(msg.type()==static_cast<typename msg_details_t::MsgType_t>(state));
203  typename msg_details_t::LogonReply reply(sim_resp.sequenceNumber);
204  if (std::strcmp(msg.userName.begin(), sim_resp.username.begin())==0) {
205  if (std::strcmp(msg.password.begin(), sim_resp.password.begin())==0) {
206  reply.rejectCode(msg_details_t::LoginResponseStatus::LoginAccepted);
207  client_skt.write(reply);
208  } else {
209  reply.rejectCode(msg_details_t::LoginResponseStatus::NotAuthorized);
210  client_skt.write(reply);
211  }
212  } else {
213  reply.rejectCode(msg_details_t::LoginResponseStatus::NotAuthorized);
214  client_skt.write(reply);
215  }
216  return next;
217  }
218 
219  private:
220  simulator_responses &sim_resp;
221  };
223  public:
224  explicit LogoutRequestResponse(simulator_responses &sr) noexcept(true)
225  : sim_resp(sr) {}
226  constexpr LogoutRequestResponse(LogoutRequestResponse const &v) noexcept(true)
227  : sim_resp(v.sim_resp) {}
228 
229  /**
230  From section 5.3: "Terminating a connection" of [1]: the response to a Logout is a Logout.
231 
232  \return The next state from the declaration of the table.
233  */
234  template<auto state, auto next, class SktT>
235  auto operator()(typename msg_details_t::msg_buffer_t const &, SktT &, SktT &client_skt) const noexcept(false) {
236  const typename msg_details_t::Logout reply(sim_resp.sequenceNumber, LogoutReason::UserRequested);
237  client_skt.write(reply);
238  return next;
239  }
240 
241  private:
242  simulator_responses &sim_resp;
243  };
245  public:
246  explicit OrderCancelRequestResponse(simulator_responses &sr) noexcept(true)
247  : sim_resp(sr) {}
248  constexpr OrderCancelRequestResponse(OrderCancelRequestResponse const &v) noexcept(true)
249  : sim_resp(v.sim_resp) {}
250 
251  /**
252  From section 9.1 "Order handling" of [1]: the response to a OrderCancelRequest.
253 
254  \return The next state from the declaration of the table.
255  */
256  template<auto state, auto, class SktT>
257  typename msg_details_t::MsgTypes_t
258  operator()(typename msg_details_t::msg_buffer_t const &buff, SktT &, SktT &client_skt) const noexcept(false) {
259  typename msg_details_t::OrderCancelRequest const &msg=reinterpret_cast<typename msg_details_t::OrderCancelRequest const &>(buff);
260  assert(static_cast<typename msg_details_t::MsgTypes_t>(msg.type())==static_cast<typename msg_details_t::MsgTypes_t>(state));
261  assert(!std::string(msg.originalClientOrderID().data()).empty());
262  auto cancelled_order=sim_resp.order_book.find(msg.originalClientOrderID());
263  if (cancelled_order!=sim_resp.order_book.end()) {
264  const typename msg_details_t::OrderCancelled reply(
265  sim_resp.sequenceNumber,
266  msg.originalClientOrderID(),
267  msg_details_t::OrderRejectReason::UserRequested,
268  cancelled_order->second.limitPrice(),
269  cancelled_order->second.side(),
270  0,
271  cancelled_order->second.orderQty()
272  );
273  sim_resp.order_book.erase(msg.originalClientOrderID());
274  client_skt.write(reply);
275  return msg_details_t::OrderCancelled::static_type;
276  } else {
277  const typename msg_details_t::OrderCancelReject reply(sim_resp.sequenceNumber, msg.originalClientOrderID(), msg_details_t::OrderRejectReason::ClOrdIDNotMatchKnownOrder);
278  client_skt.write(reply);
279  return msg_details_t::OrderCancelReject::static_type;
280  }
281  }
282 
283  private:
284  simulator_responses &sim_resp;
285  };
287  public:
288  explicit OrderCancelReplaceRequestResponse(simulator_responses &sr) noexcept(true)
289  : sim_resp(sr) {}
291  : sim_resp(v.sim_resp) {}
292 
293  /**
294  From section 9.1 "Order handling" of [1]: the response to a OrderCancelReplaceRequest.
295 
296  \return The next state from the declaration of the table.
297  */
298  template<auto state, auto, class SktT>
299  typename msg_details_t::MsgTypes_t
300  operator()(typename msg_details_t::msg_buffer_t const &buff, SktT &, SktT &client_skt) const noexcept(false) {
301  typename msg_details_t::OrderCancelReplaceRequest const &msg=reinterpret_cast<typename msg_details_t::OrderCancelReplaceRequest const &>(buff);
302  assert(static_cast<typename msg_details_t::MsgTypes_t>(msg.type())==static_cast<typename msg_details_t::MsgTypes_t>(state));
303  auto modified_order=sim_resp.order_book.find(msg.originalClientOrderID());
304  if (modified_order!=sim_resp.order_book.end()) {
305  modified_order->second.limitPrice(msg.limitPrice());
306  modified_order->second.orderQty(msg.orderQty());
307  modified_order->second.side(msg.side());
308  const typename msg_details_t::OrderModified reply(
309  sim_resp.sequenceNumber,
310  msg.originalClientOrderID(),
311  modified_order->second.limitPrice(),
312  modified_order->second.side(),
313  modified_order->second.orderQty()
314  );
315  client_skt.write(reply);
316  return msg_details_t::OrderModified::static_type;
317  } else {
318  const typename msg_details_t::UserModifyRejected reply(sim_resp.sequenceNumber, msg.originalClientOrderID(), OrderRejectReason::ClOrdIDNotMatchKnownOrder);
319  client_skt.write(reply);
320  return msg_details_t::UserModifyRejected::static_type;
321  }
322  }
323 
324  private:
325  simulator_responses &sim_resp;
326  };
328  public:
329  explicit NewOrderResponse(simulator_responses &sr) noexcept(true)
330  : sim_resp(sr) {}
331  constexpr NewOrderResponse(NewOrderResponse const &v) noexcept(true)
332  : sim_resp(v.sim_resp) {}
333 
334  /**
335  From section 9.1 "Order handling" of [1]: the response to a NewOrder.
336 
337  \return The next state from the declaration of the table.
338  */
339  template<auto state, auto, class SktT>
340  typename msg_details_t::MsgTypes_t
341  operator()(typename msg_details_t::msg_buffer_t const &buff, SktT &, SktT &client_skt) const noexcept(false) {
342  typename msg_details_t::NewOrder_t const &msg=reinterpret_cast<typename msg_details_t::NewOrder_t const &>(buff);
343  assert(static_cast<typename msg_details_t::MsgTypes_t>(msg.type())==static_cast<typename msg_details_t::MsgTypes_t>(state));
344 
345  if (msg.instrumentID()==sim_resp.instrumentID) {
346  typename msg_details_t::ExecutionReport reply(
347  sim_resp.sequenceNumber,
348  msg.clientOrderID(),
349  sim_resp.scaled_price,
350  msg.instrumentID(),
351  msg.side()
352  );
353  if (msg.orderQty()<=sim_resp.quantity_limit) {
354  if (msg.orderType()==msg_details_t::OrderType::Market
355  || (msg.side()==msg_details_t::Side::Buy && msg.limitPrice()<sim_resp.scaled_price)
356  || (msg.side()==msg_details_t::Side::Sell && msg.limitPrice()>sim_resp.scaled_price)) {
357  reply.executedQty(msg.orderQty());
358  reply.leavesQty(0);
359  reply.executedPrice(msg.limitPrice());
360  client_skt.write(reply);
361  } else {
362  sim_resp.order_book.emplace(msg.clientOrderID(), msg);
363  assert(!sim_resp.order_book.empty());
364  assert(sim_resp.order_book.find(msg.clientOrderID())!=sim_resp.order_book.end());
365  }
366  } else {
367  auto new_item=sim_resp.order_book.emplace(msg.clientOrderID(), msg);
368  new_item.first->second.orderQty(msg.orderQty()-sim_resp.quantity_limit);
369  reply.executedQty(sim_resp.quantity_limit);
370  reply.leavesQty(msg.orderQty()-sim_resp.quantity_limit);
371  assert(!sim_resp.order_book.empty());
372  assert(sim_resp.order_book.find(msg.clientOrderID())!=sim_resp.order_book.end());
373  client_skt.write(reply);
374  }
375  return msg_details_t::ExecutionReport::static_type;
376  } else {
377  const typename msg_details_t::OrderRejected reply(sim_resp.sequenceNumber, msg.clientOrderID(), msg_details_t::OrderRejectReason::SymbolNotSupported);
378  client_skt.write(reply);
379  return msg_details_t::OrderRejected::static_type;
380  }
381  }
382 
383  private:
384  simulator_responses &sim_resp;
385  };
387  public:
388  explicit RejectUnhandledResponse(simulator_responses &sr) noexcept(true)
389  : sim_resp(sr) {}
390  constexpr RejectUnhandledResponse(RejectUnhandledResponse const &v) noexcept(true)
391  : sim_resp(v.sim_resp) {}
392 
393  /**
394  \param client_skt The socket to which any responses should be written.
395  \return The next state from the declaration of the table.
396  */
397  template<auto state, auto next, class SktT>
398  decltype(next)
399  operator()(typename msg_details_t::msg_buffer_t const &, SktT &, SktT &client_skt) const {
400  const typename msg_details_t::OrderRejected response(sim_resp.sequenceNumber, typename msg_details_t::ClientOrderID_t(), msg_details_t::OrderRejected::unknown_msg);
401  client_skt.write(response);
402  return next;
403  }
404 
405  private:
406  simulator_responses &sim_resp;
407  };
408  using transition_table=typename msm_base_t::template rows<
409  typename row_t::template row<
410  msg_details_t::LogonRequest::static_type,
412  msg_details_t::LogonReply::static_type
413  >,
414  /**
415  From section 2.4: "Heartbeats" of [1]: the response to a Heartbeat is nothing.
416  */
417  typename row_t::template row<
418  msg_details_t::ClientHeartbeat::static_type,
419  typename msm_base_t::no_op,
420  msg_details_t::ServerHeartbeat::static_type
421  >,
422  typename row_t::template row<
423  msg_details_t::LogoutRequest::static_type,
425  msg_details_t::Exit
426  >,
427  typename row_t::template row<
428  msg_details_t::OrderCancelRequest::static_type,
430  msg_details_t::OrderCancelled::static_type|msg_details_t::OrderCancelReject::static_type
431  >,
432  typename row_t::template row<
433  msg_details_t::OrderCancelReplaceRequest::static_type,
435  msg_details_t::OrderModified::static_type|msg_details_t::UserModifyRejected::static_type
436  >,
437  typename row_t::template row<
438  msg_details_t::NewOrder_t::static_type,
440  msg_details_t::ExecutionReport::static_type|msg_details_t::OrderRejected::static_type
441  >,
442  /// Reject any message that has not been recognised.
443  typename row_t::template row<
444  msg_details_t::MatchAll,
446  msg_details_t::OrderRejected::static_type
447  >
448  >;
449 };
450 
451 template<class SrcMsgDetails>
452 template<class SktT, class ClientCxn>
453 inline bool
454 simulator_responses<SrcMsgDetails>::process_msg(typename msg_details_t::msg_buffer_t const &buff, SktT &exchg_skt, ClientCxn &client_cxn) {
455  ++(this->sequenceNumber);
456  typename msg_details_t::Header_t const &hdr=reinterpret_cast<typename msg_details_t::Header_t const &>(buff);
457  const auto last_state=msm.process(static_cast<typename msg_details_t::MsgTypes_t>(hdr.type()), buff, exchg_skt, client_cxn);
458  return last_state==msg_details_t::Exit;
459 }
460 
461 template<class SrcMsgDetails> inline std::string
462 simulator_responses<SrcMsgDetails>::to_string() const noexcept(false) {
463  std::ostringstream os;
464  os
465  <<base_t::to_string()
466  <<"\n\tValid instrument ID="<<instrumentID
467  <<"\n\tInvalid instrument ID="<<invalidInstrumentID;
468  return os.str();
469 }
470 
471 template<class SrcMsgDetails> inline std::ostream &
472 operator<<(std::ostream &os, simulator_responses<SrcMsgDetails> const &ec) noexcept(false) {
473  os<<ec.to_string();
474  return os;
475 }
476 
477 } } } } }