CBOE Emulator  1.0
momentum.hpp
1 // A momentum trading strategy.
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: TODO
20 //
21 
22 #ifndef STRATEGIES_MOMENTUM_HPP
23 #define STRATEGIES_MOMENTUM_HPP
24 
25 #include "data_feed/receiver.hpp"
26 #include "order_entry/client.hpp"
27 #include "maths/probability.hpp"
28 #include "maths/rate_of_change.hpp"
29 #include <nlohmann/json.hpp>
30 #include <atomic>
31 #include <algorithm>
32 #include <iostream>
33 
35 namespace Strategies {
36 
42 class Momentum {
43  private:
47  DataFeedReceiver receiver;
49  OrderEntry::Client client;
51  asio::steady_timer timer;
53  uint32_t sleep_time;
55  float P_act;
57  std::atomic<bool> is_running;
58 
62  OrderEntry::Quantity max_size;
64  double decision_boundary;
65 
67  inline void start_strategy() {
68  timer.expires_after(std::chrono::milliseconds(sleep_time));
69  timer.async_wait([this](const std::error_code& error) {
70  if (error) { // TODO: handle error code
71  std::cout << "Momentum::start_strategy - " << error << std::endl;
72  return;
73  }
74  if (Maths::Probability::boolean(P_act)) do_strategy();
75  start_strategy();
76  });
77  }
78 
80  void do_strategy() {
81  // update the rate of change based on the current midpoint price
82  roc.process(receiver.get_book().last_price());
83  // determine the quantity as product of the rate_of_change and the
84  // "wealth" of the trader, i.e., their capital
85  OrderEntry::Quantity size = std::abs(roc.get_rate_of_change() * client.get_capital());
86  static constexpr OrderEntry::Quantity min_size = 0;
87  size = std::clamp(size, min_size, max_size);
88  // place an order
89  if (roc.get_rate_of_change() >= decision_boundary) // increasing, buy
91  OrderEntry::Messages::ORDER_PRICE_MARKET,
92  size,
93  OrderEntry::Side::Buy
94  );
95  else if (roc.get_rate_of_change() <= -decision_boundary) // decreasing, sell
97  OrderEntry::Messages::ORDER_PRICE_MARKET,
98  size,
99  OrderEntry::Side::Sell
100  );
101  }
102 
103  public:
111  asio::io_context& feed_context,
112  asio::io_context& context,
113  nlohmann::json options
114  ) :
115  receiver(
116  feed_context,
117  asio::ip::make_address(options["data_feed"]["listen"].get<std::string>()),
118  asio::ip::make_address(options["data_feed"]["group"].get<std::string>()),
119  options["data_feed"]["port"].get<uint16_t>(),
120  *this
121  ),
122  client(
123  context,
124  options["order_entry"]["host"].get<std::string>(),
125  std::to_string(options["order_entry"]["port"].get<uint16_t>())
126  ),
127  timer(context),
128  sleep_time(options["strategy"]["sleep_time"].get<uint32_t>()),
129  P_act(options["strategy"]["P_act"].get<double>()),
130  is_running(false),
131  roc(options["strategy"]["history_size"].get<uint32_t>()),
132  max_size(options["strategy"]["max_size"].get<OrderEntry::Quantity>()),
133  decision_boundary(options["strategy"]["decision_boundary"].get<double>()) {
134  // login to the order entry client
135  auto username = OrderEntry::make_username(options["order_entry"]["username"].get<std::string>());
136  auto password = OrderEntry::make_password(options["order_entry"]["password"].get<std::string>());
137  client.send<OrderEntry::Messages::LoginRequest>(username, password);
138  // set the capital and shares for the client
139  client.set_capital(options["order_entry"]["capital"].get<OrderEntry::ClientCapital>());
140  client.set_shares(options["order_entry"]["shares"].get<OrderEntry::ClientShares>());
141  }
142 
148  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::StartOfSession& message) {
149  if (is_running) {
150  std::cerr << "received start of session when already running" << std::endl;
151  return;
152  }
153  // start the strategy
154  start_strategy();
155  // set the running flag to true
156  is_running = true;
157  }
158 
164  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::EndOfSession& message) {
165  if (not is_running) {
166  std::cerr << "received end of session when not running" << std::endl;
167  return;
168  }
169  // stop the strategy
170  timer.cancel();
171  is_running = false;
172  }
173 
179  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::Clear& message) { }
180 
186  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::AddOrder& message) { }
187 
193  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::DeleteOrder& message) { }
194 
200  inline void did_receive(DataFeedReceiver* receiver, const DataFeed::Messages::Trade& message) { }
201 };
202 
203 } // namespace Strategies
204 
205 #endif // STRATEGIES_MOMENTUM_HPP
OrderEntry
Logic for sending/receiving application messages in a financial market.
Definition: authorizer.hpp:26
DataFeed::LOB::LimitOrderBook::last_price
Price last_price() const
Return the current price of the asset using last prices.
Definition: limit_order_book.hpp:283
DataFeed::Receiver< Momentum >
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
OrderEntry::Client::set_shares
void set_shares(ClientShares shares_)
Set the client shares to a new value.
Definition: client.hpp:273
Strategies::Momentum::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::DeleteOrder &message)
Handle a delete order message.
Definition: momentum.hpp:193
Strategies::Momentum::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::Clear &message)
Handle a clear book message.
Definition: momentum.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
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::RateOfChange< OrderEntry::Price >
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::Momentum::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::EndOfSession &message)
Handle an end of session message.
Definition: momentum.hpp:164
OrderEntry::Client::get_capital
ClientCapital get_capital() const
Return the amount of capital the client currently has.
Definition: client.hpp:291
Strategies::Momentum::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::AddOrder &message)
Handle an add order message.
Definition: momentum.hpp:186
OrderEntry::Client
A client for interacting with the direct market access server.
Definition: client.hpp:42
OrderEntry::Client::set_capital
void set_capital(ClientCapital capital_)
Set the client capital to a new value.
Definition: client.hpp:285
Strategies::Momentum::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::Trade &message)
Handle a trade message.
Definition: momentum.hpp:200
Strategies::Momentum
The momentum trader strategy logic.
Definition: momentum.hpp:42
OrderEntry::make_username
Username make_username(std::string username)
Make a username from the given input string.
Definition: messages.hpp:46
Maths::RateOfChange::process
void process(Observation observation)
Calculate the next RoC based on observation .
Definition: rate_of_change.hpp:99
OrderEntry::Quantity
uint32_t Quantity
A type for order quantities.
Definition: messages.hpp:133
Strategies::Momentum::Momentum
Momentum(asio::io_context &feed_context, asio::io_context &context, nlohmann::json options)
Initialize the strategy.
Definition: momentum.hpp:110
Maths::RateOfChange::get_rate_of_change
RoC get_rate_of_change() const
Return the current rate of change (RoC).
Definition: rate_of_change.hpp:86
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::Momentum::did_receive
void did_receive(DataFeedReceiver *receiver, const DataFeed::Messages::StartOfSession &message)
Handle a start of session message.
Definition: momentum.hpp:148