Compare commits

..
Author SHA1 Message Date
John Colvin f2d47a62f4 check pt: Last min changes, before going back to Mich 2023-11-08 19:23:24 -05:00
John Colvin 4be9f399aa Update externals (cme_websocket) 2023-11-06 11:17:59 -05:00
John Colvin aaf55ba65b check pt: Before going to chicago 2023-11-06 11:17:27 -05:00
John Colvin 9edf1dbd1a refactor: Move cme websocket app to lib 2023-11-04 13:32:01 -04:00
John Colvin 2dc9f8d71b clean up cme websocket listener 2023-11-04 13:01:02 -04:00
John Colvin addff775e9 pretty damn good, really 2023-11-03 23:12:58 -04:00
John Colvin f18c952ab8 more cleanup 2023-10-29 23:52:54 -04:00
John Colvin 326b068905 idk 2023-10-29 23:50:06 -04:00
John Colvin 249221c654 cleanup: Clean up more cme-websocket code 2023-10-29 23:41:36 -04:00
John Colvin bc9114e5a0 cleanup: Remove garbage/scratch files 2023-10-29 23:39:04 -04:00
John Colvin 56ae5dad35 cleanup new cme-websockets code 2023-10-29 23:37:39 -04:00
John Colvin 4ea6a9138f cleanup: Clean up new CMakeLists for cme websockets 2023-10-29 23:33:23 -04:00
John Colvin a38b24a910 docker: Install libcurl4-openssl-dev in docker 2023-10-29 23:30:38 -04:00
John Colvin fd9ed211b4 cleanup: Tidy up URL.h/cpp 2023-10-29 23:24:00 -04:00
John Colvin bfc3106b00 check pt: This Works! 2023-10-29 22:57:40 -04:00
John Colvin c8fb032a13 fix cme connector code 2023-10-29 15:15:33 -04:00
John Colvin df11e6564f fix crypto deps 2023-10-29 15:15:20 -04:00
John Colvin 6cf4200bad feat: Add cme_websocket connector code 2023-10-29 15:15:09 -04:00
John Colvin d91642ecd0 cme-websockets: Remove all deps on crypto/ssl in externals
I don't have time for these dependency conflicts with the default apt
websocketpp lib (just need to get this thing working!)
2023-10-29 15:14:07 -04:00
168 changed files with 19993 additions and 3061 deletions

No files matched your search

