Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea4e599ac6 | ||
|
|
fd2ba70031 | ||
|
|
ab410c339f | ||
|
|
9e3f16d8bc | ||
|
|
a3f194d708 | ||
|
|
bf87c46fcd | ||
|
|
222d4b266e | ||
|
|
d593eaf058 | ||
|
|
55d021ccb1 |
No files matched your search
@@ -4,7 +4,6 @@ BinPackParameters: false
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
PenaltyReturnTypeOnItsOwnLine: 140
|
||||
ReflowComments: false
|
||||
---
|
||||
Language: Proto
|
||||
IndentWidth: 2
|
||||
|
||||
+1
-5
@@ -50,8 +50,4 @@ trace_data/
|
||||
*.tmp/*
|
||||
/.venv*/*
|
||||
.cache/
|
||||
/src/Spark.Admin.WebApp/tsconfig.*.g.json
|
||||
/src/Spark.Admin.WebApp/src/bin/
|
||||
**/CMakeDeps.cmake
|
||||
**/CMakeDeps_win.cmake
|
||||
**/devcontainer.json
|
||||
src/web/api/tsconfig.*.g.json
|
||||
@@ -1,3 +1,9 @@
|
||||
[submodule "externals"]
|
||||
path = externals
|
||||
url = git@bitbucket.org:bluetradesys/externals.git
|
||||
[submodule "docker/linux"]
|
||||
path = docker/linux
|
||||
url = git@bitbucket.org:bluetradesys/linux_docker.git
|
||||
[submodule "docker/windows"]
|
||||
path = docker/windows
|
||||
url = git@bitbucket.org:bluetradesys/windows_docker.git
|
||||
+72
-153
@@ -13,182 +13,101 @@ set(
|
||||
CACHE
|
||||
STRING
|
||||
"External dependencies root folder (should contain folders: lib, bin, include")
|
||||
set(SPARK_PYTHON "${EXTERNALS_ROOT}/python")
|
||||
set(SPARK_BIN "${EXTERNALS_ROOT}/bin")
|
||||
set(SPARK_INCLUDE "${EXTERNALS_ROOT}/include")
|
||||
set(SPARK_LIB "${EXTERNALS_ROOT}/lib/$<CONFIG>")
|
||||
set(BUILD_SPARK_CONFIG OFF CACHE BOOL "Build lib_spark_config")
|
||||
set(BUILD_API_SERVER OFF CACHE BOOL "Build the api server")
|
||||
set(BUILD_DOCS OFF CACHE BOOL "Build documentation")
|
||||
set(SPARK_LIB "${EXTERNALS_ROOT}/lib/${CMAKE_BUILD_TYPE}")
|
||||
set(BUILD_SDK OFF CACHE BOOL "Build the sdk")
|
||||
set(BUILD_ADMIN_WEB_APP ON CACHE BOOL "Build the Admin WebApp")
|
||||
if (WIN32)
|
||||
set(BUILD_SPARKD OFF CACHE BOOL "Build sparkd")
|
||||
else()
|
||||
set(BUILD_SPARKD ON CACHE BOOL "Build sparkd")
|
||||
endif()
|
||||
set(BUILD_WEB ON CACHE BOOL "Build the web server")
|
||||
|
||||
include_directories(BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
|
||||
link_directories(${SPARK_LIB})
|
||||
|
||||
if(WIN32)
|
||||
add_compile_definitions(SHARED_MEMORY_PREFIX="spark")
|
||||
add_compile_definitions(APP_DATA_DIR="\$\{APPDATA\}/bts/")
|
||||
# flags
|
||||
set(
|
||||
CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} \
|
||||
/MP \
|
||||
/WX /wd4251 /wd4250 /wd4251 /wd4244 /wd4267 /wd4355 /wd4275 /wd4345 \
|
||||
/bigobj \
|
||||
/DCURL_STATICLIB \
|
||||
/D_WINSOCK_DEPRECATED_NO_WARNINGS \
|
||||
/D__TBB_NO_IMPLICIT_LINKAGE \
|
||||
/DBOOST_ALL_NO_LIB \
|
||||
/std:c++17 \
|
||||
/D_SCL_SECURE_NO_WARNINGS /D_CRT_SECURE_NO_WARNINGS /D_WIN32_WINNT=0x0600")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} /MT")
|
||||
set(
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELEASE
|
||||
"${CMAKE_SHARED_LINKER_FLAGS_RELEASE} \
|
||||
/DEBUG \
|
||||
/OPT:REF \
|
||||
/OPT:ICF \
|
||||
/LTCG")
|
||||
|
||||
# subdirs
|
||||
add_subdirectory(src/lib_expression)
|
||||
add_subdirectory(src/lib)
|
||||
elseif(BUILD_SPARK_CONFIG)
|
||||
add_compile_definitions(SHARED_MEMORY_PREFIX="sparkd")
|
||||
add_compile_definitions(APP_DATA_DIR="\$\{HOME\}/sparkdata/")
|
||||
add_subdirectory(src/lib_core)
|
||||
add_subdirectory(src/lib_logging)
|
||||
add_subdirectory(src/lib_io)
|
||||
add_subdirectory(src/lib_domain)
|
||||
add_subdirectory(src/lib_protobuf)
|
||||
add_subdirectory(src/lib_threading)
|
||||
add_subdirectory(src/lib_expression)
|
||||
add_subdirectory(src/lib_md)
|
||||
add_subdirectory(src/lib_spark_config)
|
||||
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>")
|
||||
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install/${CMAKE_BUILD_TYPE}/sparkd")
|
||||
add_compile_definitions(SPARK_ENABLE_CCAPI)
|
||||
add_compile_definitions(CCAPI_ENABLE_SERVICE_MARKET_DATA)
|
||||
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_FTX)
|
||||
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
add_compile_definitions(CCAPI_ENABLE_LOG_WARN)
|
||||
|
||||
if(BUILD_API_SERVER)
|
||||
add_compile_definitions(SPARK_ENABLE_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 exchanges/services
|
||||
add_compile_definitions(CCAPI_ENABLE_SERVICE_MARKET_DATA)
|
||||
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_DERIBIT)
|
||||
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_FTX)
|
||||
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
|
||||
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
|
||||
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_COIN_FUTURES)
|
||||
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_HUOBI)
|
||||
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_HUOBI_USDT_SWAP)
|
||||
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_HUOBI_COIN_SWAP)
|
||||
|
||||
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")
|
||||
|
||||
endif()
|
||||
# flags
|
||||
set(
|
||||
CMAKE_CXX_FLAGS_DEBUG
|
||||
"${CMAKE_CXX_FLAGS_DEBUG} \
|
||||
-fPIC \
|
||||
-Wall \
|
||||
-pedantic \
|
||||
-Wextra \
|
||||
-Werror \
|
||||
-Wno-missing-braces \
|
||||
-Wno-ignored-qualifiers \
|
||||
--std=c++17 -fdiagnostics-color")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -DNDEBUG -O3")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wno-undef")
|
||||
|
||||
# subdirs
|
||||
if (BUILD_SPARKD)
|
||||
add_subdirectory(src/server/sparkd)
|
||||
add_subdirectory(src/data_files)
|
||||
# add_subdirectory(src/lib_md)
|
||||
add_subdirectory(src/lib_core)
|
||||
add_subdirectory(src/lib_logging)
|
||||
add_subdirectory(src/lib_threading)
|
||||
add_subdirectory(src/lib_expression)
|
||||
add_subdirectory(src/lib_spark_config)
|
||||
endif()
|
||||
add_subdirectory(src/server/sparkd)
|
||||
add_subdirectory(src/lib)
|
||||
if (BUILD_ADMIN_WEB_APP)
|
||||
add_subdirectory(src/Spark.Admin.WebApp)
|
||||
endif()
|
||||
if (BUILD_API_SERVER)
|
||||
set(_cs_source_dirs
|
||||
spark_api
|
||||
spark_api_server
|
||||
spark_core
|
||||
spark_websocket)
|
||||
foreach(cs_source_dir IN LISTS _cs_source_dirs)
|
||||
file(GLOB cs_source_dir "${CMAKE_SOURCE_DIR}/src/client/${cs_source_dir}/*.cs")
|
||||
list(APPEND _api_server_sources ${cs_source_dir})
|
||||
endforeach()
|
||||
|
||||
list(APPEND _api_server_sources
|
||||
${CMAKE_SOURCE_DIR}/src/client/spark_api/spark_api.csproj
|
||||
${CMAKE_SOURCE_DIR}/src/client/spark_api_server/spark_api_server.csproj
|
||||
${CMAKE_SOURCE_DIR}/src/client/spark_core/spark_core.csproj
|
||||
${CMAKE_SOURCE_DIR}/src/client/spark_websocket/spark_websocket.csproj)
|
||||
|
||||
set(_out_dir ${CMAKE_BINARY_DIR}/$<CONFIG>)
|
||||
set(_api_server_dlls
|
||||
${CMAKE_BINARY_DIR}/$<CONFIG>/bin/net7.0/spark_api.dll
|
||||
${CMAKE_BINARY_DIR}/$<CONFIG>/bin/net7.0/spark_core.dll
|
||||
${CMAKE_BINARY_DIR}/$<CONFIG>/bin/net7.0/spark_websocket.dll
|
||||
${CMAKE_BINARY_DIR}/$<CONFIG>/bin/net7.0/spark_api_server)
|
||||
add_custom_command(
|
||||
OUTPUT ${_api_server_dlls}
|
||||
COMMAND dotnet build ${CMAKE_SOURCE_DIR}/Spark.Api.Server.sln --verbosity minimal --configuration $<CONFIG>
|
||||
COMMENT "Building Spark.Api.Server.sln"
|
||||
DEPENDS ${_api_server_sources})
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${CMAKE_BINARY_DIR}/$<CONFIG>/bin/net7.0/libspark.so
|
||||
COMMAND ln -sf ARGS ${CMAKE_BINARY_DIR}/src/lib/$<CONFIG>/libspark.so ${CMAKE_BINARY_DIR}/$<CONFIG>/bin/net7.0
|
||||
COMMENT "Symlink libspark to api server bins"
|
||||
DEPENDS ${_api_server_dlls})
|
||||
add_custom_target(websocket_api_layout ALL DEPENDS ${CMAKE_BINARY_DIR}/$<CONFIG>/bin/net7.0/libspark.so)
|
||||
endif()
|
||||
if(BUILD_DOCS)
|
||||
add_subdirectory(doc/user)
|
||||
if (BUILD_WEB)
|
||||
add_subdirectory(src/web)
|
||||
endif()
|
||||
add_subdirectory(doc/user)
|
||||
if(BUILD_SDK)
|
||||
add_subdirectory(src/sdk)
|
||||
endif()
|
||||
add_subdirectory(src/data_files)
|
||||
|
||||
if(BUILD_SPARKD)
|
||||
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 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_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}/web ${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 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)
|
||||
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"
|
||||
"${CMAKE_SOURCE_DIR}/externals/src/emumba/sparkd.lic"
|
||||
DESTINATION "${SPARK_INSTALL_PREFIX}/bin")
|
||||
endif()
|
||||
install(DIRECTORY "${CMAKE_BINARY_DIR}/server/plugins" DESTINATION bin)
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/externals/linux/lib/${CMAKE_BUILD_TYPE}/libACE-5.7.5.so" DESTINATION bin)
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/externals/linux/lib/${CMAKE_BUILD_TYPE}/libOnixS.IceImpactMulticastPriceFeedHandlerCpp.so.3.16.0.0" DESTINATION bin)
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/externals/src/emumba/sparkd.lic" DESTINATION bin)
|
||||
endif()
|
||||
@@ -1,283 +0,0 @@
|
||||
{
|
||||
"version": 4,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 23,
|
||||
"patch": 0
|
||||
},
|
||||
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "spark",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"environment": {
|
||||
"SPARK_CMAKE_CXX_FLAGS": "/MP /WX /wd4250 /wd4251 /wd4244 /wd4267 /wd4355 /wd4275 /wd4345 /bigobj /EHsc /DCURL_STATICLIB /D_WINSOCK_DEPRECATED_NO_WARNINGS /D__TBB_NO_IMPLICIT_LINKAGE /DBOOST_ALL_NO_LIB /std:c++17 /D_SCL_SECURE_NO_WARNINGS /D_CRT_SECURE_NO_WARNINGS /D_WIN32_WINNT=0x0600"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_CXX_FLAGS": "$env{SPARK_CMAKE_CXX_FLAGS}",
|
||||
"CMAKE_CXX_FLAGS_DEBUG": "$env{SPARK_CMAKE_CXX_FLAGS} /MTd",
|
||||
"CMAKE_CXX_FLAGS_RELEASE": "$env{SPARK_CMAKE_CXX_FLAGS} /MT",
|
||||
"CMAKE_SHARED_LINKER_FLAGS_RELEASE": "/DEBUG /OPT:REF /OPT:ICF /LTCG",
|
||||
"EXTERNALS_ROOT": "${sourceDir}/externals/windows"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
},
|
||||
"hidden": true,
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"cacheVariables": {
|
||||
"platform": "",
|
||||
"CMAKE_BUILD_WITH_INSTALL_RPATH": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
},
|
||||
"CMAKE_INSTALL_RPATH": ".",
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}/sparkd",
|
||||
"CMAKE_EXE_LINKER_FLAGS": "-Wno-undef",
|
||||
"EXTERNALS_ROOT": "${sourceDir}/externals/linux",
|
||||
"CMAKE_CXX_STANDARD": "17",
|
||||
"BUILD_SDK": {
|
||||
"type": "BOOL",
|
||||
"value": "OFF"
|
||||
},
|
||||
"BUILD_ADMIN_WEB_APP": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-debug",
|
||||
"inherits": "sparkd",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
},
|
||||
"CCAPI_LOG_LEVEL": "Debug"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-release",
|
||||
"inherits": "sparkd",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"CCAPI_LOG_LEVEL": "Warn"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-debug-clang",
|
||||
"inherits": "sparkd-debug",
|
||||
"displayName": "Debug (clang)",
|
||||
"description": "Debug build compiled w/ clang",
|
||||
"binaryDir": "${sourceDir}/build/clang/debug",
|
||||
"cacheVariables": {
|
||||
"CMAKE_C_COMPILER": "clang",
|
||||
"CMAKE_CXX_COMPILER": "clang++",
|
||||
"CMAKE_CXX_FLAGS_DEBUG": "-g -fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++17 -fdiagnostics-color -DDEBUG",
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/clang/debug/sparkd"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-debug-gcc",
|
||||
"inherits": "sparkd-debug",
|
||||
"displayName": "Debug (gcc)",
|
||||
"description": "Debug build compiled w/ gcc",
|
||||
"binaryDir": "${sourceDir}/build/gcc/debug",
|
||||
"cacheVariables": {
|
||||
"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++17 -fdiagnostics-color -Wno-stringop-overread -DDEBUG",
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/gcc/debug/sparkd"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-release-clang",
|
||||
"inherits": "sparkd-release",
|
||||
"displayName": "Release (clang)",
|
||||
"description": "Release build compiled w/ clang",
|
||||
"binaryDir": "${sourceDir}/build/clang/release",
|
||||
"cacheVariables": {
|
||||
"CMAKE_C_COMPILER": "clang",
|
||||
"CMAKE_CXX_COMPILER": "clang++",
|
||||
"CMAKE_CXX_FLAGS_RELEASE": "-fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++17 -fdiagnostics-color -fno-omit-frame-pointer -DNDEBUG -O3",
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/clang/release/sparkd"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-release-gcc",
|
||||
"inherits": "sparkd-release",
|
||||
"displayName": "Release (gcc)",
|
||||
"description": "Release build compiled w/ gcc",
|
||||
"binaryDir": "${sourceDir}/build/gcc/release",
|
||||
"cacheVariables": {
|
||||
"CMAKE_C_COMPILER": "gcc",
|
||||
"CMAKE_CXX_COMPILER": "g++",
|
||||
"CMAKE_CXX_FLAGS_RELEASE": "-fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++17 -fdiagnostics-color -Wno-stringop-overread -Wno-maybe-uninitialized -fno-omit-frame-pointer -DNDEBUG -O3",
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/gcc/release/sparkd"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-publish",
|
||||
"inherits": "sparkd-release-gcc",
|
||||
"displayName": "Publish",
|
||||
"description": "Production build compiled w/ gcc and ready to ship"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "spark-config",
|
||||
"displayName": "Spark Config",
|
||||
"description": "Test build for lib_spark_config",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"generator": "Unix Makefiles",
|
||||
"cacheVariables": {
|
||||
"platform": "linux",
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_BUILD_WITH_INSTALL_RPATH": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
},
|
||||
"CMAKE_INSTALL_RPATH": ".",
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}",
|
||||
"CMAKE_EXE_LINKER_FLAGS": "-Wno-undef",
|
||||
"EXTERNALS_ROOT": "${sourceDir}/externals/linux",
|
||||
"CMAKE_CXX_STANDARD": "17",
|
||||
"BUILD_SPARKD": {
|
||||
"type": "BOOL",
|
||||
"value": "OFF"
|
||||
},
|
||||
"BUILD_DOCS": {
|
||||
"type": "BOOL",
|
||||
"value": "OFF"
|
||||
},
|
||||
"BUILD_SDK": {
|
||||
"type": "BOOL",
|
||||
"value": "OFF"
|
||||
},
|
||||
"BUILD_SPARK_CONFIG": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
},
|
||||
"BUILD_ADMIN_WEB_APP": {
|
||||
"type": "BOOL",
|
||||
"value": "OFF"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "spark",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"hidden": true,
|
||||
"jobs": 4
|
||||
},
|
||||
|
||||
{
|
||||
"name": "spark-dev",
|
||||
"displayName": "Development",
|
||||
"inherits": "spark",
|
||||
"configurePreset": "spark",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "spark-prod",
|
||||
"displayName": "Production",
|
||||
"inherits": "spark",
|
||||
"configurePreset": "spark",
|
||||
"configuration": "Release"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
},
|
||||
"hidden": true,
|
||||
"jobs": 4,
|
||||
"targets": [
|
||||
"data_files_dir",
|
||||
"admin_web_app_bundle",
|
||||
"install",
|
||||
"libspark",
|
||||
"rapidjson_project",
|
||||
"server_layout",
|
||||
"sparkd"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-debug-clang",
|
||||
"configurePreset": "sparkd-debug-clang",
|
||||
"displayName": "Debug (clang)",
|
||||
"inherits": "sparkd"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-debug-gcc",
|
||||
"configurePreset": "sparkd-debug-gcc",
|
||||
"displayName": "Debug (gcc)",
|
||||
"inherits": "sparkd"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-release-clang",
|
||||
"configurePreset": "sparkd-release-clang",
|
||||
"displayName": "Release (clang)",
|
||||
"inherits": "sparkd"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-release-gcc",
|
||||
"configurePreset": "sparkd-release-gcc",
|
||||
"displayName": "Release (gcc)",
|
||||
"inherits": "sparkd"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "spark-config",
|
||||
"configurePreset": "spark-config",
|
||||
"displayName": "Spark Config",
|
||||
"inherits": "sparkd"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-publish",
|
||||
"configurePreset": "sparkd-publish",
|
||||
"displayName": "Publish",
|
||||
"inherits": "sparkd",
|
||||
"targets": "install"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.3.32922.545
|
||||
MinimumVisualStudioVersion = 15.0.26124.0
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api", "src\client\spark_api\spark_api.csproj", "{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\spark_core\spark_core.csproj", "{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_websocket", "src\client\spark_websocket\spark_websocket.csproj", "{3201359D-C071-4089-9EDE-B04D759F1508}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api_server", "src\client\spark_api_server\spark_api_server.csproj", "{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spark_test", "src\client\spark_test\spark_test.csproj", "{EFC9FC4D-E92B-4C6D-9B97-26D38EAA118D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spark.Console", "src\client\Spark.Console\Spark.Console.csproj", "{89D666E6-B500-498F-B026-4D167F68ADFC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
MinSizeRel|x64 = MinSizeRel|x64
|
||||
Release|x64 = Release|x64
|
||||
RelWithDebInfo|x64 = RelWithDebInfo|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Debug|x64.Build.0 = Debug|x64
|
||||
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.MinSizeRel|x64.ActiveCfg = Release|x64
|
||||
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.MinSizeRel|x64.Build.0 = Release|x64
|
||||
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Release|x64.ActiveCfg = Release|x64
|
||||
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Release|x64.Build.0 = Release|x64
|
||||
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.ActiveCfg = Release|x64
|
||||
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.Build.0 = Debug|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.MinSizeRel|x64.ActiveCfg = Release|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.MinSizeRel|x64.Build.0 = Release|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Release|x64.ActiveCfg = Release|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Release|x64.Build.0 = Release|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.ActiveCfg = Release|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{3201359D-C071-4089-9EDE-B04D759F1508}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{3201359D-C071-4089-9EDE-B04D759F1508}.Debug|x64.Build.0 = Debug|x64
|
||||
{3201359D-C071-4089-9EDE-B04D759F1508}.MinSizeRel|x64.ActiveCfg = Debug|x64
|
||||
{3201359D-C071-4089-9EDE-B04D759F1508}.MinSizeRel|x64.Build.0 = Debug|x64
|
||||
{3201359D-C071-4089-9EDE-B04D759F1508}.Release|x64.ActiveCfg = Release|x64
|
||||
{3201359D-C071-4089-9EDE-B04D759F1508}.Release|x64.Build.0 = Release|x64
|
||||
{3201359D-C071-4089-9EDE-B04D759F1508}.RelWithDebInfo|x64.ActiveCfg = Release|x64
|
||||
{3201359D-C071-4089-9EDE-B04D759F1508}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Debug|x64.Build.0 = Debug|x64
|
||||
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.MinSizeRel|x64.ActiveCfg = Debug|x64
|
||||
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.MinSizeRel|x64.Build.0 = Debug|x64
|
||||
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Release|x64.ActiveCfg = Release|x64
|
||||
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Release|x64.Build.0 = Release|x64
|
||||
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.RelWithDebInfo|x64.ActiveCfg = Release|x64
|
||||
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{EFC9FC4D-E92B-4C6D-9B97-26D38EAA118D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{EFC9FC4D-E92B-4C6D-9B97-26D38EAA118D}.Debug|x64.Build.0 = Debug|x64
|
||||
{EFC9FC4D-E92B-4C6D-9B97-26D38EAA118D}.MinSizeRel|x64.ActiveCfg = Debug|x64
|
||||
{EFC9FC4D-E92B-4C6D-9B97-26D38EAA118D}.MinSizeRel|x64.Build.0 = Debug|x64
|
||||
{EFC9FC4D-E92B-4C6D-9B97-26D38EAA118D}.Release|x64.ActiveCfg = Release|x64
|
||||
{EFC9FC4D-E92B-4C6D-9B97-26D38EAA118D}.Release|x64.Build.0 = Release|x64
|
||||
{EFC9FC4D-E92B-4C6D-9B97-26D38EAA118D}.RelWithDebInfo|x64.ActiveCfg = Debug|x64
|
||||
{EFC9FC4D-E92B-4C6D-9B97-26D38EAA118D}.RelWithDebInfo|x64.Build.0 = Debug|x64
|
||||
{89D666E6-B500-498F-B026-4D167F68ADFC}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{89D666E6-B500-498F-B026-4D167F68ADFC}.Debug|x64.Build.0 = Debug|x64
|
||||
{89D666E6-B500-498F-B026-4D167F68ADFC}.MinSizeRel|x64.ActiveCfg = Debug|x64
|
||||
{89D666E6-B500-498F-B026-4D167F68ADFC}.MinSizeRel|x64.Build.0 = Debug|x64
|
||||
{89D666E6-B500-498F-B026-4D167F68ADFC}.Release|x64.ActiveCfg = Release|x64
|
||||
{89D666E6-B500-498F-B026-4D167F68ADFC}.Release|x64.Build.0 = Release|x64
|
||||
{89D666E6-B500-498F-B026-4D167F68ADFC}.RelWithDebInfo|x64.ActiveCfg = Debug|x64
|
||||
{89D666E6-B500-498F-B026-4D167F68ADFC}.RelWithDebInfo|x64.Build.0 = Debug|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {422D1620-0166-4C26-9A81-CA8A2EB1AB58}
|
||||
EndGlobalSection
|
||||
GlobalSection(Performance) = preSolution
|
||||
HasPerformanceSessions = true
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+12
-23
@@ -16,26 +16,13 @@ function(Protobuf_Generate_Cpp SRCS HDRS)
|
||||
|
||||
list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc")
|
||||
list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h")
|
||||
set(PROTOBUF_INCLUDE_DIR_ARGS ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
# list(APPEND PROTOBUF_INCLUDE_DIR_ARGS " -I ")
|
||||
# list(APPEND PROTOBUF_INCLUDE_DIR_ARGS ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
foreach(INC ${PROTOBUF_INCLUDE_DIRS})
|
||||
set(PROTOBUF_INCLUDE_DIR_ARGS ${PROTOBUF_INCLUDE_DIR_ARGS} -I ${INC})
|
||||
# list(APPEND PROTOBUF_INCLUDE_DIR_ARGS " -I ")
|
||||
# list(APPEND PROTOBUF_INCLUDE_DIR_ARGS ${INC})
|
||||
endforeach()
|
||||
set(PROTOBUF_INCLUDE_DIR_ARGS ${PROTOBUF_INCLUDE_DIR_ARGS} -I ${CMAKE_CURRENT_BINARY_DIR})
|
||||
# string(JOIN " -I " PROTOBUF_INCLUDE_DIR_ARGS_ ${PROTOBUF_INCLUDE_DIR_ARGS})
|
||||
message("pb inc: ${PROTOBUF_INCLUDE_DIR_ARGS}")
|
||||
# set(cmd_text "${Protobuf_PROTOC_EXECUTABLE} --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I ${PROTOBUF_INCLUDE_DIR_ARGS_} -I ${CMAKE_CURRENT_SOURCE_DIR} ${ABS_FIL}")
|
||||
message("CMD: ${Protobuf_PROTOC_EXECUTABLE} ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I ${PROTOBUF_INCLUDE_DIR_ARGS} ${ABS_FIL}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h"
|
||||
COMMAND ${Protobuf_PROTOC_EXECUTABLE} --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I ${PROTOBUF_INCLUDE_DIR_ARGS} ${ABS_FIL}
|
||||
# COMMAND
|
||||
# ${Protobuf_PROTOC_EXECUTABLE} ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I
|
||||
# ${PROTOBUF_INCLUDE_DIR_ARGS} ${ABS_FIL}
|
||||
COMMAND
|
||||
${Protobuf_PROTOC_EXECUTABLE} ARGS --cpp_out=${CMAKE_BINARY_DIR}/src -I
|
||||
${PROTOBUF_INCLUDE_DIRS} -I ${CMAKE_SOURCE_DIR}/src ${ABS_FIL}
|
||||
DEPENDS ${ABS_FIL}
|
||||
COMMENT "Running C++ protocol buffer compiler on ${FIL}"
|
||||
VERBATIM)
|
||||
@@ -103,10 +90,10 @@ function(Service_Compiler_Generate_Cpp SRCS HDRS)
|
||||
COMMAND SET PYTHONPATH=${SPARK_PYTHON}
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/.venv/Scripts/activate.bat
|
||||
COMMAND
|
||||
${Protobuf_PROTOC_EXECUTABLE} ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR}
|
||||
${Protobuf_PROTOC_EXECUTABLE} ARGS --cpp_out=${CMAKE_BINARY_DIR}/src
|
||||
--plugin=protoc-gen-service=${CMAKE_SOURCE_DIR}/src/lib/api/service_compiler/plugin.cmd
|
||||
--service_out=lang=cpp:${CMAKE_CURRENT_BINARY_DIR} -I
|
||||
${PROTOBUF_INCLUDE_DIRS} -I ${CMAKE_CURRENT_SOURCE_DIR} ${ABS_FIL}
|
||||
--service_out=lang=cpp:${CMAKE_BINARY_DIR}/src -I
|
||||
${PROTOBUF_INCLUDE_DIRS} -I ${CMAKE_SOURCE_DIR}/src ${ABS_FIL}
|
||||
DEPENDS ${ABS_FIL} ${TEMPLATES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/api/service_compiler/plugin.py
|
||||
COMMENT "Running C++ service compiler on ${FIL}"
|
||||
@@ -122,10 +109,10 @@ function(Service_Compiler_Generate_Cpp SRCS HDRS)
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/tools/venv_exec.sh
|
||||
ARGS
|
||||
${Protobuf_PROTOC_EXECUTABLE}
|
||||
--cpp_out=${CMAKE_CURRENT_BINARY_DIR}
|
||||
--cpp_out=${CMAKE_BINARY_DIR}/src
|
||||
--plugin=protoc-gen-service=${CMAKE_SOURCE_DIR}/src/lib/api/service_compiler/plugin.py
|
||||
--service_out=lang=cpp,make_file_name=${ABS_FIL}:${CMAKE_CURRENT_BINARY_DIR}
|
||||
-I ${PROTOBUF_INCLUDE_DIRS} -I ${CMAKE_CURRENT_SOURCE_DIR} ${ABS_FIL}
|
||||
--service_out=lang=cpp,make_file_name=${ABS_FIL}:${CMAKE_BINARY_DIR}/src
|
||||
-I ${PROTOBUF_INCLUDE_DIRS} -I ${CMAKE_SOURCE_DIR}/src ${ABS_FIL}
|
||||
DEPENDS ${ABS_FIL} ${TEMPLATES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/api/service_compiler/plugin.py
|
||||
COMMENT "Running C++ service compiler on ${FIL}"
|
||||
@@ -134,6 +121,8 @@ function(Service_Compiler_Generate_Cpp SRCS HDRS)
|
||||
|
||||
endforeach()
|
||||
|
||||
set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE)
|
||||
|
||||
# remove some warnings from protobuf generated files
|
||||
if(WIN32)
|
||||
set_source_files_properties(${${SRCS}} PROPERTIES COMPILE_FLAGS
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
[cmake ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools
|
||||
[cmake presets]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
|
||||
[cpp ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
|
||||
[clangd ext]: https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd
|
||||
[py venv]: https://docs.python.org/3/library/venv.html
|
||||
|
||||
# C++ Intellisense for Spark on Windows (VS Code)
|
||||
**Steps**
|
||||
1. Install the **[C++ extension][cpp ext]**
|
||||
2. Install the **[CMake extension][cmake ext]**
|
||||
3. Copy `c_cpp_properties.json` from this folder to `$workspaceFolder/.vscode/`
|
||||
4. In the VS Code Command Palette, run **CMake: Select Configure Preset**, then
|
||||
select '**spark**' from the dropdown
|
||||
|
||||
**Notes**
|
||||
* Intellisense won't work for generated files/types until you build libspark
|
||||
|
||||
# C++ Intellisense for Sparkd on Linux (VS Code)
|
||||
1. Install the **[CMake extension][cmake ext]**
|
||||
2. Install the **[Clangd extension][clangd ext]**
|
||||
3. (Optional) Install the **[C++ extension][cpp ext]** (*needed for debugging,
|
||||
but intellisense should work without it*).
|
||||
4. If the **[C++ extension][cpp ext]** is installed, disable its intellisense in
|
||||
your workspace settings (*Clangd intellisense is better*)
|
||||
```
|
||||
"C_Cpp.intelliSenseEngine": "disabled"
|
||||
```
|
||||
5. In the VS Code Command Palette, run **CMake: Select Configure Preset**, then select the
|
||||
**[preset][cmake presets]** you wish to build from the dropdown
|
||||
* *The **Clang** presets may work best with the **Clangd** extension*
|
||||
* *Intellisense won't work for generated files/types until you build libspark*
|
||||
|
||||
# Build Sparkd on Linux
|
||||
|
||||
**Notes**
|
||||
* Sparkd is built using **[CMakePresets][cmake presets]**
|
||||
|
||||
**Steps**
|
||||
1. Set up the **[Python virtual environment][py venv]**<br>
|
||||
* *Run commands from the repo root dir unless otherwise noted*
|
||||
1. Create the virtual environment
|
||||
```
|
||||
python3 -m venv .venv
|
||||
```
|
||||
2. Activate the virtual environment
|
||||
```
|
||||
. .venv/bin/activate
|
||||
```
|
||||
3. Verify `python` in the venv is first in the path<br>
|
||||
* ***Expected:** output shows the absolute path to `.venv/bin/python`*
|
||||
```
|
||||
which python
|
||||
```
|
||||
4. Update `pip`<br>
|
||||
* *The venv module initializes virtual environments with its own version
|
||||
of pip, rather than the system's version. Even if the latter is up-to-date, the
|
||||
former may not be.*
|
||||
```
|
||||
python -m pip install --upgrade pip
|
||||
```
|
||||
5. Install the packages listed `requirements.txt`
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
2. Build **Sparkd**<br>
|
||||
* From the terminal
|
||||
```
|
||||
tools/build_sparkd.sh
|
||||
```
|
||||
* *Run with `--help` to see available options*
|
||||
* From VS Code
|
||||
1. Install the **[CMake extension][cmake ext]**
|
||||
2. In the VS Code Command Palette, run **CMake: Select Configure
|
||||
Preset**, then select the **[preset][cmake presets]** you wish to
|
||||
build from the dropdown
|
||||
3. Run **CMake: Build** in the command palette.
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"version": 4,
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Windows",
|
||||
"intelliSenseMode": "windows-msvc-x64",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/src/",
|
||||
"${workspaceFolder}/build/src/",
|
||||
"${workspaceFolder}/externals/windows/include/"
|
||||
],
|
||||
"defines": ["_WIN32", "__x86_64__"],
|
||||
"browse": {
|
||||
"path": [
|
||||
"${workspaceRoot}/src",
|
||||
"${workspaceRoot}/build/src"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true
|
||||
},
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"configurationProvider": "ms-vscode.cmake-tools",
|
||||
"compilerPath": "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.34.31933/bin/Hostx64/x64/cl.exe",
|
||||
"mergeConfigurations": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
set(sources /home/taha/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Glossary/_index.md;content/bts_spark/Help/_index.md;content/bts_spark/Help/about_spark.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Launcher/_index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Logging-In-to-the-System/_index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Market/_index.md;content/bts_spark/Market/change_securities.png;content/bts_spark/Market/image11.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/market_view.png;content/bts_spark/Market/settings_icon.png;content/bts_spark/Market/time_and_sales.png;content/bts_spark/My-Windows/_index.md;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/Settings/_index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Spreader/_index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Trading/OrderTypes/index.md;content/bts_spark/Trading/_index.md;content/bts_spark/Trading/algo_studio.png;content/bts_spark/Trading/collapse_all_icon.png;content/bts_spark/Trading/cover_algo_control.png;content/bts_spark/Trading/expand_all_icon.png;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/join_algo_control.png;content/bts_spark/Trading/ladder.png;content/bts_spark/Trading/ladder_badges.png;content/bts_spark/Trading/ladder_main_part.png;content/bts_spark/Trading/lean_algo_control.png;content/bts_spark/Trading/mini_orderbook.png;content/bts_spark/Trading/oco_algo_control.png;content/bts_spark/Trading/offset_algo_control.png;content/bts_spark/Trading/pnl_calculation.png;content/bts_spark/Trading/pnl_window.png;content/bts_spark/Trading/recreate_icon.png;content/bts_spark/Trading/settings_icon.png;content/bts_spark/Trading/spark_menu_order_book.png;content/bts_spark/Trading/top_algo_control.png;content/bts_spark/Trading/trigger_algo_control.png;content/bts_spark/_index.md;content/release_notes/_index.md;content/system_requirements/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
|
||||
Submodule
+1
Submodule docker/linux added at 6537812262.
@@ -1,103 +0,0 @@
|
||||
FROM ubuntu:22.10
|
||||
|
||||
RUN \
|
||||
apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
clang-13 \
|
||||
clang-format-13 \
|
||||
clangd-13 \
|
||||
clang-15 \
|
||||
clang-format-15 \
|
||||
clangd-15 \
|
||||
cmake \
|
||||
curl \
|
||||
dotnet7 \
|
||||
fish \
|
||||
g++-11 \
|
||||
g++-12 \
|
||||
gnupg \
|
||||
liblttng-ust-dev \
|
||||
libssh2-1-dev \
|
||||
libpcap0.8-dev \
|
||||
locales \
|
||||
ninja-build \
|
||||
nodejs \
|
||||
npm \
|
||||
openssh-server \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
rapidjson-dev \
|
||||
software-properties-common \
|
||||
sqlite3 \
|
||||
sudo \
|
||||
tar \
|
||||
vim \
|
||||
wget
|
||||
|
||||
# this is done here so that ccache sees clang++ installed, not clang-10
|
||||
RUN \
|
||||
apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y ccache
|
||||
|
||||
# emumba deps
|
||||
RUN \
|
||||
apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
tzdata \
|
||||
build-essential \
|
||||
cmake-curses-gui \
|
||||
git-all \
|
||||
doxygen \
|
||||
python3-sphinx \
|
||||
graphviz \
|
||||
libgtest-dev \
|
||||
libssl-dev \
|
||||
libboost-filesystem-dev \
|
||||
libboost-program-options-dev \
|
||||
libboost-test-dev \
|
||||
libboost-date-time-dev \
|
||||
zlib1g-dev \
|
||||
netcat-traditional \
|
||||
net-tools \
|
||||
gdb gdbserver \
|
||||
pkg-config
|
||||
|
||||
# set up default executables (update-alternatives)
|
||||
RUN \
|
||||
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-13 100; \
|
||||
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-13 100; \
|
||||
update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-13 100; \
|
||||
update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-13 100; \
|
||||
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100; \
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100
|
||||
|
||||
# Add compiler symlinks for ccache
|
||||
RUN \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/clang; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/clang++; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/clang-13; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/clang++-13; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/clang-15; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/clang++-15; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/gcc; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/g++; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/gcc-11; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/g++-11; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/gcc-12; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/g++-12
|
||||
|
||||
RUN locale-gen en_US.UTF-8
|
||||
|
||||
ARG docker_user="bts"
|
||||
ARG docker_uid="1000"
|
||||
ARG docker_pass="bts"
|
||||
|
||||
RUN \
|
||||
groupadd -g "$docker_uid" "$docker_user" \
|
||||
&& useradd \
|
||||
-m \
|
||||
-u "$docker_uid" \
|
||||
-g "$docker_uid" \
|
||||
-G sudo \
|
||||
--password "$(openssl passwd -1 "$docker_pass")" \
|
||||
"$docker_user"
|
||||
@@ -1,64 +0,0 @@
|
||||
[dev containers]: https://code.visualstudio.com/docs/devcontainers/containers
|
||||
[dev containers ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers
|
||||
[docker repo sparkd-dev]: https://hub.docker.com/repository/docker/bluetradingsystems/sparkd-dev/general
|
||||
# Sparkd Dev Container
|
||||
## How to create a VS Code [Dev Container][dev containers]
|
||||
|
||||
1. Make sure the **[Dev Containers][dev containers ext]** extension is installed
|
||||
1. Make sure your Docker image is up to date
|
||||
```
|
||||
docker pull bluetradingsystems/sparkd-dev:latest
|
||||
```
|
||||
1. Copy **devcontainer.json** from this folder to **$workspaceFolder/.devcontainer/**.
|
||||
|
||||
1. In the VS Code Command Palette, run **Dev Containers: Reopen in Container**
|
||||
|
||||
---
|
||||
## How to create a Sparkd Container in the terminal (without VS Code)
|
||||
|
||||
1. Make sure your Docker image is up to date
|
||||
```
|
||||
docker pull bluetradingsystems/sparkd-dev:latest
|
||||
```
|
||||
2. Start the container
|
||||
```
|
||||
# Replace <cont_name> with a name for the container. The container's
|
||||
# "$HOME" dir will be mounted to "$HOME/.sparkd-dev/mounts/<cont_name> on
|
||||
# the host
|
||||
|
||||
./start_docker.sh --container <cont_name> --mount-existing
|
||||
```
|
||||
* *Replace <cont_name> with a name for the container*
|
||||
* *The container's `$HOME` dir will be mounted to
|
||||
`$HOME/.sparkd-dev/mounts<cont_name>` on the host*<br>
|
||||
* *Run: `./start_docker.sh --help` to see additional options*
|
||||
3. *(Optional) Attach VS Code to the running container.*<br>
|
||||
Run **Dev Containers: Attach to Running Container** from the command palette,
|
||||
then select your container in the dropdown
|
||||
|
||||
---
|
||||
## How to update the Docker image
|
||||
|
||||
1. Change the image by modifying the Dockerfile (don't tag arbitrary
|
||||
images on your machine and push them to our repo on Docker Hub)
|
||||
|
||||
1. After modifying the Dockerfile, (re)build the image
|
||||
```
|
||||
# Run in this dir
|
||||
docker build -t bluetradingsystems/sparkd-dev:latest .
|
||||
```
|
||||
***Note:** Your docker login must have write permissions to push to the
|
||||
[sparkd-dev][docker repo sparkd-dev] repository*
|
||||
|
||||
2. Commit the changes to Dockerfile in git
|
||||
|
||||
3. Push the new image to Docker Hub
|
||||
|
||||
```
|
||||
docker push johncolvin/bluetradingsystems:sparkd-dev
|
||||
```
|
||||
***Note:** The image doesn't have to be on Docker Hub, we can simply build
|
||||
it from scratch on our local machines (with the Dockerfile). However, this
|
||||
takes time. It is significantly quicker to download a prebuilt image, so we
|
||||
store it on a remote repository to save time when setting up a new dev
|
||||
machine.*
|
||||
@@ -1,119 +0,0 @@
|
||||
# ~/.bashrc: executed by bash(1) for non-login shells.
|
||||
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
|
||||
# for examples
|
||||
|
||||
# If not running interactively, don't do anything
|
||||
case $- in
|
||||
*i*) ;;
|
||||
*) return;;
|
||||
esac
|
||||
|
||||
# don't put duplicate lines or lines starting with space in the history.
|
||||
# See bash(1) for more options
|
||||
HISTCONTROL=ignoreboth
|
||||
|
||||
# append to the history file, don't overwrite it
|
||||
shopt -s histappend
|
||||
|
||||
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
|
||||
HISTSIZE=1000
|
||||
HISTFILESIZE=2000
|
||||
|
||||
# check the window size after each command and, if necessary,
|
||||
# update the values of LINES and COLUMNS.
|
||||
shopt -s checkwinsize
|
||||
|
||||
# If set, the pattern "**" used in a pathname expansion context will
|
||||
# match all files and zero or more directories and subdirectories.
|
||||
#shopt -s globstar
|
||||
|
||||
# make less more friendly for non-text input files, see lesspipe(1)
|
||||
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
|
||||
|
||||
# set variable identifying the chroot you work in (used in the prompt below)
|
||||
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
|
||||
debian_chroot=$(cat /etc/debian_chroot)
|
||||
fi
|
||||
|
||||
# set a fancy prompt (non-color, unless we know we "want" color)
|
||||
case "$TERM" in
|
||||
xterm-color|*-256color) color_prompt=yes;;
|
||||
esac
|
||||
|
||||
# uncomment for a colored prompt, if the terminal has the capability; turned
|
||||
# off by default to not distract the user: the focus in a terminal window
|
||||
# should be on the output of commands, not on the prompt
|
||||
#force_color_prompt=yes
|
||||
|
||||
if [ -n "$force_color_prompt" ]; then
|
||||
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
|
||||
# We have color support; assume it's compliant with Ecma-48
|
||||
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
|
||||
# a case would tend to support setf rather than setaf.)
|
||||
color_prompt=yes
|
||||
else
|
||||
color_prompt=
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$color_prompt" = yes ]; then
|
||||
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
|
||||
else
|
||||
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
|
||||
fi
|
||||
unset color_prompt force_color_prompt
|
||||
|
||||
# If this is an xterm set the title to user@host:dir
|
||||
case "$TERM" in
|
||||
xterm*|rxvt*)
|
||||
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
# enable color support of ls and also add handy aliases
|
||||
if [ -x /usr/bin/dircolors ]; then
|
||||
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
|
||||
alias ls='ls --color=auto'
|
||||
#alias dir='dir --color=auto'
|
||||
#alias vdir='vdir --color=auto'
|
||||
|
||||
alias grep='grep --color=auto'
|
||||
alias fgrep='fgrep --color=auto'
|
||||
alias egrep='egrep --color=auto'
|
||||
fi
|
||||
|
||||
# colored GCC warnings and errors
|
||||
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
|
||||
|
||||
# some more ls aliases
|
||||
alias ll='ls -alF'
|
||||
alias la='ls -A'
|
||||
alias l='ls -CF'
|
||||
|
||||
# Add an "alert" alias for long running commands. Use like so:
|
||||
# sleep 10; alert
|
||||
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
|
||||
|
||||
# Alias definitions.
|
||||
# You may want to put all your additions into a separate file like
|
||||
# ~/.bash_aliases, instead of adding them here directly.
|
||||
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
|
||||
|
||||
if [ -f ~/.bash_aliases ]; then
|
||||
. ~/.bash_aliases
|
||||
fi
|
||||
|
||||
# enable programmable completion features (you don't need to enable
|
||||
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
|
||||
# sources /etc/bash.bashrc).
|
||||
if ! shopt -oq posix; then
|
||||
if [ -f /usr/share/bash-completion/bash_completion ]; then
|
||||
. /usr/share/bash-completion/bash_completion
|
||||
elif [ -f /etc/bash_completion ]; then
|
||||
. /etc/bash_completion
|
||||
fi
|
||||
fi
|
||||
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
@@ -1,22 +0,0 @@
|
||||
set fish_config_home $XDG_CONFIG_HOME/fish
|
||||
if test -f "$fish_config_home/aliases.fish"
|
||||
source "$fish_config_home/aliases.fish"
|
||||
end
|
||||
# enable general colors and case insensitive search in 'less'
|
||||
set -x LESS "$LESS -R -Q -I"
|
||||
set -x LC_ALL en_US.UTF-8
|
||||
# enable syntax highlighting in 'less'
|
||||
# set highlight color scheme in 'less' (fg)(bg)
|
||||
set -g LESS_TERMCAP_md (tput bold)(tput setaf 36)
|
||||
set -g LESS_TERMCAP_me (tput sgr0)
|
||||
set -g LESS_TERMCAP_so (tput setaf 232)(tput setab 49)
|
||||
|
||||
set -q TERM
|
||||
if test $status != 0; or test $TERM = 'xterm'
|
||||
set -g TERM 'xterm-256color'
|
||||
end
|
||||
|
||||
# Fix impossible-to-see navy blue command text color
|
||||
set fish_color_command F8F8F2
|
||||
|
||||
set -g PATH "$HOME/.local/bin" "$PATH"
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"image": "bluetradingsystems/sparkd-dev:latest",
|
||||
"initializeCommand": "${localWorkspaceFolder}/docker/sparkd/dev/devcontainer_initialize.sh",
|
||||
"postCreateCommand": "/home/bts/spark/docker/sparkd/dev/devcontainer_post_create.sh",
|
||||
"containerUser": "bts",
|
||||
"workspaceMount": "source=${localWorkspaceFolder},target=/home/bts/spark,type=bind",
|
||||
"workspaceFolder": "/home/bts/spark",
|
||||
"updateRemoteUserUID": true,
|
||||
"shutdownAction": "none",
|
||||
// Note: If running multiple containers on the same host, each must use a
|
||||
// unique set of ports. Usually we just add 1000 to each port as needed. For
|
||||
// example, if 8000-8003 is taken, go with 9000-9003, then 10000-10003, etc.
|
||||
"runArgs": [
|
||||
"-p",
|
||||
"8000:8000",
|
||||
"-p",
|
||||
"8001:8001",
|
||||
"-p",
|
||||
"8002:8002",
|
||||
"-p",
|
||||
"8003:8003"
|
||||
],
|
||||
"mounts": [
|
||||
"source=${localEnv:HOME}/.sparkd-dev/mounts/sparkd-dev,target=/home/bts,type=bind"
|
||||
]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
mkdir -p "$HOME/.sparkd-dev/mounts/sparkd-dev"
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
this_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
|
||||
repo_dir="$(cd "$this_dir" && git rev-parse --show-toplevel)"
|
||||
tools_dir="$repo_dir/tools"
|
||||
gen_tag="GENERATED BY $(basename "${BASH_SOURCE[0]}")"
|
||||
|
||||
function copy_if_not_exist() {
|
||||
src="$1"
|
||||
dest="$2"
|
||||
if [[ ! -e "$src" ]]; then
|
||||
echo "Unable to copy $src to $dest. Source file doesn't exist"
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -e "$dest" ]]; then
|
||||
cp "$src" "$dest"
|
||||
fi
|
||||
}
|
||||
|
||||
# Init bashrc
|
||||
echo "Initializing bashrc"
|
||||
copy_if_not_exist "$this_dir/bashrc" "$HOME/.bashrc"
|
||||
|
||||
# Init fish config
|
||||
echo "Initializing fish config"
|
||||
fish_cfg_dir="$HOME/.config/fish/"
|
||||
mkdir -p "$fish_cfg_dir"
|
||||
exec "$tools_dir/write_tag_block.sh" \
|
||||
--tag "$gen_tag" \
|
||||
--replace \
|
||||
--in-place \
|
||||
--file "$fish_cfg_dir/config.fish" \
|
||||
--text "$(cat "$this_dir/config.fish")"
|
||||
@@ -1,250 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function _exit() {
|
||||
if [[ "$#" -gt 1 ]]; then
|
||||
# When there are multiple args, the last arg is the exit code.
|
||||
echo "${@:1:($# - 1)}"
|
||||
exit "${@: -1}"
|
||||
fi
|
||||
echo "$1"
|
||||
exit 0
|
||||
}
|
||||
|
||||
help_text="\
|
||||
Add or update a user's uid
|
||||
|
||||
This script is intended to be run in a Docker container as a means to obtain
|
||||
file permissions in mounted directories owned by the specified UID.
|
||||
|
||||
Usage: set_user.sh [OPTIONS] USER UID
|
||||
|
||||
Positionals:
|
||||
USER The name of the user
|
||||
|
||||
Options:
|
||||
--user USER The name of the user (defaults to 'bts')
|
||||
--uid UID The new uid to give USER (defaults to '1000')
|
||||
--group GROUP The primary group to give USER (defaults to USER)
|
||||
--gid GID The primary gid to give USER (defaults to UID)
|
||||
--pass PASSWORD The password to give USER (defaults to USER, only applies
|
||||
if USER hasn't been created yet)
|
||||
--modify-existing Update UID/GID even if USER already exists
|
||||
-h,--help Print this help message and exit
|
||||
|
||||
"
|
||||
|
||||
function _add_group() {
|
||||
if [[ "$#" -ne 2 ]]; then
|
||||
_exit "'_add_group' requires exactly 2 args (group, gid). Received $#." 1
|
||||
fi
|
||||
local group="$1"
|
||||
local gid="$2"
|
||||
# Check if gid already exists
|
||||
local existing_group=""
|
||||
existing_group="$(getent group "$gid" | cut -d : -f 1)"
|
||||
if [[ -n "$existing_group" ]]; then
|
||||
if [[ "$existing_group" != "$group" ]]; then
|
||||
echo "\
|
||||
The gid '$gid' is already in use by group '$existing_group' (will not rename \
|
||||
the group)"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
# Check if group with same name already exists
|
||||
local existing_gid=""
|
||||
existing_gid="$(getent group "$group" | cut -d : -f 3)"
|
||||
if [[ -n "$existing_gid" ]]; then
|
||||
# The group already exists. Only modify if --modify-existing was specified.
|
||||
if [[ "$modify_existing" -eq 0 ]]; then
|
||||
_exit "\
|
||||
The group '$group' already exists with gid '$existing_gid'.
|
||||
Specify --modify-existing to change the gid anyways." 1
|
||||
fi
|
||||
echo "Changing gid for group '$group' from '$existing_gid' to '$gid'"
|
||||
groupmod -g "$gid" "$group"
|
||||
else
|
||||
# The group doesn't exist. Add it
|
||||
echo "Adding group '$group:$gid'"
|
||||
groupadd -g "$gid" "$group"
|
||||
fi
|
||||
}
|
||||
|
||||
function _add_user() {
|
||||
local user="$1"
|
||||
local uid="$2"
|
||||
local group="$3"
|
||||
local gid="$4"
|
||||
local pass="$5"
|
||||
if [[ "$#" -ne 5 ]]; then
|
||||
_exit "'_add_user' requires exactly 5 args (user, uid, group, gid, pass). \
|
||||
Received $#." 1
|
||||
fi
|
||||
|
||||
# Check if uid is being used by another user
|
||||
local existing_user=""
|
||||
existing_user="$(getent passwd "$uid" | cut -d : -f 1)"
|
||||
if [[ -n "$existing_user" ]]; then
|
||||
if [[ "$existing_user" != "$user" ]]; then
|
||||
_exit "The uid '$uid' is already in use by user '$existing_user'." 1
|
||||
fi
|
||||
|
||||
echo "The user '$user' already has the specified uid '$uid'."
|
||||
# Modify gid if necessary
|
||||
local existing_gid=""
|
||||
existing_gid="$(id -g "$user" 2> /dev/null)"
|
||||
if [[ "$existing_gid" = "$gid" ]]; then
|
||||
echo "The user '$user' already has the specified gid '$gid'."
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$modify_existing" -eq 0 ]]; then
|
||||
_exit "\
|
||||
The user '$user' is associated with the primary group '$existing_gid'.
|
||||
Specify --modify-existing to change the gid anyways." 1
|
||||
fi
|
||||
|
||||
echo "Changing primary group for '$user' from '$existing_gid' to '$gid'."
|
||||
usermod -g "$gid" "$user"
|
||||
return
|
||||
fi
|
||||
|
||||
# Check if user with same name already exists
|
||||
local existing_uid=""
|
||||
existing_uid="$(id -u "$user" 2> /dev/null)"
|
||||
if [[ -n "$existing_uid" ]]; then
|
||||
if [[ "$existing_uid" = "$uid" ]]; then
|
||||
_exit "The user '$user' already has the specified uid '$uid'" 0
|
||||
fi
|
||||
|
||||
if [[ "$modify_existing" -eq 0 ]]; then
|
||||
_exit "\
|
||||
User '$user' already exists with uid '$existing_uid'.
|
||||
Specify --modify-existing to change the uid anyways." 1
|
||||
fi
|
||||
|
||||
echo "Changing uid for user '$user' from '$existing_uid' to '$uid'"
|
||||
if ! usermod -u "$uid" -g "$gid" "$user"; then
|
||||
_exit "usermod exited with code $?" 1
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
# The user doesn't exist. Add it
|
||||
echo "Adding user '$user:$uid' (group='$group:$gid', password='$pass')"
|
||||
useradd -m -u "$uid" -g "$gid" -G sudo --password "$(openssl passwd -1 "$pass")" "$user"
|
||||
}
|
||||
|
||||
function _update_fish_dir_perms() {
|
||||
local user="$1"
|
||||
local uid="$2"
|
||||
local gid="$3"
|
||||
# When a fish shell session starts, a temp dir named '/tmp/fish.<user>' is
|
||||
# created with the user's current uid/gid. If such a dir exists for the
|
||||
# specified USER, make sure the uid/gid match the new values.
|
||||
fish_dir="/tmp/fish.$user"
|
||||
if [[ -d "$fish_dir" ]]; then
|
||||
perms="$uid:$gid"
|
||||
existing_perms="$(stat -c %u:%g "$fish_dir")"
|
||||
if [[ "$existing_perms" != "$perms" ]]; then
|
||||
echo "Changing uid:gid for dir '$fish_dir' from '$existing_perms' to '$perms'"
|
||||
chown -R "$perms" "$fish_dir"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
user=""
|
||||
group=""
|
||||
pass=""
|
||||
declare -i uid=0
|
||||
declare -i gid=0
|
||||
declare -i modify_existing=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--user)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "USER (--user) must be non-empty" 1
|
||||
elif [[ "$2" = "root" ]]; then
|
||||
_exit "USER (--user) cannot be 'root'" 1
|
||||
fi
|
||||
user="$2"
|
||||
shift 2
|
||||
;;
|
||||
--group)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "GROUP (--group) must be non-empty" 1
|
||||
elif [[ "$2" = "root" ]]; then
|
||||
_exit "GROUP (--group) cannot be 'root'" 1
|
||||
fi
|
||||
group="$2"
|
||||
shift 2
|
||||
;;
|
||||
--uid)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "UID (--uid) must be non-empty" 1
|
||||
fi
|
||||
uid="$2"
|
||||
shift 2
|
||||
;;
|
||||
--gid)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "GID (--gid) must be non-empty" 1
|
||||
fi
|
||||
gid="$2"
|
||||
shift 2
|
||||
;;
|
||||
--pass)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "PASS (--pass) must be non-empty" 1
|
||||
fi
|
||||
pass="$2"
|
||||
shift 2
|
||||
;;
|
||||
--modify-existing)
|
||||
modify_existing=1
|
||||
shift 1
|
||||
;;
|
||||
-h)
|
||||
;&
|
||||
--help)
|
||||
_exit "$help_text" 0
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$1" ]]; then
|
||||
_exit "Invalid positional argument '$1'" 1
|
||||
elif [[ -z "$user" ]]; then
|
||||
user="$1"
|
||||
elif [[ -z "$uid" ]]; then
|
||||
uid="$1"
|
||||
else
|
||||
_exit "Unexpected argument '$1'" 1
|
||||
fi
|
||||
shift 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$user" ]]; then
|
||||
user="bts"
|
||||
fi
|
||||
|
||||
if [[ $uid -eq 0 ]]; then
|
||||
uid=1000
|
||||
fi
|
||||
|
||||
# In general, a user's primary group/gid matches the user's name/uid.
|
||||
# If group/gid args weren't specified, set them to name/uid.
|
||||
if [[ -z "$group" ]]; then
|
||||
group="$user"
|
||||
fi
|
||||
if [[ $gid -eq 0 ]]; then
|
||||
gid=$uid
|
||||
fi
|
||||
if [[ -z "$pass" ]]; then
|
||||
pass="$user"
|
||||
fi
|
||||
|
||||
_add_group "$group" "$gid"
|
||||
_add_user "$user" "$uid" "$group" "$gid" "$pass"
|
||||
_update_fish_dir_perms "$user" "$uid" "$gid"
|
||||
@@ -1,230 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function _exit() {
|
||||
if [[ "$#" -gt 1 ]]; then
|
||||
# When there are multiple args, the last arg is the exit code.
|
||||
echo "${@:1:($# - 1)}"
|
||||
exit "${@: -1}"
|
||||
fi
|
||||
echo "$1"
|
||||
exit 0
|
||||
}
|
||||
|
||||
help_text="\
|
||||
Start a docker dev container
|
||||
Usage: start_docker.sh [OPTIONS] image container
|
||||
|
||||
Positionals:
|
||||
image The image to instantiate
|
||||
container The name for the container
|
||||
|
||||
Options:
|
||||
-u,--user USER The user to add in the container (defaults to 'bts')
|
||||
-p,--pass PASS The password for USER in the container (defaults to USER)
|
||||
-i,--image IMAGE The image to instantiate (defaults to 'bluetradingsystems/sparkd-dev:latest')
|
||||
-c,--container CONT The name for the container
|
||||
-m,--mount MOUNT The name for the folder (outside the container) to create/mount to the home
|
||||
directory in the container. Mount folders are added here: $mount_prefix
|
||||
--mount-existing Mount the specified dir even if it already exists
|
||||
--port <cont_port>:<host_port> OR [<cont_port>:<host_port>,...] Port mapping(s). May be
|
||||
specified multiple times.
|
||||
-h,--help Print this help message and exit
|
||||
|
||||
"
|
||||
|
||||
mount_prefix="$HOME/.sparkd-dev/mounts"
|
||||
declare docker_user=""
|
||||
declare docker_pass=""
|
||||
declare image_name=""
|
||||
declare cont_name=""
|
||||
declare mount_name=""
|
||||
declare -i mount_existing=0
|
||||
declare -a port_mappings=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-u)
|
||||
;&
|
||||
--user)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "User (-u,--user) must be non-empty" 1
|
||||
fi
|
||||
docker_user="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p)
|
||||
;&
|
||||
--pass)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "Password (-p,--pass) must be non-empty" 1
|
||||
fi
|
||||
docker_pass="$2"
|
||||
shift 2
|
||||
;;
|
||||
-i)
|
||||
;&
|
||||
--image)
|
||||
if [[ -n "$image_name" ]]; then
|
||||
_exit "Unexpected argument '$1'" 1
|
||||
elif [[ -z "$2" ]]; then
|
||||
_exit "Image (-i,--image) must be non-empty" 1
|
||||
fi
|
||||
image_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
-c)
|
||||
;&
|
||||
--container)
|
||||
if [[ -n "$cont_name" ]]; then
|
||||
_exit "Unexpected argument '$1'" 1
|
||||
elif [[ -z "$2" ]]; then
|
||||
_exit "Container (-c,--container) must be non-empty" 1
|
||||
fi
|
||||
cont_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
--port)
|
||||
# Parse port(s) arg, and append the result to 'port_mappings'
|
||||
# Note: port(s) may be specified in bracketed notation:
|
||||
# [a:b,c:d,...,y:z]
|
||||
next_ports="$(echo "$2" | sed -e 's/\[//g' -e 's/\]//g' -e 's/,/ /g')"
|
||||
if [[ "${#port_mappings[@]}" -gt 0 ]]; then
|
||||
next_ports=" $next_ports"
|
||||
fi
|
||||
port_mappings+=("$next_ports")
|
||||
shift 2
|
||||
;;
|
||||
-m)
|
||||
;&
|
||||
--mount)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Mount (-m,--mount) must be non-empty"
|
||||
fi
|
||||
mount_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
--mount-existing)
|
||||
mount_existing=1
|
||||
shift 1
|
||||
;;
|
||||
-h)
|
||||
;&
|
||||
--help)
|
||||
_exit "$help_text" 0
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$1" ]]; then
|
||||
_exit "Invalid positional argument '$1'" "$help_text" 1
|
||||
elif [[ -z "$image_name" ]]; then
|
||||
image_name="$1"
|
||||
elif [[ -z "$cont_name" ]]; then
|
||||
cont_name="$1"
|
||||
else
|
||||
_exit "Unexpected argument '$1'" "$help_text" 1
|
||||
fi
|
||||
shift 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
if [[ -z "$image_name" ]]; then
|
||||
image_name="bluetradingsystems/sparkd-dev:latest"
|
||||
fi
|
||||
|
||||
host_name="$(hostname)"
|
||||
if [[ -n "$cont_name" ]]; then
|
||||
host_name="$host_name.$cont_name"
|
||||
fi
|
||||
|
||||
if [[ -z "$mount_name" ]]; then
|
||||
mount_name="$cont_name"
|
||||
fi
|
||||
|
||||
if [[ -z "$mount_name" && -z "$cont_name" ]]; then
|
||||
_exit "Please specify mount (-m,--mount) and/or container (-c,--container)" 1
|
||||
fi
|
||||
|
||||
if [[ -z "$docker_user" ]]; then
|
||||
docker_user="bts"
|
||||
fi
|
||||
|
||||
if [[ "$docker_user" = "root" ]]; then
|
||||
_exit "USER (--user) cannot be 'root'" 1
|
||||
fi
|
||||
|
||||
cont_home="/home/$docker_user"
|
||||
home_src="$mount_prefix/$mount_name"
|
||||
home_tgt="$cont_home"
|
||||
this_dir="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
|
||||
repo_src="$(cd "$this_dir" && git rev-parse --show-toplevel)"
|
||||
repo_tgt="$cont_home/spark"
|
||||
working_dir="$cont_home"
|
||||
|
||||
if [[ $mount_existing -eq 0 && -e "$home_src" ]]; then
|
||||
# Don't overwrite existing dir (might be mounted in another container)
|
||||
_exit "Mount dir '$home_src' already exists (use --mount-existing to mount \
|
||||
an existing directory)" 1
|
||||
fi
|
||||
|
||||
mkdir -p "$home_src"
|
||||
|
||||
if [[ "${#port_mappings[@]}" -eq 0 ]]; then
|
||||
port_mappings=("8000:8000" "8001:8001" "8002:8002" "8003:8003")
|
||||
fi
|
||||
|
||||
# Adapt port_mappings into args for `docker run` by prepending "-p " to each
|
||||
# item
|
||||
declare -a docker_port_args=()
|
||||
for port_mapping in "${port_mappings[@]}"; do
|
||||
docker_port_args+=("-p $port_mapping")
|
||||
done
|
||||
|
||||
echo "Starting container '$cont_name'
|
||||
image_name : $image_name
|
||||
cont_name : $cont_name
|
||||
home_src : $home_src
|
||||
home_tgt : $home_tgt
|
||||
repo_src : $repo_src
|
||||
repo_tgt : $repo_tgt
|
||||
working dir : $working_dir
|
||||
ports : ${port_mappings[*]}
|
||||
"
|
||||
|
||||
# DON'T wrap $docker_port_args in quotes. We want bash to split args by
|
||||
# whitespace
|
||||
docker run \
|
||||
--net bridge \
|
||||
-h "$host_name" \
|
||||
-e "TZ=America/New_York" \
|
||||
--cap-add=SYS_PTRACE \
|
||||
--security-opt seccomp=unconfined \
|
||||
--name "$cont_name" \
|
||||
-v "$home_src:$home_tgt" \
|
||||
-v "$repo_src:$repo_tgt" \
|
||||
-w "$working_dir" \
|
||||
${docker_port_args[*]} \
|
||||
--shm-size=4024m \
|
||||
-t \
|
||||
-i \
|
||||
-d \
|
||||
-e DISPLAY="$DISPLAY" \
|
||||
"$image_name"
|
||||
|
||||
# Add user w/ same uid and gid as the current user outside of docker, so the
|
||||
# user inside the docker has file permissions in the mounted dir.
|
||||
docker_group="$docker_user"
|
||||
docker_uid="$(id -u "$USER")"
|
||||
docker_gid="$(id -g "$USER")"
|
||||
if [[ -z "$docker_pass" ]]; then
|
||||
docker_pass="$docker_user"
|
||||
fi
|
||||
docker exec -u root -it "$cont_name" "$repo_tgt/docker/sparkd/dev/set_user.sh" \
|
||||
--user "$docker_user" \
|
||||
--uid "$docker_uid" \
|
||||
--group "$docker_group" \
|
||||
--gid "$docker_gid" \
|
||||
--pass "$docker_pass"
|
||||
|
||||
# Run same script VS Code runs to initialize the container
|
||||
docker exec -u "$docker_user" -it "$cont_name" "$repo_tgt/docker/sparkd/dev/devcontainer_post_create.sh"
|
||||
@@ -1,64 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function print_help_and_exit() {
|
||||
cat <<EOF
|
||||
Open a shell inside the docker container
|
||||
Usage: docker_shell.sh [OPTIONS] [CONTAINER]
|
||||
|
||||
Options:
|
||||
-u,--user The user to login with in the container (defaults to the current user)
|
||||
-c,--container The container name (may also be specified as positional arg)
|
||||
-h,--help Print this help message and exit
|
||||
|
||||
EOF
|
||||
exit "$1"
|
||||
}
|
||||
|
||||
|
||||
declare docker_user=""
|
||||
declare cont_name=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-u)
|
||||
;&
|
||||
--user)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "User (-u,--user) must be non-empty"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
docker_user="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h)
|
||||
;&
|
||||
--help)
|
||||
print_help_and_exit 0
|
||||
;;
|
||||
-c)
|
||||
;&
|
||||
--container)
|
||||
shift 1
|
||||
;&
|
||||
*)
|
||||
if [[ ! -z "$cont_name" ]]; then
|
||||
echo "Unexpected argument '$1'"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
cont_name="$1"
|
||||
shift 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
if [[ -z "$cont_name" ]]; then
|
||||
echo "Please specify the container ([-c,--container])"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$docker_user" ]]; then
|
||||
docker_user="$USER"
|
||||
fi
|
||||
|
||||
docker exec -u "$docker_user" -it "$cont_name" bash
|
||||
@@ -1,233 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
mount_prefix="$HOME/.spark-dev/mounts"
|
||||
|
||||
function print_help_and_exit() {
|
||||
cat <<EOF
|
||||
Start a docker dev container
|
||||
Usage: start_docker.sh [OPTIONS] image container
|
||||
|
||||
Positionals:
|
||||
image The image to instantiate
|
||||
container The name for the container
|
||||
|
||||
Options:
|
||||
-u,--user USER The user to add in the container (defaults to the current user)
|
||||
-p,--pass PASS The password for USER in the container (defaults to USER)
|
||||
-i,--image IMAGE The image to instantiate
|
||||
-c,--container CONT The name for the container
|
||||
-m,--mount MOUNT The name for the folder (outside the container) to create/mount to the home
|
||||
directory in the container. Mount folders are added here: $mount_prefix
|
||||
--mount-existing Mount the specified dir even if it already exists
|
||||
--port <cont_port>:<host_port> OR [<cont_port>:<host_port>,...] Port mapping(s). May be
|
||||
specified multiple times.
|
||||
-h,--help Print this help message and exit
|
||||
|
||||
EOF
|
||||
exit "$1"
|
||||
}
|
||||
|
||||
|
||||
declare docker_user=""
|
||||
declare docker_pass=""
|
||||
declare image_name=""
|
||||
declare cont_name=""
|
||||
declare mount_name=""
|
||||
declare -i mount_existing=0
|
||||
declare -a port_mappings=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-u)
|
||||
;&
|
||||
--user)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "User (-u,--user) must be non-empty"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
docker_user="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p)
|
||||
;&
|
||||
--pass)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Password (-p,--pass) must be non-empty"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
docker_pass="$2"
|
||||
shift 2
|
||||
;;
|
||||
-i)
|
||||
;&
|
||||
--image)
|
||||
if [[ -n "$image_name" ]]; then
|
||||
echo "Unexpected argument '$1'"
|
||||
print_help_and_exit 1
|
||||
elif [[ -z "$2" ]]; then
|
||||
echo "Image (-i,--image) must be non-empty"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
image_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
-c)
|
||||
;&
|
||||
--container)
|
||||
if [[ -n "$cont_name" ]]; then
|
||||
echo "Unexpected argument '$1'"
|
||||
print_help_and_exit 1
|
||||
elif [[ -z "$2" ]]; then
|
||||
echo "Container (-c,--container) must be non-empty"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
cont_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
--port)
|
||||
# Parse port(s) arg, and append the result to 'port_mappings'
|
||||
# Note: port(s) may be specified in bracketed notation:
|
||||
# [a:b,c:d,...,y:z]
|
||||
next_ports="$(echo "$2" | sed -e 's/\[//g' -e 's/\]//g' -e 's/,/ /g')"
|
||||
if [[ "${#port_mappings[@]}" -gt 0 ]]; then
|
||||
next_ports=" $next_ports"
|
||||
fi
|
||||
port_mappings+=("$next_ports")
|
||||
shift 2
|
||||
;;
|
||||
-m)
|
||||
;&
|
||||
--mount)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Mount (-m,--mount) must be non-empty"
|
||||
fi
|
||||
mount_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
--mount-existing)
|
||||
mount_existing=1
|
||||
shift 1
|
||||
;;
|
||||
-h)
|
||||
;&
|
||||
--help)
|
||||
print_help_and_exit 0
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$1" ]]; then
|
||||
echo "Invalid positional argument '$1'"
|
||||
print_help_and_exit 1
|
||||
elif [[ -z "$image_name" ]]; then
|
||||
image_name="$1"
|
||||
elif [[ -z "$cont_name" ]]; then
|
||||
cont_name="$1"
|
||||
else
|
||||
echo "Unexpected argument '$1'"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
shift 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
if [[ -z "$image_name" ]]; then
|
||||
image_name="bluetradingsystems/spark-crypto-dev"
|
||||
fi
|
||||
|
||||
host_name="$(hostname)"
|
||||
if [[ -n "$cont_name" ]]; then
|
||||
host_name="$host_name.$cont_name"
|
||||
fi
|
||||
|
||||
if [[ -z "$mount_name" ]]; then
|
||||
mount_name="$cont_name"
|
||||
fi
|
||||
|
||||
if [[ -z "$mount_name" && -z "$cont_name" ]]; then
|
||||
echo "Please specify mount (-m,--mount) and/or container (-c,--container)"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$docker_user" ]]; then
|
||||
docker_user="$USER"
|
||||
fi
|
||||
|
||||
home_src=$mount_prefix/$mount_name
|
||||
home_tgt=$HOME
|
||||
repo_src=$(git rev-parse --show-superproject-working-tree)
|
||||
repo_tgt=$HOME/$(basename "$repo_src")
|
||||
working_dir=$HOME
|
||||
|
||||
if [[ $mount_existing -eq 0 && -e "$home_src" ]]; then
|
||||
# Don't overwrite existing dir (might be mounted in another container)
|
||||
echo "Mount dir '$home_src' already exists (use --mount-existing to mount an existing directory)"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$home_src"
|
||||
|
||||
if [[ "${#port_mappings[@]}" -eq 0 ]]; then
|
||||
port_mappings=("8000:8000" "8001:8001" "8002:8002" "8003:8003")
|
||||
fi
|
||||
|
||||
# Adapt port_mappings into args for `docker run` by prepending "-p " to each
|
||||
# item
|
||||
declare -a docker_port_args=()
|
||||
for port_mapping in "${port_mappings[@]}"; do
|
||||
docker_port_args+=("-p $port_mapping")
|
||||
done
|
||||
|
||||
echo "Starting container '$cont_name'
|
||||
image_name : $image_name
|
||||
cont_name : $cont_name
|
||||
home_src : $home_src
|
||||
home_tgt : $home_tgt
|
||||
repo_src : $repo_src
|
||||
repo_tgt : $repo_tgt
|
||||
working dir : $working_dir
|
||||
ports : ${port_mappings[*]}
|
||||
"
|
||||
|
||||
# DON'T wrap $docker_port_args in quotes. We want bash to split args by
|
||||
# whitespace
|
||||
docker run \
|
||||
--net bridge \
|
||||
-h "$host_name" \
|
||||
-e "TZ=America/New_York" \
|
||||
--cap-add=SYS_PTRACE \
|
||||
--security-opt seccomp=unconfined \
|
||||
--name "$cont_name" \
|
||||
-v "$home_src:$home_tgt" \
|
||||
-v "$repo_src:$repo_tgt" \
|
||||
-w "$working_dir" \
|
||||
${docker_port_args[*]} \
|
||||
--shm-size=4024m \
|
||||
-t \
|
||||
-i \
|
||||
-d \
|
||||
-e DISPLAY="$DISPLAY" \
|
||||
"$image_name" \
|
||||
bash
|
||||
|
||||
# Add user w/ same uid and gid as the current user outside of docker, so the
|
||||
# user inside the docker has file permissions in the mounted dir.
|
||||
docker_group="$docker_user"
|
||||
docker_user_uid="$(id -u "$USER")"
|
||||
docker_user_gid="$(id -g "$USER")"
|
||||
|
||||
echo "Adding user in container '$cont_name'
|
||||
name : $docker_user
|
||||
uid : $docker_user_uid
|
||||
gid : $docker_user_gid"
|
||||
|
||||
if [[ -z "$docker_pass" ]]; then
|
||||
docker_pass="$docker_user"
|
||||
echo "Giving user '$docker_user' a secure password: $docker_pass"
|
||||
fi
|
||||
|
||||
docker exec -u root -it "$cont_name" groupadd -g "$docker_user_gid" "$docker_group"
|
||||
docker exec -u root -it "$cont_name" useradd -u "$docker_user_uid" -g "$docker_user_gid" "$docker_user"
|
||||
docker exec -u root -it "$cont_name" usermod --password "$(openssl passwd -1 "$docker_pass")" "$docker_user"
|
||||
docker exec -u root -it "$cont_name" usermod -aG sudo "$docker_user"
|
||||
docker exec -u root -it "$cont_name" "$repo_tgt/externals/src/emumba/install_emumba-deribit-connector.sh"
|
||||
Submodule
+1
Submodule docker/windows added at de7d40c09d.
+1
-1
Submodule externals updated: 4aaae5b1eb...4a3e40193e.
+1
-1
@@ -6,7 +6,7 @@ isort==5.9.3
|
||||
lazy-object-proxy==1.6.0
|
||||
mccabe==0.6.1
|
||||
platformdirs==2.3.0
|
||||
protobuf==4.21.12
|
||||
protobuf==3.19.1
|
||||
pybars3==0.9.7
|
||||
pycodestyle==2.7.0
|
||||
pycparser==2.20
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.10
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sparkd", "src\server\sparkd\sparkd.vcxproj", "{BBBAFD38-312D-4A8D-9AF0-F2BDEE72CE6F}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{502FD050-679A-3B50-B438-098500700919}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
MinSizeRel|x64 = MinSizeRel|x64
|
||||
Release|x64 = Release|x64
|
||||
RelWithDebInfo|x64 = RelWithDebInfo|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{BBBAFD38-312D-4A8D-9AF0-F2BDEE72CE6F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{BBBAFD38-312D-4A8D-9AF0-F2BDEE72CE6F}.Debug|x64.Build.0 = Debug|x64
|
||||
{BBBAFD38-312D-4A8D-9AF0-F2BDEE72CE6F}.MinSizeRel|x64.ActiveCfg = Release|x64
|
||||
{BBBAFD38-312D-4A8D-9AF0-F2BDEE72CE6F}.MinSizeRel|x64.Build.0 = Release|x64
|
||||
{BBBAFD38-312D-4A8D-9AF0-F2BDEE72CE6F}.Release|x64.ActiveCfg = Release|x64
|
||||
{BBBAFD38-312D-4A8D-9AF0-F2BDEE72CE6F}.Release|x64.Build.0 = Release|x64
|
||||
{BBBAFD38-312D-4A8D-9AF0-F2BDEE72CE6F}.RelWithDebInfo|x64.ActiveCfg = Release|x64
|
||||
{BBBAFD38-312D-4A8D-9AF0-F2BDEE72CE6F}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.Debug|x64.Build.0 = Debug|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.Release|x64.ActiveCfg = Release|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.Release|x64.Build.0 = Release|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {6A7249D7-B40C-4656-9347-60072D0B8FDB}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+1
-1
@@ -8,7 +8,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\l
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark.Console", "src\client\Spark.Console\Spark.Console.csproj", "{387566C6-C6F6-412C-A30C-9855BBCB4816}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sparkcmd", "src\client\sparkcmd\sparkcmd.csproj", "{387566C6-C6F6-412C-A30C-9855BBCB4816}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\spark_core\spark_core.csproj", "{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}"
|
||||
EndProject
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Spark.Admin.WebApp", "src\Spark.Admin.WebApp.fsproj", "{C3143A6F-8D32-4F9E-84EE-B34AF3F3A862}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C3143A6F-8D32-4F9E-84EE-B34AF3F3A862}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C3143A6F-8D32-4F9E-84EE-B34AF3F3A862}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C3143A6F-8D32-4F9E-84EE-B34AF3F3A862}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C3143A6F-8D32-4F9E-84EE-B34AF3F3A862}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -4,6 +4,7 @@
|
||||
<ComputerSpecificConfig>..\..\tools\override\$(computername).props</ComputerSpecificConfig>
|
||||
<SparkRoot>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)\..\..))</SparkRoot>
|
||||
<SourceRoot>$([System.IO.Path]::GetFullPath($(SparkRoot)\src))</SourceRoot>
|
||||
<Externals>$(SparkRoot)\externals\windows</Externals>
|
||||
<DepotName>$([System.IO.Path]::GetFileName($([System.IO.Path]::GetFullPath($(SparkRoot)))))</DepotName>
|
||||
<ErrorReport>None</ErrorReport>
|
||||
<Prefer32Bit>False</Prefer32Bit>
|
||||
@@ -15,25 +16,13 @@
|
||||
<LangVersion>11</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<Choose>
|
||||
<When Condition="'$(OS)' == 'Windows_NT'">
|
||||
<PropertyGroup>
|
||||
<Externals>$(SparkRoot)/externals/windows</Externals>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup>
|
||||
<Externals>$(SparkRoot)/externals/linux</Externals>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
|
||||
<PropertyGroup Label="Output">
|
||||
<BaseOutputPath>$(SparkRoot)\build\$(Configuration)</BaseOutputPath>
|
||||
<OutputPath>$(BaseOutputPath)\bin</OutputPath>
|
||||
<BaseIntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<IntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</IntermediateOutputPath>
|
||||
<ProtoOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</ProtoOutputPath>
|
||||
<GenDir>$(BaseOutputPath)\gen\$(MSBuildProjectName)</GenDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import
|
||||
@@ -42,41 +31,25 @@
|
||||
|
||||
<PropertyGroup Label="ProtoBuf">
|
||||
<ProtocPluginBase>$(SparkRoot)\src\lib\api\service_compiler</ProtocPluginBase>
|
||||
<protoc>$(Externals)\bin\protoc.exe</protoc>
|
||||
<protogen>$(Externals)\bin\protogen.exe</protogen>
|
||||
</PropertyGroup>
|
||||
<Choose>
|
||||
<When Condition="'$(OS)' == 'Windows_NT'">
|
||||
<PropertyGroup Label="ProtoBuf">
|
||||
<protoc>$(Externals)/bin/protoc.exe</protoc>
|
||||
<ProtocPlugin>$(ProtocPluginBase)/plugin.cmd</ProtocPlugin>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup Label="ProtoBuf">
|
||||
<protoc>$(Externals)/bin/protoc</protoc>
|
||||
<ProtocPlugin>$(ProtocPluginBase)/plugin.sh</ProtocPlugin>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<!--
|
||||
Each .proto input file produces 4 output files. For example,
|
||||
'foo_bar.proto' produces:
|
||||
* FooBar.cs
|
||||
* foo_bar.client.pb.cs
|
||||
* foo_bar.server.pb.cs
|
||||
* foo_bar.immutable.pb.cs
|
||||
Google automatically converts .proto filenames to TitleCase when
|
||||
generating .cs files. Unless the .proto file is already in TitleCase
|
||||
(against Google's recommended practice to use snake_case .proto files),
|
||||
the 'CSName' must be overridden with the TitleCase formatting.
|
||||
For example:
|
||||
<Proto3 Include="foo_bar.proto" CSName="FooBar" />
|
||||
-->
|
||||
<Proto2></Proto2>
|
||||
<Proto3>
|
||||
<OutDir>$(ProtoOutputPath)/</OutDir>
|
||||
<CSName>%(FileName)</CSName>
|
||||
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
|
||||
<OutCS>%(OutBase)%(FileName).cs</OutCS>
|
||||
<OutCSClient>%(OutBase)%(FileName).client.pb.cs</OutCSClient>
|
||||
<OutCSServer>%(OutBase)%(FileName).server.pb.cs</OutCSServer>
|
||||
<OutCSImmutable>%(OutBase)%(FileName).immutable.pb.cs</OutCSImmutable>
|
||||
<CompileOptions></CompileOptions>
|
||||
</Proto3>
|
||||
<Proto3Excel>
|
||||
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
|
||||
<OutCSExcel>%(OutBase)%(FileName).excel.pb.cs</OutCSExcel>
|
||||
<CompileOptions></CompileOptions>
|
||||
</Proto3Excel>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,94 @@
|
||||
<Project>
|
||||
|
||||
<Target
|
||||
Name="CompileProto2"
|
||||
Condition="'@(Proto2)'!=''"
|
||||
Inputs="$(MSBuildProjectFile);$(protogen);%(Proto2.FullPath)"
|
||||
Outputs="$(GenDir)\proto2\%(Proto2.filename).cs"
|
||||
AfterTargets="PreBuildEvent">
|
||||
<PropertyGroup>
|
||||
<SourceRelativeFile>$([System.String]::Copy('%(proto2.fullpath)').Replace($(SourceRoot), ''))</SourceRelativeFile>
|
||||
<SourceRelativeDir>$([System.IO.Path]::GetDirectoryName($(SourceRelativeFile)))</SourceRelativeDir>
|
||||
<WorkingDirectory>$(GenDir)\proto2</WorkingDirectory>
|
||||
</PropertyGroup>
|
||||
<MakeDir Directories="$(WorkingDirectory)" />
|
||||
<Copy
|
||||
SourceFiles="%(Proto2.FullPath)"
|
||||
DestinationFolder="$(WorkingDirectory)"
|
||||
SkipUnchangedFiles="True"
|
||||
UseHardLinksIfPossible="True" />
|
||||
<Error
|
||||
Text="protogen.exe does not exist. If you forgot to clone with externals try: git submodule update --init --recursive"
|
||||
Condition="!Exists($(protogen))" />
|
||||
<Exec
|
||||
Command="$(Protogen) %(Proto2.filename)%(proto2.extension) --csharp_out=. -I. -I$(SourceRoot) -I$(Externals)\include"
|
||||
WorkingDirectory="$(WorkingDirectory)" />
|
||||
<ItemGroup>
|
||||
<Compile Include="$(WorkingDirectory)\%(Proto2.filename).cs" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
Run this after GetAssemblyVersion because it happens after the Xaml compiler
|
||||
and exists in both xaml and non-xaml projects. One of the (many) xaml compile
|
||||
passes wipes out the additional compile includes that we add.
|
||||
-->
|
||||
<Target
|
||||
Name="CompileProto3"
|
||||
AfterTargets="GetAssemblyVersion"
|
||||
Condition="'@(Proto3)'!=''"
|
||||
Inputs="$(protoc);
|
||||
%(Proto3.Identity);$(ProtocPluginBase)\plugin.py;
|
||||
$(ProtocPluginBase)\templates\c#\client.pb.cs;
|
||||
$(ProtocPluginBase)\templates\c#\server.pb.cs;
|
||||
$(ProtocPluginBase)\templates\c#\immutable.pb.cs;"
|
||||
Outputs="%(Proto3.OutCS);%(Proto3.OutCSServer);%(Proto3.OutCSClient);%(Proto3.OutCSImmutable)">
|
||||
<MakeDir Directories="%(Proto3.OutBase)" />
|
||||
|
||||
<Message
|
||||
Importance="High"
|
||||
Text="Generating service %(Proto3.FileName)" />
|
||||
<Exec Command="$(SparkRoot)\.venv\Scripts\activate.bat" />
|
||||
<Exec Command="set PYTHONPATH=$(SparkRoot)\.venv\Scripts\python" />
|
||||
<Exec
|
||||
Command="
|
||||
$(protoc) %* %(Proto3.FullPath) ^
|
||||
--error_format=msvs ^
|
||||
--proto_path=$(SourceRoot) ^
|
||||
--plugin="protoc-gen-service=$(ProtocPluginBase)\plugin.cmd" ^
|
||||
--service_out=lang=c#,strip_out_path=1:%(Proto3.OutBase) ^
|
||||
--csharp_out=%(CompileOptions)%(Proto3.OutBase)"
|
||||
WorkingDirectory="%(relativedir)" />
|
||||
<ItemGroup>
|
||||
<Compile Include="%(Proto3.OutCS)" />
|
||||
<Compile Include="%(Proto3.OutCSClient)" />
|
||||
<Compile Include="%(Proto3.OutCSServer)" />
|
||||
<Compile Include="%(Proto3.OutCSImmutable)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="RemoveSystemRuntimeFromRefPack"
|
||||
BeforeTargets="_HandlePackageFileConflicts"
|
||||
Condition="'@(Reference -> WithMetadataValue('NugetPackageId', 'System.Runtime.Experimental'))' != ''">
|
||||
<ItemGroup>
|
||||
<Reference Remove="@(Reference)"
|
||||
Condition="$([System.String]::Copy(%(Reference.Identity)).Contains('System.Runtime.dll')) and
|
||||
'%(Reference.NuGetPackageId)' == 'Microsoft.NETCore.App.Ref'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="AfterClean"
|
||||
AfterTargets="Clean">
|
||||
<Delete Files="%(Proto3.OutCS)" />
|
||||
<Delete Files="%(Proto3.OutCSClient)" />
|
||||
<Delete Files="%(Proto3.OutCSServer)" />
|
||||
<Delete Files="%(Proto3.OutCSImmutable)" />
|
||||
<Delete Files="$(GenDir)\proto2\*" />
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="Test"
|
||||
DependsOnTargets="Build"></Target>
|
||||
|
||||
</Project>
|
||||
@@ -1,68 +0,0 @@
|
||||
<Project>
|
||||
|
||||
<!--
|
||||
Run this after GetAssemblyVersion because it happens after the Xaml compiler
|
||||
and exists in both xaml and non-xaml projects. One of the (many) xaml compile
|
||||
passes wipes out the additional compile includes that we add.
|
||||
-->
|
||||
<Target
|
||||
Name="CompileProto3"
|
||||
AfterTargets="GetAssemblyVersion"
|
||||
Condition="'@(Proto3)'!=''"
|
||||
Inputs="$(protoc);
|
||||
%(Proto3.Identity);$(ProtocPluginBase)/plugin.py;
|
||||
$(ProtocPluginBase)/templates/c#/client.pb.cs;
|
||||
$(ProtocPluginBase)/templates/c#/server.pb.cs;
|
||||
$(ProtocPluginBase)/templates/c#/immutable.pb.cs;"
|
||||
Outputs="
|
||||
%(Proto3.OutDir)/%(Proto3.CSName).cs;
|
||||
%(Proto3.OutDir)/%(Proto3.FileName).client.pb.cs;
|
||||
%(Proto3.OutDir)/%(Proto3.FileName).server.pb.cs;
|
||||
%(Proto3.OutDir)/%(Proto3.FileName).immutable.pb.cs">
|
||||
<MakeDir Directories="%(Proto3.OutDir)" />
|
||||
|
||||
<Message
|
||||
Importance="High"
|
||||
Text="Generating service %(Proto3.FileName)" />
|
||||
|
||||
<PropertyGroup>
|
||||
<GenProtosCmd>$(protoc) %(Proto3.FullPath)</GenProtosCmd>
|
||||
<GenProtosCmd>$(GenProtosCmd) --error_format=msvs</GenProtosCmd>
|
||||
<GenProtosCmd>$(GenProtosCmd) --proto_path=$(SourceRoot)</GenProtosCmd>
|
||||
<GenProtosCmd>$(GenProtosCmd) --plugin="protoc-gen-service=$(ProtocPlugin)"</GenProtosCmd>
|
||||
<GenProtosCmd>$(GenProtosCmd) --service_out=lang=c#,strip_out_path=1:%(Proto3.OutDir)</GenProtosCmd>
|
||||
<GenProtosCmd>$(GenProtosCmd) --csharp_out=%(Proto3.OutDir)</GenProtosCmd>
|
||||
</PropertyGroup>
|
||||
<Exec Command="$(GenProtosCmd)" WorkingDirectory="%(Proto3.RelativeDir)" />
|
||||
<ItemGroup>
|
||||
<Compile Include="%(Proto3.OutDir)/%(Proto3.CSName).cs" />
|
||||
<Compile Include="%(Proto3.OutDir)/%(Proto3.FileName).client.pb.cs" />
|
||||
<Compile Include="%(Proto3.OutDir)/%(Proto3.FileName).server.pb.cs" />
|
||||
<Compile Include="%(Proto3.OutDir)/%(Proto3.FileName).immutable.pb.cs" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="RemoveSystemRuntimeFromRefPack"
|
||||
BeforeTargets="_HandlePackageFileConflicts"
|
||||
Condition="'@(Reference -> WithMetadataValue('NugetPackageId', 'System.Runtime.Experimental'))' != ''">
|
||||
<ItemGroup>
|
||||
<Reference Remove="@(Reference)"
|
||||
Condition="$([System.String]::Copy(%(Reference.Identity)).Contains('System.Runtime.dll')) and
|
||||
'%(Reference.NuGetPackageId)' == 'Microsoft.NETCore.App.Ref'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="AfterClean"
|
||||
AfterTargets="Clean">
|
||||
<Delete Files="%(Proto3.OutDir)/%(Proto3.CSName).cs" />
|
||||
<Delete Files="%(Proto3.OutDir)/%(Proto3.FileName).client.pb.cs" />
|
||||
<Delete Files="%(Proto3.OutDir)/%(Proto3.FileName).server.pb.cs" />
|
||||
<Delete Files="%(Proto3.OutDir)/%(Proto3.FileName).immutable.pb.cs" />
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="Test"
|
||||
DependsOnTargets="Build"></Target>
|
||||
|
||||
</Project>
|
||||
@@ -1,32 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using CommandLine;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public static class CommandParserEx {
|
||||
public static TResult parse_and_run<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(
|
||||
this Parser parser,
|
||||
IEnumerable<string> args,
|
||||
Func<T1, TResult> on_args1,
|
||||
Func<T2, TResult> on_args2,
|
||||
Func<T3, TResult> on_args3,
|
||||
Func<T4, TResult> on_args4,
|
||||
Func<T5, TResult> on_args5,
|
||||
Func<T6, TResult> on_args6,
|
||||
Func<T7, TResult> on_args7,
|
||||
Func<T8, TResult> on_args8,
|
||||
Func<IEnumerable<Error>, TResult> on_error) =>
|
||||
parser.ParseArguments<T1, T2, T3, T4, T5, T6, T7, T8>(args)
|
||||
.MapResult(
|
||||
on_args1,
|
||||
on_args2,
|
||||
on_args3,
|
||||
on_args4,
|
||||
on_args5,
|
||||
on_args6,
|
||||
on_args7,
|
||||
on_args8,
|
||||
on_error);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using bts;
|
||||
|
||||
using mbts.config;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
class ConfigCommand {
|
||||
public static int run(ConfigCommandOptions opts) {
|
||||
if (opts.sparkd is not null) {
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
BTS.set_config_arg(args, opts.sparkd);
|
||||
}
|
||||
var keys = opts.keys?.ToArray() ?? Array.Empty<string>();
|
||||
var excluded_keys = opts.excluded_keys?.ToArray() ?? Array.Empty<string>();
|
||||
return Configuration.run_config_query(
|
||||
keys,
|
||||
keys.Length,
|
||||
excluded_keys,
|
||||
excluded_keys.Length,
|
||||
opts.all,
|
||||
opts.list_keys,
|
||||
opts.verbose);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using CommandLine;
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
[Verb("config", HelpText = "Get config values")]
|
||||
class ConfigCommandOptions {
|
||||
[Option(
|
||||
"query", MetaValue = "keys", HelpText = "Keys to query (supports regular expressions)")]
|
||||
public IEnumerable<string>? keys { get; set; }
|
||||
|
||||
[Option(
|
||||
"exclude",
|
||||
MetaValue = "keys",
|
||||
HelpText = "Keys to exclude from query (supports regular expressions)")]
|
||||
public IEnumerable<string>? excluded_keys { get; set; }
|
||||
|
||||
[Option("all", HelpText = "Query all keys")]
|
||||
public bool all { get; set; }
|
||||
|
||||
[Option("list-keys", HelpText = "List available keys")]
|
||||
public bool list_keys { get; set; }
|
||||
|
||||
[Option("verbose", HelpText = "Show verbose output")]
|
||||
public bool verbose { get; set; }
|
||||
|
||||
[Option("sparkd", MetaValue = "URL", HelpText = "The sparkd server")]
|
||||
public string? sparkd { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.md;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
static class MBOCommand {
|
||||
public static int run(MBOCommandOptions o) {
|
||||
_dump_mbo(o.key, o.side);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _dump_mbo(uint key, Side side) {
|
||||
using var ctx = new ServiceContext();
|
||||
var mbo = ctx.locate<MBOService>();
|
||||
var data = mbo.get_orders_for_security(
|
||||
new MBOSecurityQuery { SecurityId = key, PriceLevel = double.NaN });
|
||||
|
||||
Console.WriteLine($"Order data for {data.SecurityId}");
|
||||
var price = 0.0;
|
||||
var color = Console.ForegroundColor;
|
||||
if (side == Side.Sell || side == Side.Undefined) {
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
foreach (var e in data.Ask.Reverse()) {
|
||||
if (e.Price != price) {
|
||||
price = e.Price;
|
||||
Console.WriteLine("================");
|
||||
}
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
if (side == Side.Buy || side == Side.Undefined) {
|
||||
Console.ForegroundColor = ConsoleColor.Blue;
|
||||
foreach (var e in data.Bid) {
|
||||
if (e.Price != price) {
|
||||
price = e.Price;
|
||||
Console.WriteLine("================");
|
||||
}
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
Console.ForegroundColor = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using bts.api;
|
||||
|
||||
using CommandLine;
|
||||
|
||||
[Verb("mbo", HelpText = "MBO diagnostic command")]
|
||||
public class MBOCommandOptions {
|
||||
[Option('k', "key", Required = true)]
|
||||
public uint key { get; set; }
|
||||
|
||||
[Option('s', "side", Default = Side.Undefined)]
|
||||
public Side side { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using CommandLine;
|
||||
|
||||
[Verb("order-stress", HelpText = "Create a bunch of orders")]
|
||||
public class OrderStressOptions {
|
||||
// Maybe options to create fewer orders or sleep
|
||||
|
||||
[Option('s', "sleep", HelpText = "Sleep ms between order submission", Default = 100)]
|
||||
public int sleep { get; set; }
|
||||
|
||||
[Option('d', "depth", HelpText = "How many orders per side", Default = 10u)]
|
||||
public uint depth { get; set; }
|
||||
|
||||
[Option('q', "quanityt", HelpText = "Order quantity", Default = 1u)]
|
||||
public uint quantity { get; set; }
|
||||
};
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using bts.api.core;
|
||||
using bts.api.diagnostic;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reactive.Linq;
|
||||
using Spark.Core;
|
||||
using System.Threading;
|
||||
|
||||
static class ScriptCommand {
|
||||
public static int run(ScriptCommandOptions o) {
|
||||
if (o.server) {
|
||||
_run_server();
|
||||
return 0;
|
||||
}
|
||||
if (o.host is null) {
|
||||
Console.WriteLine("Please specify host");
|
||||
return 1;
|
||||
}
|
||||
if (!o.file.is_null_or_empty() && File.Exists(o.file)) {
|
||||
_run_client(o.host, File.OpenRead(o.file));
|
||||
return 0;
|
||||
}
|
||||
_run_client(o.host, Console.OpenStandardInput());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _run_client(string qualifier, Stream stream) {
|
||||
using var ctx = new ServiceContext();
|
||||
ShellDiagnosticService diag = ctx.locate<ShellDiagnosticService>(qualifier);
|
||||
var script_id = diag.create(null);
|
||||
ShellScriptHost script = ctx.locate<ShellScriptHost>(script_id.Id);
|
||||
try {
|
||||
using var sub = script.subscribei_output().Subscribe(_script_output);
|
||||
using var reader = new StreamReader(stream);
|
||||
var status = ScriptResult.Complete;
|
||||
|
||||
for (var line = string.Empty; line != "#q"; line = prompt_input()) {
|
||||
var result = script.run(new ScriptLines { Lines = { line } });
|
||||
status = result.Res;
|
||||
switch (status) {
|
||||
case ScriptResult.Invalid:
|
||||
Console.WriteLine($"Syntax error: '{result.Message}'");
|
||||
break;
|
||||
case ScriptResult.RunError:
|
||||
Console.WriteLine($"Exception: {result.Message}");
|
||||
break;
|
||||
case ScriptResult.Complete:
|
||||
if (!result.Message.is_null_or_empty()) {
|
||||
Console.WriteLine(result.Message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string prompt_input() {
|
||||
_write_prompt(status);
|
||||
return reader.ReadLine() ??
|
||||
throw new InvalidOperationException("Received null input");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine($"Done. {e.Message}");
|
||||
} finally {
|
||||
try {
|
||||
script.dispose(null);
|
||||
script.Dispose();
|
||||
} catch (Exception) { }
|
||||
}
|
||||
}
|
||||
|
||||
static void _script_output(Update<ScriptLinesImmutable, int> update) {
|
||||
foreach (var lines in update.added) {
|
||||
foreach (var line in lines.Lines) {
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _write_prompt(ScriptResult status) {
|
||||
switch (status) {
|
||||
case ScriptResult.Complete:
|
||||
Console.Write(">>> ");
|
||||
break;
|
||||
case ScriptResult.Incomplete:
|
||||
Console.Write("... ");
|
||||
break;
|
||||
case ScriptResult.Invalid:
|
||||
Console.Write("?>> ");
|
||||
break;
|
||||
case ScriptResult.RunError:
|
||||
Console.Write("!>> ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _run_server() {
|
||||
// for debugging
|
||||
using var ctx = new ServiceContext();
|
||||
var stype = SparkReflectionHelper.find_type(
|
||||
"bts.api.diagnostic.ShellDiagnosticsServiceServer");
|
||||
var server = Activator.CreateInstance(stype, ctx);
|
||||
while (true) {
|
||||
Thread.Sleep(1000);
|
||||
GC.KeepAlive(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using CommandLine;
|
||||
|
||||
[Verb("script", HelpText = "Remote control for shell diagnostic host")]
|
||||
public class ScriptCommandOptions {
|
||||
[Option('s', "server", HelpText = "Run a script server for testing.")]
|
||||
public bool server { get; set; }
|
||||
|
||||
[Option('h', "host", HelpText = "Connect to {host}")]
|
||||
public string? host { get; set; }
|
||||
|
||||
[Option('f', "fromfile", HelpText = "Read commands from file instead of stdin")]
|
||||
public string? file { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using bts;
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
class SettingsCommand {
|
||||
public static int run(SettingsCommandOptions _) {
|
||||
var type = SparkReflectionHelper.find_type(
|
||||
"bts.api.diagnostic.ShellDiagnosticsServiceServer");
|
||||
var run = type.GetMethod("run", BindingFlags.Static | BindingFlags.Public);
|
||||
Log.notnull(run).Invoke(null, Array.Empty<object>());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using CommandLine;
|
||||
|
||||
[Verb("settings", HelpText = "Copy workspaces around")]
|
||||
class SettingsCommandOptions { }
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using bts.api.core;
|
||||
using bts.test;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
class TailService {
|
||||
static PrintFormat _format = PrintFormat.ToString;
|
||||
static bool _timestamp = false;
|
||||
|
||||
enum PrintFormat {
|
||||
ToString,
|
||||
Json
|
||||
}
|
||||
|
||||
public static int run(TailServiceOptions o) {
|
||||
var split = o.method?.Split('.');
|
||||
if (split?.Length != 2) {
|
||||
Console.WriteLine("Try --method [ServiceName].[Method]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (o.json) {
|
||||
_format = PrintFormat.Json;
|
||||
}
|
||||
if (o.time) {
|
||||
_timestamp = true;
|
||||
}
|
||||
|
||||
_tail(split[0], split[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
class SubscriptionLogger<T> : IObserver<T> {
|
||||
public void OnCompleted() =>
|
||||
ConsoleEx.WriteLine("Completed.", ConsoleColor.Yellow);
|
||||
|
||||
public void OnError(Exception error) =>
|
||||
ConsoleEx.WriteLine($"Error. '{error}'", ConsoleColor.Red);
|
||||
|
||||
public void OnNext(T value) {
|
||||
if (_timestamp) {
|
||||
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff"));
|
||||
Console.Write(" ");
|
||||
}
|
||||
if (value is null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_format) {
|
||||
case PrintFormat.Json:
|
||||
dynamic dv = value;
|
||||
var json = JsonConvert.SerializeObject(new { dv.added, dv.removed, dv.changed });
|
||||
Console.WriteLine(json);
|
||||
break;
|
||||
case PrintFormat.ToString:
|
||||
Console.WriteLine(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _tail(string service_name, string method_name) {
|
||||
var service_type = SparkReflectionHelper.find_service_type(service_name);
|
||||
var subscribe_method = service_type.GetMethod("subscribei_" + method_name) ??
|
||||
throw new InvalidOperationException();
|
||||
using var ctx = new ServiceContext();
|
||||
var locate = typeof(ServiceContext).GetMethod("locate")?.MakeGenericMethod(service_type) ??
|
||||
throw new InvalidOperationException();
|
||||
var service = locate.Invoke(ctx, new[] { string.Empty });
|
||||
// Subscribe with null qualifier
|
||||
object observable = subscribe_method.Invoke(service, new object?[] { null })
|
||||
?? throw new InvalidOperationException();
|
||||
Type update_type = observable.GetType().GetGenericArguments()
|
||||
.First(arg => arg.Name.StartsWith("Update"));
|
||||
Type observer_type = typeof(SubscriptionLogger<>).MakeGenericType(update_type);
|
||||
var observer = Activator.CreateInstance(observer_type)
|
||||
?? throw new InvalidOperationException(
|
||||
$"Activator failed to instantiate observer of type '{observer_type}'");
|
||||
|
||||
var subscribe = observable.GetType().GetMethod("Subscribe", new[] { observer_type }) ??
|
||||
throw new InvalidOperationException(
|
||||
"Failed to find 'IObservable.Subscribe' method via reflection");
|
||||
var dispose = subscribe.Invoke(observable, new[] { observer });
|
||||
|
||||
while (true) {
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using CommandLine;
|
||||
|
||||
[Verb("tail", HelpText = "'console dsqa'")]
|
||||
class TailServiceOptions {
|
||||
[Option('m', "method", Required = true, HelpText = "'[ServiceName].[Method]' to call")]
|
||||
public string? method { get; set; }
|
||||
|
||||
[Option('t', "time", HelpText = "Include timestamp")]
|
||||
public bool time { get; set; }
|
||||
|
||||
[Option('p', "path", HelpText = "Path inside the row to use for display. Ex: 'Id' or 'Name' ")]
|
||||
public string? path { get; set; }
|
||||
|
||||
[Option('a', "all", HelpText = "Include 'all' rows on every print")]
|
||||
public bool all { get; set; }
|
||||
|
||||
[Option('j', "json", HelpText = "Format rows as json")]
|
||||
public bool json { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using bts.test;
|
||||
|
||||
using mbts.config;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
static class TestCommand {
|
||||
static readonly Lazy<TestEnumerator> _enumerator_backing = new(
|
||||
() => new TestEnumerator("spark_test"));
|
||||
|
||||
static TestEnumerator _enumerator => _enumerator_backing.Value;
|
||||
|
||||
public static int run(TestCommandOptions o) {
|
||||
if (o.type == TestType.Unit) {
|
||||
// Skip loading any local config
|
||||
Configuration.initialize_for_test();
|
||||
} else Configuration.initialize();
|
||||
|
||||
ConsoleEx.WriteLine(JsonConvert.SerializeObject(o), ConsoleColor.DarkGray);
|
||||
|
||||
if (o.list) {
|
||||
_list_tests(o.type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (o.tests is null) {
|
||||
Console.WriteLine("Please specify test(s) to run");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (o.timeout == 0 && o.tests.TryGetSingle(out var test) && !o.self_contained) {
|
||||
using var trr = new TestResultReceiver(true);
|
||||
InProcessTestRunner.run_test_synchronously(
|
||||
test, o.type, _enumerator, trr.get_reporter());
|
||||
trr.print_summary();
|
||||
if (trr.ResultCount == 0) {
|
||||
ConsoleEx.Write("No ", ConsoleColor.Red);
|
||||
ConsoleEx.Write(o.type.ToString(), ConsoleColor.Yellow);
|
||||
ConsoleEx.WriteLine($" tests matched filter '{test}'", ConsoleColor.Red);
|
||||
return -1;
|
||||
}
|
||||
return trr.ErrorCount;
|
||||
}
|
||||
|
||||
if (o.tests.Any()) {
|
||||
return _run_tests(o, _enumerator.match(o.type, o.tests));
|
||||
}
|
||||
return _run_tests(o, _enumerator.get_of_type(o.type));
|
||||
}
|
||||
|
||||
static int _run_tests(TestCommandOptions o, IEnumerable<TestMethod> tests) {
|
||||
var runner = new TestRunner();
|
||||
// waits for all child processes to exit or time out
|
||||
return runner.run_tests(tests, o.timeout, o.self_contained);
|
||||
}
|
||||
|
||||
static void _list_tests(TestType test_type) {
|
||||
Console.WriteLine("All tests: ");
|
||||
foreach (var t in _enumerator.get_groups_of_type(test_type)) {
|
||||
ConsoleEx.WriteLine(t.name, ConsoleColor.Green);
|
||||
foreach (var test in t.methods) {
|
||||
Console.Write($" {test.name}");
|
||||
if (test.ignored) {
|
||||
var reason = test.ignored_reason;
|
||||
if (string.IsNullOrEmpty(reason)) {
|
||||
ConsoleEx.Write(" (ignored)", ConsoleColor.Yellow);
|
||||
continue;
|
||||
}
|
||||
ConsoleEx.Write($" (ignored: '{test.ignored_reason}')", ConsoleColor.Yellow);
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using bts.test;
|
||||
|
||||
using CommandLine;
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
[Verb("test", HelpText = "Run Tests")]
|
||||
class TestCommandOptions {
|
||||
[Option('t', "timeout", HelpText = "Timeout unit tets", Default = 0u)]
|
||||
public uint timeout { get; set; }
|
||||
|
||||
[Option('l', "list", HelpText = "List tests")]
|
||||
public bool list { get; set; }
|
||||
|
||||
[Option('i', "insular", HelpText = "Tests run sparkd and use their own config (if specified)")]
|
||||
public bool self_contained { get; set; }
|
||||
|
||||
[Option("type", HelpText = "Run all tests of type [Unit|System|Shell]")]
|
||||
public TestType type { get; set; }
|
||||
|
||||
[Value(1, MetaName = "Tests", HelpText = "Test list")]
|
||||
public IEnumerable<string>? tests { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using bts.api;
|
||||
using bts.api.admin;
|
||||
using bts.api.core;
|
||||
using bts.api.synthetic;
|
||||
using bts.api.user;
|
||||
using bts.metrics;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
class TestService {
|
||||
public static int run(TestServiceOptions o) {
|
||||
if (o.synthetic) {
|
||||
_synthetic_test();
|
||||
}
|
||||
if (o.user) {
|
||||
_user_test();
|
||||
}
|
||||
if (o.admin) {
|
||||
_admin_test();
|
||||
}
|
||||
if (o.latency) {
|
||||
_latency_test();
|
||||
}
|
||||
if (o.market_data) {
|
||||
_market_data();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _market_data() {
|
||||
using var ctx = new ServiceContext();
|
||||
var md = ctx.locate<MarketDataService>();
|
||||
md.subscribe_securities().Table().Subscribe(update => {
|
||||
Console.WriteLine($"got {update.state.Count} securities");
|
||||
});
|
||||
while (true) {
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
static void _synthetic_test() {
|
||||
using var ctx = new ServiceContext();
|
||||
var security = new Security {
|
||||
Symbol = "ES",
|
||||
Exchange = Venue.Bts,
|
||||
Currency = Currency.Usd,
|
||||
Synthetic = new SyntheticContract {
|
||||
Name = "Synthetic1",
|
||||
PriceCalc = SyntheticContract.Types.PriceCalc.Standard,
|
||||
Legs = {
|
||||
new ContractLeg {
|
||||
Security = new Security {
|
||||
Id = 2,
|
||||
Symbol = "ES",
|
||||
// ESH8
|
||||
Future = new FutureContract {
|
||||
ExpirationYear = 2018,
|
||||
ExpirationMonth = 3,
|
||||
ExpirationDay = 16
|
||||
}
|
||||
}
|
||||
},
|
||||
new ContractLeg {
|
||||
Security = new Security {
|
||||
Id = 5,
|
||||
Symbol = "ES",
|
||||
// ESU8
|
||||
Future = new FutureContract {
|
||||
ExpirationYear = 2018,
|
||||
ExpirationMonth = 9,
|
||||
ExpirationDay = 21
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var service = ctx.locate<SyntheticSecurityService>();
|
||||
try {
|
||||
var result = service.create_synthetic(security);
|
||||
if (result.Error == CreateResult.Types.Error.Ok) {
|
||||
Console.WriteLine(result.Key);
|
||||
}
|
||||
} catch (ApiException e) {
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
static void _user_test() {
|
||||
var ctx = new ServiceContext();
|
||||
|
||||
var s = ctx.locate<AuthService>();
|
||||
var resp = s.get_users(new GetUsersRequest());
|
||||
Console.WriteLine("{0}", resp);
|
||||
}
|
||||
|
||||
static void _admin_test() {
|
||||
var ctx = new ServiceContext();
|
||||
|
||||
var s = ctx.locate<AdminService>();
|
||||
using var d = s.subscribei_admin_data().Subscribe(Console.WriteLine);
|
||||
|
||||
while (true) {
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
static void _latency_test() {
|
||||
var ctx = new ServiceContext();
|
||||
var s = ctx.locate<ShellLatencyService>();
|
||||
|
||||
ulong sequence = 0;
|
||||
var factor = TimeSpan.TicksPerMillisecond / 1000;
|
||||
while (true) {
|
||||
var sw = new System.Diagnostics.Stopwatch();
|
||||
sw.Start();
|
||||
var reply = s.ping(new BufferMessage() { Sequence = ++sequence });
|
||||
sw.Stop();
|
||||
Console.WriteLine(
|
||||
$"{reply.Sequence} - {sw.ElapsedMilliseconds}ms -- {sw.ElapsedTicks / factor} us");
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using CommandLine;
|
||||
|
||||
[Verb("test-service", HelpText = "Scratch space for testing services")]
|
||||
public class TestServiceOptions {
|
||||
[Option(nameof(synthetic))]
|
||||
public bool synthetic { get; set; }
|
||||
|
||||
[Option(nameof(user))]
|
||||
public bool user { get; set; }
|
||||
|
||||
[Option(nameof(admin))]
|
||||
public bool admin { get; set; }
|
||||
|
||||
[Option(nameof(latency))]
|
||||
public bool latency { get; set; }
|
||||
|
||||
[Option("md")]
|
||||
public bool market_data { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using bts;
|
||||
using bts.utils;
|
||||
|
||||
using System;
|
||||
|
||||
using CommandLine;
|
||||
|
||||
class Program {
|
||||
static int Main(string[] args) {
|
||||
// Initializing config is up to each individual command
|
||||
// We call provide_command_line here in case config is first
|
||||
// initialized from c++ instead of c#
|
||||
BTSNativeMethods.provide_command_line(args, args.Length);
|
||||
|
||||
var parser = new Parser(ps => {
|
||||
ps.CaseInsensitiveEnumValues = true;
|
||||
ps.HelpWriter = Console.Out;
|
||||
});
|
||||
|
||||
// Note: this can be made to infer the options type list from the list of delegates
|
||||
return parser.parse_and_run<
|
||||
ConfigCommandOptions,
|
||||
ScriptCommandOptions,
|
||||
TailServiceOptions,
|
||||
TestServiceOptions,
|
||||
SettingsCommandOptions,
|
||||
TestCommandOptions,
|
||||
OrderStressOptions,
|
||||
MBOCommandOptions,
|
||||
int
|
||||
>(
|
||||
BTS.remove_config_arg(args),
|
||||
ConfigCommand.run,
|
||||
ScriptCommand.run,
|
||||
TailService.run,
|
||||
TestService.run,
|
||||
SettingsCommand.run,
|
||||
TestCommand.run,
|
||||
OrderStressCommand.run,
|
||||
MBOCommand.run,
|
||||
errors => 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
namespace Spark.Console {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
static class SparkReflectionHelper {
|
||||
static readonly Assembly _spark_api_assembly;
|
||||
static readonly Assembly _spark_assembly;
|
||||
|
||||
static SparkReflectionHelper() {
|
||||
_spark_assembly = load_assembly("Spark");
|
||||
_spark_api_assembly = load_assembly("spark_api");
|
||||
|
||||
static Assembly load_assembly(string ass_name) =>
|
||||
Assembly.Load(ass_name) ?? throw new InvalidOperationException(
|
||||
$"Failed to load assembly '{ass_name}'");
|
||||
}
|
||||
|
||||
static IEnumerable<Assembly> _assemblies {
|
||||
get {
|
||||
yield return _spark_assembly;
|
||||
yield return _spark_api_assembly;
|
||||
}
|
||||
}
|
||||
|
||||
public static Type find_type(string name) =>
|
||||
_assemblies.Select(a => a.GetType(name)).ExceptNull().FirstOrDefault() ??
|
||||
throw new InvalidOperationException($"Failed to find type '{name}'");
|
||||
|
||||
public static Type find_service_type(string name) =>
|
||||
_spark_api_assembly.GetExportedTypes()
|
||||
.FirstOrDefault(t => t.Name == name) ??
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to find service '{name}' in spark_api.dll");
|
||||
}
|
||||
}
|
||||
@@ -63,9 +63,9 @@ namespace mbts.spark {
|
||||
vs_input, vs_nonvol_data.contracts, aux_data_map);
|
||||
_timer.stop();
|
||||
var tag_str = tag == null ? "Null" : tag.ToString();
|
||||
bts.Log.Gen.info($"SingleVolSurfaceCommittedOutputRetriever._recompute_and_publish " +
|
||||
$"vs_id: {_vs_id} time:{_timer.lastIntervalTime} ms tag:{tag_str} " +
|
||||
$"tid:{_tid()}"); //output: {vs_output.log_string()}*/");
|
||||
// bts.Log.Gen.info($"SingleVolSurfaceCommittedOutputRetriever._recompute_and_publish " +
|
||||
// $"vs_id: {_vs_id} time:{_timer.lastIntervalTime} ms tag:{tag_str} " +
|
||||
// $"tid:{_tid()}"); //output: {vs_output.log_string()}*/");
|
||||
_logger.log(_log_id, _log_id, _timer.lastIntervalTime);
|
||||
_bb.post(_out_bb_computed_vol_tag, vs_output);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace mbts.spark {
|
||||
var exp_price_map = OptionDriverPriceMap.get_vs_price_map(
|
||||
all_sym_price_map, (s) => _option_symbols.Contains(s));
|
||||
if (sc_data != null && vs_nonvol_input != null && exp_price_map != null) {
|
||||
bts.Log.Gen.info($"SingleVolSurfaceNonVolDataSource vs_id:{_vs_id} " +
|
||||
bts.Log.Gen.diag($"SingleVolSurfaceNonVolDataSource vs_id:{_vs_id} " +
|
||||
"publishing VolSurfaceNonVolData");
|
||||
_bb.post(_out_bb_vs_nonvol_data_tag,
|
||||
new VolSurfaceNonVolData(
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace mbts.analytics {
|
||||
output.Add(symbol, symbol_data);
|
||||
}
|
||||
IReadOnlyDictionary<string, TData> ro_output = output;
|
||||
bts.Log.Gen.info($"SymbolAggregationPublisher._on_timer publishing " +
|
||||
bts.Log.Gen.diag($"SymbolAggregationPublisher._on_timer publishing " +
|
||||
$"tag: {_out_tag} sz: {ro_output.Count}");
|
||||
_bb.post(_out_tag, ro_output);
|
||||
}
|
||||
|
||||
@@ -123,10 +123,10 @@ namespace mbts.spark {
|
||||
}
|
||||
Log.Ui.info($"VolSurfaceUsageList name_to_ids count: {name_to_ids.Count}");
|
||||
var vs_usage = mbts.config.Configuration.get<bts.config.toggles.VolSurfaceUsage>();
|
||||
Log.Ui.info($"VolSurfaceUsageList vs_usage acount: {vs_usage.Items.Count}");
|
||||
foreach (var u in vs_usage.Items) {
|
||||
string option_name = u.OptionName;
|
||||
string vs_name = u.VsName;
|
||||
Log.Ui.info($"VolSurfaceUsageList vs_usage acount: {vs_usage.vol_surface_usage.Count}");
|
||||
foreach (var u in vs_usage.vol_surface_usage) {
|
||||
string option_name = u.option_name;
|
||||
string vs_name = u.vs_name;
|
||||
int vs_key;
|
||||
if (name_to_ids.TryGetValue(vs_name, out vs_key)) {
|
||||
ulist.UsageList.Add(new VolSurfaceUsageEntry() {
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace mbts.spark {
|
||||
|
||||
using Spark.Analytics.View;
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
@@ -28,9 +28,10 @@ namespace mbts.spark {
|
||||
_candidate_value.Changed += on_candidate_changed;
|
||||
_is_dirty.Changed += (o, n) => {
|
||||
Log.assert(o != n);
|
||||
Log.Ui.info(
|
||||
$"VolSurface '{expiration}.{column}' is_dirty changed to {n}. " +
|
||||
$"commited: {committed_value}; candidate: {candidate_value}");
|
||||
// TODO: John, this was logged too many times
|
||||
// Log.Ui.info(
|
||||
// $"VolSurface '{expiration}.{column}' is_dirty changed to {n}. " +
|
||||
// $"commited: {committed_value}; candidate: {candidate_value}");
|
||||
};
|
||||
|
||||
void on_committed_changed(object? oldval, object? newval) =>
|
||||
|
||||
@@ -251,7 +251,7 @@ namespace mbts.spark.VolSurface {
|
||||
var new_hash = cols_new.aggregate_hash();
|
||||
var old_hash = _columns_model.models.aggregate_hash();
|
||||
if (new_hash != old_hash) {
|
||||
_columns_model.models.sync_with(cols_new, allow_duplicates: false);
|
||||
CollectionHelper.sync_with_never_duplicate(_columns_model.models, cols_new);
|
||||
RaisePropertyChanged("super_headers");
|
||||
}
|
||||
// Add/remove rows too
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace mbts.spark {
|
||||
using mbts.ui.utility;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -6,7 +6,6 @@ using bts.collections;
|
||||
using bts.utils;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -27,7 +26,6 @@ namespace mbts.spark {
|
||||
: this(bus, service, user.Value) { }
|
||||
|
||||
AdminModel(IBus bus, AdminService service, UserTuple user) {
|
||||
var bootstraps = new DisposableBuilder();
|
||||
this.user = user;
|
||||
// accounts
|
||||
on_accounts = service.subscribei_admin_data()
|
||||
@@ -43,30 +41,24 @@ namespace mbts.spark {
|
||||
.RefCount();
|
||||
// users
|
||||
on_users = Observable.Return(DictionaryHelper.wrap_value(user.id, user.name));
|
||||
if (user.permissions == UserTuple.Permissions.Admin) {
|
||||
all_user_tuples = bootstraps.emplace(
|
||||
service.subscribei_admin_data()
|
||||
.Select(u => u.all.FirstOrNull())
|
||||
.Where(ad => ad.HasValue)
|
||||
.Select(ad => ad.Value.Users
|
||||
.Where(u => user.can_access_user_data(u.Id))
|
||||
.Select(u => new UserTuple(u.Id, u.Name))
|
||||
.ToDictionary(UserTuple.key_fn))
|
||||
.Table(UserTuple.key_fn)
|
||||
.Cache()
|
||||
.ToObservable());
|
||||
} else {
|
||||
all_user_tuples = Observable.Return(
|
||||
TableUpdate<UserTuple, uint>.mk_empty(UserTuple.key_fn).with_added(new[] { user }));
|
||||
}
|
||||
all_user_tuples = user.permissions == UserTuple.Permissions.Admin ?
|
||||
service.subscribei_admin_data()
|
||||
.Select(u => u.all.FirstOrNull())
|
||||
.Where(ad => ad.HasValue)
|
||||
.Select(ad => ad.Value.Users
|
||||
.Where(u => user.can_access_user_data(u.Id))
|
||||
.Select(u => new UserTuple(u.Id, u.Name))
|
||||
.ToDictionary(UserTuple.key_fn))
|
||||
.Table(UserTuple.key_fn)
|
||||
.Cache() :
|
||||
Observable.Return(TableUpdate<UserTuple, uint>.mk_empty(UserTuple.key_fn).with_added(new[] { user }));
|
||||
_dispose = Disposable.Create(
|
||||
all_user_tuples.Subscribe(t => bus.post(SparkTags.Users, t.state)),
|
||||
on_accounts.Subscribe(map => {
|
||||
_account_map = map;
|
||||
var account_tuple_map = MappedDictionary.create(map, (id, name) => new AccountTuple(id, name));
|
||||
bus.post(SparkTags.AccountsTag, account_tuple_map);
|
||||
}),
|
||||
bootstraps);
|
||||
}));
|
||||
bus.post(SparkTags.CurrentUser, user);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.collections;
|
||||
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Core.Data;
|
||||
using Spark.Core.Disposables;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
@@ -21,18 +16,11 @@ using System.Text.RegularExpressions;
|
||||
using AlgoType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
|
||||
|
||||
namespace mbts.spark {
|
||||
class AlgoProviderFactory : IDisposable {
|
||||
IDisposable _dispose;
|
||||
|
||||
class AlgoProviderFactory {
|
||||
[ImportingConstructor]
|
||||
public AlgoProviderFactory(
|
||||
IAlgoStudioModel studio_model,
|
||||
OrderFactory order_factory,
|
||||
ISecurityMetadataStore sec_meta_store
|
||||
) {
|
||||
public AlgoProviderFactory(IAlgoStudioModel studio_model, DynamicOrderHelper dynamic_helper, ISecurityAuxiliaryDataModel security_auxiliary_model) {
|
||||
var from_conf = config.Configuration.get<bts.config.AlgoTypeRuleSet>();
|
||||
var disposables = new List<IDisposable>();
|
||||
if (from_conf.Rules.Count == 0) {
|
||||
if (from_conf.rules.Count == 0) {
|
||||
// There seems to be a config race bug, where the returned config values are empty, despite having entries in spark.conf.
|
||||
// [John]I've only noticed it happen when attached to the debugger,
|
||||
// but best to have a contingency plan in place anyways, to prevent any chance of regression.
|
||||
@@ -50,7 +38,7 @@ namespace mbts.spark {
|
||||
AlgoType.Tick,
|
||||
AlgoType.Trigger,
|
||||
AlgoType.QueueHolder)),
|
||||
ImmutableHashSet.CreateRange(order_factory.dynamic_types()));
|
||||
ImmutableHashSet.CreateRange(dynamic_helper.dynamic_types));
|
||||
var rules = new[] {
|
||||
// Glbx: Futures/Spreads
|
||||
new SecurityAlgoTypeSetImmutable(
|
||||
@@ -112,35 +100,18 @@ namespace mbts.spark {
|
||||
"\n\n\tAttachable\n" +
|
||||
attachable_rules.fmt_diagnostic("\n\t\t"));
|
||||
|
||||
var security_algo_provider = new AlgoProvider(
|
||||
rules, studio_model, order_factory, sec_meta_store);
|
||||
var attachable_algo_provider = new AttachableAlgoProvider(
|
||||
attachable_rules, studio_model, order_factory, sec_meta_store);
|
||||
this.security_algo_provider = security_algo_provider;
|
||||
this.attachable_algo_provider = attachable_algo_provider;
|
||||
disposables.AddRange(security_algo_provider, attachable_algo_provider);
|
||||
security_algo_provider = new AlgoProvider(rules, studio_model, dynamic_helper, security_auxiliary_model);
|
||||
attachable_algo_provider = new AttachableAlgoProvider(attachable_rules, studio_model, dynamic_helper, security_auxiliary_model);
|
||||
} else {
|
||||
var algo_type_data =
|
||||
EnumHelper.get_values_except(AlgoType.Dynamic, AlgoType.Composite, AlgoType.None)
|
||||
.Select(t => _get_type_data(t))
|
||||
.Concat(order_factory.dynamic_types().Select(dt => _get_type_data(AlgoType.Dynamic, dt)))
|
||||
.Concat(dynamic_helper.dynamic_types.Select(dt => _get_type_data(AlgoType.Dynamic, dt)))
|
||||
.ToListLazy();
|
||||
var conf_sets = from_conf.Rules.Select(r => _to_security_algo_set(r, order_factory, algo_type_data)).ToList();
|
||||
var security_algo_provider = new AlgoProvider(
|
||||
conf_sets.Select(tup => tup.standard).ToList(),
|
||||
studio_model,
|
||||
order_factory,
|
||||
sec_meta_store);
|
||||
var attachable_algo_provider = new AttachableAlgoProvider(
|
||||
conf_sets.Select(tup => tup.attachable).ToList(),
|
||||
studio_model,
|
||||
order_factory,
|
||||
sec_meta_store);
|
||||
this.security_algo_provider = security_algo_provider;
|
||||
this.attachable_algo_provider = attachable_algo_provider;
|
||||
disposables.AddRange(security_algo_provider, attachable_algo_provider);
|
||||
var conf_sets = from_conf.rules.Select(r => _to_security_algo_set(r, dynamic_helper, algo_type_data)).ToList();
|
||||
security_algo_provider = new AlgoProvider(conf_sets.Select(tup => tup.standard).ToList(), studio_model, dynamic_helper, security_auxiliary_model);
|
||||
attachable_algo_provider = new AttachableAlgoProvider(conf_sets.Select(tup => tup.attachable).ToList(), studio_model, dynamic_helper, security_auxiliary_model);
|
||||
}
|
||||
_dispose = Disposable.Create(disposables);
|
||||
}
|
||||
|
||||
[Export]
|
||||
@@ -148,9 +119,6 @@ namespace mbts.spark {
|
||||
[Export]
|
||||
public IAttachableAlgoProvider attachable_algo_provider { get; }
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
|
||||
static (string name, AlgoType factory, string dynamic) _get_type_data(AlgoType type, string dynamic_type = null) {
|
||||
string type_name = type.ToString();
|
||||
return (type_name, type, dynamic_type ?? type_name);
|
||||
@@ -160,42 +128,39 @@ namespace mbts.spark {
|
||||
context == null ?
|
||||
new SecurityCriteriaClause() :
|
||||
new SecurityCriteriaClause(
|
||||
exchanges: context.Exchanges?.Select(EnumHelper.parse<Venue>)?.ToArray(),
|
||||
security_types: context.SecurityTypes?.Select(EnumHelper.parse<Security.ContractOneofCase>)?.Select(t => t.security_type())?.ToArray(),
|
||||
symbols: context.Symbols?.ToArray());
|
||||
exchanges: context.exchanges?.Select(EnumHelper.parse<Venue>)?.ToArray(),
|
||||
security_types: context.security_types?.Select(EnumHelper.parse<Security.ContractOneofCase>)?.Select(t => t.security_type())?.ToArray(),
|
||||
symbols: context.symbols?.ToArray());
|
||||
|
||||
static (SecurityAlgoTypeSetImmutable standard, SecurityAlgoTypeSetImmutable attachable) _to_security_algo_set(
|
||||
bts.config.AlgoTypeRule rule,
|
||||
OrderFactory order_factory,
|
||||
DynamicOrderHelper dynamic_helper,
|
||||
IEnumerable<(string name, AlgoType factory, string dynamic)> algo_type_data) {
|
||||
//
|
||||
var factory_types = ImmutableHashSet<AlgoType>.Empty.ToBuilder();
|
||||
var dynamic_types = ImmutableHashSet<string>.Empty.ToBuilder();
|
||||
var attachable_factory_types = ImmutableHashSet<AlgoType>.Empty.ToBuilder();
|
||||
var attachable_dynamic_types = ImmutableHashSet<string>.Empty.ToBuilder();
|
||||
foreach (var a in rule.Algos) {
|
||||
if (Enum.TryParse(a.Name, out AlgoType sys_algo) && sys_algo != AlgoType.Composite && sys_algo != AlgoType.Dynamic) {
|
||||
on_sys_match(sys_algo, a.IsAttachable);
|
||||
continue;
|
||||
}
|
||||
var dyn_desc = order_factory.dynamic_descriptor(a.Name);
|
||||
if (dyn_desc is not null) {
|
||||
on_dyn_match(dyn_desc.DynamicOrderType, a.IsAttachable);
|
||||
continue;
|
||||
}
|
||||
foreach (var (name, factory, dynamic) in algo_type_data) {
|
||||
if (Regex.Match(name, a.Name).Success) {
|
||||
switch (factory) {
|
||||
case AlgoType.None:
|
||||
case AlgoType.Composite:
|
||||
// unexpected, but no need to crash
|
||||
continue;
|
||||
case AlgoType.Dynamic:
|
||||
on_dyn_match(dynamic, a.IsAttachable);
|
||||
break;
|
||||
default:
|
||||
on_sys_match(factory, a.IsAttachable);
|
||||
break;
|
||||
foreach (var a in rule.algos) {
|
||||
if (Enum.TryParse(a.name, out AlgoType sys_algo) && sys_algo != AlgoType.Composite && sys_algo != AlgoType.Dynamic) {
|
||||
on_sys_match(sys_algo, a.is_attachable);
|
||||
} else if (dynamic_helper.try_parse(a.name, out var dyn_type)) {
|
||||
on_dyn_match(dyn_type, a.is_attachable);
|
||||
} else {
|
||||
foreach (var (name, factory, dynamic) in algo_type_data) {
|
||||
if (Regex.Match(name, a.name).Success) {
|
||||
switch (factory) {
|
||||
case AlgoType.None:
|
||||
case AlgoType.Composite:
|
||||
// unexpected, but no need to crash
|
||||
continue;
|
||||
case AlgoType.Dynamic:
|
||||
on_dyn_match(dynamic, a.is_attachable);
|
||||
break;
|
||||
default:
|
||||
on_sys_match(factory, a.is_attachable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,13 +168,13 @@ namespace mbts.spark {
|
||||
return (
|
||||
// standard
|
||||
new SecurityAlgoTypeSetImmutable(
|
||||
_to_clause(rule.Context),
|
||||
_to_clause(rule.context),
|
||||
new AlgoTypeSetImmutable(
|
||||
factory_types.ToImmutable(),
|
||||
dynamic_types.ToImmutable())),
|
||||
// attachable
|
||||
new SecurityAlgoTypeSetImmutable(
|
||||
_to_clause(rule.Context),
|
||||
_to_clause(rule.context),
|
||||
new AlgoTypeSetImmutable(
|
||||
attachable_factory_types.ToImmutable(),
|
||||
attachable_dynamic_types.ToImmutable())));
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace mbts.spark {
|
||||
|
||||
public static IEnumerable<AlgoData> define_algos(
|
||||
this AlgoTypeSetImmutable algo_set,
|
||||
Func<string, CustomOrderDefinitionImmutable> dynamic_order_factory,
|
||||
DynamicOrderHelper dynamic_helper,
|
||||
Func<CustomOrderDefinitionImmutable, CustomOrderDefinitionImmutable> fit_to_type = null,
|
||||
double display_qty_divisor = 1) =>
|
||||
//
|
||||
@@ -101,13 +101,13 @@ namespace mbts.spark {
|
||||
}
|
||||
return def;
|
||||
})
|
||||
.Concat(algo_set.dynamic_types.Select(dynamic_order_factory))
|
||||
.Concat(algo_set.dynamic_types.Select(dynamic_helper.create_custom_defi))
|
||||
.Select(AlgoData.from_custom_def);
|
||||
|
||||
public static ImmutableDictionary<AlgoKey, AlgoData> match_algos(
|
||||
this IEnumerable<SecurityAlgoTypeSetImmutable> security_algo_sets,
|
||||
SecurityCriteriaClause clause,
|
||||
Func<string, CustomOrderDefinitionImmutable> dynamic_order_factory,
|
||||
DynamicOrderHelper dynamic_helper,
|
||||
Func<CustomOrderDefinitionImmutable, CustomOrderDefinitionImmutable> fit_to_type = null,
|
||||
double display_qty_divisor = 1) {
|
||||
//
|
||||
@@ -117,8 +117,7 @@ namespace mbts.spark {
|
||||
continue;
|
||||
} else if (rv == null) {
|
||||
rv = ImmutableDictionary.CreateRange(
|
||||
set.algo_type_set.define_algos(
|
||||
dynamic_order_factory, fit_to_type, display_qty_divisor)
|
||||
set.algo_type_set.define_algos(dynamic_helper, fit_to_type, display_qty_divisor)
|
||||
.Select(a => new KeyValuePair<AlgoKey, AlgoData>(a.key, a)));
|
||||
} else {
|
||||
foreach (var kv in rv) {
|
||||
|
||||
@@ -8,13 +8,6 @@ using mbts.spark.utilities;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Api.Orders;
|
||||
|
||||
using Spark.Core;
|
||||
using Spark.Core.Data;
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
@@ -34,28 +27,23 @@ namespace mbts.spark {
|
||||
// from the standard one wrt MEF export/import.
|
||||
interface IAttachableAlgoProvider : IAlgoProvider { }
|
||||
|
||||
class AlgoProvider : IAlgoProvider, IDisposable {
|
||||
class AlgoProvider : IAlgoProvider {
|
||||
readonly IEnumerable<SecurityAlgoTypeSetImmutable> _security_algo_types;
|
||||
readonly IAlgoStudioModel _studio_model;
|
||||
readonly ISecurityAuxiliaryDataModel _security_auxiliary_model;
|
||||
readonly IObservable<TableUpdate<AlgoData, AlgoKey>> _studio_algos_source;
|
||||
readonly OrderFactory _order_factory;
|
||||
readonly ISecurityMetadataStore _sec_meta_store;
|
||||
readonly DynamicOrderHelper _dynamic_helper;
|
||||
readonly AlgoTypeSetImmutable _full_set;
|
||||
IDisposable _dispose;
|
||||
|
||||
public AlgoProvider(
|
||||
IEnumerable<SecurityAlgoTypeSetImmutable> algo_sets,
|
||||
IAlgoStudioModel studio_model,
|
||||
OrderFactory order_factory,
|
||||
ISecurityMetadataStore sec_meta_store) {
|
||||
public AlgoProvider(IEnumerable<SecurityAlgoTypeSetImmutable> algo_sets, IAlgoStudioModel studio_model, DynamicOrderHelper dynamic_helper, ISecurityAuxiliaryDataModel security_auxiliary_model) {
|
||||
_security_algo_types = algo_sets;
|
||||
_studio_model = studio_model;
|
||||
_order_factory = order_factory;
|
||||
_dynamic_helper = dynamic_helper;
|
||||
_full_set = new AlgoTypeSetImmutable(
|
||||
ImmutableHashSet.CreateRange(
|
||||
EnumHelper.get_values_except(CustomOrderDefinition.CustomOneofCase.None, CustomOrderDefinition.CustomOneofCase.Dynamic, CustomOrderDefinition.CustomOneofCase.Composite)),
|
||||
ImmutableHashSet.CreateRange(_order_factory.dynamic_types()));
|
||||
_sec_meta_store = sec_meta_store;
|
||||
ImmutableHashSet.CreateRange(dynamic_helper.dynamic_types));
|
||||
_security_auxiliary_model = security_auxiliary_model;
|
||||
/// initialize <see cref="_studio_algos_source"/> with an empty table, so the
|
||||
/// <see cref="UpdateExtensions.AppendTable{T, TKey}(IObservable{TableUpdate{T, TKey}}, IEnumerable{T})"/>
|
||||
/// calls made by the 'subscribe_algos' methods will fire at least once, even if the underlying
|
||||
@@ -63,15 +51,9 @@ namespace mbts.spark {
|
||||
/// <see cref="subscribe_algos"/>,
|
||||
/// <see cref="subscribe_algos(SecurityCriteriaClause)"/>,
|
||||
/// <see cref="subscribe_algos(SecurityImmutable)"/>
|
||||
|
||||
var bootstraps = new DisposableBuilder();
|
||||
_studio_algos_source = bootstraps.emplace(studio_model.definitions.Cache().ToObservable());
|
||||
_dispose = bootstraps;
|
||||
_studio_algos_source = studio_model.definitions.Cache();
|
||||
}
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
|
||||
public AlgoTypeSetImmutable get_permitted_algos(SecurityImmutable security) =>
|
||||
_security_algo_types.Where(p => p.security_criteria.includes(security))
|
||||
.Select(sec_algos => sec_algos.algo_type_set)
|
||||
@@ -88,12 +70,12 @@ namespace mbts.spark {
|
||||
.AppendTable(_security_algo_types
|
||||
.Select(s => s.algo_type_set)
|
||||
.Aggregate(AlgoTypeSetImmutable.empty, (a, b) => a.union_with(b))
|
||||
.define_algos(t => _order_factory.custom_defi(t).assert_value()));
|
||||
.define_algos(_dynamic_helper));
|
||||
|
||||
public IObservable<TableUpdate<AlgoData, AlgoKey>> subscribe_algos(SecurityCriteriaClause security_clause) {
|
||||
var system_algos = _security_algo_types.match_algos(
|
||||
security_clause,
|
||||
t => _order_factory.custom_defi(t).assert_value(),
|
||||
_dynamic_helper,
|
||||
def => _fit_to_type(def, security_clause, _security_algo_types));
|
||||
|
||||
return _studio_algos_source
|
||||
@@ -112,9 +94,9 @@ namespace mbts.spark {
|
||||
// i.e., non-studio algos
|
||||
var system_algos = _security_algo_types.match_algos(
|
||||
clause,
|
||||
t => _order_factory.custom_defi(t).assert_value(),
|
||||
_dynamic_helper,
|
||||
def => _fit_to_type(def, security, _security_algo_types),
|
||||
_sec_meta_store.get_qty_display_divisor(security));
|
||||
_security_auxiliary_model.get_display_quantity_divisor(security));
|
||||
|
||||
var filtered_studio_algos = _studio_algos_source
|
||||
.StartWith(TableUpdate<AlgoData, AlgoKey>.mk_empty(AlgoData.key_fn))
|
||||
@@ -198,12 +180,8 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
class AttachableAlgoProvider : AlgoProvider, IAttachableAlgoProvider {
|
||||
public AttachableAlgoProvider(
|
||||
IEnumerable<SecurityAlgoTypeSetImmutable> algo_sets,
|
||||
IAlgoStudioModel studio_model,
|
||||
OrderFactory order_factory,
|
||||
ISecurityMetadataStore sec_meta_store)
|
||||
: base(algo_sets, studio_model, order_factory, sec_meta_store) { }
|
||||
public AttachableAlgoProvider(IEnumerable<SecurityAlgoTypeSetImmutable> algo_sets, IAlgoStudioModel studio_model, DynamicOrderHelper dynamic_helper, ISecurityAuxiliaryDataModel security_auxiliary_model)
|
||||
: base(algo_sets, studio_model, dynamic_helper, security_auxiliary_model) { }
|
||||
|
||||
// arbitrarily assign LimitOrderDefinition (this is just a placeholder; the real child is the attach target algo).
|
||||
// Note: although 'None' might seem approrpriate, the server requires a valid definition (perhaps this will change in the future).
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
namespace mbts.analytics {
|
||||
using System;
|
||||
|
||||
public readonly struct GreeksTuple : IEquatable<GreeksTuple> {
|
||||
readonly struct GreeksTuple : IEquatable<GreeksTuple> {
|
||||
public GreeksTuple(
|
||||
double vol = double.NaN,
|
||||
double driver = double.NaN,
|
||||
@@ -58,7 +58,7 @@ namespace mbts.analytics {
|
||||
public static bool operator !=(GreeksTuple a, GreeksTuple b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
public static class GreeksTupleExtensions {
|
||||
static class GreeksTupleExtensions {
|
||||
public static GreeksTuple with(
|
||||
this GreeksTuple greeks_tuple,
|
||||
double? vol = null,
|
||||
@@ -3,16 +3,15 @@
|
||||
using mbts.co2;
|
||||
|
||||
namespace mbts.analytics {
|
||||
using Size = System.Single;
|
||||
public class VolSurfaceMDUnit : IEquatable<VolSurfaceMDUnit> {
|
||||
public readonly double bid_price, ask_price;
|
||||
public readonly Size bid_size, ask_size;
|
||||
public readonly uint bid_size, ask_size;
|
||||
public readonly double mark_price = double.NaN;
|
||||
private static VolSurfaceMDUnit _empty =
|
||||
new VolSurfaceMDUnit(Double.NaN, 0, Double.NaN, 0, Double.NaN);
|
||||
|
||||
public VolSurfaceMDUnit(double this_bid_price, Size this_bid_size,
|
||||
double this_ask_price, Size this_ask_size,
|
||||
public VolSurfaceMDUnit(double this_bid_price, uint this_bid_size,
|
||||
double this_ask_price, uint this_ask_size,
|
||||
double this_mark_price) {
|
||||
bid_price = this_bid_price;
|
||||
bid_size = this_bid_size;
|
||||
@@ -42,7 +41,7 @@ namespace mbts.analytics {
|
||||
}
|
||||
|
||||
public double get_weighted_mid_price(bool get_one_sided_mid = true) {
|
||||
Size sum_q = bid_size + ask_size;
|
||||
uint sum_q = bid_size + ask_size;
|
||||
if (sum_q == 0) {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
@@ -88,8 +88,8 @@ namespace mbts.analytics {
|
||||
|
||||
public void set_driver_price(
|
||||
IDictionary<VolSurfaceExpiration, double> driver_price_map) {
|
||||
Log.Gen.info($"VolSurfaceInterpGreeksProducer.set_driver_price " +
|
||||
$"vs_id: {_vs_id} exp_count: {driver_price_map.Count}");
|
||||
// Log.Gen.info($"VolSurfaceInterpGreeksProducer.set_driver_price " +
|
||||
// $"vs_id: {_vs_id} exp_count: {driver_price_map.Count}");
|
||||
_driver_price_map = new Dictionary<VolSurfaceExpiration, double>(driver_price_map);
|
||||
_compute_and_fire(InputType.DriverPrice);
|
||||
}
|
||||
@@ -155,8 +155,7 @@ namespace mbts.analytics {
|
||||
var master_exp_keys =
|
||||
_exp_strikes_map.Keys.Select(k =>
|
||||
new SecurityExpiration(Venue.Unset, SecurityType.Option, k.symbol, k.expiration));
|
||||
// TODO: add exchange to key (so we don't have to 'Crush' the duplicate keys)
|
||||
var exp_params_no_exch = _exp_params_map.ToDictionaryCrushed(
|
||||
var exp_params_no_exch = _exp_params_map.ToDictionary(
|
||||
kv => kv.Key.unset_exchange(), kv => kv.Value);
|
||||
return exp_params_no_exch.sync(master_exp_keys, true,
|
||||
exp => AnalyticsExpirationParameters.empty);
|
||||
@@ -211,4 +210,4 @@ namespace mbts.analytics {
|
||||
kv.Value.driver_price + max_move));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,10 +38,7 @@ namespace bts.ui {
|
||||
Window about = Application.LoadComponent(new Uri("/view/Shell/About.xaml", UriKind.Relative)) as Window;
|
||||
enable_crash_on_dblclick(about);
|
||||
if (about != null) {
|
||||
about.DataContext = new {
|
||||
Version = Configuration.get<bts.config.SystemConfig>().Version,
|
||||
User = _user
|
||||
};
|
||||
about.DataContext = new { Version = Configuration.get<bts.config.System>().version, User = _user };
|
||||
about.Topmost = true;
|
||||
about.CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, (a, b) => about.Close()));
|
||||
about.CommandBindings.Add(new CommandBinding(ApplicationCommands.Open, (arg, e) => {
|
||||
|
||||
@@ -4,9 +4,7 @@ using bts.spark;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
|
||||
using Spark.Api.Orders;
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -8,7 +8,6 @@ namespace bts.daemons {
|
||||
using mbts.ui;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -9,7 +9,6 @@ using mbts.ui.spark;
|
||||
using mbts.ui.utilities;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace bts {
|
||||
|
||||
using Spark.Core.Data;
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
@@ -51,7 +50,7 @@ namespace bts {
|
||||
ss,
|
||||
user,
|
||||
security_metadata,
|
||||
TimeSpan.FromMilliseconds(Configuration.get<RelayOptions>().NaggleMs),
|
||||
TimeSpan.FromMilliseconds(Configuration.get<RelayOptions>().naggle_ms),
|
||||
TimeSpan.FromSeconds(1)) { }
|
||||
|
||||
public MDDaemon(
|
||||
|
||||
@@ -52,23 +52,17 @@ namespace bts {
|
||||
string rv = "MergedMDDaemon Options:\n";
|
||||
foreach (var kv in sec_map) {
|
||||
var sec = kv.Value.security;
|
||||
var md = kv.Value.md;
|
||||
ulong time = 0;
|
||||
NativePriceSize bid, ask;
|
||||
var has_md = md.get_bbo(ref time, out bid, out ask);
|
||||
if (sec.HasOption) {
|
||||
rv += $"id: {kv.Key} desc: {sec.security_desc()} sym: {sec.Symbol} " +
|
||||
$"exp: {sec.security_expiration()} " +
|
||||
$"k: {sec.Option.Strike} cp: {sec.Option.CallPut} " +
|
||||
$"bid: p: {bid.price} q: {bid.size} qf: {bid.size} ask: p: {ask.price} q: {ask.size} qf: {ask.size}" +
|
||||
"\n";
|
||||
$"k: {sec.Option.Strike} cp: {sec.Option.CallPut}" + "\n";
|
||||
}
|
||||
}
|
||||
Log.Gen.info(rv);
|
||||
}
|
||||
|
||||
private void _on_server_sec_map(IReadOnlyDictionary<uint, SecurityMD> sec_map) {
|
||||
_log_options(sec_map);
|
||||
//_log_options(sec_map);
|
||||
bts.Log.Gen.info(String.Format("MergedMDDaemon._on_server_sec_map sz: {0}", sec_map.Count()));
|
||||
_server_sec_map = new Dictionary<uint, SecurityMD>(sec_map);
|
||||
_merge_and_post();
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace bts.spark {
|
||||
void handle_unclean_shutdown(ErrorReporter e, int oldpid) {
|
||||
string extra = "";
|
||||
try {
|
||||
var conf = mbts.config.Configuration.get<bts.config.SystemConfig>();
|
||||
var conf = mbts.config.Configuration.get<bts.config.System>();
|
||||
extra = JsonConvert.SerializeObject(conf, Formatting.Indented);
|
||||
} catch { }
|
||||
Task.Run(() => e.report_silently(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using bts.api;
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.config;
|
||||
using bts.spark;
|
||||
|
||||
using mbts;
|
||||
using mbts.config;
|
||||
@@ -10,9 +10,7 @@ using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.spark;
|
||||
|
||||
using Spark.Api.Orders;
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Orders;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -29,20 +27,21 @@ namespace bts.shell {
|
||||
readonly IBus _bus;
|
||||
readonly OrdersModel _model;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
readonly ErrorReporter _error_reporter;
|
||||
readonly bool _close_on_send;
|
||||
IReadOnlyDictionary<uint, AccountTuple> _accounts = new Dictionary<uint, AccountTuple>(0);
|
||||
IReadOnlyDictionary<uint, SecurityMD> _md_map;
|
||||
TableUpdate<OrderRecordTuple, uint> _orders;
|
||||
TableUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint> _orders;
|
||||
IDisposable _dispose;
|
||||
|
||||
[ImportingConstructor]
|
||||
public OrderTicketDaemon(IBus bus, OrdersModel model, UserSettingsHub settings_hub)
|
||||
: this(bus, model, settings_hub, Configuration.get<OrderTicketOptions>().CloseOnSend) { }
|
||||
public OrderTicketDaemon(IBus bus, OrdersModel model, UserSettingsHub settings_hub, ErrorReporter error_reporter)
|
||||
: this(bus, model, settings_hub, error_reporter, Configuration.get<bts.config.OrderTicketOptions>().close_on_send) { }
|
||||
|
||||
public OrderTicketDaemon(IBus bus, OrdersModel model, UserSettingsHub settings_hub, bool close_on_send) {
|
||||
public OrderTicketDaemon(IBus bus, OrdersModel model, UserSettingsHub settings_hub, ErrorReporter error_reporter, bool close_on_send) {
|
||||
_bus = bus;
|
||||
_model = model;
|
||||
_settings_hub = settings_hub;
|
||||
_error_reporter = error_reporter;
|
||||
_close_on_send = close_on_send;
|
||||
_dispose = Disposable.Create(
|
||||
model.on_orders.OnDispatcher().Subscribe(o => _orders = o),
|
||||
@@ -50,8 +49,7 @@ namespace bts.shell {
|
||||
bus.subscribe(SparkTags.ShowOrderTicketFromSecurityTag).OnDispatcher().Subscribe(_show_order_ticket),
|
||||
bus.subscribe(SparkTags.ShowOrderTicketTag).OnDispatcher().Subscribe(_show_order_ticket),
|
||||
bus.subscribe(SparkTags.ShowOrderTicketAutoLookupTag).Subscribe(_show_order_tickets),
|
||||
bus.subscribe(SparkTags.ShowModifyOrderTag).OnDispatcher().Subscribe(_show_modify_order),
|
||||
bus.subscribe(SparkTags.AccountsTag).Subscribe(accs => _accounts = accs)
|
||||
bus.subscribe(SparkTags.ShowModifyOrderTag).OnDispatcher().Subscribe(_show_modify_order)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -72,25 +70,33 @@ namespace bts.shell {
|
||||
void _show_order_ticket(SecurityMD smd) {
|
||||
var settings_src = _settings_hub.subscribe_default_settings()
|
||||
.Select(s => s.get_ladder_profile(smd.security)?.value ?? LadderSettings.Default);
|
||||
var accounts_src = _bus.subscribe(SparkTags.AccountsTag);
|
||||
_show_order_window(new OrderTicket {
|
||||
DataContext = new NewOrderTicketVM(_model, smd, accounts_src, settings_src) {
|
||||
DataContext = new NewOrderTicketVM(_model, smd, settings_src) {
|
||||
close_on_send = _close_on_send
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _show_order_ticket(OrderTicketParams ot_params) {
|
||||
if (!_md_map.TryGetValue(ot_params.definition.SecurityKey, out SecurityMD smd)) {
|
||||
void _show_order_ticket((OrderDefinitionImmutable def, double price, uint size) ops) {
|
||||
var (def, price, size) = ops;
|
||||
if (!_md_map.TryGetValue(def.SecurityKey, out SecurityMD smd)) {
|
||||
return;
|
||||
}
|
||||
var def = _model.factory.order(smd.security, _accounts.Values.FirstOrNull());
|
||||
def.MergeFrom(ot_params.definition.CloneToMutable());
|
||||
var accounts = _bus.subscribe(SparkTags.AccountsTag);
|
||||
var calc_buttons = Observable.Return(LadderSettings.Default.calculator_buttons);
|
||||
|
||||
if (def.Account.is_null_or_empty() && def.AccountId == 0) {
|
||||
var settings = _settings_hub.current.get_ladder_profile(smd.security);
|
||||
var default_account = settings?.value?.default_account;
|
||||
if (default_account.HasValue) {
|
||||
var mdef = def.CloneToMutable();
|
||||
// The order ticket will resolve AccountId to a name
|
||||
mdef.AccountId = default_account.Value;
|
||||
def = new(mdef);
|
||||
}
|
||||
}
|
||||
|
||||
_show_order_window(new OrderTicket {
|
||||
DataContext = new NewOrderTicketVM(
|
||||
_model, smd, accounts, new(def), ot_params.price, ot_params.quantity, calc_buttons) {
|
||||
DataContext = new NewOrderTicketVM(_model, smd, def, price, size,
|
||||
Observable.Return(LadderSettings.Default.calculator_buttons)) {
|
||||
close_on_send = _close_on_send
|
||||
}
|
||||
});
|
||||
@@ -104,23 +110,25 @@ namespace bts.shell {
|
||||
}
|
||||
|
||||
void _show_modify_order(uint order_key) {
|
||||
if (!_orders.state.TryGetValue(order_key, out var ori) || !ori.security.HasValue) {
|
||||
return;
|
||||
if (_orders.state.TryGetValue(order_key, out var ori) && ori.sec.HasValue
|
||||
&& (ori.order.HasDef && ori.order.Def.HasCustomDef && !(ori.order.Def.CustomDef.HasStop || ori.order.Def.CustomDef.HasStopLimit))) {
|
||||
var md = _md_map.TryGetValue(ori.order.security_key());
|
||||
_show_order_window(new OrderModificationDialog {
|
||||
DataContext = new ExistingOrderTicketVM(_model,
|
||||
Observable.Return((md, ori.order)),
|
||||
Observable.Return(LadderSettings.Default.calculator_buttons)
|
||||
)
|
||||
});
|
||||
}
|
||||
switch (ori.order.order_type()) {
|
||||
case CustomOrderDefinition.CustomOneofCase.Stop:
|
||||
case CustomOrderDefinition.CustomOneofCase.StopLimit:
|
||||
case CustomOrderDefinition.CustomOneofCase.None:
|
||||
return;
|
||||
}
|
||||
|
||||
LadderSettings _get_settings(SecurityImmutable sec) {
|
||||
var settings = _settings_hub.current;
|
||||
if (settings.ladder_profiles.TryGetFirst(
|
||||
lsp => lsp.security_criteria.includes(sec), out var lsp)) {
|
||||
return lsp.value;
|
||||
}
|
||||
var md = _md_map.TryGetValue(ori.order.security_key());
|
||||
_show_order_window(new OrderModificationDialog {
|
||||
DataContext = new ExistingOrderTicketVM(_model,
|
||||
Observable.Return((md, ori.order)),
|
||||
_bus.subscribe(SparkTags.AccountsTag),
|
||||
Observable.Return(LadderSettings.Default.calculator_buttons),
|
||||
md?.quantity_display)
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,7 @@ namespace mbts.ui {
|
||||
[ImportingConstructor]
|
||||
public RecenterDaemon(IBus bus) {
|
||||
_bus = bus;
|
||||
_time_to_center = TimeSpan.FromSeconds(
|
||||
config.Configuration.get<bts.config.LadderKnobs>().SecondsToRecenter);
|
||||
_time_to_center = TimeSpan.FromSeconds(config.Configuration.get<bts.config.LadderKnobs>().seconds_to_recenter);
|
||||
_last_recenter = DateTime.UtcNow;
|
||||
|
||||
_dispose = _bus.subscribe(SparkTags.RequestRecenterLadder).Subscribe(_ => trigger_recenter(SparkTags.RecenterLadderNow));
|
||||
|
||||
@@ -1,113 +1,107 @@
|
||||
using bts.api.core;
|
||||
using bts.metrics;
|
||||
|
||||
using mbts.config;
|
||||
using mbts.ui;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bts.perf {
|
||||
[Export, Export(typeof(IShellDaemon))]
|
||||
class ShellLatencyDaemon : IShellDaemon, IDisposable {
|
||||
readonly ShellLatencyService _service;
|
||||
readonly string _hostname;
|
||||
readonly string _version;
|
||||
readonly string _username;
|
||||
IDisposable _subs;
|
||||
Ping _pinger = new Ping();
|
||||
|
||||
[ImportingConstructor]
|
||||
public ShellLatencyDaemon(ShellLatencyService service, Lazy<UserTuple> user) {
|
||||
_service = service;
|
||||
_version = Configuration.get<bts.config.SystemConfig>().Version;
|
||||
_username = user.Value.name;
|
||||
_hostname = Environment.MachineName;
|
||||
|
||||
_forever_ping_host(_pinger);
|
||||
_subs = Disposable.Create(
|
||||
_pinger,
|
||||
_service.subscribei_clock().Subscribe(on_tick)
|
||||
);
|
||||
}
|
||||
|
||||
void on_tick(Update<TickImmutable, int> update) {
|
||||
if (update.added.Any()) {
|
||||
var tick = update.added.Last();
|
||||
_service.report_async(new Report() {
|
||||
HostName = _hostname,
|
||||
Version = _version,
|
||||
User = _username,
|
||||
Timestamp = tick.Timestamp
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async void _forever_ping_host(Ping pinger) {
|
||||
var config = Configuration.get<bts.config.ShellPingOptions>();
|
||||
string server_ip = Configuration.get<bts.config.SystemConfig>().ConfigIp;
|
||||
int delay = config.WorkloopMillisecondsInterval;
|
||||
int _current_missed_ping_count = 0;
|
||||
|
||||
if (config.ShellPingIsEnabled && pinger != null) {
|
||||
while (true) {
|
||||
try {
|
||||
await Task.Delay(delay);
|
||||
var response = await pinger.SendPingAsync(server_ip, config.TimeoutMillisecondsThreshold);
|
||||
if (response.Status == IPStatus.Success) {
|
||||
_current_missed_ping_count = 0;
|
||||
if (response.RoundtripTime > config.LoggingMillisecondsThreshold) {
|
||||
Log.Ui.warn($"Ping response not received in a timely manner:\n" +
|
||||
$"\tAddress:\t{response.Address}\n" +
|
||||
$"\tRoundTrip:\t{response.RoundtripTime} ms");
|
||||
}
|
||||
// potentially reset delay to initial work period (no-op otherwise)
|
||||
delay = config.WorkloopMillisecondsInterval;
|
||||
} else {
|
||||
_current_missed_ping_count++;
|
||||
delay = _get_exp_delay(
|
||||
_current_missed_ping_count,
|
||||
config.WorkloopMillisecondsInterval,
|
||||
config.MaximumBackoffMilliseconds);
|
||||
Log.Ui.warn($"Ping was not successful. Ping response: {response.Status}. Missed ping count: {_current_missed_ping_count}. Backing off for {delay} milliseconds.");
|
||||
}
|
||||
} catch (PingException e) {
|
||||
_current_missed_ping_count++;
|
||||
delay = _get_exp_delay(
|
||||
_current_missed_ping_count,
|
||||
config.WorkloopMillisecondsInterval,
|
||||
config.MaximumBackoffMilliseconds);
|
||||
Log.Ui.error($"Ping exception. {e}\nBacking off for {delay} milliseconds.");
|
||||
}
|
||||
} // end while
|
||||
}
|
||||
|
||||
// since we're returning milliseconds here up to a 1 minute delay
|
||||
// we don't need to go crazy with the power, else we will get
|
||||
// an ArgumentOutOfRangeExecption.
|
||||
// Int.MaxValue = 2^31, so we need to be below that
|
||||
// Also, even if we're under the 2^31 int.maxvalue, we don't want to be multed over it
|
||||
// so that is why we have checked() here.
|
||||
int _get_exp_delay(int retry_num, int min_interval, int max_delay) {
|
||||
// add some jitter up to half the max delay so we don't
|
||||
// have a ping spike from all clients at once
|
||||
var jitter_amount = new Random().Next(max_delay / 2);
|
||||
int power_factor = (int)Math.Pow(2, Math.Min(retry_num, 32) - 1);
|
||||
try {
|
||||
return Math.Min(checked(min_interval * power_factor + jitter_amount), max_delay + jitter_amount);
|
||||
} catch (OverflowException) {
|
||||
return max_delay + jitter_amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Disposable.dispose(ref _subs);
|
||||
}
|
||||
}
|
||||
}
|
||||
using bts.api.core;
|
||||
using bts.metrics;
|
||||
|
||||
using mbts.config;
|
||||
using mbts.ui;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bts.perf {
|
||||
[Export, Export(typeof(IShellDaemon))]
|
||||
class ShellLatencyDaemon : IShellDaemon, IDisposable {
|
||||
readonly ShellLatencyService _service;
|
||||
readonly string _hostname;
|
||||
readonly string _version;
|
||||
readonly string _username;
|
||||
IDisposable _subs;
|
||||
Ping _pinger = new Ping();
|
||||
|
||||
[ImportingConstructor]
|
||||
public ShellLatencyDaemon(ShellLatencyService service, Lazy<UserTuple> user) {
|
||||
_service = service;
|
||||
_version = Configuration.get<bts.config.System>().version;
|
||||
_username = user.Value.name;
|
||||
_hostname = Environment.MachineName;
|
||||
|
||||
_forever_ping_host(_pinger);
|
||||
_subs = Disposable.Create(
|
||||
_pinger,
|
||||
_service.subscribei_clock().Subscribe(on_tick)
|
||||
);
|
||||
}
|
||||
|
||||
void on_tick(Update<TickImmutable, int> update) {
|
||||
if (update.added.Any()) {
|
||||
var tick = update.added.Last();
|
||||
_service.report_async(new Report() {
|
||||
HostName = _hostname,
|
||||
Version = _version,
|
||||
User = _username,
|
||||
Timestamp = tick.Timestamp
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async void _forever_ping_host(Ping pinger) {
|
||||
var config = Configuration.get<bts.config.ShellPingOptions>();
|
||||
string server_ip = Configuration.get<bts.config.System>().config_ip;
|
||||
int delay = config.workloop_milliseconds_interval;
|
||||
int _current_missed_ping_count = 0;
|
||||
|
||||
if (config.shell_ping_is_enabled && pinger != null) {
|
||||
while (true) {
|
||||
try {
|
||||
await Task.Delay(delay);
|
||||
var response = await pinger.SendPingAsync(server_ip, config.timeout_milliseconds_threshold);
|
||||
if (response.Status == IPStatus.Success) {
|
||||
_current_missed_ping_count = 0;
|
||||
if (response.RoundtripTime > config.logging_milliseconds_threshold) {
|
||||
Log.Ui.warn($"Ping response not received in a timely manner:\n" +
|
||||
$"\tAddress:\t{response.Address}\n" +
|
||||
$"\tRoundTrip:\t{response.RoundtripTime} ms");
|
||||
}
|
||||
// potentially reset delay to initial work period (no-op otherwise)
|
||||
delay = config.workloop_milliseconds_interval;
|
||||
} else {
|
||||
_current_missed_ping_count++;
|
||||
delay = _get_exp_delay(_current_missed_ping_count, config.workloop_milliseconds_interval, config.maximum_backoff_milliseconds);
|
||||
Log.Ui.warn($"Ping was not successful. Ping response: {response.Status}. Missed ping count: {_current_missed_ping_count}. Backing off for {delay} milliseconds.");
|
||||
}
|
||||
} catch (PingException e) {
|
||||
_current_missed_ping_count++;
|
||||
delay = _get_exp_delay(_current_missed_ping_count, config.workloop_milliseconds_interval, config.maximum_backoff_milliseconds);
|
||||
Log.Ui.error($"Ping exception. {e}\nBacking off for {delay} milliseconds.");
|
||||
}
|
||||
} // end while
|
||||
}
|
||||
|
||||
// since we're returning milliseconds here up to a 1 minute delay
|
||||
// we don't need to go crazy with the power, else we will get
|
||||
// an ArgumentOutOfRangeExecption.
|
||||
// Int.MaxValue = 2^31, so we need to be below that
|
||||
// Also, even if we're under the 2^31 int.maxvalue, we don't want to be multed over it
|
||||
// so that is why we have checked() here.
|
||||
int _get_exp_delay(int retry_num, int min_interval, int max_delay) {
|
||||
// add some jitter up to half the max delay so we don't
|
||||
// have a ping spike from all clients at once
|
||||
var jitter_amount = new Random().Next(max_delay / 2);
|
||||
int power_factor = (int)Math.Pow(2, Math.Min(retry_num, 32) - 1);
|
||||
try {
|
||||
return Math.Min(checked(min_interval * power_factor + jitter_amount), max_delay + jitter_amount);
|
||||
} catch (OverflowException) {
|
||||
return max_delay + jitter_amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Disposable.dispose(ref _subs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace bts.shell {
|
||||
}
|
||||
}
|
||||
bool check_version() {
|
||||
string current = Configuration.get<bts.config.SystemConfig>().Version;
|
||||
string current = Configuration.get<bts.config.System>().version;
|
||||
if (!string.IsNullOrWhiteSpace(current) && current != "private") {
|
||||
using (var key = Registry.CurrentUser.CreateSubKey(@"Software\BTS")) {
|
||||
var value = key.GetValue("version");
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
using System;
|
||||
using System.Composition;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
[Export]
|
||||
class MarketDataAuxiliaryModel : IDisposable {
|
||||
@@ -23,17 +22,9 @@
|
||||
_service = service;
|
||||
var scheduler = ThreadPoolScheduler.Instance;
|
||||
var interval = TimeSpan.FromMilliseconds(100);
|
||||
var bootstraps = new DisposableBuilder();
|
||||
var volume_at_price = bootstraps.emplace(
|
||||
service.subscribei_volume_at_price()
|
||||
.Naggle(interval, scheduler)
|
||||
.Table()
|
||||
.Cache()
|
||||
.ToObservable());
|
||||
this.volume_at_price = volume_at_price;
|
||||
volume_at_price = service.subscribei_volume_at_price().Naggle(interval, scheduler).Table().Cache();
|
||||
_security_orders_demuxer = new(o => o.RecordId, v => v.SecurityId);
|
||||
_dispose = Disposable.Create(
|
||||
volume_at_price.Subscribe(_security_orders_demuxer), bootstraps);
|
||||
_dispose = volume_at_price.Subscribe(_security_orders_demuxer);
|
||||
}
|
||||
|
||||
public IObservable<TableUpdate<VolumeAtPriceLevelImmutable, int>> volume_at_price { get; }
|
||||
|
||||
@@ -4,8 +4,6 @@ using bts.utils;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using Spark.Api.Orders;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
@@ -4,81 +4,70 @@ namespace Spark.Model.Trading {
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.api.position;
|
||||
using bts.shell.pnl;
|
||||
using bts.spark;
|
||||
|
||||
using mbts.spark;
|
||||
|
||||
using Spark.Api.Orders;
|
||||
using Spark.Api.Reactive;
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Composition;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
[Export]
|
||||
class OrderDataSource : IDisposable {
|
||||
IDisposable? _dispose;
|
||||
|
||||
class OrderDataSource {
|
||||
[ImportingConstructor]
|
||||
public OrderDataSource(
|
||||
IBus bus,
|
||||
AdminModel admin_model,
|
||||
SecurityResolver sec_resolver,
|
||||
OrderService order_service,
|
||||
ISecurityAuxiliaryDataModel security_auxiliary_model,
|
||||
ErrorReporter error_reporter
|
||||
) {
|
||||
var scheduler = ThreadPoolScheduler.Instance;
|
||||
var interval = TimeSpan.FromMilliseconds(100);
|
||||
var bootstraps = new DisposableBuilder();
|
||||
|
||||
orders = bootstraps.emplace(
|
||||
admin_model
|
||||
.permission_filter(
|
||||
order_service.subscribei_orders()
|
||||
.Naggle(interval, scheduler),
|
||||
o => o.account_id())
|
||||
.join(sec_resolver, o => o.security_key())
|
||||
.Select(o => new OrderRecordTuple(o.val, o.sec), o => o.key)
|
||||
.Table()
|
||||
.Cache()
|
||||
.ToObservable());
|
||||
orders = admin_model
|
||||
.permission_filter(
|
||||
order_service.subscribei_orders()
|
||||
.Naggle(interval, scheduler),
|
||||
o => o.account_id())
|
||||
.join(sec_resolver, o => o.security_key())
|
||||
.Table()
|
||||
.Cache();
|
||||
|
||||
working_orders = bootstraps.emplace(
|
||||
orders.Filter(tup => tup.order.is_active()).Cache().ToObservable());
|
||||
working_orders = orders.Filter(tup => tup.order.is_active()).Cache();
|
||||
|
||||
security_orders = bootstraps.emplace(
|
||||
orders.GroupTable(o => o.key, o => o.security_id).Cache().ToObservable());
|
||||
security_orders = orders
|
||||
.SelectKeyValue(tup => new OrderSecurity(tup), OrderSecurityHelper.key_fn)
|
||||
.GroupTable(OrderSecurityHelper.key_fn, tup => tup.order.Def.SecurityKey)
|
||||
.Cache();
|
||||
|
||||
account_security_orders = bootstraps.emplace(
|
||||
orders
|
||||
.GroupTable(
|
||||
o => o.key,
|
||||
o => new AccountSecurityKey(o.account_id, o.security_id))
|
||||
.Cache()
|
||||
.ToObservable());
|
||||
account_security_orders = orders.GroupTable(
|
||||
t => t.order.Key,
|
||||
t => new AccountSecurityKey(t.order.account_id(), t.order.security_key()))
|
||||
.Cache();
|
||||
|
||||
working_account_security_orders = bootstraps.emplace(
|
||||
working_orders
|
||||
.Filter(o => o.order.is_exchange_order())
|
||||
.GroupTable(
|
||||
o => o.key,
|
||||
o => new AccountSecurityKey(o.account_id, o.security_id))
|
||||
.Cache()
|
||||
.ToObservable());
|
||||
_dispose = bootstraps;
|
||||
working_account_security_orders = working_orders
|
||||
.Filter(o => o.order.is_exchange_order())
|
||||
.GroupTable(
|
||||
t => t.order.Key,
|
||||
t => new AccountSecurityKey(t.order.account_id(), t.order.security_key()))
|
||||
.Cache();
|
||||
}
|
||||
|
||||
public IObservable<TableUpdate<OrderRecordTuple, uint>> orders { get; }
|
||||
public IObservable<TableUpdate<OrderRecordTuple, uint>> working_orders { get; }
|
||||
public IObservable<TableUpdate<TableUpdateGrouping<OrderRecordTuple, uint, uint>, uint>> security_orders { get; }
|
||||
public IObservable<TableUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint>> orders { get; }
|
||||
public IObservable<TableUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint>> working_orders { get; }
|
||||
public IObservable<TableUpdate<TableUpdateGrouping<OrderSecurity, uint, uint>, uint>> security_orders { get; }
|
||||
|
||||
public IObservable<TableUpdate<TableUpdateGrouping<OrderRecordTuple, uint, AccountSecurityKey>, AccountSecurityKey>> account_security_orders { get; }
|
||||
public IObservable<TableUpdate<TableUpdateGrouping<OrderRecordTuple, uint, AccountSecurityKey>, AccountSecurityKey>> working_account_security_orders { get; }
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
public IObservable<TableUpdate<TableUpdateGrouping<(SecurityImmutable? sec, OrderRecordImmutable order), uint, AccountSecurityKey>, AccountSecurityKey>> account_security_orders { get; }
|
||||
public IObservable<TableUpdate<TableUpdateGrouping<(SecurityImmutable? sec, OrderRecordImmutable order), uint, AccountSecurityKey>, AccountSecurityKey>> working_account_security_orders { get; }
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,8 @@ namespace Spark.Trading {
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
|
||||
using Spark.Api.Orders;
|
||||
using Spark.Api.Reactive;
|
||||
using Spark.Core;
|
||||
using Spark.Core.Data;
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
using Spark.Model.Trading;
|
||||
@@ -45,38 +43,32 @@ namespace Spark.Trading {
|
||||
PositionService position_service,
|
||||
OrderService order_service,
|
||||
OrderDataSource order_data,
|
||||
ISecurityMetadataStore sec_meta_store,
|
||||
ISecurityAuxiliaryDataModel security_auxiliary_model,
|
||||
ErrorReporter error_reporter) {
|
||||
var scheduler = ThreadPoolScheduler.Instance;
|
||||
var interval = TimeSpan.FromMilliseconds(100);
|
||||
var bootstraps = new DisposableBuilder();
|
||||
|
||||
_position_service = position_service ?? throw new ArgumentNullException(nameof(position_service));
|
||||
_error_reporter = error_reporter ?? throw new ArgumentNullException(nameof(error_reporter));
|
||||
_security_positions_demuxer = new TableUpdateDemuxer<(SecurityImmutable?, PositionUnitImmutable pos), int, uint>(
|
||||
o => o.pos.RecordId, p => p.pos.SecurityId);
|
||||
|
||||
on_exchange_positions = bootstraps.emplace(
|
||||
order_service.subscribei_exchange_positions()
|
||||
.Filter(ep => ep.SecurityKey != 0)
|
||||
.SelectKeyValue(ep => ep, ep => new AccountSecurityKey(ep.AccountId, ep.SecurityKey))
|
||||
.join(sec_resolver, ep => ep.SecurityKey)
|
||||
.Table()
|
||||
.Cache()
|
||||
.ToObservable());
|
||||
on_exchange_positions = order_service.subscribei_exchange_positions()
|
||||
.Filter(ep => ep.SecurityKey != 0)
|
||||
.SelectKeyValue(ep => ep, ep => new AccountSecurityKey(ep.AccountId, ep.SecurityKey))
|
||||
.join(sec_resolver, ep => ep.SecurityKey)
|
||||
.Table()
|
||||
.Cache();
|
||||
|
||||
on_positions = bootstraps.emplace(
|
||||
admin_model
|
||||
.permission_filter(
|
||||
_position_service.subscribei_position_data().Naggle(interval, scheduler),
|
||||
p => p.AccountId)
|
||||
.join(sec_resolver, pos => pos.SecurityId)
|
||||
.Table()
|
||||
.Cache()
|
||||
.ToObservable());
|
||||
on_positions = admin_model
|
||||
.permission_filter(
|
||||
_position_service.subscribei_position_data().Naggle(interval, scheduler),
|
||||
p => p.AccountId)
|
||||
.join(sec_resolver, pos => pos.SecurityId)
|
||||
.Table()
|
||||
.Cache();
|
||||
|
||||
day_fills = bootstraps.emplace(
|
||||
DayFillsDataSource.create(bus, sec_resolver).Cache().ToObservable());
|
||||
day_fills = DayFillsDataSource.create(bus, sec_resolver);
|
||||
|
||||
pnl = bus.subscribe(SparkTags.ReportBlackboardTags.PNL.PNLData)
|
||||
.Select(d => d.position_data.ToDictionary(u => u.id(), PNLRowData.from_pnl_unit))
|
||||
@@ -91,7 +83,7 @@ namespace Spark.Trading {
|
||||
(acc_orders, pnl_id) => {
|
||||
var (security, account_id, account) =
|
||||
acc_orders.table.state.Values.TryGetFirst(out var tup) ?
|
||||
(tup.security, tup.order.account_id(), tup.order.account()) :
|
||||
(tup.sec, tup.order.account_id(), tup.order.account()) :
|
||||
(null, 0, null);
|
||||
return new PNLRowData(pnl_id,
|
||||
security: security,
|
||||
@@ -99,24 +91,19 @@ namespace Spark.Trading {
|
||||
account_name: account,
|
||||
working: WorkingOrderData.Create(
|
||||
acc_orders.table.state.Values.Select(t => t.order),
|
||||
new(
|
||||
sec_meta_store.get_qty_display_divisor(security),
|
||||
new QuantityConversionData<double>(
|
||||
security_auxiliary_model.get_display_quantity_divisor(security),
|
||||
QuantityConversionOpType.DivideActual)));
|
||||
},
|
||||
PNLRowData.key_fn)
|
||||
.Where(t => t.has_changes())
|
||||
.Cache()
|
||||
.ToObservable();
|
||||
.Cache();
|
||||
|
||||
merged_positions = bootstraps.emplace(
|
||||
ReconcileReportDataSource.create(
|
||||
on_exchange_positions, on_positions.Filter(tup => !(tup.Item1?.HasSpread ?? false)))
|
||||
.Cache()
|
||||
.ToObservable());
|
||||
merged_positions = ReconcileReportDataSource.create(
|
||||
on_exchange_positions, on_positions.Filter(tup => !(tup.Item1?.HasSpread ?? false)))
|
||||
.Cache();
|
||||
|
||||
_dispose = new(Disposable.Create(
|
||||
on_positions.Subscribe(_security_positions_demuxer),
|
||||
bootstraps));
|
||||
_dispose = new(on_positions.Subscribe(_security_positions_demuxer));
|
||||
}
|
||||
|
||||
public IObservable<TableUpdate<(SecurityImmutable? sec, RetrievedExchangePositionImmutable exch_pos), AccountSecurityKey>> on_exchange_positions { get; }
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
public override string window_type => WINDOW_TYPE;
|
||||
string ISharedWindow.thread_id =>
|
||||
config.Configuration.get<bts.config.MultiThreadedWindows>().VolSurface ? "VolSurface" : null;
|
||||
config.Configuration.get<bts.config.MultiThreadedWindows>().vol_surface ? "VolSurface" : null;
|
||||
|
||||
public int vs_id => _startup_data.id;
|
||||
public string vs_name { get; }
|
||||
|
||||
@@ -4,8 +4,6 @@ namespace mbts.spark {
|
||||
|
||||
using mbts.ui;
|
||||
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
new MarketViewWindowFactory(
|
||||
this, bus, settings_hub, orders_model, position_model, sec_meta_store),
|
||||
new OptionsViewWindowFactory(this, orders_model, bus),
|
||||
new TimeAndSalesWindowFactory(this, bus, consumer, settings_hub),
|
||||
new TimeAndSalesWindowFactory(this, bus, consumer, settings_hub, orders_model.security_auxiliary_model),
|
||||
};
|
||||
|
||||
public string name => "Market";
|
||||
|
||||
@@ -12,28 +12,16 @@ namespace bts.shell.market_view {
|
||||
public string security { get; set; }
|
||||
[ProtoMember(2)]
|
||||
public DoubleMinMaxState bid_price { get; set; }
|
||||
|
||||
// [ProtoMember(3)]
|
||||
// public UIntMinMaxState bid_merged_size { get; set; }
|
||||
[ProtoMember(103)]
|
||||
public DoubleMinMaxState bid_merged_size { get; set; }
|
||||
|
||||
[ProtoMember(3)]
|
||||
public UIntMinMaxState bid_merged_size { get; set; }
|
||||
[ProtoMember(4)]
|
||||
public DoubleMinMaxState ask_price { get; set; }
|
||||
|
||||
// [ProtoMember(5)]
|
||||
// public UIntMinMaxState ask_merged_size { get; set; }
|
||||
[ProtoMember(105)]
|
||||
public DoubleMinMaxState ask_merged_size { get; set; }
|
||||
|
||||
[ProtoMember(5)]
|
||||
public UIntMinMaxState ask_merged_size { get; set; }
|
||||
[ProtoMember(6)]
|
||||
public DoubleMinMaxState last_price { get; set; }
|
||||
|
||||
// [ProtoMember(7)]
|
||||
// public UIntMinMaxState last_size { get; set; }
|
||||
[ProtoMember(107)]
|
||||
public DoubleMinMaxState last_size { get; set; }
|
||||
|
||||
[ProtoMember(7)]
|
||||
public UIntMinMaxState last_size { get; set; }
|
||||
[ProtoMember(8)]
|
||||
public DoubleMinMaxState day_change { get; set; }
|
||||
[ProtoMember(9)]
|
||||
@@ -42,51 +30,26 @@ namespace bts.shell.market_view {
|
||||
public DoubleMinMaxState session_low { get; set; }
|
||||
[ProtoMember(11)]
|
||||
public DoubleMinMaxState session_high { get; set; }
|
||||
// [ProtoMember(12)]
|
||||
// public IntMinMaxState day_position { get; set; }
|
||||
[ProtoMember(112)]
|
||||
public DoubleMinMaxState day_position { get; set; }
|
||||
[ProtoMember(12)]
|
||||
public IntMinMaxState day_position { get; set; }
|
||||
[ProtoMember(27)]
|
||||
public DoubleMinMaxState raw_day_position { get; set; }
|
||||
|
||||
// [ProtoMember(13)]
|
||||
// public IntMinMaxState working_bid_volume { get; set; }
|
||||
[ProtoMember(113)]
|
||||
public DoubleMinMaxState working_bid_volume { get; set; }
|
||||
|
||||
// [ProtoMember(14)]
|
||||
// public IntMinMaxState working_ask_volume { get; set; }
|
||||
[ProtoMember(114)]
|
||||
public DoubleMinMaxState working_ask_volume { get; set; }
|
||||
|
||||
// [ProtoMember(15)]
|
||||
// public ULongMinMaxState volume { get; set; }
|
||||
[ProtoMember(115)]
|
||||
public DoubleMinMaxState volume { get; set; }
|
||||
|
||||
[ProtoMember(13)]
|
||||
public IntMinMaxState working_bid_volume { get; set; }
|
||||
[ProtoMember(14)]
|
||||
public IntMinMaxState working_ask_volume { get; set; }
|
||||
[ProtoMember(15)]
|
||||
public ULongMinMaxState volume { get; set; }
|
||||
[ProtoMember(16)]
|
||||
public string security_key { get; set; }
|
||||
|
||||
// [ProtoMember(17)]
|
||||
// public UIntMinMaxState bid_direct_size { get; set; }
|
||||
[ProtoMember(117)]
|
||||
public DoubleMinMaxState bid_direct_size { get; set; }
|
||||
|
||||
// [ProtoMember(18)]
|
||||
// public UIntMinMaxState bid_implied_size { get; set; }
|
||||
[ProtoMember(118)]
|
||||
public DoubleMinMaxState bid_implied_size { get; set; }
|
||||
|
||||
// [ProtoMember(19)]
|
||||
// public UIntMinMaxState ask_direct_size { get; set; }
|
||||
[ProtoMember(119)]
|
||||
public DoubleMinMaxState ask_direct_size { get; set; }
|
||||
|
||||
// [ProtoMember(20)]
|
||||
// public UIntMinMaxState ask_implied_size { get; set; }
|
||||
[ProtoMember(120)]
|
||||
public DoubleMinMaxState ask_implied_size { get; set; }
|
||||
|
||||
[ProtoMember(17)]
|
||||
public UIntMinMaxState bid_direct_size { get; set; }
|
||||
[ProtoMember(18)]
|
||||
public UIntMinMaxState bid_implied_size { get; set; }
|
||||
[ProtoMember(19)]
|
||||
public UIntMinMaxState ask_direct_size { get; set; }
|
||||
[ProtoMember(20)]
|
||||
public UIntMinMaxState ask_implied_size { get; set; }
|
||||
[ProtoMember(21)]
|
||||
public string market_description { get; set; }
|
||||
[ProtoMember(22)]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace mbts.spark {
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.domain;
|
||||
using bts.api.orderman;
|
||||
using bts.api.position;
|
||||
using bts.collections;
|
||||
@@ -15,12 +14,9 @@
|
||||
using mbts.ui.shell;
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Api.Domain;
|
||||
using Spark.Api.Orders;
|
||||
using Spark.Api.Position;
|
||||
using Spark.Core;
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Orders;
|
||||
using Spark.Trading;
|
||||
|
||||
using System;
|
||||
@@ -52,21 +48,21 @@
|
||||
|
||||
readonly CellProperty<double> _day_position;
|
||||
readonly CellProperty<double> _raw_day_position;
|
||||
readonly CellProperty<double> _last_size;
|
||||
readonly CellProperty<double> _volume;
|
||||
readonly CellProperty<uint> _last_size;
|
||||
readonly CellProperty<ulong> _volume;
|
||||
readonly CellProperty<bool> _inside_bids;
|
||||
readonly CellProperty<bool> _inside_asks;
|
||||
readonly CellProperty<double> _working_bid_volume;
|
||||
readonly CellProperty<double> _working_ask_volume;
|
||||
readonly CellProperty<int> _working_bid_volume;
|
||||
readonly CellProperty<int> _working_ask_volume;
|
||||
readonly CellProperty<double> _working_best_bid;
|
||||
readonly CellProperty<double> _working_best_ask;
|
||||
|
||||
readonly Property<double> _bid_direct_size;
|
||||
readonly Property<double> _bid_implied_size;
|
||||
readonly Property<double> _bid_merged_size;
|
||||
readonly Property<double> _ask_direct_size;
|
||||
readonly Property<double> _ask_implied_size;
|
||||
readonly Property<double> _ask_merged_size;
|
||||
readonly PropertyAutoWrapper<uint, uint> _bid_direct_size;
|
||||
readonly PropertyAutoWrapper<uint, uint> _bid_implied_size;
|
||||
readonly PropertyAutoWrapper<uint, uint> _bid_merged_size;
|
||||
readonly PropertyAutoWrapper<uint, uint> _ask_direct_size;
|
||||
readonly PropertyAutoWrapper<uint, uint> _ask_implied_size;
|
||||
readonly PropertyAutoWrapper<uint, uint> _ask_merged_size;
|
||||
|
||||
// greeks
|
||||
readonly Property<GreeksTuple> _greeks;
|
||||
@@ -81,13 +77,10 @@
|
||||
|
||||
ulong _bbo_ref;
|
||||
ulong _last_ref;
|
||||
double? _display_qty_divisor_backing;
|
||||
bool _is_visible;
|
||||
|
||||
public Row(
|
||||
IBus bus,
|
||||
SecurityMD smd,
|
||||
OrdersModel orders_model,
|
||||
IPositionModel position_model) {
|
||||
public Row(IBus bus, SecurityMD smd, OrdersModel orders_model, IPositionModel position_model) {
|
||||
_bus = bus;
|
||||
security_md = smd;
|
||||
_orders_model = orders_model;
|
||||
@@ -96,29 +89,29 @@
|
||||
smd.quantity_display.TickFactors : new(new() { TickBase = 1, TickUnit = 1 }));
|
||||
_raise_invalidated = new(() => invalidated?.Invoke());
|
||||
|
||||
_day_position = new CellProperty<double>(this, nameof(day_position), fmt_qty);
|
||||
_raw_day_position = new CellProperty<double>(this, nameof(raw_day_position), fmt_qty);
|
||||
_day_position = new CellProperty<double>(this, nameof(day_position), fmt_display_qty_double);
|
||||
_raw_day_position = new CellProperty<double>(this, nameof(raw_day_position), fmt_display_qty_double);
|
||||
_bid_price = backing(nameof(bid_price), double.NaN);
|
||||
_ask_price = backing(nameof(ask_price), double.NaN);
|
||||
_last_price = backing(nameof(last_price), double.NaN);
|
||||
_last_size = new CellProperty<double>(this, nameof(last_qty), fmt_qty);
|
||||
_last_size = new CellProperty<uint>(this, nameof(last_qty), fmt_display_qty_uint);
|
||||
_day_change = backing(nameof(day_change), double.NaN);
|
||||
_session_low = backing(nameof(session_low), double.NaN);
|
||||
_session_high = backing(nameof(session_high), double.NaN);
|
||||
_last_price_from_spread = backing(nameof(last_price_from_spread), false);
|
||||
_settlement = backing(nameof(settlement), double.NaN);
|
||||
_volume = new CellProperty<double>(this, nameof(volume), fmt_qty, 0);
|
||||
_volume = new CellProperty<ulong>(this, nameof(volume), fmt_display_qty_ulong, 0);
|
||||
_inside_bids = new CellProperty<bool>(this, nameof(inside_bids));
|
||||
_inside_asks = new CellProperty<bool>(this, nameof(inside_asks));
|
||||
_working_bid_volume = new CellProperty<double>(this, nameof(working_bid_volume));
|
||||
_working_ask_volume = new CellProperty<double>(this, nameof(working_ask_volume));
|
||||
_working_bid_volume = new CellProperty<int>(this, nameof(working_bid_volume));
|
||||
_working_ask_volume = new CellProperty<int>(this, nameof(working_ask_volume));
|
||||
|
||||
_bid_direct_size = backing(nameof(dbid_qty), 0D);
|
||||
_bid_implied_size = backing(nameof(ibid_qty), 0D);
|
||||
_bid_merged_size = backing(nameof(bid_qty), 0D);
|
||||
_ask_direct_size = backing(nameof(dask_qty), 0D);
|
||||
_ask_implied_size = backing(nameof(iask_qty), 0D);
|
||||
_ask_merged_size = backing(nameof(ask_qty), 0D);
|
||||
_bid_direct_size = backing(nameof(dbid_qty), 0u, convert_display_qty_uint);
|
||||
_bid_implied_size = backing(nameof(ibid_qty), 0u, convert_display_qty_uint);
|
||||
_bid_merged_size = backing(nameof(bid_qty), 0u, convert_display_qty_uint);
|
||||
_ask_direct_size = backing(nameof(dask_qty), 0u, convert_display_qty_uint);
|
||||
_ask_implied_size = backing(nameof(iask_qty), 0u, convert_display_qty_uint);
|
||||
_ask_merged_size = backing(nameof(ask_qty), 0u, convert_display_qty_uint);
|
||||
|
||||
_working_best_bid = new CellProperty<double>(
|
||||
this, nameof(working_best_bid), format_price, double.NaN);
|
||||
@@ -151,28 +144,32 @@
|
||||
_animation.animations_changed += on_animations_changed;
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
attach_prop_changed_cb(_bid_direct_size, mk_echo_prop_changed_cb<(uint, uint)>(nameof(dbid_qty_text))),
|
||||
attach_prop_changed_cb(_bid_implied_size, mk_echo_prop_changed_cb<(uint, uint)>(nameof(ibid_qty_text))),
|
||||
attach_prop_changed_cb(_bid_merged_size, mk_echo_prop_changed_cb<(uint, uint)>(nameof(bid_qty_text))),
|
||||
attach_prop_changed_cb(_ask_direct_size, mk_echo_prop_changed_cb<(uint, uint)>(nameof(dask_qty_text))),
|
||||
attach_prop_changed_cb(_ask_implied_size, mk_echo_prop_changed_cb<(uint, uint)>(nameof(iask_qty_text))),
|
||||
attach_prop_changed_cb(_ask_merged_size, mk_echo_prop_changed_cb<(uint, uint)>(nameof(ask_qty_text))),
|
||||
_orders_model.on_security_root_orders(security.Id).OnDispatcher().Subscribe(on_orders),
|
||||
position_model.on_security_positions(security.Id).OnDispatcher().Subscribe(on_security_positions),
|
||||
_inside_bids.OnDispatcher().Subscribe(v => _animation.animate_working_bids_inside(v.newval)),
|
||||
_inside_asks.OnDispatcher().Subscribe(v => _animation.animate_working_asks_inside(v.newval)),
|
||||
_day_change.OnDispatcher().Subscribe(v => _animation.animate_day_change(v.newval > v.oldval)),
|
||||
_bid_direct_size.invalidate_others_on_change(nameof(dbid_qty_display), nameof(dbid_qty_text)),
|
||||
_bid_implied_size.invalidate_others_on_change(nameof(ibid_qty_display), nameof(ibid_qty_text)),
|
||||
_bid_merged_size.invalidate_others_on_change(nameof(bid_qty_display), nameof(bid_qty_text)),
|
||||
_ask_direct_size.invalidate_others_on_change(nameof(dask_qty_display), nameof(dask_qty_text)),
|
||||
_ask_implied_size.invalidate_others_on_change(nameof(iask_qty_display), nameof(iask_qty_text)),
|
||||
_ask_merged_size.invalidate_others_on_change(nameof(ask_qty_display), nameof(ask_qty_text)),
|
||||
_last_size.invalidate_on_change(nameof(last_qty_display), nameof(last_qty_text)),
|
||||
_day_position.invalidate_on_change(nameof(day_position_display), nameof(day_position_text)),
|
||||
_raw_day_position.invalidate_on_change(nameof(raw_day_position_display), nameof(raw_day_position_text)),
|
||||
_working_bid_volume.invalidate_on_change(nameof(working_bid_volume_display)),
|
||||
_working_ask_volume.invalidate_on_change(nameof(working_ask_volume_display)),
|
||||
Disposable.Create(() => _animation.animations_changed -= on_animations_changed));
|
||||
|
||||
void on_animations_changed() => RaisePropertyChanged(nameof(animations));
|
||||
|
||||
string fmt_qty(double value) =>
|
||||
_qty_display.format(value);
|
||||
uint convert_display_qty_uint(uint value) =>
|
||||
(uint)((double)value).safe_divide(_display_qty_divisor);
|
||||
|
||||
string fmt_display_qty_uint(uint value) =>
|
||||
((double)value).safe_divide(_display_qty_divisor).ToString();
|
||||
|
||||
string fmt_display_qty_ulong(ulong value) =>
|
||||
((double)value).safe_divide(_display_qty_divisor).ToString();
|
||||
|
||||
string fmt_display_qty_double(double value) =>
|
||||
value.safe_divide(_display_qty_divisor).ToString();
|
||||
|
||||
void on_orders(ITableUpdateTransform<OrderRecordImmutable, uint> orders) {
|
||||
foreach (var o in orders.removed) {
|
||||
@@ -199,6 +196,15 @@
|
||||
raise_invalidated();
|
||||
}
|
||||
}
|
||||
|
||||
PropertyChange<T> mk_echo_prop_changed_cb<T>(params string[] props) =>
|
||||
(oldval, newval) => RaisePropertyChanged(props);
|
||||
|
||||
IDisposable attach_prop_changed_cb<TView, TModel>(
|
||||
PropertyAutoWrapper<TModel, TView> prop, PropertyChange<(TModel, TView)> callback) {
|
||||
prop.changed += callback;
|
||||
return Disposable.Create(() => prop.changed -= callback);
|
||||
}
|
||||
}
|
||||
|
||||
public event Action invalidated;
|
||||
@@ -211,83 +217,55 @@
|
||||
public bool inside_bids => _inside_bids.value;
|
||||
public bool inside_asks => _inside_asks.value;
|
||||
public double day_position => _day_position.value;
|
||||
public double day_position_display => _qty_display.to_display_value(day_position);
|
||||
public string day_position_text => _day_position.text;
|
||||
public double raw_day_position => _raw_day_position.value;
|
||||
public string raw_day_position_text => _raw_day_position.text;
|
||||
public double raw_day_position_display => _qty_display.to_display_value(raw_day_position);
|
||||
|
||||
public string raw_day_position_text => SparkUtils.fmt_position(raw_day_position.safe_divide(_display_qty_divisor));
|
||||
public double last_price => _last_price.Value;
|
||||
public bool last_price_from_spread => _last_price_from_spread.Value;
|
||||
public double last_qty => _last_size.value;
|
||||
public uint last_qty => _last_size.value;
|
||||
public string last_qty_text => _last_size.text;
|
||||
public double last_qty_display => _qty_display.to_display_value(last_qty);
|
||||
|
||||
// bid price/size
|
||||
public double bid_price => _bid_price.Value;
|
||||
public double dbid_qty => _bid_direct_size.Value;
|
||||
public double ibid_qty => _bid_merged_size.Value - _bid_direct_size.Value;
|
||||
public double bid_qty => _bid_merged_size.Value;
|
||||
public uint dbid_qty => _bid_direct_size.model;
|
||||
public uint ibid_qty => _bid_merged_size.model - _bid_direct_size.model;
|
||||
public uint bid_qty => _bid_merged_size.model;
|
||||
|
||||
// bid size text
|
||||
public string dbid_qty_text => _qty_display.format(dbid_qty);
|
||||
public string ibid_qty_text => _qty_display.format(ibid_qty);
|
||||
public string bid_qty_text => _qty_display.format(bid_qty);
|
||||
|
||||
// bid display size
|
||||
// The grid filters reference display values (rather than actual) since that is what the
|
||||
// user sees in the grid.
|
||||
public double dbid_qty_display => _qty_display.to_display_value(dbid_qty);
|
||||
public double ibid_qty_display => _qty_display.to_display_value(ibid_qty);
|
||||
public double bid_qty_display => _qty_display.to_display_value(bid_qty);
|
||||
public string dbid_qty_text => _bid_direct_size.view.ToString();
|
||||
public string ibid_qty_text => _bid_implied_size.view.ToString();
|
||||
public string bid_qty_text => _bid_merged_size.view.ToString();
|
||||
|
||||
// ask price/size
|
||||
public double ask_price => _ask_price.Value;
|
||||
public double dask_qty => _ask_direct_size.Value;
|
||||
public double iask_qty => _ask_implied_size.Value;
|
||||
public double ask_qty => _ask_merged_size.Value;
|
||||
public uint dask_qty => _ask_direct_size.model;
|
||||
public uint iask_qty => _ask_implied_size.model;
|
||||
public uint ask_qty => _ask_merged_size.model;
|
||||
|
||||
// ask size text
|
||||
public string dask_qty_text => _qty_display.format(dask_qty);
|
||||
public string iask_qty_text => _qty_display.format(iask_qty);
|
||||
public string ask_qty_text => _qty_display.format(ask_qty);
|
||||
public string dask_qty_text => _ask_direct_size.view.ToString();
|
||||
public string iask_qty_text => _ask_implied_size.view.ToString();
|
||||
public string ask_qty_text => _ask_merged_size.view.ToString();
|
||||
|
||||
// ask display size
|
||||
// The grid filters reference display values (rather than actual) since that is what the
|
||||
// user sees in the grid.
|
||||
public double dask_qty_display => _qty_display.to_display_value(dask_qty);
|
||||
public double iask_qty_display => _qty_display.to_display_value(iask_qty);
|
||||
public double ask_qty_display => _qty_display.to_display_value(ask_qty);
|
||||
|
||||
public double volume => _volume.value;
|
||||
public ulong volume => _volume.value;
|
||||
public string volume_text => _volume.text;
|
||||
public double settlement => _settlement.Value;
|
||||
public double day_change => _day_change.Value;
|
||||
public double session_low => _session_low.Value;
|
||||
public double session_high => _session_high.Value;
|
||||
|
||||
// working bids
|
||||
public double working_bid_volume => _working_bid_volume.value;
|
||||
public int working_bid_volume => _working_bid_volume.value;
|
||||
public int working_ask_volume => _working_ask_volume.value;
|
||||
public string working_bids_text => OrderUtils.order_collection_summary(
|
||||
_order_summarizer.best_working_bid,
|
||||
_order_summarizer.best_working_bid_qty,
|
||||
_order_summarizer.working_bid_qty,
|
||||
security.ContractSpec,
|
||||
_qty_display);
|
||||
public double working_bid_volume_display =>
|
||||
_qty_display.to_display_value(working_bid_volume);
|
||||
public double working_best_bid => _working_best_bid.value;
|
||||
|
||||
// working asks
|
||||
public double working_ask_volume => _working_ask_volume.value;
|
||||
_order_summarizer.best_working_bid_qty.safe_divide((int)_display_qty_divisor),
|
||||
_order_summarizer.working_bid_qty.safe_divide((int)_display_qty_divisor),
|
||||
security.ContractSpec);
|
||||
public string working_asks_text => OrderUtils.order_collection_summary(
|
||||
_order_summarizer.best_working_ask,
|
||||
_order_summarizer.best_working_ask_qty,
|
||||
_order_summarizer.working_ask_qty,
|
||||
security.ContractSpec,
|
||||
_qty_display);
|
||||
public double working_ask_volume_display =>
|
||||
_qty_display.to_display_value(working_ask_volume);
|
||||
((double)_order_summarizer.best_working_ask_qty).safe_divide(_display_qty_divisor),
|
||||
((double)_order_summarizer.working_ask_qty).safe_divide(_display_qty_divisor),
|
||||
security.ContractSpec);
|
||||
public double working_best_bid => _working_best_bid.value;
|
||||
public double working_best_ask => _working_best_ask.value;
|
||||
|
||||
// ice
|
||||
@@ -337,11 +315,10 @@
|
||||
public ICommand working_asks_click { get; }
|
||||
public ICommand default_command { get; }
|
||||
|
||||
double _display_qty_divisor => _display_qty_divisor_backing ??
|
||||
(_display_qty_divisor_backing = _orders_model.security_auxiliary_model.get_display_quantity_divisor(security)).Value;
|
||||
IMDConsumerHandle _md => security_md.md;
|
||||
|
||||
TickDisplaySettingsImmutable _qty_display =>
|
||||
security_md.quantity_display;
|
||||
|
||||
public string format_price(double price) =>
|
||||
security.ContractSpec.format(price);
|
||||
|
||||
@@ -413,41 +390,39 @@
|
||||
}
|
||||
|
||||
void _show_order_ticket(MarketViewColumnType col) {
|
||||
var order = _orders_model.factory.order(security);
|
||||
switch (col) {
|
||||
case MarketViewColumnType.Bid:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, get_params(bid_price));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side: Side.Sell), bid_price, 0u));
|
||||
break;
|
||||
case MarketViewColumnType.BidMergedQty:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, get_params(bid_price, bid_qty));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side: Side.Sell), bid_price, bid_qty));
|
||||
break;
|
||||
case MarketViewColumnType.BidDirectQty:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, get_params(bid_price, dbid_qty));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side: Side.Sell), bid_price, dbid_qty));
|
||||
break;
|
||||
case MarketViewColumnType.BidImpliedQty:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, get_params(bid_price, ibid_qty));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side: Side.Sell), bid_price, ibid_qty));
|
||||
break;
|
||||
case MarketViewColumnType.Ask:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, get_params(ask_price));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side: Side.Buy), ask_price, 0u));
|
||||
break;
|
||||
case MarketViewColumnType.AskMergedQty:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, get_params(ask_price, ask_qty));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side: Side.Buy), ask_price, ask_qty));
|
||||
break;
|
||||
case MarketViewColumnType.AskDirectQty:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, get_params(ask_price, dask_qty));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side: Side.Sell), ask_price, dask_qty));
|
||||
break;
|
||||
case MarketViewColumnType.AskImpliedQty:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, get_params(ask_price, iask_qty));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side: Side.Sell), ask_price, iask_qty));
|
||||
break;
|
||||
case MarketViewColumnType.Last:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, get_params(last_price));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, last_price, 0u));
|
||||
break;
|
||||
case MarketViewColumnType.LastSize:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, get_params(last_price, last_qty));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, last_price, last_qty));
|
||||
break;
|
||||
}
|
||||
|
||||
OrderTicketParams get_params(double price, double quantity = 0) =>
|
||||
new(new(new() {SecurityKey = security_key }), price, quantity);
|
||||
}
|
||||
|
||||
void _refresh_orders(ref bool dirty) {
|
||||
@@ -467,13 +442,8 @@
|
||||
}
|
||||
|
||||
void _refresh_inside_working_orers(ref bool dirty) {
|
||||
var cmp = new OrderPriceComparer();
|
||||
_inside_bids.on_next_distinct(
|
||||
!double.IsNaN(working_best_bid) && cmp.Compare(working_best_bid, bid_price) >= 0,
|
||||
ref dirty);
|
||||
_inside_asks.on_next_distinct(
|
||||
!double.IsNaN(working_best_ask) && cmp.Compare(working_best_ask, ask_price) <= 0,
|
||||
ref dirty);
|
||||
_inside_bids.on_next_distinct(!double.IsNaN(working_best_bid) && OrderDataHelper.PriceComparer.Compare(working_best_bid, bid_price) >= 0, ref dirty);
|
||||
_inside_asks.on_next_distinct(!double.IsNaN(working_best_ask) && OrderDataHelper.PriceComparer.Compare(working_best_ask, ask_price) <= 0, ref dirty);
|
||||
}
|
||||
|
||||
class MyOrderSummarizer {
|
||||
|
||||
@@ -101,8 +101,8 @@ namespace bts.shell.market_view {
|
||||
public static GridColumnSettings position => _mk(Columns.Position, ColHeader.Pos, ColWidth.Qty);
|
||||
public static GridColumnSettings raw_day_position => _mk(Columns.RawDayPosition, ColHeader.RawDayPos, ColWidth.Qty, is_visible: false);
|
||||
public static GridColumnSettings working_bids => _mk(Columns.WorkingBids, ColHeader.Working, ColWidth.Working, fg: BTSColors.MarketData.bid);
|
||||
public static GridColumnSettings bid_implied_size => _mk(Columns.BidImpliedQty, ColHeader.ImpliedQty, ColWidth.MergedQty, false, fg: BTSColors.MarketData.bid);
|
||||
public static GridColumnSettings bid_direct_size => _mk(Columns.BidDirectQty, ColHeader.DirectQty, ColWidth.MergedQty, false, fg: BTSColors.MarketData.bid);
|
||||
public static GridColumnSettings bid_implied_size => _mk(Columns.BidImpliedQty, ColHeader.ImpliedQty, ColWidth.MergedQty, fg: BTSColors.MarketData.bid);
|
||||
public static GridColumnSettings bid_direct_size => _mk(Columns.BidDirectQty, ColHeader.DirectQty, ColWidth.MergedQty, fg: BTSColors.MarketData.bid);
|
||||
public static GridColumnSettings bid_merged_size => _mk(Columns.BidMergedQty, ColHeader.MergedQty, ColWidth.MergedQty, fg: BTSColors.MarketData.bid);
|
||||
|
||||
public static GridColumnSettings bid => _mk(
|
||||
@@ -120,8 +120,8 @@ namespace bts.shell.market_view {
|
||||
bg: BTSColors.Grid.dark_column_background);
|
||||
|
||||
public static GridColumnSettings ask_merged_size => _mk(Columns.AskMergedQty, ColHeader.MergedQty, ColWidth.MergedQty, fg: BTSColors.MarketData.ask);
|
||||
public static GridColumnSettings ask_direct_size => _mk(Columns.AskDirectQty, ColHeader.DirectQty, ColWidth.MergedQty, false, fg: BTSColors.MarketData.ask);
|
||||
public static GridColumnSettings ask_implied_size => _mk(Columns.AskImpliedQty, ColHeader.ImpliedQty, ColWidth.MergedQty, false, fg: BTSColors.MarketData.ask);
|
||||
public static GridColumnSettings ask_direct_size => _mk(Columns.AskDirectQty, ColHeader.DirectQty, ColWidth.MergedQty, fg: BTSColors.MarketData.ask);
|
||||
public static GridColumnSettings ask_implied_size => _mk(Columns.AskImpliedQty, ColHeader.ImpliedQty, ColWidth.MergedQty, fg: BTSColors.MarketData.ask);
|
||||
public static GridColumnSettings working_asks => _mk(Columns.WorkingAsks, ColHeader.Working, ColWidth.Working, fg: BTSColors.MarketData.ask);
|
||||
public static GridColumnSettings settlement => _mk(Columns.Settlement, ColHeader.Settlement, ColWidth.Settlement);
|
||||
public static GridColumnSettings day_change => _mk(Columns.DayChange, ColHeader.DayChange, ColWidth.Price);
|
||||
|
||||
@@ -17,13 +17,11 @@ namespace mbts.spark {
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Api.Orders;
|
||||
using Spark.Core;
|
||||
using Spark.Core.Collections;
|
||||
using Spark.Core.Data;
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reflection;
|
||||
using Spark.Core.Reactive;
|
||||
using Spark.Trading;
|
||||
|
||||
using System;
|
||||
@@ -388,25 +386,25 @@ namespace mbts.spark {
|
||||
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_filter { get; }
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> bid_price_filter { get; } = new DoubleMinMax<Row>(r => r.bid_price, nameof(Row.bid_price));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> bid_qty_filter { get; } = new DoubleMinMax<Row>(r => r.bid_qty_display, nameof(Row.bid_qty_display));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> dbid_qty_filter { get; } = new DoubleMinMax<Row>(r => r.dbid_qty_display, nameof(Row.dbid_qty_display));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> ibid_qty_filter { get; } = new DoubleMinMax<Row>(r => r.ibid_qty_display, nameof(Row.ibid_qty_display));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> bid_qty_filter { get; } = new UIntMinMax<Row>(r => r.bid_qty, nameof(Row.bid_qty));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> dbid_qty_filter { get; } = new UIntMinMax<Row>(r => r.dbid_qty, nameof(Row.dbid_qty));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> ibid_qty_filter { get; } = new UIntMinMax<Row>(r => r.ibid_qty, nameof(Row.ibid_qty));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> ask_price_filter { get; } = new DoubleMinMax<Row>(r => r.ask_price, nameof(Row.ask_price));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> ask_qty_filter { get; } = new DoubleMinMax<Row>(r => r.ask_qty_display, nameof(Row.ask_qty_display));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> dask_qty_filter { get; } = new DoubleMinMax<Row>(r => r.dask_qty_display, nameof(Row.dask_qty_display));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> iask_qty_filter { get; } = new DoubleMinMax<Row>(r => r.iask_qty_display, nameof(Row.iask_qty_display));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> ask_qty_filter { get; } = new UIntMinMax<Row>(r => r.ask_qty, nameof(Row.ask_qty));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> dask_qty_filter { get; } = new UIntMinMax<Row>(r => r.dask_qty, nameof(Row.dask_qty));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> iask_qty_filter { get; } = new UIntMinMax<Row>(r => r.iask_qty, nameof(Row.iask_qty));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> last_price_filter { get; } = new DoubleMinMax<Row>(r => r.last_price, nameof(Row.last_price));
|
||||
public IDataGridColumnPersistentFilter<Row, string> last_spread_price_filter { get; } = new RegexFilterVM<Row>(r => r.last_price_from_spread.ToString(), nameof(Row.last_price_from_spread));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> last_qty_filter { get; } = new DoubleMinMax<Row>(r => r.last_qty_display, nameof(Row.last_qty_display));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> last_qty_filter { get; } = new UIntMinMax<Row>(r => r.last_qty, nameof(Row.last_qty));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> day_change_filter { get; } = new DoubleMinMax<Row>(r => r.day_change, nameof(Row.day_change));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> settlement_filter { get; } = new DoubleMinMax<Row>(r => r.settlement, nameof(Row.settlement));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> session_low_filter { get; } = new DoubleMinMax<Row>(r => r.session_low, nameof(Row.session_low));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> session_high_filter { get; } = new DoubleMinMax<Row>(r => r.session_high, nameof(Row.session_high));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> day_position_filter { get; } = new DoubleMinMax<Row>(r => r.day_position_display, nameof(Row.day_position_display));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> raw_day_position_filter { get; } = new DoubleMinMax<Row>(r => r.raw_day_position, nameof(Row.raw_day_position_display));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> working_bid_volume_filter { get; } = new DoubleMinMax<Row>(r => r.working_bid_volume_display, nameof(Row.working_bid_volume_display));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> working_ask_volume_filter { get; } = new DoubleMinMax<Row>(r => r.working_ask_volume_display, nameof(Row.working_ask_volume_display));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> volume_filter { get; } = new DoubleMinMax<Row>(r => r.volume, nameof(Row.volume));
|
||||
public IDataGridColumnPersistentFilter<Row, IntMinMaxState> day_position_filter { get; } = new IntMinMax<Row>(r => unchecked((int)r.day_position), nameof(Row.day_position));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> raw_day_position_filter { get; } = new DoubleMinMax<Row>(r => r.raw_day_position, nameof(Row.raw_day_position));
|
||||
public IDataGridColumnPersistentFilter<Row, IntMinMaxState> working_bid_volume_filter { get; } = new IntMinMax<Row>(r => r.working_bid_volume, nameof(Row.working_bid_volume));
|
||||
public IDataGridColumnPersistentFilter<Row, IntMinMaxState> working_ask_volume_filter { get; } = new IntMinMax<Row>(r => r.working_ask_volume, nameof(Row.working_ask_volume));
|
||||
public IDataGridColumnPersistentFilter<Row, ULongMinMaxState> volume_filter { get; } = new ULongMinMax<Row>(r => r.volume, nameof(Row.volume));
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
|
||||
|
||||
public IDataGridColumnPersistentFilter<Row, string> market_description_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.MarketDescription : "", nameof(Row.market_description));
|
||||
@@ -641,10 +639,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
OrderBookVM _mk_orderbook_vm_common(
|
||||
bts.shell.orderbook.FilterStates filters,
|
||||
IEnumerable<OrderBookGridView.ColumnKeys> hide_cols,
|
||||
IObservable<TableUpdate<OrderRecordTupleWithRoot, uint>> orders_source = null
|
||||
) {
|
||||
bts.shell.orderbook.FilterStates filters, IEnumerable<OrderBookGridView.ColumnKeys> hide_cols, IObservable<TableUpdate<OrderSecurityRootPair, uint>> orders_source = null) {
|
||||
var state = new bts.shell.orderbook.OrderBookStartupData(filters, bts.shell.orderbook.OrderBookStartupData.default_mini_grid, use_defaults: false);
|
||||
state.orderbook_grid.general = _hide_cols(state.orderbook_grid.general, hide_cols);
|
||||
var settings_man = new WindowSettingsManager<bts.shell.orderbook.OrderBookGridSettings>.DisconnectedImpl(state.orderbook_grid);
|
||||
|
||||
@@ -110,8 +110,7 @@ namespace mbts.ui.spark {
|
||||
});
|
||||
}
|
||||
|
||||
async void _show_order_ticket(
|
||||
SecurityMD security_md, double price = double.NaN, double size = 0) {
|
||||
async void _show_order_ticket(SecurityMD security_md, double price = double.NaN, uint size = 0) {
|
||||
var resources = new OrderTicketResources();
|
||||
resources.MergedDictionaries.Add(new AuxiliaryDataResources());
|
||||
|
||||
@@ -119,13 +118,7 @@ namespace mbts.ui.spark {
|
||||
var def = _model.factory.order(security_md.security, account);
|
||||
var calc_buttons = LadderSettings.Default.calculator_buttons;
|
||||
var ticket_vm = new NewOrderTicketVM(
|
||||
_model,
|
||||
security_md,
|
||||
_bus.subscribe(SparkTags.AccountsTag),
|
||||
new(def),
|
||||
price,
|
||||
size,
|
||||
Observable.Return(calc_buttons));
|
||||
_model, security_md, new(def), price, size, Observable.Return(calc_buttons));
|
||||
|
||||
_show_popup(ticket_vm, new OrderTicketHeader { bbo = ticket_vm.bbo }, resources);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ using mbts.utils;
|
||||
using Spark.Core;
|
||||
using Spark.Core.Data;
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
using Spark.Trading;
|
||||
|
||||
using System;
|
||||
@@ -68,8 +67,7 @@ namespace mbts.spark {
|
||||
_settings_hub = settings_hub;
|
||||
_settings_manager = settings_hub.create_mview_settings_manager(state.grid);
|
||||
Icon.Freeze();
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().MarketView ?
|
||||
"MarketView" : null;
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().market_view ? "MarketView" : null;
|
||||
_dispose = Disposable.Create(() => {
|
||||
Disposable.dispose(ref _subscriptions);
|
||||
_vm.Dispose();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace bts.shell.time_and_sales {
|
||||
using bts.api;
|
||||
using bts.api.domain;
|
||||
using bts.co2;
|
||||
using bts.utils;
|
||||
|
||||
@@ -29,19 +28,22 @@
|
||||
|
||||
readonly TradeMessageImmutable _trade;
|
||||
readonly GreeksTuple _greeks_tuple;
|
||||
readonly Func<SecurityImmutable?, double> _get_display_qty_divisor;
|
||||
double? _display_size;
|
||||
|
||||
public TimeAndSalesRowVM(
|
||||
int row_key,
|
||||
SecurityMD security_md,
|
||||
SecurityImmutable security,
|
||||
TradeMessageImmutable trade,
|
||||
double volume,
|
||||
GreeksTuple greeks_tuple
|
||||
) {
|
||||
GreeksTuple greeks_tuple,
|
||||
Func<SecurityImmutable?, double> get_display_qty_divisor) {
|
||||
this.row_key = row_key;
|
||||
this.security_md = security_md;
|
||||
this.security = security;
|
||||
_trade = trade;
|
||||
this.volume = volume;
|
||||
_greeks_tuple = greeks_tuple;
|
||||
_get_display_qty_divisor = get_display_qty_divisor ?? new(sec => 1);
|
||||
}
|
||||
|
||||
public event Action invalidated;
|
||||
@@ -49,16 +51,15 @@
|
||||
public int row_key { get; }
|
||||
public uint trade_key => _trade.Key;
|
||||
public uint security_key => security.Id;
|
||||
public SecurityMD security_md { get; }
|
||||
public SecurityImmutable security => security_md.security;
|
||||
public SecurityImmutable security { get; }
|
||||
|
||||
public DateTime time =>
|
||||
new LLDateTime((ulong)_trade.Time).to_datetime().ToLocalTime();
|
||||
|
||||
public double price => _trade.price();
|
||||
public double size => _trade.Size;
|
||||
public double display_size => _qty_display.to_display_value(size);
|
||||
public string size_text => _qty_display.format(size);
|
||||
public double display_size => _display_size ??
|
||||
(_display_size = size.safe_divide(_get_display_qty_divisor(security))).Value;
|
||||
public AggressorSide aggressor_side => _trade.AggressorSide;
|
||||
public double volume { get; }
|
||||
|
||||
@@ -72,8 +73,6 @@
|
||||
public double delta => _greeks_tuple.delta;
|
||||
public double display_delta => delta * 100;
|
||||
|
||||
TickDisplaySettingsImmutable _qty_display => security_md.quantity_display;
|
||||
|
||||
public void raise_invalidated() =>
|
||||
invalidated?.Invoke();
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
_refresh_filter,
|
||||
_refresh_sort,
|
||||
_refresh_selected_rows;
|
||||
readonly Func<SecurityImmutable?, double> _get_display_qty_divisor;
|
||||
IComparer<TimeAndSalesRowVM> _comparer_backing;
|
||||
Func<TimeAndSalesRowVM, bool> _filter_backing;
|
||||
ImmutableHashSet<TimeAndSalesRowVM> _selected_rows = ImmutableHashSet<TimeAndSalesRowVM>.Empty;
|
||||
@@ -69,9 +70,12 @@
|
||||
_filter_change_count,
|
||||
_sort_change_count;
|
||||
|
||||
public TimeAndSalesVM(IWindowSettingsManager<GridSettings> settings_manager) {
|
||||
public TimeAndSalesVM(
|
||||
IWindowSettingsManager<GridSettings> settings_manager,
|
||||
Func<SecurityImmutable?, double> get_display_qty_divisor) {
|
||||
_sort_manager = new ColumnSortManager(
|
||||
columns.WrapSelector(c => c as ISortableDIYGridColumn, null), ColKey.RowKey.ToString());
|
||||
_get_display_qty_divisor = get_display_qty_divisor ?? new(sec => 1);
|
||||
_comparer_backing = _sort_manager.BuildComparer(_get_column_comparer);
|
||||
_log = new LoggerContext(
|
||||
Log.Ui, $"Time And Sales VM ({Interlocked.Increment(ref _id_counter)})", null);
|
||||
@@ -82,7 +86,7 @@
|
||||
security_filter = new RegexFilterVM<TimeAndSalesRowVM>(
|
||||
r => r.security.ToString(), nameof(TimeAndSalesRowVM.security));
|
||||
price_filter = new DoubleMinMax<TimeAndSalesRowVM>(r => r.price);
|
||||
size_filter = new DoubleMinMax<TimeAndSalesRowVM>(r => r.display_size, nameof(TimeAndSalesRowVM.size));
|
||||
size_filter = new DoubleMinMax<TimeAndSalesRowVM>(r => r.size, nameof(TimeAndSalesRowVM.size));
|
||||
aggressor_filter = DiscreteValuesFilterVM.Create<TimeAndSalesRowVM, AggressorSide>(
|
||||
EnumHelper.get_values<AggressorSide>().ToArray(), r => r.aggressor_side);
|
||||
security_key_filter = new RegexFilterVM<TimeAndSalesRowVM>(r => r.security_key.ToString());
|
||||
@@ -108,7 +112,7 @@
|
||||
add_col(ColKey.Security, fmt_sec, ColWidth.Security, security_filter, FilterType.Regex);
|
||||
add_col(ColKey.Time, fmt_time, ColWidth.Time, time_filter, FilterType.TimeFilter);
|
||||
add_col(ColKey.Price, fmt_price, ColWidth.Price, price_filter, FilterType.DoubleMinMax, h_align: HorizontalAlignment.Right, get_fg: _get_aggressor_brush);
|
||||
add_col(ColKey.Size, r => r.size_text, ColWidth.Qty, size_filter, FilterType.UIntMinMax, h_align: HorizontalAlignment.Right, get_fg: _get_aggressor_brush);
|
||||
add_col(ColKey.Size, fmt_display_size, ColWidth.Qty, size_filter, FilterType.UIntMinMax, h_align: HorizontalAlignment.Right, get_fg: _get_aggressor_brush);
|
||||
add_col(ColKey.Aggressor, fmt_aggressor, ColWidth.Aggressor, aggressor_filter, FilterType.EnumValues, get_fg: _get_aggressor_brush);
|
||||
add_col(ColKey.SecurityKey, fmt_sec_key, ColWidth.SecurityKey, security_key_filter, FilterType.Regex);
|
||||
add_col(ColKey.Volume, fmt_volume, ColWidth.Volume, volume_filter, FilterType.DoubleMinMax, h_align: HorizontalAlignment.Right);
|
||||
@@ -140,6 +144,7 @@
|
||||
static string fmt_sec(TimeAndSalesRowVM row) => row.security.ToString();
|
||||
static string fmt_time(TimeAndSalesRowVM row) => row.time.ToString("h:mm:ss tt");
|
||||
static string fmt_price(TimeAndSalesRowVM row) => row.security.spec_or_default().format(row.price);
|
||||
static string fmt_display_size(TimeAndSalesRowVM row) => row.display_size.ToString();
|
||||
static string fmt_aggressor(TimeAndSalesRowVM row) => row.aggressor_side.ToString();
|
||||
static string fmt_volume(TimeAndSalesRowVM row) => new TickFactors(1, 100).format(row.volume);
|
||||
static string fmt_vol(TimeAndSalesRowVM row) => new TickFactors(1, 100).format(row.display_vol);
|
||||
@@ -351,7 +356,7 @@
|
||||
}
|
||||
var all_rows_bd = _all_rows.ToBuilder();
|
||||
foreach (var t in trades) {
|
||||
var r = _row_factory.create(t);
|
||||
var r = _row_factory.create(t, _get_display_qty_divisor);
|
||||
all_rows_bd.Add(r);
|
||||
if (_filter_backing is null || _filter_backing(r)) {
|
||||
rows.InsertSorted(r, _comparer);
|
||||
@@ -466,13 +471,16 @@
|
||||
// the row key must be unique to each row in the collection
|
||||
int _row_id_counter;
|
||||
|
||||
public TimeAndSalesRowVM create(TradeInfo trade_info) =>
|
||||
public TimeAndSalesRowVM create(
|
||||
TradeInfo trade_info,
|
||||
Func<SecurityImmutable?, double> get_display_qty_divisor) =>
|
||||
new(
|
||||
_row_id_counter++,
|
||||
trade_info.security_md,
|
||||
trade_info.security,
|
||||
trade_info.trade,
|
||||
trade_info.volume,
|
||||
trade_info.greeks_tuple ?? GreeksTuple.empty);
|
||||
trade_info.greeks_tuple ?? GreeksTuple.empty,
|
||||
get_display_qty_divisor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -41,6 +40,8 @@
|
||||
[DisallowNull]
|
||||
readonly IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
readonly bool _can_pause_updates;
|
||||
[DisallowNull]
|
||||
readonly Func<SecurityImmutable?, double> _get_display_qty_divisor;
|
||||
|
||||
TimeAndSalesVM _vm;
|
||||
SecurityCriteria _security_criteria_backing;
|
||||
@@ -57,6 +58,7 @@
|
||||
[DisallowNull] TimeAndSalesSettings state,
|
||||
[DisallowNull] IWindowSettingsManager<GridSettings> settings_manager,
|
||||
bool can_pause_updates,
|
||||
Func<SecurityImmutable?, double> get_display_qty_divisor,
|
||||
string thread_id)
|
||||
: base(WINDOW_DISPLAY_NAME, bus, factory, SparkCommands.ShowTimeAndSales.icon) {
|
||||
if (factory is null) {
|
||||
@@ -74,6 +76,7 @@
|
||||
_security_criteria_backing = _state.security_criteria;
|
||||
_settings_manager = settings_manager ?? throw new ArgumentNullException(nameof(settings_manager));
|
||||
_can_pause_updates = can_pause_updates;
|
||||
_get_display_qty_divisor = get_display_qty_divisor ?? new Func<SecurityImmutable?, double>(sec => 1);
|
||||
this.thread_id = thread_id;
|
||||
|
||||
Icon.Freeze();
|
||||
@@ -125,7 +128,7 @@
|
||||
}
|
||||
_menu_display_vm = new SparkMenuDisplayVM();
|
||||
_menu_display_vm.load_from(_state.menu_display);
|
||||
_vm = new(_settings_manager);
|
||||
_vm = new TimeAndSalesVM(_settings_manager, _get_display_qty_divisor);
|
||||
var current_sec_key_src = _observe_current_row().Select(r => {
|
||||
var result = ImmutableHashSet<uint>.Empty;
|
||||
if (r is not null) {
|
||||
@@ -190,12 +193,12 @@
|
||||
Disposable.Create(() => _vm.request_show_ladder -= show_ladder)
|
||||
};
|
||||
|
||||
var all_sec_mds_src = _bus.subscribe(SparkTags.SecuritiesTag);
|
||||
var sec_mds_src = security_criteria_src
|
||||
.StartWith(() => _security_criteria)
|
||||
.SelectSeries(sc => sc.subscribe_md_map(all_sec_mds_src));
|
||||
var trades_source = TradesSource.create(
|
||||
_consumer, sec_mds_src, _trade_poll_interval);
|
||||
_consumer,
|
||||
_bus.subscribe(SparkTags.SecuritiesTag)
|
||||
.Select(sec_md_map => MappedDictionary.create(sec_md_map, sec_md => sec_md.security)),
|
||||
security_criteria_src.StartWith(() => _security_criteria).Select(sc => new[] { sc }),
|
||||
_trade_poll_interval);
|
||||
trades_source = TradesSource.with_greeks(trades_source, _bus.subscribe(SparkTags.Greeks));
|
||||
trades_source = TradesSource.with_volume(
|
||||
trades_source, _bus.subscribe(SparkTags.SecuritiesTag));
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
|
||||
using Spark.Api.Domain;
|
||||
using Spark.Core.Disposables;
|
||||
|
||||
using System;
|
||||
@@ -22,15 +21,21 @@
|
||||
readonly IBus _bus;
|
||||
readonly MDConsumer _consumer;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
readonly ISecurityAuxiliaryDataModel _security_auxiliary_model;
|
||||
|
||||
IReadOnlyDictionary<uint, SecurityMD> _securities = new Dictionary<uint, SecurityMD>();
|
||||
|
||||
public TimeAndSalesWindowFactory(
|
||||
IShellModule module, IBus bus, MDConsumer consumer, UserSettingsHub settings_hub)
|
||||
: base(module, TimeAndSalesWindow.WINDOW_TYPE_NAME) {
|
||||
IShellModule module,
|
||||
IBus bus,
|
||||
MDConsumer consumer,
|
||||
UserSettingsHub settings_hub,
|
||||
ISecurityAuxiliaryDataModel security_auxiliary_model)
|
||||
: base(module, TimeAndSalesWindow.WINDOW_TYPE_NAME) {
|
||||
_bus = bus;
|
||||
_consumer = consumer;
|
||||
_settings_hub = settings_hub;
|
||||
_security_auxiliary_model = security_auxiliary_model;
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(s => _securities = s),
|
||||
bus.subscribe(SparkTags.ShowTimeAndSalesTag).OnDispatcher().Subscribe(show)
|
||||
@@ -39,7 +44,7 @@
|
||||
|
||||
public override Task<IShellWindow> load(TimeAndSalesSettings state) {
|
||||
var conf = Configuration.get<bts.config.TimeAndSalesOptions>();
|
||||
var thread_id = Configuration.get<bts.config.MultiThreadedWindows>().TimeAndSales ?
|
||||
var thread_id = Configuration.get<bts.config.MultiThreadedWindows>().time_and_sales ?
|
||||
TimeAndSalesWindow.WINDOW_TYPE_NAME : null;
|
||||
var settings_man = new WindowSettingsManager<GridSettings>(
|
||||
_settings_hub, state.grid, s => s.time_and_sales_grid, (s, g) => s.with(time_and_sales: g));
|
||||
@@ -49,7 +54,8 @@
|
||||
_consumer,
|
||||
state,
|
||||
settings_man,
|
||||
conf.CanPauseUpdates,
|
||||
conf.can_pause_updates,
|
||||
_security_auxiliary_model.get_display_quantity_divisor,
|
||||
thread_id);
|
||||
return Task.FromResult<IShellWindow>(win);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace bts.ui {
|
||||
SparkCommands.ShowGlobalAlerts,
|
||||
SparkCommands.ShowWorkspaces
|
||||
};
|
||||
if (Configuration.get<bts.config.toggles.ToastNotifications>().Enabled) {
|
||||
if (Configuration.get<bts.config.toggles.ToastNotifications>().enabled) {
|
||||
actions_list.Add(SparkCommands.ShowSettings);
|
||||
}
|
||||
actions = actions_list;
|
||||
|
||||
@@ -26,9 +26,9 @@ namespace bts {
|
||||
static bool _is_hidden(IShellModule module) {
|
||||
var props = Configuration.get<IShellModuleProps>();
|
||||
string name = module.GetType().Name;
|
||||
foreach (var p in props.Module) {
|
||||
if (p.Name.ToLower() == name.ToLower()) {
|
||||
return !p.Visible;
|
||||
foreach (var p in props.module) {
|
||||
if (p.name.ToLower() == name.ToLower()) {
|
||||
return !p.visible;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -8,7 +8,6 @@ using bts.spark;
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Core;
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -13,7 +13,6 @@ using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -44,7 +43,7 @@ namespace mbts.spark {
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
Icon.Freeze();
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().SyntheticManagement ?
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().synthetic_management ?
|
||||
"SyntheticManagement" : null;
|
||||
_dispose = Disposable.Create(() => {
|
||||
Disposable.dispose(ref _vm);
|
||||
|
||||
@@ -12,7 +12,6 @@ using mbts.ui.settings;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
|
||||
using Spark.Api.Orders;
|
||||
using Spark.Core.Collections;
|
||||
using Spark.Core.Disposables;
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ namespace bts.shell {
|
||||
string uristr = uri.ToString();
|
||||
string http_base =
|
||||
"http://{0}:{1}/".fmt(
|
||||
Configuration.get<bts.config.SystemConfig>().ConfigHost,
|
||||
Configuration.get<bts.config.SystemConfig>().ConfigPort);
|
||||
Configuration.get<bts.config.System>().config_host,
|
||||
Configuration.get<bts.config.System>().config_port);
|
||||
string temp = uristr;
|
||||
if (uristr.StartsWith("bts://m/")) {
|
||||
temp = uristr.Replace("bts://m/", http_base + "docs/");
|
||||
@@ -94,7 +94,7 @@ namespace bts.shell {
|
||||
emulation_key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(ke_name);
|
||||
}
|
||||
|
||||
var process_name = Configuration.get<bts.config.SystemConfig>().ProcessName;
|
||||
var process_name = Configuration.get<bts.config.System>().process_name;
|
||||
emulation_key.SetValue(process_name, 11001); // IE11
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -7,7 +7,6 @@ using mbts.ui.spark;
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
using Spark.ViewModel.Commands;
|
||||
|
||||
using System;
|
||||
@@ -44,8 +43,7 @@ namespace mbts.spark {
|
||||
: base(NAME, bus, factory, SparkCommands.ShowAlgoStudio.icon) {
|
||||
_model = model;
|
||||
_state = state;
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().AlgoStudio ?
|
||||
"AlgoStudio" : null;
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().algo_studio ? "AlgoStudio" : null;
|
||||
_dispose = Disposable.Create(_dispose_core);
|
||||
}
|
||||
|
||||
@@ -76,12 +74,12 @@ namespace mbts.spark {
|
||||
_commands.Add(new SparkCommandCommand("Add Algo", Icons.AddBoxes, _vm.add_definition_cmd));
|
||||
_commands.Add(new SparkCommandCommand("Clone Algo", Icons.CopyBoxes, _vm.clone_selected_definition_cmd));
|
||||
_commands.Add(new SparkCommandCommand("Save Algo", Icons.DiskFile, _vm.save_cmd) {
|
||||
show_in_toolbar = _ui_configuration.CanSaveSpecific
|
||||
show_in_toolbar = _ui_configuration.can_save_specific
|
||||
});
|
||||
_commands.Add(new SparkCommandCommand("Save All", Icons.Disk, ObservantAsyncCommand.create(_vm.save_all, out var save_all_observer)));
|
||||
_resources.Add(_vm.subscribe_dirty().OnDispatcher().Subscribe(save_all_observer));
|
||||
// undo/redo
|
||||
if (_ui_configuration.CanUndoRedo) {
|
||||
if (_ui_configuration.can_undo_redo) {
|
||||
// undo
|
||||
var undo_cmd = new ObservantCommand(() => _vm.selected_definition?.try_undo());
|
||||
var undo_spark_cmd = new SparkCommandCommand("Undo", Icons.UndoCircular, undo_cmd);
|
||||
|
||||
+101
-71
@@ -1,71 +1,101 @@
|
||||
namespace mbts.spark {
|
||||
using bts.api.orderman;
|
||||
using bts.api.orderman.algo;
|
||||
using bts.utils;
|
||||
|
||||
using Spark.Api.Orders;
|
||||
|
||||
using System;
|
||||
|
||||
// This is a core data type on the front end, so data validation is strict, as bad values may yield weird/difficult to track bugs.
|
||||
// Descriptive factory methods are favored over public constructors, as they are less likely to be misused.
|
||||
public readonly struct AlgoData : IEquatable<AlgoData> {
|
||||
public static readonly Func<AlgoData, AlgoKey> key_fn = a => a.key;
|
||||
|
||||
public readonly CustomOrderDefinitionImmutable value;
|
||||
public readonly string name;
|
||||
public readonly AlgoKey key;
|
||||
|
||||
AlgoData(CustomOrderDefinitionImmutable value, string name, AlgoKey key) {
|
||||
this.value = value;
|
||||
this.name = name;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public static AlgoData empty => from_custom_def(new CustomOrderDefinitionImmutable(new CustomOrderDefinition()));
|
||||
|
||||
/// <summary>
|
||||
/// This is the only <see cref="AlgoData"/> factory method that allows callers to explicitly specify a name.
|
||||
/// It should only be used within the context of creating, or renaming, studio algos.
|
||||
/// </summary>
|
||||
public static AlgoData create(CustomOrderDefinitionImmutable def, string name, AlgoKey key) =>
|
||||
new AlgoData(def, name, key);
|
||||
|
||||
/// <summary>
|
||||
/// Reconstructs <see cref="AlgoData"/> from a <see cref="CustomOrderDefinitionImmutable"/>, with a special caveat in the case of <see cref="CompositeOrderDefinition"/>:
|
||||
/// a fallback/default name is used instead of the composite algo's true name, which must be obtained from the <see cref="IAlgoStudioModel.definitions"/> stream.
|
||||
/// </summary>
|
||||
public static AlgoData from_custom_def(CustomOrderDefinitionImmutable def) {
|
||||
switch (def.CustomCase) {
|
||||
case CustomOrderDefinition.CustomOneofCase.Composite:
|
||||
string name = $"({def.CustomCase.abbreviate()}){def.Composite.Definition.CustomCase.abbreviate_algo_type()}";
|
||||
return new AlgoData(def.Composite.Definition, name, AlgoKey.from_composite_key(def.Composite.AlgoTemplateKey));
|
||||
case CustomOrderDefinition.CustomOneofCase.Dynamic:
|
||||
return new AlgoData(def, def.Dynamic.DynamicOrderType, AlgoKey.from_dynamic_type(def.Dynamic.DynamicOrderType));
|
||||
default:
|
||||
return new AlgoData(def, def.CustomCase.display_name(), AlgoKey.from_known_type(def.CustomCase));
|
||||
}
|
||||
}
|
||||
|
||||
public static AlgoData from_record(AlgoDefinitionRecordImmutable record) =>
|
||||
new AlgoData(record.Algo.Definition, record.Algo.Name, AlgoKey.from_composite_key(record.Key));
|
||||
|
||||
public static AlgoData create_unpersisted(CustomOrderDefinitionImmutable custom_def, string name) =>
|
||||
new AlgoData(custom_def, name, AlgoKey.create_unpersisted());
|
||||
|
||||
public bool Equals(AlgoData other) =>
|
||||
key == other.key &&
|
||||
name == other.name &&
|
||||
OrderEqualityComparer.custom_order_defi_comparer.Equals(value, other.value);
|
||||
|
||||
public override bool Equals(object obj) => EqualityComparer.equals_obj(this, obj);
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
key.GetHashCode(),
|
||||
EqualityComparer.safe_get_hash_code(name),
|
||||
value.GetHashCode());
|
||||
|
||||
public static bool operator ==(AlgoData a, AlgoData b) => a.Equals(b);
|
||||
public static bool operator !=(AlgoData a, AlgoData b) => !a.Equals(b);
|
||||
}
|
||||
}
|
||||
using bts.api.orderman;
|
||||
using bts.api.orderman.algo;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.spark.utilities;
|
||||
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.spark {
|
||||
// This is a core data type on the front end, so data validation is strict, as bad values may yield weird/difficult to track bugs.
|
||||
// Descriptive factory methods are favored over public constructors, as they are less likely to be misused.
|
||||
struct AlgoData : IEquatable<AlgoData> {
|
||||
public static readonly Func<AlgoData, AlgoKey> key_fn = a => a.key;
|
||||
|
||||
public readonly CustomOrderDefinitionImmutable value;
|
||||
public readonly string name;
|
||||
public readonly AlgoKey key;
|
||||
|
||||
AlgoData(CustomOrderDefinitionImmutable value, string name, AlgoKey key) {
|
||||
this.value = value;
|
||||
this.name = name;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public static AlgoData empty => from_custom_def(new CustomOrderDefinitionImmutable(new CustomOrderDefinition()));
|
||||
|
||||
/// <summary>
|
||||
/// This is the only <see cref="AlgoData"/> factory method that allows callers to explicitly specify a name.
|
||||
/// It should only be used within the context of creating, or renaming, studio algos.
|
||||
/// </summary>
|
||||
public static AlgoData create(CustomOrderDefinitionImmutable def, string name, AlgoKey key) =>
|
||||
new AlgoData(def, name, key);
|
||||
|
||||
/// <summary>
|
||||
/// Reconstructs <see cref="AlgoData"/> from a <see cref="CustomOrderDefinitionImmutable"/>, with a special caveat in the case of <see cref="CompositeOrderDefinition"/>:
|
||||
/// a fallback/default name is used instead of the composite algo's true name, which must be obtained from the <see cref="IAlgoStudioModel.definitions"/> stream.
|
||||
/// </summary>
|
||||
public static AlgoData from_custom_def(CustomOrderDefinitionImmutable def) {
|
||||
switch (def.CustomCase) {
|
||||
case CustomOrderDefinition.CustomOneofCase.Composite:
|
||||
string name = $"({OrderDataHelper.abbreviate_algo_type(def.CustomCase)}){OrderDataHelper.abbreviate_algo_type(def.Composite.Definition.CustomCase)}";
|
||||
return new AlgoData(def.Composite.Definition, name, AlgoKey.from_composite_key(def.Composite.AlgoTemplateKey));
|
||||
case CustomOrderDefinition.CustomOneofCase.Dynamic:
|
||||
return new AlgoData(def, def.Dynamic.DynamicOrderType, AlgoKey.from_dynamic_type(def.Dynamic.DynamicOrderType));
|
||||
default:
|
||||
return new AlgoData(def, def.CustomCase.display_name(), AlgoKey.from_known_type(def.CustomCase));
|
||||
}
|
||||
}
|
||||
|
||||
public static AlgoData from_record(AlgoDefinitionRecordImmutable record) =>
|
||||
new AlgoData(record.Algo.Definition, record.Algo.Name, AlgoKey.from_composite_key(record.Key));
|
||||
|
||||
public static AlgoData create_unpersisted(CustomOrderDefinitionImmutable custom_def, string name) =>
|
||||
new AlgoData(custom_def, name, AlgoKey.create_unpersisted());
|
||||
|
||||
public bool Equals(AlgoData other) =>
|
||||
key == other.key &&
|
||||
name == other.name &&
|
||||
OrderDataHelper.EqualsHelpers.custom_order_defi_comparer.Equals(value, other.value);
|
||||
|
||||
public override bool Equals(object obj) => EqualityComparer.equals_obj(this, obj);
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
key.GetHashCode(),
|
||||
EqualityComparer.safe_get_hash_code(name),
|
||||
value.GetHashCode());
|
||||
|
||||
public static bool operator ==(AlgoData a, AlgoData b) => a.Equals(b);
|
||||
public static bool operator !=(AlgoData a, AlgoData b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
struct AlgoDataVM : IEquatable<AlgoDataVM> {
|
||||
public static readonly Func<AlgoDataVM, AlgoID> id_fn = vm => AlgoID.from_algo_key(vm.data.key);
|
||||
|
||||
public readonly AlgoData data;
|
||||
public readonly AlgoDefinitionMetadata? meta;
|
||||
|
||||
public AlgoDataVM(AlgoData data, AlgoDefinitionMetadata? meta = null) {
|
||||
this.data = data;
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
public string name => data.name;
|
||||
public Brush badge_fill => OrderUtils.badge_fill(meta, OrderDataHelper.get_metadata(data.value.CustomCase));
|
||||
public AlgoDisplayGroup group => data.key.display_group();
|
||||
|
||||
public override string ToString() =>
|
||||
data.name;
|
||||
|
||||
public static AlgoDataVM from_data(AlgoData data, AlgoDefinitionMetadata? meta) =>
|
||||
new AlgoDataVM(data, meta);
|
||||
|
||||
public static AlgoDataVM from_data((AlgoData algo, AlgoDefinitionMetadata? meta) tup) =>
|
||||
new AlgoDataVM(tup.algo, tup.meta);
|
||||
|
||||
public bool Equals(AlgoDataVM other) => data == other.data && meta == other.meta;
|
||||
public override bool Equals(object obj) => obj is AlgoDataVM other && Equals(other);
|
||||
public override int GetHashCode() => EqualityComparer.aggregate_hash(data.GetHashCode(), meta.GetHashCode());
|
||||
}
|
||||
}
|
||||
Loaded 100 of 1374 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user