CBOE Emulator  1.0
market_arbitrage.hpp
1 // A market arbitrage (latency) 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 
18 #ifndef STRATEGIES_MARKET_ARBITRAGE_HPP
19 #define STRATEGIES_MARKET_ARBITRAGE_HPP
20 
21 #include "data_feed/receiver.hpp"
22 #include "order_entry/client.hpp"
23 #include "maths/probability.hpp"
24 #include <nlohmann/json.hpp>
25 #include <atomic>
26 #include <iostream>
27 
29 namespace Strategies {
30 
37  private:
41  DataFeedReceiver receiverA;
43  DataFeedReceiver receiverB;
45  OrderEntry::Client clientA;
47  OrderEntry::Client clientB;
49  asio::steady_timer timer;
51  uint32_t sleep_time;
53  float P_act;
55  std::atomic<bool> is_running;
56 
60  DataFeed::Quantity spread;
61 
63  inline void start_strategy() {
64  timer.expires_after(std::chrono::milliseconds(sleep_time));
65  timer.async_wait([this](const std::error_code& error) {
66  if (error) { // TODO: handle error code
67  std::cout << "MarketArbitrage::start_strategy - " << error << std::endl;
68  return;
69  }
70  if (Maths::Probability::boolean(P_act)) do_strategy();
71  start_strategy();
72  });
73  }
74 
76  void do_strategy() {
77  // check if A is selling for less than B is buying
78  if (receiverA.get_book().does_cross(receiverB.get_book(), spread)) {
79  // buy on A and sell on B
80  // std::cout << "buy on A and sell on B" << std::endl;
82  OrderEntry::Messages::ORDER_PRICE_MARKET,
83  size,
84  OrderEntry::Side::Buy
85  );
87  OrderEntry::Messages::ORDER_PRICE_MARKET,
88  size,
89  OrderEntry::Side::Sell
90  );
91  // check if B is selling for less than A is buying
92  } else if (receiverB.get_book().does_cross(receiverA.get_book(), spread)) {
93  // buy on B and sell on A
94  // std::cout << "buy on B and sell on A" << std::endl;
96  OrderEntry::Messages::ORDER_PRICE_MARKET,
97  size,
98  OrderEntry::Side::Buy
99  );
101  OrderEntry::Messages::ORDER_PRICE_MARKET,
102  size,
103  OrderEntry::Side::Sell
104  );
105  }
106  }
107 
108  public:
117  asio::io_context& feedA_context,
118  asio::io_context& feedB_context,
119  asio::io_context& context,
120  nlohmann::json options
121  ) :
122  receiverA(
123  feedA_context,
124  asio::ip::make_address(options["data_feed"]["A"]["listen"].get<std::string>()),
125  asio::ip::make_address(options["data_feed"]["A"]["group"].get<std::string>()),
126  options["data_feed"]["A"]["port"].get<uint16_t>(),
127  *this
128  ),
129  receiverB(
130  feedB_context,
131  asio::ip::make_address(options["data_feed"]["B"]["listen"].get<std::string>()),
132  asio::ip::make_address(options["data_feed"]["B"]["group"].get<std::string>()),
133  options["data_feed"]["B"]["port"].get<uint16_t>(),
134  *this
135  ),
136  clientA(
137  context,
138  options["order_entry"]["A"]["host"].get<std::string>(),
139  std::to_string(options["order_entry"]["A"]["port"].get<uint16_t>())
140  ),
141  clientB(
142  context,
143  options["order_entry"]["B"]["host"].get<std::string>(),
144  std::to_string(options["order_entry"]["B"]["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  size(options["strategy"]["size"].get<OrderEntry::Quantity>()),
151  spread(options["strategy"]["spread"].get<DataFeed::Quantity>()) {
152  // login to client A
153  auto usernameA = OrderEntry::make_username(options["order_entry"]["A"]["username"].get<std::string>());
154  auto passwordA = OrderEntry::make_password(options["order_entry"]["A"]["password"].get<std::string>());
155  clientA.send<OrderEntry::Messages::LoginRequest>(usernameA, passwordA);
156  // login to client B
157  auto usernameB = OrderEntry::make_username(options["order_entry"]["B"]["username"].get<std::string>());
158  auto passwordB = OrderEntry::make_password(options["order_entry"]["B"]["password"].get<std::string>());
159  clientB.send<OrderEntry::Messages::LoginRequest>(usernameB, passwordB);
160  }
161 
167  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::StartOfSession& message) {
168  if (is_running) {
169  std::cerr << "received start of session when already running" << std::endl;
170  return;
171  }
172  // check if both markets are active
173  if (receiverA.is_session_active() and receiverB.is_session_active()) {
174  // start the strategy
175  start_strategy();
176  // set the running flag to true
177  is_running = true;
178  }
179  }
180 
186  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::EndOfSession& message) {
187  if (not is_running) {
188  std::cerr << "received end of session when not running" << std::endl;
189  return;
190  }
191  // check if either market has stopped the trading session
192  if (not receiverA.is_session_active() or not receiverB.is_session_active()) {
193  // stop the strategy
194  timer.cancel();
195  is_running = false;
196  }
197  }
198 
204  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::Clear& message) { }
205 
211  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::AddOrder& message) { }
212 
218  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::DeleteOrder& message) { }
219 
225  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::Trade& message) { }
226 };
227 
228 } // namespace Strategies
229 
230 #endif // STRATEGIES_MARKET_ARBITRAGE_HPP
OrderEntry
Logic for sending/receiving application messages in a financial market.
Definition: authorizer.hpp:26
DataFeed::Receiver< MarketArbitrage >
Strategies::MarketArbitrage::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::DeleteOrder &message)
Handle a delete order message.
Definition: market_arbitrage.hpp:218
OrderEntry::Messages::LoginRequest
A request that indicates a client is attempting to create a new session.
Definition: messages.hpp:248
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
DataFeed::Messages::DeleteOrder
A message that indicates a limit order was added to the book.
Definition: messages.hpp:332
Strategies::MarketArbitrage::MarketArbitrage
MarketArbitrage(asio::io_context &feedA_context, asio::io_context &feedB_context, asio::io_context &context, nlohmann::json options)
Initialize the strategy.
Definition: market_arbitrage.hpp:116
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::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
Strategies::MarketArbitrage::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::Trade &message)
Handle a trade message.
Definition: market_arbitrage.hpp:225
Strategies::MarketArbitrage::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::Clear &message)
Handle a clear book message.
Definition: market_arbitrage.hpp:204
Strategies::MarketArbitrage
The market arbitrage strategy logic.
Definition: market_arbitrage.hpp:36
DataFeed
Logic for sending and receiving messages on a financial data feed.
Definition: heartbeat.hpp:28
DataFeed::Quantity
uint32_t Quantity
A type for order quantities.
Definition: messages.hpp:100
DataFeed::Receiver::is_session_active
bool is_session_active() const
Return whether the trading session is active.
Definition: receiver.hpp:220
Strategies::MarketArbitrage::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::AddOrder &message)
Handle an add order message.
Definition: market_arbitrage.hpp:211
OrderEntry::Client
A client for interacting with the direct market access server.
Definition: client.hpp:42
Strategies::MarketArbitrage::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::StartOfSession &message)
Handle a start of session message.
Definition: market_arbitrage.hpp:167
DataFeed::LOB::LimitOrderBook::does_cross
bool does_cross(const LimitOrderBook &other, Quantity spread=0) const
Check if this book crosses another book.
Definition: limit_order_book.hpp:360
Strategies::MarketArbitrage::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::EndOfSession &message)
Handle an end of session message.
Definition: market_arbitrage.hpp:186
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
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