CBOE Emulator  1.0
market_maker.hpp
1 // A market maker trading agent.
2 // Copyright 2020 Christian Kauten
3 //
4 // Author: Christian Kauten (kautenja@auburn.edu)
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 //
17 // Reference: High frequency trading strategies, market fragility and price
18 // spikes: an agent based model perspective
19 // Reference: An Agent-Based Model for Market Impact
20 //
21 
22 #ifndef STRATEGIES_MARKET_MAKER_HPP
23 #define STRATEGIES_MARKET_MAKER_HPP
24 
25 #include "data_feed/receiver.hpp"
26 #include "order_entry/client.hpp"
27 #include "maths/probability.hpp"
28 #include "maths/exponential_moving_average.hpp"
29 #include <nlohmann/json.hpp>
30 #include <atomic>
31 #include <iostream>
32 
34 namespace Strategies {
35 
41 class MarketMaker {
42  private:
46  DataFeedReceiver receiver;
48  OrderEntry::Client client;
50  asio::steady_timer timer;
52  uint32_t sleep_time;
54  float P_act;
56  std::atomic<bool> is_running;
57 
59  OrderEntry::Quantity minimum_size;
61  OrderEntry::Quantity maximum_size;
63  OrderEntry::Quantity hedge_size;
65  double decision_boundary;
66 
69 
71  inline void start_strategy() {
72  timer.expires_after(std::chrono::milliseconds(sleep_time));
73  timer.async_wait([this](const std::error_code& error) {
74  if (error) { // TODO: handle error code
75  std::cout << "MarketMaker::start_strategy - " << error << std::endl;
76  return;
77  }
78  if (Maths::Probability::boolean(P_act)) do_strategy();
79  start_strategy();
80  });
81  }
82 
84  void do_strategy() {
85  if (client.has_active_order()) // cancel existing orders
87  // compare the average to the decision boundary
88  if (trade_side.get_average() > decision_boundary) { // buy order likely
89  // place sell
91  receiver.get_book().last_best_sell(),
92  Maths::Probability::uniform_int(minimum_size, maximum_size),
93  OrderEntry::Side::Sell
94  );
95  // hedge buy
97  receiver.get_book().last_best_buy(),
98  hedge_size,
99  OrderEntry::Side::Buy
100  );
101  } else if (trade_side.get_average() < -decision_boundary) { // sell order likely
102  // place buy
104  receiver.get_book().last_best_buy(),
105  Maths::Probability::uniform_int(minimum_size, maximum_size),
106  OrderEntry::Side::Buy
107  );
108  // hedge sell
110  receiver.get_book().last_best_sell(),
111  hedge_size,
112  OrderEntry::Side::Sell
113  );
114  }
115  }
116 
117  public:
125  asio::io_context& feed_context,
126  asio::io_context& context,
127  nlohmann::json options
128  ) :
129  receiver(
130  feed_context,
131  asio::ip::make_address(options["data_feed"]["listen"].get<std::string>()),
132  asio::ip::make_address(options["data_feed"]["group"].get<std::string>()),
133  options["data_feed"]["port"].get<uint16_t>(),
134  *this
135  ),
136  client(
137  context,
138  options["order_entry"]["host"].get<std::string>(),
139  std::to_string(options["order_entry"]["port"].get<uint16_t>())
140  ),
141  timer(context),
142  sleep_time(options["strategy"]["sleep_time"].get<uint32_t>()),
143  P_act(options["strategy"]["P_act"].get<double>()),
144  is_running(false),
145  minimum_size(options["strategy"]["minimum_size"].get<OrderEntry::Quantity>()),
146  maximum_size(options["strategy"]["maximum_size"].get<OrderEntry::Quantity>()),
147  hedge_size(options["strategy"]["hedge_size"].get<OrderEntry::Quantity>()),
148  decision_boundary(options["strategy"]["decision_boundary"].get<double>()),
149  trade_side(options["strategy"]["weight"].get<double>(), options["strategy"]["average"].get<double>()) {
150  // login to the order entry client
151  auto username = OrderEntry::make_username(options["order_entry"]["username"].get<std::string>());
152  auto password = OrderEntry::make_password(options["order_entry"]["password"].get<std::string>());
153  client.send<OrderEntry::Messages::LoginRequest>(username, password);
154  }
155 
161  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::StartOfSession& message) {
162  if (is_running) {
163  std::cerr << "received start of session when already running" << std::endl;
164  return;
165  }
166  // clear the average trade direction
167  trade_side.reset(Maths::Probability::uniform_real(-1.0, 1.0));
168  // start the strategy
169  start_strategy();
170  // set the running flag to true
171  is_running = true;
172  }
173 
179  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::EndOfSession& message) {
180  if (not is_running) {
181  std::cerr << "received end of session when not running" << std::endl;
182  return;
183  }
184  // stop the strategy
185  timer.cancel();
186  is_running = false;
187  }
188 
194  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::Clear& message) { }
195 
201  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::AddOrder& message) { }
202 
208  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::DeleteOrder& message) { }
209 
215  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::Trade& message) {
216  trade_side.process(DataFeed::side_to_double(message.side));
217  }
218 };
219 
220 } // namespace Strategies
221 
222 #endif // STRATEGIES_MARKET_MAKER_HPP
Maths::ExponentialMovingAverage::process
void process(T observation)
Calculate the next average based on observation .
Definition: exponential_moving_average.hpp:92
OrderEntry
Logic for sending/receiving application messages in a financial market.
Definition: authorizer.hpp:26
Maths::Probability::uniform_real
T uniform_real(T min, T max)
Sample a number from a real-valued uniform distribution.
Definition: probability.hpp:61
DataFeed::Receiver< MarketMaker >
OrderEntry::Messages::LoginRequest
A request that indicates a client is attempting to create a new session.
Definition: messages.hpp:248
Strategies::MarketMaker::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::Clear &message)
Handle a clear book message.
Definition: market_maker.hpp:194
OrderEntry::make_password
Password make_password(std::string password)
Make a password from the given input string.
Definition: messages.hpp:62
DataFeed::Messages::Trade
A message that indicates a market order matches with a limit order.
Definition: messages.hpp:387
OrderEntry::Client::has_active_order
bool has_active_order() const
Return true if the client has an active order.
Definition: client.hpp:267
OrderEntry::Client::send
void send(Args &&...args)
Write a message asynchronously (non-blocking).
Definition: client.hpp:299
DataFeed::LOB::LimitOrderBook::last_best_sell
Price last_best_sell() const
Return the last best sell price.
Definition: limit_order_book.hpp:262
Strategies::MarketMaker
The market maker strategy logic.
Definition: market_maker.hpp:41
Strategies::MarketMaker::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::EndOfSession &message)
Handle an end of session message.
Definition: market_maker.hpp:179
DataFeed::Messages::DeleteOrder
A message that indicates a limit order was added to the book.
Definition: messages.hpp:332
OrderEntry::Messages::OrderRequest
A request to place a new limit / market order in the book.
Definition: messages.hpp:502
DataFeed::Receiver::get_book
const LOB::LimitOrderBook & get_book()
Return the limit order book for this receiver.
Definition: receiver.hpp:214
Maths::ExponentialMovingAverage::reset
void reset(T observation=0)
Reset the average to initial state with observation .
Definition: exponential_moving_average.hpp:71
DataFeed::Messages::EndOfSession
A message that indicates the end of a trading session.
Definition: messages.hpp:506
DataFeed::Messages::Clear
A message that indicates to clear all orders in the order book.
Definition: messages.hpp:213
Maths::Probability::boolean
bool boolean()
Return the result of a coin toss.
Definition: probability.hpp:105
DataFeed::Messages::StartOfSession
A message that indicates the start of a trading session.
Definition: messages.hpp:460
Maths::Probability::uniform_int
T uniform_int(T min, T max)
Sample a number from an integer-valued uniform distribution.
Definition: probability.hpp:49
Strategies::MarketMaker::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::DeleteOrder &message)
Handle a delete order message.
Definition: market_maker.hpp:208
Strategies::MarketMaker::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::AddOrder &message)
Handle an add order message.
Definition: market_maker.hpp:201
Maths::ExponentialMovingAverage< double >
DataFeed::Messages::Trade::side
const Side side
the side of the market order
Definition: messages.hpp:397
DataFeed::LOB::LimitOrderBook::last_best_buy
Price last_best_buy() const
Return the last best buy price.
Definition: limit_order_book.hpp:268
OrderEntry::Client
A client for interacting with the direct market access server.
Definition: client.hpp:42
OrderEntry::Messages::PurgeRequest
A request to cancel all active orders in the book.
Definition: messages.hpp:917
OrderEntry::make_username
Username make_username(std::string username)
Make a username from the given input string.
Definition: messages.hpp:46
Strategies::MarketMaker::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::Trade &message)
Handle a trade message.
Definition: market_maker.hpp:215
OrderEntry::Quantity
uint32_t Quantity
A type for order quantities.
Definition: messages.hpp:133
Strategies::MarketMaker::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::StartOfSession &message)
Handle a start of session message.
Definition: market_maker.hpp:161
Maths::ExponentialMovingAverage::get_average
T get_average() const
Return the current value of the exponential moving average .
Definition: exponential_moving_average.hpp:101
DataFeed::side_to_double
constexpr double side_to_double(Side side)
Convert an order side character to a double.
Definition: messages.hpp:60
Strategies
Direct market access trading strategies.
Definition: iceberg_liquidity_consumer.hpp:33
DataFeed::Messages::AddOrder
A message that indicates a limit order was added to the book.
Definition: messages.hpp:259
Strategies::MarketMaker::MarketMaker
MarketMaker(asio::io_context &feed_context, asio::io_context &context, nlohmann::json options)
Initialize the strategy.
Definition: market_maker.hpp:124