Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9deb8cc237 | ||
|
|
11ff46a056 |
No files matched your search
@@ -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})
|
||||
@@ -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 ¤cy_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 ¤cy_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 "e, 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 <p, const double <q);
|
||||
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 "e_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 ¤cy);
|
||||
|
||||
/**
|
||||
* @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 ¤cy);
|
||||
|
||||
/**
|
||||
* @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 ¤cy);
|
||||
|
||||
/**
|
||||
* @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 ¤cy, 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 ¤cy);
|
||||
|
||||
/**
|
||||
* @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 ¤cy);
|
||||
|
||||
/**
|
||||
* @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
|
||||
Reference in new issue
Block a user