Compare commits

..
Author SHA1 Message Date
johncolvin-og ea4e599ac6 fix build: Fix a few path errors in publish target 2023-05-31 21:59:51 -04:00
Taha fd2ba70031 Culling the logs 2023-05-29 11:35:49 -04:00
Taha ab410c339f Order Tickets from the sheets are defaulted correctly now 2023-05-29 10:32:29 -04:00
John Colvin 9e3f16d8bc fix build: Update externals so prod builds in new docker 2023-04-05 11:45:24 -04:00
John Colvin a3f194d708 build (web): Upgrade web build to net7.0 2023-04-05 11:39:37 -04:00
John Colvin bf87c46fcd fix build: Remove inappropriate ThreadStatic attr
This has likely been incorrect for a while. The problem surfaced after
upgrading to net7.0, as the new framework emits a warning (CA2259) when the
attribute is associated with a non-static field.
2023-03-07 16:30:59 -05:00
John Colvin 222d4b266e build: Disable warning cs8981
This is a new warning, introduced in net7.0, that disallows lowercase
class names. Our spark_crypto.prod branch currently uses protoc v3.19.1,
which is incompatible with this new warning, as it generates
classes with lowercase names. We can re-enable the warning when we
upgrade protoc to 3.21.12 (this has been done in the api server branch).
2023-03-07 16:25:21 -05:00
John Colvin d593eaf058 build: Upgrade TargetFramework to net7.0
We've been using several net7.0 preview features for a while now. Now that a
stable net7.0 release is available, it is time upgrade officially. This also
aids the effort to build csharp on Linux, where these preview features didn't
work out of the box. Lastly, trying to make these preview features work would
be a waste of time, as namespaces/interfaces in the official release have
changed in a way that isn't backwards compatible w/ the preview features.
2023-03-07 16:17:39 -05:00
John Colvin 55d021ccb1 fix build (publish): Copy libspark.dll to publish dir
Bugs (fixed):
  * Spark.exe generated by the installer crashes immediately on launch because
    it cannot find the libspark.dll.

  Cause: Sure enough, libspark.dll isn't in the install folder
  (AppData\Roaming\bts\Spark)

* Build libspark explicitly
* Fix libspark.dll copy src path
2023-02-07 16:41:11 -05:00
1374 changed files with 134261 additions and 180186 deletions

No files matched your search

-1
View File
@@ -4,7 +4,6 @@ BinPackParameters: false
AlignAfterOpenBracket: AlwaysBreak
AlwaysBreakTemplateDeclarations: true
PenaltyReturnTypeOnItsOwnLine: 140
ReflowComments: false
---
Language: Proto
IndentWidth: 2
+1 -5
View File
@@ -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
+6
View File
@@ -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
View File
@@ -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()
-283
View File
@@ -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"
}
]
}
-83
View File
@@ -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
View File
@@ -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
-76
View File
@@ -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.
-27
View File
@@ -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
}
]
}
+1
View File
@@ -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.
-103
View File
@@ -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"
-64
View File
@@ -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.*
-119
View File
@@ -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"
-22
View File
@@ -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"
-26
View File
@@ -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")"
-250
View File
@@ -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"
-230
View File
@@ -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"
-64
View File
@@ -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
-233
View File
@@ -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"
+1
Submodule docker/windows added at de7d40c09d.
+1 -1
View File
@@ -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
+41
View File
@@ -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
View File
@@ -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>
+94
View File
@@ -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=&quot;protoc-gen-service=$(ProtocPluginBase)\plugin.cmd&quot; ^
--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>
-68
View File
@@ -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=&quot;protoc-gen-service=$(ProtocPlugin)&quot;</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; }
}
}
-45
View File
@@ -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;
+12 -20
View File
@@ -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())));
+4 -5
View File
@@ -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) {
+14 -36
View File
@@ -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).
@@ -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));
}
}
}
}
+1 -4
View File
@@ -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;
+1 -2
View File
@@ -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(
+2 -8
View File
@@ -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();
+1 -1
View File
@@ -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(
+44 -36
View File
@@ -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;
}
}
}
+1 -2
View File
@@ -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));
+107 -113
View File
@@ -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);
}
}
}
+1 -1
View File
@@ -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; }
}
}
+23 -36
View File
@@ -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);
@@ -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