Compare commits

...
Author SHA1 Message Date
John Colvin 9deb8cc237 fix build: Trivial changes to fix emumba_deribit build
* Use "Unix Makefiles" generator when building websocket-library.
  - Ninja tries to build things in parallel, and doesn't build the
    ExternalProject dependencies in the right order. Consequently, the
    websocket-library build fails because it can't find "libspdlog.a".

* Replace a few leftover SPDLOG_ERROR macros w/ LOG_ERROR (the latter
  logs in our system, not spdlog).
2025-01-10 19:36:18 +00:00
Mohammad Arham 11ff46a056 scafolding 2025-01-09 19:56:54 +00:00
39 changed files with 5778 additions and 0 deletions

No files matched your search

+1
View File
@@ -117,6 +117,7 @@ else()
# subdirs
if (BUILD_SPARKD)
add_subdirectory(src/lib_binance_execution)
add_subdirectory(src/lib_deribit_emumba)
add_subdirectory(src/server/sparkd)
add_subdirectory(src/data_files)
endif()
@@ -0,0 +1,25 @@
include(ExternalProject)
if(NOT TARGET websocket-library_project)
ExternalProject_Add(websocket-library_project
GIT_REPOSITORY https://fintech.websocket:h_ubh-4dsWRB569wwKyg@gitlab.com/cryptoexchanges1/websocket-client-using-epoll.git
GIT_TAG enhancement/config-ssl
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/websocket-library
-DCMAKE_BUILD_TYPE=Release
-DLOG_LEVEL=INFO
-DENABLE_ENCRYPTION=True
)
endif()
ExternalProject_Get_Property(websocket-library_project BINARY_DIR)
ExternalProject_Get_Property(websocket-library_project SOURCE_DIR)
set(websocket-library_INCLUDE_DIR ${SOURCE_DIR}/include)
set(websocket-library_LIB_DIR ${BINARY_DIR}/src)
add_library(websocket-library STATIC IMPORTED)
set_property(TARGET websocket-library PROPERTY IMPORTED_LOCATION ${websocket-library_LIB_DIR}/libemumba-websocket-library.a)
add_dependencies(websocket-library websocket-library_project)
install(FILES ${websocket-library_LIB_DIR}/libemumba-websocket-library.a DESTINATION ${CMAKE_INSTALL_LIBDIR})
+32
View File
@@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.23)
project(deribit_emumba)
file(
GLOB_RECURSE ${PROJECT_NAME}_SOURCES
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
*.cpp)
add_library(${PROJECT_NAME} STATIC ${${PROJECT_NAME}_SOURCES})
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules)
find_package(spdlog REQUIRED)
target_include_directories(${PROJECT_NAME} PRIVATE ${spdlog_INCLUDE_DIR})
target_link_directories(${PROJECT_NAME} PUBLIC ${spdlog_LIB_DIR})
add_dependencies(${PROJECT_NAME} spdlog)
target_link_libraries(${PROJECT_NAME} PUBLIC spdlog)
find_package(websocket-library REQUIRED)
target_include_directories(${PROJECT_NAME} PRIVATE ${websocket-library_INCLUDE_DIR})
target_link_directories(${PROJECT_NAME} PUBLIC ${websocket-library_LIB_DIR})
add_dependencies(${PROJECT_NAME} websocket-library)
target_link_libraries(${PROJECT_NAME} PRIVATE websocket-library)
target_include_directories(
${PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(${PROJECT_NAME} PUBLIC libspark)
@@ -0,0 +1,32 @@
include(ExternalProject)
set(_flags "-fno-omit-frame-pointer -DNDEBUG -O3")
if(NOT TARGET spdlog_project)
ExternalProject_Add(spdlog_project
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.5.0
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/spdlog
-DCMAKE_BUILD_TYPE=Release
-DSPDLOG_BUILD_EXAMPLE=OFF
-DSPDLOG_BUILD_EXAMPLE_HO=OFF
-DSPDLOG_BUILD_TESTS=OFF
-DSPDLOG_BUILD_TESTS_HO=OFF
-DCMAKE_CXX_FLAGS_RELEASE=${_flags}
)
endif()
ExternalProject_Get_Property(spdlog_project BINARY_DIR)
ExternalProject_Get_Property(spdlog_project SOURCE_DIR)
set(spdlog_INCLUDE_DIR ${SOURCE_DIR}/include)
set(spdlog_LIB_DIR ${BINARY_DIR})
add_library(spdlog STATIC IMPORTED)
# set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wno-deprecated-copy")
# set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-deprecated-copy")
set_property(TARGET spdlog PROPERTY IMPORTED_LOCATION ${spdlog_LIB_DIR}/libspdlog.a)
add_dependencies(spdlog spdlog_project)
install(FILES ${spdlog_LIB_DIR}/libspdlog.a DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(DIRECTORY ${spdlog_INCLUDE_DIR}/spdlog DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
@@ -0,0 +1,26 @@
include(ExternalProject)
if(NOT TARGET websocket-library_project)
ExternalProject_Add(websocket-library_project
GIT_REPOSITORY https://fintech.websocket:h_ubh-4dsWRB569wwKyg@gitlab.com/cryptoexchanges1/websocket-client-using-epoll.git
GIT_TAG enhancement/config-ssl
CMAKE_GENERATOR "Unix Makefiles"
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/websocket-library
-DCMAKE_BUILD_TYPE=Release
-DLOG_LEVEL=INFO
-DENABLE_ENCRYPTION=True
)
endif()
ExternalProject_Get_Property(websocket-library_project BINARY_DIR)
ExternalProject_Get_Property(websocket-library_project SOURCE_DIR)
set(websocket-library_INCLUDE_DIR ${SOURCE_DIR}/include)
set(websocket-library_LIB_DIR ${BINARY_DIR}/src)
add_library(websocket-library STATIC IMPORTED)
set_property(TARGET websocket-library PROPERTY IMPORTED_LOCATION ${websocket-library_LIB_DIR}/libemumba-websocket-library.a)
add_dependencies(websocket-library websocket-library_project)
install(FILES ${websocket-library_LIB_DIR}/libemumba-websocket-library.a DESTINATION ${CMAKE_INSTALL_LIBDIR})
@@ -0,0 +1,15 @@
include(ExternalProject)
if(NOT TARGET websocketpp_project)
ExternalProject_Add(websocketpp_project
URL https://github.com/zaphoyd/websocketpp/archive/0.8.2.zip
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/websocketpp
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
)
endif()
ExternalProject_Get_Property(websocketpp_project SOURCE_DIR)
set(websocketpp_INCLUDE_DIR ${SOURCE_DIR})
@@ -0,0 +1,19 @@
/**
* @file common.h
* @brief Provides user the ease to include both the market_data_common and order_entry_common header files using a single
* header file.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned by Emumba (Pvt) Ltd [Emumba] and is
* protected by copyright law and international copyright treaties. Access to and use of the software is governed by the
* terms of the applicable Emumba Software Services Agreement (the Agreement) and Customer end user license agreements
* granting a non-assignable, non-transferable and non-exclusive license to use the software for it's own data processing
* purposes under the terms defined in the Agreement. Except as otherwise granted within the terms of the Agreement, copying
* or reproduction of any part of this source code or associated reference material to any other location for further
* reproduction or redistribution, and any amendments to this copyright notice, are expressly prohibited. Any reproduction or
* redistribution for sale or hiring of the Software not in accordance with the terms of the Agreement is a violation of
* copyright law
*/
#include "market_data_common.h"
#include "order_entry_common.h"
@@ -0,0 +1,45 @@
#include "deribit_exception.h"
#include <iostream>
using namespace emumba::deribit_connector;
std::string deribit_exception::errors_to_string(deribit_exception::error_code error) const noexcept
{
switch (error)
{
case deribit_exception::error_code::uri_failure:
return "URI for connection is incorrect";
case deribit_exception::error_code::authentication_failure:
return "Authentication credentials are incorrect";
case deribit_exception::error_code::thread_binding:
return "Thread binding to specific core failed. Check configuration";
case deribit_exception::error_code::connection_error:
return "Cannot connect to websocket";
case deribit_exception::error_code::no_instruments:
return "No instruments selected. Check configuration";
default:
return "Incorrect error code.";
}
}
deribit_exception::deribit_exception(deribit_exception::error_code error_code)
: _error_code(error_code)
{}
deribit_exception::~deribit_exception() noexcept
{}
deribit_exception::error_code deribit_exception::get_error() const noexcept
{
return _error_code;
}
void deribit_exception::print() const noexcept
{
std::cout << "connector.deribit.exception." << (int)_error_code << ": " << errors_to_string(_error_code) << std::endl;
}
@@ -0,0 +1,75 @@
/**
* @file deribit_exception.h
* @brief Provides a class for exception handling.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned by Emumba (Pvt) Ltd [Emumba] and is
* protected by copyright law and international copyright treaties. Access to and use of the software is governed by the
* terms of the applicable Emumba Software Services Agreement (the Agreement) and Customer end user license agreements
* granting a non-assignable, non-transferable and non-exclusive license to use the software for it's own data processing
* purposes under the terms defined in the Agreement. Except as otherwise granted within the terms of the Agreement, copying
* or reproduction of any part of this source code or associated reference material to any other location for further
* reproduction or redistribution, and any amendments to this copyright notice, are expressly prohibited. Any reproduction or
* redistribution for sale or hiring of the Software not in accordance with the terms of the Agreement is a violation of
* copyright law
*/
#ifndef DERIBIT_EXCEPTION_H
#define DERIBIT_EXCEPTION_H
#include <exception>
#include <string>
namespace emumba {
namespace deribit_connector {
/**
* @brief Provides exception handling for the connector.
*
*/
class deribit_exception : public std::exception
{
public:
/**
* @brief enum for exception handling.
*
*/
enum class error_code : int
{
uri_failure, /**< Value = 0. URI for connection is incorrect. */
authentication_failure, /**< Value = 1. Authentication credentials are incorrect. */
thread_binding, /**< Value = 2. Thread binding to specific core failed. */
connection_error, /**< Value = 3. Cannot connect to websocket. */
no_instruments /**< Value = 4. No instruments selected. */
};
explicit deribit_exception(error_code error_code);
~deribit_exception() noexcept;
/**
* @brief Returns an error code corresponding to an exception.
*
* @return error_code
*/
error_code get_error() const noexcept;
/**
* @brief Prints the error message on console.
*
*/
void print() const noexcept;
private:
/**
* @brief Return error_message against an error_code.
*
* @param error An error code corresponding to an exception.
* @return std::string
*/
std::string errors_to_string(error_code error) const noexcept;
protected:
error_code _error_code;
};
} // namespace deribit_connector
} // namespace emumba
#endif
@@ -0,0 +1,138 @@
/**
* @file deribit_handler.h
* @brief Provides a handler class used to create and maintain multiple trading sessions with the Deribit exchange.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned by Emumba (Pvt) Ltd [Emumba] and is
* protected by copyright law and international copyright treaties. Access to and use of the software is governed by the
* terms of the applicable Emumba Software Services Agreement (the Agreement) and Customer end user license agreements
* granting a non-assignable, non-transferable and non-exclusive license to use the software for it's own data processing
* purposes under the terms defined in the Agreement. Except as otherwise granted within the terms of the Agreement, copying
* or reproduction of any part of this source code or associated reference material to any other location for further
* reproduction or redistribution, and any amendments to this copyright notice, are expressly prohibited. Any reproduction or
* redistribution for sale or hiring of the Software not in accordance with the terms of the Agreement is a violation of
* copyright law
*/
#ifndef DERIBIT_HANDLER_H
#define DERIBIT_HANDLER_H
#include "deribit_listener.h"
#include "deribit_utils.h"
#include "trading_session_interface.h"
#include <list>
#include <memory>
#include <thread>
namespace emumba {
namespace deribit_connector {
class session_manager;
class symbol_handler;
class trader_session;
class orders_handler;
/**
* @brief Creates and maintains multiple trading sessions with the Deribit exchange.
*
*/
class deribit_handler
{
public:
/**
* @brief Construct a new deribit handler object.
*
* @param symbol_config_list List of "symbol_config" objects.
* @param trading_config "trading_session_config" object required to setup a trading session.
* @param cred_config_list list of credential_config objects.
* @param log_config "logging_config" object used to configure the logging.
* @param deribit_uri uri used to connect to the exchange.
* @param trad_listener Pointer to a "deribit_listener" derived object containing registered callbacks.
*/
deribit_handler(std::list<configuration::symbol_config> symbol_config_list,
const configuration::trading_session_config & trading_config,
const std::map<uint, emumba::deribit_connector::configuration::credential_config> &cred_config_list,
const configuration::logging_config &log_config, const std::string deribit_uri,
deribit_listener *trad_listener);
/**
* @brief Creates a new trading session and returns the corresponding trading interface used to place orders.
* @param cred_config credential_config structure containing the login credential required for trading.
* @return trading_session_interface
*/
trading_session_interface create_new_trading_session(
const emumba::deribit_connector::configuration::credential_config &credential_config);
/**
* @brief Reconfigure the instrument list for subscriptions
*
* @param symbol Symbol for instruments | Must be specified initially through config.json otherwise function won't work
* @param instrument_list List of instruments to be subscribed
* @param append Set to true if you want to append the previous list
* @return void
*/
void reconfigure_instrument_list(const std::string &symbol, const std::list<std::string> instrument_list,
const bool &append = false);
/**
* @brief Get the uuid object used by the connector to track orders.
*
* @return const std::string& uuid
*/
const std::string &get_uuid() const;
/**
* @brief Get session id of market data session. Returns 0 incase incorrect symbol_name is given.
*
* @param symbol_name symbol e.g BTC, ETH etc.
* @return const uint& session id of market data session.
*/
const uint get_session_id(const std::string &symbol_name) const;
/**
* @brief Get Future Expiries for an underlying instrument | Will be valid only after get_instruments callback in
* deribit_listener
*
* @param currency_symbol Base currency
* @param expiries Number of expiries
* @param expiry_cycle expiry cycle, defaults to week
* @return std::list<std::string>
*/
std::list<std::string> get_future_expiries(const std::string &currency_symbol, const int &expiries,
const std::string &expiry_cycle = "week");
/**
* @brief Get Future Expiries for an underlying instrument | Will be valid only after get_instruments callback in
* deribit_listener
*
* @param currency_symbol Base currency
* @param expiries Number of expiries
* @param expiry_cycle expiry cycle, defaults to week
* @return std::list<std::string>
*/
std::list<std::string> get_option_expiries(const std::string &currency_symbol, const int &expiries,
const std::string &expiry_cycle = "week");
/**
* @brief Runs the deribit connector in a thread binded to the specified core and joins it.
*
* @param detach Boolean flag. If set to true, the connector thread will run in detached mode. Default value is false.
*/
void run(const bool &detach = false);
private:
std::shared_ptr<session_manager> _sess_manager;
std::list<std::shared_ptr<symbol_handler>> _symbol_session_list;
std::list<std::shared_ptr<trader_session>> _trading_session_list;
deribit_listener * _trad_listener;
std::shared_ptr<orders_handler> _orders_handler;
std::thread _connector_thread;
};
/**
* @brief Prints the books of all configured instruments of a symbol into a file.
*
*/
void dump_books();
} // namespace deribit_connector
} // namespace emumba
#endif
@@ -0,0 +1,308 @@
/**
* @file deribit_listener.h
* @brief Provides a class with the basic interface for callbacks. These callbacks are registered for various events which
* get triggered based on configured subscriptions or posted requests.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned by Emumba (Pvt) Ltd [Emumba] and is
* protected by copyright law and international copyright treaties. Access to and use of the software is governed by the
* terms of the applicable Emumba Software Services Agreement (the Agreement) and Customer end user license agreements
* granting a non-assignable, non-transferable and non-exclusive license to use the software for it's own data processing
* purposes under the terms defined in the Agreement. Except as otherwise granted within the terms of the Agreement, copying
* or reproduction of any part of this source code or associated reference material to any other location for further
* reproduction or redistribution, and any amendments to this copyright notice, are expressly prohibited. Any reproduction or
* redistribution for sale or hiring of the Software not in accordance with the terms of the Agreement is a violation of
* copyright law
*/
#ifndef DERIBIT_LISTENER_H
#define DERIBIT_LISTENER_H
#include "common.h"
#include "deribit_utils.h"
#include "error.h"
#include "instrument_data_interface.h"
#include "price_level.h"
#include <string>
#include <vector>
namespace emumba {
namespace deribit_connector {
/**
* @brief Provides the basic interface for callbacks to be registered for various events that can get triggered
* based on configured subscriptions or posted requests.
*
*/
class deribit_listener
{
public:
// Order Entry Callbacks
/**
* @brief Handle response related to an order placed by the user. It is called whenever a response for a new order,
* edit order or cancel order is received.
*
* @param order_key Unique key assigned to order at the time of placing order.
* @param order_state Provide the current state of the order.
*/
virtual void on_order_update(const emumba::order_entry::order_key & order_key,
const emumba::order_entry::order_state &order_state);
/**
* @brief Handle respons of positions for a symbol.
*
* @param ec Error code.
* @param positions Vector of structures containing useful information related to positions.
*/
virtual void on_get_all_positions(const emumba::deribit_connector::data_types::error & ec,
const std::vector<emumba::order_entry::position const *> &positions);
/**
* @brief Handle respons of positions for a symbol.
*
* @param ec Error code.
* @param open_orders Vector of structures containing useful information related to open orders.
*/
virtual void on_get_all_open_orders(const emumba::deribit_connector::data_types::error & ec,
const std::vector<emumba::order_entry::open_order const *> &open_orders);
/**
* @brief Handle response of get user trades request.
*
* @param usr_trades_response Structure containing the response of the get user trades request.
*/
virtual void on_get_user_trades(const emumba::order_entry::user_trades_response &usr_trades_response);
/**
* @brief Handle Response for Cancel All Orders by Instrument and by currency Request.
*
* @param order_cancellation Structure containing information related to cancel orders.
*/
virtual void on_mass_cancellation(
const emumba::deribit_connector::data_types::cancellation_update &order_cancellation);
/**
* @brief Handle Response for Accounts Summary Request.
*
* @param ec Error code.
* @param msg_result Json formatted string for `result` field extracted from the response message
* of "/private/get_account_summary" request.
* @return void
*/
virtual void on_account_summary(const emumba::deribit_connector::data_types::error &ec, const std::string &msg_result);
/**
* @brief Handle Response for Cancel All Positions by Instrument Request.
*
* @param ec Error code.
* @param msg_result Json formatted string for `result` field extracted from the response message
* of "private/close_position" request for all positions against an instrument.
* @return void
*/
virtual void on_cancel_all_positions(const emumba::deribit_connector::data_types::error &ec,
const std::string & msg_result);
/**
* @brief Handle Response for Get Position for an instrument Request.
*
* @param ec Error code.
* @param msg_result Json formatted string for `result` field extracted from the response message
* of "private/get_position" request against an instrument.
* @return void
*/
virtual void on_get_position(const emumba::deribit_connector::data_types::error &ec, const std::string &msg_result);
/**
* @brief Handle Response for Get Open Orders for an instrument Request.
*
* @param ec Error code.
* @param msg_result Json formatted string for `result` field extracted from the response message
* of "private/get_open_orders_by_instrument" request against an instrument.
* @return void
*/
virtual void on_get_open_orders(const emumba::deribit_connector::data_types::error &ec, const std::string &msg_result);
/**
* @brief Handle Response for Get MMP Config for a currency
*
* @param ec Error code.
* @param msg_result Json formatted string for `result` field extracted from the response message
* of "private/get_mmp_config" request against a currency.
* @return void
*/
virtual void on_get_mmp_config(const emumba::deribit_connector::data_types::error &ec, const std::string &msg_result);
/**
* @brief Handle Response for Set MMP Config for a currency
*
* @param ec Error code.
* @param msg_result Json formatted string for `result` field extracted from the response message
* of "private/set_mmp_config" request for a currency.
* @return void
*/
virtual void on_set_mmp_config(const emumba::deribit_connector::data_types::error &ec, const std::string &msg_result);
/**
* @brief Handle Response for Reset MMP Config for a currency
*
* @param ec Error code.
* @param msg_result Json formatted string for `result` field extracted from the response message
* of "private/reset_mmp" request against a currency.
* @return void
*/
virtual void on_reset_mmp(const emumba::deribit_connector::data_types::error &ec, const std::string &msg_result);
// Market Data Callbacks
/**
* @brief Handle definition of Instrument.
*
* @param instrument_name Instrument name which has been configured and whose updates will be
* provided through callbacks.
* @return void* This function can return a pointer to a custom data structure made by user. The pointer will be
* returned in the callback as a parameter for all the updates that happen for this instrument.
* This can avoid searching of data structures on user's side if multiple instruments are configured.
*/
virtual void *on_instrument_definition(const std::string & instrument_name,
emumba::deribit_connector::instrument_data_interface *interface) = 0;
/**
* @brief Handle On Book State Change for an Instrument in case state of book changes. Trading should be done when
* book status is Green.
*
* @param instrument_name Instrument name.
* @param status_of_book Enum for book status corresponds to 0 for Red, 1 for Green.
* @param ptr User defined ptr to be returned on updates for a specific instrument. Defaults to null.
* @return void
*/
virtual void on_book_state_change(const std::string &instrument_name, const data_types::book_status &status_of_book,
const void *ptr = nullptr);
/**
* @brief Provide Quote Update Messages for an Instrument.
*
* @param quote Structure contianing quote info of an Instrument.
* @param ptr User defined ptr to be returned on updates for a specific instrument. Defaults to null.
* @return void
*/
virtual void on_quote(const emumba::market_data::quote &quote, const void *ptr = nullptr);
/**
* @brief Provide security statistics info for an Instrument.
*
* @param market_data_statistics Structure containing statistics information related to a specific instrument.
* @param ptr User defined ptr to be returned on updates for a specific instrument. Defaults to null.
* @return void
*/
virtual void on_security_statistics(const emumba::market_data::market_data_statistics &market_data_stats,
const void * ptr = nullptr);
/**
* @brief Handle Public MarkPrice Subscription Message for a Symbol.
*
* @param symbol_name Symbol name.
* @param markprice_message Json formatted string for one entry of `data` field extracted from the subscription message
* received against "markprice.options.{index_name}" subscription for a symbol.
* @return void
*/
virtual void on_subscription_public_markprice(const std::string &symbol_name, const std::string &markprice_message);
/**
* @brief Handle Public Index Subscription Message for a Symbol.
*
* @param symbol_name Symbol name.
* @param index_message Json formatted string for `data` field extracted from the subscription message
* received against "deribit_price_index.{index_name}" subscription for a symbol.
* @return void
*/
virtual void on_subscription_public_index(const std::string &symbol_name, const std::string &index_message);
/**
* @brief Handle User Portfolio Subscription Message.
*
* @param symbol_name Symbol name.
* @param index_message Json formatted string for `data` field extracted from the subscription message
* received against "user.portfolio.{currency}" subscription for a symbol.
* @return void
*/
virtual void on_subscription_private_user_portfolio(const std::string &symbol_name,
const std::string &user_portfolio_message);
/**
* @brief Handle Response Message for Get Instruments Request.
*
* @param ec Error code.
* @param symbol_name Symbol name.
* @param get_instruments_message Json formatted string for `result` field extracted from response message of
* "/public/get_instruments" request.
* @return void
*/
virtual void on_response_get_instruments(const emumba::deribit_connector::data_types::error &ec,
const std::string &symbol_name, const std::string &get_instruments_message);
/**
* @brief Provides user all instruments being traded on the exchange.
*
* @param complete_security_list Vector containing all instruments currently being traded on the exchange.
*/
virtual void on_securities(const std::vector<emumba::market_data::security const *> &complete_security_list);
/**
* @brief Handle updates in order book of an instrument.
*
* @param book Structure containing order book of an instrument.
* @param ptr User defined ptr to be returned on updates for a specific instrument. Defaults to null.
*/
virtual void on_book(const emumba::market_data::order_book &book, const void *ptr = nullptr);
/**
* @brief Handle change in the state of an instrument.
*
* @param sec_state State of an instrument.
*/
virtual void on_security_state(const emumba::market_data::security_state &sec_state, const void *ptr = nullptr);
/**
* @brief Handle trade updates for instruments.
*
* @param trades Vector of structures containing useful information related to trade updates of different instruments.
*/
virtual void on_trades(const std::vector<emumba::market_data::trade_record const *> &trades,
const void * ptr = nullptr);
/**
* @brief Handle trade updates received against an order placed by the user.
*
* @param order_key Unique key assigned to order at the time of placing order.
* @param trade Structure containing trade information related to a specific order placed by the user.
*/
virtual void on_private_trade(const emumba::order_entry::order_key & order_key,
const emumba::market_data::trade_record &trade);
/**
* @brief Handle different errors which can occur while the connector is running.
*
* @param error Structure containing the type of error which occured.
*/
virtual void on_error(const emumba::market_data::market_data_error &error);
/**
* @brief Handle disconnection of a session.
*
* @param disconnected_sess Structure containing information regarding session which has been disconnected.
*/
virtual void on_disconnection(const emumba::market_data::disconnected_session &disconnected_sess);
virtual void on_request_for_quote(__attribute__((unused)) const emumba::order_entry::rfq_response &rfq_response);
virtual void on_create_combo(__attribute__((unused)) const emumba::order_entry::combo_detail &combo_detail);
#ifdef E2E_PERF
virtual void e2e_perf_on_book(__attribute__((unused)) emumba::market_data::order_book &book,
__attribute__((unused)) const double & change_id);
#endif
};
} // namespace deribit_connector
} // namespace emumba
#endif
@@ -0,0 +1,332 @@
/**
* @file deribit_utils.h
* @brief Provides structures used in APIs by the Deribit connector.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned by Emumba (Pvt) Ltd [Emumba] and is
* protected by copyright law and international copyright treaties. Access to and use of the software is governed by the
* terms of the applicable Emumba Software Services Agreement (the Agreement) and Customer end user license agreements
* granting a non-assignable, non-transferable and non-exclusive license to use the software for it's own data processing
* purposes under the terms defined in the Agreement. Except as otherwise granted within the terms of the Agreement, copying
* or reproduction of any part of this source code or associated reference material to any other location for further
* reproduction or redistribution, and any amendments to this copyright notice, are expressly prohibited. Any reproduction or
* redistribution for sale or hiring of the Software not in accordance with the terms of the Agreement is a violation of
* copyright law
*/
#ifndef DERIBIT_UTILS_H
#define DERIBIT_UTILS_H
#include "error.h"
#include <unordered_set>
#define UNUSED_PARAM __attribute__((unused))
#define ALWAYS_INLINE inline __attribute__((always_inline))
#define UNUSED(x) (void)(x)
namespace emumba {
namespace deribit_connector {
namespace data_types {
/**
* @brief Determines the status of order book of an instrument.
*
*/
enum class book_status
{
red, /**< Value = 0. Instrument is not ready for trading. */
green /**< Value = 1. Instrument is ready for trading. */
};
/**
* @brief Determines the order cancellation request type.
*
*/
enum class cancellation_type
{
by_instrument, /**Value = 0. Orders cancellation by instrument*/
by_currency /**Value = 1. Orders cancellation by currency*/
};
/**
* @brief Structure containing information related to cancel orders.
*
*/
struct cancellation_update
{
cancellation_type type;
emumba::deribit_connector::data_types::error ec;
uint no_of_orders_cancelled;
uint subaccount_index;
};
} // namespace data_types
namespace configuration {
/**
* @brief Holds the subscription information of the trading client.
*
*/
struct subscription_config
{
/**
* @brief If true, subscribes to the "book.{instrument_name}.{interval}" for configured instruments.
*
*/
bool public_book = false;
/**
* @brief If true, subscribes to "quote.{instrument_name}" for configured instruments.
*
*/
bool public_quote = false;
/**
* @brief If true, subscribes to "trades.{instrument_name}.{interval}" for configured instruments.
*
*/
bool public_trade = false;
/**
* @brief If true, subscribes to "ticker.{instrument_name}.{interval}" for configured instruments.
*
*/
bool public_ticker = false;
/**
* @brief If true, subscribes to "markprice.options.{index_name}" for configured symbol.
*
*/
bool public_markprice = false;
/**
* @brief If true, subscribes to "deribit_price_index.{index_name}" for configured symbols.
*
*/
bool public_index = false;
/**
* @brief If true, subscribes to "user.portfolio.{currency}" for configured symbols.
*
*/
bool private_portfolio = false;
/**
* @brief If true, no aggregation will be applied on the notification of events.
*
*/
bool use_raw_interval = true;
/**
* @brief Configured depth for book updates.
*
*/
uint book_depth = 5;
/**
* @brief Max number of subscription requests that can be sent in succession.
*
*/
uint requests_limit = 20;
};
/**
* @brief Holds the configuration for the desired currency symbol.
*
*/
struct symbol_config
{
/**
* @brief Name of the currency symbol.
*
*/
std::string symbol_name;
/**
* @brief Currency type. Either "usd" or "usdt".
*
*/
std::string currency_type = "usd";
/**
* @brief Index of credential to use for subscription
*
*
*/
uint cred_index;
/**
* @brief List of unique instrument identifiers that the trading client is interested in.
*
*/
std::unordered_set<std::string> interested_instruments;
/**
* @brief Configuration of interested channels for subscription.
*
*/
subscription_config interested_subscriptions;
/**
* @brief Set true if interested in future instruments. All instruments will be of interest incase all
* intereseted_in_... flags are set to false.
*
*/
bool interested_in_future = false;
/**
* @brief Set true if interested in future combo instruments. All instruments will be of interest incase all
* intereseted_in_... flags are set to false.
*
*/
bool interested_in_future_combo = false;
/**
* @brief Set true if interested in option instruments. All instruments will be of interest incase all
* intereseted_in_... flags are set to false.
*
*/
bool interested_in_option = false;
/**
* @brief Set true if only interested in option combo instruments. All instruments will be of interest incase all
* intereseted_in_... flags are set to false.
*
*/
bool interested_in_option_combo = false;
};
/**
* @brief Holds the configuration used for logging.
*
*/
struct logging_config
{
/**
* @brief The level of logging i.e info, warning or error.
*
*/
std::string severity;
/**
* @brief If true, logs will be printed to the console.
*
*/
bool is_console_enabled;
/**
* @brief If true, logs will be printed to the log file.
*
*/
bool is_file_enabled;
/**
* @brief Sets the buffer size for async logging. Defaults to 8192.
*
*/
uint32_t queue_size;
logging_config();
};
/**
* @brief Holds the configuration for reconnection attempts in case the connection to Deribit server is lost.
* Initializing this struct through config file is optional. If not initialized, the struct fields will be
* assigned the following default values
*
* retry_count = 3;
* retry_interval = 1 second;
* reception_timeout = 60 seconds;
*/
struct reconnection_config
{
/**
* @brief Numbers of retries to reconnect to the server.
*
*/
int retry_count;
/**
* @brief Time to wait (in seconds) in between reconnection attempts.
*
*/
int retry_interval;
/**
* @brief Maximum time (in seconds) to wait for reception on a connected websocket session before re-connecting the
* session
*
*/
int reception_timeout;
reconnection_config();
};
/**
* @brief Holds uuid and internal order id used to track individual orders placed by an instance of the connector.
*
*/
struct order_tracker
{
/**
* @brief Unique identifier used as label in new order placement to identify the instance to which the order
* belongs.
*
*/
std::string uuid;
/**
* @brief Order id used internally by the connector to track each order individually.
*
*/
uint start_id;
order_tracker();
};
/**
* @brief Holds the configuration required to set up a trading session.
*
*/
struct trading_session_config
{
/**
* @brief Structure used to track orders placed by the connector.
*
*/
order_tracker orders_tracker;
/**
* @brief Binds the application to the user-specified processor core.
*
*/
uint core;
/**
* @brief The heartbeat interval in seconds. Can not be less than 10.
*
*/
int heartbeat_interval;
/**
* @brief Specifies if Cancel On Disconnect should be applied/checked for the current connection.
*
*/
bool cancel_on_disconnect; // TODO: remove this flag. This is no longer configurable by user.
/**
* @brief structure containing the settings related to reconnection i.e. retry_count, retry_interval and
* reception_timeout.
*
*/
reconnection_config reconnect_config;
trading_session_config();
};
struct credential_config
{
/**
* @brief Name of the client's trading software.
*/
std::string client_name;
/**
* @brief Client identifier used for authentication.
*/
std::string client_id;
/**
* @brief Client secret used for authentication.
*
*/
std::string secret_key;
/**
* @brief Credential config id
*
*/
uint index;
credential_config();
};
} // namespace configuration
} // namespace deribit_connector
} // namespace emumba
#endif /* DERIBIT_UTILS_H */
@@ -0,0 +1,37 @@
#ifndef DOCUMENT_TO_STRING_H
#define DOCUMENT_TO_STRING_H
#include "rapidjson/document.h"
#include "rapidjson/istreamwrapper.h"
#include "rapidjson/ostreamwrapper.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
namespace emumba {
namespace deribit_connector {
class document_to_string
{
public:
document_to_string()
: _writer(_string_buffer)
{
_string_buffer.Reserve(1000);
}
template <typename T>
const std::string to_string(const T &doc)
{
_string_buffer.Clear();
_writer.Reset(_string_buffer);
doc.Accept(_writer);
return std::string(_string_buffer.GetString());
}
private:
rapidjson::StringBuffer _string_buffer;
rapidjson::Writer<rapidjson::StringBuffer> _writer;
};
} // namespace deribit_connector
} // namespace emumba
#endif
@@ -0,0 +1,22 @@
#include "error.h"
emumba::deribit_connector::data_types::error::error(const int &err_code, const std::string &message,
const std::string &details)
: _code(err_code)
, _msg(message)
, _details(details)
{}
const int emumba::deribit_connector::data_types::error::value() const
{
return _code;
}
const std::string emumba::deribit_connector::data_types::error::message() const
{
return _msg;
}
const std::string emumba::deribit_connector::data_types::error::details() const
{
return _details;
}
@@ -0,0 +1,59 @@
/**
* @file error.h
* @brief Provides a class to represent occurrence of an error in incoming messages.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned by Emumba (Pvt) Ltd [Emumba] and is
* protected by copyright law and international copyright treaties. Access to and use of the software is governed by the
* terms of the applicable Emumba Software Services Agreement (the Agreement) and Customer end user license agreements
* granting a non-assignable, non-transferable and non-exclusive license to use the software for it's own data processing
* purposes under the terms defined in the Agreement. Except as otherwise granted within the terms of the Agreement, copying
* or reproduction of any part of this source code or associated reference material to any other location for further
* reproduction or redistribution, and any amendments to this copyright notice, are expressly prohibited. Any reproduction or
* redistribution for sale or hiring of the Software not in accordance with the terms of the Agreement is a violation of
* copyright law
*/
#ifndef EMUMBA_DERIBIT_ERROR_H
#define EMUMBA_DERIBIT_ERROR_H
#include <string>
namespace emumba {
namespace deribit_connector {
namespace data_types {
/**
*
* @brief Represents error in incoming messages.
*/
class error
{
public:
error(const int &err_code = 0, const std::string &message = "Success", const std::string &details = "");
/**
* @brief Returns the error code for a message.
*
* @return const int
*/
const int value() const;
/**
* @brief Returns the error message.
*
* @return const std::string
*/
const std::string message() const;
/**
* @brief Returns the details for error message.
*
* @return const std::string
*/
const std::string details() const;
private:
int _code;
std::string _msg;
std::string _details;
};
} // namespace data_types
} // namespace deribit_connector
} // namespace emumba
#endif
@@ -0,0 +1,286 @@
#ifndef INSTRUMENT_DATA_H
#define INSTRUMENT_DATA_H
#include "deribit_listener.h"
#include "deribit_utils.h"
#include "instrument_data_interface.h"
#include "market_data_common.h"
#include "price_level.h"
#include "spdlogger.h"
#include "utils.h"
#include <limits>
#include <map>
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include <rapidjson/ostreamwrapper.h>
#include <rapidjson/stringbuffer.h>
#include <vector>
namespace emumba {
namespace deribit_connector {
class instrument_data
{
public:
instrument_data(const std::string &instrument_name, const uint &book_depth, instrument_info inst_info,
deribit_listener *trade_listener);
ALWAYS_INLINE bool update_order_book(const rapidjson::Value &book_message)
{
std::string action;
double price;
double quantity;
_depth_levels_updated = false;
for (rapidjson::Value::ConstValueIterator bid_update_itr = book_message["data"]["bids"].Begin();
bid_update_itr != book_message["data"]["bids"].End(); ++bid_update_itr)
{
auto bid_update = bid_update_itr->GetArray();
action = bid_update[0].GetString();
price = bid_update[1].GetDouble();
quantity = bid_update[2].GetDouble();
LOG_TRACE("Bid Book Update | Action: {}, Price: {}, Quantity: {}", action, price, quantity);
if (price >= _bid_price_at_depth)
{
_depth_levels_updated = true;
}
if (action == "new")
{
if (__builtin_expect(_bid.insert(std::make_pair(price, data_types::price_level(
emumba::market_data::price(price), quantity)))
.second == false,
false))
{
LOG_ERROR("PriceLevel already exists. Could not insert new PriceLevel.");
return false;
}
++_bid_map_size;
}
else if (action == "change")
{
_iterator = _bid.find(price);
if (__builtin_expect(_iterator != _bid.end(), true))
{
_iterator->second.set_amount(quantity);
}
else
{
LOG_ERROR("PriceLevel not found.");
return false;
}
}
else if (action == "delete")
{
_iterator = _bid.find(price);
if (__builtin_expect(_iterator != _bid.end(), true))
{
_bid.erase(_iterator);
--_bid_map_size;
}
else
{
LOG_ERROR("PriceLevel not found.");
return false;
}
}
else
{
LOG_ERROR("Unknow Update Action Received: {}", action);
return false;
}
}
for (rapidjson::Value::ConstValueIterator ask_update_itr = book_message["data"]["asks"].Begin();
ask_update_itr != book_message["data"]["asks"].End(); ++ask_update_itr)
{
auto ask_update = ask_update_itr->GetArray();
action = ask_update[0].GetString();
price = ask_update[1].GetDouble();
quantity = ask_update[2].GetDouble();
LOG_TRACE("Ask Book Update | Action: {}, Price: {}, Quantity: {}", action, price, quantity);
if (price <= _ask_price_at_depth)
{
_depth_levels_updated = true;
}
if (action == "new")
{
if (__builtin_expect(_ask.insert(std::make_pair(price, data_types::price_level(
emumba::market_data::price(price), quantity)))
.second == false,
false))
{
LOG_ERROR("PriceLevel already exists. Could not insert new PriceLevel.");
return false;
}
++_ask_map_size;
}
else if (action == "change")
{
_iterator = _ask.find(price);
if (__builtin_expect(_iterator != _ask.end(), true))
{
_iterator->second.set_amount(quantity);
}
else
{
LOG_ERROR("PriceLevel not found.");
return false;
}
}
else if (action == "delete")
{
_iterator = _ask.find(price);
if (__builtin_expect(_iterator != _ask.end(), true))
{
_ask.erase(_iterator);
--_ask_map_size;
}
else
{
LOG_ERROR("PriceLevel not found.");
return false;
}
}
else
{
LOG_ERROR("Unknown Update Action Received: {}", action);
return false;
}
}
set_previous_change_id(book_message["data"]["change_id"].GetDouble());
// API ENABLED ONLY
if (_depth_levels_updated == true)
{
_book.bids.level.assign(_book_depth, emumba::market_data::book_level());
_book.asks.level.assign(_book_depth, emumba::market_data::book_level());
uint index = 0;
for (_iterator = _bid.begin(); index != _book_depth && _iterator != _bid.end(); ++_iterator, ++index)
{
_book.bids.level[index].direct_qty = _iterator->second.get_amount();
_book.bids.level[index].total_qty = _iterator->second.get_amount();
_book.bids.level[index].px = _iterator->second.get_price();
}
index = 0;
for (_iterator = _ask.begin(); index != _book_depth && _iterator != _ask.end(); ++_iterator, ++index)
{
_book.asks.level[index].direct_qty = _iterator->second.get_amount();
_book.asks.level[index].total_qty = _iterator->second.get_amount();
_book.asks.level[index].px = _iterator->second.get_price();
}
#ifndef E2E_PERF
#ifdef ENABLE_API
_trade_listener->on_book(_book, _api_handler);
#endif
#endif
LOG_TRACE("Book Update API Called, Instrument: {}", _instrument_name);
assert(_bid.size() == _bid_map_size);
assert(_ask.size() == _ask_map_size);
_bid_price_at_depth =
_bid_map_size >= _book_depth ? _book.bids.level[_book_depth - 1].px.to_double() : _lowest_possible_bid;
_ask_price_at_depth =
_ask_map_size >= _book_depth ? _book.asks.level[_book_depth - 1].px.to_double() : _highest_possible_ask;
LOG_TRACE("Instrument: {}. Bid price at depth: {}, Ask price at depth: {}", _instrument_name,
_bid_price_at_depth, _ask_price_at_depth);
}
#ifdef E2E_PERF
#ifdef ENABLE_API
_trade_listener->e2e_perf_on_book(_book, book_message["data"]["change_id"].GetDouble());
#endif
#endif
return true;
}
bool check_message_loss(const double &previous_change_id);
ALWAYS_INLINE data_types::book_status get_book_status()
{
return _book_status;
}
ALWAYS_INLINE void change_book_status(const data_types::book_status &book_status)
{
_book_status = book_status;
LOG_DEBUG("Changed book status: {}", _book_status == data_types::book_status::green ? "GREEN" : "RED");
#ifdef ENABLE_API
_trade_listener->on_book_state_change(_instrument_name, _book_status, _api_handler);
#endif
}
void reset_instrument();
void * get_api_handler();
std::ostream & print_order_book(std::ostream &out, int level = 0);
std::ostream & print_order_book_all_levels(std::ostream &out);
const double get_previous_change_id();
const uint64_t get_exchange_ts();
const std::string get_name();
const double get_last_traded_price();
const double get_last_traded_quantity();
const double get_total_traded_quantity();
const long double get_total_traded_value();
void update_trade_info(const double &ltp, const double &ltq);
data_types::price_level::iterator get_top(int side);
data_types::price_level::iterator get_level(int side, unsigned lvl);
bool is_iterator_valid(data_types::price_level::iterator it);
int get_depth(int side);
void set_instrument_data_interface(std::shared_ptr<emumba::deribit_connector::instrument_data_interface> interface);
void set_api_handler(void *ref_ptr);
emumba::market_data::security_state::state get_current_state();
void set_current_state(emumba::market_data::security_state::state new_state);
void set_inst_closed_flag(const bool &instrument_closed);
const bool & get_inst_closed_flag() const;
friend std::ostream & operator<<(std::ostream &out, instrument_data &instrument_data)
{
return instrument_data.print_order_book(out);
}
private:
ALWAYS_INLINE void set_previous_change_id(const double &change_id)
{
_previous_change_id = change_id;
LOG_TRACE("Updated _previous_change_id: {}", _previous_change_id);
}
std::string _instrument_name;
std::map<double, data_types::price_level, std::greater<double>>
_bid; // High to low order required therefore std::greater is used.
std::map<double, data_types::price_level> _ask;
std::map<double, data_types::price_level>::iterator _iterator;
double _previous_change_id;
double _lowest_possible_bid;
double _highest_possible_ask;
double _bid_price_at_depth;
double _ask_price_at_depth;
size_t _bid_map_size;
size_t _ask_map_size;
data_types::book_status _book_status;
uint _book_depth;
instrument_info _instrument_info;
deribit_listener * _trade_listener;
void * _api_handler;
bool _depth_levels_updated;
std::shared_ptr<instrument_data_interface> _interface;
uint64_t _exchange_update_ts;
double _last_traded_price;
double _last_traded_quantity;
double _total_traded_quantity;
long double _total_traded_volume;
emumba::market_data::security_state _current_sec_state;
bool _is_inst_closed;
// API ENABLED ONLY
emumba::market_data::order_book _book;
};
} // namespace deribit_connector
} // namespace emumba
#endif /* INSTRUMENT_DATA_H */
@@ -0,0 +1,129 @@
/**
* @file instrument_data_interface.h
* @brief Provides a class with an interface used to get important information (maintained by the connector) related to an
* instrument.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned by Emumba (Pvt) Ltd [Emumba] and is
* protected by copyright law and international copyright treaties. Access to and use of the software is governed by the
* terms of the applicable Emumba Software Services Agreement (the Agreement) and Customer end user license agreements
* granting a non-assignable, non-transferable and non-exclusive license to use the software for it's own data processing
* purposes under the terms defined in the Agreement. Except as otherwise granted within the terms of the Agreement, copying
* or reproduction of any part of this source code or associated reference material to any other location for further
* reproduction or redistribution, and any amendments to this copyright notice, are expressly prohibited. Any reproduction or
* redistribution for sale or hiring of the Software not in accordance with the terms of the Agreement is a violation of
* copyright law
*
*/
#ifndef INSTRUMENT_DATA_INTERFACE_H
#define INSTRUMENT_DATA_INTERFACE_H
#include "deribit_utils.h"
#include "price_level.h"
#include <string>
namespace emumba {
namespace deribit_connector {
class instrument_data;
/**
* @brief Provides functions which can be used to get useful information related to an instrument. This information is
* maintained by the connector.
*
*/
class instrument_data_interface
{
public:
instrument_data_interface(instrument_data *instrument);
/**
* @brief Get exchange timestamp for last book update of instrument
*
* @return uint64_t
*/
const uint64_t get_exchange_ts();
/**
* @brief Get Change ID last book update of instrument
*
* @return uint64_t
*/
const uint64_t get_last_change_id();
/**
* @brief Get Last Traded price for the instrument
*
* @return double
*/
const double get_last_traded_price();
/**
* @brief Get Total Traded Value uptil now for the instrument
*
* @return double
*/
const long double get_total_traded_value();
/**
* @brief Get Total Traded Quantity uptil now for the instrument
*
* @return long double
*/
const double get_total_traded_quantity();
/**
* @brief Get instrument name
*
* @return std::string
*/
const std::string get_instrument_name();
/**
* @brief Get Exchange name
*
* @return std::string
*/
const std::string get_exchange();
/**
* @brief Get Trading state for instrument
*
* @return std::string
*/
const data_types::book_status get_trading_state();
/**
* @brief Get top iterator of book
*
* @return data_types::price_level::iterator
*/
data_types::price_level::iterator top(int side);
/**
* @brief Get iterator of book for the specified level
*
* @return data_types::price_level::iterator
*/
data_types::price_level::iterator level(int side, unsigned lvl);
/**
* @brief Check validity of iterator
*
* @return bool
*/
bool valid(data_types::price_level::iterator it);
/**
* @brief Check depth of book
*
* @return uint16_t
*/
uint16_t depth(int side);
/**
* @brief Get book of instrument in pretty format
*
* @return std::string
*/
const std::string get_book_to_string();
/**
* @brief Get book of instrument up to specified level in pretty format
*
* @return std::string
*/
const std::string get_book_to_string(int lvl);
private:
instrument_data *_instrument;
};
} // namespace deribit_connector
} // namespace emumba
#endif
@@ -0,0 +1,24 @@
#ifndef _LICENSE_VALIDATOR_H
#define _LICENSE_VALIDATOR_H
#ifdef LICENSE_CHECK
namespace emumba {
namespace deribit_connector {
class license_validator
{
public:
license_validator();
bool check_license();
void set_license_valid_status();
bool is_license_file_valid();
private:
bool is_valid;
};
} // namespace deribit_connector
} // namespace emumba
#endif // LICENSE_CHECK
#endif /*_LICENSE_VALIDATOR_H*/
@@ -0,0 +1,469 @@
/**
* @file market_data_common.h
* @brief Provide structures used in market data API callbacks.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned by Emumba (Pvt) Ltd [Emumba] and is
* protected by copyright law and international copyright treaties. Access to and use of the software is governed by the
* terms of the applicable Emumba Software Services Agreement (the Agreement) and Customer end user license agreements
* granting a non-assignable, non-transferable and non-exclusive license to use the software for it's own data processing
* purposes under the terms defined in the Agreement. Except as otherwise granted within the terms of the Agreement, copying
* or reproduction of any part of this source code or associated reference material to any other location for further
* reproduction or redistribution, and any amendments to this copyright notice, are expressly prohibited. Any reproduction or
* redistribution for sale or hiring of the Software not in accordance with the terms of the Agreement is a violation of
* copyright law
*
*/
#ifndef EMUMBA_MARKET_DATA_COMMON_H
#define EMUMBA_MARKET_DATA_COMMON_H
#include "price.h"
#include <array>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <ctime>
#include <limits>
#include <ostream>
#include <string>
#include <variant>
#include <vector>
namespace emumba {
namespace market_data {
/**
* @brief A templated wrapper class with overloaded operators which can be used to define objects with different data
* types.
*
* @tparam Tag
* @tparam ValueT Data type of the templated class object.
*/
template <class Tag, class ValueT>
class value_wrapper
{
public:
constexpr value_wrapper() = default;
~value_wrapper() = default;
constexpr explicit value_wrapper(ValueT const &v)
: _v(v)
{}
constexpr explicit value_wrapper(ValueT &&v)
: _v(std::move(v))
{}
constexpr value_wrapper(value_wrapper const &v) = default;
constexpr value_wrapper(value_wrapper &&v) noexcept = default;
value_wrapper &operator=(value_wrapper const &v) = default;
value_wrapper &operator=(value_wrapper &&v) noexcept = default;
constexpr bool operator==(value_wrapper const &v) const
{
return _v == v._v;
}
constexpr bool operator!=(value_wrapper const &v) const
{
return _v != v._v;
}
constexpr bool operator<(value_wrapper const &v) const
{
return _v < v._v;
}
constexpr bool operator<=(value_wrapper const &v) const
{
return _v <= v._v;
}
constexpr bool operator>(value_wrapper const &v) const
{
return _v > v._v;
}
constexpr bool operator>=(value_wrapper const &v) const
{
return _v >= v._v;
}
constexpr ValueT const &value() const
{
return _v;
}
constexpr ValueT &value()
{
return _v;
}
private:
ValueT _v;
};
using quantity = value_wrapper<struct quantity_tag, std::variant<std::uint32_t, double>>;
using security_id = value_wrapper<struct secuirty_id_tag, std::variant<std::uint32_t, std::string>>;
// Before converting security id to integer value, make sure the orders can be placed properly since string value is
// required.
using MarketDataListenerID = value_wrapper<struct market_data_listened_id_tag, std::uint32_t>;
/**
* @brief Enum class used for the type of instrument.
*
*/
enum class security_type
{
index, /**< Value = 0. Index instrument. */
future, /**< Value = 1. Future instrument. */
spread, /**< Value = 2. Spread instrument. */
option /**< Value = 3. Option instrument. */
};
/**
* @brief Enum class used for the currency symbol.
*
*/
enum class currency
{
btc, /**< Value = 0. BTC. */
eth, /**< Value = 1. Ethereum. */
usd /**< Value = 2. US Dollar. */
};
/**
* @brief Enum class used for the exchange venue.
*
*/
enum class venue
{
glbx = 0, /**< Value = 0. CME Globex exchange. */
deribit = 1, /**< Value = 1. Deribit exchange. */
huobi = 2, /**< Value = 2. Huobi exchange. */
binance = 3, /**< Value = 3. Binance exchange. */
ftx = 4 /**< Value = 4. FTX exchange. */
};
/**
* @brief Enum class used for the type of option contract.
*
*/
enum class option_type
{
call, /**< Value = 0. Call type option. */
put /**< Value = 1. Put type option. */
};
/**
* @brief Stores exchange specific information required to configure the connector for CME.
*
*/
struct glbx_market_data_subscription
{
};
/**
* @brief Stores exchange specific information required to configure the connector for Deribit.
*
*/
struct deribit_market_data_subscription
{
};
/**
* @brief Used to configure a specific exchange for the connector. (Currently not in use)
*
*/
struct market_data_gateway_subscription
{
const venue exchange;
const std::variant<glbx_market_data_subscription, deribit_market_data_subscription> subscription = {
deribit_market_data_subscription{}};
};
/**
* @brief Used for handling the timestamp received in different update messages.
*
*/
struct exchange_timestamp
{
const uint64_t &get_timestamp() const
{
return _v;
}
void set_timestamp(uint64_t t)
{
_v = t;
}
private:
// posix time
uint64_t _v{};
};
/**
* @brief Stores exchange specific information received in the Deribit instrument definition.
*
*/
struct security_deribit
{
uint32_t contract_size;
bool is_active;
double min_trade_amount;
std::string settlement_period;
double block_trade_commission;
double maker_commission;
double taker_commission;
};
/**
* @brief Stores exchange specific information received in the CME instrument definition.
*
*/
struct security_cme
{
double display_factor{};
double cash_per_point{};
std::string asset;
bool implied_matching = false;
};
/**
* @brief Stores important fields received in the definition of the instruments which are common among different
* exchanges.
*
*/
class security_common
{
public:
venue exchange;
security_id sec_id{};
security_type sec_type;
currency ccy;
std::string symbol;
std::string security_group;
price min_price_increment{};
security_cme sec_cme;
security_deribit sec_deribit;
};
/**
* @brief Stores important fields related to future instrument.
*
*/
class future
{
public:
uint32_t leverage;
exchange_timestamp expiration_ts;
exchange_timestamp creation_ts;
};
/**
* @brief Stores important fields related to options instrument.
*
*/
class option
{
public:
option_type op_type;
price strike;
exchange_timestamp expiration_ts;
exchange_timestamp creation_ts;
};
/**
* @brief Stores important fields related to leg definition.
*
*/
struct leg_definition
{
security_id sec_id;
quantity leg_mult;
};
/**
* @brief Stores legs of a spread definition.
*
*/
class spread
{
public:
std::vector<leg_definition> legs;
};
/**
* @brief Stores important fields related to index instrument.
*
*/
class index
{
};
/**
* @brief Stores information related to an instrument.
*
*/
struct security
{
security_common sec_common;
std::variant<future, option, spread, index> sec;
security_type sec_type() const
{
if (std::holds_alternative<future>(sec))
{
return security_type::future;
}
else if (std::holds_alternative<option>(sec))
{
return security_type::option;
}
else if (std::holds_alternative<spread>(sec))
{
return security_type::spread;
}
return security_type::index;
}
};
/**
* @brief Used to handle a price level of a book.
*
*/
struct book_level
{
price px = {}; // original declarations were const
double total_qty = {};
double direct_qty = {};
size_t n_orders = 0;
double implied_qty() const
{
return double{};
}
};
/**
* @brief Stores all price levels of a book.
*
*/
struct book_side
{
std::vector<book_level> level;
};
/**
* @brief Stores books of an instrument.
*
*/
struct order_book
{
security_id sec_id;
book_side bids;
book_side asks;
};
/**
* @brief Enum class used for the aggressor side received in a trade update.
*
*/
enum class aggressor_side
{
buy = 0, /**< Value = 0. Buy. */
sell = 1, /**< Value = 1. Sell. */
none = 2 /**< Value = 2. None. */
};
/**
* @brief Stores important informantion related to a trade update.
*
*/
struct trade_record
{
security_id sec_id = {};
exchange_timestamp timestamp = {};
aggressor_side agg_side = {};
double trade_quantity;
price trade_price;
std::string exchange_order_id;
};
/**
* @brief Stores the state of an instrument.
*
*/
struct security_state
{
security_id sec_id{};
enum class state
{
preopen, /**< Value = 0. Preopen. */
open, /**< Value = 1. Open. */
closed /**< Value = 2. Closed. */
};
state state_ = state::open;
};
/**
* @brief Used for handling error messages received from the exchanges.
*
*/
struct market_data_error
{
enum class error_type
{
disconnection, /**< Value = 0. Disconnection. */
whitelist_empty /**< Value = 1. Whitelist empty. No instrument selected. Possible reason: Invalid instruments
specified in "interested_instruments" list. */
};
error_type err_type = error_type::disconnection;
std::string err_text = {};
};
/**
* @brief Stores important information related to statistics messages.
*
*/
struct market_data_statistics
{
security_id sec_id;
price low;
price high;
price open;
price settle;
price mark_price;
double volume;
};
/**
* @brief Stores important information related to a quote update.
*
*/
struct quote
{
security_id sec_id;
price best_bid_price;
price best_ask_price;
double best_bid_amount;
double best_ask_amount;
exchange_timestamp timestamp = {};
};
/**
* @brief Enum used for identifying the type of session.
*
*/
enum class session_type
{
market_data,
order_entry
};
/**
* @brief Stores information related to the disconnected session
*
*/
struct disconnected_session
{
uint session_id;
std::string name;
session_type sess_type;
};
} // namespace market_data
} // namespace emumba
#endif /*EMUMBA_MARKET_DATA_COMMON_H*/
@@ -0,0 +1,45 @@
#ifndef METHOD_IDENTIFIERS_H
#define METHOD_IDENTIFIERS_H
enum class method_id : int
{
gettime = 1000, // Supporting
hello,
test,
auth, // Authentication
exchange_token,
fork_token,
logout,
set_heartbeat, // Session Management
disable_heartbeat,
enable_cancel_on_disconnect,
disable_cancel_on_disconnect,
get_cancel_on_disconnect,
public_subscribe, // Subscription Management
public_unsubscribe,
private_subscribe,
private_unsubscribe,
get_account_summary, // Account Management
get_open_orders_by_instrument, // Trading
get_instruments, // Market Data
get_combos,
get_instrument,
get_positions,
get_open_orders_by_currency,
get_user_trades_by_currency_and_time,
new_order,
edit_order,
cancel_order,
cancel_all_by_currency,
cancel_all_by_instrument,
cancel_all_positions,
get_position,
set_mmp_config,
get_mmp_config,
reset_mmp_config,
rfq_request,
create_combo,
};
#endif /* METHOD_IDENTIFIERS_H */
@@ -0,0 +1,334 @@
/**
* @file order_entry_common.h
* @brief Provide structures used in order entry APIs.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned by Emumba (Pvt) Ltd [Emumba] and is
* protected by copyright law and international copyright treaties. Access to and use of the software is governed by the
* terms of the applicable Emumba Software Services Agreement (the Agreement) and Customer end user license agreements
* granting a non-assignable, non-transferable and non-exclusive license to use the software for it's own data processing
* purposes under the terms defined in the Agreement. Except as otherwise granted within the terms of the Agreement, copying
* or reproduction of any part of this source code or associated reference material to any other location for further
* reproduction or redistribution, and any amendments to this copyright notice, are expressly prohibited. Any reproduction or
* redistribution for sale or hiring of the Software not in accordance with the terms of the Agreement is a violation of
* copyright law
*
*/
#ifndef EMUMBA_ORDER_ENTRY_COMMON_H
#define EMUMBA_ORDER_ENTRY_COMMON_H
#include "error.h"
#include "market_data_common.h"
#include "price.h"
#include <optional>
#include <string>
#include <vector>
namespace emumba {
namespace order_entry {
using order_key = emumba::market_data::value_wrapper<struct order_key_tag, std::int64_t>;
using user_id = emumba::market_data::value_wrapper<struct user_id_tag, std::uint64_t>;
using account_id = emumba::market_data::value_wrapper<struct account_id_tag, std::uint64_t>;
/**
* @brief Enum class used for the status of an order.
*
*/
enum class status
{
working, /**< Value = 0. Order state is Working/open. */
completed, /**< Value = 1. Order state is Completed/filled. */
canceled, /**< Value = 2. Order state is Canceled. */
rejected, /**< Value = 3. Order state is Rejected. */
untriggered /**< Value = 4. Order state is Untriggered. */
};
/**
* @brief Enum class used for update type of an order
*
*/
enum class update_type
{
new_order_confirm, /**< Value = 0. New order confirmed*/
new_order_reject, /**< Value = 1. New order rejected*/
edit_order_confirm, /**< Value = 2. Edit order confirmed*/
edit_order_reject, /**< Value = 3. Edit order rejected*/
cancel_order_confirm, /**< Value = 4. Cancel order confirmed*/
cancel_order_reject, /**< Value = 5. Cancel order rejected*/
subscription_update /**< Value = 6. Subscription update message*/
};
/**
* @brief Stores exchange specific information received in the Deribit order update message.
*
*/
struct deribit_order_state
{
std::string instrument_name;
std::string exchange_order_id;
std::string direction;
emumba::market_data::exchange_timestamp timestamp;
};
/**
* @brief Stores exchange specific information received in the CME order update message.
*
*/
struct glbx_order_state
{
};
/**
* @brief Stores important information received in the order update message which is commong among different
* exchanges.
*
*/
struct order_state
{
uint subaccount_index;
emumba::deribit_connector::data_types::error error;
update_type order_update_type;
status order_status;
emumba::market_data::quantity qty;
emumba::market_data::quantity total_fills;
emumba::market_data::price px;
emumba::market_data::price avg_price;
std::vector<emumba::market_data::trade_record const *> trades;
std::variant<deribit_order_state, glbx_order_state> exchange_order_state;
order_state();
};
/**
* @brief Enum class used for time in force.
*
*/
enum class time_in_force
{
day = 48, /**< Value = 48. Good till Day. */
gtc = 49, /**< Value = 49. Good till Cancelled. */
ioc = 51, /**< Value = 51. Immediate or Cancel. */
date = 52, /**< Value = 52. Good till Date. */
fill_or_kill = 53 /**< Value = 53. Fill or Kill. */
};
/**
* @brief Enum class used for type of order.
*
*/
enum class order_type
{
limit_order = 1, /**< Value = 1. Limit Order. */
stop_limit_order, /**< Value = 2. Stop Limit Order. */
take_limit_order, /**< Value = 3. Take Limit Order. */
market_order, /**< Value = 4. Market Order. */
stop_market_order, /**< Value = 5. Stop Market Order. */
take_market_order, /**< Value = 6. Take Market Order. */
market_limit_order /**< Value = 7. Market Limit Order. */
};
/**
* @brief Enum class used for order trigger.
*
*/
enum class order_trigger
{
index_price, /**< Value = 0. Index Price. */
mark_price, /**< Value = 1. Mark Price. */
last_price /**< Value = 2. Last Price. */
};
/**
* @brief Enum class used for order advanced
*
*/
enum class order_advanced
{
usd, /**< Value = 0. USD. */
implv /**< Value = 1. IMPLV. */
};
/**
* @brief Stores optional fields required to place an order.
*
*/
struct deribit_order_def_optional
{
std::optional<double> max_show;
std::optional<emumba::market_data::price> trigger_price;
std::optional<order_trigger> trigger;
std::optional<order_advanced> advanced;
bool post_only = false;
bool reject_post_only = false;
bool reduce_only = false;
bool mmp = false;
};
/**
* @brief Stores information required to place an order on Deribit exchange.
*
*/
struct deribit_order_definition
{
order_type ord_type;
std::optional<deribit_order_def_optional> optional_fields;
};
/**
* @brief Stores information required to place an order on CME exchange.
*
*/
struct glbx_order_definition
{
};
/**
* @brief Stores information (which is common among different exchanges) required to place an order.
*
*/
struct order_definition
{
emumba::market_data::security_id sec_id;
emumba::market_data::side order_side;
std::string user;
user_id usr_id;
std::string account;
account_id acc_id;
time_in_force time_in_frc;
std::variant<deribit_order_definition, glbx_order_definition> exchange_order;
};
/**
* @brief Stores structures and fields required to place a new order.
*
*/
struct new_order
{
order_definition order_def;
std::optional<emumba::market_data::price> px;
emumba::market_data::quantity qty;
};
/**
* @brief Stores important information required to edit an order placed by the user.
*
*/
struct order_update
{
order_key ord_key;
std::optional<emumba::market_data::price> px;
emumba::market_data::quantity qty;
};
struct position
{
uint subaccount_index;
emumba::market_data::security_id sec_id;
emumba::market_data::quantity qty;
emumba::market_data::price average_price;
emumba::market_data::side position_side;
};
struct open_order
{
uint subaccount_index;
emumba::market_data::security_id sec_id;
std::string exchange_order_id;
std::uint64_t internal_order_id;
status order_status;
time_in_force time_in_frc;
emumba::market_data::side order_side;
emumba::market_data::quantity qty;
emumba::market_data::quantity filled_qty;
emumba::market_data::price px;
emumba::market_data::price average_price;
emumba::market_data::exchange_timestamp last_update_timestamp;
emumba::market_data::exchange_timestamp creation_timestamp;
bool api;
bool web;
};
struct new_limit_order
{
emumba::market_data::security_id sec_id;
emumba::market_data::quantity qty;
emumba::market_data::price px;
time_in_force time_in_frc;
};
struct new_market_order
{
emumba::market_data::security_id sec_id;
emumba::market_data::quantity qty;
time_in_force time_in_frc;
};
enum class sort
{
ascending,
decending,
default_
};
struct user_trades_request
{
std::string currency;
emumba::market_data::exchange_timestamp start_timestamp;
emumba::market_data::exchange_timestamp end_timestamp;
std::optional<uint> count;
std::optional<bool> include_old;
std::optional<sort> sorting;
};
struct user_trades_response
{
uint subaccount_index;
emumba::deribit_connector::data_types::error error;
bool has_more;
std::vector<emumba::market_data::trade_record const *> trades;
};
struct rfq_request
{
emumba::market_data::security_id sec_id;
emumba::market_data::quantity qty;
emumba::market_data::side rfq_side;
rfq_request();
};
struct rfq_response
{
emumba::deribit_connector::data_types::error error;
bool status;
uint subaccount_index;
};
struct combo_trade
{
emumba::market_data::security_id sec_id;
emumba::market_data::side side;
uint64_t qty;
combo_trade();
};
struct leg_detail
{
emumba::market_data::security_id instrument_name;
int amount;
};
struct combo_detail
{
emumba::deribit_connector::data_types::error error;
std::vector<emumba::order_entry::leg_detail> legs;
emumba::market_data::security_id sec_id;
uint subaccount_index;
};
} // namespace order_entry
} // namespace emumba
#endif /*EMUMBA_ORDER_ENTRY_COMMON_H*/
@@ -0,0 +1,826 @@
#include "orders_handler.h"
#include "spdlogger.h"
#include "utils.h"
emumba::deribit_connector::orders_handler::orders_handler(const configuration::order_tracker &orders_tracker,
deribit_listener * trad_listener)
: _trading_listener(trad_listener)
, _empty_string("")
{
_order_states.reserve(128);
if (orders_tracker.uuid == "")
{
_uuid = uuid::generate_uuid_v4();
}
else
{
_uuid = orders_tracker.uuid;
}
_internal_order_id = orders_tracker.start_id;
_uuid_length = _uuid.length();
strcpy(_label, _uuid.c_str());
_label[_uuid_length] = '-';
create_next_label_value();
}
void emumba::deribit_connector::orders_handler::process_new_order(const emumba::deribit_connector::data_types::error &ec,
const rapidjson::Value &result, order_queues &order_queues,
const uint &subaccount_index)
{
_order_state.error = ec;
_order_state.subaccount_index = subaccount_index;
if (ec.value() == 0)
{
_label_value = result["order"]["label"].GetString();
LOG_DEBUG("Processing New Order Response. Label: {}", _label_value);
parse_label_value(_label_value, _label_uuid, _label_order_id);
_order_details[_label_order_id].order_id = result["order"]["order_id"].GetString();
_order_key = emumba::order_entry::order_key(_label_order_id);
if (_order_details[_label_order_id].is_order_confirm == false)
{
LOG_DEBUG("New Order Confirmation | Order Response | Internal Order ID: {}", _label_order_id);
_order_state.order_update_type = emumba::order_entry::update_type::new_order_confirm;
_order_details[_label_order_id].is_order_confirm = true;
}
else
{
_order_state.order_update_type = emumba::order_entry::update_type::subscription_update;
}
process_response_msg(ec, result);
}
else
{
LOG_DEBUG("New order message rejected");
_order_key = order_queues.new_order_queue.front();
_label_order_id = _order_key.value();
_order_state.order_update_type = emumba::order_entry::update_type::new_order_reject;
_order_state.order_status = emumba::order_entry::status::rejected;
process_request_rejection();
LOG_DEBUG("Order rejected | Deleting order detail structure | Internal Order ID: {}", _label_order_id);
_order_details.erase(_label_order_id);
}
order_queues.new_order_queue.pop();
LOG_DEBUG("New Order Queue | Pop | Queue size: {}", order_queues.new_order_queue.size());
}
void emumba::deribit_connector::orders_handler::process_edit_order(const emumba::deribit_connector::data_types::error &ec,
const rapidjson::Value &result,
order_queues &order_queues, const uint &subaccount_index)
{
_order_state.error = ec;
_order_state.subaccount_index = subaccount_index;
if (ec.value() == 0)
{
_label_value = result["order"]["label"].GetString();
LOG_DEBUG("Processing Edit Order Response. Label: {}", _label_value);
parse_label_value(_label_value, _label_uuid, _label_order_id);
_order_key = emumba::order_entry::order_key(_label_order_id);
_order_state.order_update_type = emumba::order_entry::update_type::edit_order_confirm;
process_response_msg(ec, result);
}
else
{
LOG_DEBUG("Edit order message rejected");
_order_key = order_queues.edit_queue.front();
_label_order_id = _order_key.value();
_order_state.order_update_type = emumba::order_entry::update_type::edit_order_reject;
_order_state.order_status = emumba::order_entry::status::working;
process_request_rejection();
}
order_queues.edit_queue.pop();
LOG_DEBUG("Edit Order Queue | Pop | Queue size: {}", order_queues.edit_queue.size());
}
void emumba::deribit_connector::orders_handler::process_cancel_order(const emumba::deribit_connector::data_types::error &ec,
const rapidjson::Value &result,
order_queues & order_queues,
const uint & subaccount_index)
{
_order_state.error = ec;
_order_state.subaccount_index = subaccount_index;
if (ec.value() == 0)
{
_label_value = result["label"].GetString();
LOG_DEBUG("Processing Cancel Order Response. Label: {}", _label_value);
parse_label_value(_label_value, _label_uuid, _label_order_id);
_order_key = emumba::order_entry::order_key(_label_order_id);
_order_state.order_update_type = emumba::order_entry::update_type::cancel_order_confirm;
process_response_msg(ec, result);
}
else
{
LOG_DEBUG("Cancel Order Message Rejected");
_order_key = order_queues.cancel_queue.front();
_label_order_id = _order_key.value();
_order_state.order_update_type = emumba::order_entry::update_type::cancel_order_reject;
_order_state.order_status = emumba::order_entry::status::working;
process_request_rejection();
}
order_queues.cancel_queue.pop();
LOG_DEBUG("Cancel Order Queue | Pop | Queue size: {}", order_queues.cancel_queue.size());
}
void emumba::deribit_connector::orders_handler::process_response_msg(
UNUSED_PARAM const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result)
{
// Note: order_state.ec, order_state.order_update_type, _order_key and _label_order_id should be set before calling this
// function
if (result.HasMember("trades")) // No trades member in cancel response
{
const rapidjson::Value &order_trades = result["trades"];
// Check for trades
if (order_trades.GetArray().Size() != 0)
{
process_trade(order_trades);
}
else
{
// Timestamp is only updated using trade messages (order response and trade sub) so in case response is received
// first and there are no trades, the timestamp should be updated using order msg.
uint64_t response_msg_timestamp_value = result["order"]["last_update_timestamp"].GetUint64();
if (_order_details[_label_order_id].timestamp < response_msg_timestamp_value)
{
_order_details[_label_order_id].timestamp = response_msg_timestamp_value;
}
}
}
if (_order_state.order_update_type != emumba::order_entry::update_type::cancel_order_confirm)
{
_order_details[_label_order_id].price = result["order"]["price"].GetDouble();
_order_details[_label_order_id].total_amount = result["order"]["amount"].GetDouble();
}
else
{
_order_details[_label_order_id].price = result["price"].GetDouble();
_order_details[_label_order_id].total_amount = result["amount"].GetDouble();
}
_order_details[_label_order_id].is_order_response_received = true;
process_order_state(_order_state);
if (_order_details[_label_order_id].is_order_complete == false)
{
if (_order_state.order_update_type == emumba::order_entry::update_type::cancel_order_confirm)
{
_order_details[_label_order_id].is_order_complete = true;
_order_details[_label_order_id].total_filled_amount = result["filled_amount"].GetDouble();
if (_order_details[_label_order_id].total_filled_amount == 0)
{
_order_details[_label_order_id].is_trade_sub_msg_received = true;
}
LOG_DEBUG("Cancel Order | Order completed | Internal Order ID: {}, Filled amount: {}, Total filled amount: {}",
_label_order_id, _order_details[_label_order_id].filled_amount,
_order_details[_label_order_id].total_filled_amount);
remove_order_if_complete(_label_order_id);
}
else
{
std::string order_state = result["order"]["order_state"].GetString();
if (order_state == "filled" || order_state == "cancelled" || order_state == "rejected")
{
if (result["trades"].GetArray().Size() == 0)
{
if ((_order_state.order_update_type != emumba::order_entry::update_type::edit_order_confirm) ||
((_order_state.order_update_type == emumba::order_entry::update_type::edit_order_confirm) &&
(result["order"]["filled_amount"].GetDouble() == 0)))
{
// New Order Response. Order is complete and it has no fills so we won't be receiving any message on the
// trade subscription Edit Order Response. Order is complete and if it has no fills before and after
// editing the order, we won't be receiving any message on the trade subscription
_order_details[_label_order_id].is_trade_sub_msg_received = true;
}
}
_order_details[_label_order_id].is_order_complete = true;
_order_details[_label_order_id].total_filled_amount = result["order"]["filled_amount"].GetDouble();
LOG_DEBUG("{} | Order completed | Internal Order ID: {}, Filled amount: {}, Total filled amount: {}",
std::string(_order_state.order_update_type == emumba::order_entry::update_type::edit_order_confirm ? "Edit Order"
: "New Order"),
_label_order_id, _order_details[_label_order_id].filled_amount,
_order_details[_label_order_id].total_filled_amount);
remove_order_if_complete(_label_order_id);
}
}
}
else
{
// In case order is complete but response msg processing and/or trade callbacks are pending. Once order response msg
// is processed and all trade callbacks are called, check whether or not order completion message needs to be sent.
if ((_order_details[_label_order_id].is_order_sub_msg_received == true) &&
((_order_details[_label_order_id].total_filled_amount - _order_details[_label_order_id].filled_amount) <=
DOUBLE_LIMIT))
{
_order_state.order_status = _order_details[_label_order_id].state;
_order_state.order_update_type = emumba::order_entry::update_type::subscription_update;
#ifdef ENABLE_API
_trading_listener->on_order_update(_order_key, _order_state);
#endif
}
remove_order_if_complete(_label_order_id);
}
}
void emumba::deribit_connector::orders_handler::process_order_state(emumba::order_entry::order_state &order_state,
const bool & is_final_callback)
{
// Note: order_state.ec, order_state.order_update_type, _order_key and _label_order_id should be set before calling this
// function
if (is_final_callback == false)
{
order_state.order_status = emumba::order_entry::status::working;
}
else
{
order_state.order_status = _order_details[_label_order_id].state;
}
order_state.qty = emumba::market_data::quantity(_order_details[_label_order_id].total_amount);
order_state.total_fills = emumba::market_data::quantity(_order_details[_label_order_id].filled_amount);
order_state.px = emumba::market_data::price(_order_details[_label_order_id].price);
order_state.avg_price = emumba::market_data::price(_order_details[_label_order_id].avg_price);
_deribit_order_state.instrument_name = _order_details[_label_order_id].instrument_name;
_deribit_order_state.exchange_order_id = _order_details[_label_order_id].order_id;
_deribit_order_state.direction = _order_details[_label_order_id].direction;
_deribit_order_state.timestamp.set_timestamp(_order_details[_label_order_id].timestamp);
order_state.exchange_order_state = _deribit_order_state;
#ifdef ENABLE_API
_trading_listener->on_order_update(_order_key, order_state);
#endif
order_state.trades.clear();
_order_details[_label_order_id].trade_records_list.clear();
}
void emumba::deribit_connector::orders_handler::process_request_rejection()
{
_order_details[_label_order_id].is_order_response_received = true;
_order_state.qty = emumba::market_data::quantity(0.0);
_order_state.total_fills = emumba::market_data::quantity(0.0);
_order_state.px = emumba::market_data::price(0.0);
_order_state.avg_price = emumba::market_data::price(0.0);
_deribit_order_state.instrument_name = _order_details[_label_order_id].instrument_name;
_deribit_order_state.direction = _order_details[_label_order_id].direction;
_deribit_order_state.exchange_order_id = "";
_deribit_order_state.timestamp.set_timestamp(0);
_order_state.exchange_order_state = _deribit_order_state;
#ifdef ENABLE_API
_trading_listener->on_order_update(_order_key, _order_state);
#endif
if (_order_state.order_update_type == emumba::order_entry::update_type::edit_order_reject ||
_order_state.order_update_type == emumba::order_entry::update_type::cancel_order_reject)
{
if ((_order_details[_label_order_id].is_order_sub_msg_received == true) &&
((_order_details[_label_order_id].total_filled_amount - _order_details[_label_order_id].filled_amount) <=
DOUBLE_LIMIT))
{
_order_state.order_status = _order_details[_label_order_id].state;
_order_state.order_update_type = emumba::order_entry::update_type::subscription_update;
#ifdef ENABLE_API
_trading_listener->on_order_update(_order_key, _order_state);
#endif
remove_order_if_complete(_label_order_id);
}
}
}
void emumba::deribit_connector::orders_handler::process_trade(const rapidjson::Value &order_trades)
{
for (rapidjson::Value::ConstValueIterator trade_itr = order_trades.Begin(); trade_itr != order_trades.End(); ++trade_itr)
{
auto trade = trade_itr->GetObject();
_trade_id = trade["trade_id"].GetString();
auto &trade_ids = _order_details[_label_order_id].trade_ids;
if (std::find(trade_ids.begin(), trade_ids.end(), _trade_id) == trade_ids.end())
{
fill_trade_record(_trade_record, trade);
LOG_DEBUG("Calling Trade callback | Trade ID: {} | Internal Order ID: {}", _trade_id, _label_order_id);
_order_details[_label_order_id].trade_records_list.push_back(_trade_record);
_order_state.trades.push_back(&_order_details[_label_order_id].trade_records_list.back());
trade_ids.push_back(_trade_id);
_order_details[_label_order_id].filled_amount =
_order_details[_label_order_id].filled_amount + _trade_record.trade_quantity;
// Stop updating timestamp & avg price using trades when final order sub msg is received
if (_order_details[_label_order_id].is_order_sub_msg_received != true)
{
_order_details[_label_order_id].timestamp = _trade_record.timestamp.get_timestamp();
_trade_detail.value = _trade_record.trade_quantity / _trade_record.trade_price.to_double();
_trade_detail.price = _trade_record.trade_price.to_double();
_order_details[_label_order_id].sum_of_trade_values =
_order_details[_label_order_id].sum_of_trade_values + _trade_detail.value;
_order_details[_label_order_id].trades_detail.push_back(_trade_detail);
_avg_price = 0;
for (auto &each_trade_detail : _order_details[_label_order_id].trades_detail)
{
_avg_price = _avg_price + ((each_trade_detail.value / _order_details[_label_order_id].sum_of_trade_values) *
each_trade_detail.price);
}
_order_details[_label_order_id].avg_price = round_two(_avg_price);
}
LOG_TRACE("Trade ID: {}, Total Amount: {}, Avg Price (Calculated): {}, Avg Price (Round off): {}", _trade_id,
_order_details[_label_order_id].filled_amount, _avg_price, _order_details[_label_order_id].avg_price);
}
else
{
LOG_DEBUG("Trade callback already called | Trade ID: {} | Internal Order ID: {}", _trade_id, _label_order_id);
}
}
}
void emumba::deribit_connector::orders_handler::process_private_user_trades_subscription(
const rapidjson::Value &user_trades_message, const uint &subaccount_index)
{
for (rapidjson::Value::ConstValueIterator user_trades_msg_itr = user_trades_message["data"].Begin();
user_trades_msg_itr != user_trades_message["data"].End(); ++user_trades_msg_itr)
{
auto user_trade_message = user_trades_msg_itr->GetObject();
LOG_DEBUG("Subscription | Private | User Trades | Instrument: {}", std::string(user_trade_message["instrument_name"].GetString()));
if (user_trade_message.HasMember("label"))
{
_label_value = user_trade_message["label"].GetString();
parse_label_value(_label_value, _label_uuid, _label_order_id);
if ((_label_uuid == _uuid) && user_trade_message.HasMember("combo_trade_id") == false &&
(is_internal_order_id_active(_label_order_id) == true))
{
_trade_id = user_trade_message["trade_id"].GetString();
auto &trade_ids = _order_details[_label_order_id].trade_ids;
if (std::find(trade_ids.begin(), trade_ids.end(), _trade_id) == trade_ids.end())
{
fill_trade_record(_trade_record, user_trade_message);
LOG_DEBUG("Trade Message | Trade ID: {} | Internal Order ID: {}", _trade_id, _label_order_id);
_order_details[_label_order_id].trade_records_list.push_back(_trade_record);
_order_states[_label_order_id].trades.push_back(&_order_details[_label_order_id].trade_records_list.back());
trade_ids.push_back(_trade_id);
_order_details[_label_order_id].filled_amount =
_order_details[_label_order_id].filled_amount + _trade_record.trade_quantity;
if (_order_details[_label_order_id].is_order_sub_msg_received != true)
{
_order_details[_label_order_id].timestamp = _trade_record.timestamp.get_timestamp();
_trade_detail.value = _trade_record.trade_quantity / _trade_record.trade_price.to_double();
_trade_detail.price = _trade_record.trade_price.to_double();
_order_details[_label_order_id].sum_of_trade_values =
_order_details[_label_order_id].sum_of_trade_values + _trade_detail.value;
_order_details[_label_order_id].trades_detail.push_back(_trade_detail);
_avg_price = 0;
for (auto &each_trade_detail : _order_details[_label_order_id].trades_detail)
{
_avg_price =
_avg_price + ((each_trade_detail.value / _order_details[_label_order_id].sum_of_trade_values) *
each_trade_detail.price);
}
_order_details[_label_order_id].avg_price = round_two(_avg_price);
}
LOG_TRACE("Trade ID: {}, Total Amount: {}, Avg Price (Calculated): {}, Avg Price (Round off): {}", _trade_id,
_order_details[_label_order_id].filled_amount, _avg_price,
_order_details[_label_order_id].avg_price);
if (_order_details[_label_order_id].is_order_confirm == false)
{
_order_details[_label_order_id].last_trade.order_id = user_trade_message["order_id"].GetString();
_order_details[_label_order_id].last_trade.price = user_trade_message["price"].GetDouble();
}
if (_order_details[_label_order_id].is_order_complete != true)
{
_order_details[_label_order_id].last_trade.state = user_trade_message["state"].GetString();
}
}
else
{
LOG_DEBUG("Trade callback already called | Trade ID: {} | Internal Order ID: {}", _trade_id, _label_order_id);
_order_ids_without_trades.emplace(_label_order_id);
}
}
else
{
LOG_DEBUG(
"Trade does not belong to trader session or internal order id is no longer being tracked | UUID: {}, "
"Internal order id: {}",
_uuid, _label_order_id);
}
}
else
{
LOG_DEBUG("Label value is empty");
}
}
for (auto &order_state : _order_states)
{
_label_order_id = order_state.first;
_order_details[_label_order_id].is_trade_sub_msg_received = true;
if (_order_details[_label_order_id].is_order_confirm == false)
{
LOG_DEBUG("New Order Confirmation | Pvt Trade Msg | Internal Order ID: {}", _label_order_id);
order_state.second.order_update_type = emumba::order_entry::update_type::new_order_confirm;
_order_details[_label_order_id].order_id = _order_details[_label_order_id].last_trade.order_id;
_order_details[_label_order_id].price = _order_details[_label_order_id].last_trade.price;
_order_details[_label_order_id].is_order_confirm = true;
}
else
{
order_state.second.order_update_type = emumba::order_entry::update_type::subscription_update;
}
order_state.second.error = emumba::deribit_connector::data_types::error(0, "Success");
order_state.second.subaccount_index = subaccount_index;
_order_key = emumba::order_entry::order_key(_label_order_id);
process_order_state(order_state.second);
_order_ids_without_trades.erase(_label_order_id); // Remove since this label order id contain some trades.
if (_order_details[_label_order_id].is_order_complete != true)
{
std::string last_trade_order_state = _order_details[_label_order_id].last_trade.state;
if (last_trade_order_state == "filled" || last_trade_order_state == "cancelled" ||
last_trade_order_state == "rejected")
{
_order_details[_label_order_id].is_order_complete = true;
}
}
// Order might be already complete or it might complete after final trade message. This if should be separate from the
// above if condition.
if (_order_details[_label_order_id].is_order_complete)
{
LOG_DEBUG(
"Trade sub msg | Order completed | Internal Order ID: {}, Filled amount: {}, Total filled amount: "
"{}",
_label_order_id, _order_details[_label_order_id].filled_amount,
_order_details[_label_order_id].total_filled_amount);
if (((_order_details[_label_order_id].total_filled_amount - _order_details[_label_order_id].filled_amount) <=
DOUBLE_LIMIT) &&
(_order_details[_label_order_id].is_order_response_received == true) &&
(_order_details[_label_order_id].is_order_sub_msg_received == true))
{
order_state.second.order_status = _order_details[_label_order_id].state;
order_state.second.order_update_type = emumba::order_entry::update_type::subscription_update;
#ifdef ENABLE_API
_trading_listener->on_order_update(_order_key, order_state.second);
#endif
remove_order_if_complete(_label_order_id);
}
}
}
if (_order_ids_without_trades.empty() == false)
{
for (const auto &order_id : _order_ids_without_trades)
{
_order_details[order_id].is_trade_sub_msg_received = true;
remove_order_if_complete(order_id);
}
}
_order_ids_without_trades.clear();
_order_states.clear();
}
void emumba::deribit_connector::orders_handler::process_private_user_orders_subscription(
const rapidjson::Value &user_orders_message, const uint &subaccount_index)
{
LOG_DEBUG("Subscription | Private | User Orders | Instrument: {}",
std::string(user_orders_message["data"]["instrument_name"].GetString()));
if (user_orders_message["data"].HasMember("label"))
{
_label_value = user_orders_message["data"]["label"].GetString();
LOG_DEBUG("Processing user order message. Label: {}", _label_value);
parse_label_value(_label_value, _label_uuid, _label_order_id);
if ((_label_uuid == _uuid) && user_orders_message["data"].HasMember("combo_order_id") == false &&
(is_internal_order_id_active(_label_order_id) == true))
{
std::string order_state = user_orders_message["data"]["order_state"].GetString();
emumba::order_entry::status order_status = emumba::order_entry::status::working;
if (strcmp(order_state.c_str(), "open") == 0)
{
order_status = emumba::order_entry::status::working;
}
else if (strcmp(order_state.c_str(), "filled") == 0)
{
order_status = emumba::order_entry::status::completed;
}
else if (strcmp(order_state.c_str(), "cancelled") == 0)
{
order_status = emumba::order_entry::status::canceled;
}
else if (strcmp(order_state.c_str(), "rejected") == 0)
{
order_status = emumba::order_entry::status::rejected;
}
else if (strcmp(order_state.c_str(), "untriggered") == 0)
{
order_status = emumba::order_entry::status::untriggered;
}
else
{
LOG_WARNING("Unknown order state received: {}", order_state);
}
if (order_status == emumba::order_entry::status::completed ||
order_status == emumba::order_entry::status::canceled || order_status == emumba::order_entry::status::rejected)
{
LOG_DEBUG("Subscription | Private | User Orders | Order Completed");
_order_details[_label_order_id].is_order_complete = true;
_order_details[_label_order_id].is_order_sub_msg_received = true;
_order_details[_label_order_id].state = order_status;
_order_details[_label_order_id].total_filled_amount = user_orders_message["data"]["filled_amount"].GetDouble();
_order_details[_label_order_id].avg_price = user_orders_message["data"]["average_price"].GetDouble();
_order_details[_label_order_id].timestamp = user_orders_message["data"]["last_update_timestamp"].GetUint64();
_order_details[_label_order_id].price = user_orders_message["data"]["price"].GetDouble();
if ((_order_details[_label_order_id].is_order_response_received == true) &&
((_order_details[_label_order_id].total_filled_amount - _order_details[_label_order_id].filled_amount) <=
DOUBLE_LIMIT))
{
_order_state.order_update_type = emumba::order_entry::update_type::subscription_update;
_order_state.error = emumba::deribit_connector::data_types::error(0, "Success");
_order_state.subaccount_index = subaccount_index;
_order_key = emumba::order_entry::order_key(_label_order_id);
process_order_state(_order_state, true);
LOG_DEBUG(
"Order sub msg | Order completed | Internal Order ID: {}, Filled amount: {}, Total filled amount: {}",
_label_order_id, _order_details[_label_order_id].filled_amount,
_order_details[_label_order_id].total_filled_amount);
remove_order_if_complete(_label_order_id);
}
else
{
LOG_DEBUG("Order completed but waiting for all trades to be processed");
}
}
else
{
LOG_DEBUG("Order sub msg | Order not complete. Ignoring message");
}
}
else
{
LOG_DEBUG(
"User order does not belong to trader session or internal order id is no longer being tracked | UUID: {}, "
"Internal order id: {}",
_uuid, _label_order_id);
}
}
else
{
LOG_TRACE("label not found in User Orders Msg | Skipping message");
}
}
void emumba::deribit_connector::orders_handler::fill_trade_record(emumba::market_data::trade_record & trade_record,
const rapidjson::Value::ConstObject &user_trade_message)
{
trade_record.sec_id.value() = user_trade_message["instrument_name"].GetString();
trade_record.timestamp.set_timestamp(user_trade_message["timestamp"].GetUint64());
if ((strcmp(user_trade_message["direction"].GetString(), "buy") == 0))
{
trade_record.agg_side = emumba::market_data::aggressor_side::buy;
}
else if ((strcmp(user_trade_message["direction"].GetString(), "sell") == 0))
{
trade_record.agg_side = emumba::market_data::aggressor_side::sell;
}
else
{
trade_record.agg_side = emumba::market_data::aggressor_side::none;
}
trade_record.trade_quantity = user_trade_message["amount"].GetDouble();
trade_record.trade_price = emumba::market_data::price(user_trade_message["price"].GetDouble());
trade_record.exchange_order_id = user_trade_message["order_id"].GetString();
}
const bool emumba::deribit_connector::orders_handler::is_internal_order_id_active(const std::uint64_t &internal_order_id)
{
return _order_details.find(internal_order_id) != _order_details.end();
}
const std::string &emumba::deribit_connector::orders_handler::get_exchange_order_id(const std::uint64_t &internal_order_id)
{
if (is_internal_order_id_active(internal_order_id) == true)
{
return _order_details[internal_order_id].order_id;
}
LOG_WARNING("Order ID for Order key {} not found in record.", internal_order_id);
return _empty_string;
}
const std::uint64_t &emumba::deribit_connector::orders_handler::get_internal_order_id() const
{
return _internal_order_id;
}
void emumba::deribit_connector::orders_handler::parse_label_value(std::string label_value, std::string &label_uuid,
std::uint64_t &label_order_id)
{
LOG_TRACE("Parsing Label: {}", label_value);
std::string delimiter = "-";
size_t pos = 0;
pos = label_value.find(delimiter);
label_uuid = label_value.substr(0, pos);
LOG_TRACE("LABEL UUID: {}, Position start: {}, Position End: {}", label_uuid, 0, pos);
char *end;
auto string_order_id = label_value.substr(pos + delimiter.length(), label_value.length());
label_order_id = strtoull(string_order_id.c_str(), &end, 10);
LOG_TRACE("STRING ORDER UUID: {}, Position start: {}, Position End: {}", string_order_id, pos + delimiter.length(),
label_value.length());
}
void emumba::deribit_connector::orders_handler::create_next_label_value()
{
uint8_t label_length = _uuid_length + 1;
// count numbers of digits
for (uint64_t internal_order_id = ++_internal_order_id; internal_order_id > 0; internal_order_id /= 10, ++label_length)
;
_label[label_length] = '\0';
for (uint64_t internal_order_id = _internal_order_id; internal_order_id > 0; internal_order_id /= 10)
{
_label[--label_length] = internal_order_id % 10 + '0';
}
}
const char *emumba::deribit_connector::orders_handler::get_label()
{
return _label;
}
const std::string &emumba::deribit_connector::orders_handler::get_uuid() const
{
return _uuid;
}
double emumba::deribit_connector::orders_handler::round_two(const double &value)
{
// _rounded_value = std::ceil(value * 100.0) / 100.0;
// return _rounded_value;
_rounded_value = (int)(value * 100.0 + 0.5) / 100.0;
return _rounded_value;
}
void emumba::deribit_connector::orders_handler::reset_flag_order_response_received(const uint64_t &label_order_id)
{
LOG_DEBUG("Order response received flag reset | Internal Order ID: {}", label_order_id);
_order_details[label_order_id].is_order_response_received = false;
}
void emumba::deribit_connector::orders_handler::create_order_detail(const uint &session_id, const uint64_t &label_order_id,
const std::string &instrument_name,
const std::string &direction, const double &quantity)
{
LOG_DEBUG("Order detail object created | Internal order id: {}", label_order_id);
_order_details[label_order_id] = orders_handler::order_detail();
_order_details[label_order_id].instrument_name = instrument_name;
_order_details[label_order_id].direction = direction;
_order_details[label_order_id].total_amount = quantity;
_order_details[label_order_id].session_id = session_id;
_order_details[label_order_id].trades_detail.reserve(128);
}
void emumba::deribit_connector::orders_handler::on_trader_sess_disconnect(const uint &session_id,
const uint &subaccount_index)
{
LOG_DEBUG("Orders handler | Trader session {} disconnected.", session_id);
for (auto &each_order_detail : _order_details)
{
emumba::deribit_connector::orders_handler::order_detail &order_detail = each_order_detail.second;
if (order_detail.session_id == session_id)
{
_label_order_id = each_order_detail.first;
order_detail.state = emumba::order_entry::status::canceled;
_order_state.error = emumba::deribit_connector::data_types::error(0, "Success", "");
_order_state.subaccount_index = subaccount_index;
_order_key = emumba::order_entry::order_key(_label_order_id);
process_order_state(_order_state, true);
_order_ids_to_remove.emplace(_label_order_id);
}
}
// Remove all those order ids which have been cancelled by us due to disconnection
for (auto &each_order_id : _order_ids_to_remove)
{
LOG_DEBUG("Orders handler | Removing internal order id {} which belongs to session {}.", _label_order_id, session_id);
_order_details.erase(each_order_id);
}
LOG_DEBUG("Orders handler | Order Details size: {}", _order_details.size());
_order_ids_to_remove.clear();
}
void emumba::deribit_connector::orders_handler::remove_order_if_complete(const uint64_t &label_order_id)
{
LOG_TRACE("Remove order detail called | Internal Order ID: {}", label_order_id);
if (_order_details[label_order_id].is_order_complete) // Can be removed if called only when order is complete
{
LOG_TRACE("Remove order detail | Internal Order ID: {}, is_order_complete: {}", label_order_id,
_order_details[label_order_id].is_order_complete);
if (_order_details[label_order_id].is_order_response_received &&
_order_details[label_order_id].is_order_sub_msg_received &&
_order_details[label_order_id].is_trade_sub_msg_received)
{
LOG_TRACE("Remove order detail | Msg received from all sources | Internal Order ID: {}", label_order_id);
if ((_order_details[_label_order_id].total_filled_amount - _order_details[_label_order_id].filled_amount) <=
DOUBLE_LIMIT)
{
LOG_DEBUG("Order completed | Deleting order detail structure | Order ID: {}, Internal Order ID: {}",
_order_details[label_order_id].order_id, label_order_id);
_order_details.erase(_label_order_id);
}
}
}
}
@@ -0,0 +1,132 @@
#ifndef ORDERS_HANDLER_H
#define ORDERS_HANDLER_H
#include "deribit_handler.h"
#include "deribit_listener.h"
#include "deribit_utils.h"
#include "rapidjson/document.h"
#include "utils.h"
#include <list>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace emumba {
namespace deribit_connector {
class orders_handler
{
public:
orders_handler(const configuration::order_tracker &orders_tracker, deribit_listener *trad_listener);
void process_new_order(const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result,
order_queues &order_queues, const uint &subaccount_index);
void process_edit_order(const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result,
order_queues &order_queues, const uint &subaccount_index);
void process_cancel_order(const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result,
order_queues &order_queues, const uint &subaccount_index);
void process_private_user_trades_subscription(const rapidjson::Value &user_trades_message,
const uint & subaccount_index);
void process_private_user_orders_subscription(const rapidjson::Value &user_orders_message,
const uint & subaccount_index);
const std::string & get_exchange_order_id(const std::uint64_t &internal_order_id);
const std::uint64_t &get_internal_order_id() const;
const std::string & get_uuid() const;
void parse_label_value(std::string label_value, std::string &label_uuid, std::uint64_t &label_order_id);
void create_next_label_value();
const char *get_label();
void reset_flag_order_response_received(const uint64_t &label_order_id);
void create_order_detail(const uint &session_id, const uint64_t &label_order_id, const std::string &instrument_name,
const std::string &direction, const double &quantity);
void on_trader_sess_disconnect(const uint &session_id, const uint &subaccount_index);
static void fill_trade_record(emumba::market_data::trade_record & trade_record,
const rapidjson::Value::ConstObject &user_trade_message);
private:
void process_response_msg(const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result);
void process_order_state(emumba::order_entry::order_state &order_state, const bool &is_final_callback = false);
void process_request_rejection();
void process_trade(const rapidjson::Value &order_trades);
double round_two(const double &value);
void remove_order_if_complete(const uint64_t &label_order_id);
const bool is_internal_order_id_active(const std::uint64_t &internal_order_id); // Return true if we're tracking
struct trade_detail
{
double price;
double value;
};
struct last_trade_info
{
std::string state;
std::string order_id;
double price;
};
struct order_detail
{
uint session_id; // Used to track session from which this order was placed.
std::string order_id;
std::string instrument_name;
std::string direction;
last_trade_info last_trade;
emumba::order_entry::status state = emumba::order_entry::status::working;
// filled amount, timestamp and avg_price is calculated using trade updates. The avg price value is assigned using
// order subscription msg when order is completed
uint64_t timestamp = 0;
double filled_amount = 0;
double total_filled_amount = 0;
double total_amount = 0;
double avg_price = 0;
double price = 0;
double sum_of_trade_values = 0;
bool is_order_confirm = false;
bool is_order_complete = false;
bool is_order_response_received = false;
bool is_order_sub_msg_received = false;
bool is_trade_sub_msg_received = false;
std::vector<std::string> trade_ids; // List of trade IDs for which callback has been called
std::vector<trade_detail> trades_detail; // List containing trade price & value of each trade
std::list<emumba::market_data::trade_record>
trade_records_list; // Store data of those trades which are passed to the user in the callbacks
};
deribit_listener * _trading_listener;
emumba::order_entry::order_state _order_state;
emumba::order_entry::deribit_order_state _deribit_order_state;
emumba::order_entry::order_key _order_key;
emumba::market_data::trade_record _trade_record;
std::string _uuid;
std::string _empty_string;
std::string _label_value;
std::string _label_uuid;
std::string _trade_id;
std::uint64_t _label_order_id;
std::uint64_t _internal_order_id;
char _label[60];
uint8_t _uuid_length;
double _rounded_value;
double _avg_price;
trade_detail _trade_detail;
// In user trade updates, the following variable is used to track those order ids whose trade callbacks are already
// called but order detail structure has not been deleted yet.
std::unordered_set<uint64_t /*internal order_id*/> _order_ids_without_trades;
// Store internal order ids which will be removed incase of disconnection
std::unordered_set<uint64_t /*internal order_id*/> _order_ids_to_remove;
std::unordered_map<uint64_t /*internal order_id*/, emumba::order_entry::order_state /*order state update*/>
_order_states;
std::unordered_map<uint64_t /*internal order_id*/, order_detail /*order_detail*/> _order_details;
};
} // namespace deribit_connector
} // namespace emumba
#endif
@@ -0,0 +1,278 @@
#include "deribit_utils.h"
#include <assert.h>
#include <cstdint>
#include <limits>
namespace emumba::market_data {
#ifdef PRICE_AS_DOUBLE
inline constexpr double price::to_double() const noexcept
{
return _price;
}
inline price price::from_double(double price) noexcept
{
return {price};
}
#else
#define IS_NAN(x) (x == std::numeric_limits<std::int64_t>::min())
#define EXPONENT -9
inline constexpr price::price(std::int64_t mantissa, UNUSED_PARAM std::int8_t exponent) noexcept
: _mantissa(mantissa)
{
assert(exponent == price::exponent());
}
inline price price::from_double(double price) noexcept
{
if (std::isnan(price))
{
return nan();
}
double temp; // NOLINT
if (fabs(modf(price, &temp)) != 0.0)
{
const double rounding = 0.5 * _pow10();
price += (price > 0) ? rounding : -rounding;
price *= __pow10();
return {(std::int64_t)price};
}
return {(std::int64_t)(price * __pow10())};
}
inline constexpr double price::to_double() const noexcept
{
return is_nan() ? std::numeric_limits<double>::quiet_NaN() : _mantissa * _pow10();
}
inline constexpr float price::to_float() const noexcept
{
return is_nan() ? std::numeric_limits<float>::quiet_NaN() : _mantissa * _pow10();
}
inline constexpr price price::zero() noexcept
{
return price(std::int64_t(0));
}
inline constexpr price price::nan() noexcept
{
return price(NAN_SENTINEL);
}
inline constexpr std::int8_t price::exponent() noexcept
{
return EXPONENT;
}
inline constexpr std::int64_t price::mantissa() const noexcept
{
return _mantissa;
}
inline constexpr price price::abs() const noexcept
{
if (_mantissa >= 0)
{
return *this;
}
return price{-_mantissa};
}
inline price price::square() const noexcept
{
auto d = to_double();
return from_double(d * d);
}
inline constexpr bool price::is_nan() const noexcept
{
return IS_NAN(_mantissa);
}
inline constexpr bool price::is_better(side s, price p2) const noexcept
{
if (is_nan())
{
return false;
}
if (p2.is_nan())
{
return true;
}
if (s == side::buy)
{
return *this > p2;
}
return *this < p2;
}
inline constexpr bool price::is_worse(side s, price p2) const noexcept
{
return p2.is_better(s, *this);
}
inline constexpr bool price::is_worse_or_equal(side s, price p2) const noexcept
{
return p2.is_better_or_equal(s, *this);
}
inline constexpr bool price::is_better_or_equal(side s, price p2) const noexcept
{
return is_better_or_equal(s, *this, p2);
}
inline constexpr bool price::is_better_or_equal(side s, price p1, price p2) noexcept
{
if (p1.is_nan())
{
// This case is always false (even if p2 is nan), because nan != nan.
return false;
}
if (p2.is_nan())
{
return true;
}
if (s == side::buy)
{
return p1 >= p2;
}
return p1 <= p2;
}
inline double price::_pow10() noexcept
{
return std::pow(10.0, exponent());
}
inline double price::__pow10() noexcept
{
return std::pow(10.0, -exponent());
}
inline constexpr price price::operator-() const noexcept
{
if (is_nan())
{
return *this;
}
return {-_mantissa};
}
// If a number is nan, all comparisons are false. This is well-established
// behavior; even nan == nan evaluates to false.
// NOLINTNEXTLINE
#define PRICE_PRICE_COMPARISON(op, nan_part) \
inline constexpr bool price::operator op(price factor) const noexcept \
{ \
return nan_part _mantissa op factor._mantissa; \
}
// NOLINTNEXTLINE
#define PRICE_PRICE_BIN_OP(op) \
inline constexpr price price::operator op(price factor) const noexcept \
{ \
if (is_nan() || factor.is_nan()) \
{ \
return nan(); \
} \
return _mantissa op factor._mantissa; \
}
// NOLINTNEXTLINE
#define PRICE_PRICE_BIN_REF_OP(op) \
inline constexpr price &price::operator op(price factor) noexcept \
{ \
if (factor.is_nan()) \
{ \
_mantissa = nan()._mantissa; \
} \
else if (!is_nan()) \
{ \
_mantissa op factor._mantissa; \
} \
return *this; \
}
// NOLINTNEXTLINE
#define PRICE_INT64_COMPARISON(op, nan_part) \
inline constexpr bool price::operator op(std::int64_t factor) const noexcept \
{ \
return nan_part _mantissa op factor; \
}
// NOLINTNEXTLINE
#define PRICE_INT64_BIN_OP(op) \
inline constexpr price price::operator op(std::int64_t factor) const noexcept \
{ \
if (is_nan()) \
{ \
return *this; \
} \
return _mantissa op factor; \
}
// NOLINTNEXTLINE
#define PRICE_INT64_BIN_REF_OP(op) \
inline constexpr price &price::operator op(std::int64_t factor) noexcept \
{ \
if (!is_nan()) \
{ \
_mantissa op factor; \
} \
return *this; \
}
PRICE_PRICE_COMPARISON(!=, is_nan() ||)
PRICE_PRICE_COMPARISON(==, !is_nan() &&)
PRICE_PRICE_COMPARISON(<, !is_nan() &&)
PRICE_PRICE_COMPARISON(<=, !is_nan() &&)
PRICE_PRICE_COMPARISON(>, !is_nan() &&)
PRICE_PRICE_COMPARISON(>=, !is_nan() &&)
PRICE_PRICE_BIN_OP(+)
PRICE_PRICE_BIN_OP(-)
PRICE_PRICE_BIN_OP(*)
PRICE_PRICE_BIN_OP(/)
PRICE_PRICE_BIN_REF_OP(+=)
PRICE_PRICE_BIN_REF_OP(-=)
PRICE_PRICE_BIN_REF_OP(*=)
PRICE_PRICE_BIN_REF_OP(/=)
PRICE_INT64_COMPARISON(!=, is_nan() ||)
PRICE_INT64_COMPARISON(==, !is_nan() &&)
PRICE_INT64_COMPARISON(<, !is_nan() &&)
PRICE_INT64_COMPARISON(<=, !is_nan() &&)
PRICE_INT64_COMPARISON(>, !is_nan() &&)
PRICE_INT64_COMPARISON(>=, !is_nan() &&)
PRICE_INT64_BIN_OP(+)
PRICE_INT64_BIN_OP(-)
PRICE_INT64_BIN_OP(*)
PRICE_INT64_BIN_OP(/)
PRICE_INT64_BIN_REF_OP(+=)
PRICE_INT64_BIN_REF_OP(-=)
PRICE_INT64_BIN_REF_OP(*=)
PRICE_INT64_BIN_REF_OP(/=)
inline std::ostream &operator<<(std::ostream &out, price p) noexcept
{
out << p.to_double();
return out;
}
// Undefine all macros because this file is included in the .h
#undef NAN_SENTINEL
#undef IS_NAN
#undef PRICE_PRICE_COMPARISON
#undef PRICE_PRICE_BIN_OP
#undef PRICE_PRICE_BIN_REF_OP
#undef PRICE_INT64_COMPARISON
#undef PRICE_INT64_BIN_OP
#undef PRICE_INT64_BIN_REF_OP
#endif
} // namespace emumba::market_data
@@ -0,0 +1,179 @@
/**
* @file price.h
* @brief Provides a class used to store and calculate the price value.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned by Emumba (Pvt) Ltd [Emumba] and is
* protected by copyright law and international copyright treaties. Access to and use of the software is governed by the
* terms of the applicable Emumba Software Services Agreement (the Agreement) and Customer end user license agreements
* granting a non-assignable, non-transferable and non-exclusive license to use the software for it's own data processing
* purposes under the terms defined in the Agreement. Except as otherwise granted within the terms of the Agreement, copying
* or reproduction of any part of this source code or associated reference material to any other location for further
* reproduction or redistribution, and any amendments to this copyright notice, are expressly prohibited. Any reproduction or
* redistribution for sale or hiring of the Software not in accordance with the terms of the Agreement is a violation of
* copyright law
*
*/
#ifndef EMUMBA_PRICE_H
#define EMUMBA_PRICE_H
#include <cmath>
#include <ostream>
#include <cstdint>
namespace emumba {
namespace market_data {
/**
* @brief Enum class used for side i.e. Buy(Bid) or Sell(Ask).
*
*/
enum class side
{
buy, /**< Value = 0. Buy/Bid. */
sell, /**< Value = 1. Sell/Ask. */
none /**< Value =2. None/Zero*/
};
#define NAN_SENTINEL std::numeric_limits<std::int64_t>::min()
/**
* @brief Handle the price.
*
*/
class price
{
public:
constexpr price()
: _mantissa(NAN_SENTINEL){};
#ifdef PRICE_AS_DOUBLE
price(double price)
: _price(price)
{}
constexpr double to_double() const noexcept;
static price from_double(double) noexcept;
#else
price(double price)
{
if (std::isnan(price))
{
_mantissa = NAN_SENTINEL;
}
double temp; // NOLINT
if (fabs(modf(price, &temp)) != 0.0)
{
const double rounding = 0.5 * _pow10();
price += (price > 0) ? rounding : -rounding;
price *= __pow10();
_mantissa = (std::int64_t)price;
}
else
{
_mantissa = (std::int64_t)(price * __pow10());
}
}
constexpr price(std::int64_t mantissa, std::int8_t exponent = price::exponent()) noexcept;
constexpr price(price const &) = default;
static price from_double(double) noexcept;
constexpr double to_double() const noexcept;
constexpr float to_float() const noexcept;
static constexpr price zero() noexcept;
static constexpr price nan() noexcept;
constexpr price abs() const noexcept;
price square() const noexcept;
price improve(side s, price by) const noexcept
{
if (s == side::buy)
{
return *this + by;
}
else
{
return *this - by;
}
}
price worsen(side s, price by) const noexcept
{
if (s == side::buy)
{
return *this - by;
}
else
{
return *this + by;
}
}
constexpr bool is_nan() const noexcept;
constexpr bool is_better(side s, price p2) const noexcept;
constexpr bool is_worse(side s, price p2) const noexcept;
constexpr bool is_better_or_equal(side s, price p2) const noexcept;
constexpr bool is_worse_or_equal(side s, price p2) const noexcept;
static constexpr bool is_better_or_equal(side s, price p1, price p2) noexcept;
price &operator=(price const &) = default;
constexpr price operator-() const noexcept;
constexpr bool operator==(price) const noexcept;
constexpr bool operator!=(price) const noexcept;
constexpr bool operator<(price) const noexcept;
constexpr bool operator<=(price) const noexcept;
constexpr bool operator>(price) const noexcept;
constexpr bool operator>=(price) const noexcept;
constexpr price operator+(price) const noexcept;
constexpr price operator-(price) const noexcept;
constexpr price operator*(price) const noexcept;
constexpr price operator/(price) const noexcept;
constexpr price &operator+=(price) noexcept;
constexpr price &operator-=(price) noexcept;
constexpr price &operator*=(price) noexcept;
constexpr price &operator/=(price) noexcept;
constexpr bool operator==(std::int64_t) const noexcept;
constexpr bool operator!=(std::int64_t) const noexcept;
constexpr bool operator<(std::int64_t) const noexcept;
constexpr bool operator<=(std::int64_t) const noexcept;
constexpr bool operator>(std::int64_t) const noexcept;
constexpr bool operator>=(std::int64_t) const noexcept;
constexpr price operator+(std::int64_t) const noexcept;
constexpr price operator-(std::int64_t) const noexcept;
constexpr price operator*(std::int64_t) const noexcept;
constexpr price operator/(std::int64_t) const noexcept;
constexpr price &operator+=(std::int64_t) noexcept;
constexpr price &operator-=(std::int64_t) noexcept;
constexpr price &operator*=(std::int64_t) noexcept;
constexpr price &operator/=(std::int64_t) noexcept;
constexpr std::int64_t mantissa() const noexcept;
static constexpr std::int8_t exponent() noexcept;
#endif
private:
#ifdef PRICE_AS_DOUBLE
double _price;
#else
std::int64_t _mantissa;
static double _pow10() noexcept;
static double __pow10() noexcept;
#endif
};
#ifndef PRICE_AS_DOUBLE
std::ostream &operator<<(std::ostream &, price) noexcept;
#endif
} // namespace market_data
} // namespace emumba
#include "price.cc"
#endif
@@ -0,0 +1,138 @@
/**
* @file price_level.h
* @brief Provides a structure used to handle price levels of an order book.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned by Emumba (Pvt) Ltd [Emumba] and is
* protected by copyright law and international copyright treaties. Access to and use of the software is governed by the
* terms of the applicable Emumba Software Services Agreement (the Agreement) and Customer end user license agreements
* granting a non-assignable, non-transferable and non-exclusive license to use the software for it's own data processing
* purposes under the terms defined in the Agreement. Except as otherwise granted within the terms of the Agreement, copying
* or reproduction of any part of this source code or associated reference material to any other location for further
* reproduction or redistribution, and any amendments to this copyright notice, are expressly prohibited. Any reproduction or
* redistribution for sale or hiring of the Software not in accordance with the terms of the Agreement is a violation of
* copyright law
*/
#ifndef PRICE_LEVEL_H
#define PRICE_LEVEL_H
#include "price.h"
#include <cstdint>
#include <map>
namespace emumba {
namespace deribit_connector {
namespace data_types {
/**
* @brief Represents price level of an order book.
*
*/
class price_level
{
public:
class iterator
{
public:
typedef iterator self_type;
iterator(void *ptr, int side);
/**
* @brief Increments the iterator position
*
* @return self_type
*/
self_type operator++();
/**
* @brief Increments the iterator position
*
* @return self_type
*/
self_type operator++(int);
/**
* @brief Retrieve price level at current position of iterator
*
* @return price_level
*/
price_level operator*();
/**
* @brief Equal operator for iterator
*
* @return bool
*/
bool operator==(const self_type &rhs);
/**
* @brief Not equal operator for iterator
*
* @return bool
*/
bool operator!=(const self_type &rhs);
/**
* @brief Returns side of the book; 0:bid, 1:ask
*
* @return int
*/
int side();
/**
* @brief Returns price object of the book at the current iterator position
*
* @return double
*/
emumba::market_data::price price();
/**
* @brief Returns quantity of the book at the current iterator position
*
* @return double
*/
double quantity();
/**
* @brief Returns order count of the book at the current iterator position, Zero if not available
*
* @return uint32_t
*/
uint32_t order_count();
private:
std::map<double, data_types::price_level> * _book_ptr;
price_level * _current_ptr;
std::map<double, emumba::deribit_connector::data_types::price_level>::iterator _book_level_itr;
int _side;
};
price_level();
price_level(emumba::market_data::price price, double amount);
/**
* @brief Returns the price object of a price level.
*
* @return emumba::market_data::price
*/
emumba::market_data::price get_price() const;
/**
* @brief Returns the amount of a price level.
*
* @return double
*/
double get_amount() const;
/**
* @brief Sets the amount of a price level.
*
* @param amount
*/
void set_amount(const double &amount);
private:
emumba::market_data::price _price;
double _amount;
};
} // namespace data_types
} // namespace deribit_connector
} // namespace emumba
#endif /* PRICE_LEVEL_H */
@@ -0,0 +1,39 @@
#ifndef EMUMBA_RPC_MESSAGE_H
#define EMUMBA_RPC_MESSAGE_H
#include "document_to_string.h"
#include "error.h"
#include "rapidjson/document.h"
#include <string>
namespace emumba {
namespace deribit_connector {
namespace data_types {
class rpc_message
{
public:
rpc_message(const std::string &rpc_msg);
const int get_id() const;
const rapidjson::Value & get_method() const;
const rapidjson::Value & get_result() const;
const rapidjson::Value & get_params() const;
const emumba::deribit_connector::data_types::error get_error();
const bool is_response() const;
const bool is_notification() const;
const bool is_batch() const;
const bool is_error() const;
const bool has_id() const;
private:
rapidjson::Value _default_value;
rapidjson::Document _message;
document_to_string _convert_document;
};
} // namespace data_types
} // namespace deribit_connector
} // namespace emumba
#endif /* EMUMBA_RPC_MESSAGE_H */
@@ -0,0 +1,139 @@
#ifndef SESSION_H
#define SESSION_H
#include "error.h"
#include "rpc_message.h"
#ifdef TRACEPOINTS
#include "timer.h"
#endif
#include "document_to_string.h"
#include "market_data_common.h"
#include "utils.h"
#include "websocket.h"
#include <functional>
#include <cstring>
#include <string>
#include <vector>
#ifndef ENABLE_BOOST_WEBSOCKET
#include "epoll_timer.h"
#include "io_handler.h"
#endif
typedef std::function<void(const emumba::deribit_connector::data_types::error &,
const emumba::deribit_connector::data_types::rpc_message &)>
on_receive_cb_t;
typedef std::function<void()> on_reconnect_cb_t;
typedef std::function<void()> on_print_book_cb_t;
typedef std::function<void(const emumba::market_data::market_data_error &)> on_error_cb_t;
namespace emumba {
namespace deribit_connector {
class session
{
public:
enum state
{
not_connected,
connecting,
connected
};
session(emumba::connector::io_handler &io, uint id);
void set_session_params(const std::string &uri_in, const configuration::trading_session_config &trading_config,
const configuration::credential_config &cred_config);
void set_on_receive_callback(on_receive_cb_t fn_in);
void set_on_reconnect_callback(on_reconnect_cb_t fn_in);
void set_on_print_book_callback(on_print_book_cb_t fn_in);
void set_on_error_callback(on_error_cb_t fn_in);
#ifndef ENABLE_BOOST_WEBSOCKET
const bool &is_reception_active() const;
void clear_reception_state();
void on_rate_timer_expiry();
#endif
void print_book();
void start_session();
void poll();
void send_message(const char *msg_buff);
void on_close();
const state &get_state() const;
const uint & get_session_id() const;
#ifdef TRACEPOINTS
void set_timer_reference(emumba::utils::timer *timer);
#endif
private:
on_receive_cb_t on_receive_cb;
on_reconnect_cb_t on_reconnect_cb;
on_print_book_cb_t on_print_book_cb;
on_error_cb_t on_error_cb;
std::string _websocket_uri;
std::string _client_name;
std::string _client_id;
std::string _client_secret;
int _interval;
std::vector<std::string> _buffered_messages;
emumba::connector::websocket::client _socket;
emumba::deribit_connector::configuration::reconnection_config _reconnect_config;
#ifndef ENABLE_BOOST_WEBSOCKET
bool _reception_active;
uint8_t _num_of_requests;
emumba::connector::epoll_timer _rate_limit_timer;
#endif
char _message_set_heartbeat[100];
char _message_test[100];
state _state;
uint8_t _number_of_tries;
uint _id;
std::string _access_token;
std::string _refresh_token;
document_to_string _convert_document;
bool _reconnection;
rapidjson::StringBuffer _str_buff;
rapidjson::Writer<rapidjson::StringBuffer> _writer;
char _jsonrpc_version[4];
#ifdef TRACEPOINTS
emumba::utils::timer *_timer;
#endif // TRACEPOINTS
private:
void create_heartbeat_messages();
/**
* @brief Parses the received response, constructs the json message to be sent by
* client application and returns it as string.
*
* @par This function gets called when:
* A response is received on the websocket and the application wants to send a new request.
* The function creates the json message to be sent and returns it in std::string format.
*
* @return std::string Message to be sent on the websocket
* @param resp_payload Received message in raw string form.
*/
void process_response(std::string resp_payload);
/**
* @brief Send the authentication message upon successful connection to the exchange websocket
*
* @return std::string
*/
void send_auth_message();
/**
* @brief Called at the end of a websocket transmission. Used in case the creator of this session
* wants to send packets continuously without waiting for reception.
*
* @return std::string
*/
const std::string auth = "public/auth";
const std::string set_heartbeat = "public/set_heartbeat";
const std::string test = "public/test"; // set these value in constructor
};
} // namespace deribit_connector
} // namespace emumba
#endif /* SESSION_H */
@@ -0,0 +1,62 @@
#ifndef SESSION_MANAGER_H
#define SESSION_MANAGER_H
#include "session.h"
#ifndef ENABLE_BOOST_WEBSOCKET
#include "epoll_timer.h"
#include "io_handler.h"
#endif
#include <memory>
#include <string>
#include <vector>
namespace emumba {
namespace deribit_connector {
class session_manager
{
public:
session_manager(const std::string &uri_in, const configuration::trading_session_config &trading_config);
/**
* @brief Creates a new session and returns an integer ID to identify session
*
* @return int id: An ID associated with the session to keep track of multiple
* sessions created by trader_session and market_handler classes.
*/
std::shared_ptr<session> create_new_session(const configuration::credential_config &cred_config,
on_receive_cb_t session_receive_cb);
/**
* @brief Polls all active sessions in an infinite loop. This function subsequently calls
* the underlying websocket's run_one() function of every session instance.
*
*/
void run();
uint get_core_id();
const bool & get_cancel_on_disconnect() const;
emumba::connector::io_handler &get_io();
#ifndef ENABLE_BOOST_WEBSOCKET
void on_timeout();
#endif
private:
uint _id;
std::string _websocket_uri;
configuration::trading_session_config _trading_config;
std::string outbound_message;
#ifndef ENABLE_BOOST_WEBSOCKET
emumba::connector::io_handler _io;
emumba::connector::epoll_timer _reconnection_timer;
#endif
std::vector<std::shared_ptr<session>> active_session_list;
void init_nanolog();
};
} // namespace deribit_connector
} // namespace emumba
#endif /* SESSION_MANAGER_H */
@@ -0,0 +1,37 @@
/**
* @file spdlogger.h
* @brief Provides MACROS for logging. Also contains functions to configure
* logging.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned
* by Emumba (Pvt) Ltd [Emumba] and is protected by copyright law and
* international copyright treaties. Access to and use of the software is
* governed by the terms of the applicable Emumba Software Services Agreement
* (the Agreement) and Customer end user license agreements granting a
* non-assignable, non-transferable and non-exclusive license to use the
* software for it's own data processing purposes under the terms defined in the
* Agreement. Except as otherwise granted within the terms of the Agreement,
* copying or reproduction of any part of this source code or associated
* reference material to any other location for further reproduction or
* redistribution, and any amendments to this copyright notice, are expressly
* prohibited. Any reproduction or redistribution for sale or hiring of the
* Software not in accordance with the terms of the Agreement is a violation of
* copyright law.
*/
#ifndef EMUMBA_SPDLOGGER_H
#define EMUMBA_SPDLOGGER_H
#include "lib/logging/logging.h"
// New macro that includes severity, filename and line number
#define LOG_ERROR(...) LOG(OrderMan, bts::Error, __VA_ARGS__)
#define LOG_WARNING(...) LOG(OrderMan, bts::Warn, __VA_ARGS__)
#define LOG_INFO(...) LOG(OrderMan, bts::Info, __VA_ARGS__)
#define LOG_DEBUG(...) LOG(OrderMan, bts::Diag, __VA_ARGS__)
#define LOG_TRACE(...) LOG(OrderMan, bts::Diag, __VA_ARGS__)
#endif /* EMUMBA_SPDLOGGER_H */
@@ -0,0 +1,144 @@
#ifndef SYMBOL_HANDLER_H
#define SYMBOL_HANDLER_H
#include "deribit_listener.h"
#include "deribit_utils.h"
#include "document_to_string.h"
#include "market_data_common.h"
#include "orders_handler.h"
#include "rapidjson/document.h"
#include "rpc_message.h"
#include "session_manager.h"
#include "symbol_instruments.h"
#include "utils.h"
#ifdef TRACEPOINTS
#include "timer.h"
#endif // TRACEPOINTS
namespace emumba {
namespace deribit_connector {
class symbol_handler
{
public:
symbol_handler(const configuration::symbol_config &symbol_configuration, std::shared_ptr<session_manager> manager_ref,
std::shared_ptr<orders_handler> orders_handler, const int &heartbeat_interval,
deribit_listener *trade_listener, const configuration::credential_config &cred_config);
void print_all_books();
std::list<std::string> get_future_expiries(const std::string &underlying_instrument, const int &expiries,
const std::string &expiry_cycle = "week");
std::list<std::string> get_options_expiries(const std::string &underlying_instrument, const int &expiries,
const std::string &expiry_cycle = "week");
const std::string &get_symbol_name() const;
const uint & get_session_id() const;
void reconfigure_instrument_list(const std::list<std::string> instrument_list, const bool &append);
private:
void initialize();
void send_request_get_instruments();
void send_request_get_combos();
void subscribe_to_symbol_channels();
void on_receive(const emumba::deribit_connector::data_types::error &ec, const data_types::rpc_message &received_msg);
void on_reconnect();
void on_error(const emumba::market_data::market_data_error &err);
void timer_cb();
void send_message(const char *send_msg);
void subscribe_to_instrument_state();
void subscribe_to_public_markprice();
void subscribe_to_public_index();
void subscribe_to_private_portfolio();
void unsubscribe_to_public_markprice();
void unsubscribe_to_public_index();
void unsubscribe_to_private_portfolio();
/**
* @brief Processes the subscription messages
*
* @param subscription_message Json structure containing the params object of subscription message
*/
void process_subscription(const data_types::rpc_message &subscription_message);
/**
* @brief Processes the reponse messages
*
* @param response_message Json structure containing the whole response message
*/
void process_response(const emumba::deribit_connector::data_types::error &ec,
const data_types::rpc_message & response_message);
void process_private_book_subscription(const rapidjson::Value &book_message);
void process_public_quote_subscription(const rapidjson::Value &quote_message);
void process_public_trades_subscription(const rapidjson::Value &trades_message);
void process_public_ticker_subscription(const rapidjson::Value &ticker_message);
void process_public_markprice_subscription(const rapidjson::Value &markprice_message);
void process_public_index_subscription(const rapidjson::Value &index_message);
void process_private_user_portfolio_subscription(const rapidjson::Value &user_portfolio_message);
void process_instrument_state_subscription(const rapidjson::Value &instrument_state_message);
void process_get_instruments_response(const emumba::deribit_connector::data_types::error &ec,
const data_types::rpc_message & get_instruments_message);
void process_get_combos_response(const emumba::deribit_connector::data_types::error &ec,
const data_types::rpc_message & get_combos_message);
void process_public_subscribe_response(const emumba::deribit_connector::data_types::error &ec,
const data_types::rpc_message & public_subscribe_message);
void process_private_subscribe_response(const emumba::deribit_connector::data_types::error &ec,
const data_types::rpc_message & private_subscribe_message);
void process_public_unsubscribe_response(const emumba::deribit_connector::data_types::error &ec,
const data_types::rpc_message &public_unsubscribe_message);
void process_private_unsubscribe_response(const emumba::deribit_connector::data_types::error &ec,
const data_types::rpc_message &private_unsubscribe_message);
uint _session_id;
std::string _symbol_name;
std::string _currency_type;
std::string _instrument_types;
instruments_set _interested_instruments;
configuration::symbol_config _symbol_config;
std::unique_ptr<symbol_instruments> _symbol_instruments;
emumba::market_data::market_data_statistics _market_data_statistics;
emumba::market_data::quote _quote;
std::unordered_set<std::string> _interested_instruments_type;
std::string _public_subscribe_method = "public/subscribe";
std::string _public_unsubscribe_method = "public/unsubscribe";
std::string _private_subscribe_method = "private/subscribe";
std::string _private_unsubscribe_method = "private/unsubscribe";
std::shared_ptr<session_manager> _session_manager;
emumba::connector::epoll_timer _epoll_timer;
std::shared_ptr<session> _session;
std::shared_ptr<orders_handler> _orders_handler;
deribit_listener * _trade_listener;
document_to_string _convert_document;
const std::string _jsonrpc;
rapidjson::StringBuffer _str_buff;
rapidjson::Writer<rapidjson::StringBuffer> _writer;
std::vector<instrument_info> _get_instrument_data;
std::map<std::string /*instrument name*/, instrument_info * /*get instrument api info*/> _get_instrument_ref;
std::unordered_map<std::string /*combo name*/, std::vector<legs_info> /*get instrument api info*/> _combos_data;
std::list<emumba::market_data::security> _complete_securities_data;
std::vector<emumba::market_data::security const *> _complete_securities_ref;
std::list<emumba::market_data::trade_record> _trade_records_list;
std::vector<emumba::market_data::trade_record const *> _trade_records_ref;
emumba::market_data::trade_record _trade_rec;
configuration::credential_config _cred_config;
legs_info _legs_info;
instruments_set _instruments_to_add;
instruments_set _instruments_to_remove;
bool _get_inst_req_sent;
bool _is_whitelist_init;
bool _is_timer_start;
#ifdef TRACEPOINTS
emumba::utils::timer _timer;
#endif // TRACEPOINTS
};
} // namespace deribit_connector
} // namespace emumba
#endif /* SYMBOL_HANDLER_H */
@@ -0,0 +1,121 @@
#ifndef SYMBOL_INSTRUMENTS_H
#define SYMBOL_INSTRUMENTS_H
#include "deribit_listener.h"
#include "document_to_string.h"
#include "instrument_data.h"
#include "rapidjson/document.h"
#include "session.h"
#include <bytell_hash_map.hpp>
#include <future>
#include <list>
#include <unordered_map>
#include <unordered_set>
#include <vector>
typedef std::unordered_set<std::string> instruments_set;
namespace emumba {
namespace deribit_connector {
class symbol_instruments
{
public:
symbol_instruments(const std::string &symbol_name, const std::shared_ptr<session> session,
const configuration::subscription_config &sub_config, deribit_listener *trade_listener);
void initialize_instruments(const instruments_set & set_of_instruments,
std::map<std::string, instrument_info *> instruments_info); // Used during Bootup
void add_new_instruments(
const instruments_set & set_of_instruments,
std::map<std::string, instrument_info *> instruments_info); // Used when new instruments added during run time
void unsub_instruments(const instruments_set &set_of_instruments);
void resubscribe_to_private_book(const std::string &instrument_name);
bool is_instrument_being_resubscribed(const std::string &instrument_name);
void check_if_channel_is_being_resubscribed(const std::string &channel_name);
void reset_symbol_instruments();
std::ostream & print_all_books(std::ostream &out);
friend std::ostream &operator<<(std::ostream &out, symbol_instruments &symbol_instruments)
{
return symbol_instruments.print_all_books(out);
}
ALWAYS_INLINE instrument_data &get_instrument_data(const std::string &instrument_name)
{
return _white_list.at(instrument_name);
}
ALWAYS_INLINE bool is_instrument_in_whitelist(const std::string &instrument_name)
{
return _white_list.find(instrument_name) != _white_list.end();
}
private:
void add_to_whitelist(const std::string &instrument_name, instrument_info *&instr_info);
void sub_instruments();
void send_message(const char *msg_buff);
void gen_interested_channels_str();
void gen_subscribe_to_public_channels_msg(const std::string &instrument_name);
void gen_subscribe_to_private_channels_msg(const std::string &instrument_name);
void send_subscribe_to_public_channels_msg();
void send_subscribe_to_private_channels_msg();
void gen_unsubscribe_to_public_channels_msg(const std::string &instrument_name);
void gen_unsubscribe_to_private_channels_msg(const std::string &instrument_name);
void send_unsubscribe_to_public_channels_msg();
void send_unsubscribe_to_private_channels_msg();
void subscribe_to_private_book(const std::string &instrument_name);
void unsubscribe_to_private_book(const std::string &instrument_name);
ska::flat_hash_map<std::string /*Instrument Name*/, instrument_data /*Instrument data*/,
ska::power_of_two_std_hash<std::string>>
_white_list;
std::unordered_set<std::string> _instruments_to_resubscribe;
std::string _symbol_name;
std::string _interested_channels_str;
std::string _subscription_interval;
std::shared_ptr<session> _session;
const session::state & _session_state;
configuration::subscription_config _subscriptions_config;
uint _max_whitelist_size;
// uint _no_of_requests_sent;
deribit_listener * _trade_listener;
document_to_string _convert_document;
std::vector<std::future<void>> _async_f_list;
bool _all_sub_req_sent;
const std::string _public_subscribe_method;
const std::string _public_unsubscribe_method;
const std::string _private_subscribe_method;
const std::string _private_unsubscribe_method;
char _jsonrpc_version[4];
rapidjson::StringBuffer _str_buff;
rapidjson::Writer<rapidjson::StringBuffer> _writer;
// TODO: Can use a struct for the following variables
// subscribe message variables
instruments_set _instruments_to_subscribe;
rapidjson::StringBuffer _public_sub_str_buff;
rapidjson::Writer<rapidjson::StringBuffer> _public_sub_writer;
rapidjson::StringBuffer _private_sub_str_buff;
rapidjson::Writer<rapidjson::StringBuffer> _private_sub_writer;
uint _public_subs_in_request_msg;
uint _private_subs_in_request_msg;
bool _is_new_public_sub_msg;
bool _is_new_private_sub_msg;
// unsubscribe message variables
rapidjson::StringBuffer _public_unsub_str_buff;
rapidjson::Writer<rapidjson::StringBuffer> _public_unsub_writer;
rapidjson::StringBuffer _private_unsub_str_buff;
rapidjson::Writer<rapidjson::StringBuffer> _private_unsub_writer;
uint _public_unsubs_in_request_msg;
uint _private_unsubs_in_request_msg;
bool _is_new_public_unsub_msg;
bool _is_new_private_unsub_msg;
};
} // namespace deribit_connector
} // namespace emumba
#endif /* SYMBOL_INSTRUMENTS_H */
@@ -0,0 +1,42 @@
#ifndef TIMER_H
#define TIMER_H
#include <sys/time.h>
#include <time.h>
namespace emumba {
namespace utils {
class timer
{
private:
struct timespec start_time, end_time;
public:
timer()
{}
inline void start()
{
clock_gettime(CLOCK_MONOTONIC, &start_time);
}
inline void stop()
{
clock_gettime(CLOCK_MONOTONIC, &end_time);
}
inline double get_elapsed_ms()
{
return get_elapsed_ns() / 1000000.0;
}
inline double get_elapsed_us()
{
return get_elapsed_ns() / 1000.0;
}
double get_elapsed_ns()
{
return ((1000000000.0 * static_cast<double>(end_time.tv_sec - start_time.tv_sec)) +
static_cast<double>(end_time.tv_nsec - start_time.tv_nsec));
}
};
} // namespace utils
} // namespace emumba
#endif
@@ -0,0 +1,703 @@
#include "trader_session.h"
#include "deribit_utils.h"
#include "method_identifiers.h"
#include "rpc_message.h"
#include "spdlogger.h"
#include "utils.h"
emumba::deribit_connector::trader_session::trader_session(
std::shared_ptr<session_manager> manager_ref, std::shared_ptr<orders_handler> orders_handler,
const emumba::deribit_connector::configuration::credential_config &credential_config,
std::vector<std::string> &configured_symbols, deribit_listener *trad_listener)
: _sess_manager(manager_ref)
, _orders_handler(orders_handler)
, _trading_listener(trad_listener)
, _subaccount_index(credential_config.index)
, _credential_config(credential_config)
, _configured_symbols(configured_symbols)
, _private_subscribe_method("private/subscribe")
, _jsonrpc_version("2.0")
{
open_new_connection();
subscribe_to_user_channels();
get_cancel_on_disconnect();
}
void emumba::deribit_connector::trader_session::open_new_connection()
{
_session = _sess_manager->create_new_session(
_credential_config, std::bind(&trader_session::on_receive, this, std::placeholders::_1, std::placeholders::_2));
_session->set_on_reconnect_callback(std::bind(&trader_session::on_reconnect, this));
_session->set_on_error_callback(std::bind(&trader_session::on_error, this, std::placeholders::_1));
#ifdef TRACEPOINTS
_session->set_timer_reference(&_timer);
#endif // TRACEPOINTS
_session_id = _session->get_session_id();
}
void emumba::deribit_connector::trader_session::on_reconnect()
{
subscribe_to_user_channels();
get_cancel_on_disconnect();
_order_queues.clear();
}
void emumba::deribit_connector::trader_session::on_error(const emumba::market_data::market_data_error &err)
{
// This function is called incase disconnection occurs
if (err.err_type == emumba::market_data::market_data_error::error_type::disconnection)
{
emumba::market_data::disconnected_session disconnected_sess;
disconnected_sess.session_id = _session_id;
disconnected_sess.sess_type = emumba::market_data::session_type::order_entry;
disconnected_sess.name = "";
#ifdef ENABLE_API
_trading_listener->on_disconnection(disconnected_sess);
#endif
// call on order updates
_orders_handler->on_trader_sess_disconnect(_session_id, _subaccount_index);
}
else
{
#ifdef ENABLE_API
_trading_listener->on_error(err);
#endif
}
}
void emumba::deribit_connector::trader_session::get_cancel_on_disconnect()
{
_str_buff.Clear();
_writer.Reset(_str_buff);
_writer.StartObject();
_writer.Key("jsonrpc");
_writer.String("2.0");
_writer.Key("method");
_writer.String("private/get_cancel_on_disconnect");
_writer.Key("id");
_writer.Uint64((int)method_id::get_cancel_on_disconnect);
_writer.Key("params");
_writer.StartObject();
_writer.Key("scope");
_writer.String("connection");
_writer.EndObject();
_writer.EndObject();
LOG_DEBUG(
"Get cancel on disconnect request: {}",
std::string{_str_buff.GetString()});
send_message(_str_buff.GetString());
}
void emumba::deribit_connector::trader_session::send_message(const char *msg_buff)
{
_session->send_message(msg_buff);
}
void emumba::deribit_connector::trader_session::subscribe_to_user_channels()
{
subscribe_to_user_orders();
subscribe_to_user_trades();
}
void emumba::deribit_connector::trader_session::subscribe_to_user_orders()
{
_str_buff.Clear();
_writer.Reset(_str_buff);
_writer.StartObject();
_writer.Key("jsonrpc");
_writer.String(_jsonrpc_version);
_writer.Key("method");
_writer.String(_private_subscribe_method.c_str());
_writer.Key("id");
_writer.Uint64((int)method_id::private_subscribe);
_writer.Key("params");
_writer.StartObject();
_writer.Key("channels");
_writer.StartArray();
for (const auto &each_symbol : _configured_symbols)
{
auto request_string = "user.orders.any." + each_symbol + ".raw";
_writer.String(request_string.c_str());
}
_writer.EndArray();
_writer.EndObject();
_writer.EndObject();
send_message(_str_buff.GetString());
}
void emumba::deribit_connector::trader_session::subscribe_to_user_trades()
{
_str_buff.Clear();
_writer.Reset(_str_buff);
_writer.StartObject();
_writer.Key("jsonrpc");
_writer.String(_jsonrpc_version);
_writer.Key("method");
_writer.String(_private_subscribe_method.c_str());
_writer.Key("id");
_writer.Uint64((int)method_id::private_subscribe);
_writer.Key("params");
_writer.StartObject();
_writer.Key("channels");
_writer.StartArray();
for (const auto &each_symbol : _configured_symbols)
{
auto request_string = "user.trades.any." + each_symbol + ".raw";
_writer.String(request_string.c_str());
}
_writer.EndArray();
_writer.EndObject();
_writer.EndObject();
send_message(_str_buff.GetString());
}
void emumba::deribit_connector::trader_session::on_receive(const emumba::deribit_connector::data_types::error &ec,
const data_types::rpc_message & received_msg)
{
if (__builtin_expect(received_msg.is_notification(), true))
{
process_subscription(received_msg);
}
else if (received_msg.is_response() || received_msg.is_error())
{
process_response(ec, received_msg);
}
else
{
LOG_WARNING("Message received other than notification and response");
}
}
void emumba::deribit_connector::trader_session::process_response(const emumba::deribit_connector::data_types::error &ec,
const data_types::rpc_message &response_message)
{
const rapidjson::Value &result = response_message.get_result();
UNUSED(ec);
UNUSED(result);
LOG_TRACE("Trader session | on_receive | {}", _convert_document.to_string(result));
switch (response_message.get_id())
{
case (int)method_id::get_account_summary: {
#ifdef ENABLE_API
_trading_listener->on_account_summary(ec, _convert_document.to_string(result));
#endif
break;
}
case (int)method_id::new_order: {
_orders_handler->process_new_order(ec, result, _order_queues, _subaccount_index);
break;
}
case (int)method_id::edit_order: {
_orders_handler->process_edit_order(ec, result, _order_queues, _subaccount_index);
break;
}
case (int)method_id::cancel_order: {
_orders_handler->process_cancel_order(ec, result, _order_queues, _subaccount_index);
break;
}
case (int)method_id::cancel_all_by_currency: {
#ifdef ENABLE_API
_order_cancellation_info.ec = ec;
ec.value() == 0 ? _order_cancellation_info.no_of_orders_cancelled = result.GetUint()
: _order_cancellation_info.no_of_orders_cancelled = 0;
_order_cancellation_info.type = data_types::cancellation_type::by_currency;
_order_cancellation_info.subaccount_index = _subaccount_index;
_trading_listener->on_mass_cancellation(_order_cancellation_info);
#endif
break;
}
case (int)method_id::cancel_all_by_instrument: {
#ifdef ENABLE_API
_order_cancellation_info.ec = ec;
ec.value() == 0 ? _order_cancellation_info.no_of_orders_cancelled = result.GetUint()
: _order_cancellation_info.no_of_orders_cancelled = 0;
_order_cancellation_info.type = data_types::cancellation_type::by_instrument;
_order_cancellation_info.subaccount_index = _subaccount_index;
_trading_listener->on_mass_cancellation(_order_cancellation_info);
#endif
break;
}
case (int)method_id::cancel_all_positions: {
#ifdef ENABLE_API
_trading_listener->on_cancel_all_positions(ec, _convert_document.to_string(result));
#endif
break;
}
case (int)method_id::get_position: {
#ifdef ENABLE_API
_trading_listener->on_get_position(ec, _convert_document.to_string(result));
#endif
break;
}
case (int)method_id::get_open_orders_by_instrument: {
#ifdef ENABLE_API
_trading_listener->on_get_open_orders(ec, _convert_document.to_string(result));
#endif
break;
}
case (int)method_id::get_mmp_config: {
#ifdef ENABLE_API
_trading_listener->on_get_mmp_config(ec, _convert_document.to_string(result));
#endif
break;
}
case (int)method_id::set_mmp_config: {
#ifdef ENABLE_API
_trading_listener->on_set_mmp_config(ec, _convert_document.to_string(result));
#endif
break;
}
case (int)method_id::reset_mmp_config: {
#ifdef ENABLE_API
_trading_listener->on_reset_mmp(ec, _convert_document.to_string(result));
#endif
break;
}
case (int)method_id::get_cancel_on_disconnect: {
process_get_cancel_on_disconnect(ec, result);
break;
}
case (int)method_id::enable_cancel_on_disconnect: {
process_enable_cancel_on_disconnect(ec, result);
break;
}
case (int)method_id::disable_cancel_on_disconnect: {
process_disable_cancel_on_disconnect(ec, result);
break;
}
case (int)method_id::get_positions: {
process_get_positions(ec, result);
break;
}
case (int)method_id::get_open_orders_by_currency: {
process_get_open_orders(ec, result);
break;
}
case (int)method_id::get_user_trades_by_currency_and_time: {
process_get_user_trades_by_currency_and_time(ec, result);
break;
}
case (int)method_id::rfq_request: {
process_rfq_response(ec, result);
break;
}
case (int)method_id::create_combo: {
process_create_combo(ec, result);
break;
}
case (int)method_id::private_subscribe: {
LOG_TRACE("Response | Private | subscribe | Message: {}", _convert_document.to_string(result));
break;
}
default: {
LOG_WARNING("Unknown response received for message id: {}", response_message.get_id());
}
}
}
void emumba::deribit_connector::trader_session::process_subscription(
const emumba::deribit_connector::data_types::rpc_message &subscription_message)
{
const rapidjson::Value &subscription_message_params = subscription_message.get_params();
std::string subscription_channel = subscription_message_params["channel"].GetString();
LOG_TRACE("Subscription channel: {}, Subscription Message Received: {}", subscription_channel,
_convert_document.to_string(subscription_message_params));
if (strncmp(subscription_channel.c_str(), "user.orders.any", 15) == 0)
{
_orders_handler->process_private_user_orders_subscription(subscription_message_params, _subaccount_index);
}
else if (strncmp(subscription_channel.c_str(), "user.trades.any", 15) == 0)
{
_orders_handler->process_private_user_trades_subscription(subscription_message_params, _subaccount_index);
}
}
void emumba::deribit_connector::trader_session::new_order_queue_push(const emumba::order_entry::order_key &order_key)
{
_order_queues.new_order_queue.push(order_key);
LOG_DEBUG("New Order Queue | Push | Queue size: {}", _order_queues.new_order_queue.size());
}
void emumba::deribit_connector::trader_session::edit_queue_push(const emumba::order_entry::order_key &order_key)
{
_order_queues.edit_queue.push(order_key);
LOG_DEBUG("Edit Order Queue | Push | Queue size: {}", _order_queues.edit_queue.size());
}
void emumba::deribit_connector::trader_session::cancel_queue_push(const emumba::order_entry::order_key &order_key)
{
_order_queues.cancel_queue.push(order_key);
LOG_DEBUG("Cancel Order Queue | Push | Queue size: {}", _order_queues.cancel_queue.size());
}
const uint &emumba::deribit_connector::trader_session::get_session_id() const
{
return _session_id;
}
void emumba::deribit_connector::trader_session::process_get_cancel_on_disconnect(
const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result)
{
if (!ec.value())
{
bool is_enabled = result["enabled"].GetBool();
LOG_DEBUG("Cancel on disconnect | Exchange value: {}", is_enabled);
if (is_enabled == false)
{
enable_cancel_on_disconnect();
}
}
else
{
LOG_ERROR(
"Error in response ec.value: {}, ec.message: {}", ec.value(),
ec.message());
}
}
void emumba::deribit_connector::trader_session::enable_cancel_on_disconnect()
{
_str_buff.Clear();
_writer.Reset(_str_buff);
_writer.StartObject();
_writer.Key("jsonrpc");
_writer.String("2.0");
_writer.Key("method");
_writer.String("private/enable_cancel_on_disconnect");
_writer.Key("id");
_writer.Uint64((int)method_id::enable_cancel_on_disconnect);
_writer.Key("params");
_writer.StartObject();
_writer.Key("scope");
_writer.String("connection");
_writer.EndObject();
_writer.EndObject();
LOG_DEBUG(
"Enable cancel on disconnect request: {}",
std::string{_str_buff.GetString()});
send_message(_str_buff.GetString());
}
void emumba::deribit_connector::trader_session::disable_cancel_on_disconnect()
{
// TODO: This function and its response process function can be removed.
_str_buff.Clear();
_writer.Reset(_str_buff);
_writer.StartObject();
_writer.Key("jsonrpc");
_writer.String("2.0");
_writer.Key("method");
_writer.String("private/disable_cancel_on_disconnect");
_writer.Key("id");
_writer.Uint64((int)method_id::disable_cancel_on_disconnect);
_writer.Key("params");
_writer.StartObject();
_writer.Key("scope");
_writer.String("connection");
_writer.EndObject();
_writer.EndObject();
LOG_DEBUG(
"Disable cancel on disconnect request: {}",
std::string{_str_buff.GetString()});
send_message(_str_buff.GetString());
}
void emumba::deribit_connector::trader_session::process_enable_cancel_on_disconnect(
const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result)
{
if (!ec.value())
{
if (strcmp(result.GetString(), "ok") == 0)
{
LOG_DEBUG("Enable cancel on disconnect successful");
}
else
{
LOG_ERROR(
"Enable cancel on disconnect failed. Result: {}",
std::string{result.GetString()});
}
}
else
{
LOG_ERROR(
"Error in response ec.value: {}, ec.message: {}", ec.value(),
ec.message());
}
}
void emumba::deribit_connector::trader_session::process_disable_cancel_on_disconnect(
const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result)
{
// TODO: This function can be removed
if (!ec.value())
{
if (strcmp(result.GetString(), "ok") == 0)
{
LOG_DEBUG("Disable cancel on disconnect successful");
}
else
{
LOG_ERROR(
"Disable cancel on disconnect failed. Result: {}",
std::string{result.GetString()});
}
}
else
{
LOG_ERROR(
"Error in response ec.value: {}, ec.message: {}", ec.value(),
ec.message());
}
}
void emumba::deribit_connector::trader_session::process_get_positions(
UNUSED_PARAM const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result)
{
for (rapidjson::Value::ConstValueIterator each_position_itr = result.Begin(); each_position_itr != result.End();
++each_position_itr)
{
_position_rec.subaccount_index = _subaccount_index;
auto each_position = each_position_itr->GetObject();
_position_rec.sec_id.value() = each_position["instrument_name"].GetString();
_position_rec.qty = emumba::market_data::quantity(each_position["size"].GetDouble());
_position_rec.average_price = each_position["average_price"].GetDouble();
const char *side = each_position["direction"].GetString();
if ((strcmp(side, "buy") == 0))
{
_position_rec.position_side = emumba::market_data::side::buy;
}
else if ((strcmp(side, "sell") == 0))
{
_position_rec.position_side = emumba::market_data::side::sell;
}
else
{
_position_rec.position_side = emumba::market_data::side::none;
}
_position_records_list.push_back(_position_rec);
_position_records_ref.push_back(&_position_records_list.back());
}
LOG_DEBUG("No of Positions: {}", _position_records_ref.size());
#ifdef ENABLE_API
_trading_listener->on_get_all_positions(ec, _position_records_ref);
#endif
_position_records_ref.clear();
_position_records_list.clear();
}
void emumba::deribit_connector::trader_session::process_get_open_orders(
UNUSED_PARAM const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result)
{
for (rapidjson::Value::ConstValueIterator each_open_order_itr = result.Begin(); each_open_order_itr != result.End();
++each_open_order_itr)
{
_open_order_rec.subaccount_index = _subaccount_index;
auto each_open_order = each_open_order_itr->GetObject();
_open_order_rec.sec_id.value() = each_open_order["instrument_name"].GetString();
_open_order_rec.exchange_order_id = each_open_order["order_id"].GetString();
std::string label =
each_open_order["label"]
.GetString(); // Check what if some open order does not have any label. Is this empty or doesnt exist?
std::string _label_uuid;
std::uint64_t _label_order_id;
_orders_handler->parse_label_value(label, _label_uuid, _label_order_id);
_open_order_rec.internal_order_id = _label_order_id;
const char *order_state = each_open_order["order_state"].GetString();
if (strcmp(order_state, "open") == 0)
{
_open_order_rec.order_status = emumba::order_entry::status::working;
}
else if (strcmp(order_state, "filled") == 0)
{
_open_order_rec.order_status = emumba::order_entry::status::completed;
}
else if (strcmp(order_state, "cancelled") == 0)
{
_open_order_rec.order_status = emumba::order_entry::status::canceled;
}
else if (strcmp(order_state, "rejected") == 0)
{
_open_order_rec.order_status = emumba::order_entry::status::rejected;
}
else if (strcmp(order_state, "untriggered") == 0)
{
_open_order_rec.order_status = emumba::order_entry::status::untriggered;
}
else
{
LOG_WARNING(
"Unknown order state received: {}", std::string{order_state});
}
const char *time_in_force = each_open_order["time_in_force"].GetString();
if (strcmp(time_in_force, "good_til_cancelled") == 0)
{
_open_order_rec.time_in_frc = emumba::order_entry::time_in_force::gtc;
}
else if (strcmp(time_in_force, "good_til_day") == 0)
{
_open_order_rec.time_in_frc = emumba::order_entry::time_in_force::day;
}
else if (strcmp(time_in_force, "fill_or_kill") == 0)
{
_open_order_rec.time_in_frc = emumba::order_entry::time_in_force::fill_or_kill;
}
else if (strcmp(time_in_force, "immediate_or_cancel") == 0)
{
_open_order_rec.time_in_frc = emumba::order_entry::time_in_force::ioc;
}
else
{
LOG_WARNING(
"Unknown time in force received: {}", std::string{time_in_force});
}
const char *side = each_open_order["direction"].GetString();
if ((strcmp(side, "buy") == 0))
{
_open_order_rec.order_side = emumba::market_data::side::buy;
}
else if ((strcmp(side, "sell") == 0))
{
_open_order_rec.order_side = emumba::market_data::side::sell;
}
else
{
_open_order_rec.order_side = emumba::market_data::side::none;
}
_open_order_rec.qty = emumba::market_data::quantity(each_open_order["amount"].GetDouble());
_open_order_rec.filled_qty = emumba::market_data::quantity(each_open_order["filled_amount"].GetDouble());
_open_order_rec.px = each_open_order["price"].GetDouble();
_open_order_rec.average_price = each_open_order["average_price"].GetDouble();
_open_order_rec.last_update_timestamp.set_timestamp(each_open_order["last_update_timestamp"].GetUint64());
_open_order_rec.creation_timestamp.set_timestamp(each_open_order["creation_timestamp"].GetUint64());
_open_order_rec.api = each_open_order["api"].GetBool();
if (each_open_order.HasMember("web"))
{
_open_order_rec.web = each_open_order["web"].GetBool();
}
else
{
_open_order_rec.web = false;
}
_open_order_records_list.push_back(_open_order_rec);
_open_order_records_ref.push_back(&_open_order_records_list.back());
}
LOG_DEBUG("No of Open Orders: {}", _open_order_records_ref.size());
#ifdef ENABLE_API
_trading_listener->on_get_all_open_orders(ec, _open_order_records_ref);
#endif
_open_order_records_ref.clear();
_open_order_records_list.clear();
}
void emumba::deribit_connector::trader_session::process_get_user_trades_by_currency_and_time(
const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result)
{
LOG_DEBUG("Trader session | process_get_user_trades_by_currency_and_time | Subaccount id: {}", _subaccount_index);
_user_trades_response.error = ec;
_user_trades_response.subaccount_index = _subaccount_index;
if (ec.value() == 0)
{
_user_trades_response.has_more = result["has_more"].GetBool();
const rapidjson::Value &user_trades = result["trades"];
for (rapidjson::Value::ConstValueIterator each_user_trade_itr = user_trades.Begin();
each_user_trade_itr != user_trades.End(); ++each_user_trade_itr)
{
auto each_user_trade = each_user_trade_itr->GetObject();
orders_handler::fill_trade_record(_trade_record, each_user_trade);
_trade_records_list.push_back(_trade_record);
_user_trades_response.trades.push_back(&_trade_records_list.back());
}
}
else
{
LOG_DEBUG("Error received in the process_get_user_trades_by_currency_and_time response | Subaccount id: {}",
_subaccount_index);
}
#ifdef ENABLE_API
_trading_listener->on_get_user_trades(_user_trades_response);
#endif
_user_trades_response.trades.clear();
_trade_records_list.clear();
}
void emumba::deribit_connector::trader_session::process_rfq_response(const emumba::deribit_connector::data_types::error &ec,
const rapidjson::Value &result)
{
_rfq_response.status = false;
_rfq_response.error = ec;
_rfq_response.subaccount_index = _subaccount_index;
if (ec.value() == 0)
{
std::string status = result.GetString();
if (status == "ok")
{
_rfq_response.status = true;
}
}
_trading_listener->on_request_for_quote(_rfq_response);
}
void emumba::deribit_connector::trader_session::process_create_combo(const emumba::deribit_connector::data_types::error &ec,
const rapidjson::Value &result)
{
_combo_detail.error = ec;
_combo_detail.subaccount_index = _subaccount_index;
if (ec.value() == 0)
{
_combo_detail.legs.clear();
_combo_detail.sec_id.value() = result["id"].GetString();
const rapidjson::Value &leg_trades = result["legs"];
for (rapidjson::Value::ConstValueIterator trade_itr = leg_trades.Begin(); trade_itr != leg_trades.End(); ++trade_itr)
{
auto trade = trade_itr->GetObject();
_leg_detail.instrument_name.value() = trade["instrument_name"].GetString();
_leg_detail.amount = trade["amount"].GetInt();
_combo_detail.legs.push_back(_leg_detail);
}
}
_trading_listener->on_create_combo(_combo_detail);
}
@@ -0,0 +1,106 @@
#ifndef TRADER_SESSION_H
#define TRADER_SESSION_H
#include "common.h"
#include "deribit_listener.h"
#include "deribit_utils.h"
#include "document_to_string.h"
#include "error.h"
#include "order_entry_common.h"
#include "orders_handler.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "session.h"
#include "session_manager.h"
#include "utils.h"
#ifdef TRACEPOINTS
#include "timer.h"
#endif // TRACEPOINTS
#include <list>
#include <string>
#include <vector>
namespace emumba {
namespace deribit_connector {
class trader_session
{
public:
trader_session(std::shared_ptr<session_manager> manager_ref, std::shared_ptr<orders_handler> orders_handler,
const emumba::deribit_connector::configuration::credential_config &credential_config,
std::vector<std::string> &configured_symbols, deribit_listener *trad_listener);
void send_message(const char *msg_buff);
void new_order_queue_push(const emumba::order_entry::order_key &order_key);
void edit_queue_push(const emumba::order_entry::order_key &order_key);
void cancel_queue_push(const emumba::order_entry::order_key &order_key);
const uint &get_session_id() const;
private:
void on_reconnect();
void on_error(const emumba::market_data::market_data_error &err);
void subscribe_to_user_channels();
void subscribe_to_user_orders();
void subscribe_to_user_trades();
void on_receive(const emumba::deribit_connector::data_types::error &ec, const data_types::rpc_message &received_msg);
void process_subscription(const data_types::rpc_message &subscription_message);
void process_response(const emumba::deribit_connector::data_types::error &ec,
const data_types::rpc_message & response_message);
void process_get_cancel_on_disconnect(const emumba::deribit_connector::data_types::error &ec,
const rapidjson::Value & result);
void open_new_connection();
void get_cancel_on_disconnect();
void enable_cancel_on_disconnect();
void disable_cancel_on_disconnect();
void process_enable_cancel_on_disconnect(const emumba::deribit_connector::data_types::error &ec,
const rapidjson::Value & result);
void process_disable_cancel_on_disconnect(const emumba::deribit_connector::data_types::error &ec,
const rapidjson::Value & result);
void process_get_positions(const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result);
void process_get_open_orders(const emumba::deribit_connector::data_types::error &ec, const rapidjson::Value &result);
void process_get_user_trades_by_currency_and_time(const emumba::deribit_connector::data_types::error &ec,
const rapidjson::Value & result);
void process_rfq_response(const emumba::deribit_connector::data_types::error &ec,
const rapidjson::Value & result);
void process_create_combo(const emumba::deribit_connector::data_types::error &ec,
const rapidjson::Value & result);
uint _session_id;
std::shared_ptr<session_manager> _sess_manager;
std::shared_ptr<session> _session;
std::shared_ptr<orders_handler> _orders_handler;
deribit_listener * _trading_listener;
document_to_string _convert_document;
order_queues _order_queues;
std::list<emumba::order_entry::position> _position_records_list;
std::vector<emumba::order_entry::position const *> _position_records_ref;
emumba::order_entry::position _position_rec;
std::list<emumba::order_entry::open_order> _open_order_records_list;
std::vector<emumba::order_entry::open_order const *> _open_order_records_ref;
emumba::order_entry::open_order _open_order_rec;
rapidjson::StringBuffer _str_buff;
rapidjson::Writer<rapidjson::StringBuffer> _writer;
uint _subaccount_index;
configuration::credential_config _credential_config;
std::vector<std::string> _configured_symbols;
const std::string _private_subscribe_method;
char _jsonrpc_version[4];
emumba::deribit_connector::data_types::cancellation_update _order_cancellation_info;
emumba::order_entry::user_trades_response _user_trades_response;
emumba::market_data::trade_record _trade_record;
std::list<emumba::market_data::trade_record> _trade_records_list;
emumba::order_entry::rfq_response _rfq_response;
emumba::order_entry::leg_detail _leg_detail;
emumba::order_entry::combo_detail _combo_detail;
#ifdef TRACEPOINTS
emumba::utils::timer _timer;
#endif // TRACEPOINTS
};
} // namespace deribit_connector
} // namespace emumba
#endif /* TRADER_H */
@@ -0,0 +1,224 @@
/**
* @file trading_session_interface.h
* @brief Provides a class with the basic interface for calling different private methods linked to a trading connection.
*
* @copyright Copyright (c) 2021
* Copyright Emumba (Pvt) Ltd [Emumba]. All rights reserved. This software owned by Emumba (Pvt) Ltd [Emumba] and is
* protected by copyright law and international copyright treaties. Access to and use of the software is governed by the
* terms of the applicable Emumba Software Services Agreement (the Agreement) and Customer end user license agreements
* granting a non-assignable, non-transferable and non-exclusive license to use the software for it's own data processing
* purposes under the terms defined in the Agreement. Except as otherwise granted within the terms of the Agreement, copying
* or reproduction of any part of this source code or associated reference material to any other location for further
* reproduction or redistribution, and any amendments to this copyright notice, are expressly prohibited. Any reproduction or
* redistribution for sale or hiring of the Software not in accordance with the terms of the Agreement is a violation of
* copyright law
*/
#ifndef TRADING_SESSION_INTERFACE_H
#define TRADING_SESSION_INTERFACE_H
#include "common.h"
#include <memory>
#include <string>
namespace emumba {
namespace deribit_connector {
class trader_session;
class orders_handler;
/**
* @brief Provides the basic interface for calling different private methods linked to a trading connection.
*
*/
class trading_session_interface
{
public:
trading_session_interface(std::shared_ptr<trader_session> trader_session_impl,
std::shared_ptr<orders_handler> orders_handler);
/**
* @brief Place a new Buy or Sell order.
*
* @param order Object containing necessary information required to place a new order. User will provide the
* information required to place an order using this object.
* @return emumba::market_data::order_key A unique key generated by the connector against each order. The user will
* provide this Key when an order needs to be updated or cancelled. Order Key will have a value of -1 in case the order
* is not sent successfully.
*/
emumba::order_entry::order_key launch_new_order(const emumba::order_entry::new_order &order);
/**
* @brief Linked to method "private/edit" to edit an order.
*
* @param update Object containing necessary information required to edit a specific order. This object will also
* contain the "emumba::market_data::order_key" provided to the user when a new order was placed.
* @return true The edit order message is sent successfully.
* @return false The edit order message is unsuccessfull. This can happen if the "emumba::market_data::order_key"
* provided in the "update" structure can not be mapped by the connector against an order.
*/
bool update_order(const emumba::order_entry::order_update &update);
/**
* @brief Linked to method "private/cancel" to cancel an order.
*
* @param key The "emumba::market_data::order_key" provided to the user when a new order was placed.
* @return true The cancel order message is sent successfully.
* @return false The cancel order message is unsuccessfull. This can happen if the "emumba::market_data::order_key"
* provided can not be mapped by the connector against an order.
*/
bool cancel_order(const emumba::order_entry::order_key &key);
/**
* @brief Cancel all open orders for the specified currency.
*
* @param currency Currency for which open orders need to be canceled. example btc, eth etc.
*/
void cancel_orders_by_currency(const std::string &currency);
/**
* @brief Linked to method "private/cancel_all_by_instrument" to cancel all orders against an instrument.
*
* @param instrument_name Instrument name.
* @return void
*/
void cancel_orders_by_instrument(const std::string &instrument_name);
/**
* @brief Get the all positions for the specified currency.
*
* @param currency Currency for which positions are required. example btc, eth etc.
*/
void get_all_positions(const std::string &currency);
/**
* @brief Get the all open orders for the specified currency.
*
* @param currency Currency for which open orders are required. example btc, eth etc.
*/
void get_all_open_orders(const std::string &currency);
/**
* @brief Get the user trades.
*
* @param usr_trades_request Object containing necessary information used to get the required user trades.
*/
void get_user_trades(const emumba::order_entry::user_trades_request &usr_trades_request);
/**
* @brief Linked to method "private/close_position" to place a request to cancell all positions.
*
* @param instrument_name Instrument name.
* @return void
*/
void send_cancel_all_positions(const std::string &instrument_name);
/**
* @brief Linked to method "private/get_position" to retrieve user position.
*
* @param instrument_name Instrument name.
* @return void
*/
void send_get_position(const std::string &instrument_name);
/**
* @brief Linked to method "private/get_open_orders_by_instrument" to retrieve list of user's open orders within given
* instrument.
*
* @param instrument_name Instrument name.
* @return void
*/
void send_get_open_orders(const std::string &instrument_name);
/**
* @brief Linked to method "private/set_mmp_config" to set config for Market Maker Protection.
*
* @param currency The currency symbol.
* @param interval MMP Interval in seconds, if set to 0 MMP is disabled.
* @param frozen_time MMP frozen time in seconds, if set to 0 manual reset is required.
* @param quantity_limit Quantity limit.
* @param delta_limit Delta limit.
* @return void
*/
void send_set_mmp_config(const std::string &currency, const int &interval, const int &frozen_time,
const double &quantity_limit, const double &delta_limit);
/**
* @brief Linked to method "private/get_mmp_config" to get config for Market Maker Protection.
*
* @param currency The currency symbol.
* @return void
*/
void send_get_mmp_config(const std::string &currency);
/**
* @brief Linked to method "private/reset_mmp" to reset Market Maker Protection.
*
* @param currency The currency symbol.
* @return void
*/
void send_reset_mmp(const std::string &currency);
/**
* @brief Place a new limit buy order.
*
* @param order Object containing necessary information required to place a new limit buy order.
* @return emumba::order_entry::order_key A unique key generated by the connector against each order. The user will
* provide this Key when an order needs to be updated or cancelled. Order Key will have a value of -1 in case the order
* is not sent successfully.
*/
emumba::order_entry::order_key launch_limit_buy_order(emumba::order_entry::new_limit_order &order);
/**
* @brief Place a new limit sell order.
*
* @param order Object containing necessary information required to place a new limit sell order.
* @return emumba::order_entry::order_key A unique key generated by the connector against each order. The user will
* provide this Key when an order needs to be updated or cancelled. Order Key will have a value of -1 in case the order
* is not sent successfully.
*/
emumba::order_entry::order_key launch_limit_sell_order(emumba::order_entry::new_limit_order &order);
/**
* @brief Place a new market buy order.
*
* @param order Object containing necessary information required to place a new market buy order.
* @return emumba::order_entry::order_key A unique key generated by the connector against each order. The user will
* provide this Key when an order needs to be updated or cancelled. Order Key will have a value of -1 in case the order
* is not sent successfully.
*/
emumba::order_entry::order_key launch_market_buy_order(emumba::order_entry::new_market_order &order);
/**
* @brief Place a new market sell order.
*
* @param order Object containing necessary information required to place a new market sell order.
* @return emumba::order_entry::order_key A unique key generated by the connector against each order. The user will
* provide this Key when an order needs to be updated or cancelled. Order Key will have a value of -1 in case the order
* is not sent successfully.
*/
emumba::order_entry::order_key launch_market_sell_order(emumba::order_entry::new_market_order &order);
/**
* @brief Get the session id. This session id is unique for each session created.
*
* @return const uint& session id
*/
const uint &get_session_id() const;
void send_rfq(const emumba::order_entry::rfq_request &rfq_request);
bool create_combo(const std::vector<emumba::order_entry::combo_trade const *> &combo_trades);
emumba::order_entry::order_key order_key;
#ifdef E2E_PERF
void e2e_perf_send_buy_order_market(emumba::order_entry::new_market_order &order, const double &change_id);
#endif
private:
std::shared_ptr<trader_session> _trader_session;
std::shared_ptr<orders_handler> _orders_handler;
};
} // namespace deribit_connector
} // namespace emumba
#endif
@@ -0,0 +1,52 @@
#include "utils.h"
std::string emumba::deribit_connector::uuid::generate_uuid_v4()
{
std::stringstream string_stream;
int i;
string_stream << std::hex;
for (i = 0; i < 8; i++)
{
string_stream << dis(gen);
}
string_stream << "_";
for (i = 0; i < 4; i++)
{
string_stream << dis(gen);
}
string_stream << "_4";
for (i = 0; i < 3; i++)
{
string_stream << dis(gen);
}
string_stream << "_";
string_stream << dis2(gen);
for (i = 0; i < 3; i++)
{
string_stream << dis(gen);
}
string_stream << "_";
for (i = 0; i < 12; i++)
{
string_stream << dis(gen);
};
return string_stream.str();
}
void emumba::deribit_connector::order_queues::clear()
{
while (!new_order_queue.empty())
{
new_order_queue.pop();
}
while (!edit_queue.empty())
{
edit_queue.pop();
}
while (!cancel_queue.empty())
{
cancel_queue.pop();
}
}
@@ -0,0 +1,77 @@
#ifndef UTILS_H
#define UTILS_H
#include "order_entry_common.h"
#include <cstdint>
#include <limits>
#include <queue>
#include <random>
#include <sstream>
#include <string>
#define NAN_PRICE std::numeric_limits<std::int64_t>::min()
#define NAN_DOUBLE std::numeric_limits<double>::quiet_NaN()
#define DOUBLE_LIMIT 0.00000001
namespace emumba {
namespace deribit_connector {
namespace uuid {
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_int_distribution<> dis(0, 15);
static std::uniform_int_distribution<> dis2(8, 11);
std::string generate_uuid_v4();
} // namespace uuid
/**
* @brief Determines the state of the application while it requests public/private subscriptions from the exchange.
*
*/
enum class subscription_state
{
subscribing,
subscribed
};
struct instrument_info
{
std::string instrument_name = "";
double tick_size = 0;
double taker_commission = 0;
double block_trade_commission = 0;
std::string settlement_period = "";
std::string quote_currency = "";
double min_trade_amount = 0;
double maker_commission = 0;
int leverage = 0;
std::string kind = "";
bool is_active = false;
uint64_t expiration_timestamp = 0;
uint64_t creation_timestamp = 0;
int contract_size = 0;
std::string base_currency = "";
double strike = 0;
std::string option_type = "";
};
struct legs_info
{
std::string instrument_name;
int quantity_ratio;
};
struct order_queues
{
std::queue<emumba::order_entry::order_key> new_order_queue;
std::queue<emumba::order_entry::order_key> edit_queue;
std::queue<emumba::order_entry::order_key> cancel_queue;
void clear();
};
} // namespace deribit_connector
} // namespace emumba
#endif /* UTILS_H */
@@ -0,0 +1,26 @@
#ifndef WRITER_H
#define WRITER_H
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
namespace emumba {
namespace deribit_connector {
class writer
{
public:
rapidjson::StringBuffer str_buff;
rapidjson::Writer<rapidjson::StringBuffer> message;
ALWAYS_INLINE __attribute__((always_inline)) void reset()
{
str_buff.Clear();
message.Reset(str_buff);
message.StartObject();
message.Key("jsonrpc");
message.String("2.0");
message.Key("method");
}
} writer;
} // namespace deribit_connector
} // namespace emumba
#endif