Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
511aeaf648 | ||
|
|
a37c5b396a | ||
|
|
039e836acc | ||
|
|
7dee64ea70 | ||
|
|
c30e23406c | ||
|
|
836f22597d | ||
|
|
38c1ebcf01 |
No files matched your search
+9
-1
@@ -34,6 +34,7 @@ else()
|
||||
set(BUILD_ADMIN_WEB_APP ON CACHE BOOL "Build the Admin WebApp")
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_SOURCE_DIR}/cmake_modules)
|
||||
include_directories(BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
|
||||
link_directories(${SPARK_LIB})
|
||||
|
||||
@@ -116,11 +117,18 @@ else()
|
||||
|
||||
# subdirs
|
||||
if (BUILD_SPARKD)
|
||||
# deribit_emumba deps
|
||||
add_subdirectory(third_party/lib_flat_hash_map)
|
||||
add_subdirectory(third_party/lib_spdlog)
|
||||
|
||||
add_subdirectory(src/lib)
|
||||
add_subdirectory(src/lib_binance_execution)
|
||||
add_subdirectory(src/lib_deribit_emumba)
|
||||
add_subdirectory(src/server/sparkd)
|
||||
add_subdirectory(src/data_files)
|
||||
else()
|
||||
add_subdirectory(src/lib)
|
||||
endif()
|
||||
add_subdirectory(src/lib)
|
||||
if (BUILD_ADMIN_WEB_APP)
|
||||
add_subdirectory(src/Spark.Admin.WebApp)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
include_guard(GLOBAL)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/SetFirstNonEmpty.cmake)
|
||||
|
||||
function(git_version_tag)
|
||||
set(_options)
|
||||
set(_one_value_args RESULT DEFAULT)
|
||||
set(_multi_value_args)
|
||||
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
|
||||
set(_result "${_ARGS_RESULT}")
|
||||
if (_result STREQUAL "")
|
||||
foreach(_arg ${ARGN})
|
||||
set(_result ${_arg})
|
||||
break()
|
||||
endforeach()
|
||||
if (_result STREQUAL "")
|
||||
set(_result "${PROJECT_NAME}_VERSION")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Git QUIET)
|
||||
if(GIT_FOUND)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} describe --tags --always --dirty
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ${_result}
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
|
||||
if("${${_result}}" STREQUAL "")
|
||||
set_first_non_empty(${_result} "${_ARGS_DEFAULT}" "0.0.0")
|
||||
endif()
|
||||
set(${_result} "${${_result}}" PARENT_SCOPE)
|
||||
message("GIT VERSION: ${_result} = ${${_result}}")
|
||||
endfunction()
|
||||
@@ -0,0 +1,61 @@
|
||||
include_guard(DIRECTORY)
|
||||
include(GNUInstallDirs)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/PackBasic.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/SetFirstDefined.cmake)
|
||||
|
||||
function(install_basic)
|
||||
set(_options)
|
||||
set(_one_value_args NAME INCLUDE_DIR BUILD_INCLUDE_DIR VERSION)
|
||||
set(_multi_value_args)
|
||||
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
|
||||
set_first_defined(_name _ARGS_NAME PROJECT_NAME)
|
||||
set_first_defined(_include_dir _ARGS_INCLUDE_DIR _name)
|
||||
set_first_defined(_build_include_dir _ARGS_BUILD_INCLUDE_DIR _name)
|
||||
|
||||
set(_base_dirs)
|
||||
|
||||
# Look for headers in source dir and build dir
|
||||
set(_headers)
|
||||
if (_include_dir)
|
||||
list(APPEND _base_dirs "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
file(
|
||||
GLOB_RECURSE _headers
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${_include_dir}/*.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${_include_dir}/*.hpp
|
||||
)
|
||||
endif()
|
||||
set(_build_headers)
|
||||
if(_build_include_dir)
|
||||
list(APPEND _base_dirs "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
file(
|
||||
GLOB_RECURSE _build_headers
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${_build_include_dir}/*.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${_build_include_dir}/*.hpp
|
||||
)
|
||||
endif()
|
||||
target_sources(
|
||||
${_name}
|
||||
PUBLIC
|
||||
FILE_SET HEADERS
|
||||
BASE_DIRS ${_base_dirs}
|
||||
FILES
|
||||
${_headers}
|
||||
${_build_headers}
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS ${_name}
|
||||
EXPORT ${_name}Targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
install(
|
||||
EXPORT ${_name}Targets
|
||||
FILE ${_name}Targets.cmake
|
||||
NAMESPACE ${_name}::
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${_name})
|
||||
|
||||
pack_basic(NAME ${_name} VERSION "${_ARGS_VERSION}")
|
||||
endfunction()
|
||||
@@ -0,0 +1,34 @@
|
||||
include_guard(DIRECTORY)
|
||||
include(CMakePackageConfigHelpers)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/GitVersionTag.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/SetFirstDefined.cmake)
|
||||
|
||||
function(pack_basic)
|
||||
set(_options)
|
||||
set(_one_value_args NAME VERSION)
|
||||
set(_multi_value_args)
|
||||
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
|
||||
set_first_defined(_name _ARGS_NAME PROJECT_NAME)
|
||||
set(_version "${_ARGS_VERSION}")
|
||||
if (NOT _ARGS_VERSION)
|
||||
git_version_tag(_version)
|
||||
endif()
|
||||
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${_name}ConfigVersion.cmake"
|
||||
VERSION ${_version}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
configure_package_config_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${_name}Config.cmake"
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${_name}
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${_name}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${_name}ConfigVersion.cmake"
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${_name})
|
||||
endfunction()
|
||||
@@ -0,0 +1,10 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
macro(set_first_defined RESULT)
|
||||
foreach(_arg ${ARGN})
|
||||
if (DEFINED ${_arg})
|
||||
set(${RESULT} ${${_arg}})
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
endmacro()
|
||||
@@ -0,0 +1,10 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
macro(set_first_non_empty RESULT)
|
||||
foreach(_arg ${ARGN})
|
||||
if (NOT "${_arg}" STREQUAL "")
|
||||
set(${RESULT} ${_arg})
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
endmacro()
|
||||
@@ -0,0 +1,14 @@
|
||||
include(ExternalProject)
|
||||
|
||||
if(NOT TARGET flathashmap_project)
|
||||
ExternalProject_Add(flathashmap_project
|
||||
GIT_REPOSITORY https://github.com/skarupke/flat_hash_map.git
|
||||
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/flathashmap
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
endif()
|
||||
|
||||
ExternalProject_Get_Property(flathashmap_project SOURCE_DIR)
|
||||
set(flathashmap_INCLUDE_DIR ${SOURCE_DIR})
|
||||
@@ -0,0 +1,35 @@
|
||||
include(ExternalProject)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
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_INSTALL_PREFIX}
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DLOG_LEVEL=INFO
|
||||
-DENABLE_ENCRYPTION=True
|
||||
BUILD_BYPRODUCTS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/websocket-library_project-prefix/src/websocket-library_project-build/src/libemumba-websocket-library.a"
|
||||
)
|
||||
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,7 @@
|
||||
#pragma once
|
||||
|
||||
#define BTS_LOCK_GUARD(lockable) \
|
||||
std::lock_guard<decltype(lockable)> __lock_guard { lockable }
|
||||
|
||||
#define BTS_LOCK_GUARD_AS(lockable, name) \
|
||||
std::lock_guard<decltype(lockable)> __lock_guard_##name { lockable }
|
||||
@@ -281,8 +281,7 @@ struct GeneralMarketDataListener::PImpl {
|
||||
const bool use_given_sec_key = false;
|
||||
const std::string cc_name_prefix = "cc_security";
|
||||
size_t n_bk_updates = 0;
|
||||
bts::persistence::IDGenerator &id_gen =
|
||||
bts::persistence::IDGenerator::instance();
|
||||
bts::persistence::IDGenerator *id_gen;
|
||||
GeneralSecurityIndex &index;
|
||||
MDProducer &md_producer;
|
||||
const std::shared_ptr<bts::orderman::SimExecutionGateway> sim_ex_gwy =
|
||||
@@ -300,10 +299,10 @@ struct GeneralMarketDataListener::PImpl {
|
||||
api::domain::Venue venue,
|
||||
GeneralSecurityIndex &index,
|
||||
MDProducer &md_producer,
|
||||
std::shared_ptr<bts::orderman::SimExecutionGateway> sim_ex_gwy)
|
||||
: venue(venue), index(index),
|
||||
md_producer(md_producer),
|
||||
sim_ex_gwy(std::move(sim_ex_gwy)) {
|
||||
std::shared_ptr<bts::orderman::SimExecutionGateway> sim_ex_gwy,
|
||||
bts::persistence::IDGenerator *sec_id_store)
|
||||
: venue(venue), id_gen(sec_id_store), index(index),
|
||||
md_producer(md_producer), sim_ex_gwy(std::move(sim_ex_gwy)) {
|
||||
if (this->sim_ex_gwy != nullptr) {
|
||||
this->sim_ex_gwy->set_producers(&this->index);
|
||||
}
|
||||
@@ -365,13 +364,17 @@ void copy_books(
|
||||
// interface functions ////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
GeneralMarketDataListener::GeneralMarketDataListener(
|
||||
GeneralSecurityIndex &index, MDProducer &md_producer, api::Venue venue)
|
||||
GeneralSecurityIndex &index,
|
||||
MDProducer &md_producer,
|
||||
api::Venue venue,
|
||||
persistence::IDGenerator *sec_id_store)
|
||||
: me(std::make_unique<PImpl>(
|
||||
domain::DomainSecurityUtils::convert(venue),
|
||||
index,
|
||||
md_producer,
|
||||
bts::orderman::SimExecutionGatewayFactory::get()->get_getayeway(
|
||||
venue))) {
|
||||
venue),
|
||||
sec_id_store)) {
|
||||
LOG(md, Info,
|
||||
"GeneralMarketDataListener::GeneralMarketDataListener venue: {}",
|
||||
bts::api::Venue_Name(venue));
|
||||
@@ -508,7 +511,7 @@ CCSecSearchResult GeneralMarketDataListener::_find_sec(
|
||||
if (add_if_absent) {
|
||||
uint64_t bts_id = cc_id.value();
|
||||
if (!me->use_given_sec_key) {
|
||||
bts_id = me->id_gen.get_or_add(me->venue, sec.type(), sec.desc());
|
||||
bts_id = me->id_gen->get_or_add(me->venue, sec.type(), sec.desc());
|
||||
}
|
||||
me->cc_sec_id_map.insert({cc_id, {bts_id}});
|
||||
LOG(md, Info,
|
||||
@@ -655,7 +658,7 @@ uint64_t GeneralMarketDataListener::_generate_security_key(
|
||||
auto logical_key = get_logical_key(sec);
|
||||
// if (me->use_given_sec_key)
|
||||
// return sec.security_common.id.value();
|
||||
return me->id_gen.get_or_add(me->venue, sec.type(), sec.desc());
|
||||
return me->id_gen->get_or_add(me->venue, sec.type(), sec.desc());
|
||||
}
|
||||
|
||||
void GeneralMarketDataListener::_possibly_match_with_sim_orders(
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "lib/md/GeneralSecurityIndex.h"
|
||||
#include "lib/md/IMarketDataListener.h"
|
||||
#include "lib/md/Shm/FixedSecurity.h"
|
||||
#include "lib/persistence/IDGenerator.h"
|
||||
|
||||
namespace bts::md {
|
||||
class MDProducer;
|
||||
@@ -16,7 +17,10 @@ namespace bts::md {
|
||||
class GeneralMarketDataListener : public IMarketDataListener {
|
||||
public:
|
||||
GeneralMarketDataListener(
|
||||
GeneralSecurityIndex &index, MDProducer &md_producer, api::Venue venue);
|
||||
GeneralSecurityIndex &index,
|
||||
MDProducer &md_producer,
|
||||
api::Venue venue,
|
||||
persistence::IDGenerator *sec_id_store);
|
||||
virtual ~GeneralMarketDataListener();
|
||||
void on_securities(const std::vector<domain::Security const *>
|
||||
&complete_security_list) override;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/bts_chrono.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
#include "server/sparkd/md/platforms/emumba/IEMOrderMuxer.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <boost/pool/object_pool.hpp>
|
||||
@@ -55,7 +54,6 @@ struct FakeExecutionGateway::PImpl {
|
||||
std::vector<OrderMsg> buffered_msgs = {};
|
||||
unsigned long exchange_oid = 0;
|
||||
std::vector<ExecutionReport> exe_reports = {};
|
||||
IEMOrderMuxer *_order_mux = nullptr;
|
||||
};
|
||||
|
||||
FakeExecutionGateway::FakeExecutionGateway(md::SecurityOracle o)
|
||||
@@ -196,4 +194,4 @@ OnExchangeOpenOrdersState &FakeExecutionGateway::on_exchange_open_orders() {
|
||||
}
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
} // namespace bts
|
||||
@@ -31,7 +31,6 @@
|
||||
#include "lib/utils/bts_chrono.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
#include "lib/utils/thread_manager.h"
|
||||
#include "server/sparkd/md/platforms/emumba/IEMOrderMuxer.h"
|
||||
|
||||
#include <bits/stdint-uintn.h>
|
||||
#include <boost/pool/object_pool.hpp>
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/bts_chrono.h"
|
||||
#include "server/sparkd/md/platforms/emumba/EmMDListener.h"
|
||||
#include <algorithm>
|
||||
#include <bits/stdint-uintn.h>
|
||||
#include <optional>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
cmake_minimum_required(VERSION 3.23)
|
||||
project(deribit_emumba)
|
||||
include(FetchContent)
|
||||
|
||||
file(
|
||||
GLOB_RECURSE ${PROJECT_NAME}_SOURCES
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
*.cpp)
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC ${${PROJECT_NAME}_SOURCES})
|
||||
|
||||
# Websocket
|
||||
find_package(websocket-library REQUIRED)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${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} PUBLIC websocket-library)
|
||||
|
||||
target_include_directories(
|
||||
${PROJECT_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
PUBLIC
|
||||
libspark
|
||||
flat_hash_map
|
||||
spdlog
|
||||
)
|
||||
@@ -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,162 @@
|
||||
#include "deribit_handler.h"
|
||||
|
||||
#include "deribit_exception.h"
|
||||
#include "orders_handler.h"
|
||||
#include "session_manager.h"
|
||||
#include "spdlogger.h"
|
||||
#include "symbol_handler.h"
|
||||
#include "trader_session.h"
|
||||
|
||||
using namespace emumba::deribit_connector;
|
||||
|
||||
#ifdef LICENSE_CHECK
|
||||
#include "license_validator.h"
|
||||
license_validator license_obj;
|
||||
#endif
|
||||
|
||||
std::atomic<int> print_books = 0;
|
||||
|
||||
deribit_handler::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)
|
||||
: _orders_handler(std::make_shared<orders_handler>(trading_config.orders_tracker, trad_listener))
|
||||
{
|
||||
// (void)log_config;
|
||||
emumba::logger::init_spdlog(log_config.severity, log_config.is_console_enabled, log_config.is_file_enabled);
|
||||
|
||||
#ifdef LICENSE_CHECK
|
||||
license_obj.check_license();
|
||||
#endif
|
||||
|
||||
#ifdef CONNECTOR_VERSION
|
||||
LOG_INFO("Deribit connector version: {}", CONNECTOR_VERSION);
|
||||
#else
|
||||
LOG_WARNING("Deribit connector version not defined");
|
||||
#endif
|
||||
|
||||
_trad_listener = trad_listener;
|
||||
|
||||
_sess_manager = std::make_shared<session_manager>(deribit_uri, trading_config);
|
||||
|
||||
for (auto _symbol : symbol_config_list)
|
||||
{
|
||||
auto cred_config = cred_config_list.find(_symbol.cred_index);
|
||||
if (cred_config != cred_config_list.end())
|
||||
{
|
||||
auto symbol_handl = std::make_shared<symbol_handler>(
|
||||
_symbol, _sess_manager, _orders_handler, trading_config.heartbeat_interval, _trad_listener, cred_config->second);
|
||||
_symbol_session_list.push_back(symbol_handl);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Invalid Credential index");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trading_session_interface deribit_handler::create_new_trading_session(
|
||||
const emumba::deribit_connector::configuration::credential_config &credential_config)
|
||||
{
|
||||
std::vector<std::string> configured_symbols;
|
||||
for (const auto &each_symbol : _symbol_session_list)
|
||||
{
|
||||
configured_symbols.push_back(each_symbol->get_symbol_name());
|
||||
}
|
||||
|
||||
auto _trad = std::make_shared<trader_session>(_sess_manager, _orders_handler, credential_config, configured_symbols,
|
||||
_trad_listener);
|
||||
_trading_session_list.push_back(_trad);
|
||||
trading_session_interface tr_api(_trad, _orders_handler);
|
||||
return tr_api;
|
||||
}
|
||||
|
||||
void deribit_handler::run(const bool &detach)
|
||||
{
|
||||
_connector_thread = std::thread(std::bind(&emumba::deribit_connector::session_manager::run, _sess_manager.get()));
|
||||
pthread_setname_np(_connector_thread.native_handle(), "deribit_connector");
|
||||
cpu_set_t cpuset;
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(_sess_manager->get_core_id(), &cpuset);
|
||||
int rc = pthread_setaffinity_np(_connector_thread.native_handle(), sizeof(cpu_set_t), &cpuset);
|
||||
if (rc != 0)
|
||||
{
|
||||
std::cerr << "Error calling pthread_setaffinity_np: " << rc << "\n";
|
||||
throw deribit_exception(deribit_exception::error_code::thread_binding);
|
||||
}
|
||||
if (detach == false)
|
||||
{
|
||||
_connector_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::dump_books()
|
||||
{
|
||||
print_books.store(1);
|
||||
}
|
||||
|
||||
void deribit_handler::reconfigure_instrument_list(const std::string & symbol_name,
|
||||
const std::list<std::string> instrument_list, const bool &append)
|
||||
{
|
||||
for (auto &symbol : _symbol_session_list)
|
||||
{
|
||||
if (symbol->get_symbol_name() == symbol_name)
|
||||
{
|
||||
symbol->reconfigure_instrument_list(instrument_list, append);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &deribit_handler::get_uuid() const
|
||||
{
|
||||
return _orders_handler->get_uuid();
|
||||
}
|
||||
|
||||
const uint deribit_handler::get_session_id(const std::string &symbol_name) const
|
||||
{
|
||||
uint session_id = 0;
|
||||
|
||||
for (auto &symbol : _symbol_session_list)
|
||||
{
|
||||
if (symbol->get_symbol_name() == symbol_name)
|
||||
{
|
||||
session_id = symbol->get_session_id();
|
||||
}
|
||||
}
|
||||
|
||||
if (session_id == 0)
|
||||
{
|
||||
LOG_ERROR("get_session_id | symbol_name | Incorrect value.");
|
||||
}
|
||||
|
||||
return session_id;
|
||||
}
|
||||
|
||||
std::list<std::string> deribit_handler::get_future_expiries(const std::string ¤cy_symbol, const int &expiries,
|
||||
const std::string &expiry_cycle)
|
||||
{
|
||||
std::list<std::string> future_list;
|
||||
for (auto &symbol : _symbol_session_list)
|
||||
{
|
||||
if (symbol->get_symbol_name() == currency_symbol)
|
||||
{
|
||||
future_list = symbol->get_future_expiries(currency_symbol, expiries, expiry_cycle);
|
||||
}
|
||||
}
|
||||
return future_list;
|
||||
}
|
||||
|
||||
std::list<std::string> deribit_handler::get_option_expiries(const std::string ¤cy_symbol, const int &expiries,
|
||||
const std::string &expiry_cycle)
|
||||
{
|
||||
std::list<std::string> options_list;
|
||||
for (auto &symbol : _symbol_session_list)
|
||||
{
|
||||
if (symbol->get_symbol_name() == currency_symbol)
|
||||
{
|
||||
options_list = symbol->get_options_expiries(currency_symbol, expiries, expiry_cycle);
|
||||
}
|
||||
}
|
||||
return options_list;
|
||||
}
|
||||
@@ -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,146 @@
|
||||
#include "deribit_listener.h"
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_order_update(__attribute__((unused))
|
||||
const emumba::order_entry::order_key &order_key,
|
||||
__attribute__((unused))
|
||||
const emumba::order_entry::order_state &order_state)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_get_all_positions(
|
||||
__attribute__((unused)) const emumba::deribit_connector::data_types::error & ec,
|
||||
__attribute__((unused)) const std::vector<emumba::order_entry::position const *> &positions)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_get_all_open_orders(
|
||||
__attribute__((unused)) const emumba::deribit_connector::data_types::error & ec,
|
||||
__attribute__((unused)) const std::vector<emumba::order_entry::open_order const *> &open_orders)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_get_user_trades(
|
||||
__attribute__((unused)) const emumba::order_entry::user_trades_response &usr_trades_response)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_mass_cancellation(
|
||||
__attribute__((unused)) const emumba::deribit_connector::data_types::cancellation_update &order_cancellation)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_account_summary(__attribute__((unused))
|
||||
const emumba::deribit_connector::data_types::error &ec,
|
||||
__attribute__((unused)) const std::string &msg_result)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_cancel_all_positions(
|
||||
__attribute__((unused)) const emumba::deribit_connector::data_types::error &ec,
|
||||
__attribute__((unused)) const std::string & msg_result)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_get_position(__attribute__((unused))
|
||||
const emumba::deribit_connector::data_types::error &ec,
|
||||
__attribute__((unused)) const std::string &msg_result)
|
||||
{}
|
||||
void emumba::deribit_connector::deribit_listener::on_get_open_orders(__attribute__((unused))
|
||||
const emumba::deribit_connector::data_types::error &ec,
|
||||
__attribute__((unused)) const std::string &msg_result)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_get_mmp_config(__attribute__((unused))
|
||||
const emumba::deribit_connector::data_types::error &ec,
|
||||
__attribute__((unused)) const std::string &msg_result)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_set_mmp_config(__attribute__((unused))
|
||||
const emumba::deribit_connector::data_types::error &ec,
|
||||
__attribute__((unused)) const std::string &msg_result)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_reset_mmp(__attribute__((unused))
|
||||
const emumba::deribit_connector::data_types::error &ec,
|
||||
__attribute__((unused)) const std::string &msg_result)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_book_state_change(__attribute__((unused))
|
||||
const std::string &instrument_name,
|
||||
__attribute__((unused))
|
||||
const data_types::book_status & status_of_book,
|
||||
__attribute__((unused)) const void *ptr)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_quote(__attribute__((unused)) const emumba::market_data::quote "e,
|
||||
__attribute__((unused)) const void * ptr)
|
||||
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_security_statistics(
|
||||
__attribute__((unused)) const emumba::market_data::market_data_statistics &market_data_stats,
|
||||
__attribute__((unused)) const void * ptr)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_subscription_public_markprice(__attribute__((unused))
|
||||
const std::string &symbol_name,
|
||||
__attribute__((unused))
|
||||
const std::string &markprice_message)
|
||||
{}
|
||||
void emumba::deribit_connector::deribit_listener::on_subscription_public_index(__attribute__((unused))
|
||||
const std::string &symbol_name,
|
||||
__attribute__((unused))
|
||||
const std::string &index_message)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_subscription_private_user_portfolio(
|
||||
__attribute__((unused)) const std::string &symbol_name, __attribute__((unused)) const std::string &user_portfolio_message)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_response_get_instruments(
|
||||
__attribute__((unused)) const emumba::deribit_connector::data_types::error &ec,
|
||||
__attribute__((unused)) const std::string & symbol_name,
|
||||
__attribute__((unused)) const std::string & get_instruments_message)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_securities(
|
||||
__attribute__((unused)) const std::vector<emumba::market_data::security const *> &complete_security_list)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_book(__attribute__((unused))
|
||||
const emumba::market_data::order_book &book,
|
||||
__attribute__((unused)) const void * ptr)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_security_state(__attribute__((unused))
|
||||
const emumba::market_data::security_state &sec_state,
|
||||
__attribute__((unused)) const void * ptr)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_trades(
|
||||
__attribute__((unused)) const std::vector<emumba::market_data::trade_record const *> &trades,
|
||||
__attribute__((unused)) const void * ptr)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_private_trade(__attribute__((unused))
|
||||
const emumba::order_entry::order_key &order_key,
|
||||
__attribute__((unused))
|
||||
const emumba::market_data::trade_record &trade)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_error(__attribute__((unused))
|
||||
const emumba::market_data::market_data_error &error)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_disconnection(
|
||||
__attribute__((unused)) const emumba::market_data::disconnected_session &disconnected_sess)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_request_for_quote(
|
||||
__attribute__((unused)) const emumba::order_entry::rfq_response &rfq_response)
|
||||
{}
|
||||
|
||||
void emumba::deribit_connector::deribit_listener::on_create_combo(
|
||||
__attribute__((unused)) const emumba::order_entry::combo_detail &combo_detail)
|
||||
{}
|
||||
|
||||
|
||||
#ifdef E2E_PERF
|
||||
void emumba::deribit_connector::deribit_listener::e2e_perf_on_book(__attribute__((unused))
|
||||
emumba::market_data::order_book & book,
|
||||
__attribute__((unused)) const double &change_id)
|
||||
{}
|
||||
#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,35 @@
|
||||
#include "deribit_utils.h"
|
||||
|
||||
emumba::deribit_connector::configuration::logging_config::logging_config()
|
||||
{
|
||||
severity = "info";
|
||||
is_console_enabled = true;
|
||||
is_file_enabled = true;
|
||||
queue_size = 8192;
|
||||
}
|
||||
|
||||
emumba::deribit_connector::configuration::trading_session_config::trading_session_config()
|
||||
{
|
||||
heartbeat_interval = 10;
|
||||
cancel_on_disconnect = true;
|
||||
}
|
||||
|
||||
emumba::deribit_connector::configuration::reconnection_config::reconnection_config()
|
||||
{
|
||||
retry_count = 3;
|
||||
retry_interval = 1;
|
||||
reception_timeout = 60;
|
||||
}
|
||||
emumba::deribit_connector::configuration::credential_config::credential_config()
|
||||
{
|
||||
client_name = "";
|
||||
client_id = "";
|
||||
secret_key = "";
|
||||
index = 0;
|
||||
}
|
||||
|
||||
emumba::deribit_connector::configuration::order_tracker::order_tracker()
|
||||
{
|
||||
uuid = "";
|
||||
start_id = 0;
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
/**
|
||||
* @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 <cstdint>
|
||||
#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.
|
||||
*
|
||||
*/
|
||||
std::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,24 @@
|
||||
#ifndef EMUMBA_ENUM_H
|
||||
#define EMUMBA_ENUM_H
|
||||
|
||||
#define _EMUMBA_ENUM_DECLARE_VALUE(t, n, x) x = n,
|
||||
#define _EMUMBA_ENUM_CONCAT(x, y) x##y
|
||||
#define _EMUMBA_ENUM_NAME_CASE(type, n, value) \
|
||||
case type::value: \
|
||||
return #value;
|
||||
|
||||
// clang-format off
|
||||
#define EMUMBA_ENUM(type, for_all) \
|
||||
enum class type { for_all(_EMUMBA_ENUM_DECLARE_VALUE) }; \
|
||||
inline const char *_EMUMBA_ENUM_CONCAT(type, _name)(type value) { \
|
||||
switch (value) { \
|
||||
for_all(_EMUMBA_ENUM_NAME_CASE) default : return "ENUM_UNKNOWN"; \
|
||||
} \
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
// #undef _NAME_CASE
|
||||
// #undef _IDENTITY
|
||||
// #undef _DECLARE_VALUE
|
||||
// #undef _CONCAT
|
||||
#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,289 @@
|
||||
#include "instrument_data.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
using namespace emumba::deribit_connector;
|
||||
using namespace std;
|
||||
|
||||
instrument_data::instrument_data(
|
||||
const std::string &instrument_name,
|
||||
const uint &book_depth,
|
||||
instrument_info inst_info,
|
||||
deribit_listener * /*trade_listener*/)
|
||||
: _instrument_name(instrument_name), _previous_change_id(-1),
|
||||
_lowest_possible_bid(std::numeric_limits<double>::lowest()),
|
||||
_highest_possible_ask(std::numeric_limits<double>::max()),
|
||||
_bid_price_at_depth(_lowest_possible_bid),
|
||||
_ask_price_at_depth(_highest_possible_ask), _bid_map_size(0),
|
||||
_ask_map_size(0), _book_depth(book_depth), _instrument_info(inst_info),
|
||||
_is_inst_closed(false) {
|
||||
_book_status = data_types::book_status::red;
|
||||
_last_traded_price = 0;
|
||||
_last_traded_quantity = 0;
|
||||
_total_traded_quantity = 0;
|
||||
_total_traded_volume = 0;
|
||||
LOG_TRACE("Constructed instrument_data object");
|
||||
|
||||
_book.bids.level.reserve(_book_depth);
|
||||
_book.asks.level.reserve(_book_depth);
|
||||
|
||||
_book.sec_id.value() = _instrument_name;
|
||||
_current_sec_state = emumba::market_data::security_state{};
|
||||
}
|
||||
|
||||
bool instrument_data::check_message_loss(const double &previous_change_id)
|
||||
{
|
||||
LOG_TRACE("Checking message loss. Message change id: {}, Expected changed id: {}", previous_change_id,
|
||||
_previous_change_id);
|
||||
return previous_change_id == _previous_change_id;
|
||||
}
|
||||
|
||||
void instrument_data::reset_instrument()
|
||||
{
|
||||
change_book_status(data_types::book_status::red);
|
||||
_bid.clear();
|
||||
_ask.clear();
|
||||
_bid_map_size = 0;
|
||||
_ask_map_size = 0;
|
||||
_last_traded_price = 0;
|
||||
_last_traded_quantity = 0;
|
||||
_bid_price_at_depth = _lowest_possible_bid;
|
||||
_ask_price_at_depth = _highest_possible_ask;
|
||||
LOG_DEBUG("Reset instrument: {}.", _instrument_name);
|
||||
}
|
||||
|
||||
void *instrument_data::get_api_handler()
|
||||
{
|
||||
return _api_handler;
|
||||
}
|
||||
|
||||
// Print levels of both books till assigned book depth
|
||||
std::ostream &instrument_data::print_order_book(std::ostream &out, int level)
|
||||
{
|
||||
if (level == 0)
|
||||
{
|
||||
level = _book_depth;
|
||||
}
|
||||
int index = 0;
|
||||
out << std::fixed << "Change ID: " << std::setprecision(0) << _previous_change_id << std::endl;
|
||||
out << "Status: " << (_book_status == data_types::book_status::green ? "GREEN" : "RED") << std::endl;
|
||||
out << "\nBid Book:" << std::endl;
|
||||
out << "#,Price,Size" << std::endl;
|
||||
|
||||
auto _bid_itr = _bid.begin();
|
||||
for (; index < level && _bid_itr != _bid.end();)
|
||||
{
|
||||
out << ++index << "," << std::setprecision(9) << _bid_itr->second.get_price().to_double();
|
||||
out << "," << std::setprecision(9) << _bid_itr->second.get_amount() << std::endl;
|
||||
_bid_itr++;
|
||||
}
|
||||
|
||||
while (index < level)
|
||||
{
|
||||
out << ++index << "," << std::setprecision(1) << 0.0;
|
||||
out << "," << std::setprecision(1) << 0.0 << std::endl;
|
||||
}
|
||||
|
||||
out << std::endl;
|
||||
out << "Ask Book:" << std::endl;
|
||||
out << "#,Price,Size" << std::endl;
|
||||
index = 0;
|
||||
auto _ask_itr = _ask.begin();
|
||||
for (; index < level && _ask_itr != _ask.end();)
|
||||
{
|
||||
out << ++index << "," << std::setprecision(9) << _ask_itr->second.get_price().to_double();
|
||||
out << "," << std::setprecision(9) << _ask_itr->second.get_amount() << std::endl;
|
||||
_ask_itr++;
|
||||
}
|
||||
|
||||
while (index < level)
|
||||
{
|
||||
out << ++index << "," << std::setprecision(1) << 0.0;
|
||||
out << "," << std::setprecision(1) << 0.0 << std::endl;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Print all levels of both books
|
||||
std::ostream &instrument_data::print_order_book_all_levels(std::ostream &out)
|
||||
{
|
||||
int index;
|
||||
out << std::fixed << "Change ID: " << std::setprecision(0) << _previous_change_id << std::endl;
|
||||
out << "Status: " << (_book_status == data_types::book_status::green ? "GREEN" : "RED") << std::endl;
|
||||
out << "\nBid Book:" << std::endl;
|
||||
out << "#,Price,Size" << std::endl;
|
||||
|
||||
index = 0;
|
||||
for (auto &bid_price_level : _bid)
|
||||
{
|
||||
out << ++index << "," << std::setprecision(9) << bid_price_level.second.get_price().to_double();
|
||||
out << "," << std::setprecision(9) << bid_price_level.second.get_amount() << std::endl;
|
||||
}
|
||||
|
||||
out << std::endl;
|
||||
out << "Ask Book:" << std::endl;
|
||||
out << "#,Price,Size" << std::endl;
|
||||
index = 0;
|
||||
for (auto &ask_price_level : _ask)
|
||||
{
|
||||
out << ++index << "," << std::setprecision(9) << ask_price_level.second.get_price().to_double();
|
||||
out << "," << std::setprecision(9) << ask_price_level.second.get_amount() << std::endl;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const double instrument_data::get_previous_change_id()
|
||||
{
|
||||
return _previous_change_id;
|
||||
}
|
||||
|
||||
const uint64_t instrument_data::get_exchange_ts()
|
||||
{
|
||||
return _exchange_update_ts;
|
||||
}
|
||||
|
||||
const std::string instrument_data::get_name()
|
||||
{
|
||||
return _instrument_name;
|
||||
}
|
||||
|
||||
void instrument_data::update_trade_info(const double <p, const double <q)
|
||||
{
|
||||
_last_traded_price = ltp;
|
||||
_last_traded_quantity = ltq;
|
||||
_total_traded_quantity = _total_traded_quantity + ltq;
|
||||
_total_traded_volume = _total_traded_volume + (_last_traded_price * _last_traded_quantity);
|
||||
}
|
||||
|
||||
const double instrument_data::get_last_traded_price()
|
||||
{
|
||||
return _last_traded_price;
|
||||
}
|
||||
|
||||
const double instrument_data::get_last_traded_quantity()
|
||||
{
|
||||
return _last_traded_quantity;
|
||||
}
|
||||
|
||||
const double instrument_data::get_total_traded_quantity()
|
||||
{
|
||||
return _total_traded_quantity;
|
||||
}
|
||||
|
||||
const long double instrument_data::get_total_traded_value()
|
||||
{
|
||||
return _total_traded_volume;
|
||||
}
|
||||
|
||||
data_types::price_level::iterator instrument_data::get_top(int side)
|
||||
{
|
||||
if (side == 0)
|
||||
{
|
||||
return data_types::price_level::iterator(&_bid, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return data_types::price_level::iterator(&_ask, 1);
|
||||
}
|
||||
}
|
||||
|
||||
data_types::price_level::iterator instrument_data::get_level(int side, unsigned lvl)
|
||||
{
|
||||
if (side == 0)
|
||||
{
|
||||
data_types::price_level::iterator itr(&_bid, 0);
|
||||
for (uint i = 0; i < lvl; i++)
|
||||
{
|
||||
itr++;
|
||||
}
|
||||
return itr;
|
||||
}
|
||||
else
|
||||
{
|
||||
data_types::price_level::iterator itr(&_ask, 1);
|
||||
for (uint i = 0; i < lvl; i++)
|
||||
{
|
||||
itr++;
|
||||
}
|
||||
return itr;
|
||||
}
|
||||
}
|
||||
|
||||
bool instrument_data::is_iterator_valid(data_types::price_level::iterator it)
|
||||
{
|
||||
if (it.side() == 0)
|
||||
{
|
||||
data_types::price_level::iterator bid_itr(&_bid, 0);
|
||||
for (int i = 0; i < get_depth(0); i++)
|
||||
{
|
||||
if (bid_itr == it)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bid_itr++;
|
||||
}
|
||||
}
|
||||
else if (it.side() == 1)
|
||||
{
|
||||
data_types::price_level::iterator ask_itr(&_ask, 1);
|
||||
for (int i = 0; i < get_depth(1); i++)
|
||||
{
|
||||
if (ask_itr == it)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ask_itr++;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int instrument_data::get_depth(int side)
|
||||
{
|
||||
if (side == 0)
|
||||
{
|
||||
return _bid.size();
|
||||
}
|
||||
else if (side == 1)
|
||||
{
|
||||
return _ask.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void instrument_data::set_instrument_data_interface(
|
||||
std::shared_ptr<emumba::deribit_connector::instrument_data_interface> interface)
|
||||
{
|
||||
_interface = interface;
|
||||
}
|
||||
|
||||
void instrument_data::set_api_handler(void *ref_ptr)
|
||||
{
|
||||
_api_handler = ref_ptr;
|
||||
}
|
||||
|
||||
emumba::market_data::security_state::state instrument_data::get_current_state()
|
||||
{
|
||||
return _current_sec_state.state_;
|
||||
}
|
||||
|
||||
void instrument_data::set_current_state(emumba::market_data::security_state::state new_state)
|
||||
{
|
||||
_current_sec_state.state_ = new_state;
|
||||
}
|
||||
|
||||
void instrument_data::set_inst_closed_flag(const bool &instrument_closed)
|
||||
{
|
||||
_is_inst_closed = instrument_closed;
|
||||
}
|
||||
|
||||
const bool &instrument_data::get_inst_closed_flag() const
|
||||
{
|
||||
return _is_inst_closed;
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
#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: {}",
|
||||
std::string{
|
||||
_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,90 @@
|
||||
#include "instrument_data_interface.h"
|
||||
|
||||
#include "deribit_utils.h"
|
||||
#include "instrument_data.h"
|
||||
#include "spdlogger.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace emumba::deribit_connector;
|
||||
|
||||
emumba::deribit_connector::instrument_data_interface::instrument_data_interface(
|
||||
emumba::deribit_connector::instrument_data *instrument)
|
||||
{
|
||||
_instrument = instrument;
|
||||
LOG_TRACE("Created interface object for instrument: {}", get_instrument_name());
|
||||
}
|
||||
|
||||
const uint64_t emumba::deribit_connector::instrument_data_interface::get_exchange_ts()
|
||||
{
|
||||
return _instrument->get_exchange_ts();
|
||||
}
|
||||
|
||||
const uint64_t emumba::deribit_connector::instrument_data_interface::get_last_change_id()
|
||||
{
|
||||
return _instrument->get_previous_change_id();
|
||||
}
|
||||
|
||||
const double emumba::deribit_connector::instrument_data_interface::get_last_traded_price()
|
||||
{
|
||||
return _instrument->get_last_traded_price();
|
||||
}
|
||||
|
||||
const long double emumba::deribit_connector::instrument_data_interface::get_total_traded_value()
|
||||
{
|
||||
return _instrument->get_total_traded_value();
|
||||
}
|
||||
|
||||
const double emumba::deribit_connector::instrument_data_interface::get_total_traded_quantity()
|
||||
{
|
||||
return _instrument->get_total_traded_quantity();
|
||||
}
|
||||
|
||||
const std::string emumba::deribit_connector::instrument_data_interface::get_instrument_name()
|
||||
{
|
||||
return _instrument->get_name();
|
||||
}
|
||||
|
||||
const std::string emumba::deribit_connector::instrument_data_interface::get_exchange()
|
||||
{
|
||||
return "Deribit";
|
||||
}
|
||||
|
||||
const data_types::book_status emumba::deribit_connector::instrument_data_interface::get_trading_state()
|
||||
{
|
||||
return _instrument->get_book_status();
|
||||
}
|
||||
|
||||
data_types::price_level::iterator emumba::deribit_connector::instrument_data_interface::top(int side)
|
||||
{
|
||||
return _instrument->get_top(side);
|
||||
}
|
||||
|
||||
data_types::price_level::iterator emumba::deribit_connector::instrument_data_interface::level(int side, unsigned lvl)
|
||||
{
|
||||
return _instrument->get_level(side, lvl);
|
||||
}
|
||||
|
||||
bool emumba::deribit_connector::instrument_data_interface::valid(data_types::price_level::iterator it)
|
||||
{
|
||||
return _instrument->is_iterator_valid(it);
|
||||
}
|
||||
|
||||
uint16_t emumba::deribit_connector::instrument_data_interface::depth(int side)
|
||||
{
|
||||
return _instrument->get_depth(side);
|
||||
}
|
||||
|
||||
const std::string emumba::deribit_connector::instrument_data_interface::get_book_to_string()
|
||||
{
|
||||
std::ostringstream ss;
|
||||
_instrument->print_order_book_all_levels(ss);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
const std::string emumba::deribit_connector::instrument_data_interface::get_book_to_string(int lvl)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
_instrument->print_order_book(ss, lvl);
|
||||
return ss.str();
|
||||
}
|
||||
@@ -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,63 @@
|
||||
#ifdef LICENSE_CHECK
|
||||
|
||||
#include "license_validator.h"
|
||||
|
||||
#include "spdlogger.h"
|
||||
|
||||
#include <licensecc/licensecc.h>
|
||||
#include <unordered_map>
|
||||
|
||||
emumba::deribit_connector::license_validator::license_validator()
|
||||
{
|
||||
is_valid = false;
|
||||
}
|
||||
|
||||
bool emumba::deribit_connector::license_validator::check_license()
|
||||
{
|
||||
std::unordered_map<LCC_EVENT_TYPE, std::string> stringByEventType = {
|
||||
{LICENSE_OK, "OK "},
|
||||
{LICENSE_FILE_NOT_FOUND, "License file not found "},
|
||||
{LICENSE_SERVER_NOT_FOUND, "License server can't be contacted "},
|
||||
{ENVIRONMENT_VARIABLE_NOT_DEFINED, "Environment variable not defined "},
|
||||
{FILE_FORMAT_NOT_RECOGNIZED, "License file has invalid format (not .ini file) "},
|
||||
{LICENSE_MALFORMED, "Some mandatory field are missing, or data can't be fully read. "},
|
||||
{PRODUCT_NOT_LICENSED, "This product was not licensed "},
|
||||
{PRODUCT_EXPIRED, "License expired "},
|
||||
{LICENSE_CORRUPTED, "License signature didn't match with current license "},
|
||||
{IDENTIFIERS_MISMATCH, "Calculated identifier and the one provided in license didn't match"}};
|
||||
|
||||
LicenseInfo licenseInfo;
|
||||
size_t pc_id_sz = LCC_API_PC_IDENTIFIER_SIZE + 1;
|
||||
char pc_identifier[LCC_API_PC_IDENTIFIER_SIZE + 1];
|
||||
|
||||
LCC_EVENT_TYPE result = acquire_license(nullptr, nullptr, &licenseInfo);
|
||||
|
||||
if (result == LICENSE_OK)
|
||||
{
|
||||
LOG_INFO("License authentication successful.");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("License authentication failed. Error: {}", stringByEventType[result].c_str());
|
||||
if (!(identify_pc(STRATEGY_DEFAULT, pc_identifier, &pc_id_sz, nullptr)))
|
||||
{
|
||||
LOG_TRACE("Error in identifying PC signature.");
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::license_validator::set_license_valid_status()
|
||||
{
|
||||
is_valid = true;
|
||||
}
|
||||
|
||||
bool emumba::deribit_connector::license_validator::is_license_file_valid()
|
||||
{
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
#endif // LICENSE_CHECK
|
||||
@@ -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,17 @@
|
||||
#include "order_entry_common.h"
|
||||
|
||||
emumba::order_entry::order_state::order_state()
|
||||
{
|
||||
trades.reserve(128);
|
||||
}
|
||||
|
||||
emumba::order_entry::rfq_request::rfq_request()
|
||||
{
|
||||
qty.value() = 0.0;
|
||||
}
|
||||
|
||||
emumba::order_entry::combo_trade::combo_trade()
|
||||
{
|
||||
sec_id.value() = "";
|
||||
qty = 0;
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
/**
|
||||
* @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 "deribit_emumba/emumba_enum.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>;
|
||||
|
||||
// clang-format off
|
||||
#define ENUM_STATUS(op) \
|
||||
op(status, 0, working) \
|
||||
op(status, 1, completed) \
|
||||
op(status, 2, canceled) \
|
||||
op(status, 3, rejected) \
|
||||
op(status, 4, untriggered)
|
||||
EMUMBA_ENUM(status, ENUM_STATUS)
|
||||
#undef ENUM_STATUS
|
||||
// clang-format on
|
||||
|
||||
// clang-format off
|
||||
#define ENUM_UPDATE_TYPE(op) \
|
||||
op(update_type, 0, new_order_confirm) \
|
||||
op(update_type, 1, new_order_reject) \
|
||||
op(update_type, 2, edit_order_confirm) \
|
||||
op(update_type, 3, edit_order_reject) \
|
||||
op(update_type, 4, cancel_order_confirm) \
|
||||
op(update_type, 5, cancel_order_reject) \
|
||||
op(update_type, 6, subscription_update)
|
||||
EMUMBA_ENUM(update_type, ENUM_UPDATE_TYPE)
|
||||
#undef ENUM_UPDATE_TYPE
|
||||
// clang-format on
|
||||
|
||||
/**
|
||||
* @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();
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
#define ENUM_TIME_IN_FORCE(op) \
|
||||
op(time_in_force, 48, day) /* Good till Day. */ \
|
||||
op(time_in_force, 49, gtc) /* Good till Cancelled. */ \
|
||||
op(time_in_force, 51, ioc) /* Immediate or Cancel. */ \
|
||||
op(time_in_force, 52, date) /* Good till Date. */ \
|
||||
op(time_in_force, 53, fill_or_kill) /* Fill or Kill. */
|
||||
EMUMBA_ENUM(time_in_force, ENUM_TIME_IN_FORCE)
|
||||
#undef EMUMBA_TIME_IN_FORCE
|
||||
|
||||
#define ENUM_ORDER_TYPE(op) \
|
||||
op(order_type, 1, limit_order) /* Limit Order. */ \
|
||||
op(order_type, 2, stop_limit_order) /* Stop Limit Order. */ \
|
||||
op(order_type, 3, take_limit_order) /* Take Limit Order. */ \
|
||||
op(order_type, 4, market_order) /* Market Order. */ \
|
||||
op(order_type, 5, stop_market_order) /* Stop Market Order. */ \
|
||||
op(order_type, 6, take_market_order) /* Take Market Order. */ \
|
||||
op(order_type, 7, market_limit_order) /* Market Limit Order. */
|
||||
EMUMBA_ENUM(order_type, ENUM_ORDER_TYPE)
|
||||
#undef EMUMBA_ORDER_TYPE
|
||||
|
||||
#define ENUM_ORDER_TRIGGER(op) \
|
||||
op(order_trigger, 0, index_price) /* Index Price. */ \
|
||||
op(order_trigger, 1, mark_price) /* Mark Price. */ \
|
||||
op(order_trigger, 2, last_price) /* Last Price. */
|
||||
EMUMBA_ENUM(order_trigger, ENUM_ORDER_TRIGGER)
|
||||
#undef EMUMBA_ORDER_TRIGGER
|
||||
|
||||
#define ENUM_ORDER_ADVANCED(op) \
|
||||
op(order_advanced, 0, usd) /* USD. */ \
|
||||
op(order_advanced, 1, implv) /* IMPLV. */
|
||||
EMUMBA_ENUM(order_advanced, ENUM_ORDER_ADVANCED)
|
||||
#undef EMUMBA_ORDER_ADVANCED
|
||||
// clang-format on
|
||||
|
||||
/**
|
||||
* @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;
|
||||
};
|
||||
|
||||
#define ENUM_SORT(op) \
|
||||
op(sort, 0, ascending) \
|
||||
op(sort, 1, decending) \
|
||||
op(sort, 2, default_)
|
||||
EMUMBA_ENUM(sort, ENUM_SORT)
|
||||
#undef EMUMBA_SORT
|
||||
|
||||
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,82 @@
|
||||
#include "price_level.h"
|
||||
|
||||
using namespace emumba::deribit_connector::data_types;
|
||||
|
||||
price_level::price_level()
|
||||
: _price(emumba::market_data::price())
|
||||
, _amount(0)
|
||||
{}
|
||||
|
||||
price_level::price_level(emumba::market_data::price price, double amount)
|
||||
: _price(price)
|
||||
, _amount(amount)
|
||||
{}
|
||||
|
||||
emumba::market_data::price price_level::get_price() const
|
||||
{
|
||||
return _price;
|
||||
}
|
||||
|
||||
double price_level::get_amount() const
|
||||
{
|
||||
return _amount;
|
||||
}
|
||||
|
||||
void price_level::set_amount(const double &amount)
|
||||
{
|
||||
_amount = amount;
|
||||
}
|
||||
|
||||
price_level::iterator::iterator(void *ptr, int side)
|
||||
{
|
||||
_book_ptr = (std::map<double, data_types::price_level> *)ptr;
|
||||
_book_level_itr = _book_ptr->begin();
|
||||
_current_ptr = &_book_level_itr->second;
|
||||
_side = side;
|
||||
}
|
||||
|
||||
price_level::iterator::self_type price_level::iterator::operator++()
|
||||
{
|
||||
_book_level_itr++;
|
||||
_current_ptr = &_book_level_itr->second;
|
||||
return *this;
|
||||
}
|
||||
price_level::iterator::self_type price_level::iterator::operator++(int)
|
||||
{
|
||||
_book_level_itr++;
|
||||
_current_ptr = &_book_level_itr->second;
|
||||
return *this;
|
||||
}
|
||||
|
||||
price_level price_level::iterator::operator*()
|
||||
{
|
||||
return *_current_ptr;
|
||||
}
|
||||
bool price_level::iterator::operator==(const price_level::iterator::self_type &rhs)
|
||||
{
|
||||
return _current_ptr == rhs._current_ptr;
|
||||
}
|
||||
bool price_level::iterator::operator!=(const price_level::iterator::self_type &rhs)
|
||||
{
|
||||
return _current_ptr != rhs._current_ptr;
|
||||
}
|
||||
|
||||
int price_level::iterator::side()
|
||||
{
|
||||
return _side;
|
||||
}
|
||||
|
||||
emumba::market_data::price price_level::iterator::price()
|
||||
{
|
||||
return _current_ptr->get_price();
|
||||
}
|
||||
|
||||
double price_level::iterator::quantity()
|
||||
{
|
||||
return _current_ptr->get_amount();
|
||||
}
|
||||
|
||||
uint32_t price_level::iterator::order_count()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -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,76 @@
|
||||
#include "rpc_message.h"
|
||||
|
||||
#include "spdlogger.h"
|
||||
|
||||
emumba::deribit_connector::data_types::rpc_message::rpc_message(const std::string &rpc_msg)
|
||||
{
|
||||
_message.Parse<rapidjson::ParseFlag::kParseFullPrecisionFlag>(rpc_msg.c_str());
|
||||
}
|
||||
|
||||
const int emumba::deribit_connector::data_types::rpc_message::get_id() const
|
||||
{
|
||||
return (_message.HasMember("id") ? _message["id"].GetInt() : 0);
|
||||
}
|
||||
|
||||
const rapidjson::Value &emumba::deribit_connector::data_types::rpc_message::get_method() const
|
||||
{
|
||||
return (_message.HasMember("method") ? _message["method"] : _default_value);
|
||||
}
|
||||
|
||||
const rapidjson::Value &emumba::deribit_connector::data_types::rpc_message::get_result() const
|
||||
{
|
||||
return (_message.HasMember("result") ? _message["result"] : _default_value);
|
||||
}
|
||||
|
||||
const rapidjson::Value &emumba::deribit_connector::data_types::rpc_message::get_params() const
|
||||
{
|
||||
return (_message.HasMember("params") ? _message["params"] : _default_value);
|
||||
}
|
||||
|
||||
const emumba::deribit_connector::data_types::error emumba::deribit_connector::data_types::rpc_message::get_error()
|
||||
{
|
||||
if (is_error())
|
||||
{
|
||||
if (_message["error"].IsObject())
|
||||
{
|
||||
return emumba::deribit_connector::data_types::error(
|
||||
_message["error"]["code"].GetInt(), _message["error"]["message"].GetString(),
|
||||
_message["error"].HasMember("data") ? _convert_document.to_string(_message["error"]["data"])
|
||||
: _convert_document.to_string(_default_value));
|
||||
}
|
||||
else
|
||||
{
|
||||
return emumba::deribit_connector::data_types::error(_message["error"].GetInt(), _message["message"].GetString(),
|
||||
"");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return emumba::deribit_connector::data_types::error(0, "Success");
|
||||
}
|
||||
}
|
||||
|
||||
const bool emumba::deribit_connector::data_types::rpc_message::is_response() const
|
||||
{
|
||||
return _message.HasMember("result");
|
||||
}
|
||||
|
||||
const bool emumba::deribit_connector::data_types::rpc_message::is_notification() const
|
||||
{
|
||||
return _message.HasMember("method") && (strcmp(_message["method"].GetString(), "subscription") == 0);
|
||||
}
|
||||
|
||||
const bool emumba::deribit_connector::data_types::rpc_message::is_batch() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool emumba::deribit_connector::data_types::rpc_message::is_error() const
|
||||
{
|
||||
return _message.HasMember("error");
|
||||
}
|
||||
|
||||
const bool emumba::deribit_connector::data_types::rpc_message::has_id() const
|
||||
{
|
||||
return _message.HasMember("id");
|
||||
}
|
||||
@@ -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,424 @@
|
||||
#include "session.h"
|
||||
|
||||
#ifdef TRACEPOINTS
|
||||
#include "NanoLogCpp17.h"
|
||||
#endif // TRACEPOINTS
|
||||
|
||||
#include "deribit_exception.h"
|
||||
#include "method_identifiers.h"
|
||||
#include "spdlogger.h"
|
||||
|
||||
#include <rapidjson/istreamwrapper.h>
|
||||
#include <rapidjson/ostreamwrapper.h>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
#include <rapidjson/writer.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
emumba::deribit_connector::session::session(
|
||||
emumba::connector::io_handler &io, uint id)
|
||||
: _socket(io, id)
|
||||
#ifndef ENABLE_BOOST_WEBSOCKET
|
||||
,
|
||||
_rate_limit_timer(io)
|
||||
#endif
|
||||
,
|
||||
_id(id), _reconnection(false), _jsonrpc_version("2.0") {
|
||||
_socket.set_connect_callback(std::bind(&session::send_auth_message, this));
|
||||
_socket.set_receive_callback(std::bind(&session::process_response, this, std::placeholders::_1));
|
||||
_socket.set_close_callback(std::bind(&session::on_close, this));
|
||||
#ifndef ENABLE_BOOST_WEBSOCKET
|
||||
_num_of_requests = 9;
|
||||
_rate_limit_timer.set_cb(std::bind(&session::on_rate_timer_expiry, this));
|
||||
_rate_limit_timer.set_interval(1);
|
||||
_rate_limit_timer.start();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef ENABLE_BOOST_WEBSOCKET
|
||||
void emumba::deribit_connector::session::on_rate_timer_expiry()
|
||||
{
|
||||
LOG_TRACE("Rate timer exhausted | Sending buffered message on socket id: {}", _id);
|
||||
if (_state == state::connected)
|
||||
{
|
||||
_num_of_requests = 9;
|
||||
auto msg_itr = _buffered_messages.begin();
|
||||
while (_num_of_requests != 0 && msg_itr != _buffered_messages.end())
|
||||
{
|
||||
_socket.send(msg_itr->c_str());
|
||||
msg_itr = _buffered_messages.erase(msg_itr);
|
||||
_num_of_requests--;
|
||||
}
|
||||
}
|
||||
_rate_limit_timer.reset();
|
||||
}
|
||||
#endif
|
||||
|
||||
void emumba::deribit_connector::session::set_session_params(const std::string & uri_in,
|
||||
const configuration::trading_session_config &trading_config,
|
||||
const configuration::credential_config & cred_config)
|
||||
{
|
||||
_websocket_uri = uri_in;
|
||||
_client_name = cred_config.client_name;
|
||||
_client_id = cred_config.client_id;
|
||||
_client_secret = cred_config.secret_key;
|
||||
_interval = trading_config.heartbeat_interval;
|
||||
_state = state::not_connected;
|
||||
_reconnect_config = trading_config.reconnect_config;
|
||||
|
||||
LOG_TRACE(
|
||||
"In set_session_params | reconnection config set | retries count: {}, retry interval: {} | credential config set | "
|
||||
"client id: {} | secret key: {}",
|
||||
_reconnect_config.retry_count, _reconnect_config.retry_interval, cred_config.client_id, cred_config.secret_key);
|
||||
create_heartbeat_messages();
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session::set_on_receive_callback(on_receive_cb_t fn_in)
|
||||
{
|
||||
on_receive_cb = fn_in;
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session::set_on_reconnect_callback(on_reconnect_cb_t fn_in)
|
||||
{
|
||||
on_reconnect_cb = fn_in;
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session::set_on_error_callback(on_error_cb_t fn_in)
|
||||
{
|
||||
on_error_cb = fn_in;
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session::set_on_print_book_callback(on_print_book_cb_t fn_in)
|
||||
{
|
||||
on_print_book_cb = fn_in;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_BOOST_WEBSOCKET
|
||||
|
||||
const bool &emumba::deribit_connector::session::is_reception_active() const
|
||||
{
|
||||
return _reception_active;
|
||||
}
|
||||
void emumba::deribit_connector::session::clear_reception_state()
|
||||
{
|
||||
_reception_active = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void emumba::deribit_connector::session::print_book()
|
||||
{
|
||||
if (on_print_book_cb)
|
||||
{
|
||||
on_print_book_cb();
|
||||
}
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session::create_heartbeat_messages()
|
||||
{
|
||||
|
||||
_str_buff.Clear();
|
||||
_writer.Reset(_str_buff);
|
||||
_writer.StartObject();
|
||||
_writer.Key("jsonrpc");
|
||||
_writer.String(_jsonrpc_version);
|
||||
_writer.Key("id");
|
||||
_writer.Uint64((int)method_id::set_heartbeat);
|
||||
_writer.Key("method");
|
||||
_writer.String(set_heartbeat.c_str());
|
||||
_writer.Key("params");
|
||||
_writer.StartObject();
|
||||
_writer.Key("interval");
|
||||
_writer.Int64(_interval);
|
||||
_writer.EndObject();
|
||||
_writer.EndObject();
|
||||
strcpy(_message_set_heartbeat, _str_buff.GetString());
|
||||
|
||||
_str_buff.Clear();
|
||||
_writer.Reset(_str_buff);
|
||||
_writer.StartObject();
|
||||
_writer.Key("jsonrpc");
|
||||
_writer.String(_jsonrpc_version);
|
||||
_writer.Key("id");
|
||||
_writer.Uint64((int)method_id::test);
|
||||
_writer.Key("method");
|
||||
_writer.String(test.c_str());
|
||||
_writer.EndObject();
|
||||
strcpy(_message_test, _str_buff.GetString());
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session::start_session()
|
||||
{
|
||||
LOG_INFO("Connecting to websocket on socket id: {}", _id);
|
||||
_number_of_tries = 3;
|
||||
_socket.connect(_websocket_uri);
|
||||
_state = state::connecting;
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session::poll()
|
||||
{
|
||||
#ifdef ENABLE_BOOST_WEBSOCKET
|
||||
_socket.poll();
|
||||
#endif
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session::process_response(std::string response)
|
||||
{
|
||||
#ifdef TRACEPOINTS
|
||||
_timer->start();
|
||||
#endif // TRACEPOINTS
|
||||
|
||||
#ifndef ENABLE_BOOST_WEBSOCKET
|
||||
_reception_active = true;
|
||||
#endif
|
||||
|
||||
data_types::rpc_message rpc_msg_received(response);
|
||||
if (likely(!rpc_msg_received.is_error()))
|
||||
{
|
||||
if (__builtin_expect(_state == state::connected, true))
|
||||
{
|
||||
if (__builtin_expect(rpc_msg_received.is_notification(), true))
|
||||
{
|
||||
on_receive_cb(rpc_msg_received.get_error(), rpc_msg_received);
|
||||
}
|
||||
else if (rpc_msg_received.has_id() == 0) // no id present; indicates that the response is a heartbeat
|
||||
{
|
||||
if (strncmp(_convert_document.to_string(rpc_msg_received.get_method()).c_str(), "\"heartbeat\"", 11) == 0)
|
||||
{
|
||||
#ifndef ENABLE_BOOST_WEBSOCKET
|
||||
LOG_TRACE("Heartbeat message received, resetting heartbeat timeout");
|
||||
#endif
|
||||
|
||||
LOG_DEBUG("Received Test Request Message on socket id: {}", _id);
|
||||
_socket.send(_message_test);
|
||||
}
|
||||
}
|
||||
else if (rpc_msg_received.get_id() == (int)method_id::test)
|
||||
{
|
||||
LOG_TRACE("Received response to test message on socket id: {}", _id);
|
||||
}
|
||||
else
|
||||
{
|
||||
on_receive_cb(rpc_msg_received.get_error(), rpc_msg_received);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int id = rpc_msg_received.get_id();
|
||||
if (id == (int)method_id::auth)
|
||||
{
|
||||
const rapidjson::Value &result = rpc_msg_received.get_result();
|
||||
_access_token = result["access_token"].GetString();
|
||||
_refresh_token = result["refresh_token"].GetString();
|
||||
LOG_DEBUG("Received Auth response on socket id: {}, access_token: {}, refresh_token: {}", _id, _access_token,
|
||||
_refresh_token);
|
||||
_socket.send(_message_set_heartbeat);
|
||||
}
|
||||
|
||||
else if (id == (int)method_id::set_heartbeat)
|
||||
{
|
||||
LOG_DEBUG("Received Set Heartbeat Ack on socket id: {}", _id);
|
||||
_state = state::connected;
|
||||
if (_reconnection)
|
||||
{
|
||||
if (on_reconnect_cb)
|
||||
{
|
||||
on_reconnect_cb();
|
||||
}
|
||||
_reconnection = false;
|
||||
}
|
||||
|
||||
LOG_INFO("Session: {} is Ready", _id);
|
||||
for (auto msg : _buffered_messages)
|
||||
{
|
||||
LOG_DEBUG("Sending buffered message: {} on socket id: {}", msg, _id);
|
||||
_socket.send(msg.c_str());
|
||||
}
|
||||
_buffered_messages.clear();
|
||||
}
|
||||
|
||||
else if (id == (int)method_id::test)
|
||||
{
|
||||
LOG_TRACE("Received response to test message on socket id: {}", _id);
|
||||
}
|
||||
|
||||
else if (std::strncmp(_convert_document.to_string(rpc_msg_received.get_method()).c_str(), "\"heartbeat\"", 11) == 0)
|
||||
{
|
||||
LOG_DEBUG("Received Test Request Message on socket id: {}", _id);
|
||||
_socket.send(_message_test);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
on_receive_cb(rpc_msg_received.get_error(), rpc_msg_received);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ec = rpc_msg_received.get_error();
|
||||
LOG_ERROR("Error response in jsonrpc on socket id: {}. Error code: {}, Error message: {}, Error details: {}", _id,
|
||||
ec.value(), ec.message(), ec.details());
|
||||
if (rpc_msg_received.get_id() == (int)method_id::auth)
|
||||
{
|
||||
if (ec.value() == 13004)
|
||||
{
|
||||
LOG_ERROR("Authentication Failed | session id: {}", _id);
|
||||
}
|
||||
else if (ec.value() == 11050)
|
||||
{
|
||||
LOG_ERROR("URI Failed | session id: {}", _id);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Couldn't complete auth process for session id: {}. Check configuration", _id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
on_receive_cb(ec, rpc_msg_received);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session::send_auth_message()
|
||||
{
|
||||
_str_buff.Clear();
|
||||
_writer.Reset(_str_buff);
|
||||
_writer.StartObject();
|
||||
_writer.Key("jsonrpc");
|
||||
_writer.String(_jsonrpc_version);
|
||||
_writer.Key("id");
|
||||
_writer.Uint64((int)method_id::auth);
|
||||
_writer.Key("method");
|
||||
_writer.String(auth.c_str());
|
||||
_writer.Key("params");
|
||||
_writer.StartObject();
|
||||
_writer.Key("grant_type");
|
||||
_writer.String("client_credentials");
|
||||
_writer.Key("client_id");
|
||||
_writer.String(_client_id.c_str());
|
||||
_writer.Key("client_secret");
|
||||
_writer.String(_client_secret.c_str());
|
||||
_writer.EndObject();
|
||||
_writer.EndObject();
|
||||
LOG_TRACE(
|
||||
"Socket Connected: Sending auth message: {}",
|
||||
string{_str_buff.GetString()});
|
||||
_socket.send(_str_buff.GetString());
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session::send_message(const char *msg_buff)
|
||||
{
|
||||
if (_state == state::connected)
|
||||
{
|
||||
#ifndef ENABLE_BOOST_WEBSOCKET
|
||||
if (_num_of_requests > 0)
|
||||
{
|
||||
#endif
|
||||
_socket.send(msg_buff);
|
||||
#ifndef ENABLE_BOOST_WEBSOCKET
|
||||
_num_of_requests--;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG(
|
||||
"Internal Rate limit reached | Buffering message: {} on socket "
|
||||
"id: {}",
|
||||
string{msg_buff}, _id);
|
||||
_buffered_messages.push_back(std::string(msg_buff));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (_state == state::connecting)
|
||||
{
|
||||
LOG_DEBUG(
|
||||
"Buffering message: {} on socket id: {}", string{msg_buff}, _id);
|
||||
_buffered_messages.push_back(std::string(msg_buff));
|
||||
}
|
||||
else if (_state == state::not_connected)
|
||||
{
|
||||
_buffered_messages.push_back(std::string(msg_buff));
|
||||
LOG_DEBUG("Disconnection detected. Trying to connect again on socket id: {}", _id);
|
||||
_state = state::connecting;
|
||||
_number_of_tries = _reconnect_config.retry_count;
|
||||
bool is_connected = false;
|
||||
while (_number_of_tries != 0 && !is_connected)
|
||||
{
|
||||
is_connected = _socket.socket_reconnect(_websocket_uri);
|
||||
|
||||
if (is_connected == false)
|
||||
{
|
||||
sleep(_reconnect_config.retry_interval);
|
||||
}
|
||||
--_number_of_tries;
|
||||
}
|
||||
if (is_connected)
|
||||
{
|
||||
LOG_DEBUG("Reconnection for session id {} successful", _id);
|
||||
_state = state::connecting;
|
||||
_reconnection = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Cannot connect to server. Closing application");
|
||||
sleep(1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session::on_close()
|
||||
{
|
||||
emumba::market_data::market_data_error error;
|
||||
error.err_type = emumba::market_data::market_data_error::error_type::disconnection;
|
||||
error.err_text = "Disconnection Detected";
|
||||
|
||||
if (on_error_cb)
|
||||
on_error_cb(error);
|
||||
|
||||
LOG_DEBUG("Disconnection detected. Trying to connect again on socket id: {}", _id);
|
||||
_state = state::connecting;
|
||||
_number_of_tries = _reconnect_config.retry_count;
|
||||
bool is_connected = false;
|
||||
while (_number_of_tries != 0 && !is_connected)
|
||||
{
|
||||
is_connected = _socket.socket_reconnect(_websocket_uri);
|
||||
|
||||
if (is_connected == false)
|
||||
{
|
||||
sleep(_reconnect_config.retry_interval);
|
||||
}
|
||||
--_number_of_tries;
|
||||
}
|
||||
if (is_connected)
|
||||
{
|
||||
LOG_DEBUG("Reconnection for session id {} successful", _id);
|
||||
if (on_reconnect_cb)
|
||||
{
|
||||
on_reconnect_cb();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Cannot connect to server. Closing application");
|
||||
sleep(1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const emumba::deribit_connector::session::state &emumba::deribit_connector::session::get_state() const
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
const uint &emumba::deribit_connector::session::get_session_id() const
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
#ifdef TRACEPOINTS
|
||||
void emumba::deribit_connector::session::set_timer_reference(emumba::utils::timer *timer)
|
||||
{
|
||||
_timer = timer;
|
||||
}
|
||||
#endif
|
||||
@@ -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,134 @@
|
||||
#include "session_manager.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef TRACEPOINTS
|
||||
#include "NanoLogCpp17.h"
|
||||
#endif // TRACEPOINTS
|
||||
|
||||
#include "method_identifiers.h"
|
||||
#include "spdlogger.h"
|
||||
|
||||
extern std::atomic<int> print_books;
|
||||
|
||||
emumba::deribit_connector::session_manager::session_manager(const std::string & uri_in,
|
||||
const configuration::trading_session_config &trading_config)
|
||||
: _websocket_uri(uri_in)
|
||||
, _trading_config(trading_config)
|
||||
#ifndef ENABLE_BOOST_WEBSOCKET
|
||||
, _reconnection_timer(_io)
|
||||
#endif
|
||||
|
||||
{
|
||||
_id = 0;
|
||||
#ifdef TRACEPOINTS
|
||||
init_nanolog();
|
||||
#endif
|
||||
if (trading_config.heartbeat_interval < 10)
|
||||
{
|
||||
LOG_WARNING("Heartbeat interval value less than 10. Value: {}. Using default interval value (10) for heartbeats.",
|
||||
trading_config.heartbeat_interval);
|
||||
_trading_config.heartbeat_interval = 10;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_BOOST_WEBSOCKET
|
||||
_reconnection_timer.set_cb(std::bind(&session_manager::on_timeout, this));
|
||||
_reconnection_timer.set_interval(_trading_config.reconnect_config.reception_timeout);
|
||||
_reconnection_timer.start();
|
||||
#endif
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session_manager::init_nanolog()
|
||||
{
|
||||
#ifdef TRACEPOINTS
|
||||
NanoLog::setLogFile("./performance-logs");
|
||||
NanoLog::preallocate();
|
||||
NanoLog::setLogLevel(NanoLog::NOTICE);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::shared_ptr<emumba::deribit_connector::session> emumba::deribit_connector::session_manager::create_new_session(
|
||||
const configuration::credential_config &cred_config, on_receive_cb_t session_receive_cb)
|
||||
{
|
||||
std::shared_ptr<session> new_session = std::make_shared<session>(_io, ++_id);
|
||||
new_session->set_session_params(_websocket_uri, _trading_config, cred_config);
|
||||
new_session->set_on_receive_callback(session_receive_cb);
|
||||
new_session->start_session();
|
||||
|
||||
active_session_list.push_back(new_session);
|
||||
|
||||
return new_session;
|
||||
}
|
||||
|
||||
void emumba::deribit_connector::session_manager::run()
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (__builtin_expect(print_books.load() == 0, true))
|
||||
{
|
||||
#ifdef ENABLE_BOOST_WEBSOCKET
|
||||
for (auto &it : active_session_list)
|
||||
{
|
||||
it->poll();
|
||||
}
|
||||
#else
|
||||
_io.poll();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ENABLE_BOOST_WEBSOCKET
|
||||
for (auto &it : active_session_list)
|
||||
{
|
||||
it->poll();
|
||||
it->print_book();
|
||||
}
|
||||
#else
|
||||
_io.poll();
|
||||
for (auto &it : active_session_list)
|
||||
{
|
||||
it->print_book();
|
||||
}
|
||||
#endif
|
||||
print_books.store(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint emumba::deribit_connector::session_manager::get_core_id()
|
||||
{
|
||||
return _trading_config.core;
|
||||
}
|
||||
|
||||
const bool &emumba::deribit_connector::session_manager::get_cancel_on_disconnect() const
|
||||
{
|
||||
// TODO: Can be removed as cancel on disconnect is no longer configurable
|
||||
return _trading_config.cancel_on_disconnect;
|
||||
}
|
||||
|
||||
emumba::connector::io_handler &emumba::deribit_connector::session_manager::get_io()
|
||||
{
|
||||
return _io;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_BOOST_WEBSOCKET
|
||||
void emumba::deribit_connector::session_manager::on_timeout()
|
||||
{
|
||||
for (auto &it : active_session_list)
|
||||
{
|
||||
if (it->is_reception_active() == false)
|
||||
{
|
||||
LOG_ERROR("No reception on session {} | Connection loss detected | Trying to reconnect this session.",
|
||||
it->get_session_id());
|
||||
|
||||
it->on_close();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_TRACE("Session {} is in correct reception state", it->get_session_id());
|
||||
it->clear_reception_state();
|
||||
}
|
||||
}
|
||||
_reconnection_timer.reset();
|
||||
}
|
||||
#endif
|
||||
@@ -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,83 @@
|
||||
#include "spdlogger.h"
|
||||
#include "spdlog/async.h" //support for async logging
|
||||
#include <iostream>
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
|
||||
void emumba::logger::set_spdlog_filter(const std::string &svl)
|
||||
{
|
||||
// set severity:
|
||||
if (svl == "trace")
|
||||
spdlog::set_level(spdlog::level::trace);
|
||||
else if (svl == "debug")
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
else if (svl == "info")
|
||||
spdlog::set_level(spdlog::level::info);
|
||||
else if (svl == "warning")
|
||||
spdlog::set_level(spdlog::level::warn);
|
||||
else if (svl == "error")
|
||||
spdlog::set_level(spdlog::level::err);
|
||||
else
|
||||
spdlog::set_level(spdlog::level::info);
|
||||
}
|
||||
|
||||
void emumba::logger::init_spdlog(const std::string &svl, bool isConsoleEnabled, bool isFileEnabled)
|
||||
{
|
||||
uint32_t queue_size = 8192;
|
||||
spdlog::init_thread_pool(queue_size, 1);
|
||||
try
|
||||
{
|
||||
if (isFileEnabled && isConsoleEnabled)
|
||||
{
|
||||
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("emumba-deribit-connector.log");
|
||||
std::vector<spdlog::sink_ptr> sinks{console_sink, file_sink};
|
||||
auto _emumba_logger =
|
||||
std::make_shared<spdlog::async_logger>("emumba_logger", sinks.begin(), sinks.end(), spdlog::thread_pool(),
|
||||
spdlog::async_overflow_policy::overrun_oldest);
|
||||
spdlog::set_default_logger(_emumba_logger);
|
||||
// set severity:
|
||||
set_spdlog_filter(svl);
|
||||
}
|
||||
else if (isFileEnabled)
|
||||
{
|
||||
auto _emumba_logger =
|
||||
spdlog::basic_logger_mt<spdlog::async_factory>("emumba_logger", "emumba-deribit-connector.log");
|
||||
spdlog::set_default_logger(_emumba_logger);
|
||||
// set severity:
|
||||
set_spdlog_filter(svl);
|
||||
}
|
||||
else if (isConsoleEnabled)
|
||||
{
|
||||
auto _emumba_logger = spdlog::stdout_color_mt<spdlog::async_factory>("emumba_logger");
|
||||
spdlog::set_default_logger(_emumba_logger);
|
||||
// set severity:
|
||||
set_spdlog_filter(svl);
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::set_level(spdlog::level::off);
|
||||
}
|
||||
|
||||
// spdlog::flush_every(std::chrono::seconds(3));
|
||||
spdlog::flush_on(spdlog::level::debug);
|
||||
}
|
||||
catch (const spdlog::spdlog_ex &ex)
|
||||
{
|
||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool emumba::logger::flush_logs()
|
||||
{
|
||||
try
|
||||
{
|
||||
spdlog::get("emumba_logger")->flush();
|
||||
}
|
||||
catch (const spdlog::spdlog_ex &ex)
|
||||
{
|
||||
std::cout << "Exception: " << ex.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @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"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// 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__)
|
||||
namespace emumba {
|
||||
namespace logger {
|
||||
|
||||
// use during runtime to change severity log output.
|
||||
/**
|
||||
* @brief Set the severity of logger.
|
||||
*
|
||||
* @param svl The serverity level of logger. i.e. trace, debug, info, warning or
|
||||
* error.
|
||||
*/
|
||||
void set_spdlog_filter(const std::string &svl);
|
||||
|
||||
/**
|
||||
* @brief Initializes the logger used by the feedhandler.
|
||||
*
|
||||
* @param svl The severity level of logger. i.e. trace, debug, info, warning or
|
||||
* error.
|
||||
* @param isConsoleEnabled Flag to determine whether logs should be printed to
|
||||
* console or not.
|
||||
* @param isFileEnabled Flag to determine whether logs should be printed to log
|
||||
* file or not.
|
||||
*/
|
||||
void init_spdlog(const std::string &svl, bool isConsoleEnabled, bool isFileEnabled);
|
||||
|
||||
/**
|
||||
* @brief Clears the buffers used by spdlogger.
|
||||
*
|
||||
* @return true In case of success.
|
||||
* @return false In case of failure.
|
||||
*/
|
||||
bool flush_logs();
|
||||
|
||||
} // namespace logger
|
||||
} // namespace emumba
|
||||
|
||||
#endif /* EMUMBA_SPDLOGGER_H */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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,565 @@
|
||||
#include "symbol_instruments.h"
|
||||
|
||||
#include "deribit_exception.h"
|
||||
#include "method_identifiers.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <rapidjson/writer.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
using namespace emumba::deribit_connector;
|
||||
|
||||
symbol_instruments::symbol_instruments(const std::string &symbol_name, const std::shared_ptr<session> session,
|
||||
const configuration::subscription_config &sub_config,
|
||||
deribit_listener * trade_listener)
|
||||
: _symbol_name(symbol_name)
|
||||
, _subscription_interval("raw")
|
||||
, _session(session)
|
||||
, _session_state(_session->get_state()) // TODO: Remove this
|
||||
, _subscriptions_config(sub_config) // TODO: look into handling of request limit in session class
|
||||
, _trade_listener(trade_listener)
|
||||
, _public_subscribe_method("public/subscribe")
|
||||
, _public_unsubscribe_method("public/unsubscribe")
|
||||
, _private_subscribe_method("private/subscribe")
|
||||
, _private_unsubscribe_method("private/unsubscribe")
|
||||
, _jsonrpc_version("2.0")
|
||||
, _writer(_str_buff)
|
||||
, _public_sub_writer(_public_sub_str_buff)
|
||||
, _private_sub_writer(_private_sub_str_buff)
|
||||
, _public_subs_in_request_msg(0)
|
||||
, _private_subs_in_request_msg(0)
|
||||
, _is_new_public_sub_msg(true)
|
||||
, _is_new_private_sub_msg(true)
|
||||
, _public_unsubs_in_request_msg(0)
|
||||
, _private_unsubs_in_request_msg(0)
|
||||
, _is_new_public_unsub_msg(true)
|
||||
, _is_new_private_unsub_msg(true)
|
||||
{
|
||||
_str_buff.Reserve(1000);
|
||||
_public_sub_str_buff.Reserve(1000);
|
||||
_private_sub_str_buff.Reserve(1000);
|
||||
|
||||
if (_subscriptions_config.use_raw_interval)
|
||||
{
|
||||
_subscription_interval = "raw";
|
||||
}
|
||||
else
|
||||
{
|
||||
_subscription_interval = "100ms";
|
||||
}
|
||||
|
||||
_max_whitelist_size = 65535;
|
||||
_white_list.reserve(_max_whitelist_size);
|
||||
_all_sub_req_sent = false;
|
||||
|
||||
LOG_TRACE("Constructed symbol_instruments object");
|
||||
}
|
||||
|
||||
void symbol_instruments::add_to_whitelist(const std::string &instrument_name, instrument_info *&instr_info)
|
||||
{
|
||||
LOG_DEBUG("Initializing Instrument: {}", instrument_name);
|
||||
|
||||
_white_list.emplace(std::make_pair(
|
||||
instrument_name, instrument_data(instrument_name, _subscriptions_config.book_depth, *instr_info, _trade_listener)));
|
||||
|
||||
emumba::deribit_connector::instrument_data & inst_data = _white_list.at(instrument_name);
|
||||
std::shared_ptr<emumba::deribit_connector::instrument_data_interface> interface =
|
||||
std::make_shared<emumba::deribit_connector::instrument_data_interface>(&inst_data);
|
||||
inst_data.set_instrument_data_interface(interface);
|
||||
void *_api_handler = _trade_listener->on_instrument_definition(inst_data.get_name(), interface.get());
|
||||
inst_data.set_api_handler(_api_handler);
|
||||
|
||||
_instruments_to_subscribe.emplace(instrument_name);
|
||||
}
|
||||
|
||||
void symbol_instruments::initialize_instruments(const instruments_set & set_of_instruments,
|
||||
std::map<std::string, instrument_info *> instruments_info)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (set_of_instruments.empty() == false)
|
||||
{
|
||||
|
||||
LOG_INFO("Symbol: {}, No of interested instruments: {}, Subscription interval: {}, Book Depth: {}", _symbol_name,
|
||||
set_of_instruments.size(), _subscription_interval, _subscriptions_config.book_depth);
|
||||
for (const std::string &instrument_name : set_of_instruments)
|
||||
{
|
||||
add_to_whitelist(instrument_name, instruments_info.at(instrument_name));
|
||||
}
|
||||
gen_interested_channels_str();
|
||||
sub_instruments();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR(
|
||||
"Whitelist empty. No instrument selected. Possible reason: Invalid instruments specified in "
|
||||
"\"interested_instruments\" list. Symbol: {}",
|
||||
_symbol_name);
|
||||
emumba::market_data::market_data_error mrkt_data_error;
|
||||
mrkt_data_error.err_type = emumba::market_data::market_data_error::error_type::whitelist_empty;
|
||||
mrkt_data_error.err_text =
|
||||
"Whitelist empty. No instrument selected. Possible reason: Invalid instruments specified in "
|
||||
"\"interested_instruments\" list. Symbol: " +
|
||||
_symbol_name;
|
||||
#ifdef ENABLE_API
|
||||
_trade_listener->on_error(mrkt_data_error);
|
||||
#endif
|
||||
throw emumba::deribit_connector::deribit_exception(
|
||||
emumba::deribit_connector::deribit_exception::error_code::no_instruments);
|
||||
}
|
||||
}
|
||||
catch (emumba::deribit_connector::deribit_exception &deribit_exception)
|
||||
{
|
||||
deribit_exception.print();
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_instruments::add_new_instruments(const instruments_set & set_of_instruments,
|
||||
std::map<std::string, instrument_info *> instruments_info)
|
||||
{
|
||||
_instruments_to_subscribe.clear();
|
||||
|
||||
if (set_of_instruments.empty() == false)
|
||||
{
|
||||
LOG_DEBUG("New instruments added | Symbol: {}, No of new instruments: {}", _symbol_name, set_of_instruments.size());
|
||||
for (const std::string &instrument_name : set_of_instruments)
|
||||
{
|
||||
if (_white_list.find(instrument_name) == _white_list.end()) // Sanity check
|
||||
{
|
||||
add_to_whitelist(instrument_name, instruments_info.at(instrument_name));
|
||||
LOG_INFO("New Instrument added | Symbol: {}, Instrument: {}", _symbol_name, instrument_name);
|
||||
}
|
||||
}
|
||||
sub_instruments();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG("No new instrument added. Symbol: {}", _symbol_name);
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_instruments::gen_interested_channels_str()
|
||||
{
|
||||
_interested_channels_str = "";
|
||||
|
||||
if (_subscriptions_config.public_book)
|
||||
{
|
||||
_interested_channels_str = _interested_channels_str + " Book,";
|
||||
}
|
||||
|
||||
if (_subscriptions_config.public_quote)
|
||||
{
|
||||
_interested_channels_str = _interested_channels_str + " Quote,";
|
||||
}
|
||||
|
||||
if (_subscriptions_config.public_trade)
|
||||
{
|
||||
_interested_channels_str = _interested_channels_str + " Trade,";
|
||||
}
|
||||
|
||||
if (_subscriptions_config.public_ticker)
|
||||
{
|
||||
_interested_channels_str = _interested_channels_str + " Ticker,";
|
||||
}
|
||||
|
||||
if (_interested_channels_str.empty())
|
||||
{
|
||||
_interested_channels_str = " Nil";
|
||||
}
|
||||
else
|
||||
{
|
||||
_interested_channels_str.pop_back();
|
||||
}
|
||||
|
||||
LOG_INFO("Subscribing channels | Symbol: {} | Configured channels:{}", _symbol_name, _interested_channels_str);
|
||||
}
|
||||
|
||||
void symbol_instruments::send_message(const char *msg_buff)
|
||||
{
|
||||
if (_session_state == session::state::connected)
|
||||
{
|
||||
// Symbol instruments class sends instrument level subs/unsub messages. In bootup state, this class will send the
|
||||
// subscription requests once the response for get instruments whitelist is received by the symbol handler which can
|
||||
// only happen once the session state is connected. In normal state if the session state is not connected, then symbol
|
||||
// instruments does not need to send sub/unsub messages because once the session state goes back to connected the
|
||||
// symbol instruments class will get reinitialized.
|
||||
_session->send_message(msg_buff);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG("Session not in connected state. Sub/Unsub message not sent | Symbol: {}", _symbol_name);
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_instruments::subscribe_to_private_book(const std::string &instrument_name)
|
||||
{
|
||||
auto request_string = "book." + instrument_name + "." + _subscription_interval;
|
||||
|
||||
_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();
|
||||
_writer.String(request_string.c_str());
|
||||
_writer.EndArray();
|
||||
_writer.EndObject();
|
||||
_writer.EndObject();
|
||||
|
||||
send_message(_str_buff.GetString());
|
||||
}
|
||||
|
||||
void symbol_instruments::unsubscribe_to_private_book(const std::string &instrument_name)
|
||||
{
|
||||
auto request_string = "book." + instrument_name + "." + _subscription_interval;
|
||||
|
||||
_str_buff.Clear();
|
||||
_writer.Reset(_str_buff);
|
||||
_writer.StartObject();
|
||||
_writer.Key("jsonrpc");
|
||||
_writer.String(_jsonrpc_version);
|
||||
_writer.Key("method");
|
||||
_writer.String(_private_unsubscribe_method.c_str());
|
||||
_writer.Key("id");
|
||||
_writer.Uint64((int)method_id::private_unsubscribe);
|
||||
_writer.Key("params");
|
||||
_writer.StartObject();
|
||||
_writer.Key("channels");
|
||||
_writer.StartArray();
|
||||
_writer.String(request_string.c_str());
|
||||
_writer.EndArray();
|
||||
_writer.EndObject();
|
||||
_writer.EndObject();
|
||||
|
||||
send_message(_str_buff.GetString());
|
||||
}
|
||||
|
||||
void symbol_instruments::resubscribe_to_private_book(const std::string &instrument_name)
|
||||
{
|
||||
LOG_INFO("Resubscribing Order book for Instrument: {}", instrument_name);
|
||||
_instruments_to_resubscribe.insert(instrument_name);
|
||||
unsubscribe_to_private_book(instrument_name);
|
||||
}
|
||||
|
||||
bool symbol_instruments::is_instrument_being_resubscribed(const std::string &instrument_name)
|
||||
{
|
||||
return _instruments_to_resubscribe.find(instrument_name) != _instruments_to_resubscribe.end();
|
||||
}
|
||||
|
||||
void symbol_instruments::check_if_channel_is_being_resubscribed(const std::string &channel_name)
|
||||
{
|
||||
for (auto &each_instrument_name : _instruments_to_resubscribe)
|
||||
{
|
||||
if (channel_name.find(each_instrument_name) != std::string::npos)
|
||||
{
|
||||
subscribe_to_private_book(each_instrument_name);
|
||||
_instruments_to_resubscribe.erase(each_instrument_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_instruments::sub_instruments()
|
||||
{
|
||||
for (auto &instrument_name : _instruments_to_subscribe)
|
||||
{
|
||||
LOG_DEBUG("Generating subscription message for instrument: {}", instrument_name);
|
||||
gen_subscribe_to_public_channels_msg(instrument_name);
|
||||
gen_subscribe_to_private_channels_msg(instrument_name);
|
||||
}
|
||||
|
||||
if (_public_subs_in_request_msg != 0)
|
||||
{
|
||||
send_subscribe_to_public_channels_msg();
|
||||
}
|
||||
|
||||
if (_private_subs_in_request_msg != 0)
|
||||
{
|
||||
send_subscribe_to_private_channels_msg();
|
||||
}
|
||||
|
||||
if (__builtin_expect(_all_sub_req_sent == false, false))
|
||||
{
|
||||
LOG_INFO("All Subscription requests sent | Symbol: {}", _symbol_name);
|
||||
_all_sub_req_sent = true;
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_instruments::gen_subscribe_to_public_channels_msg(const std::string &instrument_name)
|
||||
{
|
||||
std::string channel_name;
|
||||
if (_is_new_public_sub_msg == true)
|
||||
{
|
||||
_is_new_public_sub_msg = false;
|
||||
_public_sub_str_buff.Clear();
|
||||
_public_sub_writer.Reset(_public_sub_str_buff);
|
||||
_public_sub_writer.StartObject();
|
||||
_public_sub_writer.Key("jsonrpc");
|
||||
_public_sub_writer.String(_jsonrpc_version);
|
||||
_public_sub_writer.Key("method");
|
||||
_public_sub_writer.String(_public_subscribe_method.c_str());
|
||||
_public_sub_writer.Key("id");
|
||||
_public_sub_writer.Uint64((int)method_id::public_subscribe);
|
||||
_public_sub_writer.Key("params");
|
||||
_public_sub_writer.StartObject();
|
||||
_public_sub_writer.Key("channels");
|
||||
_public_sub_writer.StartArray();
|
||||
}
|
||||
|
||||
if (_subscriptions_config.public_quote == true)
|
||||
{
|
||||
channel_name = "quote." + instrument_name;
|
||||
_public_sub_writer.String(channel_name.c_str());
|
||||
++_public_subs_in_request_msg;
|
||||
}
|
||||
if (_subscriptions_config.public_trade == true)
|
||||
{
|
||||
channel_name = "trades." + instrument_name + "." + _subscription_interval;
|
||||
_public_sub_writer.String(channel_name.c_str());
|
||||
++_public_subs_in_request_msg;
|
||||
}
|
||||
if (_subscriptions_config.public_ticker == true)
|
||||
{
|
||||
channel_name = "ticker." + instrument_name + "." + _subscription_interval;
|
||||
_public_sub_writer.String(channel_name.c_str());
|
||||
++_public_subs_in_request_msg;
|
||||
}
|
||||
|
||||
if (_public_subs_in_request_msg >= 500)
|
||||
{
|
||||
send_subscribe_to_public_channels_msg();
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_instruments::send_subscribe_to_public_channels_msg()
|
||||
{
|
||||
_public_sub_writer.EndArray();
|
||||
_public_sub_writer.EndObject();
|
||||
_public_sub_writer.EndObject();
|
||||
|
||||
LOG_TRACE(
|
||||
"Symbol: {}, Total public channels: {}, Public subscription message: {}",
|
||||
_symbol_name, _public_subs_in_request_msg,
|
||||
string{_public_sub_str_buff.GetString()});
|
||||
send_message(_public_sub_str_buff.GetString());
|
||||
_is_new_public_sub_msg = true;
|
||||
_public_subs_in_request_msg = 0;
|
||||
}
|
||||
|
||||
void symbol_instruments::gen_subscribe_to_private_channels_msg(const std::string &instrument_name)
|
||||
{
|
||||
std::string channel_name;
|
||||
if (_is_new_private_sub_msg == true)
|
||||
{
|
||||
_is_new_private_sub_msg = false;
|
||||
_private_sub_str_buff.Clear();
|
||||
_private_sub_writer.Reset(_private_sub_str_buff);
|
||||
_private_sub_writer.StartObject();
|
||||
_private_sub_writer.Key("jsonrpc");
|
||||
_private_sub_writer.String(_jsonrpc_version);
|
||||
_private_sub_writer.Key("method");
|
||||
_private_sub_writer.String(_private_subscribe_method.c_str());
|
||||
_private_sub_writer.Key("id");
|
||||
_private_sub_writer.Uint64((int)method_id::private_subscribe);
|
||||
_private_sub_writer.Key("params");
|
||||
_private_sub_writer.StartObject();
|
||||
_private_sub_writer.Key("channels");
|
||||
_private_sub_writer.StartArray();
|
||||
}
|
||||
|
||||
if (_subscriptions_config.public_book == true)
|
||||
{
|
||||
channel_name = "book." + instrument_name + "." + _subscription_interval;
|
||||
_private_sub_writer.String(channel_name.c_str());
|
||||
++_private_subs_in_request_msg;
|
||||
}
|
||||
|
||||
if (_private_subs_in_request_msg >= 500)
|
||||
{
|
||||
send_subscribe_to_private_channels_msg();
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_instruments::send_subscribe_to_private_channels_msg()
|
||||
{
|
||||
_private_sub_writer.EndArray();
|
||||
_private_sub_writer.EndObject();
|
||||
_private_sub_writer.EndObject();
|
||||
|
||||
LOG_TRACE(
|
||||
"Symbol: {}, Total private channels: {}, Private subscription message: "
|
||||
"{}",
|
||||
_symbol_name, _private_subs_in_request_msg,
|
||||
string{_private_sub_str_buff.GetString()});
|
||||
send_message(_private_sub_str_buff.GetString());
|
||||
_is_new_private_sub_msg = true;
|
||||
_private_subs_in_request_msg = 0;
|
||||
}
|
||||
|
||||
void symbol_instruments::unsub_instruments(const instruments_set &set_of_instruments)
|
||||
{
|
||||
for (auto &instrument_name : set_of_instruments)
|
||||
{
|
||||
gen_unsubscribe_to_public_channels_msg(instrument_name);
|
||||
gen_unsubscribe_to_private_channels_msg(instrument_name);
|
||||
}
|
||||
|
||||
if (_public_unsubs_in_request_msg != 0)
|
||||
{
|
||||
send_unsubscribe_to_public_channels_msg();
|
||||
}
|
||||
|
||||
if (_private_unsubs_in_request_msg != 0)
|
||||
{
|
||||
send_unsubscribe_to_private_channels_msg();
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_instruments::gen_unsubscribe_to_public_channels_msg(const std::string &instrument_name)
|
||||
{
|
||||
std::string channel_name;
|
||||
if (_is_new_public_unsub_msg == true)
|
||||
{
|
||||
_is_new_public_unsub_msg = false;
|
||||
_public_unsub_str_buff.Clear();
|
||||
_public_unsub_writer.Reset(_public_unsub_str_buff);
|
||||
_public_unsub_writer.StartObject();
|
||||
_public_unsub_writer.Key("jsonrpc");
|
||||
_public_unsub_writer.String(_jsonrpc_version);
|
||||
_public_unsub_writer.Key("method");
|
||||
_public_unsub_writer.String(_public_unsubscribe_method.c_str());
|
||||
_public_unsub_writer.Key("id");
|
||||
_public_unsub_writer.Uint64((int)method_id::public_unsubscribe);
|
||||
_public_unsub_writer.Key("params");
|
||||
_public_unsub_writer.StartObject();
|
||||
_public_unsub_writer.Key("channels");
|
||||
_public_unsub_writer.StartArray();
|
||||
}
|
||||
|
||||
if (_subscriptions_config.public_quote == true)
|
||||
{
|
||||
channel_name = "quote." + instrument_name;
|
||||
_public_unsub_writer.String(channel_name.c_str());
|
||||
++_public_unsubs_in_request_msg;
|
||||
}
|
||||
if (_subscriptions_config.public_trade == true)
|
||||
{
|
||||
channel_name = "trades." + instrument_name + "." + _subscription_interval;
|
||||
_public_unsub_writer.String(channel_name.c_str());
|
||||
++_public_unsubs_in_request_msg;
|
||||
}
|
||||
if (_subscriptions_config.public_ticker == true)
|
||||
{
|
||||
channel_name = "ticker." + instrument_name + "." + _subscription_interval;
|
||||
_public_unsub_writer.String(channel_name.c_str());
|
||||
++_public_unsubs_in_request_msg;
|
||||
}
|
||||
|
||||
if (_public_subs_in_request_msg >= 500)
|
||||
{
|
||||
send_unsubscribe_to_public_channels_msg();
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_instruments::send_unsubscribe_to_public_channels_msg()
|
||||
{
|
||||
_public_unsub_writer.EndArray();
|
||||
_public_unsub_writer.EndObject();
|
||||
_public_unsub_writer.EndObject();
|
||||
|
||||
LOG_TRACE(
|
||||
"Symbol: {}, Total public channels: {}, Public unsubscription message: "
|
||||
"{}",
|
||||
_symbol_name, _public_unsubs_in_request_msg,
|
||||
string{_public_unsub_str_buff.GetString()});
|
||||
send_message(_public_unsub_str_buff.GetString());
|
||||
_is_new_public_unsub_msg = true;
|
||||
_public_unsubs_in_request_msg = 0;
|
||||
}
|
||||
|
||||
void symbol_instruments::gen_unsubscribe_to_private_channels_msg(const std::string &instrument_name)
|
||||
{
|
||||
std::string channel_name;
|
||||
if (_is_new_private_unsub_msg == true)
|
||||
{
|
||||
_is_new_private_unsub_msg = false;
|
||||
_private_unsub_str_buff.Clear();
|
||||
_private_unsub_writer.Reset(_private_unsub_str_buff);
|
||||
_private_unsub_writer.StartObject();
|
||||
_private_unsub_writer.Key("jsonrpc");
|
||||
_private_unsub_writer.String(_jsonrpc_version);
|
||||
_private_unsub_writer.Key("method");
|
||||
_private_unsub_writer.String(_private_unsubscribe_method.c_str());
|
||||
_private_unsub_writer.Key("id");
|
||||
_private_unsub_writer.Uint64((int)method_id::private_unsubscribe);
|
||||
_private_unsub_writer.Key("params");
|
||||
_private_unsub_writer.StartObject();
|
||||
_private_unsub_writer.Key("channels");
|
||||
_private_unsub_writer.StartArray();
|
||||
}
|
||||
|
||||
if (_subscriptions_config.public_book == true)
|
||||
{
|
||||
channel_name = "book." + instrument_name + "." + _subscription_interval;
|
||||
_private_unsub_writer.String(channel_name.c_str());
|
||||
++_private_unsubs_in_request_msg;
|
||||
}
|
||||
|
||||
if (_private_subs_in_request_msg >= 500)
|
||||
{
|
||||
send_subscribe_to_private_channels_msg();
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_instruments::send_unsubscribe_to_private_channels_msg()
|
||||
{
|
||||
_private_unsub_writer.EndArray();
|
||||
_private_unsub_writer.EndObject();
|
||||
_private_unsub_writer.EndObject();
|
||||
|
||||
LOG_TRACE(
|
||||
"Symbol: {}, Total private channels: {}, Private unsubscription "
|
||||
"message: {}",
|
||||
_symbol_name, _private_unsubs_in_request_msg,
|
||||
string{_private_unsub_str_buff.GetString()});
|
||||
send_message(_private_unsub_str_buff.GetString());
|
||||
_is_new_private_unsub_msg = true;
|
||||
_private_unsubs_in_request_msg = 0;
|
||||
}
|
||||
|
||||
void symbol_instruments::reset_symbol_instruments()
|
||||
{
|
||||
LOG_TRACE("Reconnection occured. Resetting symbol instrument | Symbol: {}", _symbol_name);
|
||||
_white_list.clear();
|
||||
_instruments_to_resubscribe.clear();
|
||||
_instruments_to_subscribe.clear();
|
||||
_all_sub_req_sent = false;
|
||||
}
|
||||
|
||||
std::ostream &symbol_instruments::print_all_books(std::ostream &out)
|
||||
{
|
||||
std::vector<std::string> ordered_white_list;
|
||||
|
||||
for (auto each_instrument_data : _white_list)
|
||||
{
|
||||
ordered_white_list.emplace_back(each_instrument_data.first);
|
||||
}
|
||||
|
||||
std::sort(ordered_white_list.begin(), ordered_white_list.end());
|
||||
|
||||
for (auto each_instrument : ordered_white_list)
|
||||
{
|
||||
out << "-----------------------------------------------------------------" << std::endl;
|
||||
out << "Instrument: " << each_instrument << std::endl;
|
||||
out << _white_list.at(each_instrument);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
#ifndef SYMBOL_INSTRUMENTS_H
|
||||
#define SYMBOL_INSTRUMENTS_H
|
||||
|
||||
#include "deribit_listener.h"
|
||||
#include "document_to_string.h"
|
||||
#include "flat_hash_map/bytell_hash_map.hpp"
|
||||
#include "instrument_data.h"
|
||||
#include "rapidjson/document.h"
|
||||
#include "session.h"
|
||||
|
||||
#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 */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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,32 @@
|
||||
#ifndef WRITER_H
|
||||
#define WRITER_H
|
||||
|
||||
#include "deribit_emumba/deribit_utils.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 std::string to_string() {
|
||||
return std::string{str_buff.GetString()};
|
||||
}
|
||||
|
||||
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
|
||||
@@ -24,10 +24,11 @@ target_include_directories(
|
||||
sparkd PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/src/server/sparkd/cmds/spartan
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src/server/sparkd/cmds/spartan ${emumba-deribit-connector-library_INCLUDE_DIRS})
|
||||
${CMAKE_BINARY_DIR}/src/server/sparkd/cmds/spartan
|
||||
)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(emumba-deribit-connector-library REQUIRED emumba-deribit-connector)
|
||||
# find_package(PkgConfig REQUIRED)
|
||||
# pkg_check_modules(emumba-deribit-connector-library REQUIRED emumba-deribit-connector)
|
||||
|
||||
# This dir contains rapidjsonConfig.cmake
|
||||
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules)
|
||||
@@ -78,7 +79,7 @@ target_link_libraries(sparkd PUBLIC
|
||||
libspark
|
||||
binance_execution
|
||||
databento::databento
|
||||
${emumba-deribit-connector-library_LIBRARIES}
|
||||
deribit_emumba
|
||||
ssl
|
||||
cryptopp
|
||||
tbb
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Run.h"
|
||||
#include "binance_execution/execution_gateway_provider.h"
|
||||
#include "lib/api/md/MarketDataConfig.pb.h"
|
||||
#include "lib/api/public/ServiceContext.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/ports.pb.h"
|
||||
@@ -21,6 +22,7 @@
|
||||
#include "lib/orderman/orders/algo/algo_orders/ProRataAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/ScratchAlgo.h"
|
||||
#include "lib/os/os.h"
|
||||
#include "lib/persistence/IDGenerator.h"
|
||||
#include "lib/persistence/PersistenceServiceServer.h"
|
||||
#include "lib/plugin/SparkServices.h"
|
||||
#include "lib/plugin/plugin.h"
|
||||
@@ -31,8 +33,10 @@
|
||||
#include "md/cmds/Run.h"
|
||||
#include "server/sparkd/algo/AlgoDefinitionServiceServer.h"
|
||||
#include "server/sparkd/analytics/AnalyticsServiceServer.h"
|
||||
#include "server/sparkd/cmds/md/cmds/Cleanup.h"
|
||||
#include "server/sparkd/http/WebServer.h"
|
||||
#include "server/sparkd/md/MarketDataAuxiliaryServiceServer.h"
|
||||
#include "server/sparkd/md/MarketDataGateway.h"
|
||||
#include "server/sparkd/md/platforms/mdp3_onixs/TradeSummarySignalDispatcher.h"
|
||||
#include "server/sparkd/md/recording/MDRecorder.h"
|
||||
#include "server/sparkd/position/MarginServiceServer.h"
|
||||
@@ -101,7 +105,11 @@ static void execute() {
|
||||
|
||||
bts::md::MBOProcessor mbo_processor;
|
||||
bts::md::TradeSummarySignalDispatcher tss_dispatcher;
|
||||
md::cmds::Run::run();
|
||||
md::cmds::Cleanup::execute();
|
||||
bts::md::MarketDataGateway md_gateway{
|
||||
config::get<api::md::MarketDataConfig>(),
|
||||
persistence::IDGenerator::instance_ptr()};
|
||||
md_gateway.start();
|
||||
|
||||
api::synthetic::SyntheticSecurityService synthetic_service(sc);
|
||||
bts::md::MDClient mdclient(
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "DeribitDemo.h"
|
||||
#include "DeribitDemoConfiguration.h"
|
||||
#include "deribit_emumba/deribit_exception.h"
|
||||
#include "deribit_emumba/deribit_handler.h"
|
||||
#include "deribit_emumba/deribit_listener.h"
|
||||
#include "deribit_emumba/trading_session_interface.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "server/sparkd/cmds/crypto/DeribitDemoListener.h"
|
||||
#include <csignal>
|
||||
#include <emumba-deribit-connector/deribit_exception.h>
|
||||
#include <emumba-deribit-connector/deribit_handler.h>
|
||||
#include <emumba-deribit-connector/deribit_listener.h>
|
||||
#include <emumba-deribit-connector/trading_session_interface.h>
|
||||
#include <list>
|
||||
|
||||
namespace bts::cmds {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
#include "RapidJsonReader.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "rapidjson/istreamwrapper.h"
|
||||
#include <emumba-deribit-connector/deribit_utils.h>
|
||||
#include "deribit_emumba/deribit_utils.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <optional>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/core/FMT_IMPL.h"
|
||||
#include "deribit_emumba/deribit_utils.h"
|
||||
|
||||
#include <emumba-deribit-connector/deribit_utils.h>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
#include "LiveLadderDemo.h"
|
||||
#include "lib/api/md/MarketDataConfig.pb.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/persistence/IDGenerator.h"
|
||||
#include "server/sparkd/md/MarketDataGateway.h"
|
||||
#include "server/sparkd/md/MarketDataRegistration.h"
|
||||
#include "server/sparkd/md/listeners/LiveLadderPrinter.h"
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace bts::cmds {
|
||||
|
||||
LiveLadderDemo::LiveLadderDemo(const api::md::MarketDataConfig &) {
|
||||
using bts::api::md::MarketDataConfig;
|
||||
using bts::persistence::IDGenerator;
|
||||
|
||||
LiveLadderDemo::LiveLadderDemo(const MarketDataConfig &config) {
|
||||
char ssl_cert_file[] = "SSL_CERT_FILE=/etc/ssl/cert/ca-certificates.crt";
|
||||
putenv(ssl_cert_file);
|
||||
|
||||
md::MarketDataGateway gw;
|
||||
md::MarketDataGateway gw{config, IDGenerator::instance_ptr()};
|
||||
md::listeners::LiveLadderPrinter ladder_printer;
|
||||
gw.run();
|
||||
gw.start();
|
||||
|
||||
const std::size_t n_loops = 16;
|
||||
for (std::size_t n = 0; n < n_loops; n++) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Run.h"
|
||||
#include "Cleanup.h"
|
||||
#include "lib/api/md/MarketDataConfig.pb.h"
|
||||
#include "lib/config/protos/market_data.pb.h"
|
||||
#include "lib/domain/SecurityCriteria.h"
|
||||
#include "lib/logging/logging.h"
|
||||
@@ -7,6 +8,7 @@
|
||||
#include "lib/md/UserSymbol.h"
|
||||
#include "lib/md/relay/RelayRecorder.h"
|
||||
#include "lib/os/os.h"
|
||||
#include "lib/persistence/IDGenerator.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/thread_manager.h"
|
||||
#include "server/sparkd/md/MarketDataGateway.h"
|
||||
@@ -14,6 +16,7 @@
|
||||
#include "server/sparkd/md/platforms/ice_onixs/ICEMain.h"
|
||||
#include "server/sparkd/md/platforms/mdp3_onixs/MDP3Main.h"
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace bts::cmds::md::cmds {
|
||||
|
||||
@@ -91,9 +94,14 @@ static void run_onixs(
|
||||
static void run_md_gwy() {
|
||||
LOG(md, Info, "Run::run_md_gwy t_id:{} t_name:{}", os::get_thread_id(),
|
||||
os::get_thread_name());
|
||||
bts::md::MarketDataGateway gwy = {};
|
||||
// this keeps going in a loop
|
||||
gwy.run();
|
||||
bts::md::MarketDataGateway gwy{
|
||||
config::get<api::md::MarketDataConfig>(),
|
||||
persistence::IDGenerator::instance_ptr()};
|
||||
gwy.start();
|
||||
while (true) {
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(2s);
|
||||
}
|
||||
}
|
||||
|
||||
void Run::run() {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "EMExecutionGateway.h"
|
||||
#include "deribit_emumba/market_data_common.h"
|
||||
#include "deribit_emumba/order_entry_common.h"
|
||||
#include "deribit_emumba/price.h"
|
||||
#include "lib/api/service_helpers/AdminServiceUtils.h"
|
||||
#include "lib/api/services/AdminService.pb.h"
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
@@ -41,9 +44,6 @@
|
||||
#include <boost/proto/transform/impl.hpp>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <emumba-deribit-connector/market_data_common.h>
|
||||
#include <emumba-deribit-connector/order_entry_common.h>
|
||||
#include <emumba-deribit-connector/price.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tbb/concurrent_queue.h>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "IMarketDataProvider.h"
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
IMarketDataProvider::~IMarketDataProvider() = default;
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/api/domain/Venue.pb.h"
|
||||
#include "server/sparkd/md/IMarketDataSource.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
/**
|
||||
* @brief This is a single function interface, whose sole purpose is to provide a list of market
|
||||
* data sources (per venue). A good example implementation is the `CCApiMarketDataProvider`, which
|
||||
* reads `MarketDataConfig` and returns an `IMarketDataSource` for each venue configuration (e.g.,
|
||||
* `bts::api::md::BinanceMarketDataConfig`, `bts::api::md::CoinbaseMarketDataConfig`, etc.).
|
||||
*/
|
||||
class IMarketDataProvider {
|
||||
public:
|
||||
virtual ~IMarketDataProvider();
|
||||
|
||||
virtual std::vector<
|
||||
std::pair<bts::api::domain::Venue, std::shared_ptr<IMarketDataSource>>>
|
||||
get_sources() = 0;
|
||||
};
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "IMarketDataSource.h"
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
IMarketDataSource::~IMarketDataSource() = default;
|
||||
|
||||
void IMarketDataSource::stop() {}
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
class IMarketDataListener;
|
||||
|
||||
/**
|
||||
* @brief This interface represents a source of market data. Call `IMarketDataSource::start` to
|
||||
* connect an `IMarketDataListener` to the source and begin receiving market data.
|
||||
*/
|
||||
class IMarketDataSource {
|
||||
public:
|
||||
virtual ~IMarketDataSource();
|
||||
virtual void start(std::shared_ptr<IMarketDataListener> listener) = 0;
|
||||
virtual void stop();
|
||||
};
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -1,69 +1,40 @@
|
||||
#include "MarketDataGateway.h"
|
||||
#include "lib/api/domain/Venue.pb.h"
|
||||
#include "lib/api/md/EmumbaDeribitMarketDataConfig.pb.h"
|
||||
#include "lib/api/md/MarketDataConfig.pb.h"
|
||||
#include "lib/api/md/MarketDataConfigUtils.h"
|
||||
#include "lib/api/md/MarketDataReplayConfig.pb.h"
|
||||
#include "lib/collections/Contains.h"
|
||||
#include "lib/config/protos/system_config.pb.h"
|
||||
#include "lib/core/FMT.h"
|
||||
#include "lib/domain/DomainSecurityUtils.h"
|
||||
#include "lib/domain/SecurityID.h"
|
||||
#include "lib/domain_model/Security.pb.h"
|
||||
#include "lib/io/ProtoPrinter.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/md/GeneralMarketDataListener.h"
|
||||
#include "lib/md/GeneralSecurityIndex.h"
|
||||
#include "lib/md/IMarketDataListener.h"
|
||||
#include "lib/md/MDProducer.h"
|
||||
#include "lib/os/os.h"
|
||||
#include "lib/persistence/IDGenerator.h"
|
||||
#include "lib/utils/Dispatcher.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/bts_fatal.h"
|
||||
#include "lib/utils/thread_manager.h"
|
||||
#include "server/sparkd/md/MarketDataListenerID.h"
|
||||
#include "server/sparkd/md/MarketDataRegistration.h"
|
||||
#include "server/sparkd/md/MarketDataRegistrationError.h"
|
||||
#include "server/sparkd/md/MarketDataRegistrationFactory.h"
|
||||
#include "server/sparkd/md/MarketDataSubscription.h"
|
||||
#include "server/sparkd/md/binance/BinanceSubscription.h"
|
||||
#include "server/sparkd/md/binance_coin_futures/binance_coin_futures_subscription.h"
|
||||
#include "server/sparkd/md/binance_us/BinanceUsSubscription.h"
|
||||
#include "server/sparkd/md/binance_usds_futures/BinanceUsdsFuturesSubscription.h"
|
||||
#include "server/sparkd/md/cboe/CboeMarketDataHandler.h"
|
||||
#include "server/sparkd/md/coinbase/CoinbaseSubscription.h"
|
||||
#include "server/sparkd/md/deribit/DeribitSubscription.h"
|
||||
#include "server/sparkd/md/emumba/EmumbaDeribitData.h"
|
||||
#include "server/sparkd/md/ftx/FTXSubscription.h"
|
||||
#include "server/sparkd/md/glbx/GlbxMarketDataHandler.h"
|
||||
#include "server/sparkd/md/okx/OkxSubscription.h"
|
||||
#include "server/sparkd/md/platforms/mdp3_onixs/MDP3Main.h"
|
||||
#include "server/sparkd/md/recording/MDReplayer.h"
|
||||
#include "server/sparkd/md/taifex/TaifexDataGateway.h"
|
||||
#include "server/sparkd/md/IMarketDataProvider.h"
|
||||
#include "server/sparkd/md/OnixsMarketDataProvider.h"
|
||||
#include "server/sparkd/md/ReplayMarketDataProvider.h"
|
||||
#include "server/sparkd/md/ccapi/CCApiMarketDataProvider.h"
|
||||
#include "server/sparkd/md/databento/DatabentoMarketDataProvider.h"
|
||||
#include "server/sparkd/md/emumba/EmumbaMarketDataProvider.h"
|
||||
#include "server/sparkd/md/taifex/TaifexMarketDataProvider.h"
|
||||
|
||||
#include <bits/stdint-uintn.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <ccapi_cpp/ccapi_event_dispatcher.h>
|
||||
#include <emumba-deribit-connector/deribit_utils.h>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
using bts::api::md::MarketDataConfig;
|
||||
using bts::config::SystemConfig;
|
||||
//using bts::domain::Security;
|
||||
using bts::domain::SecurityID;
|
||||
using bts::io::ProtoPrinter;
|
||||
using bts::persistence::IDGenerator;
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
using std::unordered_map;
|
||||
using std::vector;
|
||||
|
||||
using Path = boost::filesystem::path;
|
||||
|
||||
static bool validate(const MarketDataConfig &cfg, vector<string> &errs) {
|
||||
auto orig_n_errs = errs.size();
|
||||
if (!cfg.deribit().empty() && !cfg.emumba_deribit().empty()) {
|
||||
@@ -73,363 +44,68 @@ static bool validate(const MarketDataConfig &cfg, vector<string> &errs) {
|
||||
return errs.size() == orig_n_errs;
|
||||
}
|
||||
|
||||
struct MarketDataListenerData {
|
||||
MarketDataListenerData() = default;
|
||||
MarketDataListenerData(const MarketDataListenerData &) noexcept = delete;
|
||||
MarketDataListenerData(MarketDataListenerData &&) noexcept = default;
|
||||
MarketDataListenerData &operator=(const MarketDataListenerData &) noexcept =
|
||||
delete;
|
||||
MarketDataListenerData &operator=(MarketDataListenerData &&) noexcept =
|
||||
default;
|
||||
~MarketDataListenerData() = default;
|
||||
|
||||
MarketDataListenerID id;
|
||||
MarketDataSubscription subscription;
|
||||
};
|
||||
|
||||
struct MDReplayData {
|
||||
bts::api::md::MarketDataReplayConfig replay_conf;
|
||||
bts::api::Venue venue;
|
||||
bts::md::GeneralSecurityIndex sec_index;
|
||||
bts::md::MDProducer md_producer;
|
||||
bts::md::recording::MDReplayer md_replayer;
|
||||
|
||||
MDReplayData(const bts::api::md::MarketDataReplayConfig &replay_conf)
|
||||
: replay_conf(replay_conf), venue(replay_conf.venue()), sec_index(),
|
||||
md_replayer(replay_conf.venue(), sec_index, md_producer, replay_conf.replay_speed()) {}
|
||||
};
|
||||
|
||||
struct VenueListenerData {
|
||||
bts::md::GeneralSecurityIndex sec_index;
|
||||
bts::md::MDProducer md_producer;
|
||||
std::shared_ptr<bts::md::IMarketDataListener> listener;
|
||||
utils::DispatcherTimer timer;
|
||||
|
||||
VenueListenerData(bts::api::Venue venue)
|
||||
: sec_index(),
|
||||
listener(std::make_shared<bts::md::GeneralMarketDataListener>(
|
||||
sec_index, md_producer, venue)),
|
||||
timer(utils::Dispatcher::background(), 200, [&]() {
|
||||
md_producer.heartbeat();
|
||||
}) {}
|
||||
};
|
||||
|
||||
struct MarketDataGateway::PImpl {
|
||||
api::md::MarketDataConfig md_config =
|
||||
config::get<api::md::MarketDataConfig>();
|
||||
ccapi::EventDispatcher dispatcher;
|
||||
MarketDataRegistrationFactory registration_factory;
|
||||
std::shared_ptr<IDGenerator> sec_id_store = IDGenerator::instance_ptr();
|
||||
unordered_map<MarketDataListenerID, MarketDataListenerData> listeners;
|
||||
std::vector<std::unique_ptr<MDReplayData>> replays = {};
|
||||
// we create these and pass them to the cc md sources
|
||||
std::vector<std::shared_ptr<VenueListenerData>> venue_listeners = {};
|
||||
struct MdSink {
|
||||
GeneralSecurityIndex sec_index;
|
||||
MDProducer md_producer;
|
||||
shared_ptr<IMarketDataListener> listener;
|
||||
utils::DispatcherTimer timer;
|
||||
|
||||
MdSink(api::Venue venue, IDGenerator *sec_id_store)
|
||||
: sec_index(), listener(std::make_shared<GeneralMarketDataListener>(
|
||||
sec_index, md_producer, venue, sec_id_store)),
|
||||
timer(utils::Dispatcher::background(), 200, [&]() {
|
||||
md_producer.heartbeat();
|
||||
}) {}
|
||||
};
|
||||
|
||||
MarketDataConfig config;
|
||||
shared_ptr<IDGenerator> sec_id_store;
|
||||
vector<unique_ptr<IMarketDataProvider>> providers;
|
||||
vector<unique_ptr<MdSink>> sinks;
|
||||
vector<unique_ptr<IMarketDataSource>> sources;
|
||||
|
||||
PImpl(MarketDataConfig config, shared_ptr<IDGenerator> sec_id_store)
|
||||
: config(std::move(config)), sec_id_store(std::move(sec_id_store)) {
|
||||
providers.emplace_back(std::make_unique<CCApiMarketDataProvider>(
|
||||
this->config, this->sec_id_store));
|
||||
providers.emplace_back(std::make_unique<EmumbaMarketDataProvider>(
|
||||
this->config, this->sec_id_store));
|
||||
providers.emplace_back(
|
||||
std::make_unique<OnixsMarketDataProvider>(this->config));
|
||||
providers.emplace_back(std::make_unique<DatabentoMarketDataProvider>(
|
||||
this->config, this->sec_id_store));
|
||||
providers.emplace_back(std::make_unique<TaifexMarketDataProvider>(
|
||||
this->config, this->sec_id_store));
|
||||
providers.emplace_back(std::make_unique<ReplayMarketDataProvider>(
|
||||
this->config, this->sec_id_store));
|
||||
}
|
||||
};
|
||||
|
||||
MarketDataGateway::MarketDataGateway()
|
||||
: me_(std::make_unique<MarketDataGateway::PImpl>()) {}
|
||||
MarketDataGateway::MarketDataGateway(
|
||||
MarketDataConfig config, shared_ptr<IDGenerator> sec_id_store)
|
||||
: me_(std::make_unique<MarketDataGateway::PImpl>(
|
||||
std::move(config), std::move(sec_id_store))) {}
|
||||
|
||||
MarketDataGateway::~MarketDataGateway() = default;
|
||||
|
||||
// starts the market data sources and then keeps the function going
|
||||
void MarketDataGateway::run() {
|
||||
void MarketDataGateway::start() {
|
||||
vector<string> cfg_errs;
|
||||
if (!validate(me_->md_config, cfg_errs)) {
|
||||
if (!validate(me_->config, cfg_errs)) {
|
||||
BTS_FATAL_MSGF("Invalid MarketDataConfig:", FMT_JOIN(cfg_errs, "\n"));
|
||||
return;
|
||||
}
|
||||
LOG(md, Info, "MarketDataGateway::run starting t_id:{} t_name:{}",
|
||||
os::get_thread_id(), os::get_thread_name());
|
||||
_start_cc(me_->md_config);
|
||||
if (!me_->md_config.emumba_deribit().empty()) {
|
||||
bts_assert(me_->md_config.emumba_deribit().size() == 1);
|
||||
_start_emumba_deribit(me_->md_config.emumba_deribit().at(0));
|
||||
}
|
||||
start_glbx(me_->md_config);
|
||||
start_taifex(me_->md_config);
|
||||
start_cboe(me_->md_config);
|
||||
_start_replays(me_->md_config);
|
||||
while (true) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
LOG(md, Info, "MarketDataGateway::run ending t_id:{} t_name:{}",
|
||||
os::get_thread_id(), os::get_thread_name());
|
||||
}
|
||||
|
||||
// CCAPI //////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
MarketDataRegistration MarketDataGateway::_start_cc(
|
||||
const MarketDataConfig &cfg) noexcept {
|
||||
LOG(md, Info, "MarketDataGateway::_start_cc starting t_id:{} t_name:{}",
|
||||
os::get_thread_id(), os::get_thread_name());
|
||||
auto reg = me_->registration_factory.create();
|
||||
bts_assert(reg.listener_id.has_value());
|
||||
bts_assert(!CONTAINS_KEY(me_->listeners, *reg.listener_id));
|
||||
LOG(md, Info, "MarketDataGateway subscribing to data:\n{}",
|
||||
ProtoPrinter::to_string(cfg));
|
||||
|
||||
vector<string> cfg_errs;
|
||||
if (!validate(cfg, cfg_errs)) {
|
||||
return MarketDataRegistration{
|
||||
{MarketDataRegistrationError::Type::CONFIG_ERROR,
|
||||
fmt::to_string(fmt::join(cfg_errs, "\n"))}};
|
||||
}
|
||||
|
||||
MarketDataListenerData &listener_data = me_->listeners[*reg.listener_id];
|
||||
listener_data.id = *reg.listener_id;
|
||||
|
||||
// FTX
|
||||
for (const auto &ftx : cfg.ftx()) {
|
||||
auto p = me_->venue_listeners.emplace_back(
|
||||
std::make_shared<VenueListenerData>(bts::api::VENUE_FTX));
|
||||
listener_data.subscription.add(ftx::FTXSubscription{
|
||||
&me_->dispatcher, ftx, me_->sec_id_store.get(), p->listener.get()});
|
||||
}
|
||||
|
||||
// Binance
|
||||
for (const auto &binance : cfg.binance()) {
|
||||
auto p = me_->venue_listeners.emplace_back(
|
||||
std::make_shared<VenueListenerData>(bts::api::VENUE_BINANCE));
|
||||
listener_data.subscription.add(binance::BinanceSubscription{
|
||||
&me_->dispatcher, binance, me_->sec_id_store.get(),
|
||||
p->listener.get()});
|
||||
}
|
||||
|
||||
// Binance Coin Futures
|
||||
for (const auto &binance_cfs : cfg.binance_coin_futures()) {
|
||||
auto p =
|
||||
me_->venue_listeners.emplace_back(std::make_shared<VenueListenerData>(
|
||||
bts::api::VENUE_BINANCE_COIN_FUTURES));
|
||||
listener_data.subscription.add(
|
||||
binance_coin_futures::BinanceCoinFuturesSubscription{
|
||||
&me_->dispatcher, binance_cfs, me_->sec_id_store.get(),
|
||||
p->listener.get()});
|
||||
}
|
||||
|
||||
// Binance US
|
||||
for (const auto &binance_us : cfg.binance_us()) {
|
||||
auto p = me_->venue_listeners.emplace_back(
|
||||
std::make_shared<VenueListenerData>(bts::api::VENUE_BINANCE_US));
|
||||
listener_data.subscription.add(binance_us::BinanceUsSubscription{
|
||||
&me_->dispatcher, binance_us, me_->sec_id_store.get(),
|
||||
p->listener.get()});
|
||||
}
|
||||
// Coinbase
|
||||
for (const auto &coinbase : cfg.coinbase()) {
|
||||
auto p = me_->venue_listeners.emplace_back(
|
||||
std::make_shared<VenueListenerData>(bts::api::VENUE_COINBASE));
|
||||
listener_data.subscription.add(coinbase::CoinbaseSubscription{
|
||||
&me_->dispatcher, coinbase, me_->sec_id_store.get(),
|
||||
p->listener.get()});
|
||||
}
|
||||
// Binance USDS Futures
|
||||
for (const auto &binance_ufs : cfg.binance_usds_futures()) {
|
||||
auto p =
|
||||
me_->venue_listeners.emplace_back(std::make_shared<VenueListenerData>(
|
||||
bts::api::VENUE_BINANCE_USDS_FUTURES));
|
||||
listener_data.subscription.add(
|
||||
binance_usds_futures::BinanceUsdsFuturesSubscription{
|
||||
&me_->dispatcher, binance_ufs, me_->sec_id_store.get(),
|
||||
p->listener.get()});
|
||||
}
|
||||
|
||||
// Deribit
|
||||
for (const auto &deribit : cfg.deribit()) {
|
||||
auto p = me_->venue_listeners.emplace_back(
|
||||
std::make_shared<VenueListenerData>(bts::api::VENUE_DERIBIT));
|
||||
listener_data.subscription.add(deribit::DeribitSubscription{
|
||||
&me_->dispatcher, deribit, me_->sec_id_store.get(),
|
||||
p->listener.get()});
|
||||
}
|
||||
|
||||
// Okx
|
||||
for (const auto &okx : cfg.okx()) {
|
||||
auto p = me_->venue_listeners.emplace_back(
|
||||
std::make_shared<VenueListenerData>(bts::api::VENUE_OKX));
|
||||
listener_data.subscription.add(okx::OkxSubscription{
|
||||
&me_->dispatcher, okx, me_->sec_id_store.get(), p->listener.get()});
|
||||
}
|
||||
|
||||
LOG(md, Info, "MarketDataGateway::_start_cc ending t_id:{} t_name:{}",
|
||||
os::get_thread_id(), os::get_thread_name());
|
||||
return reg;
|
||||
}
|
||||
|
||||
void MarketDataGateway::unsubscribe(MarketDataListenerID id) {
|
||||
LOG(md, Info, "Unsubscribing listener {}", id);
|
||||
me_->listeners.erase(id);
|
||||
}
|
||||
/// glbx //////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
void MarketDataGateway::start_glbx(const bts::api::md::MarketDataConfig &cfg){
|
||||
for (const auto &glbx : cfg.glbx()) {
|
||||
if(!glbx.use_databento()){
|
||||
_start_onix_glbx(glbx);
|
||||
return;
|
||||
for (auto &provider : me_->providers) {
|
||||
for (auto &[venue, source] : provider->get_sources()) {
|
||||
const auto v = domain::DomainSecurityUtils::convert(venue);
|
||||
auto &sink = me_->sinks.emplace_back(
|
||||
std::make_unique<PImpl::MdSink>(v, me_->sec_id_store.get()));
|
||||
source->start(sink->listener);
|
||||
}
|
||||
auto p = me_->venue_listeners.emplace_back(
|
||||
std::make_shared<VenueListenerData>(bts::api::VENUE_GLBX));
|
||||
// Capture `p->listener` and `me_->sec_id_store` by value
|
||||
auto listener = p->listener;
|
||||
auto sec_id_store = me_->sec_id_store;
|
||||
std::string tid_name = "glbx";
|
||||
bts::threading::make_thread(tid_name, [listener, sec_id_store, glbx](){
|
||||
api::md::glbx::GlbxMarketDatahandler::run(
|
||||
listener,
|
||||
sec_id_store,
|
||||
glbx);
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
static void run_onixs() {
|
||||
LOG(md, Info, "MarketDataGateway::run_onixs t_id:{} t_name",
|
||||
os::get_thread_id(), os::get_thread_name());
|
||||
bts::md::MBOWriter *mbo = nullptr;
|
||||
bts::md::TradeSummarySignalDispatcher *tssd = nullptr;
|
||||
std::vector<std::shared_ptr<bts::md::IMDPlatform>> platforms;
|
||||
// get all glbx symbols
|
||||
auto cme_symbols = SymbolLoader::load(
|
||||
bts::domain::SecurityCriteria::create({api::domain::VENUE_GLBX}));
|
||||
if (!cme_symbols.empty()) {
|
||||
auto *main = new bts::md::MDP3Main();
|
||||
main->start({cme_symbols.begin(), cme_symbols.end()}, mbo, tssd);
|
||||
platforms.emplace_back(main);
|
||||
}
|
||||
// deribit
|
||||
// auto *d_platform = new bts::md::EmMDListener()
|
||||
// Timers
|
||||
auto &disp = utils::Dispatcher::current();
|
||||
utils::DispatcherTimer heartbeat_timer(disp, 250, [&]() {
|
||||
for (auto &p : platforms)
|
||||
p->send_heartbeats();
|
||||
});
|
||||
heartbeat_timer.start();
|
||||
|
||||
disp.run();
|
||||
}
|
||||
|
||||
void MarketDataGateway::_start_onix_glbx(
|
||||
bts::api::md::GlbxMarketDataConfig cfg) {
|
||||
LOG(md, Info,
|
||||
"MarketDataGateway::_start_onix_glbx starting t_id:{} t_name:{}. "
|
||||
"cfg:\n{}",
|
||||
os::get_thread_id(), os::get_thread_name(), cfg.DebugString());
|
||||
bts::threading::make_thread("Onix CME", [&]() {
|
||||
bts::md::run_onixs();
|
||||
}).detach();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// deribit ////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MarketDataGateway::_start_emumba_deribit(
|
||||
bts::api::md::EmumbaDeribitMarketDataConfig cfg) {
|
||||
LOG(md, Info,
|
||||
"MarketDataGateway::_start_emumba_deribit starting t_id:{} t_name:{}. "
|
||||
"cfg:\n{}",
|
||||
os::get_thread_id(), os::get_thread_name(), cfg.DebugString());
|
||||
auto start_deribit_thr = [cfg = std::move(cfg)]() {
|
||||
const auto config_path = Path{config::get<SystemConfig>().config_path()} /
|
||||
cfg.json_conf_file_name();
|
||||
const auto conf =
|
||||
bts::cmds::DeribitDemoConfiguration::read_file(config_path.string());
|
||||
bts_assert_msgf(
|
||||
conf.has_value(), "Failed to parse deribit config '{}'",
|
||||
config_path.string());
|
||||
// log
|
||||
for (const auto &s : conf.value().symbol_config_list) {
|
||||
LOG(md, Info, "sub.symbol: {}", s.symbol_name);
|
||||
LOG(md, Info, "sub.futs: {} sub.options: {}", s.interested_in_future,
|
||||
s.interested_in_option);
|
||||
for (const auto &i : s.interested_instruments) {
|
||||
LOG(md, Info, "sub.instrument: {}", i);
|
||||
}
|
||||
}
|
||||
EmumbaDeribitData ddata_obj;
|
||||
EmumbaDeribitData *ddata = &ddata_obj;
|
||||
// end log
|
||||
bts_assert(conf.has_value());
|
||||
LOG(md, Info, "Deribit conf: {}", conf.value());
|
||||
ddata->real_handler =
|
||||
std::make_shared<emumba::deribit_connector::deribit_handler>(
|
||||
conf.value().symbol_config_list, conf.value().trading_config,
|
||||
conf.value().credential_map, conf.value().logging_configuration,
|
||||
conf.value().deribit_url, &ddata->listener);
|
||||
// since the trading interface is housed inside the EmMDListener, we need to
|
||||
// create and send emumba's trading_session_interface
|
||||
for (const auto &p : conf.value().credential_map) {
|
||||
const auto &sess = p.second;
|
||||
auto ts_ptr = std::make_shared<
|
||||
emumba::deribit_connector::trading_session_interface>(
|
||||
ddata->real_handler->create_new_trading_session(sess));
|
||||
ddata->trading_session_vec.push_back({sess, ts_ptr});
|
||||
}
|
||||
bts_assert(!ddata->trading_session_vec.empty());
|
||||
ddata->listener.set_handler(
|
||||
ddata->real_handler.get(), ddata->trading_session_vec);
|
||||
ddata->real_handler->run();
|
||||
};
|
||||
threading::make_thread("Emumba", start_deribit_thr).detach();
|
||||
LOG(md, Info, "MarketDataGateway::_start_deribit ending t_id:{} t_name:{}",
|
||||
os::get_thread_id(), os::get_thread_name());
|
||||
}
|
||||
|
||||
// replays ////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void MarketDataGateway::_start_replays(
|
||||
const bts::api::md::MarketDataConfig &cfg) {
|
||||
LOG(md, Info, "MarketDataGateway::_start_replays starting t_id:{} t_name:{}",
|
||||
os::get_thread_id(), os::get_thread_name());
|
||||
for (const auto &r :
|
||||
MarketDataConfigUtils::get_effective_replay_config(cfg)) {
|
||||
me_->replays.emplace_back(std::make_unique<MDReplayData>(r));
|
||||
}
|
||||
for (const auto &replayer : me_->replays) {
|
||||
std::string tid_name = "MDReplayer_" + Venue_Name(replayer->venue);
|
||||
bts::threading::make_thread(tid_name, [&]() {
|
||||
replayer->md_replayer.run();
|
||||
}).detach();
|
||||
}
|
||||
LOG(md, Info, "MarketDataGateway::_start_replays ending t_id:{} t_name:{}",
|
||||
os::get_thread_id(), os::get_thread_name());
|
||||
}
|
||||
|
||||
void MarketDataGateway::start_taifex(const bts::api::md::MarketDataConfig &cfg) {
|
||||
// taifex
|
||||
for (const auto &taifex : cfg.taifex()) {
|
||||
auto p = me_->venue_listeners.emplace_back(
|
||||
std::make_shared<VenueListenerData>(bts::api::VENUE_TAIFEX));
|
||||
// Capture `p->listener` and `me_->sec_id_store` by value
|
||||
auto listener = p->listener;
|
||||
auto sec_id_store = me_->sec_id_store;
|
||||
std::string tid_name = "taifex";
|
||||
bts::threading::make_thread(tid_name, [listener, sec_id_store, taifex]() {
|
||||
bts::md::taifex::TaifexDataGateway::run(
|
||||
taifex, listener, sec_id_store);
|
||||
}).detach();
|
||||
|
||||
}
|
||||
}
|
||||
void MarketDataGateway::start_cboe(const bts::api::md::MarketDataConfig &cfg) {
|
||||
for (const api::md::CboeMarketDataConfig &cboe : cfg.cboe()) {
|
||||
auto p = me_->venue_listeners.emplace_back(
|
||||
std::make_shared<VenueListenerData>(bts::api::VENUE_CBOE));
|
||||
std::string tid_name = "CBOE";
|
||||
|
||||
// Capture `p->listener` and `me_->sec_id_store` by value
|
||||
auto listener = p->listener;
|
||||
auto sec_id_store = me_->sec_id_store;
|
||||
|
||||
bts::threading::make_thread(tid_name, [listener, sec_id_store, cboe]() {
|
||||
api::md::cboe::CboeMarketDatahandler::run(
|
||||
listener,
|
||||
sec_id_store,
|
||||
cboe);
|
||||
}).detach();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -1,44 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/api/md/GlbxMarketDataConfig.pb.h"
|
||||
#include "server/sparkd/md/MarketDataListenerID.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace bts::api::md {
|
||||
class EmumbaDeribitMarketDataConfig;
|
||||
class MarketDataConfig;
|
||||
} // namespace bts::api::md
|
||||
}
|
||||
|
||||
namespace bts::persistence {
|
||||
class IDGenerator;
|
||||
}
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
class IMarketDataListener;
|
||||
class MarketDataRegistration;
|
||||
|
||||
/**
|
||||
* @brief The `MarketDataGateway` is responsible for starting all configured market data sources.
|
||||
* It connects each source to a special `IMarketDataListener` that writes the data to shared memory.
|
||||
*/
|
||||
class MarketDataGateway {
|
||||
public:
|
||||
MarketDataGateway();
|
||||
explicit MarketDataGateway(
|
||||
api::md::MarketDataConfig config,
|
||||
std::shared_ptr<persistence::IDGenerator> sec_id_store);
|
||||
~MarketDataGateway();
|
||||
|
||||
/**
|
||||
* @brief Connects a market data listener to a market data source
|
||||
* @return MarketDataRegistration
|
||||
*/
|
||||
void run();
|
||||
void unsubscribe(MarketDataListenerID id);
|
||||
void start();
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me_;
|
||||
MarketDataRegistration _start_cc(const api::md::MarketDataConfig &) noexcept;
|
||||
static void _start_emumba_deribit(
|
||||
bts::api::md::EmumbaDeribitMarketDataConfig cfg);
|
||||
static void _start_onix_glbx(
|
||||
bts::api::md::GlbxMarketDataConfig cfg);
|
||||
void _start_replays(const bts::api::md::MarketDataConfig &cfg);
|
||||
void start_glbx(const bts::api::md::MarketDataConfig &cfg);
|
||||
void start_cboe(const bts::api::md::MarketDataConfig &cfg);
|
||||
void start_taifex(const bts::api::md::MarketDataConfig &cfg);
|
||||
};
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -1,68 +0,0 @@
|
||||
#include "MarketDataSubscription.h"
|
||||
#include "server/sparkd/md/binance/BinanceSubscription.h"
|
||||
#include "server/sparkd/md/binance_coin_futures/binance_coin_futures_subscription.h"
|
||||
#include "server/sparkd/md/binance_us/BinanceUsSubscription.h"
|
||||
#include "server/sparkd/md/binance_usds_futures/BinanceUsdsFuturesSubscription.h"
|
||||
#include "server/sparkd/md/coinbase/CoinbaseSubscription.h"
|
||||
#include "server/sparkd/md/deribit/DeribitSubscription.h"
|
||||
#include "server/sparkd/md/ftx/FTXSubscription.h"
|
||||
#include "server/sparkd/md/okx/OkxSubscription.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
using std::vector;
|
||||
|
||||
struct MarketDataSubscription::PImpl {
|
||||
vector<ftx::FTXSubscription> ftx_subs;
|
||||
vector<binance::BinanceSubscription> binance_subs;
|
||||
vector<binance_coin_futures::BinanceCoinFuturesSubscription>
|
||||
binance_coin_futures_subs;
|
||||
vector<binance_us::BinanceUsSubscription> binance_us_subs;
|
||||
vector<coinbase::CoinbaseSubscription> coinbase_subs;
|
||||
vector<binance_usds_futures::BinanceUsdsFuturesSubscription>
|
||||
binance_usds_futures_subs;
|
||||
vector<deribit::DeribitSubscription> deribit_subs;
|
||||
vector<okx::OkxSubscription> okx_subs;
|
||||
// TODO: add more exchanges
|
||||
};
|
||||
|
||||
MarketDataSubscription::MarketDataSubscription()
|
||||
: me_(std::make_unique<PImpl>()) {}
|
||||
|
||||
MarketDataSubscription::~MarketDataSubscription() = default;
|
||||
|
||||
void MarketDataSubscription::add(binance::BinanceSubscription sub) {
|
||||
me_->binance_subs.push_back(std::move(sub));
|
||||
}
|
||||
|
||||
void MarketDataSubscription::add(
|
||||
binance_coin_futures::BinanceCoinFuturesSubscription sub) {
|
||||
me_->binance_coin_futures_subs.push_back(std::move(sub));
|
||||
}
|
||||
|
||||
void MarketDataSubscription::add(binance_us::BinanceUsSubscription sub) {
|
||||
me_->binance_us_subs.push_back(std::move(sub));
|
||||
}
|
||||
void MarketDataSubscription::add(coinbase::CoinbaseSubscription sub) {
|
||||
me_->coinbase_subs.push_back(std::move(sub));
|
||||
}
|
||||
void MarketDataSubscription::add(
|
||||
binance_usds_futures::BinanceUsdsFuturesSubscription sub) {
|
||||
me_->binance_usds_futures_subs.push_back(std::move(sub));
|
||||
}
|
||||
|
||||
void MarketDataSubscription::add(deribit::DeribitSubscription sub) {
|
||||
me_->deribit_subs.push_back(std::move(sub));
|
||||
}
|
||||
|
||||
void MarketDataSubscription::add(ftx::FTXSubscription sub) {
|
||||
me_->ftx_subs.push_back(std::move(sub));
|
||||
}
|
||||
|
||||
void MarketDataSubscription::add(okx::OkxSubscription sub) {
|
||||
me_->okx_subs.push_back(std::move(sub));
|
||||
}
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -1,64 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace bts::md::binance {
|
||||
class BinanceSubscription;
|
||||
}
|
||||
|
||||
namespace bts::md::binance_coin_futures {
|
||||
class BinanceCoinFuturesSubscription;
|
||||
}
|
||||
|
||||
namespace bts::md::binance_us {
|
||||
class BinanceUsSubscription;
|
||||
}
|
||||
namespace bts::md::coinbase {
|
||||
class CoinbaseSubscription;
|
||||
}
|
||||
namespace bts::md::binance_usds_futures {
|
||||
class BinanceUsdsFuturesSubscription;
|
||||
}
|
||||
|
||||
namespace bts::md::deribit {
|
||||
class DeribitSubscription;
|
||||
}
|
||||
|
||||
namespace bts::md::ftx {
|
||||
class FTXSubscription;
|
||||
}
|
||||
|
||||
namespace bts::md::okx {
|
||||
class OkxSubscription;
|
||||
}
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
class MarketDataSubscription {
|
||||
public:
|
||||
MarketDataSubscription();
|
||||
|
||||
MarketDataSubscription(const MarketDataSubscription &) noexcept = delete;
|
||||
MarketDataSubscription(MarketDataSubscription &&) noexcept = default;
|
||||
MarketDataSubscription &operator=(const MarketDataSubscription &) noexcept =
|
||||
delete;
|
||||
MarketDataSubscription &operator=(MarketDataSubscription &&) noexcept =
|
||||
default;
|
||||
|
||||
~MarketDataSubscription();
|
||||
|
||||
void add(binance::BinanceSubscription);
|
||||
void add(binance_coin_futures::BinanceCoinFuturesSubscription);
|
||||
void add(binance_us::BinanceUsSubscription);
|
||||
void add(coinbase::CoinbaseSubscription);
|
||||
void add(binance_usds_futures::BinanceUsdsFuturesSubscription);
|
||||
void add(deribit::DeribitSubscription);
|
||||
void add(ftx::FTXSubscription);
|
||||
void add(okx::OkxSubscription);
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me_;
|
||||
};
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "OnixsMarketDataProvider.h"
|
||||
#include "lib/api/domain/Venue.pb.h"
|
||||
#include "server/sparkd/md/glbx/OnixsGlbxSubscription.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
using bts::api::domain::Venue;
|
||||
using bts::api::md::MarketDataConfig;
|
||||
using bts::md::OnixsMarketDataProvider;
|
||||
using std::pair;
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
struct OnixsMarketDataProvider::PImpl {
|
||||
MarketDataConfig config;
|
||||
vector<shared_ptr<OnixsGlbxSubscription>> sources;
|
||||
};
|
||||
|
||||
OnixsMarketDataProvider::OnixsMarketDataProvider(MarketDataConfig config)
|
||||
: me_(std::make_unique<PImpl>()) {
|
||||
me_->config = std::move(config);
|
||||
}
|
||||
|
||||
OnixsMarketDataProvider::~OnixsMarketDataProvider() = default;
|
||||
|
||||
vector<pair<Venue, shared_ptr<IMarketDataSource>>>
|
||||
OnixsMarketDataProvider::get_sources() {
|
||||
vector<pair<Venue, shared_ptr<IMarketDataSource>>> rv;
|
||||
for (const auto &glbx : me_->config.glbx()) {
|
||||
if (glbx.use_databento()) {
|
||||
continue;
|
||||
}
|
||||
me_->sources.push_back(std::make_shared<OnixsGlbxSubscription>(glbx));
|
||||
rv.push_back({Venue::VENUE_GLBX, me_->sources.back()});
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/api/md/MarketDataConfig.pb.h"
|
||||
#include "server/sparkd/md/IMarketDataProvider.h"
|
||||
#include "server/sparkd/md/IMarketDataSource.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
class OnixsMarketDataProvider : public IMarketDataProvider {
|
||||
public:
|
||||
OnixsMarketDataProvider();
|
||||
explicit OnixsMarketDataProvider(api::md::MarketDataConfig config);
|
||||
~OnixsMarketDataProvider();
|
||||
|
||||
std::vector<
|
||||
std::pair<bts::api::domain::Venue, std::shared_ptr<IMarketDataSource>>>
|
||||
get_sources() override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me_;
|
||||
};
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "ReplayMarketDataProvider.h"
|
||||
#include "lib/api/md/MarketDataConfig.pb.h"
|
||||
#include "lib/api/md/MarketDataConfigUtils.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/domain/DomainSecurityUtils.h"
|
||||
#include "lib/persistence/IDGenerator.h"
|
||||
#include "server/sparkd/md/IMarketDataSource.h"
|
||||
#include "server/sparkd/md/recording/MDReplayer.h"
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
using bts::api::md::MarketDataConfig;
|
||||
using bts::domain::DomainSecurityUtils;
|
||||
using bts::md::recording::MDReplayer;
|
||||
using bts::persistence::IDGenerator;
|
||||
using std::pair;
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
struct ReplayMarketDataProvider::PImpl {
|
||||
MarketDataConfig config;
|
||||
shared_ptr<IDGenerator> sec_id_store;
|
||||
vector<pair<api::domain::Venue, shared_ptr<IMarketDataSource>>> sources;
|
||||
|
||||
PImpl(MarketDataConfig config, shared_ptr<IDGenerator> sec_id_store)
|
||||
: config(std::move(config)), sec_id_store(std::move(sec_id_store)) {}
|
||||
};
|
||||
|
||||
ReplayMarketDataProvider::ReplayMarketDataProvider()
|
||||
: ReplayMarketDataProvider(
|
||||
config::get<MarketDataConfig>(), IDGenerator::instance_ptr()) {}
|
||||
|
||||
ReplayMarketDataProvider::ReplayMarketDataProvider(
|
||||
MarketDataConfig config, shared_ptr<IDGenerator> sec_id_store)
|
||||
: me_(std::make_unique<PImpl>(std::move(config), std::move(sec_id_store))) {
|
||||
}
|
||||
|
||||
ReplayMarketDataProvider::~ReplayMarketDataProvider() = default;
|
||||
|
||||
vector<pair<api::domain::Venue, shared_ptr<IMarketDataSource>>>
|
||||
ReplayMarketDataProvider::get_sources() {
|
||||
const auto replay_cfgs =
|
||||
MarketDataConfigUtils::get_effective_replay_config(me_->config);
|
||||
for (const auto &r : replay_cfgs) {
|
||||
const auto v = DomainSecurityUtils::convert(r.venue());
|
||||
me_->sources.push_back({v, std::make_shared<MDReplayer>(r.venue())});
|
||||
}
|
||||
return me_->sources;
|
||||
}
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/api/md/MarketDataConfig.pb.h"
|
||||
#include "lib/persistence/IDGenerator.h"
|
||||
#include "server/sparkd/md/IMarketDataProvider.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
class ReplayMarketDataProvider : public IMarketDataProvider {
|
||||
public:
|
||||
ReplayMarketDataProvider();
|
||||
explicit ReplayMarketDataProvider(
|
||||
api::md::MarketDataConfig config,
|
||||
std::shared_ptr<persistence::IDGenerator> sec_id_store);
|
||||
~ReplayMarketDataProvider();
|
||||
|
||||
std::vector<
|
||||
std::pair<bts::api::domain::Venue, std::shared_ptr<IMarketDataSource>>>
|
||||
get_sources() override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me_;
|
||||
};
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "ReplayMarketDataSubscription.h"
|
||||
#include "lib/api/md/MarketDataReplayConfig.pb.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/md/GeneralMarketDataListener.h"
|
||||
#include "lib/md/IMarketDataListener.h"
|
||||
#include "lib/md/MDProducer.h"
|
||||
#include "lib/utils/thread_manager.h"
|
||||
#include "server/sparkd/md/recording/MDReplayer.h"
|
||||
|
||||
#include <ccapi_cpp/ccapi_event_dispatcher.h>
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
using bts::api::md::MarketDataReplayConfig;
|
||||
using bts::md::recording::MDReplayer;
|
||||
using std::string;
|
||||
|
||||
struct ReplayMarketDataSource::PImpl {
|
||||
MarketDataReplayConfig config;
|
||||
|
||||
PImpl(const MarketDataReplayConfig &config) : config(config) {}
|
||||
};
|
||||
|
||||
ReplayMarketDataSource::ReplayMarketDataSource(MarketDataReplayConfig config)
|
||||
: me_(std::make_unique<PImpl>(config)) {
|
||||
me_->config = std::move(config);
|
||||
}
|
||||
|
||||
ReplayMarketDataSource::~ReplayMarketDataSource() = default;
|
||||
|
||||
void ReplayMarketDataSource::start(
|
||||
std::shared_ptr<IMarketDataListener> listener) {
|
||||
LOG(md, Info, "MarketDataGateway::_start_replays starting t_id:{} t_name:{}",
|
||||
os::get_thread_id(), os::get_thread_name());
|
||||
auto md_replayer = std::make_shared<MDReplayer>(me_->config.venue());
|
||||
std::string tid_name = "MDReplayer_" + Venue_Name(me_->config.venue());
|
||||
bts::threading::make_thread(tid_name, [&, listener, md_replayer]() {
|
||||
md_replayer->start(listener);
|
||||
}).detach();
|
||||
LOG(md, Info, "MarketDataGateway::_start_replays ending t_id:{} t_name:{}",
|
||||
os::get_thread_id(), os::get_thread_name());
|
||||
}
|
||||
|
||||
void ReplayMarketDataSource::stop() {}
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/api/md/MarketDataReplayConfig.pb.h"
|
||||
#include "lib/md/IMarketDataListener.h"
|
||||
#include "server/sparkd/md/IMarketDataSource.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
class ReplayMarketDataSource : public IMarketDataSource {
|
||||
public:
|
||||
ReplayMarketDataSource();
|
||||
explicit ReplayMarketDataSource(api::md::MarketDataReplayConfig config);
|
||||
~ReplayMarketDataSource();
|
||||
|
||||
void start(std::shared_ptr<IMarketDataListener> listener) override;
|
||||
void stop() override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me_;
|
||||
};
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -171,7 +171,7 @@ struct BinanceSubscription::PImpl {
|
||||
|
||||
// The session automatically starts/stops in its ctr/dtr
|
||||
CCApiSession session;
|
||||
IMarketDataListener *listener;
|
||||
std::shared_ptr<IMarketDataListener> listener;
|
||||
BinanceSecurityRequestor sec_requestor;
|
||||
|
||||
// These aliases are useful for quickly switching the handler types (e.g.,
|
||||
@@ -194,11 +194,10 @@ struct BinanceSubscription::PImpl {
|
||||
explicit PImpl(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
BinanceMarketDataConfig cfg,
|
||||
IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener)
|
||||
IDGenerator *sec_id_store)
|
||||
: sec_id_store(sec_id_store), cfg(std::move(cfg)),
|
||||
criteria(SecurityCriteria::from_proto(this->cfg.security_criteria())),
|
||||
session(dispatcher, mk_session_cfg(this->cfg), {}), listener(listener),
|
||||
session(dispatcher, mk_session_cfg(this->cfg), {}),
|
||||
sec_requestor(
|
||||
&session,
|
||||
this->criteria,
|
||||
@@ -209,6 +208,11 @@ struct BinanceSubscription::PImpl {
|
||||
LOG(md, Info, "Initializing BinanceSubscription:\n{}",
|
||||
ProtoPrinter::to_string(this->cfg));
|
||||
sec_requestor.callback().bind<&PImpl::on_securities>(*this);
|
||||
}
|
||||
|
||||
void start(std::shared_ptr<IMarketDataListener> listener) {
|
||||
bts_assert(listener != nullptr);
|
||||
this->listener = std::move(listener);
|
||||
sec_requestor.request();
|
||||
}
|
||||
|
||||
@@ -216,7 +220,7 @@ struct BinanceSubscription::PImpl {
|
||||
SecurityType sec_type, const string &sec_desc) {
|
||||
SecurityID sec_id{sec_id_store->get_or_add(VENUE, sec_type, sec_desc)};
|
||||
auto [it, is_new] =
|
||||
depth_handlers.try_emplace(sec_desc, sec_id, listener);
|
||||
depth_handlers.try_emplace(sec_desc, sec_id, listener.get());
|
||||
bts_assert(is_new);
|
||||
return it->second;
|
||||
}
|
||||
@@ -225,7 +229,7 @@ struct BinanceSubscription::PImpl {
|
||||
SecurityType sec_type, const string &sec_desc) {
|
||||
SecurityID sec_id{sec_id_store->get_or_add(VENUE, sec_type, sec_desc)};
|
||||
auto [it, is_new] =
|
||||
ticker_handlers.try_emplace(sec_desc, sec_id, listener);
|
||||
ticker_handlers.try_emplace(sec_desc, sec_id, listener.get());
|
||||
bts_assert_msgf(
|
||||
is_new, "Ticker handler already created for '{}'", sec_desc);
|
||||
return it->second;
|
||||
@@ -242,7 +246,7 @@ struct BinanceSubscription::PImpl {
|
||||
}
|
||||
auto [it, is_new] = stats_handlers.try_emplace(
|
||||
hnd_id,
|
||||
std::make_unique<StatsHandler>(sec_desc_id_tuples, listener));
|
||||
std::make_unique<StatsHandler>(sec_desc_id_tuples, listener.get()));
|
||||
bts_assert_msgf(
|
||||
is_new, "StatsHandler already created with id '{}'", hnd_id);
|
||||
return *it->second;
|
||||
@@ -333,10 +337,8 @@ struct BinanceSubscription::PImpl {
|
||||
BinanceSubscription::BinanceSubscription(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
BinanceMarketDataConfig cfg,
|
||||
IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener)
|
||||
: me_(std::make_unique<PImpl>(
|
||||
dispatcher, std::move(cfg), sec_id_store, listener)) {}
|
||||
IDGenerator *sec_id_store)
|
||||
: me_(std::make_unique<PImpl>(dispatcher, std::move(cfg), sec_id_store)) {}
|
||||
|
||||
BinanceSubscription::BinanceSubscription(BinanceSubscription &&) noexcept =
|
||||
default;
|
||||
@@ -345,4 +347,8 @@ BinanceSubscription &BinanceSubscription::operator=(
|
||||
|
||||
BinanceSubscription::~BinanceSubscription() = default;
|
||||
|
||||
void BinanceSubscription::start(std::shared_ptr<IMarketDataListener> listener) {
|
||||
me_->start(listener);
|
||||
}
|
||||
|
||||
} // namespace bts::md::binance
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "server/sparkd/md/IMarketDataSource.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ccapi {
|
||||
@@ -25,13 +27,12 @@ namespace bts::md::binance {
|
||||
*
|
||||
* @a https://binance-docs.github.io/apidocs/spot/en/#exchange-information
|
||||
*/
|
||||
class BinanceSubscription {
|
||||
class BinanceSubscription : public IMarketDataSource {
|
||||
public:
|
||||
BinanceSubscription(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
api::md::BinanceMarketDataConfig cfg,
|
||||
persistence::IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener);
|
||||
persistence::IDGenerator *sec_id_store);
|
||||
|
||||
BinanceSubscription(const BinanceSubscription &) noexcept = delete;
|
||||
BinanceSubscription(BinanceSubscription &&) noexcept;
|
||||
@@ -39,10 +40,9 @@ class BinanceSubscription {
|
||||
delete;
|
||||
BinanceSubscription &operator=(BinanceSubscription &&) noexcept;
|
||||
|
||||
~BinanceSubscription();
|
||||
~BinanceSubscription() override;
|
||||
|
||||
void add_listener(IMarketDataListener *listener);
|
||||
void remove_listener(IMarketDataListener *listener);
|
||||
void start(std::shared_ptr<IMarketDataListener> listener) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
|
||||
@@ -47,6 +47,7 @@ using bts::io::ProtoPrinter;
|
||||
using bts::io::QueryParams;
|
||||
using bts::persistence::IDGenerator;
|
||||
using std::pair;
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::tuple;
|
||||
using std::uint32_t;
|
||||
@@ -174,7 +175,7 @@ struct BinanceUsSubscription::PImpl {
|
||||
|
||||
// The session automatically starts/stops in its ctr/dtr
|
||||
CCApiSession session;
|
||||
IMarketDataListener *listener;
|
||||
shared_ptr<IMarketDataListener> listener;
|
||||
BinanceUsSecurityRequestor sec_requestor;
|
||||
|
||||
// These aliases are useful for quickly switching the handler types (e.g.,
|
||||
@@ -191,17 +192,16 @@ struct BinanceUsSubscription::PImpl {
|
||||
// different map, 'stats_handlers' (unique ids are generated w/ a counter,
|
||||
// 'stats_handlers_id_counter').
|
||||
unordered_map<string, StatsHandler *> sec_stats_handler_map;
|
||||
unordered_map<uint32_t, std::unique_ptr<StatsHandler>> stats_handlers;
|
||||
unordered_map<uint32_t, unique_ptr<StatsHandler>> stats_handlers;
|
||||
uint32_t stats_handlers_id_counter = 0;
|
||||
|
||||
explicit PImpl(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
BinanceUsMarketDataConfig cfg,
|
||||
IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener)
|
||||
IDGenerator *sec_id_store)
|
||||
: sec_id_store(sec_id_store), cfg(std::move(cfg)),
|
||||
criteria(SecurityCriteria::from_proto(this->cfg.security_criteria())),
|
||||
session(dispatcher, mk_session_cfg(this->cfg), {}), listener(listener),
|
||||
session(dispatcher, mk_session_cfg(this->cfg), {}),
|
||||
sec_requestor(
|
||||
&session,
|
||||
this->criteria,
|
||||
@@ -212,6 +212,11 @@ struct BinanceUsSubscription::PImpl {
|
||||
LOG(md, Info, "Initializing BinanceUsSubscription:\n{}",
|
||||
ProtoPrinter::to_string(this->cfg));
|
||||
sec_requestor.callback().bind<&PImpl::on_securities>(*this);
|
||||
}
|
||||
|
||||
void start(std::shared_ptr<IMarketDataListener> listener) {
|
||||
bts_assert(listener != nullptr);
|
||||
this->listener = std::move(listener);
|
||||
sec_requestor.request();
|
||||
}
|
||||
|
||||
@@ -219,7 +224,7 @@ struct BinanceUsSubscription::PImpl {
|
||||
SecurityType sec_type, const string &sec_desc) {
|
||||
SecurityID sec_id{sec_id_store->get_or_add(VENUE, sec_type, sec_desc)};
|
||||
auto [it, is_new] =
|
||||
depth_handlers.try_emplace(sec_desc, sec_id, listener);
|
||||
depth_handlers.try_emplace(sec_desc, sec_id, listener.get());
|
||||
bts_assert(is_new);
|
||||
return it->second;
|
||||
}
|
||||
@@ -228,7 +233,7 @@ struct BinanceUsSubscription::PImpl {
|
||||
SecurityType sec_type, const string &sec_desc) {
|
||||
SecurityID sec_id{sec_id_store->get_or_add(VENUE, sec_type, sec_desc)};
|
||||
auto [it, is_new] =
|
||||
ticker_handlers.try_emplace(sec_desc, sec_id, listener);
|
||||
ticker_handlers.try_emplace(sec_desc, sec_id, listener.get());
|
||||
bts_assert_msgf(
|
||||
is_new, "Ticker handler already created for '{}'", sec_desc);
|
||||
return it->second;
|
||||
@@ -245,7 +250,7 @@ struct BinanceUsSubscription::PImpl {
|
||||
}
|
||||
auto [it, is_new] = stats_handlers.try_emplace(
|
||||
hnd_id,
|
||||
std::make_unique<StatsHandler>(sec_desc_id_tuples, listener));
|
||||
std::make_unique<StatsHandler>(sec_desc_id_tuples, listener.get()));
|
||||
bts_assert_msgf(
|
||||
is_new, "StatsHandler already created with id '{}'", hnd_id);
|
||||
return *it->second;
|
||||
@@ -335,10 +340,8 @@ struct BinanceUsSubscription::PImpl {
|
||||
BinanceUsSubscription::BinanceUsSubscription(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
BinanceUsMarketDataConfig cfg,
|
||||
IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener)
|
||||
: me_(std::make_unique<PImpl>(
|
||||
dispatcher, std::move(cfg), sec_id_store, listener)) {}
|
||||
IDGenerator *sec_id_store)
|
||||
: me_(std::make_unique<PImpl>(dispatcher, std::move(cfg), sec_id_store)) {}
|
||||
|
||||
BinanceUsSubscription::BinanceUsSubscription(
|
||||
BinanceUsSubscription &&) noexcept = default;
|
||||
@@ -347,4 +350,8 @@ BinanceUsSubscription &BinanceUsSubscription::operator=(
|
||||
|
||||
BinanceUsSubscription::~BinanceUsSubscription() = default;
|
||||
|
||||
void BinanceUsSubscription::start(shared_ptr<IMarketDataListener> listener) {
|
||||
me_->start(std::move(listener));
|
||||
}
|
||||
|
||||
} // namespace bts::md::binance_us
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "server/sparkd/md/IMarketDataSource.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ccapi {
|
||||
@@ -25,13 +27,12 @@ namespace bts::md::binance_us {
|
||||
*
|
||||
* @a https://docs.binance.us/#get-exchange-information
|
||||
*/
|
||||
class BinanceUsSubscription {
|
||||
class BinanceUsSubscription : public IMarketDataSource {
|
||||
public:
|
||||
BinanceUsSubscription(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
api::md::BinanceUsMarketDataConfig cfg,
|
||||
persistence::IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener);
|
||||
persistence::IDGenerator *sec_id_store);
|
||||
|
||||
BinanceUsSubscription(const BinanceUsSubscription &) noexcept = delete;
|
||||
BinanceUsSubscription(BinanceUsSubscription &&) noexcept;
|
||||
@@ -41,8 +42,7 @@ class BinanceUsSubscription {
|
||||
|
||||
~BinanceUsSubscription();
|
||||
|
||||
void add_listener(IMarketDataListener *listener);
|
||||
void remove_listener(IMarketDataListener *listener);
|
||||
void start(std::shared_ptr<IMarketDataListener> listener) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
|
||||
@@ -41,6 +41,7 @@ using bts::io::ProtoPrinter;
|
||||
using bts::io::QueryParams;
|
||||
using bts::persistence::IDGenerator;
|
||||
using std::pair;
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::uint32_t;
|
||||
using std::unique_ptr;
|
||||
@@ -167,7 +168,7 @@ struct BinanceUsdsFuturesSubscription::PImpl {
|
||||
SecurityCriteria criteria;
|
||||
// The session automatically starts/stops in its ctr/dtr
|
||||
CCApiSession session;
|
||||
IMarketDataListener *listener;
|
||||
std::shared_ptr<IMarketDataListener> listener;
|
||||
BinanceUsdsFuturesSecurityRequestor sec_requestor;
|
||||
|
||||
// These aliases are useful for quickly switching the handler types (e.g.,
|
||||
@@ -183,15 +184,19 @@ struct BinanceUsdsFuturesSubscription::PImpl {
|
||||
explicit PImpl(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
BinanceUsdsFuturesMarketDataConfig cfg,
|
||||
IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener)
|
||||
IDGenerator *sec_id_store)
|
||||
: sec_id_store(sec_id_store), cfg(std::move(cfg)),
|
||||
criteria(SecurityCriteria::from_proto(this->cfg.security_criteria())),
|
||||
session(dispatcher, mk_session_cfg(this->cfg), {}), listener(listener),
|
||||
session(dispatcher, mk_session_cfg(this->cfg), {}),
|
||||
sec_requestor(&session, this->cfg, sec_id_store) {
|
||||
LOG(md, Info, "Initializing BinanceUsdsFuturesSubscription:\n{}",
|
||||
ProtoPrinter::to_string(this->cfg));
|
||||
sec_requestor.callback().bind<&PImpl::on_securities>(*this);
|
||||
}
|
||||
|
||||
void start(shared_ptr<IMarketDataListener> listener) {
|
||||
bts_assert(listener != nullptr);
|
||||
this->listener = std::move(listener);
|
||||
sec_requestor.request();
|
||||
}
|
||||
|
||||
@@ -203,7 +208,7 @@ struct BinanceUsdsFuturesSubscription::PImpl {
|
||||
SecurityType sec_type, const string &sec_desc) {
|
||||
const auto sec_id = get_sec_id(sec_type, sec_desc);
|
||||
auto [it, is_new] =
|
||||
depth_handlers.try_emplace(sec_desc, sec_id, listener);
|
||||
depth_handlers.try_emplace(sec_desc, sec_id, listener.get());
|
||||
bts_assert_msgf(
|
||||
is_new, "Depth handler already created for '{}'", sec_desc);
|
||||
return it->second;
|
||||
@@ -213,7 +218,7 @@ struct BinanceUsdsFuturesSubscription::PImpl {
|
||||
SecurityType sec_type, const string &sec_desc) {
|
||||
const auto sec_id = get_sec_id(sec_type, sec_desc);
|
||||
auto [it, is_new] =
|
||||
ticker_handlers.try_emplace(sec_desc, sec_id, listener);
|
||||
ticker_handlers.try_emplace(sec_desc, sec_id, listener.get());
|
||||
bts_assert_msgf(
|
||||
is_new, "Ticker handler already created for '{}'", sec_desc);
|
||||
return it->second;
|
||||
@@ -223,7 +228,7 @@ struct BinanceUsdsFuturesSubscription::PImpl {
|
||||
SecurityType sec_type, const string &sec_desc) {
|
||||
const auto sec_id = get_sec_id(sec_type, sec_desc);
|
||||
auto [it, is_new] =
|
||||
stats_handlers.try_emplace(sec_desc, sec_id, listener);
|
||||
stats_handlers.try_emplace(sec_desc, sec_id, listener.get());
|
||||
bts_assert_msgf(
|
||||
is_new, "Stats handler already created for '{}'", sec_desc);
|
||||
return it->second;
|
||||
@@ -297,10 +302,8 @@ struct BinanceUsdsFuturesSubscription::PImpl {
|
||||
BinanceUsdsFuturesSubscription::BinanceUsdsFuturesSubscription(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
BinanceUsdsFuturesMarketDataConfig cfg,
|
||||
IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener)
|
||||
: me_(std::make_unique<PImpl>(
|
||||
dispatcher, std::move(cfg), sec_id_store, listener)) {}
|
||||
IDGenerator *sec_id_store)
|
||||
: me_(std::make_unique<PImpl>(dispatcher, std::move(cfg), sec_id_store)) {}
|
||||
|
||||
BinanceUsdsFuturesSubscription::BinanceUsdsFuturesSubscription(
|
||||
BinanceUsdsFuturesSubscription &&) noexcept = default;
|
||||
@@ -309,4 +312,9 @@ BinanceUsdsFuturesSubscription &BinanceUsdsFuturesSubscription::operator=(
|
||||
|
||||
BinanceUsdsFuturesSubscription::~BinanceUsdsFuturesSubscription() = default;
|
||||
|
||||
void BinanceUsdsFuturesSubscription::start(
|
||||
shared_ptr<IMarketDataListener> listener) {
|
||||
me_->start(std::move(listener));
|
||||
}
|
||||
|
||||
} // namespace bts::md::binance_usds_futures
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "server/sparkd/md/IMarketDataSource.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ccapi {
|
||||
@@ -25,13 +27,12 @@ namespace bts::md::binance_usds_futures {
|
||||
*
|
||||
* @a https://binance-docs.github.io/apidocs/futures/en/#exchange-information
|
||||
*/
|
||||
class BinanceUsdsFuturesSubscription {
|
||||
class BinanceUsdsFuturesSubscription : public IMarketDataSource {
|
||||
public:
|
||||
BinanceUsdsFuturesSubscription(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
api::md::BinanceUsdsFuturesMarketDataConfig cfg,
|
||||
persistence::IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener);
|
||||
persistence::IDGenerator *sec_id_store);
|
||||
|
||||
BinanceUsdsFuturesSubscription(
|
||||
const BinanceUsdsFuturesSubscription &) noexcept = delete;
|
||||
@@ -43,8 +44,7 @@ class BinanceUsdsFuturesSubscription {
|
||||
|
||||
~BinanceUsdsFuturesSubscription();
|
||||
|
||||
void add_listener(IMarketDataListener *listener);
|
||||
void remove_listener(IMarketDataListener *listener);
|
||||
void start(std::shared_ptr<IMarketDataListener> listener) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
#include "CCApiMarketDataProvider.h"
|
||||
#include "lib/api/domain/Venue.pb.h"
|
||||
#include "lib/api/md/MarketDataConfig.pb.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/io/ProtoPrinter.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/md/GeneralMarketDataListener.h"
|
||||
#include "lib/md/GeneralSecurityIndex.h"
|
||||
#include "lib/md/MDProducer.h"
|
||||
#include "lib/persistence/IDGenerator.h"
|
||||
#include "server/sparkd/md/binance/BinanceSubscription.h"
|
||||
#include "server/sparkd/md/binance_us/BinanceUsSubscription.h"
|
||||
#include "server/sparkd/md/binance_usds_futures/BinanceUsdsFuturesSubscription.h"
|
||||
#include "server/sparkd/md/coinbase/CoinbaseSubscription.h"
|
||||
#include "server/sparkd/md/deribit/DeribitSubscription.h"
|
||||
#include "server/sparkd/md/ftx/FTXSubscription.h"
|
||||
#include "server/sparkd/md/okx/OkxSubscription.h"
|
||||
|
||||
#include <ccapi_cpp/ccapi_event_dispatcher.h>
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
using bts::api::md::MarketDataConfig;
|
||||
using bts::persistence::IDGenerator;
|
||||
using std::pair;
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
using std::unordered_map;
|
||||
using std::vector;
|
||||
|
||||
static bool validate(
|
||||
const MarketDataConfig & /*cfg*/, vector<string> & /*errs*/) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct CCApiMarketDataProvider::PImpl {
|
||||
struct VenueSink {
|
||||
GeneralSecurityIndex sec_index;
|
||||
MDProducer md_producer;
|
||||
GeneralMarketDataListener listener;
|
||||
|
||||
VenueSink(api::Venue venue, shared_ptr<IDGenerator> sec_id_store)
|
||||
: sec_index(),
|
||||
listener(sec_index, md_producer, venue, sec_id_store.get()) {}
|
||||
};
|
||||
|
||||
MarketDataConfig config;
|
||||
ccapi::EventDispatcher dispatcher;
|
||||
shared_ptr<IDGenerator> sec_id_store;
|
||||
unordered_map<api::Venue, unique_ptr<VenueSink>> venue_sinks;
|
||||
vector<std::pair<api::domain::Venue, std::shared_ptr<IMarketDataSource>>>
|
||||
sources;
|
||||
|
||||
PImpl(MarketDataConfig config, shared_ptr<IDGenerator> sec_id_store)
|
||||
: config(std::move(config)), sec_id_store(std::move(sec_id_store)) {}
|
||||
|
||||
VenueSink *get_venue_sink(api::Venue venue) {
|
||||
auto it = venue_sinks.find(venue);
|
||||
if (it != venue_sinks.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
return venue_sinks
|
||||
.try_emplace(venue, std::make_unique<VenueSink>(venue, sec_id_store))
|
||||
.first->second.get();
|
||||
}
|
||||
};
|
||||
|
||||
CCApiMarketDataProvider::CCApiMarketDataProvider(
|
||||
MarketDataConfig config, shared_ptr<IDGenerator> sec_id_store)
|
||||
: me_(std::make_unique<PImpl>(std::move(config), std::move(sec_id_store))) {
|
||||
}
|
||||
|
||||
CCApiMarketDataProvider::~CCApiMarketDataProvider() = default;
|
||||
|
||||
vector<pair<api::domain::Venue, shared_ptr<IMarketDataSource>>>
|
||||
CCApiMarketDataProvider::get_sources() {
|
||||
auto &cfg = me_->config;
|
||||
vector<string> cfg_errs;
|
||||
if (!validate(cfg, cfg_errs)) {
|
||||
LOG(md, Error,
|
||||
"CCApiMarketDataProvider::get_sources failed. Invalid config: {}",
|
||||
fmt::to_string(fmt::join(cfg_errs, "\n")));
|
||||
}
|
||||
|
||||
// Binance
|
||||
for (const auto &binance : cfg.binance()) {
|
||||
me_->sources.emplace_back(
|
||||
api::domain::VENUE_BINANCE,
|
||||
std::make_shared<binance::BinanceSubscription>(
|
||||
&me_->dispatcher, binance, me_->sec_id_store.get()));
|
||||
}
|
||||
// Binance US
|
||||
for (const auto &binance_us : cfg.binance_us()) {
|
||||
me_->sources.emplace_back(
|
||||
api::domain::VENUE_BINANCE_US,
|
||||
std::make_shared<binance_us::BinanceUsSubscription>(
|
||||
&me_->dispatcher, binance_us, me_->sec_id_store.get()));
|
||||
}
|
||||
// Binance USDS Futures
|
||||
for (const auto &binance_ufs : cfg.binance_usds_futures()) {
|
||||
me_->sources.emplace_back(
|
||||
api::domain::VENUE_BINANCE_USDS_FUTURES,
|
||||
std::make_shared<
|
||||
binance_usds_futures::BinanceUsdsFuturesSubscription>(
|
||||
&me_->dispatcher, binance_ufs, me_->sec_id_store.get()));
|
||||
}
|
||||
// Coinbase
|
||||
for (const auto &coinbase : cfg.coinbase()) {
|
||||
me_->sources.emplace_back(
|
||||
api::domain::VENUE_COINBASE,
|
||||
std::make_shared<coinbase::CoinbaseSubscription>(
|
||||
&me_->dispatcher, coinbase, me_->sec_id_store.get()));
|
||||
}
|
||||
// Deribit
|
||||
for (const auto &deribit : cfg.deribit()) {
|
||||
me_->sources.emplace_back(
|
||||
api::domain::VENUE_DERIBIT,
|
||||
std::make_shared<deribit::DeribitSubscription>(
|
||||
&me_->dispatcher, deribit, me_->sec_id_store.get()));
|
||||
}
|
||||
// FTX
|
||||
for (const auto &ftx : cfg.ftx()) {
|
||||
me_->sources.emplace_back(
|
||||
api::domain::VENUE_FTX,
|
||||
std::make_shared<ftx::FTXSubscription>(
|
||||
&me_->dispatcher, ftx, me_->sec_id_store.get()));
|
||||
}
|
||||
// Okx
|
||||
for (const auto &okx : cfg.okx()) {
|
||||
me_->sources.emplace_back(
|
||||
api::domain::VENUE_OKX,
|
||||
std::make_shared<okx::OkxSubscription>(
|
||||
&me_->dispatcher, okx, me_->sec_id_store.get()));
|
||||
}
|
||||
return me_->sources;
|
||||
}
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/api/md/MarketDataConfig.pb.h"
|
||||
#include "lib/persistence/IDGenerator.h"
|
||||
#include "server/sparkd/md/IMarketDataProvider.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
class CCApiMarketDataProvider : public IMarketDataProvider {
|
||||
public:
|
||||
explicit CCApiMarketDataProvider(
|
||||
api::md::MarketDataConfig config,
|
||||
std::shared_ptr<persistence::IDGenerator> sec_id_store);
|
||||
~CCApiMarketDataProvider();
|
||||
|
||||
std::vector<
|
||||
std::pair<api::domain::Venue, std::shared_ptr<IMarketDataSource>>>
|
||||
get_sources() override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me_;
|
||||
};
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -5,8 +5,10 @@
|
||||
#include "lib/domain/SecurityCriteria.h"
|
||||
#include "lib/io/ProtoPrinter.h"
|
||||
#include "lib/io/QueryParams.h"
|
||||
#include "lib/md/IMarketDataListener.h"
|
||||
#include "lib/md/common.h"
|
||||
#include "lib/persistence/IDGenerator.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "server/sparkd/md/ccapi/CCApiMarketDepthAdaptor.h"
|
||||
#include "server/sparkd/md/ccapi/CCApiSession.h"
|
||||
#include "server/sparkd/md/ccapi/CCApiTradeAdaptor.h"
|
||||
@@ -36,6 +38,7 @@ using bts::io::ProtoPrinter;
|
||||
using bts::io::QueryParams;
|
||||
using bts::persistence::IDGenerator;
|
||||
using std::pair;
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
using std::unordered_map;
|
||||
@@ -151,7 +154,7 @@ struct CoinbaseSubscription::PImpl {
|
||||
|
||||
// The session automatically starts/stops in its ctr/dtr
|
||||
CCApiSession session;
|
||||
IMarketDataListener *listener;
|
||||
shared_ptr<IMarketDataListener> listener;
|
||||
CoinbaseSecurityRequestor sec_requestor;
|
||||
|
||||
// These aliases are useful for quickly switching the handler types (e.g.,
|
||||
@@ -172,11 +175,10 @@ struct CoinbaseSubscription::PImpl {
|
||||
explicit PImpl(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
CoinbaseMarketDataConfig cfg,
|
||||
IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener)
|
||||
IDGenerator *sec_id_store)
|
||||
: sec_id_store(sec_id_store), cfg(std::move(cfg)),
|
||||
criteria(SecurityCriteria::from_proto(this->cfg.security_criteria())),
|
||||
session(dispatcher, mk_session_cfg(this->cfg), {}), listener(listener),
|
||||
session(dispatcher, mk_session_cfg(this->cfg), {}),
|
||||
sec_requestor(
|
||||
&session,
|
||||
this->criteria,
|
||||
@@ -187,6 +189,11 @@ struct CoinbaseSubscription::PImpl {
|
||||
LOG(md, Info, "Initializing CoinbaseSubscription:\n{}",
|
||||
ProtoPrinter::to_string(this->cfg));
|
||||
sec_requestor.callback().bind<&PImpl::on_securities>(*this);
|
||||
}
|
||||
|
||||
void start(shared_ptr<IMarketDataListener> listener) {
|
||||
bts_assert(listener != nullptr);
|
||||
this->listener = std::move(listener);
|
||||
sec_requestor.request();
|
||||
}
|
||||
|
||||
@@ -195,7 +202,7 @@ struct CoinbaseSubscription::PImpl {
|
||||
auto sec_id =
|
||||
SecurityID{sec_id_store->get_or_add(VENUE, sec_type, sec_desc)};
|
||||
auto [it, is_new] =
|
||||
stats_handlers.try_emplace(sec_desc, sec_id, listener);
|
||||
stats_handlers.try_emplace(sec_desc, sec_id, listener.get());
|
||||
bts_assert_msgf(
|
||||
is_new, "StatsHandler already created with id '{}'", sec_id);
|
||||
return it->second;
|
||||
@@ -204,7 +211,7 @@ struct CoinbaseSubscription::PImpl {
|
||||
SecurityType sec_type, const string &sec_desc) {
|
||||
SecurityID sec_id{sec_id_store->get_or_add(VENUE, sec_type, sec_desc)};
|
||||
auto [it, is_new] =
|
||||
depth_handlers.try_emplace(sec_desc, sec_id, listener);
|
||||
depth_handlers.try_emplace(sec_desc, sec_id, listener.get());
|
||||
bts_assert(is_new);
|
||||
return it->second;
|
||||
}
|
||||
@@ -213,7 +220,7 @@ struct CoinbaseSubscription::PImpl {
|
||||
SecurityType sec_type, const string &sec_desc) {
|
||||
SecurityID sec_id{sec_id_store->get_or_add(VENUE, sec_type, sec_desc)};
|
||||
auto [it, is_new] =
|
||||
ticker_handlers.try_emplace(sec_desc, sec_id, listener);
|
||||
ticker_handlers.try_emplace(sec_desc, sec_id, listener.get());
|
||||
bts_assert_msgf(
|
||||
is_new, "Ticker handler already created for '{}'", sec_desc);
|
||||
return it->second;
|
||||
@@ -285,10 +292,8 @@ struct CoinbaseSubscription::PImpl {
|
||||
CoinbaseSubscription::CoinbaseSubscription(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
CoinbaseMarketDataConfig cfg,
|
||||
IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener)
|
||||
: me_(std::make_unique<PImpl>(
|
||||
dispatcher, std::move(cfg), sec_id_store, listener)) {}
|
||||
IDGenerator *sec_id_store)
|
||||
: me_(std::make_unique<PImpl>(dispatcher, std::move(cfg), sec_id_store)) {}
|
||||
|
||||
CoinbaseSubscription::CoinbaseSubscription(CoinbaseSubscription &&) noexcept =
|
||||
default;
|
||||
@@ -297,4 +302,8 @@ CoinbaseSubscription &CoinbaseSubscription::operator=(
|
||||
|
||||
CoinbaseSubscription::~CoinbaseSubscription() = default;
|
||||
|
||||
void CoinbaseSubscription::start(shared_ptr<IMarketDataListener> listener) {
|
||||
me_->start(std::move(listener));
|
||||
}
|
||||
|
||||
} // namespace bts::md::coinbase
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "server/sparkd/md/IMarketDataSource.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ccapi {
|
||||
@@ -20,13 +22,12 @@ class IDGenerator;
|
||||
|
||||
namespace bts::md::coinbase {
|
||||
|
||||
class CoinbaseSubscription {
|
||||
class CoinbaseSubscription : public IMarketDataSource {
|
||||
public:
|
||||
CoinbaseSubscription(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
api::md::CoinbaseMarketDataConfig cfg,
|
||||
persistence::IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener);
|
||||
persistence::IDGenerator *sec_id_store);
|
||||
|
||||
CoinbaseSubscription(const CoinbaseSubscription &) noexcept = delete;
|
||||
CoinbaseSubscription(CoinbaseSubscription &&) noexcept;
|
||||
@@ -36,8 +37,7 @@ class CoinbaseSubscription {
|
||||
|
||||
~CoinbaseSubscription();
|
||||
|
||||
void add_listener(IMarketDataListener *listener);
|
||||
void remove_listener(IMarketDataListener *listener);
|
||||
void start(std::shared_ptr<IMarketDataListener> listener) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "DatabentoMarketDataProvider.h"
|
||||
#include "lib/api/domain/Venue.pb.h"
|
||||
#include "lib/api/md/MarketDataConfig.pb.h"
|
||||
#include "lib/persistence/IDGenerator.h"
|
||||
#include "server/sparkd/md/IMarketDataSource.h"
|
||||
#include "server/sparkd/md/databento/DatabentoDataHandler.h"
|
||||
#include "server/sparkd/md/glbx/DatabentoGlbxSubscription.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
using bts::api::md::MarketDataConfig;
|
||||
using bts::persistence::IDGenerator;
|
||||
using std::pair;
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
struct DatabentoMarketDataProvider::PImpl {
|
||||
MarketDataConfig config;
|
||||
shared_ptr<IDGenerator> sec_id_store;
|
||||
vector<shared_ptr<IMarketDataSource>> glbx_sources;
|
||||
|
||||
PImpl(MarketDataConfig config, shared_ptr<IDGenerator> sec_id_store)
|
||||
: config(std::move(config)), sec_id_store(std::move(sec_id_store)) {}
|
||||
};
|
||||
|
||||
DatabentoMarketDataProvider::DatabentoMarketDataProvider(
|
||||
MarketDataConfig config, shared_ptr<IDGenerator> sec_id_store)
|
||||
: me_(std::make_unique<PImpl>(std::move(config), std::move(sec_id_store))) {
|
||||
me_->config = std::move(config);
|
||||
}
|
||||
|
||||
DatabentoMarketDataProvider::~DatabentoMarketDataProvider() = default;
|
||||
|
||||
vector<pair<api::domain::Venue, shared_ptr<IMarketDataSource>>>
|
||||
DatabentoMarketDataProvider::get_sources() {
|
||||
vector<pair<api::domain::Venue, shared_ptr<IMarketDataSource>>> rv;
|
||||
for (const auto &glbx : me_->config.glbx()) {
|
||||
if (glbx.use_databento()) {
|
||||
me_->glbx_sources.push_back(
|
||||
std::make_shared<DatabentoGlbxSubscription>(
|
||||
glbx, me_->sec_id_store));
|
||||
rv.push_back({api::domain::VENUE_GLBX, me_->glbx_sources.back()});
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "server/sparkd/md/IMarketDataProvider.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace bts::persistence {
|
||||
class IDGenerator;
|
||||
}
|
||||
|
||||
namespace bts::api::md {
|
||||
class MarketDataConfig;
|
||||
}
|
||||
|
||||
namespace bts::md {
|
||||
|
||||
class DatabentoMarketDataProvider : public IMarketDataProvider {
|
||||
public:
|
||||
explicit DatabentoMarketDataProvider(
|
||||
api::md::MarketDataConfig config,
|
||||
std::shared_ptr<persistence::IDGenerator> sec_id_store);
|
||||
~DatabentoMarketDataProvider();
|
||||
|
||||
std::vector<
|
||||
std::pair<api::domain::Venue, std::shared_ptr<IMarketDataSource>>>
|
||||
get_sources() override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me_;
|
||||
};
|
||||
|
||||
} // namespace bts::md
|
||||
@@ -52,6 +52,7 @@ using bts::io::websocket::JsonRpcId;
|
||||
using bts::io::websocket::JsonRpcRequest;
|
||||
using bts::persistence::IDGenerator;
|
||||
using nlohmann::json;
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::uint32_t;
|
||||
using std::unique_ptr;
|
||||
@@ -134,14 +135,13 @@ struct DeribitSubscription::PImpl {
|
||||
const bool subscribe_trades = true;
|
||||
size_t sub_id_counter = 0;
|
||||
CCApiSession session;
|
||||
IMarketDataListener *listener;
|
||||
shared_ptr<IMarketDataListener> listener;
|
||||
DeribitChannelDemuxer channel_demuxer;
|
||||
|
||||
explicit PImpl(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
DeribitMarketDataConfig cfg,
|
||||
IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener)
|
||||
IDGenerator *sec_id_store)
|
||||
: cfg(std::move(cfg)),
|
||||
criteria(SecurityCriteria::from_proto(this->cfg.security_criteria())),
|
||||
excluded_secs(
|
||||
@@ -159,11 +159,15 @@ struct DeribitSubscription::PImpl {
|
||||
criteria,
|
||||
&instrument_store,
|
||||
{this->cfg.currencies().begin(), this->cfg.currencies().end()}),
|
||||
session(dispatcher, mk_session_cfg(this->cfg), {}),
|
||||
listener(listener) {
|
||||
session(dispatcher, mk_session_cfg(this->cfg), {}) {
|
||||
// DON'T reference the ctr arg 'cfg' (it has been moved to 'this->cfg')
|
||||
_INFO("ctr. config:\n{}", ProtoPrinter::to_string(this->cfg));
|
||||
sec_requestor.callback().bind<&PImpl::on_securities>(*this);
|
||||
}
|
||||
|
||||
void start(std::shared_ptr<IMarketDataListener> listener) {
|
||||
bts_assert(listener != nullptr);
|
||||
this->listener = std::move(listener);
|
||||
sec_requestor.request_all();
|
||||
subscribe_instrument_state();
|
||||
}
|
||||
@@ -172,7 +176,7 @@ struct DeribitSubscription::PImpl {
|
||||
SecurityType sec_type, const string &sec_desc) {
|
||||
const SecurityID sid{sec_id_store->get_or_add(VENUE, sec_type, sec_desc)};
|
||||
auto [it, is_new] = security_data_map.try_emplace(
|
||||
sec_desc, std::make_unique<SecurityData>(sid, listener));
|
||||
sec_desc, std::make_unique<SecurityData>(sid, listener.get()));
|
||||
_ASSERT_MSGF(is_new, "add_security_data. Already added '{}'", sec_desc);
|
||||
_INFO("add_security_data. sec: '{}'", sec_desc);
|
||||
return *it->second;
|
||||
@@ -303,10 +307,8 @@ struct DeribitSubscription::PImpl {
|
||||
DeribitSubscription::DeribitSubscription(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
DeribitMarketDataConfig cfg,
|
||||
IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener)
|
||||
: me_(std::make_unique<PImpl>(
|
||||
dispatcher, std::move(cfg), sec_id_store, listener)) {}
|
||||
IDGenerator *sec_id_store)
|
||||
: me_(std::make_unique<PImpl>(dispatcher, std::move(cfg), sec_id_store)) {}
|
||||
|
||||
DeribitSubscription::DeribitSubscription(DeribitSubscription &&) noexcept =
|
||||
default;
|
||||
@@ -315,4 +317,8 @@ DeribitSubscription &DeribitSubscription::operator=(
|
||||
|
||||
DeribitSubscription::~DeribitSubscription() = default;
|
||||
|
||||
void DeribitSubscription::start(shared_ptr<IMarketDataListener> listener) {
|
||||
me_->start(std::move(listener));
|
||||
}
|
||||
|
||||
} // namespace bts::md::deribit
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "server/sparkd/md/IMarketDataSource.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ccapi {
|
||||
@@ -24,13 +26,12 @@ namespace bts::md::deribit {
|
||||
* @brief Subscribes to market data at Deribit
|
||||
* @a https://docs.deribit.com/?#market-data
|
||||
*/
|
||||
class DeribitSubscription {
|
||||
class DeribitSubscription : public IMarketDataSource {
|
||||
public:
|
||||
DeribitSubscription(
|
||||
ccapi::EventDispatcher *dispatcher,
|
||||
api::md::DeribitMarketDataConfig cfg,
|
||||
persistence::IDGenerator *sec_id_store,
|
||||
IMarketDataListener *listener);
|
||||
persistence::IDGenerator *sec_id_store);
|
||||
|
||||
DeribitSubscription(const DeribitSubscription &) noexcept = delete;
|
||||
DeribitSubscription(DeribitSubscription &&) noexcept;
|
||||
@@ -40,9 +41,8 @@ class DeribitSubscription {
|
||||
|
||||
~DeribitSubscription();
|
||||
|
||||
void add_listener(IMarketDataListener *listener);
|
||||
void remove_listener(IMarketDataListener *listener);
|
||||
|
||||
void start(std::shared_ptr<IMarketDataListener> listener) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me_;
|
||||
|
||||
Loaded 100 of 127 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user