libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
processing_rules.hpp
Go to the documentation of this file.
1 #ifndef ISIMUD_EXCHANGES_COMMON_PROCESSING_RULES_HPP
2 #define ISIMUD_EXCHANGES_COMMON_PROCESSING_RULES_HPP
3 
4 /******************************************************************************
5 ** Copyright © 2015 by J.M.McGuiness, isimud@hussar.me.uk
6 **
7 ** This library is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU Lesser General Public
9 ** License as published by the Free Software Foundation; either
10 ** version 2.1 of the License, or (at your option) any later version.
11 **
12 ** This library is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ** Lesser General Public License for more details.
16 **
17 ** You should have received a copy of the GNU Lesser General Public
18 ** License along with this library; if not, write to the Free Software
19 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 
22 #include <boost/mpl/front.hpp>
23 #include <boost/mpl/pop_front.hpp>
24 
25 #include <map>
26 
27 namespace isimud { namespace ISIMUD_VER_NAMESPACE { namespace exchanges { namespace common {
28 
29 /// A simple, MIT-protocol exchange simulator.
30 /**
31  The behaviour of this simulator is a simplification derived from the specification in [1].
32  [1] "MIT203 - MILLENNIUM EXCHANGE Native Trading Gateway"
33 */
34 template<class SrcMsgDetails, class DestMsgDetails, class Derived, class FlowMsgTypes>
36 public:
37  /// A type containing the details of the messages that will be received from the client.
38  using src_msg_details_t=SrcMsgDetails;
39  /// A type containing the details of the messages that will be sent to the exchange.
40  using dest_msg_details_t=DestMsgDetails;
41  using derived_t=Derived;
42  using flow_msg_types=FlowMsgTypes;
43 
44 protected:
45  /// Just default-construct the specified message-type and send it to the exchange socket.
46  template<class OutMsg>
48  using dest_msg_t=OutMsg;
49 
50  template<class ...Args>
51  explicit constexpr just_send_to_exchg(Args &&...) noexcept(true) {}
52 
53  /**
54  \param exchg_skt The socket to which any responses should be written.
55  \return The next state from the declaration of the table.
56  */
57  template<auto state, auto next, class SktT, class ClientCxn>
58  decltype(next)
59  operator()(typename src_msg_details_t::msg_buffer_t const &, SktT &exchg_skt, ClientCxn &) const {
60  exchg_skt.write(dest_msg_t());
61  return next;
62  }
63  };
64  /// Construct the specified output message-type from the input message and send it to the client socket.
65  template<class InMsg, class OutMsg>
67  using src_msg_t=InMsg;
68  using dest_msg_t=OutMsg;
69 
70  template<class ...Args>
71  explicit constexpr convert_then_send(Args &&...) noexcept(true) {}
72 
73  /**
74  \param buff The message that was received, that shall be processed.
75  \param client_skt The socket to which any responses should be written.
76  \return The next state from the declaration of the table.
77  */
78  template<auto state, auto next, class SktT, class ClientCxn>
79  decltype(next)
80  operator()(typename src_msg_details_t::msg_buffer_t const &buff, SktT &, ClientCxn &client_cxn) const {
81  assert(client_cxn);
82  src_msg_t const &msg=reinterpret_cast<src_msg_t const &>(buff);
83  assert(static_cast<typename src_msg_details_t::MsgTypes_t>(msg.type())==static_cast<typename src_msg_details_t::MsgTypes_t>(state));
84  client_cxn->socket().write(dest_msg_t(msg));
85  return next;
86  }
87  };
88  /// Construct the specified output message-type from the input message plus reference data and send it to the client socket.
89  template<class InMsg, class OutMsg>
91  public:
92  using src_msg_t=InMsg;
93  using dest_msg_t=OutMsg;
94  using ref_data=typename dest_msg_details_t::ref_data;
95 
96  explicit convert_then_send_ref_data(ref_data const &rd) noexcept(true)
97  : ref_data_(rd) {}
98  constexpr convert_then_send_ref_data(convert_then_send_ref_data const &v) noexcept(true)
99  : ref_data_(v.ref_data_) {}
100 
101  /**
102  \param buff The message that was received, that shall be processed.
103  \param client_skt The socket to which any responses should be written.
104  \return The next state from the declaration of the table.
105  */
106  template<auto state, auto next, class SktT, class ClientCxn>
107  decltype(next)
108  operator()(typename src_msg_details_t::msg_buffer_t const &buff, SktT &, ClientCxn &client_skt) const {
109  src_msg_t const &msg=reinterpret_cast<src_msg_t const &>(buff);
110  assert(msg.type()==state);
111  client_skt.write(dest_msg_t(msg, ref_data_));
112  return next;
113  }
114 
115  private:
116  ref_data const &ref_data_;
117  };
118  /// Construct the specified output message-type from the input message plus reference data and send it to the client socket.
119  template<class InMsg, class OutMsg, bool ToClient=true>
121  public:
122  using src_msg_t=InMsg;
123  using dest_msg_t=OutMsg;
124 
125  explicit convert_then_send_seq_num(typename dest_msg_details_t::SeqNum_t &sq) noexcept(true)
126  : sequenceNumber(sq) {}
127  template<class MSM>
128  explicit convert_then_send_seq_num(std::tuple<typename dest_msg_details_t::SeqNum_t &, MSM> a) noexcept(true)
129  : sequenceNumber(std::get<0>(a)) {}
130  constexpr convert_then_send_seq_num(convert_then_send_seq_num const &v) noexcept(true)
131  : sequenceNumber(v.sequenceNumber) {}
132 
133  /**
134  \param buff The message that was received, that shall be processed.
135  \param client_skt The socket to which any responses should be written.
136  \return The next state from the declaration of the table.
137  */
138  template<auto state, auto next, class SktT>
139  decltype(next)
140  operator()(typename src_msg_details_t::msg_buffer_t const &buff, SktT &exchg_skt, SktT &client_skt) const {
141  src_msg_t const &msg=reinterpret_cast<src_msg_t const &>(buff);
142  assert(static_cast<typename src_msg_details_t::MsgTypes_t>(msg.type())==static_cast<typename src_msg_details_t::MsgTypes_t>(state));
143  if constexpr (ToClient) {
144  client_skt.write(dest_msg_t(msg, sequenceNumber));
145  } else {
146  exchg_skt.write(dest_msg_t(sequenceNumber));
147  }
148  return next;
149  }
150  /**
151  \param buff The message that was received, that shall be processed.
152  \param client_skt The socket to which any responses should be written.
153  \return The next state from the declaration of the table.
154  */
155  template<auto state, auto next, class SktT, class ClientCxn>
156  decltype(next)
157  operator()(typename src_msg_details_t::msg_buffer_t const &buff, SktT &exchg_skt, ClientCxn &client_cxn) const {
158  src_msg_t const &msg=reinterpret_cast<src_msg_t const &>(buff);
159  assert(static_cast<typename src_msg_details_t::MsgTypes_t>(msg.type())==static_cast<typename src_msg_details_t::MsgTypes_t>(state));
160  if constexpr (ToClient) {
161  assert(client_cxn);
162  client_cxn.socket().write(dest_msg_t(msg, sequenceNumber));
163  } else {
164  exchg_skt.write(dest_msg_t(sequenceNumber));
165  }
166  return next;
167  }
168 
169  private:
170  typename dest_msg_details_t::SeqNum_t &sequenceNumber;
171  };
172  template<class OutMsg, bool ToClient>
173  struct send_reject {
174  using dest_msg_t=OutMsg;
175 
176  template<class ...Args>
177  explicit constexpr send_reject(Args &&...) noexcept(true) {}
178 
179  /**
180  \param client_skt The socket to which any responses should be written.
181  \return The next state from the declaration of the table.
182  */
183  template<auto state, auto next, class SktT>
184  decltype(next)
185  operator()(typename src_msg_details_t::msg_buffer_t const &, SktT &exchg_skt, SktT &client_skt) const {
186  if constexpr (ToClient) {
187  client_skt.write(dest_msg_t(dest_msg_t::unknown_msg));
188  } else {
189  exchg_skt.write(dest_msg_t(dest_msg_t::unknown_msg));
190  }
191  return next;
192  }
193  template<auto state, auto next, class SktT, class ClientCxn>
194  decltype(next)
195  operator()(typename src_msg_details_t::msg_buffer_t const &buff, SktT &exchg_skt, ClientCxn &client_cxn) const {
196  return operator()<state, next>(buff, exchg_skt, client_cxn->socket());
197  }
198  };
199  template<class MSM>
201  public:
202  explicit constexpr match_all_response(MSM const &msm) noexcept(true)
203  : business_msm(msm) {}
204  explicit constexpr match_all_response(std::tuple<typename dest_msg_details_t::SeqNum_t &, MSM> a) noexcept(true)
205  : business_msm(std::get<1>(a)) {}
206  constexpr match_all_response(match_all_response const &) noexcept(true)=default;
207 
208  /**
209  \return False to continue processing messages, true otherwise.
210  */
211  template<auto state, auto next, class SktT, class ClientCxn>
212  auto operator()(typename src_msg_details_t::msg_buffer_t const &buff, SktT &exchg_skt, ClientCxn &client_cxn) const noexcept(false) {
213  // We have a message for a client. But the connection info has not got to us yet, so wait for it. We are guaranteed to get a client, because only messages from clients can cause messages to be sent to clients. (Unless there is an order that has been cancelled by a business reject & that was held overnight and cancelled before a client connected. >24hr holding periods for HFT are unknown, so ignore this eventuality for now...)
214  while (!client_cxn) {
215  std::this_thread::yield();
216  }
217  assert(client_cxn->socket().is_open());
218  typename src_msg_details_t::Header_t const &hdr=reinterpret_cast<typename src_msg_details_t::Header_t const &>(buff);
219  const auto next_from_business=business_msm.process(static_cast<typename src_msg_details_t::MsgTypes_t>(hdr.type()), buff, exchg_skt, client_cxn);
220  return next_from_business;
221  }
222 
223  private:
224  MSM const &business_msm;
225  };
226 };
227 
228 /// A simple, MIT-protocol exchange simulator.
229 /**
230  This server is single-threaded, and permits only one client connection at a time.
231  The behaviour of this simulator is a simplification derived from the specification in [1].
232  [1] "MIT203 - MILLENNIUM EXCHANGE Native Trading Gateway"
233 */
234 template<class MsgDetails, class Derived>
235 class simulator_responses : public message_responses<MsgDetails, MsgDetails, Derived, typename MsgDetails::client_to_exchange_messages_t> {
236 public:
237  using base_t=message_responses<MsgDetails, MsgDetails, Derived, typename MsgDetails::client_to_exchange_messages_t>;
238  using msg_details_t=typename base_t::src_msg_details_t;
239  using src_msg_details_t=msg_details_t;
240  using derived_t=typename base_t::derived_t;
241  using base_t::convert_then_send;
242  using base_t::convert_then_send_ref_data;
243  using base_t::send_reject;
244  using Price_t=typename std::conditional<std::is_integral<typename msg_details_t::Price_t>::value, typename msg_details_t::Price_t, std::uint64_t>::type;
245 
246  /// The only valid username.
247  static inline constexpr typename msg_details_t::UserName_t username{"usr"};
248  /// The only valid password.
249  static inline constexpr typename msg_details_t::Password_t password{"fubar"};
250  /// The only valid new password.
251  static inline constexpr typename msg_details_t::Password_t new_password{"snafu"};
252  /// The magic quantity value.
253  /**
254  A value less than this will result in a full-filled ExecutionReport, otherwise a partial fill ExecutionReport will be returned, and the remaining quantity left "on the market".
255  */
256  static inline constexpr std::int32_t quantity_limit=100;
257  /// For limit orders, this is the price at which the order will be traded, otherwise it will be left "on the market".
258  static inline constexpr Price_t price=42;
259  /// For limit orders, this is the native, scaled price at which the order will be traded, otherwise it will be left "on the market", which is used within the simulator.
260  static inline constexpr Price_t scaled_price=42*msg_details_t::implied_decimal_places;
261  BOOST_MPL_ASSERT_RELATION(scaled_price, >, 0);
262 
263  std::string to_string() const noexcept(false);
264 
265 protected:
268  std::int32_t sequenceNumber{};
269 
272 };
273 
274 template<class MsgDetails, class Derived> inline std::ostream &
275 operator<<(std::ostream &os, simulator_responses<MsgDetails, Derived> const &ec) noexcept(false);
276 
277 } } } }
278 
280 
281 #endif