-3
View File
@@ -1,6 +1,3 @@
[submodule "externals"]
path = externals
url = git@bitbucket.org:bluetradesys/externals.git
[submodule "vcpkg"]
path = vcpkg
url = https://github.com/microsoft/vcpkg.git
+7 -40
View File
@@ -42,8 +42,8 @@ if(WIN32)
add_subdirectory(src/lib)
else()
# make sparkd look for libspark.so in its directory
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH ".")
# set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
# set(CMAKE_INSTALL_RPATH ".")
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install")
set(SPARK_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/$<CONFIG>")
@@ -112,12 +112,14 @@ else()
# subdirs
if (BUILD_SPARKD)
add_subdirectory(src/lib_bts-system)
add_subdirectory(src/lib_bts-http)
add_subdirectory(src/lib_bts-websocket)
add_subdirectory(src/lib_bts-cme-websocket)
add_subdirectory(src/app_bts-cme-websocket-listener)
add_subdirectory(src/server/sparkd)
add_subdirectory(src/data_files)
endif()
if (BUILD_ACTIV_MD)
add_subdirectory(src/app_activ-tester)
endif()
add_subdirectory(src/lib)
if (BUILD_ADMIN_WEB_APP)
add_subdirectory(src/Spark.Admin.WebApp)
@@ -130,36 +132,6 @@ else()
endif()
if(BUILD_SPARKD)
function(copy_shared_lib lib_file_name)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/src/server/sparkd/${lib_file_name}
COMMAND ${CMAKE_COMMAND} -E copy
${EXTERNALS_ROOT}/lib/Debug/${lib_file_name}
${CMAKE_BINARY_DIR}/src/server/sparkd/${lib_file_name}
DEPENDS ${EXTERNALS_ROOT}/lib/Debug/${lib_file_name})
endfunction()
copy_shared_lib(libActivAgentApplication_x86-64_ubuntu22-04_gcc123_mdid.so)
copy_shared_lib(libActivBase_x86-64_ubuntu22-04_gcc123_mdid.so)
copy_shared_lib(libActivFramework_x86-64_ubuntu22-04_gcc123_mdid.so)
copy_shared_lib(libActivOneApi_x86-64_ubuntu22-04_gcc123_mdid.so)
copy_shared_lib(libActivDatabase_x86-64_ubuntu22-04_gcc123_mdid.so)
copy_shared_lib(libActivServices_x86-64_ubuntu22-04_gcc123_mdid.so)
copy_shared_lib(libBoostAtomic_x86-64_ubuntu22-04_gcc123_mdid.so)
copy_shared_lib(libBoostChrono_x86-64_ubuntu22-04_gcc123_mdid.so)
copy_shared_lib(libBoostFilesystem_x86-64_ubuntu22-04_gcc123_mdid.so)
copy_shared_lib(libBoostProgramOptions_x86-64_ubuntu22-04_gcc123_mdid.so)
add_custom_target(activ_shared_libs ALL DEPENDS
${CMAKE_BINARY_DIR}/src/server/sparkd/libActivAgentApplication_x86-64_ubuntu22-04_gcc123_mdid.so
${CMAKE_BINARY_DIR}/src/server/sparkd/libActivBase_x86-64_ubuntu22-04_gcc123_mdid.so
${CMAKE_BINARY_DIR}/src/server/sparkd/libActivFramework_x86-64_ubuntu22-04_gcc123_mdid.so
${CMAKE_BINARY_DIR}/src/server/sparkd/libActivOneApi_x86-64_ubuntu22-04_gcc123_mdid.so
${CMAKE_BINARY_DIR}/src/server/sparkd/libActivDatabase_x86-64_ubuntu22-04_gcc123_mdid.so
${CMAKE_BINARY_DIR}/src/server/sparkd/libActivServices_x86-64_ubuntu22-04_gcc123_mdid.so
${CMAKE_BINARY_DIR}/src/server/sparkd/libBoostAtomic_x86-64_ubuntu22-04_gcc123_mdid.so
${CMAKE_BINARY_DIR}/src/server/sparkd/libBoostChrono_x86-64_ubuntu22-04_gcc123_mdid.so
${CMAKE_BINARY_DIR}/src/server/sparkd/libBoostFilesystem_x86-64_ubuntu22-04_gcc123_mdid.so
${CMAKE_BINARY_DIR}/src/server/sparkd/libBoostProgramOptions_x86-64_ubuntu22-04_gcc123_mdid.so)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/server
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/server
@@ -184,11 +156,6 @@ else()
FILES
"${SPARK_LIB}/libACE-5.7.5.so"
"${SPARK_LIB}/libOnixS.IceImpactMulticastPriceFeedHandlerCpp.so.3.16.0.0"
"${SPARK_LIB}/libActivFramework_x86-64_ubuntu22-04_gcc123_mdid.so"
"${SPARK_LIB}/libActivOneApi_x86-64_ubuntu22-04_gcc123_mdid.so"
"${SPARK_LIB}/libActivServices_x86-64_ubuntu22-04_gcc123_mdid.so"
"${SPARK_LIB}/libBoostAtomic_x86-64_ubuntu22-04_gcc123_mdid.so"
"${SPARK_LIB}/libBoostFilesystem_x86-64_ubuntu22-04_gcc123_mdid.so"
DESTINATION "${SPARK_INSTALL_PREFIX}/bin")
endif()
endif()
+2 -31
View File
@@ -26,7 +26,6 @@
"CMAKE_CXX_STANDARD": "20",
"CMAKE_SHARED_LINKER_FLAGS_DEBUG": "/DEBUG",
"CMAKE_SHARED_LINKER_FLAGS_RELEASE": "/OPT:REF /OPT:ICF /LTCG",
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
"EXTERNALS_ROOT": "${sourceDir}/externals/windows",
"CONFIG_DEFAULTS_PATH": "config/profiles/default/*.conf",
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0",
@@ -46,12 +45,7 @@
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"platform": "",
"CMAKE_BUILD_WITH_INSTALL_RPATH": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_CXX_STANDARD": "20",
"CMAKE_INSTALL_RPATH": ".",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}/sparkd",
"CMAKE_EXE_LINKER_FLAGS": "-Wno-undef",
"EXTERNALS_ROOT": "${sourceDir}/externals/linux",
@@ -113,29 +107,13 @@
"description": "Debug build compiled w/ gcc",
"binaryDir": "${sourceDir}/build/gcc/debug",
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc-12",
"CMAKE_CXX_COMPILER": "g++-12",
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++",
"CMAKE_CXX_FLAGS_DEBUG": "-g -fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++20 -fdiagnostics-color -Wno-stringop-overread -DDEBUG",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/gcc/debug/sparkd"
}
},
{
"name": "activ-tester",
"inherits": "sparkd-debug-gcc",
"displayName": "Activ Tester Debug (gcc)",
"description": "Activ Tester Debug build compiled w/ gcc",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"BUILD_ACTIV_MD": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}/sparkd",
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake"
}
},
{
"name": "sparkd-release-clang",
"inherits": "sparkd-release",
@@ -307,13 +285,6 @@
"inherits": "sparkd"
},
{
"name": "activ-tester",
"configurePreset": "activ-tester",
"displayName": "Activ Tester Debug (gcc)",
"inherits": "sparkd"
},
{
"name": "sparkd-release-clang",
"configurePreset": "sparkd-release-clang",
-124
View File
@@ -1,124 +0,0 @@
# Symbols {
# value {
# venues {
# name: "VENUE_BINANCE_US"
# spot: "BTC"
# spot: "ETH"
# }
# }
# }
#
# MarketDataConfig {
# value {
# binance_us {
# num_depth_levels: 5
# security_criteria {
# clauses {
# security_types: SECURITY_TYPE_SPOT
# symbols: "BTC"
# }
# }
# }
# }
# }
MarketDataConfig {
value {
# binance {
# num_depth_levels: 5
# security_criteria {
# clauses {
# security_types: SECURITY_TYPE_SPOT
# symbols: "BTC"
# symbols: "ETH"
# }
# }
# url_rest_base: "https://testnet.binance.vision"
# url_ws_base: "wss://testnet.binance.vision"
# }
# binance_usds_futures {
# num_depth_levels: 5
# security_criteria {
# clauses {
# security_types: SECURITY_TYPE_FUTURE
# symbols: "BTC"
# symbols: "ETH"
# }
# }
# security_desc_formattings {
# expression: "^(.+)(USD)(?=_)(.*)"
# format: "$1USDT$3"
# }
# url_rest_base: "https://testnet.binancefuture.com"
# url_ws_base: "wss://fstream.binance.com"
# }
binance_us {
num_depth_levels: 5
security_criteria {
clauses {
security_types: SECURITY_TYPE_SPOT
symbols: "BTC"
symbols: "ETH"
}
}
url_rest_base: "https://api.binance.us"
url_ws_base: "wss://stream.binance.us:9443"
}
# deribit {
# num_depth_levels: 10
# security_criteria {
# clauses {
# security_types: SECURITY_TYPE_SPOT
# security_types: SECURITY_TYPE_FUTURE
# security_types: SECURITY_TYPE_OPTION
# security_types: SECURITY_TYPE_SPREAD
# symbols: "BTC"
# symbols: "ETH"
# }
# }
# currencies: "BTC"
# currencies: "USDC"
# url_rest_base: "https://test.deribit.com"
# url_ws_base: "wss://test.deribit.com"
# }
# okx {
# num_depth_levels: 5
# security_criteria {
# clauses {
# security_types: SECURITY_TYPE_SPOT
# security_types: SECURITY_TYPE_FUTURE
# security_types: SECURITY_TYPE_OPTION
# symbols: "BTC"
# symbols: "ETH"
# }
# }
# url_rest_base: "https://aws.okx.com"
# url_ws_base: "wss://wsaws.okx.com:8443"
# is_protocol_ping_pong_enabled: true
# protocol_ping_interval_ms: 60000
# protocol_pong_timeout_ms: 30000
# is_application_ping_pong_enabled: true
# application_ping_interval_ms: 20000
# application_pong_timeout_ms: 10000
# }
# recording {
# num_of_seconds_to_run: 300
# venue: VENUE_DERIBIT
# }
# replay {
# venue: VENUE_DERIBIT
# }
}
}
Symbols {
value {
venues {
name: "VENUE_BINANCE_US"
spot: "BTC"
spot: "ETH"
}
}
}
-210
View File
@@ -1,210 +0,0 @@
cmake_minimum_required(VERSION 3.12)
project(spark)
set(CMAKE_CXX_STANDARD 20)
if(WIN32)
set(platform "windows")
else()
set(platform "linux")
endif()
set(
EXTERNALS_ROOT
"${CMAKE_SOURCE_DIR}/externals/${platform}"
CACHE
STRING
"External dependencies root folder (should contain folders: lib, bin, include")
set(SPARK_BIN "${EXTERNALS_ROOT}/bin")
set(SPARK_INCLUDE "${EXTERNALS_ROOT}/include")
set(SPARK_LIB "${EXTERNALS_ROOT}/lib/$<CONFIG>")
set(BUILD_API_SERVER OFF CACHE BOOL "Build the api server")
set(BUILD_DOCS OFF CACHE BOOL "Build documentation")
set(BUILD_SDK OFF CACHE BOOL "Build the sdk")
if (WIN32)
set(BUILD_SPARKD OFF CACHE BOOL "Build sparkd")
set(BUILD_ADMIN_WEB_APP OFF CACHE BOOL "Build the Admin WebApp")
else()
set(BUILD_SPARKD ON CACHE BOOL "Build sparkd")
set(BUILD_ADMIN_WEB_APP ON CACHE BOOL "Build the Admin WebApp")
endif()
include_directories(BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
link_directories(${SPARK_LIB})
if(BUILD_API_SERVER)
add_compile_definitions(SPARK_ENABLE_API_SERVER)
endif()
if(WIN32)
add_compile_definitions(SHARED_MEMORY_PREFIX="spark")
add_compile_definitions(APP_DATA_DIR="\$\{APPDATA\}/bts/")
# subdirs
add_subdirectory(src/lib)
else()
# make sparkd look for libspark.so in its directory
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH ".")
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install")
set(SPARK_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/$<CONFIG>")
if(BUILD_API_SERVER)
add_compile_definitions(APP_DATA_DIR="\$\{HOME\}/spark_api/")
add_compile_definitions(SHARED_MEMORY_PREFIX="spark_api")
elseif(BUILD_SPARKD)
add_compile_definitions(SHARED_MEMORY_PREFIX="sparkd")
add_compile_definitions(APP_DATA_DIR="\$\{HOME\}/sparkdata/")
# Note: All macros that begin w/ 'CCAPI' are ccapi's macros (defined and/or
# read by their code). We override some below.
add_compile_definitions(SPARK_ENABLE_CCAPI)
# This allows us to override hardcoded exchange-urls with ones read from
# config (i.e., resolved at runtime rather than compile-time)
add_compile_definitions(CCAPI_EXPOSE_INTERNAL)
# Enable ccapi services
add_compile_definitions(CCAPI_ENABLE_SERVICE_MARKET_DATA)
add_compile_definitions(CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT)
# Enable ccapi exchanges
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_COIN_FUTURES)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_COINBASE)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_DERIBIT)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_OKX)
set(CCAPI_LOG_LEVEL
"Warn"
CACHE STRING "<Fatal|Error|Warn|Info|Debug|Trace> Log level for ccapi")
string(TOUPPER "${CCAPI_LOG_LEVEL}" _ccapi_log_level_upper)
add_compile_definitions("CCAPI_ENABLE_LOG_${_ccapi_log_level_upper}")
# ** Binance **
add_compile_definitions(CCAPI_BINANCE_URL_REST_BASE="https://api.binance.com")
add_compile_definitions(BINANCE_INSTRUMENTS_PATH="/api/v3/exchangeInfo")
add_compile_definitions(BINANCE_PRICE_STATS_PATH="/api/v3/ticker/24hr")
# ** Binance US **
add_compile_definitions(BINANCE_US_INSTRUMENTS_PATH="/api/v3/exchangeInfo")
add_compile_definitions(BINANCE_US_PRICE_STATS_PATH="/api/v3/ticker/24hr")
# ** Binance USDS Futures **
add_compile_definitions(CCAPI_BINANCE_USDS_FUTURES_URL_REST_BASE="https://dapi.binance.com")
add_compile_definitions(BINANCE_USDS_FUTURES_INSTRUMENTS_PATH="/dapi/v1/exchangeInfo")
add_compile_definitions(BINANCE_USDS_FUTURES_PRICE_STATS_PATH="/dapi/v1/ticker/24hr")
# ** Deribit **
add_compile_definitions(DERIBIT_INSTRUMENT_PATH="/api/v2/public/get_instrument")
add_compile_definitions(DERIBIT_INSTRUMENTS_PATH="/api/v2/public/get_instruments")
add_compile_definitions(DERIBIT_COMBOS_PATH="/api/v2/public/get_combos")
add_compile_definitions(DERIBIT_TICKER_PATH="/api/v2/public/ticker")
add_compile_definitions(DERIBIT_CURRENCIES_METHOD="public/get_currencies")
add_compile_definitions(DERIBIT_INSTRUMENT_METHOD="public/get_instrument")
add_compile_definitions(DERIBIT_INSTRUMENTS_METHOD="public/get_instruments")
add_compile_definitions(DERIBIT_COMBO_DETAILS_METHOD="public/get_combo_details")
add_compile_definitions(DERIBIT_COMBOS_METHOD="public/get_combos")
# ** Okx **
add_compile_definitions(OKX_INSTRUMENTS_PATH="/api/v5/public/instruments")
add_compile_definitions(OKX_UNDERLYING_PATH="/api/v5/public/underlying")
endif()
# subdirs
if (BUILD_SPARKD)
add_subdirectory(src/server/sparkd)
add_subdirectory(src/data_files)
endif()
add_subdirectory(src/lib)
if (BUILD_ADMIN_WEB_APP)
add_subdirectory(src/Spark.Admin.WebApp)
endif()
if(BUILD_DOCS)
add_subdirectory(doc/user)
endif()
if(BUILD_SDK)
add_subdirectory(src/sdk)
endif()
if(BUILD_SPARKD)
function(copy_shared_lib lib_file_name)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/src/server/sparkd/${lib_file_name}
COMMAND ${CMAKE_COMMAND} -E copy
${EXTERNALS_ROOT}/lib/Debug/${lib_file_name}
${CMAKE_BINARY_DIR}/src/server/sparkd/${lib_file_name}
DEPENDS ${EXTERNALS_ROOT}/lib/Debug/${lib_file_name})
add_custom_target(copy_shared_lib_${lib_file_name} ALL DEPENDS ${CMAKE_BINARY_DIR}/src/server/sparkd/${lib_file_name})
endfunction()
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/server
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/server/sparkd/sparkd ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/data_files ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/lib/libspark.so ${CMAKE_BINARY_DIR}/server
COMMAND
ln ARGS -sf
${EXTERNALS_ROOT}/lib/Release/libOnixS.IceImpactMulticastPriceFeedHandlerCpp.so.3.16.0.0
${CMAKE_BINARY_DIR}/server
COMMAND
${CMAKE_COMMAND} -E copy
${EXTERNALS_ROOT}/lib/Debug/libActivAgentApplication_x86-64_ubuntu22-04_gcc114_mdid.so
${CMAKE_BINARY_DIR}/server
COMMAND
${CMAKE_COMMAND} -E copy
${EXTERNALS_ROOT}/lib/Debug/libActivBase_x86-64_ubuntu22-04_gcc114_mdid.so
${CMAKE_BINARY_DIR}/server
COMMAND
${CMAKE_COMMAND} -E copy
${EXTERNALS_ROOT}/lib/Debug/libActivFramework_x86-64_ubuntu22-04_gcc114_mdid.so
${CMAKE_BINARY_DIR}/server
COMMAND
${CMAKE_COMMAND} -E copy
${EXTERNALS_ROOT}/lib/Debug/libActivOneApi_x86-64_ubuntu22-04_gcc114_mdid.so
${CMAKE_BINARY_DIR}/server
COMMAND
${CMAKE_COMMAND} -E copy
${EXTERNALS_ROOT}/lib/Debug/libActivDatabase_x86-64_ubuntu22-04_gcc114_mdid.so
${CMAKE_BINARY_DIR}/server
COMMAND
${CMAKE_COMMAND} -E copy
${EXTERNALS_ROOT}/lib/Debug/libActivServices_x86-64_ubuntu22-04_gcc114_mdid.so
${CMAKE_BINARY_DIR}/server
COMMAND
${CMAKE_COMMAND} -E copy
${EXTERNALS_ROOT}/lib/Debug/libBoostAtomic_x86-64_ubuntu22-04_gcc114_mdid.so
${CMAKE_BINARY_DIR}/server
COMMAND
${CMAKE_COMMAND} -E copy
${EXTERNALS_ROOT}/lib/Debug/libBoostChrono_x86-64_ubuntu22-04_gcc114_mdid.so
${CMAKE_BINARY_DIR}/server
COMMAND
${CMAKE_COMMAND} -E copy
${EXTERNALS_ROOT}/lib/Debug/libBoostFilesystem_x86-64_ubuntu22-04_gcc114_mdid.so
${CMAKE_BINARY_DIR}/server
COMMAND
${CMAKE_COMMAND} -E copy
${EXTERNALS_ROOT}/lib/Debug/libBoostProgramOptions_x86-64_ubuntu22-04_gcc114_mdid.so
${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${EXTERNALS_ROOT}/lib/Release/libACE-5.7.5.so ${CMAKE_BINARY_DIR}/server
COMMAND mkdir ARGS -p ${CMAKE_BINARY_DIR}/server/plugins
COMMENT "Creating server runtime layout"
DEPENDS sparkd data_files_dir)
add_custom_target(server_layout ALL DEPENDS ${CMAKE_BINARY_DIR}/server)
install(
DIRECTORY "${CMAKE_BINARY_DIR}/server/plugins"
DESTINATION "${SPARK_INSTALL_PREFIX}/bin")
install(
FILES
"${SPARK_LIB}/libACE-5.7.5.so"
"${SPARK_LIB}/libOnixS.IceImpactMulticastPriceFeedHandlerCpp.so.3.16.0.0"
"${SPARK_LIB}/libActivFramework_x86-64_ubuntu22-04_gcc114_mdid.so"
"${SPARK_LIB}/libActivOneApi_x86-64_ubuntu22-04_gcc114_mdid.so"
"${SPARK_LIB}/libActivServices_x86-64_ubuntu22-04_gcc114_mdid.so"
"${SPARK_LIB}/libBoostAtomic_x86-64_ubuntu22-04_gcc114_mdid.so"
"${SPARK_LIB}/libBoostFilesystem_x86-64_ubuntu22-04_gcc114_mdid.so"
DESTINATION "${SPARK_INSTALL_PREFIX}/bin")
endif()
endif()
-33
View File
@@ -1,33 +0,0 @@
diff.astextplain.textconv=astextplain
filter.lfs.clean=git-lfs clean -- %f
filter.lfs.smudge=git-lfs smudge -- %f
filter.lfs.process=git-lfs filter-process
filter.lfs.required=true
http.sslbackend=openssl
http.sslcainfo=C:/Program Files/Git/mingw64/etc/ssl/certs/ca-bundle.crt
core.autocrlf=true
core.fscache=true
core.symlinks=false
pull.rebase=false
credential.helper=manager
credential.https://dev.azure.com.usehttppath=true
init.defaultbranch=master
status.short=true
core.repositoryformatversion=0
core.filemode=false
core.bare=false
core.logallrefupdates=true
core.symlinks=false
core.ignorecase=true
submodule.active=.
remote.origin.url=git@bitbucket.org:bluetradesys/spark.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.spark_crypto.dev.remote=origin
branch.spark_crypto.dev.merge=refs/heads/spark_crypto.dev
submodule.docker/linux.url=git@bitbucket.org:bluetradesys/linux_docker.git
submodule.docker/windows.url=git@bitbucket.org:bluetradesys/windows_docker.git
submodule.externals.url=git@bitbucket.org:bluetradesys/externals.git
branch.development.remote=origin
branch.development.merge=refs/heads/development
user.name=John Colvin
user.email=john.colvin@bluetradesys.com
-12
View File
@@ -1,12 +0,0 @@
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
remote.origin.url=git@bitbucket.org:bluetradesys/spark.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.spark_crypto.dev.remote=origin
branch.spark_crypto.dev.merge=refs/heads/spark_crypto.dev
branch.development.remote=origin
branch.development.merge=refs/heads/development
submodule.externals.active=true
submodule.externals.url=git@bitbucket.org:bluetradesys/externals.git
-8
View File
@@ -1,8 +0,0 @@
function(init_executable source_dir)
file(
GLOB_RECURSE ${PROJECT_NAME}_Sources
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
${source_dir}/*.cpp)
add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_Sources})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
endfunction(init_executable)
+4
View File
@@ -45,6 +45,10 @@ RUN apt-get update && apt-get install -y \
libssh2-1-dev \
openssh-server
# curl (lib)
RUN apt-get update && apt-get install -y \
libcurl4-openssl-dev
# cmake
RUN \
curl -fsSL https://apt.kitware.com/keys/kitware-archive-latest.asc 2> /dev/null \
-3
View File
@@ -24,6 +24,3 @@
those re-entered explicitly
- With this release we add per user location and smp id tags. At least
the location will have to be set through the admin UI for every user
libBoostFilesystem_x86-64_ubuntu22-04_gcc123_mdid.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=75427020663511112b3aadeef5023e4f368aafd4, stripped
/usr/lib/x86_64-linux-gnu/libboost_filesystem.so.1.74.0: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=f0cb6f85ac07ed93816bae06c584d4a1fabb0e70, stripped
@@ -1,13 +1,13 @@
using System;
using System.Data;
using System.Collections;
using BCG.Utilities;
using BCG.Utilities.General;
namespace BCG
{
// using System;
// using System.Data;
// using System.Collections;
// using BCG.Utilities;
// using BCG.Utilities.General;
// namespace BCG
// {
public class AddColumnsStream : MapStream
{
public AddColumnsStream(ViewStream vs, string columnDef) : base (vs, new ColumnAdder (columnDef)) {}
}
}
// public class AddColumnsStream : MapStream
// {
// public AddColumnsStream(ViewStream vs, string columnDef) : base (vs, new ColumnAdder (columnDef)) {}
// }
// }
@@ -104,13 +104,13 @@ namespace BCG {
if (!p.replayed)
return p;
}
else
{
double secondsElapsed = new BinaryReader(myFileReader).ReadDouble();
myNextPacketTime = myPlaybackStart + new TimeSpan(0, 0, 0, 0, (int)(1000 * secondsElapsed / myPlaybackSpeed));
if (onProgress != null)
onProgress(myFileReader.Position, myFileReader.Length);
}
return null;
}
private PlaybackPacket _get_previous_packet(PlaybackPacket p) {
if (p.idx == 0)
return null;
return _packets[p.idx - 1];
}
#region IWorker Members
@@ -102,6 +102,14 @@ namespace BCG
System.Threading.Thread.Sleep(1000);
}
// Task.Run(async () =>
// {
// while (!_conn.isDisposed)
// {
// await _conn.send_async(buff);
// await Task.Delay(100);
// }
// });
}
public void onData(RemoteMessage msg)
+13016 -1
View File
File diff suppressed because it is too large. Load diff
-51
View File
@@ -1,51 +0,0 @@
cmake_minimum_required(VERSION 3.23)
project(activ-tester)
include(${CMAKE_SOURCE_DIR}/cmake/InitExecutable.cmake)
init_executable(activ-tester)
target_link_libraries(${PROJECT_NAME} PUBLIC
protobuf
double-conversion
boost_system
boost_thread
boost_filesystem
boost_regex
boost_chrono
boost_date_time
boost_atomic
SQLiteCpp
sqlite3
curl
uuid
ssl
crypto
debug fmtd
optimized fmt
tbb
cryptopp
pugixml
libspark
lttng-ust
Pcap++
Packet++
Common++
ssh2
BoostAtomic_x86-64_ubuntu22-04_gcc123_mdid
BoostChrono_x86-64_ubuntu22-04_gcc123_mdid
BoostFilesystem_x86-64_ubuntu22-04_gcc123_mdid
BoostProgramOptions_x86-64_ubuntu22-04_gcc123_mdid
ActivAgentApplication_x86-64_ubuntu22-04_gcc123_mdid
ActivBase_x86-64_ubuntu22-04_gcc123_mdid
ActivDatabase_x86-64_ubuntu22-04_gcc123_mdid
ActivFramework_x86-64_ubuntu22-04_gcc123_mdid
ActivOneApi_x86-64_ubuntu22-04_gcc123_mdid
ActivServices_x86-64_ubuntu22-04_gcc123_mdid
-lrt
-ldl
-lpcap
-static-libgcc
-static-libstdc++
-pthread)
# cli11
find_package(CLI11 CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC CLI11::CLI11)
@@ -1,75 +0,0 @@
// #include "lib/md/cme/CMEActivSession.h"
// #include "lib/testing/catch_test_runner.h"
// #include "lib/utils/init_spark_lib.h"
// #include "server/sparkd/cmds/AdminReport.h"
// #include "server/sparkd/cmds/ConfigCmd.h"
// #include "server/sparkd/cmds/KeyCmd.h"
// #include "server/sparkd/cmds/Md.h"
// #include "server/sparkd/cmds/QuickTest.h"
// #include "server/sparkd/cmds/Run.h"
// #include "server/sparkd/cmds/Web.h"
// #include "server/sparkd/cmds/crypto/CryptoCmd.h"
#include <CLI/CLI.hpp>
#include <functional>
#include <iostream>
#include <string>
// using namespace bts;
void print_args(int argc, char **argv) {
if (argc < 2) {
std::cout << "No args" << std::endl;
return;
}
for (int i = 1; i < argc; i++) {
std::cout << "arg " << i << ": " << argv[i] << std::endl;
}
}
int main(int argc, char **argv) {
print_args(argc, argv);
CLI::App app("Futures trading system", "sparkd");
app.require_subcommand();
// app.add_subcommand("")
return 0;
}
// int main(int argc, char **argv) {
// utils::SparkLibInit init;
// // The catch testing framework has its own command line parser so
// // if we are testing we just forward the entire command to catch
// if (argc > 1 && std::string(argv[1]) == "test")
// return run_tests(argc, argv);
// else {
// CLI::App app("Futures trading system", "sparkd");
// app.require_subcommand();
// app.add_subcommand(CLI::App_p(new cmds::AdminReport()));
// app.add_subcommand(CLI::App_p(new cmds::CryptoCmd()));
// app.add_subcommand(CLI::App_p(new cmds::ConfigCmd()));
// app.add_subcommand(CLI::App_p(new cmds::Md()));
// app.add_subcommand(CLI::App_p(new cmds::QuickTest()));
// app.add_subcommand(CLI::App_p(new cmds::Run()));
// app.add_subcommand(CLI::App_p(new cmds::KeyCmd()));
// app.add_subcommand(CLI::App_p(new cmds::Web()));
// // Activ md example
// struct {
// std::string host{"aop-ny4-replay-1w.activfinancial.com"};
// std::string user{"bltdsy-replay"};
// std::string password{"redhawk0281"};
// CLI::App *cmd;
// } activ;
// activ.cmd = app.add_subcommand("activ-md", "Run Activ md demo");
// activ.cmd->add_option("--host", activ.host, "Activ host (server)");
// activ.cmd->add_option("--user", activ.user, "Activ login user");
// activ.cmd->add_option("--pass", activ.password, "Activ login password");
// activ.cmd->callback([&activ]() {
// md::cme::CMEActivSession::run(activ.host, activ.user, activ.password);
// });
// app.add_subcommand(
// "test", "Run unit test(s)"); // so this shows up in help
// CLI11_PARSE(app, argc, argv);
// }
// }
@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.23)
project(bts-cme-websocket-listener)
file(
GLOB_RECURSE ${PROJECT_NAME}_Sources
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
${PROJECT_NAME}/*.cpp)
add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_Sources})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${PROJECT_NAME} PUBLIC bts-system bts-http bts-websocket bts-cme-websocket)
# # cli11
# FetchContent_Declare(
# cli11
# GIT_REPOSITORY git@github.com:CLIUtils/CLI11.git
# GIT_TAG v2.3.2
# SYSTEM
# OVERRIDE_FIND_PACKAGE
# )
# FetchContent_MakeAvailable(cli11)
# target_include_directories(${PROJECT_NAME} PUBLIC ${cli11_SOURCE_DIR}/include)
# target_link_libraries(${PROJECT_NAME} PUBLIC CLI11)
@@ -0,0 +1,73 @@
#pragma once
#include "bts-cme-websocket/cme_websocket_session.h"
#include "bts-cme-websocket/cme_websocket_session_config.h"
#include "bts-system/json/json_dumper.h"
#include "bts-system/json/json_reader.h"
#include <curlpp/cURLpp.hpp>
#include <fstream>
#include <iostream>
namespace bts::websocket {
class CmeWebsocketListener {
static CmeWebsocketSessionConfig parse_config(
const std::string &config_arg) {
std::ifstream cfg_ifs{config_arg};
const auto cfg_j = nlohmann::json::parse(cfg_ifs);
bts::system::json::JsonReader cfg_jr{&cfg_j};
CmeWebsocketSessionConfig cfg;
bts_assert(cfg_jr.read(cfg));
cfg.websocket.timeout = bts::system::TimeSpan::from_seconds(20);
return cfg;
}
void on_trade_ticker(const std::vector<CmeTradeSummaryPayload> &payload) {
LOG(Md, Info, "on_trade_ticker.\n{}",
system::json::JsonDumper::dump(payload.begin(), payload.end()));
}
static void on_top_of_book(const std::vector<CmeTopOfBookPayload> &payload) {
LOG(Md, Info, "on_top_of_book.\n{}",
system::json::JsonDumper::dump(payload.begin(), payload.end()));
}
public:
void run(const std::string &config_arg) {
const auto cfg = parse_config(config_arg);
// Ensure curpp::initialize/terminate are called (even if exception is thrown)
struct CurlInit {
CurlInit() { curlpp::initialize(); }
~CurlInit() { curlpp::terminate(); }
} _CURL_INIT;
// Init websocket session (it won't actually connect until we call
// `CmeWebsocketSession::start`)
CmeWebsocketSession session{cfg};
// Bind various callbacks
// Bind 'trade_ticker' callback to a member function
session.trade_ticker_callback()
.bind<&CmeWebsocketListener::on_trade_ticker>(*this);
// Bind 'top_of_book' callback to a static function
session.top_of_book_callback()
.bind<&CmeWebsocketListener::on_top_of_book>();
// Connect to the websocket server
session.start();
// Send subscription request (should start receiving messages in the
// callbacks we bound above)
session.subscribe(
{{CmeSubscriptionTopic::TRADE_TICKER, {"XCME", "FUT", "BTC"}},
{CmeSubscriptionTopic::TOP_OF_BOOK, {"XCME", "FUT", "BTC"}}});
static const int sleep_secs = 3;
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(sleep_secs));
}
}
};
} // namespace bts::websocket
@@ -0,0 +1,126 @@
#include "bts-cme-websocket-listener/cme_websocket_listener.h"
#include "bts-cme-websocket/cme_message_handler.h"
#include "bts-cme-websocket/cme_message_handler_proxy.h"
#include "bts-cme-websocket/cme_message_logger.h"
#include "bts-cme-websocket/cme_subscription_topic.h"
#include "bts-cme-websocket/cme_websocket_session.h"
#include "bts-cme-websocket/cme_websocket_session_config.h"
#include "bts-cme-websocket/messages/cme_product_key.h"
#include "bts-cme-websocket/messages/cme_trade_summary_payload.h"
#include "bts-system/UNUSED.h"
#include "bts-system/json/json_dumper.h"
#include "bts-system/json/json_reader.h"
#include "bts-websocket/websocket_client.h"
#include <CLI11/CLI11.hpp>
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <curlpp/cURLpp.hpp>
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>
#include <nlohmann/json_fwd.hpp>
#include <string>
#include <vector>
using bts::system::json::JsonDumper;
using bts::system::json::JsonReader;
using bts::websocket::CmeMessageHandlerProxy;
using bts::websocket::CmeSubscriptionTopic;
using bts::websocket::CmeTopOfBookPayload;
using bts::websocket::CmeTradeSummaryPayload;
using bts::websocket::CmeWebsocketSession;
using bts::websocket::CmeWebsocketSessionConfig;
using CLI::App;
using std::ifstream;
using std::string;
using std::vector;
static CmeWebsocketSessionConfig parse_config(const string &config_arg) {
ifstream cfg_ifs{config_arg};
const auto cfg_j = nlohmann::json::parse(cfg_ifs);
JsonReader cfg_jr{&cfg_j};
CmeWebsocketSessionConfig cfg;
bts_assert(cfg_jr.read(cfg));
cfg.websocket.timeout = bts::system::TimeSpan::from_seconds(20);
return cfg;
}
static void on_trade_ticker(const vector<CmeTradeSummaryPayload> &payload) {
LOG(Md, Info, "on_trade_ticker.\n{}",
JsonDumper::dump(payload.begin(), payload.end()));
}
static void on_top_of_book(const vector<CmeTopOfBookPayload> &payload) {
LOG(Md, Info, "on_top_of_book.\n{}",
JsonDumper::dump(payload.begin(), payload.end()));
}
static void run_alt(const std::string &config_arg) {
bts::websocket::CmeWebsocketListener listener{};
listener.run(config_arg);
}
static void run(const std::string &config_arg) {
if (config_arg.size() < 999999) {
run_alt(config_arg);
return;
}
const auto cfg = parse_config(config_arg);
struct CurlInit {
CurlInit() { curlpp::initialize(); }
~CurlInit() { curlpp::terminate(); }
} _CURL_INIT;
// Init websocket session (it won't actually connect until we call
// `CmeWebsocketSession::start`)
CmeWebsocketSession session{cfg};
// Bind callbacks
session.trade_ticker_callback().bind<&on_trade_ticker>();
session.top_of_book_callback().bind<&on_top_of_book>();
// Connect to the websocket server
session.start();
// Send subscription request (should start receiving messages in the
// callbacks we bound above)
session.subscribe(
{{CmeSubscriptionTopic::TRADE_TICKER, {"XCME", "FUT", "BTC"}},
{CmeSubscriptionTopic::TOP_OF_BOOK, {"XCME", "FUT", "BTC"}}});
// std::vector<bts::websocket::CmeProductKey>{{"XCME", "FUT", "BTC"}},
// std::vector<bts::websocket::CmeSubscriptionTopic>{
// bts::websocket::CmeSubscriptionTopic::TRADE_TICKER},
// &trade_handler);
static const int sleep_secs = 3;
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(sleep_secs));
// string text{R"({
// "header": {
// "messageType": "subscribe",
// "sentTime": "",
// "version": "",
// "requestId": "1",
// "token": ""
// },
// "payload": {
// "subscriptionMessageTypes": ["TRD"],
// "subscriptions": [
// {
// "exchangeMic": "XCME",
// "productType": "FUT",
// "productCode": "BTC"
// }
// ]
// }
// })"};
}
}
int main(int argc, char **argv) {
App app{"Cme websocket listener", "bts-websocket-listener"};
string config_arg;
app.add_option(
"--config", config_arg, "Path to CmeWebsocketConfig json file")
->required();
app.callback([&config_arg]() { run(config_arg); });
CLI11_PARSE(app, argc, argv);
}
@@ -0,0 +1,35 @@
{
"header": {
"messageType": "TS",
"sentTime": "null",
"version": "1.0"
},
"payload": [
{
"lastUpdateTime": "2023-01-23T10:00:12.509489271Z",
"tradeSummary": {
"aggressorSide": "2",
"mdTradeEntryId": "172277",
"tradePrice": "22855",
"tradeQty": "1",
"tradeOrderCount": "2",
"tradeUpdateAction": "NEW",
"orderQty": {
"orderId": "2",
"lastOrdQty": "1"
}
},
"instrument": {
"definitionSource": "E",
"exchangeMic": "XCME",
"id": "64598",
"marketSegmentId": "74",
"periodCode": "202301",
"productCode": "BTC",
"productType": "FUT",
"productGroup": "BF",
"symbol": "BTCF3"
}
}
]
}
@@ -0,0 +1,26 @@
{
"websocket": {
"url": "wss://markets.api.uat.cmegroup.com/marketdatastream/v1",
"auth": {
"oauth": {
"url": "https://authnr.cmegroup.com/as/token.oauth2",
"client_id": "api_thinkalpha321",
"client_secret": "4T39A4t_G#7erR$TYgrJ9hmT"
}
}
},
"subscriptions": [
{
"topics": [
"TRD"
],
"products": [
{
"exchangeMic": "XCME",
"productType": "FUT",
"productCode": "BTC"
}
]
}
]
}
@@ -1197,6 +1197,7 @@ namespace System.Collections.Generic {
/// A tuple whose items each represent the sum of their repsective items in
/// <paramref name="source"/>
/// </returns>
[RequiresPreviewFeatures]
public static (T1, T2) Sum<T1, T2>(this IEnumerable<(T1, T2)> source)
where T1 : INumber<T1>
where T2 : INumber<T2> =>
@@ -1209,6 +1210,7 @@ namespace System.Collections.Generic {
/// A tuple whose items each represent the sum of their repsective items in
/// <paramref name="source"/>
/// </returns>
[RequiresPreviewFeatures]
public static (T1, T2) Sum<T1, T2>(this IEnumerable<(T1, T2)> source, T1 seed1, T2 seed2)
where T1 : IAdditionOperators<T1, T1, T1>
where T2 : IAdditionOperators<T2, T2, T2> =>
@@ -1222,6 +1224,7 @@ namespace System.Collections.Generic {
/// A tuple whose items each represent the sum of their repsective items in
/// <paramref name="source"/>
/// </returns>
[RequiresPreviewFeatures]
public static (T1, T2, T3) Sum<T1, T2, T3>(this IEnumerable<(T1, T2, T3)> source)
where T1 : INumber<T1>
where T2 : INumber<T2>
@@ -1235,6 +1238,7 @@ namespace System.Collections.Generic {
/// A tuple whose items each represent the sum of their repsective items in
/// <paramref name="source"/>
/// </returns>
[RequiresPreviewFeatures]
public static (T1, T2, T3) Sum<T1, T2, T3>(
this IEnumerable<(T1, T2, T3)> source, T1 seed1, T2 seed2, T3 seed3)
where T1 : IAdditionOperators<T1, T1, T1>
@@ -1251,6 +1255,7 @@ namespace System.Collections.Generic {
/// A tuple whose items each represent the sum of their repsective items in
/// <paramref name="source"/>
/// </returns>
[RequiresPreviewFeatures]
public static (T1, T2, T3, T4) Sum<T1, T2, T3, T4>(this IEnumerable<(T1, T2, T3, T4)> source)
where T1 : INumber<T1>
where T2 : INumber<T2>
@@ -1265,6 +1270,7 @@ namespace System.Collections.Generic {
/// A tuple whose items each represent the sum of their repsective items in
/// <paramref name="source"/>
/// </returns>
[RequiresPreviewFeatures]
public static (T1, T2, T3, T4) Sum<T1, T2, T3, T4>(
this IEnumerable<(T1, T2, T3, T4)> source, T1 seed1, T2 seed2, T3 seed3, T4 seed4)
where T1 : IAdditionOperators<T1, T1, T1>
@@ -0,0 +1,21 @@
{
"header": {
"messageType": "subscriptionStatus",
"requestId": 0,
"sentTime": "2023-11-01T15:21:12.376000000Z",
"version": "1.0",
"sequenceNumber": "2"
},
"payload": {
"status": "subscribed",
"subscriptionMessageTypes": [
"TRD"
],
"subscriptions": [
{
"productType": "FUT",
"productCode": "BTC"
}
]
}
}
+31
View File
@@ -0,0 +1,31 @@
{
"header": {
"messageType": "TOB",
"sentTime": "2023-11-08T20:10:16.439000000Z",
"sequenceNumber": "2",
"version": "1.0"
},
"payload": [
{
"instrument": {
"definitionSource": "E",
"exchangeMic": "XCME",
"id": "42013807",
"marketSeqmentId": "74",
"periodCode": "",
"productCode": "BTC",
"productGroup": "BF",
"productType": "FUT",
"symbol": "BTCG4"
},
"askLevel": [
{
"lastUpdateTime": "2023-11-08T20:03:58.709512329Z",
"orderCnt": "1",
"price": "36460.0",
"qty": "1.0"
}
]
}
]
}
+42
View File
@@ -0,0 +1,42 @@
{
"header": {
"messageType": "TRD",
"sentTime": "2023-11-08T18:39:31.545000000Z",
"sequenceNumber": "56",
"version": "1.0"
},
"payload": [
{
"instrument": {
"definitionSource": "E",
"exchangeMic": "XCME",
"id": "87394",
"marketSeqmentId": "74",
"periodCode": "",
"productCode": "BTC",
"productGroup": "BF",
"productType": "FUT",
"symbol": "BTCZ3"
},
"lastUpdateTime": "2023-11-08T04:54:03.451575431Z",
"tradeSummary": {
"aggressorSide": "Buy",
"mdTradeEntryId": "174893",
"tradePrice": "35810.0",
"tradeQty": "1",
"tradeOrderCount": "2",
"tradeUpdateAction": "New",
"orderQty": [
{
"orderId": "74467988366",
"lastQty": "1"
},
{
"orderId": "74467981241",
"lastQty": "1"
}
]
}
}
]
}
@@ -0,0 +1,18 @@
{
"websocket": {
"url": "wss://markets.api.uat.cmegroup.com/marketdatastream/v1",
"auth": {
"oauth": {
"url": "https://authnr.cmegroup.com/as/token.oauth2",
"client_id": "api_thinkalpha321",
"client_secret": "4T39A4t_G#7erR$TYgrJ9hmT"
}
}
},
"subscription_payloads": [],
"thread": {
"name_rx": "websocket_thread",
"exclude": false,
"cores": [1]
}
}
+1 -1
View File
@@ -55,7 +55,7 @@ target_include_directories(
get_link_libraries(_link_libs)
print_list(_link_libs VERBOSE)
target_link_libraries(${PROJECT_NAME} PUBLIC ${_link_libs})
target_link_libraries(${PROJECT_NAME} ${_link_libs})
# Copy binaries to Dotnet build dirs (for csprojs that depend on libspark)
if (DEFINED DOTNET_BINARY_DIR)
-22
View File
@@ -1,22 +0,0 @@
syntax = "proto3";
import "lib/api/domain/SecurityCriteria.proto";
package bts.api.md;
option csharp_namespace = "bts.api.md";
message GlbxMarketDataConfig {
uint32 unique_check_value = 1;
//uint32 num_depth_levels = 1;
//uint32 depth_throttle_ms = 2;
bts.api.domain.SecurityCriteria security_criteria = 3;
// Market data isn't subscribed for securities listed here (even if they are
// included by 'security_criteria'). There are some securities that don't
// fit into our system just yet, such as 'BTCBIDR', which trades in prices
// larger than our cpp 'domain::Price' class supports.
//repeated string excluded_securities = 4;
//string url_rest_base = 5;
//string url_ws_base = 6;
}
+1 -2
View File
@@ -10,7 +10,7 @@ import "lib/api/md/MarketDataReplayConfig.proto";
import "lib/api/md/DeribitMarketDataConfig.proto";
import "lib/api/md/EmumbaDeribitMarketDataConfig.proto";
import "lib/api/md/OkxMarketDataConfig.proto";
import "lib/api/md/GlbxMarketDataConfig.proto";
option csharp_namespace = "bts.api.md";
@@ -24,7 +24,6 @@ message MarketDataConfig {
repeated DeribitMarketDataConfig deribit = 5;
repeated CoinbaseMarketDataConfig coinbase =6;
repeated OkxMarketDataConfig okx = 7;
repeated GlbxMarketDataConfig glbx = 8;
// Add an entry here for all the exchanges that need their market data to be recorded
repeated EmumbaDeribitMarketDataConfig emumba_deribit = 100;
-2
View File
@@ -19,8 +19,6 @@ std::set<bts::api::Venue> MarketDataConfigUtils::get_live_exchanges(
rv.insert(bts::api::VENUE_BINANCE_US);
if (cfg.deribit_size() > 0)
rv.insert(bts::api::VENUE_DERIBIT);
if (cfg.glbx_size()>0)
rv.insert(bts::api::VENUE_GLBX);
return rv;
}
@@ -11,5 +11,4 @@ option cc_enable_arenas = true;
message MarketDataRecordingConfig {
uint32 num_of_seconds_to_run = 10;
bts.api.Venue venue = 20;
uint32 n_book_levels = 30;
}
@@ -25,16 +25,6 @@ function(get_link_libraries dest)
Packet++
Common++
ssh2
BoostAtomic_x86-64_ubuntu22-04_gcc123_mdid
BoostChrono_x86-64_ubuntu22-04_gcc123_mdid
BoostFilesystem_x86-64_ubuntu22-04_gcc123_mdid
BoostProgramOptions_x86-64_ubuntu22-04_gcc123_mdid
ActivAgentApplication_x86-64_ubuntu22-04_gcc123_mdid
ActivBase_x86-64_ubuntu22-04_gcc123_mdid
ActivDatabase_x86-64_ubuntu22-04_gcc123_mdid
ActivFramework_x86-64_ubuntu22-04_gcc123_mdid
ActivOneApi_x86-64_ubuntu22-04_gcc123_mdid
ActivServices_x86-64_ubuntu22-04_gcc123_mdid
-lrt
-ldl
-lpcap
@@ -1,8 +1,8 @@
function(get_link_libraries dest)
find_package(fmt CONFIG REQUIRED)
set(${dest}
crypt32
fmt::fmt
debug fmtd
optimized fmt
libcrypto
libcurl
libboost_system
@@ -9,7 +9,7 @@ HistoricMarketDataConfig {
PlatformMap {
value {
glbx_platform: "PLATFORM_MDP3ONIXS"
glbx_platform: "PLATFORM_SIM"
ice_platform: "PLATFORM_ICEONIXS"
}
}
@@ -24,8 +24,6 @@ PublisherOptions {
hot_growth: 1024
cold_initial: 8192
cold_growth: 2048
channel_prefix: ""
disable_heartbeat: false
}
}
@@ -99,18 +97,11 @@ CMEOptions {
feed_engine {
name: "mdp3feed"
spin_time: 0
channels_per_thread: 3
data_wait: 10
upp_socket_buffer_size: 268435456
}
channel {
id: "*"
feed_engine: "mdp3feed"
use_tcp_replay: false
recovery_queue_size: 640000
lost_packet_wait_timeout_ms: 10
use_natural_refresh: false
join_snapshot_on_gap: false
}
}
}
@@ -231,10 +222,3 @@ MarketDataConfig {
}
}
}
OnixsOptions {
value{
onixs_license: "{default}"
}
}
+1 -5
View File
@@ -9,11 +9,7 @@ OrdersSoftCleanup {
enabled: true
}
}
PlacementInQueue {
vlaue{
enabled: true;
}
}
RiskLimit {
value{
risk_limits{
-185
View File
@@ -1,185 +0,0 @@
#include "lib/utils/bts_assert.h"
#include <any>
#include <iostream>
#include <map>
#include <string>
#include <typeindex>
#include <unordered_map>
#include <variant>
#include <vector>
// this file has all the classes that represent streaming tabular data
// schema ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace bts::streams {
using RowID = int;
// Represents a column of a schema (tuple of type, name and col number). For the type we use
// std::type_index
class ColumnInfo {
public:
ColumnInfo(
const std::string &column_name,
const std::type_index &column_type,
size_t col_index)
: _name(column_name), _type(column_type), _index(col_index) {}
const std::string &column_name() const { return _name; }
size_t col_index() const { return _index; }
const std::type_index &column_type() const { return _type; }
private:
const std::string _name;
const std::type_index _type;
const size_t _index;
};
class StreamSchema {
public:
bool add_column(
const std::string &column_name, const std::type_index &column_type) {
auto it = _col_map.find(column_name);
if (it != _col_map.end()) {
return false;
} else {
_col_map.insert(
{column_name, {column_name, column_type, _col_map.size()}});
return true;
}
}
ColumnInfo const *get_column(const std::string &col_name) const {
auto it = _col_map.find(col_name);
if (it == _col_map.end())
return nullptr;
return &it->second;
}
ColumnInfo *get_column(const std::string &col_name) {
auto it = _col_map.find(col_name);
if (it == _col_map.end())
return nullptr;
return &it->second;
}
size_t size() const { return _col_map.size(); }
private:
std::map<std::string, ColumnInfo> _col_map = {};
};
// row ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class StreamRow {
public:
StreamRow(const StreamSchema &schema) : schema(schema) {
data.reserve(schema.size());
for (size_t i = 0; i < schema.size(); i++) {
std::any any;
data.push_back(any);
}
}
RowID id() const { return get_value<int>("id"); }
template <typename T>
bool set_value(const std::string &columnName, const T &value) {
ColumnInfo const *col_info = schema.get_column(columnName);
if (col_info == nullptr) {
throw std::invalid_argument(
"Column " + columnName + " not found in the schema");
return false;
}
if (col_info->column_type() != typeid(T)) {
throw std::invalid_argument("Column " + columnName + " type mismatch");
return false;
}
data[col_info->col_index()] = value;
return true;
}
template <typename T>
T get_value(const std::string &columnName) const {
ColumnInfo const *col_info = schema.get_column(columnName);
if (col_info == nullptr) {
throw std::invalid_argument(
"Column " + columnName + " not found in the schema");
}
if (col_info->column_type() != typeid(T)) {
throw std::invalid_argument("Column " + columnName + " type mismatch");
}
return std::any_cast<T>(data[col_info->col_index()]);
}
// template <typename T> // T operator[](const std::string &column_name) const { // return get_value<T>(column_name); // }
private:
const StreamSchema &schema; // this is where the actual memory is held
std::vector<std::any> data;
};
// table /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class StreamTable {
private:
std::map<RowID, StreamRow const *> _row_map;
public:
StreamTable(const StreamSchema &schema_) : _schema(schema_) {}
bool add_row(StreamRow const *row) {
RowID id = row->id();
auto it = _row_map.find(id);
if (it != _row_map.end()) {
return false;
} else {
_row_map.insert({id, row});
return true;
}
}
StreamRow const *get_row(RowID id) const {
const auto &it = _row_map.find(id);
if (it == _row_map.end())
return nullptr;
return it->second;
}
StreamRow const *operator[](RowID id) const { return get_row(id); }
size_t size() const { return _row_map.size(); }
const StreamSchema &schema() const { return _schema; }
private:
const StreamSchema &_schema;
};
// stream updates and subscription ////////////////////////////////////////////
class StreamUpdate {
public:
const StreamTable &complete_state;
std::set<RowID> deleted_ids;
std::vector<StreamRow const *> added_rows;
StreamUpdate(
const StreamTable &complete_state_,
const std::set<RowID> deleted_ids_,
const std::vector<StreamRow const *> &added_rows_)
: complete_state(complete_state_), deleted_ids(deleted_ids_),
added_rows(added_rows_) {}
};
class DirectSubsription {};
class CloudSubscirption {};
// template <
// typename T,
// typename = std::enable_if_t<
// std::is_same<T, DirectSubsription>::value ||
// std::is_same<T, CloudSubscirption>::value>>
class StreamSubscription {
private:
std::variant<DirectSubsription, CloudSubscirption> subscription;
const size_t _sub_id;
public:
StreamSubscription(const DirectSubsription &, size_t subscription_id_)
: _sub_id(subscription_id_) {}
StreamSubscription(const CloudSubscirption &, size_t subscription_id_)
: _sub_id(subscription_id_) {}
size_t sub_id() const { return _sub_id; }
};
class IStreamSync {
public:
virtual void on_stream_update(
const StreamSubscription &, const StreamUpdate &stream_update) = 0;
};
class StreamSource {
public:
void subscribe_stream_expression(const StreamSubscription &, IStreamSync &) {
}
};
} // namespace bts::streams
@@ -1,97 +0,0 @@
#include "catch/catch.hpp"
// #include "lib/api/service_helpers/AdminServiceUtils.h"
// #include "lib/api/services/AdminService.server.pb.h"
// #include "lib/config/config.h"
#include "lib/datastreams/Tables/StreamData.h"
// #include "lib/utils/bts_chrono.h"
// #include "lib/utils/helper_macros.h"
#include <memory>
using namespace bts::streams;
TEST_CASE("schema", "[streams]") {
// create schema
bts::streams::StreamSchema schema = {};
schema.add_column("id", std::type_index(typeid(int)));
schema.add_column("sec_id", std::type_index(typeid(uint32_t)));
schema.add_column("bid_price", std::type_index(typeid(double)));
schema.add_column("bid_qty", std::type_index(typeid(uint32_t)));
schema.add_column("ask_price", std::type_index(typeid(double)));
schema.add_column("ask_qty", std::type_index(typeid(uint32_t)));
ColumnInfo const *ci = schema.get_column("bid_price");
REQUIRE(ci != nullptr);
REQUIRE(ci->col_index() == 2);
REQUIRE(ci->column_type() == typeid(double));
}
TEST_CASE("row", "[streams]") {
// create schema
bts::streams::StreamSchema schema = {};
schema.add_column("id", std::type_index(typeid(int)));
schema.add_column("sec_id", std::type_index(typeid(uint32_t)));
schema.add_column("bid_price", std::type_index(typeid(double)));
schema.add_column("bid_qty", std::type_index(typeid(uint32_t)));
schema.add_column("ask_price", std::type_index(typeid(double)));
schema.add_column("ask_qty", std::type_index(typeid(uint32_t)));
ColumnInfo const *ci = schema.get_column("bid_price");
REQUIRE(ci != nullptr);
REQUIRE(ci->col_index() == 2);
REQUIRE(ci->column_type() == typeid(double));
bts::streams::StreamRow r(schema);
r.set_value("id", 0);
r.set_value("sec_id", (uint)0);
r.set_value("bid_price", 1.0);
r.set_value("bid_qty", (uint32_t)42);
r.set_value("ask_price", 1.0);
r.set_value("ask_qty", (uint32_t)42);
REQUIRE(r.get_value<uint32_t>("bid_qty") == 42);
REQUIRE(r.get_value<double>("bid_price") == 1.0);
}
TEST_CASE("table", "[streams]") {
// create schema
bts::streams::StreamSchema schema = {};
schema.add_column("id", std::type_index(typeid(int)));
schema.add_column("sec_id", std::type_index(typeid(uint32_t)));
schema.add_column("bid_price", std::type_index(typeid(double)));
schema.add_column("bid_qty", std::type_index(typeid(uint32_t)));
schema.add_column("ask_price", std::type_index(typeid(double)));
schema.add_column("ask_qty", std::type_index(typeid(uint32_t)));
ColumnInfo const *ci = schema.get_column("bid_price");
REQUIRE(ci != nullptr);
REQUIRE(ci->col_index() == 2);
REQUIRE(ci->column_type() == typeid(double));
bts::streams::StreamRow r1(schema);
r1.set_value("id", 0);
r1.set_value("sec_id", (uint)0);
r1.set_value("bid_price", 1.0);
r1.set_value("bid_qty", (uint32_t)42);
r1.set_value("ask_price", 1.0);
r1.set_value("ask_qty", (uint32_t)42);
REQUIRE(r1.get_value<uint32_t>("bid_qty") == 42);
REQUIRE(r1.get_value<double>("bid_price") == 1.0);
bts::streams::StreamTable table(schema);
table.add_row(&r1);
bts::streams::StreamRow r2(schema);
r2.set_value("id", 1);
r2.set_value("sec_id", (uint)1);
r2.set_value("bid_price", 1.0);
r2.set_value("bid_qty", (uint32_t)52);
r2.set_value("ask_price", 1.0);
r2.set_value("ask_qty", (uint32_t)52);
// REQUIRE(r2["ask_qty"]<uint32_t> == 52);
REQUIRE(r2.get_value<uint32_t>("bid_qty") == 52);
REQUIRE(r2.get_value<double>("bid_price") == 1.0);
table.add_row(&r2);
auto r1_ret = table[0];
REQUIRE(r1_ret != nullptr);
REQUIRE(r1_ret->id() == 0);
REQUIRE(r1_ret->get_value<uint32_t>("bid_qty") == 42);
REQUIRE(r1_ret->get_value<double>("bid_price") == 1.0);
auto r2_ret = table[1];
REQUIRE(r2_ret != nullptr);
REQUIRE(r2_ret->id() == 1);
REQUIRE(r2_ret->get_value<uint32_t>("bid_qty") == 52);
REQUIRE(r2_ret->get_value<double>("bid_price") == 1.0);
}
+1 -40
View File
@@ -37,8 +37,6 @@ class JsonReader {
template <class T>
bool read(const std::string &name, T &result) const;
template <class T>
bool try_read(const std::string &name, T &result) const;
template <class T>
bool read_one_or_more(
@@ -86,14 +84,6 @@ class JsonReader {
template <class T>
bool read(T &result) const;
/**
* Reads a json field into \p result
*
* @note Doesn't log error if read op fails (unlike `read`)
*/
template <class T>
bool try_read(T &result) const;
template <class T>
T read() const;
@@ -145,23 +135,6 @@ inline bool JsonReader::read(const std::string &name, T &result) const {
return true;
}
template <class T>
inline bool JsonReader::try_read(const std::string &name, T &result) const {
if (!json_ || !json_->is_object()) {
return false;
}
auto it = json_->find(name);
if (it == json_->end()) {
return false;
}
try {
it->get_to(result);
} catch (...) {
return false;
}
return true;
}
template <class T>
inline bool JsonReader::read_one_or_more(
const std::string &name, std::vector<T> &result, bool append) const {
@@ -246,18 +219,6 @@ inline bool JsonReader::read(T &result) const {
}
}
template <class T>
inline bool JsonReader::try_read(T &result) const {
try {
json_->get_to(result);
return true;
} catch (...) {
const auto j_str = json_->dump(2);
LOG(md, Error, "Exception parsing json:\n{}", j_str);
return false;
}
}
template <class T>
inline T JsonReader::read() const {
T result;
@@ -301,7 +262,7 @@ inline T JsonReader::read(const std::string &name) const {
template <class T>
inline bool JsonReader::read_or(
const std::string &name, T &result, T default_value) const {
if (try_read(name, result)) {
if (read(name, result)) {
return true;
}
result = default_value;
-45
View File
@@ -1,5 +1,4 @@
#if !defined(_WIN32) && defined(SPARK_ENABLE_CCAPI)
#include "server/sparkd/md/platforms/mdp3_onixs/MDP3Main.h"
#include "MarketDataGateway.h"
#include "lib/api/md/EmumbaDeribitMarketDataConfig.pb.h"
#include "lib/api/md/MarketDataConfig.pb.h"
@@ -142,10 +141,6 @@ void MarketDataGateway::run() {
bts_assert(me_->md_config.emumba_deribit().size() == 1);
_start_emumba_deribit(me_->md_config.emumba_deribit().at(0));
}
if(!me_->md_config.glbx().empty()){
bts_assert(me_->md_config.glbx().size() == 1);
_start_onix_glbx(me_->md_config.glbx().at(0));
}
_start_replays(me_->md_config);
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -241,46 +236,6 @@ void MarketDataGateway::unsubscribe(MarketDataListenerID id) {
LOG(md, Info, "Unsubscribing listener {}", id);
me_->listeners.erase(id);
}
/// glbx //////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
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::md::run_onixs();
}
// deribit ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
-4
View File
@@ -1,7 +1,5 @@
#pragma once
#if !defined(_WIN32) && defined(SPARK_ENABLE_CCAPI)
#include "lib/api/md/GlbxMarketDataConfig.pb.h"
#include "MarketDataListenerID.h"
#include <memory>
@@ -33,8 +31,6 @@ class MarketDataGateway {
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);
};
-52
View File
@@ -1,52 +0,0 @@
#include "CMEActivSession.h"
#include "lib/logging/logging.h"
#include "lib/md/cme/CMEActivSessionHandler.h"
#include <ActivMiddleware/ActivBase/Exception.h>
#include <ActivMiddleware/ActivBase/FieldTypes/ActivFieldTypes.h>
#include <ActivMiddleware/ActivBase/FieldTypes/FieldMap.h>
#include <ActivOneApi/Constants/FieldIds.h>
#include <ActivOneApi/Session.h>
#include <exception>
namespace bts::md::cme {
using Activ::FieldMap;
using Activ::OneApi::Session;
using std::string;
void CMEActivSession::run(const string &host, const string &user, const string &password) {
try {
// Session callback handler.
CMEActivSessionHandler sessionHandler;
FieldMap fields;
// Construct the Session with a default configuration passing in our sessionHandler.
Session session(fields, sessionHandler);
FieldMap connectParameters;
connectParameters.Set(Activ::OneApi::FID_HOST, host.c_str());
connectParameters.Set(Activ::OneApi::FID_USER_ID, user.c_str());
connectParameters.Set(Activ::OneApi::FID_PASSWORD, password.c_str());
// Connect synchronously with a 10 second timeout (will throw on failure).
session.Connect(connectParameters, 10000);
// Alternatively an asynchronous connection can be used.
// Connection completion is notified via ISessionHandler::OnSessionConnect().
// session.AsyncConnect(connectParameters);
// Run the internal session message loop.
session.Run();
} catch (const Activ::Exception &ex) {
std::cerr << "Error running CMEActivSession (activ): " << ex.GetMessage()
<< std::endl;
} catch (const std::exception &ex) {
std::cerr << "Error running CMEActivSession: " << ex.what() << std::endl;
// LOG(md, Error, "Error running CMEActivSession: {}",
// std::string{ex.what()});
}
}
} // namespace bts::md::cme
-15
View File
@@ -1,15 +0,0 @@
#pragma once
#include <string>
namespace bts::md::cme {
class CMEActivSession {
public:
static void run(
const std::string &host,
const std::string &user,
const std::string &password);
};
} // namespace bts::md::cme
-32
View File
@@ -1,32 +0,0 @@
#include "CMEActivSessionHandler.h"
#include <ActivMiddleware/ActivBase/ActivBase.h>
#include <ActivOneApi/Session.h>
namespace bts::md::cme {
using Activ::LogType;
using Activ::StatusCode;
using Activ::String;
using Activ::OneApi::Session;
void CMEActivSessionHandler::OnSessionLogMessage(
Session & /*session*/, const LogType log_type, const String &message) {
std::cout << Activ::LogTypeToString(log_type) << ": " << message
<< std::endl;
}
void CMEActivSessionHandler::OnSessionConnect(Session & /*session*/) {
std::cout << "connected" << std::endl;
}
void CMEActivSessionHandler::OnSessionDisconnect(Session & /*session*/) {
std::cout << "disconnected" << std::endl;
}
void CMEActivSessionHandler::OnSessionError(
Session & /*session*/, const StatusCode statusCode) {
std::string err{Activ::StatusCodeToString(statusCode)};
std::cerr << "CMEActivSessionHandler::OnSessionError. " << err << std::endl;
}
} // namespace bts::md::cme
-24
View File
@@ -1,24 +0,0 @@
#pragma once
#include <ActivMiddleware/ActivBase/ActivBase.h>
#include <ActivOneApi/ISessionHandler.h>
namespace bts::md::cme {
class CMEActivSessionHandler : public Activ::OneApi::ISessionHandler {
public:
static void run();
void OnSessionLogMessage(
Activ::OneApi::Session &session,
const Activ::LogType log_type,
const Activ::String &message) override;
void OnSessionConnect(Activ::OneApi::Session &session) override;
void OnSessionDisconnect(Activ::OneApi::Session &session) override;
void OnSessionError(
Activ::OneApi::Session &session,
const Activ::StatusCode statusCode) override;
};
} // namespace bts::md::cme
-1
View File
@@ -12,7 +12,6 @@
#include "lib/utils/datetime.h"
#include "lib/utils/download.h"
#include <ActivOneApi/OneApi.h>
#include <boost/filesystem.hpp>
#include <iostream>
#include <mutex>
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/json.hpp>
#include <string>
#include "lib/io/URL.h"
namespace bts::md::cme_websocket {
class CmeOauthConfig {
public:
std::string protocol{"https"};
std::string host;
std::uint16_t port{443};
std::string endpoint;
std::string client_id;
std::string client_secret;
io::URL get_url() const;
void get_url(io::URL &dest) const;
};
//////////////////////////////////////////////////
// Definition
//////////////////////////////////////////////////
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
CmeOauthConfig, protocol, host, port, endpoint, client_id, client_secret);
inline io::URL CmeOauthConfig::get_url() const {
io::URL url;
get_url(url);
return url;
}
inline void CmeOauthConfig::get_url(io::URL &dest) const {
dest.fill_host(host);
dest.fill_path(endpoint);
dest.fill_port(port);
dest.fill_protocol("https");
}
} // namespace bts::websocket
@@ -0,0 +1,26 @@
#pragma once
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/json.hpp>
#include <string>
// {
// "access_token":
// "eyJhbGciOiJSUzI1NiIsImtpZCI6IjF0al9MTVFrT2pGVzJWQTZvdkZzOEZHUWViMCIsInBpLmF0bSI6IjEifQ.eyJzY29wZSI6W10sImNsaWVudF9pZCI6ImFwaV90aGlua2FscGhhMzIxIiwiYXVkIjoiTnIiLCJqdGkiOiJ5bzlWZUdOMWxudzBXNHhCIiwic3ViIjoiYXBpX3RoaW5rYWxwaGEzMjEiLCJleHAiOjE2OTc1Nzg2NjJ9.HO-UzBvYcdHH6EP0WbmKNbwpZWUF3UbScUQ6bEF4iZqzPyZ1eFLS20VSDULbUpTc_Kl0g9IltJL4ZhnhRqoAX_cWGpqDCDI4wk-04NrcU0-I7YZuNXpDaTwJLChTASQX0PXmmGYDascdem-8FAEp3EgRMrQPXug4FOfp1aKXlgmB5U0Q5lKbZsy_52n56N928iuieYH63JhWRFG0wvleXeyeDDKJNmizKLpGWuBPRwyUYhZWFNQCxbla2TwDTXBWLlO_eZyPq16SS_NKVssTarmY8ohAwuD7N_D7mQbWcbeggks1B9r2w66DuKotRzIrSuhK4VlbHj94FLyqE2oVYw",
// "token_type": "Bearer",
// "expires_in": 1799
// }
namespace bts::md::cme_websocket {
class CmeOauthResponse {
public:
std::string access_token;
std::string token_type;
std::int64_t expires_in;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
CmeOauthResponse, access_token, token_type, expires_in);
} // namespace bts::md::cme_websocket
@@ -0,0 +1,35 @@
#include "CmeOauthConfig.h"
#include "CmeOauthResponse.h"
#include "lib/io/JsonDumper.h"
#include "lib/io/http/HTTPRequest.h"
#include "lib/io/http/HTTPClient.h"
namespace bts::md::cme_websocket {
class CmeWebsocketOauthRequestor {
public:
using HTTPRequest = io::http::HTTPRequest;
using HTTPClient = io::http::HTTPClient;
static HTTPRequest build_oauth_request(const CmeOauthConfig &cfg) {
HTTPRequest http_req;
http_req.url() = cfg.get_url();
io::QueryParams qp;
qp["grant_type"] = "client_credentials";
qp["client_id"] = cfg.client_id;
qp["client_secret"] = cfg.client_secret;
http_req.body() = qp.to_string();
return http_req;
}
static CmeOauthResponse request_oauth_token(const CmeOauthConfig &cfg) {
HTTPRequest req = build_oauth_request(cfg);
LOG(Gen, Info, "Requesting oauth:\n{}", io::JsonDumper::dump(req));
HTTPClient client;
HTTPResponse res = client.post(req);
LOG(Gen, Info, "Received oauth response:\n{}", io::JsonDumper::dump(res));
const auto j = nlohmann::json::parse(res.body());
return io::JsonReader{&j}.read<OauthResponse>();
}
};
} // namespace bts::md::cme_websocket
@@ -0,0 +1,60 @@
#include "CmeWebsocketSubscription.h"
#include <iostream>
#include <websocketpp/client.hpp>
#include <websocketpp/common/thread.hpp>
#include <websocketpp/config/asio_client.hpp>
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/roles/client_endpoint.hpp>
namespace bts::md::cme_websocket {
struct CmeWebsocketSubscription::PImpl {
// TODO
void connect() {}
static void on_open(websocketpp::connection_hdl hdl) {
(void)(hdl);
// Connection opened, you can send WebSocket messages here
}
int run() {
websocketpp::client<websocketpp::config::asio_tls_client> client;
// Set up connection settings
std::string server_uri = "wss://example.com/websocket_endpoint";
client.init_asio();
client.set_open_handler(&on_open);
// Define the headers, including the Authorization header
websocketpp::lib::error_code ec;
websocketpp::client<websocketpp::config::asio_tls_client>::connection_ptr
con = client.get_connection(server_uri, ec);
if (!ec) {
con->append_header("Authorization", "Bearer your_token_here");
// Connect to the server
client.connect(con);
// Start the WebSocket++ event loop
client.run();
} else {
std::cerr << "Error creating the connection: " << ec.message()
<< std::endl;
}
return 0;
}
};
CmeWebsocketSubscription::CmeWebsocketSubscription()
: me_(std::make_unique<PImpl>()) {
// TODO
}
CmeWebsocketSubscription::~CmeWebsocketSubscription() = default;
int CmeWebsocketSubscription::run() { return me_->run(); }
} // namespace bts::md::cme_websocket
@@ -0,0 +1,17 @@
#pragma once
#include <memory>
namespace bts::md::cme_websocket {
class CmeWebsocketSubscription {
public:
CmeWebsocketSubscription();
~CmeWebsocketSubscription();
int run();
private:
struct PImpl;
std::unique_ptr<PImpl> me_;
};
}
-13
View File
@@ -1,13 +0,0 @@
#pragma once
namespace bts {
namespace md {
class IMDPlatform {
public:
virtual void send_heartbeats();
virtual ~IMDPlatform() {}
};
} // namespace md
} // namespace bts
@@ -1,204 +0,0 @@
#include "DeribitDemoConfiguration.h"
#include "lib/io/JsonReader.h"
#include "lib/logging/logging.h"
#include "lib/utils/FileUtils.h"
#include "rapidjson/istreamwrapper.h"
#include <emumba-deribit-connector/deribit_utils.h>
#include <exception>
#include <fstream>
#include <list>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/json.hpp>
#include <optional>
namespace emumba::deribit_connector::configuration {
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
emumba::deribit_connector::configuration::subscription_config,
public_book,
public_index,
public_markprice,
public_quote,
public_ticker,
public_trade,
private_portfolio,
use_raw_interval,
book_depth,
requests_limit);
// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
// emumba::deribit_connector::configuration::symbol_config,
// symbol_name,
// currency_type,
// cred_index,
// interested_instruments,
// interested_subscriptions,
// interested_in_future,
// interested_in_future_combo,
// interested_in_option,
// interested_in_option_combo);
inline void to_json(
nlohmann::json &j,
const emumba ::deribit_connector::configuration::symbol_config &s) {
j["symbol_name"] = s.symbol_name;
j["currency_type"] = s.currency_type;
j["cred_index"] = s.cred_index;
j["interested_instruments"] = s.interested_instruments;
j["interested_subscriptions"] = s.interested_subscriptions;
j["interested_in_future"] = s.interested_in_future;
j["interested_in_future_combo"] = s.interested_in_future_combo;
j["interested_in_option"] = s.interested_in_option;
j["interested_in_option_combo"] = s.interested_in_option_combo;
}
inline void from_json(
const nlohmann::json &j,
emumba::deribit_connector::configuration::symbol_config &s) {
j.at("symbol_name").get_to(s.symbol_name);
j.at("currency_type").get_to(s.currency_type);
j.at("cred_index").get_to(s.cred_index);
j.at("interested_instruments").get_to(s.interested_instruments);
j.at("interested_subscriptions").get_to(s.interested_subscriptions);
j.at("interested_in_future").get_to(s.interested_in_future);
j.at("interested_in_future_combo").get_to(s.interested_in_future_combo);
j.at("interested_in_option").get_to(s.interested_in_option);
j.at("interested_in_option_combo").get_to(s.interested_in_option_combo);
}
inline void to_json(nlohmann::json &j, const logging_config &l) {
j["severity"] = l.severity;
j["console"] = l.is_console_enabled;
j["file"] = l.is_file_enabled;
j["queuesize"] = l.queue_size;
}
inline void from_json(const nlohmann::json &j, logging_config &l) {
bts::io::JsonReader jr{&j};
jr.read("severity", l.severity);
jr.read("console", l.is_console_enabled);
jr.read("file", l.is_file_enabled);
jr.read("queuesize", l.queue_size);
}
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
reconnection_config, retry_count, retry_interval, reception_timeout);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(order_tracker, uuid, start_id);
inline void to_json(nlohmann::json &j, const trading_session_config &t) {
j["orders_tracker"] = t.orders_tracker;
j["core"] = t.core;
j["cancel_on_disconnect"] = t.cancel_on_disconnect;
j["session"] = nlohmann::json{{"heartbeat_interval", t.heartbeat_interval}};
}
inline void from_json(const nlohmann::json &j, trading_session_config &ts) {
j.at("orders_tracker").get_to(ts.orders_tracker);
j.at("core").get_to(ts.core);
j.at("cancel_on_disconnect").get_to(ts.cancel_on_disconnect);
j.at("session").at("heartbeat_interval").get_to(ts.heartbeat_interval);
}
} // namespace emumba::deribit_connector::configuration
namespace bts::cmds {
static constexpr const char *test_site() {
return "wss://test.deribit.com/ws/api/v2";
}
static std::string read_url(nlohmann::json &doc) {
io::JsonReader jr{&doc};
std::string url;
jr.try_read("uri", url);
if (url.empty()) {
return test_site();
}
return url;
}
template <class Obj>
static void read_instruments(
Obj &obj, std::unordered_set<std::string> &instruments) {
const char *instruments_field = "instruments";
bts_assert(obj.HasMember(instruments_field));
for (auto &instrument : obj[instruments_field].GetArray()) {
instruments.insert(instrument.GetString());
}
}
static std::list<emumba::deribit_connector::configuration::symbol_config>
read_symbol_config(rapidjson::Document &doc) {
using RJR = RapidJsonReader;
std::list<emumba::deribit_connector::configuration::symbol_config> syms_out;
for (auto &sym_in : doc["symbol"].GetArray()) {
auto &sym = syms_out.emplace_back();
sym.symbol_name = RJR::get_string(sym_in, "id");
sym.interested_in_future = RJR::get_bool(sym_in, "interested_in_future");
sym.interested_in_option = RJR::get_bool(sym_in, "interested_in_option");
sym.currency_type = RJR::get_string(sym_in, "currency_type", "usd");
sym.cred_index = 1;
read_instruments(sym_in, sym.interested_instruments);
auto chan_in = sym_in["channel"].GetObject();
auto &subs = sym.interested_subscriptions;
subs.requests_limit = RJR::get_uint(sym_in, "requests_limit", 20u);
subs.book_depth = RJR::get_uint(sym_in, "book_depth", 5u);
subs.public_book = RJR::get_bool(chan_in, "PUBLIC_BOOK");
subs.public_quote = RJR::get_bool(chan_in, "PUBLIC_QUOTE");
subs.public_trade = RJR::get_bool(chan_in, "PUBLIC_TRADE");
subs.public_ticker = RJR::get_bool(chan_in, "PUBLIC_TICKER");
subs.public_markprice = RJR::get_bool(chan_in, "PUBLIC_MARKPRICE");
subs.public_index = RJR::get_bool(chan_in, "PUBLIC_INDEX");
// subs.private_orders = RJR::get_bool(chan_in, "PRIVATE_ORDERS");
// subs.private_trades = RJR::get_bool(chan_in, "PRIVATE_TRADES");
subs.private_portfolio = RJR::get_bool(chan_in, "PRIVATE_PORTFOLIO");
subs.use_raw_interval = RJR::get_bool(sym_in, "raw_enable", true);
}
return syms_out;
}
static std::
map<uint, emumba::deribit_connector::configuration::credential_config>
read_credential_config(rapidjson::Document &doc) {
using RJR = RapidJsonReader;
using namespace emumba::deribit_connector;
std::map<uint, configuration::credential_config> rv;
for (auto &cred_in : doc["credentials"].GetArray()) {
configuration::credential_config rv_i;
rv_i.client_name = RJR::get_string(cred_in, "client_name");
rv_i.client_id = RJR::get_string(cred_in, "id");
rv_i.index = RJR::get_uint(cred_in, "subaccount_index");
rv_i.secret_key = RJR::get_string(cred_in, "secret_key");
rv.insert(
std::pair<uint, configuration::credential_config>(rv_i.index, rv_i));
}
return rv;
}
std::optional<DeribitDemoConfiguration> DeribitDemoConfiguration::read_file(
const std::string &config_path) {
nlohmann::json conf_j;
try {
conf_j =
nlohmann::json::parse(bts::spartan::FileUtils::read_file(config_path));
} catch (const std::exception &ex) {
LOG(md, Error, "Error in parsing config from {}: {}", config_path,
static_cast<int>(conf_j.GetParseError()));
return std::nullopt;
}
DeribitDemoConfiguration conf;
io::JsonReader jr{&conf_j};
jr.try_read("app_logging", conf.logging_configuration);
conf.logging_configuration = read_app_logging(doc);
conf.deribit_url = read_url(conf_j);
conf.trading_config = read_trading_session_config(doc);
conf.symbol_config_list = read_symbol_config(doc);
conf.credential_map = read_credential_config(doc);
return conf;
}
} // namespace bts::cmds
@@ -1,54 +0,0 @@
#pragma once
#include "lib/core/FMT_IMPL.h"
#include <emumba-deribit-connector/deribit_utils.h>
#include <iostream>
#include <list>
#include <map>
#include <optional>
#include <sstream>
#include <string>
namespace bts::cmds {
struct DeribitDemoConfiguration {
std::string deribit_url;
std::list<emumba::deribit_connector::configuration::symbol_config>
symbol_config_list;
emumba::deribit_connector::configuration::logging_config
logging_configuration;
emumba::deribit_connector::configuration::trading_session_config
trading_config;
std::map<uint, emumba::deribit_connector::configuration::credential_config>
credential_map;
static std::optional<DeribitDemoConfiguration> read_file(
const std::string &path);
};
inline std::ostream &operator<<(
std::ostream &out,
emumba::deribit_connector::configuration::credential_config const &c) {
out << "index: " << c.index << " client_name: " << c.client_name
<< " client_id: " << c.client_id << " secret_key: " << c.secret_key;
return out;
}
inline std::ostream &operator<<(
std::ostream &out, DeribitDemoConfiguration const &c) {
out << "deribit_url: " << c.deribit_url
<< " symbols: " << c.symbol_config_list.size()
<< " core: " << c.trading_config.core
<< " heartbeat_interval: " << c.trading_config.heartbeat_interval
<< " cancel_on_disconnect: " << c.trading_config.cancel_on_disconnect
<< " credential_map: " << c.credential_map.size();
for (const auto &p : c.credential_map) {
out << " " << p.second;
}
return out;
}
} // namespace bts::cmds
FMT_IMPL_OSTREAM(bts::cmds::DeribitDemoConfiguration);
@@ -1,16 +0,0 @@
#pragma once
#include "lib/md/platforms/emumba/DeribitDemoConfiguration.h"
#include <emumba-deribit-connector/deribit_handler.h>
#include <emumba-deribit-connector/deribit_listener.h>
#include <emumba-deribit-connector/trading_session_interface.h>
namespace bts::orderman {
struct EMTradingSession {
emumba::deribit_connector::configuration::credential_config credential;
std::shared_ptr<emumba::deribit_connector::trading_session_interface>
trading_interface;
};
} // namespace bts::orderman
-21
View File
@@ -1,21 +0,0 @@
#pragma once
#include "lib/md/platforms/IMDPlatform.h"
#include <memory>
namespace bts::md {
class EmumbaMain : public IMDPlatform {
public:
EmumbaMain();
virtual ~EmumbaMain();
// IMDPlatform implementation
void send_heartbeats() override;
void run();
private:
struct PImpl;
std::unique_ptr<PImpl> me;
};
} // namespace bts::md
+2 -3
View File
@@ -143,7 +143,7 @@ struct MDRecorder::PImpl {
const bts::api::md::MarketDataRecordingConfig &recording_config)
: venue(venue),
settings(MDRecordingSettings{
recording_config.n_book_levels(),
10,
static_cast<uint64_t>(recording_config.num_of_seconds_to_run()) *
1000 * 1000 * 1000,
100000, 1000000000}),
@@ -184,8 +184,7 @@ void MDRecorder::_poll_md() {
uint64_t now_ns = core::TimeStamp::now_ns();
for (auto &kv : me->handles) {
MDRecordingSecEntry &md_entry = kv.second;
if(me->settings.n_book_levels > 0)
_poll_books(md_entry);
_poll_books(md_entry);
_poll_trades(md_entry);
_poll_misc(md_entry);
}
+1 -1
View File
@@ -41,7 +41,7 @@ OnixsTemplLicense onixs_create_temporary_license() {
const auto &options = config::get<OnixsOptions>();
OnixsTemplLicense linfo;
linfo.path = options.onixs_license();
if (linfo.path == "{default}" || linfo.path =="") {
if (linfo.path == "{default}") {
linfo.path = Path{config::get<SystemConfig>().data_path()} / "licenses";
auto ident = boost::filesystem::unique_path("%%%%-%%%%-%%%%-%%%%.lic");
linfo.license = {linfo.path / ident};
+4 -4
View File
@@ -2,10 +2,10 @@
#include "AuthSessionManager.h"
#include "lib/utils/FileUtils.h"
#include "lib/utils/crypto/CryptoInit.h"
#include "lib/utils/crypto/KeyCodeHelper.h"
#include "lib/utils/crypto/PrivateKey.h"
#include "lib/utils/crypto/PublicKey.h"
// #include "lib/utils/crypto/CryptoInit.h"
// #include "lib/utils/crypto/KeyCodeHelper.h"
// #include "lib/utils/crypto/PrivateKey.h"
// #include "lib/utils/crypto/PublicKey.h"
#include "lib/utils/datetime.h"
-12
View File
@@ -1,12 +0,0 @@
#pragma once
namespace bts {
namespace crypto {
class AlgoType {
public:
enum type { RSA = 0 };
};
} // namespace crypto
} // namespace bts
-10
View File
@@ -1,10 +0,0 @@
#include "CipherType.h"
namespace bts {
namespace crypto {
const char *CipherType::name[] = {
"None", "DES-EDE3-CBC", "3des", "idea", "aes128"};
}
} // namespace bts
-13
View File
@@ -1,13 +0,0 @@
#pragma once
namespace bts {
namespace crypto {
class CipherType {
public:
static const char *name[];
enum type { None = 0, DES = 1, TripleDES = 2, IDEA = 3, AES128 = 4 };
};
} // namespace crypto
} // namespace bts
-48
View File
@@ -1,48 +0,0 @@
#ifndef _WIN32
#include "CryptoInit.h"
#include <iostream>
#include <stdlib.h>
#include <tbb/spin_mutex.h>
namespace openssl {
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
} // namespace openssl
namespace bts {
namespace crypto {
static const char rnd_seed[] =
"string to make the random number generator think it has entropy";
bool CryptoInit::init = false;
tbb::spin_mutex _lock;
void CryptoInit::initialize() {
if (!init) {
tbb::spin_mutex::scoped_lock lock(_lock);
if (!init) {
openssl::OpenSSL_add_all_algorithms();
openssl::OpenSSL_add_all_ciphers();
openssl::OpenSSL_add_all_digests();
openssl::RAND_seed(rnd_seed, sizeof rnd_seed);
init = true;
}
}
}
void CryptoInit::cleanup() {
if (init) {
tbb::spin_mutex::scoped_lock lock(_lock);
if (init) {
init = false;
}
}
}
} // namespace crypto
} // namespace bts
#endif
-16
View File
@@ -1,16 +0,0 @@
#pragma once
#include "lib/utils/bts_no_copy.h"
namespace bts {
namespace crypto {
struct CryptoInit {
BTS_NO_COPY(CryptoInit);
static bool init;
static void initialize();
static void cleanup();
};
} // namespace crypto
} // namespace bts
-64
View File
@@ -1,64 +0,0 @@
#ifndef _WIN32
#include "Digest.h"
#include "PrivateKey.h"
#include "lib/utils/bts_assert.h"
namespace openssl {
#include <openssl/evp.h>
#include <openssl/pem.h>
} // namespace openssl
namespace bts {
namespace crypto {
const std::string digest_algo = "sha256";
void Digest::allocate_array(unsigned char **out_digest, unsigned int *out_len) {
*out_len = EVP_MAX_MD_SIZE;
*out_digest = new unsigned char[*out_len];
}
void Digest::deallocate_array(unsigned char *out_digest) {
delete[] out_digest;
}
void Digest::compute_digest(
const unsigned char *data,
unsigned long size,
unsigned char *out_digest,
unsigned int *out_len) {
bts_assert(*out_len >= EVP_MAX_MD_SIZE);
const openssl::EVP_MD *md =
openssl::EVP_get_digestbyname(digest_algo.c_str());
bts_assert(md);
openssl::EVP_MD_CTX *mdctx = openssl::EVP_MD_CTX_create();
openssl::EVP_DigestInit_ex(mdctx, md, NULL);
openssl::EVP_DigestUpdate(mdctx, data, size);
openssl::EVP_DigestFinal_ex(mdctx, out_digest, out_len);
openssl::EVP_MD_CTX_destroy(mdctx);
}
void Digest::compute_digest(const std::string &data, std::string *out_digest) {
unsigned int len = EVP_MAX_MD_SIZE;
unsigned char ret[EVP_MAX_MD_SIZE];
char str[EVP_MAX_MD_SIZE + 1];
compute_digest(
reinterpret_cast<const unsigned char *>(data.c_str()), data.size(),
&ret[0], &len);
for (size_t i = 0; i < len; i++)
sprintf(&str[i * 2], "%02x", ret[i]);
std::string val(str, len * 2);
*out_digest = val;
}
std::string Digest::compute_digest(const std::string &data) {
std::string ret;
compute_digest(data, &ret);
return ret;
}
} // namespace crypto
} // namespace bts
#endif
-26
View File
@@ -1,26 +0,0 @@
#pragma once
#include "lib/utils/bts_no_copy.h"
#include <string>
namespace bts {
namespace crypto {
class Digest {
public:
static void
allocate_array(unsigned char **out_digest, unsigned int *out_len);
static void deallocate_array(unsigned char *out_digest);
static void compute_digest(
const unsigned char *data,
unsigned long size,
unsigned char *out_digest,
unsigned int *out_len);
static void compute_digest(const std::string &data, std::string *out_digest);
static std::string compute_digest(const std::string &data);
};
} // namespace crypto
} // namespace bts
-38
View File
@@ -1,38 +0,0 @@
#ifndef _WIN32
#include "KeyCodeHelper.h"
#include "lib/utils/crypto/CryptoInit.h"
#include "lib/utils/crypto/PrivateKey.h"
#include "lib/utils/crypto/PublicKey.h"
#include "lib/utils/datetime.h"
#include "lib/config/protos/keycode.pb.h"
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
#include <sstream>
#include <string>
namespace bts {
namespace crypto {
bool decrypt_keycode(
const bts::crypto::PublicKey &public_key,
const bts::config::EncryptedKeyCode &kc,
bts::config::KeyCode *dk) {
std::stringstream kss, out;
kss << "-----BEGIN BTS-----\n" + kc.keycode() + "\n-----END BTS-----\n";
if (public_key.decrypt(kss, out)) {
bts::config::KeyCode kc;
google::protobuf::io::IstreamInputStream kc_stream(&out);
google::protobuf::TextFormat::Parse(&kc_stream, &kc);
if (dk) {
*dk = kc;
return true;
}
}
return false;
}
} // namespace crypto
} // namespace bts
#endif
-16
View File
@@ -1,16 +0,0 @@
#pragma once
namespace bts {
namespace config {
class EncryptedKeyCode;
class KeyCode;
} // namespace config
namespace crypto {
class PublicKey;
bool decrypt_keycode(
const bts::crypto::PublicKey &public_key,
const bts::config::EncryptedKeyCode &kc,
bts::config::KeyCode *dk);
} // namespace crypto
} // namespace bts
-50
View File
@@ -1,50 +0,0 @@
#include "PasswordCallback.h"
#include <iostream>
#include <stdio.h>
namespace bts {
namespace crypto {
PasswordCallback::PasswordCallback() : _callback(0) {}
PasswordCallback::PasswordCallback(password_callback cb) : _callback(cb) {}
PasswordCallback::~PasswordCallback() {}
void PasswordCallback::prompt(
const std::string &user_info, std::string &password) {
if (user_info == "")
std::cout << "Enter Private Key Passphrase:";
else
std::cout << "Enter Private Key Passphrase (" + user_info + "): ";
std::cin >> password;
}
void PasswordCallback::request_password(
const std::string &user_info, std::string &password) const {
if (_callback)
_callback(user_info, password);
else {
prompt(user_info, password);
}
}
CachedPassword::CachedPassword() : _pass() {
PasswordCallback::prompt("", _pass);
}
CachedPassword::CachedPassword(const std::string &pass) : _pass(pass) {
if (pass == "")
PasswordCallback::prompt("", _pass);
}
CachedPassword::~CachedPassword() {}
void CachedPassword::request_password(
const std::string &, std::string &password) const {
password = _pass;
}
} // namespace crypto
} // namespace bts
-48
View File
@@ -1,48 +0,0 @@
#pragma once
#include "lib/utils/bts_no_copy.h"
#include <string>
namespace bts {
namespace crypto {
class PasswordCallback {
BTS_NO_COPY(PasswordCallback);
public:
typedef void
password_callback(const std::string &user_info, std::string &password);
public:
PasswordCallback();
PasswordCallback(password_callback cb);
virtual ~PasswordCallback();
static void prompt(const std::string &user_info, std::string &password);
virtual void
request_password(const std::string &user_info, std::string &password) const;
static void
prerequest_password(const std::string &user_info, std::string &password);
protected:
password_callback *_callback;
};
class CachedPassword : public PasswordCallback {
BTS_NO_COPY(CachedPassword);
public:
CachedPassword();
CachedPassword(const std::string &);
virtual ~CachedPassword();
virtual void
request_password(const std::string &user_info, std::string &password) const;
protected:
std::string _pass;
};
} // namespace crypto
} // namespace bts
-297
View File
@@ -1,297 +0,0 @@
#ifndef _WIN32
#include "PrivateKey.h"
#include "CryptoInit.h"
#include "PublicKey.h"
#include "iostreambio.h"
#include "lib/logging/logging.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/helper_macros.h"
#include <cstring>
#include <tbb/spin_mutex.h>
namespace openssl {
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
} // namespace openssl
namespace bts {
namespace crypto {
struct PrivateKey::Pimp {
BTS_NO_COPY(Pimp);
const PasswordCallback *password;
PasswordCallback default_password;
PublicKeyPtr public_key;
Pimp(const PasswordCallback *pass)
: password(pass), default_password(), public_key() {
initialize();
if (!password)
password = &default_password;
}
~Pimp() {}
static void initialize() { CryptoInit::initialize(); }
};
int password_callback(char *buf, int size, int rwflag, void *u) {
UNUSED(rwflag);
std::string data;
if (u) {
PasswordCallback *pwd = reinterpret_cast<PasswordCallback *>(u);
pwd->request_password(data, data);
} else {
PasswordCallback ddefault;
ddefault.request_password(data, data);
}
int sz = data.size();
if ((int)data.size() > size)
sz = size;
std::memcpy(buf, data.c_str(), sz);
return sz;
}
const PublicKeyPtr PrivateKey::public_key() const {
if (!me->public_key)
me->public_key = PublicKey::create(*this);
return me->public_key;
}
const PrivateKeyPtr
PrivateKey::create(int bits, AlgoType::type at, const PasswordCallback *pass) {
openssl::EVP_PKEY *key = openssl::EVP_PKEY_new();
if (at == AlgoType::RSA) {
openssl::RSA *rsa = openssl::RSA_new();
openssl::BIGNUM *bn = openssl::BN_new();
openssl::BN_set_word(bn, RSA_F4);
int keyok = openssl::RSA_generate_key_ex(rsa, bits, bn, 0);
openssl::BN_free(bn);
bts_assert_msg(keyok != -1, "Unable to create key");
int i = openssl::EVP_PKEY_assign_RSA(key, rsa);
bts_assert_msg(1 == i, "Unable to create key");
} else
bts_assert_msg(false, "only support RSA");
return PrivateKeyPtr(new PrivateKey(key, pass));
}
const PrivateKeyPtr PrivateKey::read_file(
const std::string &filename, const PasswordCallback *pass) {
PrivateKey::Pimp::initialize();
PrivateKeyPtr ret;
openssl::BIO *bio = openssl::BIO_new_file(filename.c_str(), "r");
bts_assert_msg(bio, "can't load keyfile");
openssl::EVP_PKEY *key = openssl::PEM_read_bio_PrivateKey(
bio, 0, password_callback, (void *)pass);
if (!key)
LOG(Gen, Error, "Invalid private key passphrase");
else
ret.reset(new PrivateKey(key, pass));
return ret;
}
const PrivateKeyPtr PrivateKey::read_stream(
const std::istream &stream, const PasswordCallback *pass) {
PrivateKey::Pimp::initialize();
PrivateKeyPtr ret;
const_cast<std::istream &>(stream).seekg(0, std::ios::end);
int length = const_cast<std::istream &>(stream).tellg();
const_cast<std::istream &>(stream).seekg(0, std::ios::beg);
char *data = new char[length];
const_cast<std::istream &>(stream).read(data, length);
openssl::BIO *bio = openssl::BIO_new(openssl::BIO_s_mem());
BIO_write(bio, data, length);
delete[] data;
openssl::EVP_PKEY *key = openssl::PEM_read_bio_PrivateKey(
bio, 0, password_callback, (void *)pass);
if (!key)
LOG(Gen, Error, "Invalid private key passphrase");
else
ret.reset(new PrivateKey(key, pass));
return ret;
}
PrivateKey::PrivateKey(openssl::EVP_PKEY *key, const PasswordCallback *pass)
: me(new Pimp(pass)), _key(key) {}
PrivateKey::~PrivateKey() {
if (_key)
EVP_PKEY_free(_key);
}
typedef int encrypt_decrypt_init(openssl::EVP_PKEY_CTX *ctx);
typedef int encrypt_decrypt(
openssl::EVP_PKEY_CTX *ctx,
unsigned char *out,
size_t *outlen,
const unsigned char *in,
size_t inlen);
bool encrypt_decrypt_function(
const std::iostream &in_dat,
std::iostream &out_data,
encrypt_decrypt_init *init_function,
encrypt_decrypt *function,
openssl::EVP_PKEY *key,
bool encrypt) {
UNUSED(encrypt);
openssl::EVP_PKEY_CTX *ctx = openssl::EVP_PKEY_CTX_new(key, 0);
bts_assert(ctx);
if (init_function)
bts_assert(init_function(ctx));
bts_assert(openssl::EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING));
char *name, *header;
unsigned char *in, *out;
long inlen;
size_t outlen;
int i = 0;
iostreambio biostream(const_cast<std::iostream *>(&in_dat));
if (!encrypt) {
auto bio = biostream.getBIO();
i = openssl::PEM_read_bio(bio, &name, &header, &in, &inlen);
} else {
std::iostream seeker(in_dat.rdbuf());
seeker.seekg(0, std::ios::end);
inlen = seeker.tellg();
seeker.seekg(0, std::ios::beg);
in = new unsigned char[inlen];
i = openssl::BIO_read(biostream.getBIO(), in, inlen);
}
// BIO_flush(biostream.getBIO());
BIO_free(biostream.getBIO());
bts_assert_msg(i > 0, "file not read, too short?");
bts_assert(function(ctx, NULL, &outlen, in, inlen)); // to get size
out = new unsigned char[outlen];
bts_assert(function(ctx, out, &outlen, in, inlen));
// if(encrypt)
// delete[] in;
std::string header_name = "BTS", header_header = "";
iostreambio biostream_out(const_cast<std::iostream *>(&out_data));
if (encrypt)
i = openssl::PEM_write_bio(
biostream_out.getBIO(), header_name.c_str(),
const_cast<char *>(header_header.c_str()), out, outlen);
else
i = openssl::BIO_write(biostream_out.getBIO(), out, outlen);
// BIO_flush(biostream_out.getBIO());
BIO_free(biostream_out.getBIO());
// delete[] out;
return (i > 0);
}
int private_encrypt_func(
openssl::EVP_PKEY_CTX *ctx,
unsigned char *out,
size_t *outlen,
const unsigned char *in,
size_t inlen) {
openssl::EVP_PKEY *pk = openssl::EVP_PKEY_CTX_get0_pkey(ctx);
auto rsa = openssl::EVP_PKEY_get0_RSA(pk);
if (out == 0) {
*outlen = openssl::RSA_size(rsa);
return -1;
} else {
return RSA_private_encrypt(
inlen, const_cast<unsigned char *>(in), out, rsa, RSA_PKCS1_PADDING);
}
}
int public_decrypt_func(
openssl::EVP_PKEY_CTX *ctx,
unsigned char *out,
size_t *outlen,
const unsigned char *in,
size_t inlen) {
openssl::EVP_PKEY *pk = openssl::EVP_PKEY_CTX_get0_pkey(ctx);
auto rsa = openssl::EVP_PKEY_get0_RSA(pk);
if (out == 0) {
*outlen = openssl::RSA_size(rsa);
return -1;
} else {
int i = RSA_public_decrypt(
inlen, const_cast<unsigned char *>(in), out, rsa, RSA_PKCS1_PADDING);
out[i] = '\0';
*outlen = i;
return i;
}
}
bool PrivateKey::private_encrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return encrypt_decrypt_function(
in_data, out_data, 0, private_encrypt_func, _key, true);
}
bool PrivateKey::private_decrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return encrypt_decrypt_function(
in_data, out_data, openssl::EVP_PKEY_decrypt_init,
openssl::EVP_PKEY_decrypt, _key, false);
}
bool PrivateKey::public_encrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return encrypt_decrypt_function(
in_data, out_data, openssl::EVP_PKEY_encrypt_init,
openssl::EVP_PKEY_encrypt, _key, true);
}
bool PrivateKey::public_decrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return encrypt_decrypt_function(
in_data, out_data, 0, public_decrypt_func, _key, false);
}
bool PrivateKey::encrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return private_encrypt(in_data, out_data);
}
bool PrivateKey::decrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return private_decrypt(in_data, out_data);
}
void PrivateKey::write(openssl::BIO *bio, CipherType::type cipher_type) const {
// see rsa.c ln 213
const openssl::EVP_CIPHER *ciph =
openssl::EVP_get_cipherbyname(CipherType::name[(int)cipher_type]);
bts_assert_msg(
cipher_type == CipherType::None || ciph, "unknown cipher type");
int nbytes = openssl::PEM_write_bio_PKCS8PrivateKey(
bio, _key, ciph, 0, 0, password_callback, (void *)me->password);
UNUSED(nbytes);
int err = BIO_flush(bio);
UNUSED(err);
}
void PrivateKey::write_file(
const std::string &filename, CipherType::type cipher_type) const {
openssl::BIO *bio = openssl::BIO_new_file(filename.c_str(), "w+");
write(bio, cipher_type);
BIO_free(bio);
}
void PrivateKey::write_stream(
std::iostream &stream, CipherType::type cipher_type) const {
iostreambio biostream_out(&stream);
write(biostream_out.getBIO(), cipher_type);
BIO_free(biostream_out.getBIO());
}
} // namespace crypto
} // namespace bts
#endif
-78
View File
@@ -1,78 +0,0 @@
#pragma once
#include "lib/utils/bts_no_copy.h"
#include "AlgoType.h"
#include "CipherType.h"
#include "PasswordCallback.h"
#include <memory>
#include <iostream>
#include <string>
namespace openssl {
struct evp_pkey_st;
typedef struct evp_pkey_st EVP_PKEY;
struct bio_st;
typedef struct bio_st BIO;
} // namespace openssl
namespace bts {
namespace crypto {
class PrivateKey;
class PublicKey;
typedef std::shared_ptr<PrivateKey> PrivateKeyPtr;
typedef std::shared_ptr<PublicKey> PublicKeyPtr;
class PrivateKey {
BTS_NO_COPY(PrivateKey);
public:
virtual ~PrivateKey();
static const PrivateKeyPtr
create(int bits, AlgoType::type, const PasswordCallback *pass = 0);
static const PrivateKeyPtr
read_file(const std::string &filename, const PasswordCallback *pass = 0);
static const PrivateKeyPtr
read_stream(const std::istream &stream, const PasswordCallback *pass = 0);
public:
virtual void write_file(
const std::string &filename,
CipherType::type cipher_type = CipherType::None) const;
virtual void write_stream(
std::iostream &stream,
CipherType::type cipher_type = CipherType::None) const;
virtual bool
encrypt(const std::iostream &in_dat, std::iostream &out_data) const;
virtual bool
decrypt(const std::iostream &in_dat, std::iostream &out_data) const;
const PublicKeyPtr public_key() const;
protected:
friend class PublicKey;
friend int password_callback(char *buf, int size, int rwflag, void *u);
PrivateKey(openssl::EVP_PKEY *key = 0, const PasswordCallback *pass = 0);
virtual void write(openssl::BIO *bio, CipherType::type cipher_type) const;
bool
private_encrypt(const std::iostream &in_dat, std::iostream &out_data) const;
bool
private_decrypt(const std::iostream &in_dat, std::iostream &out_data) const;
bool
public_encrypt(const std::iostream &in_dat, std::iostream &out_data) const;
bool
public_decrypt(const std::iostream &in_dat, std::iostream &out_data) const;
protected:
struct Pimp;
std::shared_ptr<Pimp> me;
openssl::EVP_PKEY *_key;
};
} // namespace crypto
} // namespace bts
-130
View File
@@ -1,130 +0,0 @@
#include "lib/utils/crypto/PrivateKey.h"
#ifndef _WIN32
#include "PublicKey.h"
#include "lib/config/protos/keycode.pb.h"
#include "lib/logging/logging.h"
#include "lib/utils/FileUtils.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/crypto/iostreambio.h"
#include "lib/utils/helper_macros.h"
#include <sstream>
namespace openssl {
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
} // namespace openssl
namespace bts::crypto {
PublicKey::PublicKey(openssl::EVP_PKEY *key) : PrivateKey(key) {}
PublicKey::PublicKey(const PrivateKey &key) : PrivateKey() {
auto *bio = openssl::BIO_new(openssl::BIO_s_mem());
bts_assert_msg(bio, "Failed to create new BIO");
int res = openssl::PEM_write_bio_PUBKEY(bio, key._key);
bts_assert_msgf(
res == 1, "Failed to read public key (PEM_write_bio_PUBKEY returned {})",
res);
res = BIO_flush(bio);
bts_assert_msgf(
res == 1, "Failed to flush BIO buffer (BIO_flush returned {})", res);
auto *k = openssl::PEM_read_bio_PUBKEY(bio, 0, 0, 0);
bts_assert_msg(k, "Failed to read public key");
BIO_free(bio);
bts_assert(k);
this->_key = k;
}
PublicKey::~PublicKey() = default;
const PublicKeyPtr PublicKey::create(const PrivateKey &private_key) {
PublicKeyPtr ret(new PublicKey(private_key));
return ret;
}
const PublicKeyPtr PublicKey::read_file(const std::string &path) {
auto *bio = openssl::BIO_new_file(path.c_str(), "r");
if (!bio) {
LOG(Gen, Error, "Failed to load public key file '{}'", path);
return nullptr;
}
auto *key = openssl::PEM_read_bio_PUBKEY(bio, 0, 0, 0);
if (!key) {
LOG(Gen, Error, "Failed to read public key file '{}'", path);
return nullptr;
}
return PublicKeyPtr(new PublicKey(key));
}
const PublicKeyPtr PublicKey::read(const bts::config::PublicKey &public_key) {
if (public_key.key_lines().size() > 0) {
return read_string(
fmt::to_string(fmt::join(public_key.key_lines(), "\n")));
}
return read_string(
bts::spartan::FileUtils::read_file(public_key.key_file_path()));
}
const PublicKeyPtr PublicKey::read_string(const std::string &public_key) {
std::stringstream oss;
oss << public_key;
bts::crypto::iostreambio biostream(&oss);
auto *key = openssl::PEM_read_bio_PUBKEY(biostream.getBIO(), 0, 0, 0);
if (!key) {
LOG(Gen, Error, "Invalid public key read");
return nullptr;
}
return PublicKeyPtr(new PublicKey(key));
}
const PublicKeyPtr PublicKey::read_stream(std::istream &stream) {
PublicKeyPtr ret;
stream.seekg(0, std::ios::end);
int length = stream.tellg();
stream.seekg(0, std::ios::beg);
char *data = new char[length];
stream.read(data, length);
openssl::BIO *bio = openssl::BIO_new(openssl::BIO_s_mem());
BIO_write(bio, data, length);
delete[] data;
openssl::EVP_PKEY *key = openssl::PEM_read_bio_PUBKEY(bio, 0, 0, 0);
if (!key)
LOG(Gen, Error, "Invalid public key read");
else
ret.reset(new PublicKey(key));
return ret;
}
void PublicKey::write(openssl::BIO *bio, CipherType::type) const {
// const openssl::EVP_CIPHER* ciph =
// openssl::EVP_get_cipherbyname(CipherType::name[(int)cipher_type]);
// bts_assert_msg(ciph, "unknown cipher type");
int nbytes = openssl::PEM_write_bio_PUBKEY(bio, _key);
UNUSED(nbytes);
int err = BIO_flush(bio);
UNUSED(err);
}
bool PublicKey::encrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return public_encrypt(in_data, out_data);
}
bool PublicKey::decrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return public_decrypt(in_data, out_data);
}
} // namespace bts::crypto
#endif
-36
View File
@@ -1,36 +0,0 @@
#pragma once
#include "PrivateKey.h"
#include "lib/utils/bts_no_copy.h"
namespace bts::config {
class PublicKey;
}
namespace bts::crypto {
class PublicKey : public PrivateKey {
public:
PublicKey(const PrivateKey &);
PublicKey(const PublicKey &) = delete;
PublicKey &operator=(const PublicKey &) = delete;
virtual ~PublicKey();
virtual bool encrypt(
const std::iostream &in_dat, std::iostream &out_data) const;
virtual bool decrypt(
const std::iostream &in_dat, std::iostream &out_data) const;
static const PublicKeyPtr create(const PrivateKey &private_key);
static const PublicKeyPtr read(const bts::config::PublicKey &public_key);
static const PublicKeyPtr read_file(const std::string &filename);
static const PublicKeyPtr read_stream(std::istream &stream);
static const PublicKeyPtr read_string(const std::string &public_key);
protected:
virtual void write(openssl::BIO *bio, CipherType::type cipher_type) const;
protected:
PublicKey(openssl::EVP_PKEY *);
};
} // namespace bts::crypto
-208
View File
@@ -1,208 +0,0 @@
#ifndef _WIN32
#include "iostreambio.h"
#include "lib/logging/logging.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/helper_macros.h"
namespace openssl {
#include "openssl_compat.h"
#include <openssl/bio.h>
#include <openssl/err.h>
} // namespace openssl
using namespace std;
using namespace openssl;
namespace bts {
namespace crypto {
const char *const iostreambio::name = "iostreambio";
const int iostreambio::type = BIO_TYPE_SOURCE_SINK;
/* callback functions for openssl */
static int bwrite(BIO *bio, const char *buf, int len);
static int bread(BIO *bio, char *buf, int len);
// static int bputs(BIO *bio, const char *);
static int bgets(BIO *bio, char *, int);
static long ctrl(BIO *bio, int cmd, long num, void *ptr);
static int create(BIO *bio);
// static int destroy(BIO *bio);
// static long (*callback_ctrl)(BIO *, int, bio_info_cb *);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
BIO_METHOD *BIO_iostream_method() {
static BIO_METHOD *iostream_method = NULL;
if (!iostream_method) {
iostream_method = BIO_meth_new(iostreambio::type, iostreambio::name);
BIO_meth_set_write(iostream_method, bwrite);
BIO_meth_set_read(iostream_method, bread);
BIO_meth_set_ctrl(iostream_method, ctrl);
BIO_meth_set_create(iostream_method, create);
BIO_meth_set_gets(iostream_method, bgets);
}
return iostream_method;
}
#else
static BIO_METHOD iostream_method = {
iostreambio::type,
iostreambio::name,
bwrite,
bread,
NULL, // bputs
bgets,
ctrl,
create,
NULL, // destroy,
NULL // callback_ctrl
};
BIO_METHOD *BIO_iostream_method() { return &iostream_method; }
#endif
iostreambio::iostreambio(iostream *stream) : _bio(0) {
_bio = BIO_new(BIO_iostream_method());
bts_assert(_bio);
BIO_set_data(_bio, (void *)stream);
}
BIO *iostreambio::getBIO() { return _bio; }
static int create(BIO *bio) {
BIO_set_data(bio, NULL);
BIO_set_shutdown(bio, 1);
BIO_set_init(bio, 1);
#if OPENSSL_VERSION_NUMBER < 0x10100000L
/* from bss_mem.c (openssl):
* bio->num is used to hold the value to return on 'empty', if it is
* 0, should_retry is not set
*
* => -1 means the caller can retry, 0: retry is useless
* it is set to 0 since the underlying stream is blocking
*/
bio->num = 0;
#endif
return 1;
}
static long ctrl(BIO *bio, int cmd, long num, void *ptr) {
UNUSED(ptr);
UNUSED(num);
long ret;
iostream *stream = reinterpret_cast<iostream *>(BIO_get_data(bio));
LOG(Gen, Warn, "iostreambio: ctrl called, cmd: {}, num: {}", cmd, num);
switch (cmd) {
case BIO_CTRL_PUSH:
case BIO_CTRL_POP:
ret = 0;
break;
// case BIO_CTRL_GET_CLOSE:
// ret = bio->shutdown;
// break;
// case BIO_CTRL_SET_CLOSE:
// bio->shutdown = (int)num;
// ret = 1;
// break;
case BIO_CTRL_FLUSH:
/* try to flush the underlying stream */
ret = 1;
try {
stream->flush();
} catch (
ios_base::failure &) { /* ignore, the badbit is checked instead */
}
if (stream->bad()) {
LOG(Gen, Error,
"iostreambio: underlying Stream went bad while flushing.");
ret = 0;
}
break;
default:
LOG(Gen, Error, "iostreambio: ctrl called with unhandled cmd: {}", cmd);
ret = 0;
break;
}
return ret;
}
static int bread(BIO *bio, char *buf, int len) {
iostream *stream = reinterpret_cast<iostream *>(BIO_get_data(bio));
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
int num_bytes = 0;
#else
streamsize num_bytes = bio->num;
#endif
try {
/* make sure to read at least 1 byte and then read as much as we can */
// num_bytes = stream->read(buf, 1).readsome(buf+1, len-1) + 1;
int i = 0;
while (stream->good() && i < len) {
buf[i++] = stream->get();
}
num_bytes = i;
} catch (ios_base::failure &) {
}
return num_bytes;
}
static int bgets(BIO *bio, char *buf, int len) {
iostream *stream = reinterpret_cast<iostream *>(BIO_get_data(bio));
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
int num_bytes = 0;
#else
streamsize num_bytes = bio->num;
#endif
try {
/* make sure to read at least 1 byte and then read as much as we can */
// num_bytes = stream->read(buf, 1).readsome(buf+1, len-1) + 1;
int i = 0;
while (stream->good() && i < len) {
buf[i] = stream->get();
if (buf[i] == '\n') {
i++;
break;
}
i++;
}
buf[i] = '\0';
num_bytes = i;
} catch (ios_base::failure &) {
}
return num_bytes;
}
static int bwrite(BIO *bio, const char *buf, int len) {
if (len == 0) {
return 0;
}
iostream *stream = reinterpret_cast<iostream *>(BIO_get_data(bio));
/* write the data */
try {
stream->write(buf, len);
} catch (ios_base::failure &) { /* ignore, the badbit is checked instead */
}
if (stream->bad()) {
LOG(Gen, Error, "iostreambio: underlying Stream went bad while writing.");
return 0;
}
/* flush the underlying stream */
try {
stream->flush();
} catch (ios_base::failure &) { /* ignore, the badbit is checked instead */
}
if (stream->bad()) {
LOG(Gen, Error,
"iostreambio: underlying Stream went bad while flushing (bwrite).");
return 0;
}
return len;
}
} // namespace crypto
} // namespace bts
#endif
-26
View File
@@ -1,26 +0,0 @@
#pragma once
namespace openssl {
struct bio_st;
typedef struct bio_st BIO;
} // namespace openssl
#include <iostream>
namespace bts {
namespace crypto {
class iostreambio {
public:
explicit iostreambio(std::iostream *stream);
openssl::BIO *getBIO();
static const char *const name;
static const int type;
private:
openssl::BIO *_bio;
};
} // namespace crypto
} // namespace bts
-39
View File
@@ -1,39 +0,0 @@
#ifndef OPENSSL_COMPAT_H
#define OPENSSL_COMPAT_H
#include <openssl/crypto.h>
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#include <openssl/evp.h>
#include <openssl/hmac.h>
static inline EVP_MD_CTX *EVP_MD_CTX_new() {
EVP_MD_CTX *ctx;
ctx = (EVP_MD_CTX *)OPENSSL_malloc(sizeof(EVP_MD_CTX));
EVP_MD_CTX_init(ctx);
return ctx;
}
#define EVP_MD_CTX_free(c) \
if (c != NULL) \
OPENSSL_free(c)
static inline HMAC_CTX *HMAC_CTX_new() {
HMAC_CTX *ctx;
ctx = (HMAC_CTX *)OPENSSL_malloc(sizeof(HMAC_CTX));
HMAC_CTX_init(ctx);
return ctx;
}
#define HMAC_CTX_free(c) \
if (c != NULL) \
OPENSSL_free(c)
#define BIO_get_data(b) b->ptr
#define BIO_set_data(b, v) b->ptr = v
#define BIO_set_shutdown(b, v) b->shutdown = v
#define BIO_set_init(b, v) b->init = v
#endif /* OPENSSL_VERSION_NUMBER */
#endif /* OPENSSL_COMPAT_H */
+11
View File
@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.23)
project(bts-cme-websocket)
file(
GLOB_RECURSE ${PROJECT_NAME}_Sources
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
${PROJECT_NAME}/*.cpp)
add_library(${PROJECT_NAME} ${${PROJECT_NAME}_Sources})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${PROJECT_NAME} PUBLIC bts-system bts-http bts-websocket)
@@ -0,0 +1,16 @@
#pragma once
#include "bts-cme-websocket/messages/cme_message.h"
#include "bts-system/delegate.h"
#include <string>
namespace bts::websocket {
class CmeMessageHandler {
public:
virtual ~CmeMessageHandler() = default;
virtual void on_message(const CmeMessage &msg) = 0;
};
} // namespace bts::websocket
@@ -0,0 +1,51 @@
#pragma once
#include "bts-cme-websocket/cme_message_handler.h"
#include "bts-cme-websocket/messages/cme_message.h"
#include "bts-system/delegate.h"
#include <string>
namespace bts::websocket {
/**
* CmeMessageHandler that provides a bindable callback to handle payloads
* (parsed from json to \tparam T).
*/
template <class T = nlohmann::json>
class CmeMessageHandlerProxy : public CmeMessageHandler {
public:
using Callback = system::Delegate<void(T)>;
constexpr Callback &callback();
void on_message(const CmeMessage &msg) override;
private:
Callback callback_;
};
//////////////////////////////////////////////////
// Definition
//////////////////////////////////////////////////
template <class T>
constexpr typename CmeMessageHandlerProxy<T>::Callback &
CmeMessageHandlerProxy<T>::callback() {
return callback_;
}
template <>
inline void CmeMessageHandlerProxy<nlohmann::json>::on_message(
const CmeMessage &msg) {
callback_(msg);
}
template <class T>
inline void CmeMessageHandlerProxy<T>::on_message(const CmeMessage &msg) {
if (msg.payload) {
callback_(msg.payload->get<T>());
}
}
} // namespace bts::websocket
@@ -0,0 +1,19 @@
#pragma once
#include "bts-cme-websocket/cme_message_handler.h"
#include "bts-cme-websocket/messages/cme_message.h"
#include "bts-system/json/json_dumper.h"
#include <nlohmann/json.hpp>
#include <vector>
namespace bts::websocket {
struct CmeMessageLogger : public bts::websocket::CmeMessageHandler {
void on_message(const CmeMessage &msg) override {
LOG(Md, Info, "CmeMessageLogger::on_message.\n{}",
bts::system::json::JsonDumper::dump(msg));
}
};
} // namespace bts::websocket
@@ -0,0 +1,10 @@
#pragma once
#include "bts-system/value_wrapper.h"
namespace bts::websocket {
using CmeSubscriptionId =
system::ValueWrapper<struct CmeSubscriptionTag, std::uint32_t>;
} // namespace bts::websocket
@@ -0,0 +1,83 @@
#pragma once
#include "bts-system/enum_macros.h"
#include "bts-system/fmt_macros.h"
#include "bts-system/hash_macros.h"
#include <nlohmann/json.hpp>
namespace bts::websocket {
// clang-format off
#define FOR_ALL_VALUES(per_value) \
per_value(CmeSubscriptionTopic, NONE) \
per_value(CmeSubscriptionTopic, TRADE_TICKER) \
per_value(CmeSubscriptionTopic, TOP_OF_BOOK) \
per_value(CmeSubscriptionTopic, STAT)
// clang-format on
/**
* Represents subscription topics (message types) at Cme. The names aren't
* verbatim matches with the subscriptionMessageType fields they represent in
* the websocket request json. See CmeWebsocketRequestFactory for details.
*/
ENUM_CLASS_DEF(CmeSubscriptionTopic, FOR_ALL_VALUES)
inline std::string topic_outgoing_name(CmeSubscriptionTopic topic) {
switch (topic.value) {
case CmeSubscriptionTopic::NONE:
return {};
case CmeSubscriptionTopic::TRADE_TICKER:
return "TRD";
case CmeSubscriptionTopic::TOP_OF_BOOK:
return "TOB";
default:
return topic.name();
}
}
inline std::string topic_incoming_name(CmeSubscriptionTopic topic) {
switch (topic.value) {
case CmeSubscriptionTopic::NONE:
return {};
case CmeSubscriptionTopic::TRADE_TICKER:
return "TRD";
case CmeSubscriptionTopic::TOP_OF_BOOK:
return "TOB";
default:
return topic.name();
}
}
// inline CmeSubscriptionTopic topic_from_incoming_name(const std::string &name) {
// if (name == "TS") {
// return CmeSubscriptionTopic::TRADE_TICKER;
// }
// if (name == "OBTS") {
// return CmeSubscriptionTopic::TOP_OF_BOOK;
// }
// return CmeSubscriptionTopic::NONE;
// }
#undef FOR_ALL_VALUES
inline void to_json(nlohmann::json &j, const CmeSubscriptionTopic &t) {
j = t.name();
}
inline void from_json(const nlohmann::json &j, CmeSubscriptionTopic &t) {
if (!CmeSubscriptionTopic::parse(j.get<std::string>(), &t)) {
throw std::runtime_error{fmt::format(
"Failed to parse CmeSubscriptionTopic from '{}'", j.dump())};
}
}
} // namespace bts::websocket
FMT_IMPL(bts::websocket::CmeSubscriptionTopic, t, "{}", t.name());
#define FOR_HASH_MEMBERS(op) op(value)
HASH_IMPL(bts::websocket::CmeSubscriptionTopic, FOR_HASH_MEMBERS);
#undef FOR_HASH_MEMBERS
@@ -0,0 +1,250 @@
#include "cme_websocket_session.h"
#include "bts-cme-websocket/cme_message_handler.h"
#include "bts-cme-websocket/cme_subscription_id.h"
#include "bts-cme-websocket/cme_subscription_topic.h"
#include "bts-cme-websocket/cme_websocket_session_config.h"
#include "bts-cme-websocket/messages/cme_message.h"
#include "bts-cme-websocket/messages/cme_message_header.h"
#include "bts-cme-websocket/messages/cme_product_key.h"
#include "bts-cme-websocket/messages/cme_top_of_book_payload.h"
#include "bts-cme-websocket/messages/cme_trade_summary_payload.h"
#include "bts-cme-websocket/messages/cme_websocket_request.h"
#include "bts-cme-websocket/messages/cme_websocket_request_factory.h"
#include "bts-cme-websocket/ssl_certificate_verifier.h"
#include "bts-cme-websocket/tls_initializer.h"
#include "bts-http/http_client.h"
#include "bts-http/http_request.h"
#include "bts-http/http_response.h"
#include "bts-http/oauth_request.h"
#include "bts-http/oauth_requestor.h"
#include "bts-http/oauth_response.h"
#include "bts-system/UNUSED.h"
#include "bts-system/bts_assert.h"
#include "bts-system/equality_macros.h"
#include "bts-system/hash_macros.h"
#include "bts-system/json/json_dumper.h"
#include "bts-system/json/json_reader.h"
#include "bts-system/overloaded.h"
#include "bts-system/time_span.h"
#include "bts-system/time_stamp.h"
#include "bts-system/tuple_cutter.h"
#include "bts-websocket/websocket_client.h"
#include <atomic>
#include <curl/curl.h>
#include <curl/easy.h>
#include <fmt/format.h>
#include <exception>
#include <functional>
#include <optional>
#include <stdexcept>
#include <string>
#include <thread>
#include <type_traits>
#include <unordered_map>
#include <variant>
#include <vector>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/common/thread.hpp>
#include <websocketpp/config/asio_client.hpp>
#include <websocketpp/config/core_client.hpp>
#include <websocketpp/roles/client_endpoint.hpp>
#define FOR_ALL_SUBSCRIPTION_KEY_MEMBERS(op) op(topic) op(product)
namespace bts::websocket {
EQUALS_IMPL(
CmeWebsocketSession::SubscriptionKey, FOR_ALL_SUBSCRIPTION_KEY_MEMBERS)
} // namespace bts::websocket
HASH_IMPL(
bts::websocket::CmeWebsocketSession::SubscriptionKey,
FOR_ALL_SUBSCRIPTION_KEY_MEMBERS);
#undef FOR_ALL_SUBSCRIPTION_KEY_MEMBERS
namespace bts::websocket {
using namespace std::chrono_literals;
using bts::system::TupleCutter;
using nlohmann::json;
using std::optional;
using std::pair;
using std::shared_ptr;
using std::string;
using std::tuple;
using std::unordered_map;
using std::vector;
using system::json::JsonDumper;
using SslContext = boost::asio::ssl::context;
// ws endpoint http://markets.api.uat.cmegroup.com/marketdatastream/v1
#define _ASSERT_MSGF(condition, ...) \
bts_assert_msgf(condition, "CmeWebsocketSession::" __VA_ARGS__)
#define _LOG(level, ...) LOG(md, level, "CmeWebsocketSession::" __VA_ARGS__)
#define _DIAG(...) _LOG(Diag, __VA_ARGS__)
#define _INFO(...) _LOG(Info, __VA_ARGS__)
#define _WARN(...) _LOG(Warn, __VA_ARGS__)
#define _ERROR(...) _LOG(Error, __VA_ARGS__)
struct CmeWebsocketSession::PImpl {
PImpl(const CmeWebsocketSessionConfig &cfg)
: cfg(cfg), client(cfg.websocket) {}
CmeWebsocketSessionConfig cfg;
WebsocketClient client;
CmeSubscriptionId sub_id_counter{0};
string server_name;
std::optional<std::thread> run_thread;
int session_id{session_id_counter_++};
CmeWebsocketRequestFactory req_factory_;
TradeTickerCallback trade_ticker_callback;
TopOfBookCallback top_of_book_callback;
~PImpl() {
if (run_thread) {
run_thread->join();
}
}
void on_login_status(const CmeMessage &msg) {
_INFO("on_login_status:\n{}", JsonDumper::dump(msg));
if (msg.payload->at("status").get<string>() != "authenticated") {
_ERROR(
"on_login_status. status not authenticated:\n{}",
JsonDumper::dump(msg));
return;
}
}
void on_subscription_status(const CmeMessage &msg) {
// If there is an error, log it and return.
_INFO("on_subscription_status:\n{}", JsonDumper::dump(msg));
if (msg.payload->at("status").get<string>() != "subscribed") {
_ERROR(
"on_subscription_status. status not subscribed:\n{}",
JsonDumper::dump(msg));
}
}
void on_heartbeat(const CmeMessage & /*msg*/) {
// Placeholder for hearbeat handler
}
void on_message(const string &msg_str) {
_INFO("on_message\n{}", msg_str);
CmeMessage msg;
try {
nlohmann::json::parse(msg_str).get_to(msg);
} catch (const std::exception &ex) {
_ERROR("on_message. Failed to parse json. {}\n{}", ex.what(), msg_str);
return;
}
if (msg.header.messageType == "heartbeat") {
on_heartbeat(msg);
return;
}
if (msg.header.messageType == "loginStatus") {
on_login_status(msg);
return;
}
if (msg.header.messageType == "subscriptionStatus") {
on_subscription_status(msg);
return;
}
if (msg.header.messageType == "TRD") {
trade_ticker_callback(
msg.payload->get<vector<CmeTradeSummaryPayload>>());
return;
}
if (msg.header.messageType == "TOB") {
top_of_book_callback(msg.payload->get<vector<CmeTopOfBookPayload>>());
return;
}
_ERROR("Received message of unknown type:\n{}", msg_str);
// auto topic = topic_from_incoming_name(msg.header.messageType);
// switch (topic.value) {
// case CmeSubscriptionTopic::TRADE_TICKER: {
// auto trade_summary = msg.payload->get<CmeTradeSummaryPayload>();
// trade_ticker_callback(trade_summary);
// break;
// }
// case CmeSubscriptionTopic::TOP_OF_BOOK: {
// auto top_of_book = msg.payload->get<CmeTopOfBookPayload>();
// top_of_book_callback(top_of_book);
// break;
// }
// default:
// _ERROR("Received message of unknown type:\n{}", msg_str);
// break;
// }
}
private:
static std::atomic<int> session_id_counter_;
};
CmeWebsocketSession::CmeWebsocketSession(CmeWebsocketSessionConfig cfg)
: me_(std::make_unique<PImpl>(std::move(cfg))) {
me_->client.message_handler().bind<&PImpl::on_message>(*me_);
}
CmeWebsocketSession::~CmeWebsocketSession() = default;
std::atomic_int CmeWebsocketSession::PImpl::session_id_counter_ = 0;
CmeWebsocketSession::TradeTickerCallback &
CmeWebsocketSession::trade_ticker_callback() {
return me_->trade_ticker_callback;
}
CmeWebsocketSession::TopOfBookCallback &
CmeWebsocketSession::top_of_book_callback() {
return me_->top_of_book_callback;
}
void CmeWebsocketSession::start() { me_->client.connect(); }
void CmeWebsocketSession::subscribe(vector<SubscriptionKey> subs) {
vector<CmeProductKey> products;
vector<CmeSubscriptionTopic> topics;
for (const auto &s : subs) {
auto &[t, p] = s;
if (std::find(products.begin(), products.end(), p) == products.end()) {
products.push_back(p);
}
if (std::find(topics.begin(), topics.end(), t) == topics.end()) {
topics.push_back(t);
}
}
auto sub_req = me_->req_factory_.subscription(products, topics);
if (!me_->client.is_connected()) {
throw std::runtime_error{
"Unable to add subscription because client is not connected."};
}
me_->client.send_text(JsonDumper::dump(sub_req));
}
bool CmeWebsocketSession::unsubscribe(std::vector<SubscriptionKey> subs) {
UNUSED(subs);
// TODO
// auto it = me_->topic_handlers.find(id);
// if (it == me_->subscriptions.end()) {
// return false;
// }
// CmeWebsocketRequest req = it->second.request;
// req.header.messageType = "unsubscribe";
// req.header.sentTime = core::TimeStamp::now();
// me_->subscriptions.erase(it);
// me_->client.send_text(JsonDumper::dump(req));
return true;
}
} // namespace bts::websocket
@@ -0,0 +1,64 @@
#pragma once
#include "bts-cme-websocket/cme_message_handler.h"
#include "bts-cme-websocket/cme_subscription_id.h"
#include "bts-cme-websocket/cme_subscription_topic.h"
#include "bts-cme-websocket/messages/cme_product_key.h"
#include "bts-cme-websocket/messages/cme_top_of_book_payload.h"
#include "bts-cme-websocket/messages/cme_trade_summary_payload.h"
#include "bts-cme-websocket/messages/cme_websocket_request.h"
#include "bts-http/url.h"
#include "bts-system/delegate.h"
#include "bts-system/value_wrapper.h"
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <cstddef>
#include <curl/curl.h>
#include <iostream>
#include <memory>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
namespace bts::websocket {
class CmeOauthConfig;
class CmeWebsocketSessionConfig;
class CmeWebsocketSession {
public:
struct SubscriptionKey {
CmeSubscriptionTopic topic;
CmeProductKey product;
};
using TradeTickerCallback =
system::Delegate<void(const std::vector<CmeTradeSummaryPayload> &)>;
using TopOfBookCallback =
system::Delegate<void(const std::vector<CmeTopOfBookPayload> &)>;
explicit CmeWebsocketSession(CmeWebsocketSessionConfig cfg);
~CmeWebsocketSession();
void start();
TradeTickerCallback &trade_ticker_callback();
TopOfBookCallback &top_of_book_callback();
/**
* Subscribes to topics for the specified products (a handler is
* paired with each topic).
*/
void subscribe(std::vector<SubscriptionKey> subs);
bool unsubscribe(std::vector<SubscriptionKey> subs);
private:
struct PImpl;
std::unique_ptr<PImpl> me_;
};
} // namespace bts::websocket
@@ -0,0 +1,61 @@
#pragma once
#include "bts-cme-websocket/cme_subscription_topic.h"
#include "bts-cme-websocket/messages/cme_product_key.h"
#include "bts-cme-websocket/messages/cme_websocket_request.h"
#include "bts-http/oauth_request.h"
#include "bts-http/url.h"
#include "bts-system/json/json_reader.h"
#include "bts-system/threading/thread_config.h"
#include "bts-websocket/websocket_client_config.h"
#include <nlohmann/json.hpp>
#include <stdexcept>
#include <string>
#include <vector>
namespace bts::websocket {
class CmeWebsocketSessionConfig {
public:
struct SubscriptionConfig {
std::vector<CmeSubscriptionTopic> topics;
std::vector<CmeProductKey> products;
};
// Needed to obtain the access token (ignored if access token is non-empty)
websocket::WebsocketClientConfig websocket;
std::vector<SubscriptionConfig> subscriptions;
};
//////////////////////////////////////////////////
// Definition
//////////////////////////////////////////////////
inline void to_json(
nlohmann::json &j,
const CmeWebsocketSessionConfig::SubscriptionConfig &cfg) {
j["topics"] = cfg.topics;
j["products"] = cfg.products;
}
inline void from_json(
const nlohmann::json &j,
CmeWebsocketSessionConfig::SubscriptionConfig &cfg) {
system::json::JsonReader jr{&j};
jr.read("topics", cfg.topics);
jr.read("products", cfg.products);
};
inline void to_json(nlohmann::json &j, const CmeWebsocketSessionConfig &cfg) {
j["websocket"] = cfg.websocket;
j["subscriptions"] = cfg.subscriptions;
}
inline void from_json(const nlohmann::json &j, CmeWebsocketSessionConfig &cfg) {
system::json::JsonReader jr{&j};
jr.read("websocket", cfg.websocket);
jr.read("subscriptions", cfg.subscriptions);
};
} // namespace bts::websocket
@@ -0,0 +1,35 @@
#pragma once
#include "bts-system/json/json_reader.h"
#include "bts-system/time_stamp.h"
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/json.hpp>
namespace bts::websocket {
class CmeBookLevel {
public:
system::TimeStamp lastUpdateTime;
std::uint32_t orderCnt;
double price;
double qty;
};
inline void to_json(nlohmann::json &j, const CmeBookLevel &l) {
j["lastUpdateTime"] = l.lastUpdateTime.to_iso_string();
j["orderCnt"] = l.orderCnt;
j["price"] = l.price;
j["qty"] = l.qty;
}
inline void from_json(const nlohmann::json &j, CmeBookLevel &l) {
l.lastUpdateTime = system::TimeStamp::from_iso_string(
j.at("lastUpdateTime").get<std::string>());
system::json::JsonReader jr{&j};
jr.read("orderCnt", l.orderCnt);
jr.read("price", l.price);
jr.read("qty", l.qty);
};
} // namespace bts::websocket
@@ -0,0 +1,91 @@
#pragma once
#include "bts-system/time_stamp.h"
#include <nlohmann/json.hpp>
#include <optional>
#include <string>
namespace bts::websocket {
class CmeInstrument {
public:
std::string guid;
std::int64_t guidInt;
std::int64_t productGuidInt;
std::string instrumentName;
std::optional<std::string> globexSecurityId;
std::string globexSymbol;
core::TimeStamp positionRemovalDate;
core::TimeStamp lastUpdated;
core::TimeStamp lastTradeDate;
std::optional<core::TimeStamp> lastNoticeDate;
core::TimeStamp lastDeliveryDate;
std::optional<core::TimeStamp> globexLastTradeDate;
std::optional<core::TimeStamp> globexFirstTradeDate;
core::TimeStamp firstTradeDate;
std::optional<core::TimeStamp> firstNoticeDate;
core::TimeStamp firstDeliveryDate;
core::TimeStamp finalSettlementDate;
std::optional<core::TimeStamp> initialInventoryDueDate;
std::optional<core::TimeStamp> lastInventoryDueDate;
std::optional<core::TimeStamp> lastEfpDate;
std::optional<double> strikePxCcy;
std::optional<std::string> putCallIndicator;
std::optional<double> priceBandD1;
std::optional<double> priceBand;
std::optional<std::string> zeroPriceEligible;
std::optional<double> originalContractSize;
core::TimeStamp contractMonth;
std::string flexIndicator;
// TODO: like 40 more fields :()!
template <class T>
static void write_opt(
const std::optional<T> &dest,
nlohmann::json &j,
const std::string &field) {
if (dest) {
j.at(field) = *dest;
}
}
template <class T>
static void read_opt(
nlohmann::json &j, const std::string &field, std::optional<T> &dest) {
auto it = j.find(field);
if (it != j.end()) {
it->get_to(dest.emplace());
}
}
};
inline void to_json(nlohmann ::json &j, const CmeInstrument &i) {
j["guid"] = i.guid;
j["guidInt"] = i.guidInt;
j["productGuidInt"] = i.productGuidInt;
j["instrumentName"] = i.instrumentName;
if (i.globexSecurityId) {
j["globexSecurityId"] = *i.globexSecurityId;
}
j["globexSymbol"] = i.globexSymbol;
if (i.positionRemovalDate) {
// TODO
// j["positionRemovalDate"] = {};
}
j["lastUpdated"] = i.lastUpdated;
j["lastTradeDate"] = i.lastTradeDate;
}
inline void from_json(const nlohmann ::json &j, CmeInstrument &i) {
j.at("guid").get_to(i.guid);
j.at("guidInt").get_to(i.guidInt);
j.at("productGuidInt").get_to(i.productGuidInt);
j.at("instrumentName").get_to(i.instrumentName);
j.at("globexSecurityId").get_to(i.globexSecurityId);
j.at("globexSymbol").get_to(i.globexSymbol);
j.at("positionRemovalDate").get_to(i.positionRemovalDate);
j.at("lastUpdated").get_to(i.lastUpdated);
j.at("lastTradeDate").get_to(i.lastTradeDate);
};
} // namespace bts::websocket
@@ -0,0 +1,49 @@
#pragma once
#include "bts-system/json/json_reader.h"
#include <nlohmann/json.hpp>
#include <string>
namespace bts::websocket {
class CmeInstrumentMetadata {
public:
std::string definitionSource;
std::string exchangeMic;
std::int64_t id;
// Note: this typo is intentional (it reflects an actual typo in Cme
// payloads, where marketSegmentId is spelled with a 'q' instead of 'g')
std::string marketSeqmentId;
std::string periodCode;
std::string productCode;
std::string productType;
std::string productGroup;
std::string symbol;
};
inline void to_json(nlohmann::json &j, const CmeInstrumentMetadata &i) {
j["definitionSource"] = i.definitionSource;
j["exchangeMic"] = i.exchangeMic;
j["id"] = i.id;
j["marketSeqmentId"] = i.marketSeqmentId;
j["periodCode"] = i.periodCode;
j["productCode"] = i.productCode;
j["productType"] = i.productType;
j["productGroup"] = i.productGroup;
j["symbol"] = i.symbol;
}
inline void from_json(const nlohmann::json &j, CmeInstrumentMetadata &i) {
system::json::JsonReader jr{&j};
j.at("definitionSource").get_to(i.definitionSource);
j.at("exchangeMic").get_to(i.exchangeMic);
jr.read("id", i.id);
jr.read("marketSeqmentId", i.marketSeqmentId);
j.at("periodCode").get_to(i.periodCode);
j.at("productCode").get_to(i.productCode);
j.at("productType").get_to(i.productType);
j.at("productGroup").get_to(i.productGroup);
j.at("symbol").get_to(i.symbol);
};
} // namespace bts::websocket
@@ -0,0 +1,35 @@
#pragma once
#include "bts-cme-websocket/messages/cme_message_header.h"
#include <nlohmann/json.hpp>
namespace bts::websocket {
template <class Payload = nlohmann::json>
class CmeMessageT {
public:
CmeMessageHeader header;
std::optional<Payload> payload;
};
using CmeMessage = CmeMessageT<nlohmann::json>;
template <class Payload>
inline void to_json(nlohmann::json &j, const CmeMessageT<Payload> &msg) {
j["header"] = msg.header;
if (msg.payload) {
j["payload"] = *msg.payload;
}
}
template <class Payload>
inline void from_json(const nlohmann::json &j, CmeMessageT<Payload> &msg) {
j.at("header").get_to(msg.header);
auto payload = j.find("payload");
if (payload != j.end()) {
payload->get_to(msg.payload.emplace());
}
};
} // namespace bts::websocket
@@ -0,0 +1,53 @@
#pragma once
#include "bts-cme-websocket/cme_subscription_id.h"
#include "bts-system/json/json_reader.h"
#include "bts-system/time_stamp.h"
#include <nlohmann/json.hpp>
#include <nlohmann/json_fwd.hpp>
#include <optional>
#include <string>
namespace bts::websocket {
class CmeMessageHeader {
public:
std::string messageType{};
std::optional<std::string> requestId{};
std::optional<system::TimeStamp> sentTime{};
std::string token{};
std::optional<std::string> version{};
};
inline void to_json(nlohmann::json &j, const CmeMessageHeader &h) {
j["messageType"] = h.messageType;
if (h.sentTime) {
j["sentTime"] = h.sentTime->to_iso_extended_string();
}
if (h.version) {
j["version"] = *h.version;
}
if (h.requestId) {
j["requestId"] = *h.requestId;
}
}
inline void from_json(const nlohmann ::json &j, CmeMessageHeader &h) {
j.at("messageType").get_to(h.messageType);
std::string sentTime;
h.sentTime =
system::json::JsonReader{&j}.read("sentTime", sentTime)
? decltype(h.sentTime){system::TimeStamp::from_iso_string(sentTime)}
: std::nullopt;
std::string version;
h.version = system::json::JsonReader{&j}.read("version", version)
? decltype(h.version){version}
: std::nullopt;
std::string requestId;
h.requestId = system::json::JsonReader{&j}.read("requestId", requestId)
? decltype(h.requestId){requestId}
: std::nullopt;
};
} // namespace bts::websocket
@@ -0,0 +1,29 @@
#pragma once
#include "bts-system/equality_macros.h"
#include "bts-system/hash_macros.h"
#include <nlohmann/json.hpp>
#include <string>
#define FOR_ALL_VALUES(op) op(exchangeMic) op(productType) op(productCode)
namespace bts::websocket {
class CmeProductKey {
public:
std::string exchangeMic;
std::string productType;
std::string productCode;
};
EQUALS_IMPL(CmeProductKey, FOR_ALL_VALUES)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
CmeProductKey, exchangeMic, productType, productCode);
} // namespace bts::websocket
HASH_IMPL(bts::websocket::CmeProductKey, FOR_ALL_VALUES);
#undef FOR_ALL_VALUES
@@ -0,0 +1,41 @@
#pragma once
#include "bts-cme-websocket/messages/cme_book_level.h"
#include "bts-cme-websocket/messages/cme_instrument_metadata.h"
#include "bts-system/logging.h"
#include <nlohmann/detail/macro_scope.hpp>
#include <string>
#include <vector>
namespace bts::websocket {
class CmeTopOfBookPayload {
public:
CmeInstrumentMetadata instrument;
std::vector<CmeBookLevel> askLevel;
std::vector<CmeBookLevel> bidLevel;
};
inline void to_json(nlohmann ::json &j, const CmeTopOfBookPayload &tob) {
j["instrument"] = tob.instrument;
if (!tob.askLevel.empty()) {
j["askLevel"] = tob.askLevel;
}
if (!tob.bidLevel.empty()) {
j["bidLevel"] = tob.bidLevel;
}
}
inline void from_json(const nlohmann ::json &j, CmeTopOfBookPayload &tob) {
j.at("instrument").get_to(tob.instrument);
auto it = j.find("askLevel");
if (it != j.end()) {
it->get_to(tob.askLevel);
}
it = j.find("bidLevel");
if (it != j.end()) {
it->get_to(tob.bidLevel);
}
}
} // namespace bts::websocket
@@ -0,0 +1,75 @@
#pragma once
#include "bts-cme-websocket/messages/cme_instrument_metadata.h"
#include "bts-system/json/json_reader.h"
#include <nlohmann/json.hpp>
#include <string>
namespace bts::websocket {
class CmeTradeSummaryPayload {
public:
// struct Instrument {
// std::string definitionSource;
// std::string exchangeMic;
// std::string id;
// std::string marketSegmentId;
// std::string periodCode;
// std::string productCode;
// std::string productGroup;
// std::string productType;
// std::string symbol;
// } instrument;
CmeInstrumentMetadata instrument;
// TODO: convert to timestamp
std::string lastUpdateTime;
struct TradeSummary {
std::int64_t aggressorSide;
std::int64_t mdTradeEntryId;
double tradePrice;
double tradeQty;
std::uint32_t tradeOrderCount;
std::string tradeUpdateAction;
} tradeSummary;
};
// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
// CmeTradeSummaryPayload::Instrument,
// definitionSource,
// exchangeMic,
// id,
// marketSegmentId,
// periodCode,
// productCode,
// productGroup,
// productType,
// symbol)
inline void to_json(
nlohmann ::json &j, const CmeTradeSummaryPayload::TradeSummary &ts) {
j["aggressorSide"] = ts.aggressorSide;
j["mdTradeEntryId"] = ts.mdTradeEntryId;
j["tradePrice"] = ts.tradePrice;
j["tradeQty"] = ts.tradeQty;
j["tradeOrderCount"] = ts.tradeOrderCount;
j["tradeUpdateAction"] = ts.tradeUpdateAction;
}
inline void from_json(
const nlohmann ::json &j, CmeTradeSummaryPayload ::TradeSummary &ts) {
system::json::JsonReader jr{&j};
jr.read("aggressorSide", ts.aggressorSide);
jr.read("mdTradeEntryId", ts.mdTradeEntryId);
jr.read("tradePrice", ts.tradePrice);
jr.read("tradeQty", ts.tradeQty);
jr.read("tradeOrderCount", ts.tradeOrderCount);
jr.read("tradeUpdateAction", ts.tradeUpdateAction);
}
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
CmeTradeSummaryPayload, instrument, lastUpdateTime, tradeSummary)
} // namespace bts::websocket
@@ -0,0 +1,10 @@
#pragma once
#include "bts-cme-websocket/messages/cme_message.h"
#include "bts-cme-websocket/messages/cme_websocket_request_payload.h"
namespace bts::websocket {
using CmeWebsocketRequest = CmeMessageT<CmeWebsocketRequestPayload>;
} // namespace bts::websocket
@@ -0,0 +1,64 @@
#pragma once
#include "bts-cme-websocket/cme_subscription_topic.h"
#include "bts-cme-websocket/messages/cme_message_header.h"
#include "bts-cme-websocket/messages/cme_product_key.h"
#include "bts-cme-websocket/messages/cme_websocket_request.h"
#include "bts-system/time_stamp.h"
#include <nlohmann/json.hpp>
namespace bts::websocket {
class CmeWebsocketRequestFactory {
public:
CmeWebsocketRequest subscription(
const std::vector<CmeProductKey> &subscriptions,
const std::vector<CmeSubscriptionTopic> &topics);
CmeWebsocketRequest unsubscription(
const std::vector<CmeProductKey> &subscriptions,
const std::vector<CmeSubscriptionTopic> &topics);
private:
// Ensure unique ids for each request
std::atomic<std::uint32_t> id_counter_;
};
//////////////////////////////////////////////////
// Definition
//////////////////////////////////////////////////
inline CmeWebsocketRequest CmeWebsocketRequestFactory::subscription(
const std::vector<CmeProductKey> &products,
const std::vector<CmeSubscriptionTopic> &topics) {
CmeWebsocketRequest sub;
sub.payload.emplace();
sub.payload->subscriptions = std::move(products);
sub.payload->subscriptionMessageTypes.reserve(topics.size());
for (const auto t : topics) {
sub.payload->subscriptionMessageTypes.push_back(topic_outgoing_name(t));
}
sub.header.requestId = std::to_string(id_counter_++);
sub.header.messageType = "subscribe";
sub.header.sentTime = system::TimeStamp::now();
return sub;
}
inline CmeWebsocketRequest CmeWebsocketRequestFactory::unsubscription(
const std::vector<CmeProductKey> &products,
const std::vector<CmeSubscriptionTopic> &topics) {
CmeWebsocketRequest sub;
sub.payload.emplace();
sub.payload->subscriptions = std::move(products);
sub.payload->subscriptionMessageTypes.resize(topics.size());
for (const auto t : topics) {
sub.payload->subscriptionMessageTypes.push_back(topic_outgoing_name(t));
}
sub.header.requestId = std::to_string(id_counter_++);
sub.header.messageType = "unsubscribe";
sub.header.sentTime = system::TimeStamp::now();
return sub;
}
} // namespace bts::websocket
@@ -0,0 +1,18 @@
#pragma once
#include "bts-cme-websocket/cme_subscription_topic.h"
#include "bts-cme-websocket/messages/cme_product_key.h"
#include <nlohmann/json.hpp>
namespace bts::websocket {
struct CmeWebsocketRequestPayload {
std::vector<CmeProductKey> subscriptions;
std::vector<std::string> subscriptionMessageTypes;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
CmeWebsocketRequestPayload, subscriptions, subscriptionMessageTypes);
} // namespace bts::websocket
@@ -0,0 +1,17 @@
#pragma once
#include <cstdint>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/json.hpp>
namespace bts::websocket {
class PrivateOrderQty {
public:
std::int64_t orderId;
double lastOrderQty;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(PrivateOrderQty, orderId, lastOrderQty)
} // namespace bts::websocket
@@ -0,0 +1,120 @@
#include "ssl_certificate_verifier.h"
namespace bts::websocket {
/// Verify that the certificate common name matches the given hostname
bool SslCertificateVerifier::verify_common_name(char const *hostname, X509 *cert) {
// Find the position of the CN field in the Subject field of the certificate
int common_name_loc = X509_NAME_get_index_by_NID(
X509_get_subject_name(cert), NID_commonName, -1);
if (common_name_loc < 0) {
return false;
}
// Extract the CN field
X509_NAME_ENTRY *common_name_entry =
X509_NAME_get_entry(X509_get_subject_name(cert), common_name_loc);
if (common_name_entry == NULL) {
return false;
}
// Convert the CN field to a C string
ASN1_STRING *common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
if (common_name_asn1 == NULL) {
return false;
}
char const *common_name_str =
(char const *)ASN1_STRING_get0_data(common_name_asn1);
// Make sure there isn't an embedded NUL character in the CN
if (static_cast<size_t>(ASN1_STRING_length(common_name_asn1)) !=
strlen(common_name_str)) {
return false;
}
// Compare expected hostname with the CN
return (strcasecmp(hostname, common_name_str) == 0);
}
/// Verify that one of the subject alternative names matches the given hostname
bool SslCertificateVerifier::verify_subject_alternative_name(
const char *hostname, X509 *cert) {
STACK_OF(GENERAL_NAME) *san_names = NULL;
san_names = (STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i(
cert, NID_subject_alt_name, NULL, NULL);
if (san_names == NULL) {
return false;
}
int san_names_count = sk_GENERAL_NAME_num(san_names);
bool result = false;
for (int i = 0; i < san_names_count; i++) {
const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i);
if (current_name->type != GEN_DNS) {
continue;
}
char const *dns_name =
(char const *)ASN1_STRING_get0_data(current_name->d.dNSName);
// Make sure there isn't an embedded NUL character in the DNS name
if (static_cast<size_t>(ASN1_STRING_length(current_name->d.dNSName)) !=
strlen(dns_name)) {
break;
}
// Compare expected hostname with the CN
result = (strcasecmp(hostname, dns_name) == 0);
}
sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
return result;
}
/**
* This code is derived from examples and documentation found ato00po
* http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/example/cpp03/ssl/client.cpp
* and
* https://github.com/iSECPartners/ssl-conservatory
*/
bool SslCertificateVerifier::verify_certificate(
const char *hostname,
bool preverified,
boost::asio::ssl::verify_context &ctx) {
// The verify callback can be used to check whether the certificate that is
// being presented is valid for the peer. For example, RFC 2818 describes
// the steps involved in doing this for HTTPS. Consult the OpenSSL
// documentation for more details. Note that the callback is called once
// for each certificate in the certificate chain, starting from the root
// certificate authority.
// Retrieve the depth of the current cert in the chain. 0 indicates the
// actual server cert, upon which we will perform extra validation
// (specifically, ensuring that the hostname matches. For other certs we
// will use the 'preverified' flag from Asio, which incorporates a number of
// non-implementation specific OpenSSL checking, such as the formatting of
// certs and the trusted status based on the CA certs we imported earlier.
int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle());
// if we are on the final cert and everything else checks out, ensure that
// the hostname is present on the list of SANs or the common name (CN).
if (depth == 0 && preverified) {
X509 *cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
if (verify_subject_alternative_name(hostname, cert)) {
return true;
} else if (verify_common_name(hostname, cert)) {
return true;
} else {
return false;
}
}
return preverified;
}
} // namespace bts::websocket
@@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <thread>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/config/asio_client.hpp>
namespace bts::websocket {
class SslCertificateVerifier {
public:
static bool verify_common_name(char const *hostname, X509 *cert);
static bool verify_subject_alternative_name(
const char *hostname, X509 *cert);
static bool verify_certificate(
const char *hostname,
bool preverified,
boost::asio::ssl::verify_context &ctx);
};
} // namespace bts::websocket
Loaded 100 of 168 files, more files were not shown because too many files have changed in this diff. Show more