CBOE Emulator  1.0
iceberg_liquidity_consumer.hpp
1 // A liquidity consumer agent based on an iceberg trading scheme.
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_ICEBERG_TRADER_HPP
23 #define STRATEGIES_ICEBERG_TRADER_HPP
24 
25 #include "data_feed/receiver.hpp"
26 #include "order_entry/client.hpp"
27 #include "maths/probability.hpp"
28 #include <nlohmann/json.hpp>
29 #include <atomic>
30 #include <iostream>
31 
33 namespace Strategies {
34 
41  private:
45  DataFeedReceiver receiver;
47  OrderEntry::Client client;
49  asio::steady_timer timer;
51  uint32_t sleep_time;
53  float P_act;
55  std::atomic<bool> is_running;
56 
58  OrderEntry::Quantity minimum_size;
60  OrderEntry::Quantity maximum_size;
62  OrderEntry::Quantity iceberg_size;
64  OrderEntry::Side side = OrderEntry::Side::Sell;
66  OrderEntry::Quantity size = 0;
67 
69  inline void sell_order() {
70  auto volume = receiver.get_book().volume_buy_best();
71  if (volume == 0) return;
72  auto order_size = std::min(size, iceberg_size);
73  // std::cout << "placing sell order for " << order_size << " shares" << std::endl;
75  OrderEntry::Messages::ORDER_PRICE_MARKET,
76  order_size,
77  OrderEntry::Side::Sell
78  );
79  // remove the amount ordered from the size for the day
80  size -= order_size;
81  // std::cout << size << " shares left in todays order quantity" << std::endl;
82  }
83 
85  inline void buy_order() {
86  auto volume = receiver.get_book().volume_sell_best();
87  if (volume == 0) return;
88  auto order_size = std::min(size, iceberg_size);
89  // std::cout << "placing buy order for " << order_size << " shares" << std::endl;
91  OrderEntry::Messages::ORDER_PRICE_MARKET,
92  order_size,
93  OrderEntry::Side::Buy
94  );
95  // remove the amount ordered from the size for the day
96  size -= order_size;
97  // std::cout << size << " shares left in todays order quantity" << std::endl;
98  }
99 
101  inline void start_strategy() {
102  timer.expires_after(std::chrono::milliseconds(sleep_time));
103  timer.async_wait([this](const std::error_code& error) {
104  if (error) { // TODO: handle error code
105  std::cout << "IcebergLiquidityConsumer::start_strategy - " << error << std::endl;
106  return;
107  }
108  if (Maths::Probability::boolean(P_act)) do_strategy();
109  start_strategy();
110  });
111  }
112 
114  void do_strategy() {
115  // make an order based on the agent's side for the day
116  switch (side) {
117  case OrderEntry::Side::Sell: { sell_order(); break; }
118  case OrderEntry::Side::Buy: { buy_order(); break; }
119  }
120  }
121 
122  public:
130  asio::io_context& feed_context,
131  asio::io_context& context,
132  nlohmann::json options
133  ) :
134  receiver(
135  feed_context,
136  asio::ip::make_address(options["data_feed"]["listen"].get<std::string>()),
137  asio::ip::make_address(options["data_feed"]["group"].get<std::string>()),
138  options["data_feed"]["port"].get<uint16_t>(),
139  *this
140  ),
141  client(
142  context,
143  options["order_entry"]["host"].get<std::string>(),
144  std::to_string(options["order_entry"]["port"].get<uint16_t>())
145  ),
146  timer(context),
147  sleep_time(options["strategy"]["sleep_time"].get<uint32_t>()),
148  P_act(options["strategy"]["P_act"].get<double>()),
149  is_running(false),
150  minimum_size(options["strategy"]["minimum_size"].get<double>()),
151  maximum_size(options["strategy"]["maximum_size"].get<double>()),
152  iceberg_size(options["strategy"]["iceberg_size"].get<double>()) {
153  // login to the order entry client
154  auto username = OrderEntry::make_username(options["order_entry"]["username"].get<std::string>());
155  auto password = OrderEntry::make_password(options["order_entry"]["password"].get<std::string>());
156  client.send<OrderEntry::Messages::LoginRequest>(username, password);
157  }
158 
164  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::StartOfSession& message) {
165  if (is_running) {
166  std::cerr << "received start of session when already running" << std::endl;
167  return;
168  }
169  // std::cout << "trading session started" << std::endl;
170  // select a random side for the day's orders
171  side = Maths::Probability::boolean() ? OrderEntry::Side::Buy : OrderEntry::Side::Sell;
172  // std::cout << "placing " << side << " orders today" << std::endl;
173  size = Maths::Probability::uniform_int(minimum_size, maximum_size);
174  // std::cout << "ordering " << size << " shares today" << std::endl;
175  // start the strategy
176  start_strategy();
177  is_running = true;
178  }
179 
185  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::EndOfSession& message) {
186  if (not is_running) {
187  std::cerr << "received end of session when not running" << std::endl;
188  return;
189  }
190  // stop the strategy
191  timer.cancel();
192  is_running = false;
193  // std::cout << "trading session ended" << std::endl;
194  }
195 
201  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::Clear& message) { }
202 
208  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::AddOrder& message) { }
209 
215  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::DeleteOrder& message) { }
216 
222  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::Trade& message) { }
223 };
224 
225 } // namespace Strategies
226 
227 #endif // STRATEGIES_ICEBERG_TRADER_HPP
DataFeed::Receiver< IcebergLiquidityConsumer >
OrderEntry::Messages::LoginRequest
A request that indicates a client is attempting to create a new session.
Definition: messages.hpp:248
Strategies::IcebergLiquidityConsumer::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::Trade &message)
Handle a trade message.
Definition: iceberg_liquidity_consumer.hpp:222
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::send
void send(Args &&...args)
Write a message asynchronously (non-blocking).
Definition: client.hpp:299
Strategies::IcebergLiquidityConsumer::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::DeleteOrder &message)
Handle a delete order message.
Definition: iceberg_liquidity_consumer.hpp:215
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
DataFeed::LOB::LimitOrderBook::volume_sell_best
Volume volume_sell_best() const
Return the volume at the best sell price.
Definition: limit_order_book.hpp:300
DataFeed::Messages::EndOfSession
A message that indicates the end of a trading session.
Definition: messages.hpp:506
Strategies::IcebergLiquidityConsumer
The iceberg trader strategy logic.
Definition: iceberg_liquidity_consumer.hpp:40
DataFeed::Messages::Clear
A message that indicates to clear all orders in the order book.
Definition: messages.hpp:213
Strategies::IcebergLiquidityConsumer::IcebergLiquidityConsumer
IcebergLiquidityConsumer(asio::io_context &feed_context, asio::io_context &context, nlohmann::json options)
Initialize the strategy.
Definition: iceberg_liquidity_consumer.hpp:129
DataFeed::LOB::LimitOrderBook::volume_buy_best
Volume volume_buy_best() const
Return the volume at the best sell price.
Definition: limit_order_book.hpp:318
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
Strategies::IcebergLiquidityConsumer::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::Clear &message)
Handle a clear book message.
Definition: iceberg_liquidity_consumer.hpp:201
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::IcebergLiquidityConsumer::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::EndOfSession &message)
Handle an end of session message.
Definition: iceberg_liquidity_consumer.hpp:185
OrderEntry::Client
A client for interacting with the direct market access server.
Definition: client.hpp:42
OrderEntry::make_username
Username make_username(std::string username)
Make a username from the given input string.
Definition: messages.hpp:46
OrderEntry::Quantity
uint32_t Quantity
A type for order quantities.
Definition: messages.hpp:133
Strategies::IcebergLiquidityConsumer::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::StartOfSession &message)
Handle a start of session message.
Definition: iceberg_liquidity_consumer.hpp:164
Strategies::IcebergLiquidityConsumer::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::AddOrder &message)
Handle an add order message.
Definition: iceberg_liquidity_consumer.hpp:208
OrderEntry::Side
Side
The side of an order.
Definition: messages.hpp:71
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