Compare commits

..
Author SHA1 Message Date
John Colvin caad455784 Update externals
Removed fmt and range-3, since we get them from vcpkg now
2024-08-28 23:39:34 -04:00
John Colvin ac35c789ce Set up devcontainer for sparkd
With config from development branch
2024-08-28 23:26:28 -04:00
John Colvin 1361d7cf5d Port build configuration over from development branch
It's cleaner here, and makes transitioning between branches easier.
The new build_spark.py uses cmake --preset and dotnet build.
2024-08-28 23:25:04 -04:00
John Colvin 7b30dbf6b4 build: Update nuget packages in Spark.csproj 2024-08-28 00:05:46 -04:00
John Colvin 7c96b54101 fix build (spark client): Remove deps on preview features
Namely, the 'Sum' extension function for an IEnumerable of tuples.
2024-08-28 00:05:46 -04:00
John Colvin c6e4f090f5 build (windows): Update to c++ 20 2024-08-28 00:05:46 -04:00
John Colvin dee847a30c fix build (windows): Update libfmt to 10.2.1 2024-08-28 00:05:10 -04:00
John Colvin b0c69f7a02 fix build: Remove unnecessary cpp files from win build 2024-08-27 23:28:16 -04:00
John Colvin ae2e892311 build: Add vcpkg submodule 2024-08-27 13:14:14 -04:00
John Colvin 99e9dbb886 cleanup: Remove unused docker submodules 2024-08-27 13:13:16 -04:00
Taha Afzal a606a4749f Merging from spark_crypto.cme 2023-03-20 20:14:25 -04:00
Taha Afzal fa22cbd386 Some name changes and cleanup 2023-03-20 10:23:21 -04:00
Taha Afzal 6612a6d7e1 Created a moat, SpreaderFractionalQtyHelper, around SpreaderOrder that normalizes the data coming in and de-normalizes the data going out 2023-03-09 16:59:27 -05:00
Taha Afzal e6b2d6f0b8 Using EMExecutionGateway instead of CCAPIExecutionGateway 2023-03-08 10:25:56 -05:00
Taha Afzal b5828fbadc Merge branch 'spark_crypto.prod' into spark_crypto.ccapi.fractional_qty_md 2023-03-07 22:10:11 -05:00
Taha Afzal 3844d9f3ee Merging from prod 2023-03-07 21:55:17 -05:00
Taha Afzal 309b0e64b2 Adding better logs 2023-01-16 21:46:27 -05:00
Taha Afzal cfdca08cfe CME Template 8 2023-01-06 11:52:30 -05:00
Taha Afzal 51c89fb8cc Merge branch 'spark_crypto.prod' into spark_crypto.cme 2022-12-21 20:38:42 -05:00
Taha Afzal ded674d87c Enabling CME market data, and adding some new logs to track initialization of resources 2022-12-19 14:15:04 -05:00
Taha Afzal 648e6206ab Deactivating FTX market data 2022-11-21 15:41:47 -05:00
Taha Afzal ef07463a7d Deribit execution via CCAPI works now (account to key resolution needs to be done) 2022-11-11 23:34:24 -05:00
Taha Afzal 124817d60d Adding test that used CCAPI to send orders 2022-10-31 22:22:03 -04:00
Taha Afzal 828afb6173 TC2 can now show show fractional qtys 2022-10-24 22:02:21 -04:00
Taha Afzal 852782232c Merge branch 'spark_crypto.ccapi.fractional_qty_md' of bitbucket.org:bluetradesys/spark into spark_crypto.ccapi.fractional_qty_md 2022-10-24 21:57:53 -04:00
Taha Afzal add0c3412c PriceSizeMessage proto's size changed to float from uint32 2022-10-24 21:57:45 -04:00
Taha Afzal e20b7338c4 Merge branch 'spark_crypto.ccapi.fractional_qty_md' of bitbucket.org:bluetradesys/spark into spark_crypto.ccapi.fractional_qty_md 2022-10-24 13:45:19 -04:00
Taha Afzal b5da4b3129 Changing managed PriceSize structure's size into floats 2022-10-24 13:45:14 -04:00
Taha Afzal 9f7bafa423 Deribit market data is now using floating point qtys 2022-10-24 13:40:09 -04:00
Taha Afzal 95e99f4855 Changing the Size in PriceSize to float 2022-10-22 13:06:50 -04:00
Taha Afzal 4c4bc3d451 Reducing the logs and couts that were put while interfacing with CC 2022-10-21 13:36:21 -04:00
Taha Afzal 649be1e74e Enabling spot market dtaa in the UI 2022-10-21 09:13:49 -04:00
Taha Afzal 776c69d96c Enabling spot market data 2022-10-21 09:12:55 -04:00
Taha Afzal d371e09e72 Merging 2022-10-20 17:05:47 -04:00
Taha Afzal c035befe7d Removing temporary security creation from CCMarketDataListener 2022-10-20 14:06:06 -04:00
Taha Afzal be3bf93154 UI changes in SecurityExtensions to accomodate FTX 2022-10-20 14:01:01 -04:00
Taha Afzal 9ec5a2d394 Removing #include of a deleted generated header 2022-10-20 11:14:44 -04:00
Taha Afzal 0dbdc6cd62 Merging CCListener work from a different branch 2022-10-20 10:39:06 -04:00
Taha Afzal 0eeb37427f Fixing the build, unused variable 2022-10-19 22:46:15 -04:00
Taha Afzal 25cd86d44f QueryServer while handling mic's use switch for exchange instead of ifs 2022-10-19 17:36:02 -04:00
Taha Afzal bf6a232b5b IDGenerator is a thread-safe singleton now 2022-10-19 15:29:40 -04:00
John Colvin 47fdae4a71 chore: Remove unnecessary includes 2022-10-19 15:09:24 -04:00
Taha Afzal 32d1714cf6 CCMarketDataListener now listens to CC and writes data to shared memory 2022-10-19 13:55:52 -04:00
John Colvin df0018a8c0 chore: Remove unused files CCApiRequestor.h/cpp 2022-10-19 13:55:44 -04:00
John Colvin 200919d714 refactor: Return error if md cfg invalid rather than assert/crash 2022-10-19 13:55:44 -04:00
John Colvin 1a2a6b3fb2 fix: Fix typo in 'MarketDataRegistrationError' type name
The 'Registration' part was misspelled 'Regstration'
2022-10-19 13:55:44 -04:00
John Colvin ab5ec98d78 refactor: Hook up ccapi 'on_securities' callback
Clean up ccapi md code while we're at it
2022-10-19 13:55:44 -04:00
John Colvin fd47c3aca9 chore: Remove Binance-specific md impl from MarketDataGateway
It was creating needless clutter, as it was written before we knew about
ccapi. The new plan is to use a simple adaptor to interface with ccapi
for every exchange md.

Note: There are a handful of files related to the historic Binance impl
in the folder /lib/md/binance. I'm keeping them around for now, since
they are generally independent/isolated, and aren't causing clutter or
compromsing other code. Some of the utils there may come in handy down
the road.
2022-10-19 13:55:44 -04:00
John Colvin 24fb5d0041 refactor: Replace FTX ccapi impl w/ exchange agnostic one 2022-10-19 13:55:44 -04:00
John Colvin 74a4fa133c chore: Include <string> in MarketDataRegistrationError
Apparently <string> was included in all source files that referenced
MarketDataRegistrationError by coicidence (else the build would have been broken)
2022-10-19 13:55:44 -04:00
John Colvin febba9b3f7 build: Add -g to CMAKE_CXX_FLAGS_DEBUG
This flag tells compilers (both clang and gcc) to produce debug info
2022-10-19 13:53:29 -04:00
John Colvin ad2bc86688 chore: Sort includes 2022-10-12 22:11:47 -04:00
John Colvin dd84ec6720 fix build (g++-12 rel): Add '-Wnomaybe-uninitialized' flag
For some reason, this warning isn't suppressed in ccapi_date.h, which is
included as a system header (like all headers in externals). According to GCC's
documentation, compiler warnings shouldn't be emitted from system headers.

Disabling the warning for now so we can focus on bigger fish.
2022-10-12 20:58:24 -04:00
John Colvin 9d79490a20 fix build (client): Change 'WIN32' macros to '_WIN32'
Not sure exactly how this broke, but the WIN32 macro is no longer defined.
2022-10-12 20:13:18 -04:00
John Colvin 24059df312 refactor: Remove redundant optional.h
It was from a time when Windows std didn't provide the 'optional' header
2022-10-12 20:13:18 -04:00
John Colvin 41862474e9 build: Fix build_spark.py (broken since CMakePresets introduced)
Pass additional cmake opts through cli. Specifically, those that were
moved from CMakeLists.txt to CMakePrests.json
2022-10-12 20:13:18 -04:00
John Colvin ccbb1dd5c9 build (refactor): Add cmake_utils.py in py/core
So it can be referenced by both client/server build scripts
2022-10-12 20:13:18 -04:00
John Colvin e24c767c41 Update docker/linux for ubuntu 22.10 image 2022-10-12 20:13:18 -04:00
John Colvin 2b23c3d881 build (web): Update npm packages 2022-10-12 20:13:18 -04:00
John Colvin 0306da2a58 build (refactor): Remove unused cmake var SPARK_PYTHON 2022-10-12 19:29:51 -04:00
John Colvin 511856f989 build (refactor): Change some cmake cache vars to options 2022-10-12 19:29:51 -04:00
John Colvin 09ae3c23a5 build: Fix build_sparkd.py (broken since CMakePresets introduced)
Pass additional cmake opts through cli. Specifically, those that were
moved from CMakeLists.txt to CMakePrests.json
2022-10-12 19:29:51 -04:00
John Colvin 9dddecbc06 fix build: Update externals
Disables a few warnings-as-errors in btree source files
2022-10-12 19:29:51 -04:00
John Colvin 02e31ecae6 fix build (gcc-12, clang>=11): Update externals
Disables a few warnings-as-errors in catch2 source files
2022-10-12 19:29:51 -04:00
John Colvin 8722d80f2d build: Replace setup_builds w/ CMakePresets.json
Clean up CMakeLists a bit in the process:
   * Define more cmake vars in CMakePresets
   * Split linux/windows presets into separate files
2022-10-12 19:29:51 -04:00
John Colvin 8360c3f63f build (sparkd): Automatically create py venv in build script 2022-10-12 19:29:51 -04:00
John Colvin c310544783 build (sparkd): Pipe 'eval' cmd stdout/err back to shell
So we can actually see the build output
2022-10-12 19:29:51 -04:00
John Colvin e9ca7748a4 fix build: Compile with gcc/g++ (version agnostic)
We don't really need a strict dependency on a compiler version, though
best practice is to use the latest gcc (12).
2022-10-12 19:29:51 -04:00
John Colvin 83ee97419c tools (log): Fix 'pretty' script & rename 'show_last_run' 2022-10-12 19:29:51 -04:00
John Colvin b6a1dc61db fix build (gcc): Disable warning stringop-overread
This warning (as error) was introduced with g++ 12
2022-10-12 19:29:51 -04:00
John Colvin 4fb04aafd7 diag: Log emumba cfg parse error code 2022-10-12 19:29:51 -04:00
John Colvin 51cb7d91ba refactor: Include <limits> in sane_algorithm.h
Although the build was fine, many methods in this header file depend on 'limits'
so it is appropriate to include it. Presumably all references to such methods are
made from source files that include <limits> by coincidence.
2022-10-12 19:29:50 -04:00
John Colvin 14dd9e0204 fix build (gcc): Don't let switch case fall through
Even if the case is simply 'bts_assert(false)'
2022-10-12 19:29:50 -04:00
John Colvin ee330fdf6d build: Benign changes to remove errors in gcc 12 2022-10-12 19:29:50 -04:00
John Colvin 78b82db3e7 build: Benign changes to remove errors in clang 15 2022-10-12 19:29:50 -04:00
460 changed files with 134289 additions and 117626 deletions

No files matched your search

-18
View File
@@ -7,24 +7,6 @@
"commands": [
"dotnet-format"
]
},
"fable": {
"version": "4.0.0-theta-018",
"commands": [
"fable"
]
},
"fantomas": {
"version": "5.2.2",
"commands": [
"fantomas"
]
},
"fake-cli": {
"version": "6.0.0",
"commands": [
"fake"
]
}
}
}
+129
View File
@@ -0,0 +1,129 @@
FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
# Note installations that take a while (more than 10s)
RUN apt-get update
RUN apt-get install -y ca-certificates
RUN apt-get install -y ccache
RUN apt-get install -y curl
RUN apt-get install -y fish
RUN apt-get install -y gdb
# git-all: ~250s
RUN apt-get install -y git-all
RUN apt-get install -y gnupg
# liblttng-ust-dev: ~30s
RUN apt-get install -y liblttng-ust-dev
RUN apt-get install -y libssh2-1-dev
RUN apt-get install -y locales
RUN apt-get install -y ninja-build
# openssh-server: ~40s
RUN apt-get install -y openssh-server
RUN apt-get install -y pkg-config
RUN apt-get install -y software-properties-common
RUN apt-get install -y sudo
RUN apt-get install -y tzdata
RUN apt-get install -y vim
RUN apt-get install -y zip
# Python: ~30s
RUN apt-get install -y \
python3-pip \
python3-venv \
&& python3 -m pip install --upgrade pip
# GCC
ARG gcc_major=13
RUN \
add-apt-repository ppa:ubuntu-toolchain-r/test \
&& apt-get update \
&& apt-get install -y g++-$gcc_major
# Clang
ARG clang_major=17
RUN \
curl -fsSL https://apt.llvm.org/llvm-snapshot.gpg.key 2> /dev/null \
| gpg --dearmor - \
| tee /usr/share/keyrings/llvm-snapshot.gpg > /dev/null \
&& echo "deb [signed-by=/usr/share/keyrings/llvm-snapshot.gpg] http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-$clang_major main" \
| tee -a "/etc/apt/sources.list.d/llvm.list" \
&& apt-get update \
&& apt-get install -y \
clang-$clang_major \
clang-format-$clang_major \
clangd-$clang_major
# CMake
RUN \
curl -fsSL https://apt.kitware.com/keys/kitware-archive-latest.asc 2> /dev/null \
| gpg --dearmor - \
| tee /usr/share/keyrings/kitware-archive-keyring.gpg > /dev/null \
&& echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" \
| tee /etc/apt/sources.list.d/kitware.list > /dev/null \
&& apt-get update \
&& apt-get install -y cmake
# Dotnet
RUN apt-get update && apt-get install -y dotnet7
# Node
ARG node_major=18
RUN mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
| gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$node_major.x nodistro main" \
| tee /etc/apt/sources.list.d/nodesource.list \
&& apt-get update \
&& apt-get install -y nodejs
# Emumba (deps)
RUN apt-get update && apt-get -y install rapidjson-dev
# Emumba
COPY emumba /emumba
RUN dpkg -i /emumba/emumba-deribit-connector-3.3.1-Linux.LICENSE_DISABLED.deb \
&& rm -r /emumba
# Pcap
RUN apt-get update && apt-get install -y libpcap0.8-dev
RUN apt-get update && apt-get install -y \
docker.io \
docker-compose \
docker-compose-v2
# Set up default executables (update-alternatives)
RUN \
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-$clang_major 100; \
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-$clang_major 100; \
update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-$clang_major 100; \
update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-$clang_major 100; \
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-$gcc_major 100; \
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-$gcc_major 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-$clang_major; \
ln -s /usr/bin/ccache /usr/local/bin/clang++-$clang_major; \
ln -s /usr/bin/ccache /usr/local/bin/gcc; \
ln -s /usr/bin/ccache /usr/local/bin/g++
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"
RUN usermod -aG docker "$docker_user"
+43
View File
@@ -0,0 +1,43 @@
{
"dockerFile": "Dockerfile",
// Creates local dir to mount to HOME in docker
"initializeCommand": "mkdir -p ${localEnv:HOME}/.bts/devcontainer-mounts/${devcontainerId}",
"hostRequirements": {
"memory": "4gb",
"storage": "6gb"
},
"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": [
"--shm-size",
"2gb",
"--network",
"host"
],
"mounts": [
"source=${localEnv:HOME}/.bts/devcontainer-mounts/${devcontainerId},target=/home/bts,type=bind",
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
],
"customizations": {
"vscode": {
"settings": {
"clangd.arguments": [
"--compile-commands-dir=${workspaceFolder}/build/clang/debug"
]
},
"extensions": [
"eamodio.gitlens",
"llvm-vs-code-extensions.vscode-clangd",
"ms-vscode.cmake-tools",
"ms-vscode.cpptools"
]
}
}
}
+1 -1
View File
@@ -42,7 +42,7 @@ trace_data/
/.ignore
*.rej
.clangd/
*CMakeDeps.cmake
*CMakeDeps*.cmake
/sniper.ipynb
/src/.clang-format
/src/server/sparkd/cmds/spartan/analysis/.ipynb_checkpoints
+3 -6
View File
@@ -1,9 +1,6 @@
[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
[submodule "vcpkg"]
path = vcpkg
url = https://github.com/microsoft/vcpkg.git
+25 -76
View File
@@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.12)
project(spark)
set(CMAKE_CXX_STANDARD 20)
if(WIN32)
set(platform "windows")
@@ -13,101 +14,49 @@ 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/${CMAKE_BUILD_TYPE}")
set(EXTERNALS_BIN "${EXTERNALS_ROOT}/bin")
set(EXTERNALS_INCLUDE "${EXTERNALS_ROOT}/include")
set(EXTERNALS_LIB "${EXTERNALS_ROOT}/lib/$<CONFIG>")
set(BUILD_DOCS OFF CACHE BOOL "Build documentation")
set(BUILD_SDK OFF CACHE BOOL "Build the sdk")
set(BUILD_WEB ON CACHE BOOL "Build the web server")
set(EXPORT_MD_CONSUMER OFF CACHE BOOL "Export (extern) C functions for MD consumer interop")
if (WIN32)
set(BUILD_SPARKD OFF CACHE BOOL "Build sparkd")
set(BUILD_WEB OFF CACHE BOOL "Build the web server")
else()
set(BUILD_SPARKD ON CACHE BOOL "Build sparkd")
set(BUILD_WEB ON CACHE BOOL "Build the web server")
endif()
include_directories(BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
link_directories(${SPARK_LIB})
include_directories(BEFORE SYSTEM ${EXTERNALS_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
link_directories(${EXTERNALS_LIB})
add_compile_definitions(EXPORT_MD_CONSUMER="${EXPORT_MD_CONSUMER}")
if(WIN32)
# 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)
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/${CMAKE_BUILD_TYPE}/sparkd")
add_compile_definitions(SPARK_ENABLE_CCAPI)
add_compile_definitions(CCAPI_ENABLE_SERVICE_MARKET_DATA)
add_compile_definitions(CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_FTX)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_DERIBIT)
add_compile_definitions(CCAPI_ENABLE_LOG_WARN)
# 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
add_subdirectory(src/server/sparkd)
if (BUILD_SPARKD)
add_subdirectory(src/server/sparkd)
add_subdirectory(src/data_files)
endif()
add_subdirectory(src/lib)
if (BUILD_WEB)
add_subdirectory(src/web)
endif()
add_subdirectory(doc/user)
if(BUILD_DOCS)
add_subdirectory(doc/user)
endif()
if(BUILD_SDK)
add_subdirectory(src/sdk)
endif()
add_subdirectory(src/data_files)
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)
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()
+215
View File
@@ -0,0 +1,215 @@
{
"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/${presetName}",
"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++20 /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_CXX_STANDARD": "20",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}",
"CMAKE_SHARED_LINKER_FLAGS_DEBUG": "/DEBUG",
"CMAKE_SHARED_LINKER_FLAGS_RELEASE": "/OPT:REF /OPT:ICF /LTCG",
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
"EXTERNALS_ROOT": "${sourceDir}/externals/windows",
"CONFIG_DEFAULTS_PATH": "config/defaults/*.conf",
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0;${sourceDir}/build/$<CONFIG>/bin/net7.0-windows",
"EXPORT_MD_CONSUMER": {
"type": "BOOL",
"value": "ON"
}
}
},
{
"name": "sparkd",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"platform": "",
"CMAKE_CXX_STANDARD": "20",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}",
"CMAKE_EXE_LINKER_FLAGS": "-Wno-undef",
"CONFIG_DEFAULTS_PATH": "config/defaults/*.conf",
"EXTERNALS_ROOT": "${sourceDir}/externals/linux",
"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": "Info"
}
},
{
"name": "sparkd-release",
"inherits": "sparkd",
"hidden": true,
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CCAPI_LOG_LEVEL": "Warn"
}
},
{
"name": "sparkd-debug-clang",
"inherits": "sparkd-debug",
"displayName": "Sparkd (Debug | Clang)",
"description": "Debug build compiled w/ clang",
"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++20 -fdiagnostics-color -DDEBUG"
}
},
{
"name": "sparkd-debug-gcc",
"inherits": "sparkd-debug",
"displayName": "Sparkd (Debug | Gcc)",
"description": "Debug build compiled w/ gcc",
"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++20 -fdiagnostics-color -Wno-stringop-overread -DDEBUG"
}
},
{
"name": "sparkd-release-clang",
"inherits": "sparkd-release",
"displayName": "Sparkd (Release | Clang)",
"description": "Release build compiled w/ clang",
"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++20 -fdiagnostics-color -fno-omit-frame-pointer -DNDEBUG -O3"
}
},
{
"name": "sparkd-release-gcc",
"inherits": "sparkd-release",
"displayName": "Sparkd (Release | Gcc)",
"description": "Release build compiled w/ gcc",
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++",
"CMAKE_CXX_FLAGS_RELEASE": "-fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers -Wno-stringop-overflow --std=c++20 -fdiagnostics-color -Wno-stringop-overread -Wno-maybe-uninitialized -fno-omit-frame-pointer -DNDEBUG -O3"
}
}
],
"buildPresets": [
{
"name": "spark",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"hidden": true
},
{
"name": "spark-debug",
"displayName": "Spark (Debug)",
"inherits": "spark",
"configurePreset": "spark",
"configuration": "Debug"
},
{
"name": "spark-release",
"displayName": "Spark (Release)",
"inherits": "spark",
"configurePreset": "spark",
"configuration": "Release"
},
{
"name": "sparkd",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"hidden": true
},
{
"name": "sparkd-debug-clang",
"configurePreset": "sparkd-debug-clang",
"displayName": "Sparkd (Debug | Clang)",
"inherits": "sparkd"
},
{
"name": "sparkd-debug-clang-j6",
"configurePreset": "sparkd-debug-clang",
"displayName": "Sparkd (Debug | Clang | jobs=6)",
"inherits": "sparkd",
"jobs": 6
},
{
"name": "sparkd-debug-gcc",
"configurePreset": "sparkd-debug-gcc",
"displayName": "Sparkd (Debug | Gcc)",
"inherits": "sparkd"
},
{
"name": "sparkd-release-clang",
"configurePreset": "sparkd-release-clang",
"displayName": "Sparkd (Release | Clang)",
"inherits": "sparkd"
},
{
"name": "sparkd-release-gcc",
"configurePreset": "sparkd-release-gcc",
"displayName": "Sparkd (Release | Gcc)",
"inherits": "sparkd"
}
]
}
+39
View File
@@ -0,0 +1,39 @@
include(${CMAKE_SOURCE_DIR}/cmake/PackBasic.cmake)
include(GNUInstallDirs)
function(install_basic)
set(_options)
set(_one_value_args)
set(_multi_value_args HEADERS)
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
if (_ARGS_HEADERS)
set(${PROJECT_NAME}_HEADERS ${_ARGS_HEADERS})
else()
file(
GLOB_RECURSE ${PROJECT_NAME}_HEADERS
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
*.h
)
endif()
target_sources(${PROJECT_NAME}
PUBLIC
FILE_SET HEADERS FILES ${${PROJECT_NAME}_HEADERS})
install(
TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}Targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(
EXPORT ${PROJECT_NAME}Targets
FILE ${PROJECT_NAME}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
pack_basic()
endfunction()
+36
View File
@@ -0,0 +1,36 @@
include(CMakePackageConfigHelpers)
function(pack_basic)
find_package(Git QUIET)
if(GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --tags --always --dirty
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE PROJECT_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
if(NOT GIT_FOUND OR PROJECT_VERSION STREQUAL "")
set(PROJECT_VERSION "0.0.0")
endif()
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)
install(
FILES
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
endfunction()
+12
View File
@@ -0,0 +1,12 @@
function(print_list var)
set(CMAKE_MESSAGE_INDENT "")
set(_var_str "")
foreach(item IN LISTS ${var})
set(_var_str "${_var_str}\n ${item}")
endforeach()
if ("${ARGN}" STREQUAL "VERBOSE")
message(VERBOSE "***${var}:${_var_str}")
else()
message("***${var}:${_var_str}")
endif()
endfunction()
+2 -2
View File
@@ -1,5 +1,5 @@
set(Protobuf_PROTOC_EXECUTABLE "${SPARK_BIN}/protoc")
set(PROTOBUF_INCLUDE_DIRS "${SPARK_INCLUDE}")
set(Protobuf_PROTOC_EXECUTABLE "${EXTERNALS_BIN}/protoc")
set(PROTOBUF_INCLUDE_DIRS "${EXTERNALS_INCLUDE}")
set(PROTOBUF_STATIC_LIB "libprotobuf")
function(Protobuf_Generate_Cpp SRCS HDRS)
File renamed without changes.
Submodule docker/linux deleted from 6537812262.
Submodule docker/windows deleted from de7d40c09d.
-38
View File
@@ -1,38 +0,0 @@
pipeline {
agent {
label 'lab-main'
}
stages {
stage('Build') {
steps {
echo 'Building..'
if (!fileExists('.venv')) {
echo 'Creating python virtual env'
sh 'python3 -m venv .venv'
}
sh """
. .venv/bin/activate
pip install -r requirements.txt
python tools/py/build_sparkd.py --compiler gcc --configuration release
"""
}
}
stage('Installing') {
steps {
echo 'Installing..'
sh """
cd build/gcc/release
cmake --build . --target install
"""
sh 'tar -czf install.tar.gz install'
}
}
stage('Archiving') {
steps {
echo 'Archiving..'
archiveArtifacts artifacts: 'install.tar.gz', followSymlinks: false, onlyIfSuccessful: true
}
}
}
}
-5
View File
@@ -4,8 +4,6 @@ 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("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}"
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}") = "sparkcmd", "src\client\sparkcmd\sparkcmd.csproj", "{387566C6-C6F6-412C-A30C-9855BBCB4816}"
@@ -13,9 +11,6 @@ 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_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
ProjectSection(ProjectDependencies) = postProject
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25} = {FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_test", "src\client\spark_test\spark_test.csproj", "{ECD78CC5-138F-4809-A764-2358C2AC2E82}"
EndProject
@@ -3,6 +3,7 @@ namespace mbts.spark {
using bts.api;
using bts.api.analytics;
using bts.collections;
using bts.reactive;
using bts.utils;
using mbts;
@@ -17,7 +18,6 @@ namespace mbts.spark {
using Spark.Core;
using Spark.Core.Collections;
using Spark.Core.Disposables;
using Spark.Core.Reactive;
using Spark.ViewModel.Commands;
using System;
@@ -1,120 +0,0 @@
namespace mbts.spark {
using bts;
using bts.api;
using mbts;
using mbts.analytics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
// For each vs, it sends the vol surface calc request (_do_periodic_auto_fit)
// and then commits the response
public class SingleVolSurfaceAutoFitSource : IDisposable {
private readonly ThreadedBlackboard _bb;
private readonly int _vs_id;
private readonly ITag<VolSurfaceInput> _vs_input_tag;
private readonly ITag<VolSurfaceCalculationResponse> _vs_calc_response_tag;
private DateTime _last_req_time = DateTime.MinValue;
private TimeSpan _req_period = TimeSpan.MaxValue;
private int _req_id = 0;
public SingleVolSurfaceAutoFitSource(int vs_id, ThreadedBlackboard bb) {
_bb = bb;
_vs_id = vs_id;
_vs_input_tag = SparkTags.VolSurfaceCommittedInputTag(vs_id);
_vs_calc_response_tag = SparkTags.VolSurfaceAutoCalculationResponseTag(vs_id);
_bb.subscribe(_vs_input_tag);
_bb.subscribe(_vs_calc_response_tag);
_bb.subscribe(SparkTags.CurrentUser);
_bb.data_callback += _on_bb_data;
_bb.timer_callback += _on_bb_timer;
}
private void _on_bb_data(object tag, object data) {
if (tag.Equals(_vs_input_tag))
_possibly_autofit();
else if (tag.Equals(_vs_calc_response_tag)) {
VolSurfaceCalculationResponse calc_resp = data as VolSurfaceCalculationResponse;
bts.Log.Gen.info($"SingleVolSurfaceAutoFitSource._on_bb_data vs_id: {_vs_id} " +
$"received calculation response");
_bb.post(SparkTags.VolSurfaceCommitTag(_vs_id),
calc_resp.volsurface_calculation_output.volsurface_input);
}
}
private bool _user_has_vs_control() {
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
string current_user = _bb.read_state(SparkTags.CurrentUser).name;
return vs_input != null &&
vs_input.surface_settings.core_settings.vs_manager == current_user;
}
private void _on_bb_timer() {
_possibly_autofit();
}
private void _possibly_autofit() {
var auto_fit_period = _get_auto_fit_periodic();
if (!auto_fit_period.HasValue)
return;
if (DateTime.Now - _last_req_time <= auto_fit_period)
return;
if (!_user_has_vs_control())
return;
// auto fit here
bts.Log.Gen.info($"SingleVolSurfaceAutoFitSource vs_id: {_vs_id} requesting fit " +
$"last_req_time: {_last_req_time} period: {auto_fit_period.Value}");
_send_request();
}
private Nullable<TimeSpan> _get_auto_fit_periodic() {
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
if (vs_input == null)
return null;
var fit_settings =
VolSurfaceDataFunctions.get_market_fit_settings(vs_input.surface_settings);
if (fit_settings == null)
return null;
var auto_fit_setting = fit_settings.vs_auto_fit_settings;
if (auto_fit_setting == null)
return null;
if (auto_fit_setting.auto_fit_region == VolSurfaceEnums.VolCurveFitRegion.None)
return null;
var time_based_fit_setting = auto_fit_setting.time_based_settings;
if (time_based_fit_setting == null)
return null;
if (!time_based_fit_setting.period_seconds.HasValue)
return null;
return TimeSpan.FromSeconds(time_based_fit_setting.period_seconds.Value);
}
private void _send_request() {
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
if (vs_input == null)
return;
VolCurveMarketFitAction mf_action = new VolCurveMarketFitAction(
VolCurveMarketFitAction.ActionType.FitWholeCurve, null, false);
VolCurveAction vc_action = new VolCurveAction(mf_action);
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
vs_input.surface_parameters.curve_parameters_map.ToDictionary(
i => i.Item1,
i => vc_action
);
VolSurfaceCalculationRequest req = new VolSurfaceCalculationRequest(vs_input,
_req_id++, action_map, true, true);
_last_req_time = DateTime.Now;
_bb.post(SparkTags.VolSurfaceAutoCalculationRequestTag(_vs_id), req);
}
#region IDisposable
public void Dispose() {
_bb.data_callback -= _on_bb_data;
_bb.timer_callback -= _on_bb_timer;
}
#endregion
}
}
@@ -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);
}
@@ -4,7 +4,6 @@
using bts.api.analytics;
using mbts.analytics;
using mbts.ui;
using Spark.Core.Protobuf;
@@ -14,7 +13,6 @@
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Windows;
// For a single VolSurface this class
// 1. Listents to commmits and calls the service to persist data
@@ -26,6 +24,7 @@
private readonly ITag<VolSurfaceInput> _in_bb_committ_tag;
private readonly AnalyticsService _analytics_service;
private readonly HighPerformanceTimer _timer = new HighPerformanceTimer();
private readonly string _user;
private readonly List<IDisposable> _disposables = new List<IDisposable>();
private IDictionary<UInt64, VolSurfaceInputSnapshot> _snp_shot_map = new
Dictionary<UInt64, VolSurfaceInputSnapshot>();
@@ -36,10 +35,10 @@
volsurface_id));
_volsurface_id = volsurface_id;
_bb = threaded_bb;
_user = "TEST_USER"; // TODO
_in_bb_committ_tag = SparkTags.VolSurfaceCommitTag(volsurface_id);
_analytics_service = analytics_service;
_bb.subscribe(_in_bb_committ_tag);
_bb.subscribe(SparkTags.CurrentUser);
_bb.data_callback += _on_bb_data;
var vs_sub = _analytics_service.subscribei_volsurfaces()
.ObserveOn(SynchronizationContext.Current)
@@ -55,10 +54,8 @@
private void _on_vss(bts.api.core.Update<VolSurfaceEntryImmutable, UInt64> update) {
IEnumerable<VolSurfaceEntryImmutable> all_vss = update.all;
var this_vs_vss = all_vss.Where(vs => vs.VsId == _volsurface_id);
bts.Log.Gen.info(
$"SingleVolSurfaceGateway._on_vss vs_count: {this_vs_vss.Count()} " +
$"all: {update.all.Count()} added: {update.added.Count()} " +
$"updated: {update.changed.Count()} removed: {update.removed.Count()}");
bts.Log.Gen.info(String.Format("SingleVolSurfaceGateway._on_vss count:{0} vs_count:{1}",
all_vss.Count(), this_vs_vss.Count()));
if (this_vs_vss.Count() == 0) {
return;
}
@@ -86,39 +83,22 @@
static VolSurfaceInput _to_vs_input(VolSurfaceEntryImmutable vs) =>
ProtobufSerializationHelper.deserialize<VolSurfaceInput>(vs.Volsurface.ToByteArray());
// private void _show_save_error(string message) {
// BTSMessageBox.Show(message, "Vol Save Error", BTSMessageBoxButtons.OK);
// }
private void _on_bb_data(object tag, object data) {
if (_in_bb_committ_tag.Equals(tag)) {
try {
_timer.start();
VolSurfaceInput vs_input = data as VolSurfaceInput;
var user_tup = _bb.read_state(SparkTags.CurrentUser);
string current_user = user_tup.name;
string vs_manager = vs_input.surface_settings.core_settings.vs_manager;
if (current_user != vs_manager) {
string msg = $"User {current_user} does not have the permission to save vols. " +
$"Permission can be obtained via the grid context menu item 'Acquire ownership from {vs_manager}'";
BTSMessageBox.Show(msg, "Vol Save Error", BTSMessageBoxButtons.OK);
return;
}
_sanitize_input(ref vs_input);
vs_input = VolSurfaceDataFunctions.unset_driver_overrides(vs_input);
VolSurfaceUtilityFunctions.save_volsurface(vs_input, _analytics_service, current_user);
vs_input.saver_login = _user;
VolSurfaceUtilityFunctions.save_volsurface(vs_input, _analytics_service, _user);
_timer.stop();
Log.Gen.info(String.Format(
"SingleVolSurfaceGateway._on_bb_data Time to commit volsurface {0} {1} msecs",
_volsurface_id, _timer.lastIntervalTime));
var archived_theos = _get_theos();
if (archived_theos != null) {
_timer.start();
if (archived_theos != null)
_analytics_service.archive_theos(archived_theos);
_timer.stop();
Log.Gen.info($"SingleVolSurfaceGateway._on_bb_data archiving theos size: " +
$"{archived_theos.SecTheo.Count} time: {_timer.lastIntervalTime} msecs");
}
} catch (Exception ex) {
Log.Gen.warn(ex.ToString());
_bb.post(SparkTags.MessageDisplayTag,
@@ -36,11 +36,7 @@ namespace mbts.spark {
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Committed, _vs_id, _bb,
a_syms => new SingleVolSurfaceCommittedOutputRetriever(_vs_id, _bb, a_syms)),
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Combined, _vs_id, _bb,
a_syms => new SingleVolSurfaceRequestCalculator(
SingleVolSurfaceRequestCalculator.RequestType.UI, _vs_id, a_syms, _bb)),
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Combined, _vs_id, _bb,
a_syms => new SingleVolSurfaceRequestCalculator(
SingleVolSurfaceRequestCalculator.RequestType.AUTO, _vs_id, a_syms, _bb)),
a_syms => new SingleVolSurfaceRequestCalculator(_vs_id, a_syms, _bb)),
new SingleVolSurfaceMarketImpliedVolSource(_vs_id, _bb),
// new SingleVolSurfaceTheoSource(_vs_id, _bb),
// TODO: suspending heatmaps for now (12/24/21)
@@ -48,7 +44,6 @@ namespace mbts.spark {
// server work that can optionally be done on the client
new SingleVolSurfaceDataSource (SingleVolSurfaceDataSource.SymbolListType.Committed, _vs_id, _bb,
a_syms => new SingleVolSurfaceInterpGreeksPublisher (_vs_id, _bb, a_syms)),
new SingleVolSurfaceAutoFitSource(_vs_id, _bb),
};
}
@@ -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.diag($"SingleVolSurfaceNonVolDataSource vs_id:{_vs_id} " +
bts.Log.Gen.info($"SingleVolSurfaceNonVolDataSource vs_id:{_vs_id} " +
"publishing VolSurfaceNonVolData");
_bb.post(_out_bb_vs_nonvol_data_tag,
new VolSurfaceNonVolData(
@@ -18,16 +18,10 @@ namespace mbts.spark {
// Pubs :
// 1. Calc Resp
public class SingleVolSurfaceRequestCalculator : IDisposable {
public enum RequestType {
UI = 0,
AUTO
}
private readonly RequestType _request_type = RequestType.UI;
private readonly int _vs_id;
private readonly HashSet<string> _symbols;
private readonly ThreadedBlackboard _bb;
private readonly ITag<VolSurfaceCalculationRequest> _in_bb_request_tag;
private readonly ITag<VolSurfaceCalculationRequest> _in_bb_auto_request_tag;
private readonly ITag<VolSurfaceNonVolData> _in_bb_nonvol_data_tag;
private readonly ITag<VolSurfaceStandardTime> _in_bb_vs_standard_time_tag;
private readonly ITag<VolSurfaceEnums.MarketSide> _in_bb_market_impliedvol_side_tag;
@@ -36,22 +30,18 @@ namespace mbts.spark {
/// data
private VolSurfaceOutput _last_output;
public SingleVolSurfaceRequestCalculator(RequestType request_type,
int vs_id, IEnumerable<string> symbols,
public SingleVolSurfaceRequestCalculator(int vs_id, IEnumerable<string> symbols,
ThreadedBlackboard threaded_bb) {
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator cons vs_id: {vs_id} request_type: {request_type}");
_request_type = request_type;
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator cons vs_id: {vs_id}");
_vs_id = vs_id;
_symbols = new HashSet<string>(symbols);
_bb = threaded_bb;
_in_bb_request_tag = SparkTags.VolSurfaceCalculationRequestTag(vs_id);
_in_bb_auto_request_tag = SparkTags.VolSurfaceAutoCalculationRequestTag(vs_id);
_in_bb_nonvol_data_tag = SparkTags.VolSurfaceNonVolDataTag(vs_id);
_in_bb_vs_standard_time_tag = SparkTags.VolSurfaceStandardTimeTag(vs_id);
_in_bb_market_impliedvol_side_tag = SparkTags.VolSurfaceMarketImpliedVolSide(vs_id);
_in_bb_dirty_tag = SparkTags.VolSurfaceDirtyStateTag(vs_id);
_bb.subscribe(_in_bb_request_tag);
_bb.subscribe(_in_bb_auto_request_tag);
_bb.subscribe(_in_bb_nonvol_data_tag);
_bb.subscribe(_in_bb_vs_standard_time_tag);
_bb.subscribe(_in_bb_vs_standard_time_tag);
@@ -61,21 +51,11 @@ namespace mbts.spark {
}
private void _on_bb_data(object tag, object data) {
switch (_request_type) {
case RequestType.UI: {
if (_in_bb_request_tag.Equals(tag) || _in_bb_dirty_tag.Equals(tag))
_recompute_and_publish(true);
else if (_in_bb_nonvol_data_tag.Equals(tag) || _in_bb_vs_standard_time_tag.Equals(tag))
_recompute_and_publish(false);
break;
}
case RequestType.AUTO: {
if (tag.Equals(_in_bb_auto_request_tag)) {
_serve_auto_request();
}
break;
}
}
if (_in_bb_request_tag.Equals(tag) || _in_bb_dirty_tag.Equals(tag))
_recompute_and_publish(true);
else if (_in_bb_nonvol_data_tag.Equals(tag) || _in_bb_vs_standard_time_tag.Equals(tag))
_recompute_and_publish(false);
}
private void _recompute_and_publish(bool is_new_request) {
@@ -100,7 +80,7 @@ namespace mbts.spark {
nonvol_data.driver_price_map,
vs_standard_time, true);
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
_post_process_action(_bb, _vs_id, effective_req, aux_data_map);
_post_process_action(effective_req, aux_data_map);
_last_output = GeneralVolSurfaceCalculator.compute(effective_req.volsurface_input,
nonvol_data.contracts,
aux_data_map,
@@ -109,49 +89,16 @@ namespace mbts.spark {
effective_req.cascade_up,
effective_req.cascade_down);
_timer.stop();
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._recompute_and_publish vs_id: {_vs_id} " +
$"time: {_timer.lastIntervalTime}");
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._recompute_and_publish time: " +
$"{_timer.lastIntervalTime}");
_bb.post(SparkTags.VolSurfaceCalculationResponseTag(_vs_id),
new VolSurfaceCalculationResponse(_last_output, effective_req.request_id));
}
}
}
private void _serve_auto_request() {
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._serve_auto_request " +
$"vs_id: {_vs_id} auto request received");
bool is_dirty = _bb.read_state(_in_bb_dirty_tag, false);
VolSurfaceNonVolData nonvol_data = _bb.read_state(_in_bb_nonvol_data_tag);
VolSurfaceStandardTime vs_standard_time = _bb.read_state(_in_bb_vs_standard_time_tag);
VolSurfaceCalculationRequest auto_req = _bb.read_state(_in_bb_auto_request_tag);
if (is_dirty || nonvol_data == null || vs_standard_time == null || auto_req == null) {
return;
}
_timer.start();
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map =
VolSurfaceUtilityFunctions.compute_aux_data(auto_req.volsurface_input,
nonvol_data.input,
nonvol_data.driver_price_map,
vs_standard_time, true);
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
_post_process_action(_bb, _vs_id, auto_req, aux_data_map);
var vs_output = GeneralVolSurfaceCalculator.compute(auto_req.volsurface_input,
nonvol_data.contracts,
aux_data_map,
action_map,
null,
auto_req.cascade_up,
auto_req.cascade_down);
_timer.stop();
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._serve_auto_request vs_id: {_vs_id} " +
$"time: {_timer.lastIntervalTime}");
_bb.post(SparkTags.VolSurfaceAutoCalculationResponseTag(_vs_id),
new VolSurfaceCalculationResponse(vs_output, auto_req.request_id));
}
/// Computes the impl vol if the action is fit to market
private static IDictionary<VolSurfaceExpiration, VolCurveAction> _post_process_action(
ThreadedBlackboard bb, int vs_id,
private IDictionary<VolSurfaceExpiration, VolCurveAction> _post_process_action(
VolSurfaceCalculationRequest req,
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map) {
IDictionary<VolSurfaceExpiration, VolCurveAction> rv = req.action_map;
@@ -162,7 +109,7 @@ namespace mbts.spark {
VolCurveAction rep_action = rv.ElementAt(0).Value;
if (rep_action.action_type == VolCurveAction.ActionType.FitToMarket) {
IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> impl_vol_map =
_fill_in_implied_vols(bb, vs_id, req.volsurface_input, req.action_map, aux_data_map,
_fill_in_implied_vols(req.volsurface_input, req.action_map, aux_data_map,
req.cascade_up, req.cascade_down);
rv = impl_vol_map.ToDictionary(kv => kv.Key, kv => new VolCurveAction(
new VolCurveMarketFitAction(rep_action.market_fit_action.fit_action, kv.Value,
@@ -172,8 +119,7 @@ namespace mbts.spark {
return rv;
}
private static IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> _fill_in_implied_vols(
ThreadedBlackboard bb, int vs_id,
private IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> _fill_in_implied_vols(
VolSurfaceInput vs_inp,
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map,
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map,
@@ -194,15 +140,15 @@ namespace mbts.spark {
applicable_exps = applicable_exps.Where(vs_exp => master_exps.Contains(vs_exp));
VolSurfaceEnums.MarketSide impliedvol_side =
SingleVolSurfaceMarketImpliedVolSource._get_market_impliedvol_side(bb, vs_id);
SingleVolSurfaceMarketImpliedVolSource._get_market_impliedvol_side(_bb, _vs_id);
foreach (VolSurfaceExpiration vs_exp in applicable_exps) {
VolSurfaceExpiration md_exp;
VolCurveMarketImpliedVolData exp_impl_data =
VolSurfaceCoreFunctions.compute_implied_vols(vs_inp, vs_exp, aux_data_map,
arg_vs_exp => SingleVolSurfaceMarketImpliedVolSource.get_option_bbo(bb, arg_vs_exp),
arg_vs_exp => SingleVolSurfaceMarketImpliedVolSource.get_option_bbo(_bb, arg_vs_exp),
impliedvol_side,
SingleVolSurfaceMarketImpliedVolSource._ignore_market_implied_vol_source_settings(
bb, vs_id),
_bb, _vs_id),
out md_exp);
rv[vs_exp] = exp_impl_data;
}
@@ -81,7 +81,7 @@ namespace mbts.analytics {
output.Add(symbol, symbol_data);
}
IReadOnlyDictionary<string, TData> ro_output = output;
bts.Log.Gen.diag($"SymbolAggregationPublisher._on_timer publishing " +
bts.Log.Gen.info($"SymbolAggregationPublisher._on_timer publishing " +
$"tag: {_out_tag} sz: {ro_output.Count}");
_bb.post(_out_tag, ro_output);
}
@@ -270,7 +270,6 @@ namespace mbts.spark {
new mbts.analytics.VolSurfaceNonVolInput(exp_param_map.exp_map.ToDictionary()));
} else if (_in_bb_req_vol.Equals(tag)) {
ClientArchivedVolRequest client_req = _bb.read_state(_in_bb_req_vol);
bts.Log.Ui.info($"VolSurfaceBusManager._on_bb_update ClientArchivedVolRequest: {client_req.ToString()}");
ArchivedTheoRetrieval ret = client_req.ret;
if (ret == null || ret.SecTup == null) {
bts.Log.Ui.warn($"VolSurfaceBusManager malformed ArchivedTheoRetrieval");
@@ -5,7 +5,6 @@ using bts.api;
using bts.collections;
using System.Linq;
using bts.api.orderman;
using bts.api.analytics;
using bts;
namespace mbts.spark {
@@ -24,8 +23,6 @@ namespace mbts.spark {
_order_service = order_service;
_bb.subscribe(SparkTags.AllInterpGreeks);
_bb.subscribe(SparkTags.SystemContractsTag);
_bb.subscribe(SparkTags.CurrentUser);
_bb.subscribe(SparkTags.VolSurfaceUsageTag);
_bb.data_callback += _on_data;
}
@@ -58,32 +55,10 @@ namespace mbts.spark {
return rv;
}
private IReadOnlyDictionary<string, OptionTheoInterpolationData>
_filter_for_publish(IReadOnlyDictionary<string, OptionTheoInterpolationData> all_theos) {
HashSet<string> syms = new HashSet<string>();
VolSurfaceUsageListImmutable usage = _bb.read_state(SparkTags.VolSurfaceUsageTag);
string current_user = _bb.read_state(SparkTags.CurrentUser).name;
foreach (var u in usage.UsageList) {
VolSurfaceInput vs_input =
_bb.read_state(SparkTags.VolSurfaceCommittedInputTag(u.VsId));
if (vs_input != null) {
if (vs_input.surface_settings.core_settings.vs_manager ==
current_user) {
syms.Add(u.SymbolName);
}
}
}
return
all_theos.Where(kv => syms.Contains(kv.Key)).ToDictionary(kv => kv.Key, kv => kv.Value);
}
private void _publish_data() {
SystemContractData sc_data = _bb.read_state(SparkTags.SystemContractsTag, null);
IReadOnlyDictionary<string, OptionTheoInterpolationData> all_theos =
_bb.read_state(SparkTags.AllInterpGreeks, null);
all_theos = _filter_for_publish(all_theos);
// find the symbol vs vs_id, get the committed vs, see if it is
// owned by the current user, and only then publish it
IReadOnlyDictionary<string, OptionDriverPriceMap> all_drivers =
_bb.read_state(SparkTags.AllOptionDriverBBOTag, null);
if (sc_data == null || all_theos == null || all_drivers == null)
@@ -26,13 +26,12 @@ namespace mbts.spark {
_refresh = new(_is_dirty.refresh);
_committed_value.Changed += on_committed_changed;
_candidate_value.Changed += on_candidate_changed;
if (column.type.is_editable()) {
_is_dirty.Changed += (o, n) => {
Log.Ui.info(
$"VolSurface '{expiration}.{column}' is_dirty changed to {n}. " +
$"commited: {committed_value}; candidate: {candidate_value}");
};
}
_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}");
};
void on_committed_changed(object? oldval, object? newval) =>
_is_dirty.refresh();
@@ -29,7 +29,6 @@ namespace mbts.spark.VolSurface {
class VolSurfaceGridVM : DisposableViewModelBase {
readonly VolSurfaceVM _parent;
readonly UserTuple _user;
readonly ObservableCollection<VolSurfaceDisplayGridExpiration> _expirations = new();
readonly DeferrableAction _invalidate_rows;
readonly ColumnsModel _columns_model;
@@ -42,12 +41,10 @@ namespace mbts.spark.VolSurface {
VolSurfaceVM vm,
VolSurfaceDisplayModel model,
ISet<string> hidden_columns,
ISet<VolSurfaceExpiration> hidden_expirations,
UserTuple user) {
ISet<VolSurfaceExpiration> hidden_expirations) {
Log.notnull(hidden_columns);
Log.notnull(hidden_expirations);
_parent = vm;
_user = user;
this.model = model;
_columns_model = new(create_exp_column_view());
_hidden_columns = hidden_columns;
@@ -86,7 +83,7 @@ namespace mbts.spark.VolSurface {
_columns_model);
VolSurfaceRowVM row_factory(VolSurfaceDisplayGridExpiration exp) =>
new(this, exp, _user);
new(this, exp);
IDisposable connect_row(VolSurfaceRowVM row) {
row.request_calculate += request_recalc;
@@ -1,6 +1,5 @@
#nullable enable
namespace mbts.spark {
using bts;
using bts.collections;
using mbts.spark.VolSurface;
@@ -10,7 +9,6 @@ namespace mbts.spark {
using mbts.ui.utility;
using Spark.Core.Disposables;
using Spark.Core.Reactive;
using System;
using System.Collections.Generic;
@@ -24,10 +22,6 @@ namespace mbts.spark {
readonly VolSurfaceGridVM _parent;
readonly Property<VolSurfaceColumn?> _column;
readonly Property<DIYGridSuperHeaderVM?> _super_header;
readonly UserTuple _user;
readonly Property<bool> _is_open;
readonly DelegateProperty<string> _vs_manager;
readonly List<IDisposable> _resources = new();
ICommand? _revert_cmd;
ICommand? _shift_selection_cmd;
@@ -40,29 +34,21 @@ namespace mbts.spark {
ICommand? _copy_all_to_clipboard_cmd;
ICommand? _smart_copy_rolls_rates_cmd;
ICommand? _import_csv_cmd;
ObservantCommand? _acquire_ownership_cmd;
public VolSurfaceRowContextMenuVM(VolSurfaceGridVM parent, VolSurfaceRowVM row, UserTuple user) {
public VolSurfaceRowContextMenuVM(VolSurfaceGridVM parent, VolSurfaceRowVM row) {
_parent = parent;
_row = row;
_user = user;
_column = backing<VolSurfaceColumn?>(nameof(column));
_super_header = backing<DIYGridSuperHeaderVM?>(nameof(super_header));
_vs_manager = backing(
nameof(vs_manager),
() => _parent.model.get_active_settings()?.core_settings?.vs_manager ?? string.Empty);
_is_open = backing(nameof(is_open), false);
_resources.AddRange(
_dispose = Disposable.Create(
_column.invalidate_others_on_change(nameof(column_name)),
_super_header.invalidate_others_on_change(nameof(group_name)),
_is_open.Subscribe(_ => _vs_manager.refresh()),
_parent.visible_rows.ObserveSelf()
.BufferLatest(TimeSpan.FromSeconds(1))
.OnDispatcher()
.Subscribe(_ => sync_symbols()));
_dispose = Disposable.Create(() => _resources.DisposeAll(true));
void sync_symbols() {
var visible_exps = _parent.visible_rows.Map(r => r.expiration.symbol);
symbols.sync_with_models(
@@ -178,34 +164,6 @@ namespace mbts.spark {
_parent.model.csv_importing_enabled,
null);
public ICommand acquire_ownership_cmd {
get {
if (_acquire_ownership_cmd is null) {
_acquire_ownership_cmd = new(
() => {
var settings = _parent.model.get_active_settings();
if (settings is null) {
Log.Ui.error("Unable to acquire volsurface ownership: settings are null.");
return;
}
settings.core_settings.vs_manager = _user.name;
_parent.model.set_candidate_settings(settings);
});
_resources.Add(_vs_manager.observe_current(true)
.Subscribe(SelectorObserver.builder(_acquire_ownership_cmd).wrap<string>(
curr => curr != _user.name)));
}
return _acquire_ownership_cmd;
}
}
public bool is_open {
get => _is_open.Value;
set => _is_open.set(value);
}
public string vs_manager => _vs_manager.Value;
VolSurfaceDisplayGridColumn _assert_model() =>
column?.model ?? throw new InvalidOperationException();
@@ -1,7 +1,5 @@
#nullable enable
namespace mbts.spark.VolSurface {
using bts;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.shell;
@@ -18,16 +16,14 @@ namespace mbts.spark.VolSurface {
readonly VolSurfaceGridVM _parent;
readonly VolSurfaceDisplayGridExpiration _exp;
readonly Dictionary<string, CellData> _cells = new();
readonly UserTuple _user;
readonly DeferrableAction _request_calculate;
VolSurfaceRowContextMenuVM? _context_menu;
bool _is_selected;
public VolSurfaceRowVM(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp, UserTuple user) {
public VolSurfaceRowVM(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp) {
_parent = parent;
_exp = exp;
_user = user;
_request_calculate = new(() => request_calculate?.Invoke());
_dispose = Disposable.Create(() => {
_cells.Values.DisposeAll();
@@ -53,7 +49,7 @@ namespace mbts.spark.VolSurface {
}
public VolSurfaceRowContextMenuVM context_menu =>
_context_menu ??= new(_parent, this, _user);
_context_menu ??= new(_parent, this);
public VolSurfaceCellVM get_cell(VolSurfaceDisplayGridColumn column) =>
_cells.GetOrAdd(column.unique_col_name, _ => _create_cell(column)).vm;
@@ -1,89 +0,0 @@
#nullable enable
namespace mbts.spark {
using bts.api.domain;
using mbts.analytics;
using mbts.ui.shell;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using VolCurveFitRegion = analytics.VolSurfaceEnums.VolCurveFitRegion;
public class VolSurfaceAutoFitSettingsVM : DisposableViewModelBase {
readonly Property<VolCurveFitRegion> _fit_region;
readonly Property<uint?> _period_secs;
readonly Property<double?> _driver_points_moved;
readonly Property<double?> _atm_points_moved;
readonly Property<bool> _is_auto_fit_enabled;
public VolSurfaceAutoFitSettingsVM() {
_fit_region = backing(nameof(fit_region), VolCurveFitRegion.None);
_is_auto_fit_enabled = backing(
nameof(is_auto_fit_enabled),
_fit_region.observe_current(true),
r => r != VolCurveFitRegion.None);
_period_secs = backing<uint?>(nameof(period_secs), 10);
_driver_points_moved = backing<double?>(nameof(driver_points_moved));
_atm_points_moved = backing<double?>(nameof(atm_points_moved));
_fit_region.Changed += (o, n) => RaisePropertyChanged(nameof(is_auto_fit_enabled));
}
public IEnumerable<VolCurveFitRegion> fit_regions =>
EnumHelper.get_values<VolCurveFitRegion>();
public VolCurveFitRegion fit_region {
get => _fit_region.Value;
set => _fit_region.set(value);
}
public bool is_auto_fit_enabled => _is_auto_fit_enabled.Value;
public uint? period_secs {
get => _period_secs.Value;
set => _period_secs.set(value);
}
public double? driver_points_moved {
get => _driver_points_moved.Value;
set => _driver_points_moved.set(value);
}
public TickDisplaySettingsImmutable? driver_points_display =>
user.settings.TickFactors.pennies.to_api_tick_display_settingsi();
public double? atm_points_moved {
get => _atm_points_moved.Value;
set => _atm_points_moved.set(value);
}
public TickDisplaySettingsImmutable? atm_points_display =>
user.settings.TickFactors.pennies.to_api_tick_display_settingsi();
public void load_from(VolSurfaceAutoFitSettings value) {
fit_region = value?.auto_fit_region ?? VolCurveFitRegion.None;
period_secs = value?.time_based_settings?.period_seconds;
driver_points_moved = value?.driver_move_based_settings?.points_moved;
atm_points_moved = value?.atm_implied_vol_based_settings?.points_moved;
}
public void assign_to(VolSurfaceAutoFitSettings value) {
if (value is null) {
throw new ArgumentNullException(nameof(value));
}
value.auto_fit_region = fit_region;
value.time_based_settings = new() {
period_seconds = period_secs
};
value.driver_move_based_settings = new() {
points_moved = driver_points_moved
};
value.atm_implied_vol_based_settings = new() {
points_moved = atm_points_moved
};
}
}
}
@@ -40,7 +40,6 @@ namespace mbts.spark {
vol_model_cubic_spline_vm.load_from(settings.spline_model_settings);
vol_model_dual_quadratic_vm.load_from(settings.dualquadratic_model_settings);
vol_path_parabolic_vm.load_from(settings.core_settings.volpath_settings.parabolic_vol_path_settings);
auto_fit_vm.load_from(settings.core_settings.market_fit_settings.vs_auto_fit_settings);
init_rotation_vms(call_rotations, _core_settings.call_rotation.strike_vol_movement_pairs);
init_rotation_vms(put_rotations, _core_settings.put_rotation.strike_vol_movement_pairs);
@@ -106,7 +105,6 @@ namespace mbts.spark {
}).ToList();
_core_settings.strangle.strike_vol_movement_pairs = strangles.SelectMany(r => new[] { Tuple.Create(r.target_put_strike, r.put_move), Tuple.Create(r.target_call_strike, r.call_move) }).ToList();
_core_settings.atm_shift.atm_strike_vol_movement = atm_shifts.Count == 1 ? atm_shifts[0].move : 0;
auto_fit_vm.assign_to(_core_settings.market_fit_settings.vs_auto_fit_settings);
commit(_settings);
}
@@ -235,7 +233,23 @@ namespace mbts.spark {
}
}
public VolSurfaceAutoFitSettingsVM auto_fit_vm { get; } = new();
public bool enable_auto_fit {
get => _core_settings.market_fit_settings.enable_auto_fit;
set {
using var _ = detect_property_change();
_core_settings.market_fit_settings.enable_auto_fit = value;
}
}
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region {
get => _core_settings.market_fit_settings.auto_fit_region;
set { _core_settings.market_fit_settings.auto_fit_region = value; }
}
public double auto_fit_time_ms {
get => _core_settings.market_fit_settings.auto_fit_time_ms;
set { _core_settings.market_fit_settings.auto_fit_time_ms = value; }
}
public bool auto_decay_vol_time {
get => _core_settings.time_settings.time_mode == VolSurfaceEnums.VolSurfaceTimeMode.AutoDecay;
+2 -10
View File
@@ -1,6 +1,5 @@
#nullable enable
namespace mbts.spark {
using bts;
using bts.api;
using bts.collections;
@@ -28,7 +27,6 @@ namespace mbts.spark {
readonly IBus _bus;
readonly string _request_id = Guid.NewGuid().ToString();
readonly VolSurfaceDisplayModel _model;
readonly UserTuple _user;
readonly IRaiseCanExecuteChangedCommand _revert_cmd;
readonly IRaiseCanExecuteChangedCommand _commit_cmd;
readonly IDialogCommand _main_settings_cmd;
@@ -40,16 +38,10 @@ namespace mbts.spark {
bool _ignore_implied_vol_settings;
public VolSurfaceVM(
VolSurfaceDisplayModel model,
int id,
IBus bus,
VolSurfaceWindowSettings startup_data,
UserTuple user
) {
VolSurfaceDisplayModel model, int id, IBus bus, VolSurfaceWindowSettings startup_data) {
_model = model;
_vs_id = id;
_bus = bus;
_user = user;
macro_actions = new VolSurfaceMacroActionsVM(_model);
globals = new VolSurfaceGlobalParameterVM(_model);
@@ -57,7 +49,7 @@ namespace mbts.spark {
_grid_settings = startup_data.macro_grid_settings ?? new VolSurfaceMacroGridSettings();
var hidden_exps_set = startup_data.hidden_exps.empty_if_null().ToHashSet();
grid = new VolSurfaceGridVM(
this, _model, _grid_settings.hidden_cols(), hidden_exps_set, _user);
this, _model, _grid_settings.hidden_cols(), hidden_exps_set);
grid.on_selection_changed += _on_grid_selection_changed;
_model.publish_grid_settings(_grid_settings);
@@ -4,11 +4,13 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.analytics {
namespace mbts.analytics
{
/// These are the VolSurface settings that are common
/// to any Vol Model
[ProtoBuf.ProtoContract(Name = "VolSurfaceCoreSettings")]
public class VolSurfaceCoreSettings {
public class VolSurfaceCoreSettings
{
[ProtoBuf.ProtoMember(1)]
public string vol_surface_name;
[ProtoBuf.ProtoMember(2)]
@@ -85,7 +87,5 @@ namespace mbts.analytics {
get => _time_settings ??= new();
set => _time_settings = value;
}
[ProtoBuf.ProtoMember(240)]
public string vs_manager { get; set; } = string.Empty;
}
}
@@ -75,7 +75,6 @@ namespace mbts.analytics {
if (core_settings.market_fit_settings == null) {
core_settings.market_fit_settings = new VolSurfaceMarketFitSettings();
}
core_settings.market_fit_settings.vs_auto_fit_settings ??= new();
if (core_settings.time_settings == null) {
core_settings.time_settings = new VolSurfaceTimeSettings();
}
@@ -141,7 +141,6 @@ namespace mbts.analytics
public enum VolCurveFitRegion
{
None,
ATM,
All
}
@@ -231,4 +230,4 @@ namespace mbts.analytics
USE_CALENDAR_FOR_BUSINESS
}
}
}
}
@@ -3,15 +3,16 @@
using mbts.co2;
namespace mbts.analytics {
using Size = System.Single;
public class VolSurfaceMDUnit : IEquatable<VolSurfaceMDUnit> {
public readonly double bid_price, ask_price;
public readonly uint bid_size, ask_size;
public readonly Size 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, uint this_bid_size,
double this_ask_price, uint this_ask_size,
public VolSurfaceMDUnit(double this_bid_price, Size this_bid_size,
double this_ask_price, Size this_ask_size,
double this_mark_price) {
bid_price = this_bid_price;
bid_size = this_bid_size;
@@ -28,7 +29,7 @@ namespace mbts.analytics {
public VolSurfaceMDUnit(NativePriceSize bid, NativePriceSize ask,
double mark_price)
: this(bid.price, bid.size, ask.price, ask.size, mark_price) {
: this(bid.price, bid.size_f, ask.price, ask.size_f, mark_price) {
}
public bool is_two_sided() { return bid_size != 0 && ask_size != 0; }
@@ -41,7 +42,7 @@ namespace mbts.analytics {
}
public double get_weighted_mid_price(bool get_one_sided_mid = true) {
uint sum_q = bid_size + ask_size;
Size sum_q = bid_size + ask_size;
if (sum_q == 0) {
return Double.NaN;
}
@@ -4,9 +4,11 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.analytics {
namespace mbts.analytics
{
[ProtoBuf.ProtoContract(Name = "VolSurfaceMarketFitSettings")]
public class VolSurfaceMarketFitSettings {
public class VolSurfaceMarketFitSettings
{
/// use one sided market or not
[ProtoBuf.ProtoMember(10)]
public bool ignore_one_sided_markets;
@@ -20,44 +22,19 @@ namespace mbts.analytics {
public double graph_update_period_ms = 2500;
/// If this is set to true a regression curve is drawn
/// and the vol model adheres to that
[ProtoBuf.ProtoMember(50)]
[ProtoBuf.ProtoMember(50)]
public bool use_weighted_regression_fit = false;
[ProtoBuf.ProtoMember(55)]
public uint regression_order = 4;
[ProtoBuf.ProtoMember(90)]
public VolSurfaceAutoFitSettings vs_auto_fit_settings { get; set; }
}
[ProtoBuf.ProtoContract(Name = "VolSurfaceAutoFitSettings")]
public class VolSurfaceAutoFitSettings {
// each of these settings can be enabled independently. if they are enabled
// then any of them being true will result in an autmatic fit
[ProtoBuf.ProtoContract(Name = "TimeBasedSettings")]
public class TimeBasedSettings {
[ProtoBuf.ProtoMember(10)]
public uint? period_seconds { get; set; }
}
[ProtoBuf.ProtoContract(Name = "DriverMoveBasedSettings")]
public class DriverMoveBasedSettings {
[ProtoBuf.ProtoMember(10)]
public double? points_moved { get; set; }
}
[ProtoBuf.ProtoContract(Name = "ATMImpliedVolBasedSettings")]
public class ATMImpliedVolBasedSettings {
[ProtoBuf.ProtoMember(20)]
public double? points_moved { get; set; }
// we can do price terms moved as well
}
[ProtoBuf.ProtoMember(5)]
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region;
[ProtoBuf.ProtoMember(10)]
public TimeBasedSettings time_based_settings = null;
[ProtoBuf.ProtoMember(20)]
public DriverMoveBasedSettings driver_move_based_settings = null;
[ProtoBuf.ProtoMember(30)]
public ATMImpliedVolBasedSettings atm_implied_vol_based_settings = null;
/// The system auto fits and saves vols if this is
/// set to true
[ProtoBuf.ProtoMember(60)]
public bool enable_auto_fit = false;
/// If enable_auto_fit = true, this governs
/// how often the system auto-fits
[ProtoBuf.ProtoMember(70)]
public double auto_fit_time_ms = 30000;
[ProtoBuf.ProtoMember(80)]
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region = VolSurfaceEnums.VolCurveFitRegion.ATM;
}
}
@@ -475,7 +475,6 @@ namespace mbts.analytics {
public static ResultMessage save_volsurface(
VolSurfaceInput vs_input, AnalyticsService service, string user = "") {
vs_input.saver_login = user;
vs_input.surface_settings.core_settings.vs_manager = user;
// convert to VolSurfaceEntry
var to_save = new VolSurfaceEntry();
var serialized_vs = ProtobufSerializationHelper.serialize(vs_input);
@@ -596,7 +595,7 @@ namespace mbts.analytics {
_greek_getter(g)(t, is_call);
public static IDictionary<double, IEnumerable<Theos>> hash_strikes(IEnumerable<Theos> theos) =>
theos.Map(t => t.strike);
theos.Map(t => t.strike);
static Func<Theos, bool, double> _greek_getter(VolSurfaceGreeks g) =>
g switch {
@@ -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);
}
+1 -4
View File
@@ -135,10 +135,7 @@ namespace bts {
var meta = _security_metadata.get(smd.security);
if (meta.HasValue) {
exch_secs[smd.id] = new(
smd.security,
smd.md,
meta.Value.HasPriceDisplay ? meta.Value.PriceDisplay : null,
meta.Value.HasQuantityDisplay ? meta.Value.QuantityDisplay : null);
smd.security, smd.md, meta.Value.PriceDisplay, meta.Value.QuantityDisplay);
}
}
_bus.post(SparkTags.ServerSecuritiesTag, exch_secs);
+8 -2
View File
@@ -52,17 +52,23 @@ 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}" + "\n";
$"k: {sec.Option.Strike} cp: {sec.Option.CallPut} " +
$"bid: p: {bid.price} q: {bid.size} qf: {bid.size_f} ask: p: {ask.price} q: {ask.size} qf: {ask.size_f}" +
"\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();
@@ -56,7 +56,7 @@ namespace mbts.spark {
r => new ReportImmutable?(r),
r => r?.Id ?? throw new ArgumentNullException(nameof(r)));
_window_factories = new() {
new VolSurfaceWindowFactory(this, bus, user.Value),
new VolSurfaceWindowFactory(this, bus),
new AnalyticsExpirationParametersWindowFactory(this, bus),
new SlideReportWindowFactory(this, bus, reports_demuxer.subscribe),
new ComboReportWindowFactory(this, bus, reports_demuxer.subscribe),
@@ -793,106 +793,70 @@
Grid.Row="5"
Grid.Column="1"
Grid.ColumnSpan="4"
DataContext="{Binding auto_fit_vm}"
Header="Auto Fit"
Margin="0,5,0,0"
Padding="5">
Padding="5"
IsEnabled="False">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox
Grid.ColumnSpan="2" Content="Enabled" IsChecked="{Binding enable_auto_fit}" />
<TextBlock Grid.Row="1" Text="Region" Margin="16,5,0,0">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger
Binding="{Binding enable_auto_fit}"
Value="False">
<Setter
Property="Foreground"
Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Grid.Row="0" Text="Fit Region" />
<ComboBox
Grid.Row="0"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="8,0,0,0"
VerticalAlignment="Center"
ItemsSource="{Binding fit_regions}"
SelectedItem="{Binding fit_region}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={x:Static shell:VolSurfaceSettingsEditor.AutoFitRegionDisplayTextConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock
Grid.Row="1"
Margin="0,5,0,0"
Text="Every" />
<c:TickUpDownNullableUInt32
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Minimum="30"
Margin="8,0,0,0"
Width="60"
Value="{Binding period_secs}"
IsEnabled="{Binding is_auto_fit_enabled}" />
<TextBlock
Grid.Row="1"
Grid.Column="2"
HorizontalAlignment="Left"
Margin="3,5,0,0"
Text="secs" />
Margin="12,0,0,0"
HorizontalAlignment="Stretch"
ItemsSource="{Binding auto_fit_region_choices}"
SelectedValue="{Binding auto_fit_region}"
IsEnabled="{Binding enable_auto_fit}" />
<TextBlock
Grid.Row="2"
Margin="0,5,0,0"
Text="When Driver moves" />
<c:TickUpDownNullableDouble
<TextBlock Grid.Row="2" Text="Fit Time" Margin="16,5,0,0">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding enable_auto_fit}" Value="False">
<Setter Property="Foreground" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<c:TextBoxDouble
Grid.Row="2"
Grid.Column="1"
ToolTipService.ShowOnDisabled="True"
c:TextBoxEx.Suffix="ms"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="8,0,0,0"
Width="60"
Value="{Binding driver_points_moved}"
Increment="0.01"
TickDisplaySettings="{Binding driver_points_display}"
IsEnabled="False"
ToolTip="Not yet supported (coming soon)" />
<TextBlock
Grid.Row="2"
Grid.Column="2"
HorizontalAlignment="Left"
Margin="3,5,0,0"
Text="%" />
<TextBlock
Grid.Row="3"
Margin="0,5,0,0"
Text="When ATM moves" />
<c:TickUpDownNullableDouble
Grid.Row="3"
Grid.Column="1"
ToolTipService.ShowOnDisabled="True"
HorizontalAlignment="Left"
Margin="8,0,0,0"
Width="60"
Value="{Binding atm_points_moved}"
Increment="0.01"
TickDisplaySettings="{Binding atm_points_display}"
IsEnabled="False"
ToolTip="Not yet supported (coming soon)" />
<TextBlock
Grid.Row="3"
Grid.Column="2"
HorizontalAlignment="Left"
Margin="3,5,0,0"
Text="%" />
Margin="12,5,0,0"
IsEnabled="{Binding enable_auto_fit}"
Value="{Binding auto_fit_time_ms}"
Template="{StaticResource SuffixTemplate}" />
</Grid>
</GroupBox>
</Grid>
@@ -1,20 +1,9 @@
#nullable enable
namespace mbts.ui.shell {
using mbts.analytics;
using mbts.ui.converters;
using System.Windows;
using System.Windows.Data;
partial class VolSurfaceSettingsEditor : Window {
public VolSurfaceSettingsEditor() =>
InitializeComponent();
public static IValueConverter AutoFitRegionDisplayTextConverter =>
ValueConverter.create<VolSurfaceEnums.VolCurveFitRegion, string>(fr =>
fr switch {
VolSurfaceEnums.VolCurveFitRegion.None => "Disabled",
_ => fr.ToString()
});
}
}
@@ -262,7 +262,7 @@
IsDragAdornerEnabled="False"
ContextMenuOpening="_on_context_menu_opening">
<c:DIYGrid.ContextMenu>
<ContextMenu IsOpen="{Binding is_open}">
<ContextMenu>
<MenuItem
Header="Populate Empty Expirations"
Command="{Binding populate_empty_exps_cmd}" />
@@ -413,11 +413,6 @@
</MenuItem.ItemContainerStyle>
</MenuItem>
<MenuItem Header="Import CSV..." Command="{Binding import_csv_cmd}" />
<MenuItem Command="{Binding acquire_ownership_cmd}">
<MenuItem.Header>
<TextBlock Text="{Binding vs_manager, StringFormat='Acquire ownership from \'{0}\''}" />
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</c:DIYGrid.ContextMenu>
<c:DIYGrid.RowStyle>
@@ -122,7 +122,7 @@
}
var mouse_pos = Mouse.GetPosition(this);
if (!this.ChildAtPoint<VolSurfaceRowView>(mouse_pos, out var row)) {
cm.SetCurrentValue(ContextMenu.IsOpenProperty, false);
cm.IsOpen = false;
return;
}
var cgs = g.GetColumnGeometry().ToList();
@@ -1,5 +1,4 @@
namespace mbts.spark {
using bts;
using bts.api;
using bts.spark;
@@ -15,20 +14,14 @@
public const string WINDOW_TYPE = "VolSurface";
readonly VolSurfaceWindowSettings _startup_data;
readonly UserTuple _user;
VolSurfaceVM _vm;
VolSurfaceView _view;
public VolSurfaceWindow(
VolSurfaceWindowFactory factory,
IBus bus,
string vs_name,
VolSurfaceWindowSettings startup_data,
UserTuple user
) : base(vs_name, bus, factory, Icons.vol_surface) {
VolSurfaceWindowFactory factory, IBus bus, string vs_name, VolSurfaceWindowSettings startup_data) :
base(vs_name, bus, factory, Icons.vol_surface) {
_startup_data = startup_data ?? throw new ArgumentNullException(nameof(startup_data));
_user = user;
this.vs_name = vs_name;
_dispose = Disposable.Create(() => Disposable.dispose(ref _vm));
}
@@ -44,7 +37,7 @@
public override UIElement Content => _view;
public void thread_init() {
_vm = new VolSurfaceVM(new (vs_id, _bus, true), vs_id, _bus, _startup_data, _user);
_vm = new VolSurfaceVM(new VolSurfaceDisplayModel(vs_id, _bus, true), vs_id, _bus, _startup_data);
_view = new VolSurfaceView {
DataContext = _vm
};
@@ -1,5 +1,4 @@
namespace mbts.spark {
using bts;
using bts.api;
using bts.shell;
@@ -11,12 +10,10 @@ namespace mbts.spark {
class VolSurfaceWindowFactory : ShellWindowFactory<VolSurfaceWindowSettings> {
readonly IBus _bus;
readonly UserTuple _user;
public VolSurfaceWindowFactory(IShellModule module, IBus bus, UserTuple user)
public VolSurfaceWindowFactory(IShellModule module, IBus bus)
: base(module, "VolSurface") {
_bus = bus;
_user = user;
_dispose = bus.subscribe(SparkTags.ShowVolSurface).OnDispatcher().Subscribe(
async state => _bus.post(SparkTags.ShowWindow, await _create(state)));
}
@@ -25,7 +22,7 @@ namespace mbts.spark {
_create(state);
async Task<IShellWindow> _create(VolSurfaceWindowSettings state) =>
new VolSurfaceWindow(this, _bus, await _lookup_vs_name(state.id), state, _user);
new VolSurfaceWindow(this, _bus, await _lookup_vs_name(state.id), state);
async Task<string> _lookup_vs_name(int vs_id) {
var (vol_surfaces, success) = await _bus.subscribe(SparkTags.VolSurfaceListTag).FirstAsyncTask(TimeSpan.FromSeconds(2));
@@ -176,11 +176,10 @@
</usp:DataGridParameterCellTemplateConverter.QuantityMeasureTemplate>
</usp:DataGridParameterCellTemplateConverter>
<Style x:Key="ExpanderToggleButton" TargetType="ToggleButton">
<Setter Property="Foreground" Value="{u:BTSColor ExecutionStrategy.expander_foreground}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid Background="{u:BTSColor ExecutionStrategy.expander_background}">
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="12" />
<ColumnDefinition/>
@@ -191,7 +190,7 @@
HorizontalAlignment="Center"
Margin="0,5,0,0"
SnapsToDevicePixels="False"
Stroke="{u:BTSColor ExecutionStrategy.arrow_background}"
Stroke="#77333333"
StrokeThickness="2"
VerticalAlignment="Top"
/>
@@ -200,7 +199,6 @@
ContentSource="{TemplateBinding Content}"
Margin="3,0,0,0"
/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
@@ -213,7 +211,7 @@
<Setter Property="Stroke" TargetName="arrow" Value="Black"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Stroke" TargetName="arrow" Value="{u:BTSColor ExecutionStrategy.arrow_background_selected}"/>
<Setter Property="Stroke" TargetName="arrow" Value="#FF707070"/>
<Setter Property="Foreground" Value="Gray"/>
</Trigger>
</ControlTemplate.Triggers>
@@ -280,8 +278,7 @@
<ColumnDefinition/>
</SelectiveScrollingGrid.ColumnDefinitions>
<DataGridRowHeader
Foreground="{u:BTSColor ExecutionStrategy.expander_foreground}"
Background="{u:BTSColor ExecutionStrategy.data_grid_row_header_background}"
Background="Transparent"
Padding="{Binding section_count, Converter={StaticResource RowHeaderGroupIndentConverter}}"
FontWeight="{Binding font_weight, FallbackValue={u:FontWeight Normal}}"
SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
@@ -320,17 +317,9 @@
<Style x:Key="DataGridRowStyle" TargetType="DataGridRow">
<Setter Property="BorderThickness" Value="1,0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Background" Value="{u:BTSColor ExecutionStrategy.alternate_column_background}"/>
<Setter Property="Template" Value="{StaticResource DataGridRowTemplate}"/>
<Setter Property="usp:ExecutionStrategyEditor.RowHasErrors" Value="{Binding has_errors}"/>
<Style.Triggers>
<Trigger Property="ListBox.AlternationIndex" Value="1">
<Setter Property="Background" Value="{u:BTSColor ExecutionStrategy.data_grid_background}"/>
</Trigger>
<!-- <Trigger Property="ListBox.AlternationIndex" Value="2">
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="Foreground" Value="Navy"/>
</Trigger> -->
<DataTrigger Binding="{Binding read_only}" Value="True">
<Setter Property="TextElement.Foreground" Value="{u:BTSColor UI.input_disabled_text_dark}"/>
<Setter Property="ToolTip">
@@ -57,7 +57,7 @@ namespace mbts.spark {
main_vm.rows.AddRange(parsed_synths.Select(ps => {
var sec = ps.result;
var r = new SyntheticImportRowVM(orders_model.admin_model.user.id, used_names.Contains) {
tick_factors = TickUtils.get_regional_ticks(sec.immutable()).low_tick,
tick_factors = TickUtils.get_regional_ticks(sec.ContractSpec).low_tick,
name = sec.Synthetic.Name,
parsed_synth = ps
};
@@ -56,7 +56,7 @@ namespace mbts.spark {
public SyntheticEditorVM(SpreaderModel model, SecurityImmutable synthetic, ExecutionStrategyImmutable strategy, IObservable<bool> security_has_working_orders)
: this(synthetic, strategy, false) {
ExceptionAssert.InvalidOperation.That(synthetic.Id != 0, "This constructor may only be used for already-existing synthetic securities (id must be non-zero).");
_tick_factors = TickUtils.get_regional_ticks(synthetic).low_tick;
_tick_factors = TickUtils.get_regional_ticks(synthetic.ContractSpec).low_tick;
if (TickUtils.is_integer_tick(synthetic.ContractSpec)) {
_use_fractional_ticks = false;
_integer_tick_size = DoubleHelper.to_optional_int32(synthetic.ContractSpec.LowTick) ?? 0;
@@ -112,7 +112,7 @@ namespace mbts.spark {
_set_ticks_automatically = false;
_set_quantities_automatically = false;
_set_price_mults_automatically = false;
_tick_factors = TickUtils.get_regional_ticks(_synthetic.immutable()).low_tick;
_tick_factors = TickUtils.get_regional_ticks(_synthetic.ContractSpec).low_tick;
} else {
_deduce_name();
_deduce_ticks();
@@ -411,7 +411,7 @@ namespace mbts.spark {
}
void _deduce_ticks() {
_tick_factors = TickUtils.deduce_ticks(leg_tuples.Select(l => l.leg.Security.immutable()));
_tick_factors = TickUtils.deduce_ticks(leg_tuples.Select(l => l.leg.Security.ContractSpec));
RaisePropertyChanged(nameof(tick_factors));
}
@@ -371,7 +371,6 @@
<usp:AlgoEditorView
DataContext="{Binding selected_definition}"
Background="Transparent"
Foreground="{u:BTSColor UI.default_text}"
BorderBrush="{Binding algo_root, Converter={StaticResource OrderTypeBrushConverter}}"
Padding="12,8" />
</ScrollViewer>
@@ -79,7 +79,7 @@ namespace Spark.Trading {
meta.Value.Value.PriceDisplay : Security.Defaults.price_display_settingsi;
if (security.HasValue) {
rv = rv.with(
tick_factors: security.Value.tick_factors().to_api_tick_factors());
tick_factors: security.spec_or_default().tick_factors().to_api_tick_factors());
}
return rv;
}
@@ -218,10 +218,14 @@ namespace mbts.spark {
background = buy_sell_background_fill_color;
hoverbackground = _mk_hover_bg_brush(dark, _darkbg, 0);
(_num_root_orders, _filled, _quantity) = _root_nodes
.Select(n => (1u, n.filled_quantity(), n.quantity()))
.Sum();
_num_root_orders = 0;
_filled = 0;
_quantity = 0;
foreach (var n in _root_nodes) {
_num_root_orders++;
_filled += n.filled_quantity();
_quantity += n.quantity();
}
if (_num_root_orders == 1) {
var order = _root_nodes.First();
border = _owner.model.order_gateway.factory.get_metadata(order).Color.rgb_brush();
@@ -241,7 +245,7 @@ namespace mbts.spark {
}
public OrderBookVM? create_orderbook_vm() {
var ticks = _owner.spec.count_ticks(real_price);
var ticks = _owner.spec.full_ticks_from_zero(real_price);
if (!ticks.HasValue) {
return null;
}
@@ -191,7 +191,7 @@ namespace mbts.spark {
void increment_ticks_per_row() {
var tpr = ticks_per_row;
for (uint i = 1; i < 100; i++) {
var new_tpr = TickUtils.fit_ticks_per_row_to_dollars(security, ticks_per_row + i);
var new_tpr = TickUtils.fit_ticks_per_row_to_dollars(security.ContractSpec, ticks_per_row + i);
if (new_tpr != tpr) {
ticks_per_row = new_tpr;
break;
@@ -201,7 +201,7 @@ namespace mbts.spark {
void decrement_ticks_per_row() {
var tpr = ticks_per_row;
for (uint i = 1; i < tpr; i++) {
var new_tpr = TickUtils.fit_ticks_per_row_to_dollars(security, tpr - i);
var new_tpr = TickUtils.fit_ticks_per_row_to_dollars(security.ContractSpec, tpr - i);
if (new_tpr != tpr) {
ticks_per_row = new_tpr;
break;
@@ -239,8 +239,7 @@ namespace mbts.spark {
// since settings profiles may be shared among ladders
// of different securities/tick sizes, the ticks per row setting
// may need to be rounded to a number that is a factor of $1.00
public uint? rounded_ticks_per_row =>
TickUtils.fit_ticks_per_row_to_dollars(_security, ticks_per_row);
public uint? rounded_ticks_per_row => TickUtils.fit_ticks_per_row_to_dollars(_security.ContractSpec, ticks_per_row);
public MarketSizeDisplayVM size_display { get; } = new MarketSizeDisplayVM();
public SimpleGridSettingsVM mini_ob { get; } = new SimpleGridSettingsVM(OrderBookGridSettingsManager.hard_column_flags);
@@ -99,7 +99,7 @@ namespace mbts.spark {
IReadOnlyDictionary<uint, OrderSecurity> _cache = DictionaryHelper.empty<uint, OrderSecurity>();
ImmutableDictionary<AlgoKey, LadderFavoriteAlgoMetadata> _favorite_algo_metadata_map =
ImmutableDictionary<AlgoKey, LadderFavoriteAlgoMetadata>.Empty;
RegionalTickFactors? _spec;
ContractSpecificationImmutable? _spec;
LadderVM(
IBus bus,
@@ -243,9 +243,10 @@ namespace mbts.spark {
var orders_source = settings.Select(s => s.ticks_per_row).DistinctUntilChanged()
.SelectSeries(tpr => {
var spec = TickUtils.multiply_tick_size(security.regional_ticks(), tpr);
var spec = TickUtils.multiply_tick_size(security.ContractSpec, tpr);
var price_comp = Comparer<double>.Create((a, b) =>
Comparer<long?>.Default.Compare(spec.count_ticks(a), spec.count_ticks(b)));
Comparer<int?>.Default.Compare(
spec.full_ticks_from_zero(a), spec.full_ticks_from_zero(b)));
return _order_price_source(model.orders, price_comp, p => TickUtils.normalize_price(spec, p));
});
_day_change.Changed += (o, n) => RaisePropertyChanged(
@@ -393,10 +394,9 @@ namespace mbts.spark {
}
public SecurityImmutable security => _model.security;
public RegionalTickFactors spec => _spec ?? (
public ContractSpecificationImmutable spec => _spec ?? (
_spec = TickUtils.multiply_tick_size(
security.regional_ticks(), current_settings?.ticks_per_row ?? 1))
?? RegionalTickFactors.Default;
security.ContractSpec, current_settings?.ticks_per_row ?? 1)).Value;
public double display_quantity_divisor { get; }
public LadderSettings current_settings { get; private set; }
@@ -826,7 +826,7 @@ namespace mbts.spark {
if (!price.is_real()) {
msg = "not a real number";
} else if (settlement.is_real()) {
var ticks_from_settle = security.tickdiff(price, settlement);
var ticks_from_settle = security.ContractSpec.tickdiff(price, settlement);
if (ticks_from_settle.HasValue) {
var max_cross = (current_settings?.ladder_order_limits?.max_settlement_cross_ticks)
.as_nullable();
@@ -858,7 +858,7 @@ namespace mbts.spark {
} else {
ulong _ = 0;
_model.md.get_bbo(ref _, out var bid, out var ask);
var mid = TickUtils.mid_tick(security.regional_ticks(), bid.price, ask.price);
var mid = TickUtils.mid_tick(security.ContractSpec, bid.price, ask.price);
_day_change.set(mid - md.settlement_price);
}
_volume.set(volume);
@@ -32,8 +32,6 @@ using System.Windows.Media.Animation;
using System.Windows.Threading;
namespace mbts.spark {
using RegionalTickFactors = user.settings.RegionalTickFactors;
partial class LadderGridHost : Control {
static readonly int _initial_row_count;
static readonly bool _show_scrollbar;
@@ -85,6 +83,7 @@ namespace mbts.spark {
bool _price_dirty = false, _piq_dirty = false;
LadderPriceList _price_list = new() {
length = _initial_row_count,
spec = Security.Defaults.ContractSpecificationImmutable
};
LadderPriceList _pending_price_list = null;
IComparer<double> _price_comparer;
@@ -385,16 +384,16 @@ namespace mbts.spark {
Column[] _columns { get; set; } = Array.Empty<Column>();
IEnumerable<Column> _visible_columns => _columns.Where(c => c.settings.show);
RegionalTickFactors _spec_backing;
RegionalTickFactors _spec {
ContractSpecificationImmutable? _spec_backing;
ContractSpecificationImmutable _spec {
get {
if (_spec_backing is not null) {
return _spec_backing;
if (_spec_backing.HasValue) {
return _spec_backing.Value;
} else if (_vm != null) {
_spec_backing = TickUtils.multiply_tick_size(security.regional_ticks(), _settings?.ticks_per_row ?? 1);
return _spec_backing;
_spec_backing = TickUtils.multiply_tick_size(security.ContractSpec, _settings?.ticks_per_row ?? 1);
return _spec_backing.Value;
}
return RegionalTickFactors.Default;
return Security.Defaults.ContractSpecificationImmutable;
}
}
@@ -448,15 +447,6 @@ namespace mbts.spark {
return _price_list.high_tick + tick_diff;
}
public bool scroll_to_tick(long tick) {
if (tick > int.MaxValue) {
Log.Ui.error(
$"Unable to scroll to tick {tick} because it exceeds int.MaxValue");
return false;
}
return scroll_to_tick((int)tick);
}
public bool scroll_to_tick(int tick) {
if (_vm is null || _settings is null || _scroll_viewer is null) {
return false;
@@ -470,23 +460,21 @@ namespace mbts.spark {
// The scrollbar's vertical offset won't change. Find the 'top tick' that will put the
// target `tick` in the center of the viewport.
checked {
var top_tick = (int)_get_target_top_tick(tick);
if (_settings.animate_recenter) {
var anim = new Int32Animation {
From = (int)_price_list.high_tick,
To = top_tick,
Duration = TimeSpan.FromSeconds(0.15)
};
var sb = new Storyboard();
sb.Children.Add(anim);
Storyboard.SetTarget(anim, this);
Storyboard.SetTargetProperty(anim, new PropertyPath(TopTickProperty));
sb.Begin();
} else {
_set_scrollbar_range(high_tick: top_tick);
_refresh_prices();
}
var top_tick = (int)_get_target_top_tick(tick);
if (_settings.animate_recenter) {
var anim = new Int32Animation {
From = _price_list.high_tick,
To = top_tick,
Duration = TimeSpan.FromSeconds(0.15)
};
var sb = new Storyboard();
sb.Children.Add(anim);
Storyboard.SetTarget(anim, this);
Storyboard.SetTargetProperty(anim, new PropertyPath(TopTickProperty));
sb.Begin();
} else {
_set_scrollbar_range(high_tick: top_tick);
_refresh_prices();
}
return true;
}
@@ -756,12 +744,12 @@ namespace mbts.spark {
internal double price_for_point(Point point) =>
_price_for_row(_row_for_point(point));
static uint? _size_from_row(int row, IList<double> price_list, NativePriceSize[] depth, RegionalTickFactors spec) {
static uint? _size_from_row(int row, IList<double> price_list, NativePriceSize[] depth, ContractSpecificationImmutable spec) {
if (price_list == null || depth == null) return null;
else return row <= price_list.Count ? _size_from_price(price_list[row], depth, spec) : null;
}
static uint? _size_from_price(double price, NativePriceSize[] depth, RegionalTickFactors spec) {
static uint? _size_from_price(double price, NativePriceSize[] depth, ContractSpecificationImmutable spec) {
if (!double.IsNaN(price) && depth.TryGetFirst(nps => spec.tick_equals(price, nps.price), out NativePriceSize result))
return result.size;
return null;
@@ -1111,16 +1099,10 @@ namespace mbts.spark {
Log.assert(_price_dirty);
_price_list = new LadderPriceList {
length = _initial_row_count,
security = security
spec = security.ContractSpec
};
var mid_tick = security.tickdiff(_mkt_break_lines.origin, 0) ?? 0;
if (mid_tick > int.MaxValue) {
Log.Ui.error(
$"Unable to set scrollbar range about mid_tick {mid_tick}: " +
$"tick count exceeds int.MaxValue");
} else {
_set_scrollbar_range(high_tick: (int)mid_tick + (_price_list.Count << 1));
}
var mid_tick = security.ContractSpec.tickdiff(_mkt_break_lines.origin, 0) ?? 0;
_set_scrollbar_range(high_tick: mid_tick + (_price_list.Count << 1));
_price_dirty = true;
}
if (_price_dirty) {
@@ -1199,12 +1181,12 @@ namespace mbts.spark {
_price_list, _vm.md.settlement_price,
_settings.net_change == LadderSettings.NetChangeTerms.Ticks,
_settings.row_height, _settings.price_column,
_spec);
security.ContractSpec);
} else {
LadderRender.render_prices(
_price_vis_tup.visual, _price_vis_tup.glyph_context,
_price_list, _settings.row_height,
_settings.price_column, _spec,
_settings.price_column, security.ContractSpec,
_settings.price_display_format);
}
}
@@ -1359,7 +1341,7 @@ namespace mbts.spark {
return;
if (_price_list is null) {
_price_list = new LadderPriceList {
security = security
spec = security.ContractSpec
};
}
_price_list.reset(null, null, _initial_row_count);
@@ -1443,7 +1425,7 @@ namespace mbts.spark {
_pending_scroll_to_origin |= _settings == null;
_invalidate_spec();
if (_price_list != null) {
_price_list.security = security;
_price_list.spec = _spec;
}
_bid_vis_tup.glyph_context = glyph_context(s.bid_column);
_ask_vis_tup.glyph_context = glyph_context(s.ask_column);
@@ -1,11 +1,9 @@
using bts.api;
namespace mbts.spark {
using RegionalTickFactors = user.settings.RegionalTickFactors;
class LadderPriceLineViewAdapter {
public static double bid_line(
double bid, double ask, double settlement, RegionalTickFactors spec) {
double bid, double ask, double settlement, ContractSpecificationImmutable? spec) {
return TickUtils.normalize_price(spec, impl());
double impl() {
if (!double.IsNaN(bid)) {
@@ -19,7 +17,7 @@ namespace mbts.spark {
}
public static double ask_line(
double bid, double ask, double settlement, RegionalTickFactors spec) {
double bid, double ask, double settlement, ContractSpecificationImmutable? spec) {
return TickUtils.normalize_price(spec, impl());
double impl() {
if (!double.IsNaN(ask)) {
@@ -33,7 +31,7 @@ namespace mbts.spark {
}
public static double mid_line(
double bid, double ask, double settlement, RegionalTickFactors spec) {
double bid, double ask, double settlement, ContractSpecificationImmutable? spec) {
return TickUtils.normalize_price(spec, impl());
double impl() {
if (double.IsNaN(bid)) {
@@ -45,16 +43,16 @@ namespace mbts.spark {
if (double.IsNaN(ask)) {
return bid;
}
if (spec is not null) {
return TickUtils.mid_tick(spec, bid, ask);
if (spec.HasValue) {
return TickUtils.mid_tick(spec.Value, bid, ask);
}
return bid;
}
}
static double _try_tick_down(RegionalTickFactors spec, double value) {
if (spec is not null) {
return spec.tickdown(value);
static double _try_tick_down(ContractSpecificationImmutable? spec, double value) {
if (spec.HasValue) {
return spec.Value.tickdown(value);
}
return value;
}
@@ -1,5 +1,4 @@
using bts.api;
using bts.utils;
using mbts.utils;
@@ -9,78 +8,52 @@ using System.Collections.Generic;
using System.Collections.Specialized;
namespace mbts.spark {
using RegionalTickFactors = user.settings.RegionalTickFactors;
public class LadderPriceList : IReadOnlyList<double>, IList<double>, INotifyCollectionChanged, IEquatable<LadderPriceList> {
const long _MAX_LENGTH = int.MaxValue;
long _length;
long _high_tick;
SecurityImmutable? _security;
RegionalTickFactors _regional_tick_backing;
RegionalTickFactors _regional_tick =>
_regional_tick_backing ??= TickUtils.get_regional_ticks(_security);
int _length;
int _high_tick;
public LadderPriceList() { }
public LadderPriceList(LadderPriceList other) {
_length = other._length;
_high_tick = other._high_tick;
_security = other._security;
spec = other.spec;
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
public SecurityImmutable? security {
get => _security;
set {
if (value == _security) {
return;
}
_security = value;
_regional_tick_backing = null;
}
}
public ContractSpecificationImmutable spec { get; set; }
public long high_tick {
public int high_tick {
get => _high_tick;
set => _high_tick = value;
}
public long low_tick {
public int low_tick {
get => high_tick - length;
set => reset(null, value, null);
}
public double start {
get => _regional_tick.tickup<double>(0, (uint)high_tick);
set => high_tick = _regional_tick.tickdiff(value, 0) ?? high_tick;
get => spec.tickup(0, high_tick);
set => high_tick = spec.tickdiff(value, 0) ?? high_tick;
}
public long length {
public int length {
get => _length;
set {
if (value < 0 || value > _MAX_LENGTH) {
throw new ArgumentException(
$"Value 'v' must be >= 0 and < {_MAX_LENGTH} (actually {value})");
if (value < 0) {
throw new ArgumentException($"Value must be >= 0 (actually {value})");
}
_length = value;
}
}
public int Count {
get {
checked {
return (int)length;
}
}
}
public int Count => (int)length;
public bool IsReadOnly => false;
public double this[int idx] {
get => _regional_tick.tick<double>(0, high_tick - idx);
get => spec.tickup(0, high_tick - idx);
set => throw new NotSupportedException();
}
@@ -94,13 +67,11 @@ namespace mbts.spark {
GetEnumerator();
public int IndexOf(double item) {
var idx = _regional_tick.tickdiff(start, item) ?? -1;
var idx = spec.tickdiff(start, item) ?? -1;
if (idx < 0 || idx >= length) {
return -1;
}
checked {
return (int)idx;
}
return idx;
}
public void Insert(int index, double item) =>
@@ -167,15 +138,15 @@ namespace mbts.spark {
}
}
public void reset(long? high_tick = null, long? low_tick = null, long? length = null) {
public void reset(int? high_tick = null, int? low_tick = null, int? length = null) {
if (!high_tick.HasValue && !low_tick.HasValue && !length.HasValue) {
return;
}
if (length.HasValue && length.Value < 0) {
throw new ArgumentException("Must be >= 0", nameof(length));
}
long new_high_tick = 0;
long new_length = 0;
int new_high_tick = 0;
int new_length = 0;
if (high_tick.HasValue) {
new_high_tick = high_tick.Value;
if (low_tick.HasValue) {
@@ -209,7 +180,8 @@ namespace mbts.spark {
other != null &&
_high_tick == other._high_tick &&
_length == other._length &&
_regional_tick.safe_equals(other._regional_tick);
spec.LowTick.Equals(other.spec.LowTick) &&
spec.PriceBreak.Equals(other.spec.PriceBreak);
public override bool Equals(object obj) =>
Equals(obj as LadderPriceList);
@@ -217,10 +189,13 @@ namespace mbts.spark {
public override int GetHashCode() =>
HashCode.Combine(_high_tick, _length);
double _tick_to_price(long tick) =>
_regional_tick.tick<double>(0, tick);
double _tick_to_price(int tick) =>
spec.tickup(0, tick);
void _add_tick(long tick) {
int? _price_to_tick(double price) =>
spec.tickdiff(price, 0);
void _add_tick(int tick) {
int idx;
if (length == 0) {
_high_tick = tick;
@@ -229,9 +204,7 @@ namespace mbts.spark {
_high_tick = tick;
idx = 0;
} else if (tick == low_tick - 1) {
checked {
idx = (int)length;
}
idx = length;
} else {
throw new ArgumentException($"Must be == {nameof(high_tick)} ({high_tick}) + 1, or == {nameof(low_tick)} ({low_tick}) - 1)");
}
@@ -239,7 +212,7 @@ namespace mbts.spark {
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, _tick_to_price(tick), idx));
}
void _remove_tick(long tick) {
void _remove_tick(int tick) {
if (length == 0) {
throw new InvalidOperationException("Cannot remove tick because length is 0");
}
@@ -248,9 +221,7 @@ namespace mbts.spark {
_high_tick--;
idx = 0;
} else if (tick == low_tick) {
checked {
idx = (int)(length - 1);
}
idx = length - 1;
} else {
throw new ArgumentException(
$"Can only remove {nameof(low_tick)} ({low_tick}) or " +
@@ -9,7 +9,6 @@ using mbts.ui;
using mbts.ui.controls;
using mbts.ui.utility.text;
using Spark.Core;
using Spark.Core.Disposables;
using System;
@@ -20,8 +19,6 @@ using System.Windows;
using System.Windows.Media;
namespace mbts.spark {
using RegionalTickFactors = user.settings.RegionalTickFactors;
static class BadgeRender {
// IMPORTANT: the return values for this method MUST BE IN SYNC with the "format" method, else "measure_height" and "LadderRender.measure_row_height" will be broken.
static int _line_count_for_style(LadderSettings.BadgeStyle badge_style) => badge_style == LadderSettings.BadgeStyle.Alternate ? 2 : 1;
@@ -76,16 +73,6 @@ namespace mbts.spark {
}
public static int index_of(IList<double> prices, double price, IComparer<double> comparer, bool relative_idx) {
for (int i = 1; i < prices.Count; i++) {
Log.assert(prices[i - 1] > prices[i]);
var c = comparer.Compare(prices[i - 1], prices[i]);
if (c >= 0) {
c = comparer.Compare(prices[i - 1], prices[i]);
} else {
c = comparer.Compare(prices[i - 1], prices[i]);
}
Log.assert(c < 0);
}
var idx = CollectionHelper.binary_search(prices, price, comparer);
if (relative_idx && idx < 0) {
idx = ~idx;
@@ -108,13 +95,30 @@ namespace mbts.spark {
settings.font_size, settings.width, row_height, HorizontalAlignment.Right,
-4, VerticalAlignment.Center, row * row_height, true);
public static GlyphRun[] format_prices(IList<double> prices, double reference_price, bool format_in_tick_terms, double row_height, LadderColumnSettings settings, GlyphContext glyph_context, RegionalTickFactors spec) {
public static IList<double> gen_prices(double mid, int row_count, ContractSpecificationImmutable spec) {
bool is_odd = row_count % 2 == 1;
var result = new double[row_count];
int range = row_count / 2;
int middle = row_count / 2;
result[middle] = mid;
double up = mid, down = mid;
// Starting from the middle iterate out to the edges
for (int x = 1; x <= range; ++x) {
up = spec.tickup(up);
down = spec.tickdown(down);
result[middle - x] = up;
result[middle + x] = down;
}
return result;
}
public static GlyphRun[] format_prices(IList<double> prices, double reference_price, bool format_in_tick_terms, double row_height, LadderColumnSettings settings, GlyphContext glyph_context, ContractSpecificationImmutable spec) {
var result = new GlyphRun[prices.Count];
Parallel.For(0, prices.Count, i =>
result[i] = format_text(spec.format(prices[i] - reference_price, format_in_tick_terms), i, row_height, settings, glyph_context));
return result;
}
public static GlyphRun[] format_prices(IList<double> prices, double row_height, LadderColumnSettings settings, GlyphContext glyph_context, RegionalTickFactors spec, LadderSettings.PriceDisplayFormat price_display_format) {
public static GlyphRun[] format_prices(IList<double> prices, double row_height, LadderColumnSettings settings, GlyphContext glyph_context, ContractSpecificationImmutable spec, LadderSettings.PriceDisplayFormat price_display_format) {
var result = new GlyphRun[prices.Count];
Parallel.For(0, prices.Count, i =>
result[i] = format_text(spec.format(prices[i]), i, row_height, settings, glyph_context, price_display_format));
@@ -139,12 +143,12 @@ namespace mbts.spark {
bool format_in_tick_terms,
double row_height,
LadderColumnSettings settings,
RegionalTickFactors tick) {
ContractSpecificationImmutable spec) {
if (price_list.IsNullOrEmpty()) {
return;
}
render_prices(
price, glyph_context, price_list, row_height, settings, tick,
price, glyph_context, price_list, row_height, settings, spec,
wrt_price, format_in_tick_terms);
}
@@ -154,12 +158,12 @@ namespace mbts.spark {
IList<double> dprices,
double row_height,
LadderColumnSettings settings,
RegionalTickFactors tick,
ContractSpecificationImmutable spec,
double wrt_price,
bool format_in_tick_terms) {
var foreground = settings.foreground.rgb_brush();
//var background = new SolidColorBrush(settings.background.rgb_color());
var fprices = format_prices(dprices, wrt_price, format_in_tick_terms, row_height, settings, glyph_context, tick);
var fprices = format_prices(dprices, wrt_price, format_in_tick_terms, row_height, settings, glyph_context, spec);
using (var dc = price.RenderOpen())
using (_draw_with_clip(dc, settings, row_height, dprices.Count)) {
//dc.DrawRectangle(background, null, new Rect(0, 0, settings.width, row_count * row_height));
@@ -174,11 +178,11 @@ namespace mbts.spark {
IList<double> dprices,
double row_height,
LadderColumnSettings settings,
RegionalTickFactors tick,
ContractSpecificationImmutable spec,
LadderSettings.PriceDisplayFormat price_display_format) {
var foreground = settings.foreground.rgb_brush();
//var background = new SolidColorBrush(settings.background.rgb_color());
var fprices = format_prices(dprices, row_height, settings, glyph_context, tick, price_display_format);
var fprices = format_prices(dprices, row_height, settings, glyph_context, spec, price_display_format);
using (var dc = price.RenderOpen())
using (_draw_with_clip(dc, settings, row_height, dprices.Count)) {
//dc.DrawRectangle(background, null, new Rect(0, 0, settings.width, row_count * row_height));
@@ -211,7 +215,7 @@ namespace mbts.spark {
IList<double> prices,
NativePriceSize[] rungs,
double row_height,
RegionalTickFactors spec,
ContractSpecificationImmutable spec,
LadderColumnSettings settings,
Func<NativePriceSize, string> fmt,
bool lighten_market_cells,
@@ -244,18 +248,17 @@ namespace mbts.spark {
direct_size: aggr.direct_size + rung.direct_size,
implied_size: aggr.implied_size + rung.implied_size),
rung => {
var ticks = spec.count_ticks(rung.price);
//spec.round_to_tick(rung.price);
var ticks = spec.full_ticks_from_zero(rung.price);
return ticks.HasValue ? spec.tickup(0, ticks.Value) : rung.price;
});
foreach (var rung in row_tick_groups) {
//double price = spec.round_to_tick(rung.price);
var ticks = spec.count_ticks(rung.price);
var ticks = spec.full_ticks_from_zero(rung.price);
var price = ticks.HasValue ? spec.tickup(0, ticks.Value) : rung.price;
// prices are in descending order, so get diff from curr_price - price
var tick_diff = spec.tickdiff(curr_price, price).assert_value();
checked {
curr_index += (int)tick_diff;
}
int tick_diff = spec.tickdiff_unchecked(curr_price, price);
curr_index += tick_diff;
curr_price = price;
if (curr_index >= 0 && curr_index < prices.Count) {
if (market_cell_background_brush != null)
@@ -275,7 +278,7 @@ namespace mbts.spark {
IList<double> prices,
IEnumerable<VolumeAtPriceLevelImmutable> data,
double row_height,
RegionalTickFactors spec,
ContractSpecificationImmutable spec,
VolumeColumnSettings settings,
int last_index,
string last_size_and_direction,
@@ -368,7 +371,7 @@ namespace mbts.spark {
public delegate void RenderCell<T>(DrawingContext dc, int row, T data_item);
static void _render_cells<T>(DrawingVisual dv, double row_height, IEnumerable<(double price, T value)> data, LadderColumnSettings settings, IList<double> prices, RegionalTickFactors spec, RenderCell<T> render_cell) {
static void _render_cells<T>(DrawingVisual dv, double row_height, IEnumerable<(double price, T value)> data, LadderColumnSettings settings, IList<double> prices, ContractSpecificationImmutable spec, RenderCell<T> render_cell) {
using (var dc = dv.RenderOpen())
using (_draw_with_clip(dc, settings, row_height, prices.Count)) {
if (data.is_null_or_empty())
@@ -379,11 +382,8 @@ namespace mbts.spark {
if (double.IsNaN(price))
continue;
// prices are in descending order, so get diff from curr_price - price
var tick_diff = spec.tickdiff(curr_price, price) ?? 0;
Log.assert(
tick_diff > (int.MaxValue - curr_index),
$"Unable to index ladder row for price {price}: tick count exceeds int.MaxValue");
curr_index += (int)tick_diff;
int tick_diff = spec.tickdiff(curr_price, price) ?? 0;
curr_index += tick_diff;
curr_price = price;
if (curr_index >= 0 && curr_index < prices.Count) {
render_cell(dc, curr_index, value);
@@ -409,7 +409,7 @@ namespace mbts.spark {
IList<double> prices,
IEnumerable<(double price, uint piq)> pvp,
double row_height,
RegionalTickFactors spec,
ContractSpecificationImmutable spec,
LadderColumnSettings settings,
Func<uint, string> fmt_value) {
//
@@ -425,10 +425,8 @@ namespace mbts.spark {
foreach (var entry in pvp) {
double price = entry.price;
// prices are in descending order, so get diff from curr_price - price
var tick_diff = spec.tickdiff(curr_price, price).assert_value();
checked {
curr_index += (int)tick_diff;
}
int tick_diff = spec.tickdiff_unchecked(curr_price, price);
curr_index += tick_diff;
curr_price = price;
if (curr_index >= 0 && curr_index < prices.Count) {
var gr = format_text(fmt_value(entry.piq), curr_index, row_height, settings, glyph_context);
@@ -31,12 +31,10 @@
ItemsSource="{Binding rows_view}"
MinRowHeight="17"
RowHeaderWidth="22"
Background="{u:BTSColor ManualFills.background}"
>
<!--Row Header Style-->
<DataGrid.RowHeaderStyle>
<Style TargetType="DataGridRowHeader">
<Setter Property="Background" Value="transparent"/>
<Setter Property="Content" Value="{Binding}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
@@ -125,12 +123,8 @@
<!--Row Style-->
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{u:BTSColor Grid.default_row_background}"/>
<Setter Property="IsSelected" Value="{Binding is_selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Style.Triggers>
<Trigger Property="DataGridRow.AlternationIndex" Value="1">
<Setter Property="Background" Value="{u:BTSColor Grid.alternate_row_background}"/>
</Trigger>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="{StaticResource SelectedRowBackground}"/>
</DataTrigger>
@@ -73,7 +73,7 @@ namespace Spark.Trading {
meta.Value.Value.PriceDisplay : Security.Defaults.price_display_settingsi;
if (security.HasValue) {
rv = rv.with(
tick_factors: security.Value.regional_ticks().low_tick.to_api_tick_factors());
tick_factors: security.spec_or_default().tick_factors().to_api_tick_factors());
}
return rv;
}
@@ -769,7 +769,7 @@ namespace mbts.spark {
return;
}
var price_display = TickUtils.get_tick(row.security, price ?? 0).to_api_tick_display_settingsi();
var price_display = row.contract_spec?.tick_factors()?.to_api_tick_display_settingsi();
var update_desc = OrderUtils.update_description(
row.order, price, quantity, price_display, row.quantity_display_settings);
Log.Ui.info(
@@ -179,7 +179,8 @@ namespace mbts.spark {
using var _ = detect_property_changes(
nameof(spec),
nameof(display_quantity_divisor),
nameof(quantity_display));
nameof(quantity_display),
nameof(unit_quantity));
_security_md = value;
bbo.security_md = value;
_payup_bid_cmd.RaiseCanExecuteChanged();
@@ -209,6 +210,9 @@ namespace mbts.spark {
}
}
public double unit_quantity =>
(security?.ContractSpec)?.tick_factors()?.tick_unit_frac() ?? 1;
public AlgoDefinitionVM algo_vm { get; }
public IReadOnlyObservableCollection<AlgoParamRowVM> param_rows { get; }
public BBOVM bbo { get; }
+8 -30
View File
@@ -1,12 +1,10 @@
using mbts.analytics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BCG {
using Size = System.Single;
public enum BidAskAlertZone { buy, sell, none }
public class BBOPacket {
public enum MarketSide {
@@ -15,8 +13,8 @@ namespace BCG {
private double _bid_price => md.bid_price;
private double _ask_price => md.ask_price;
private UInt32 _bid_size => md.bid_size;
private UInt32 _ask_size => md.ask_size;
private Size _bid_size => md.bid_size;
private Size _ask_size => md.ask_size;
private static BBOPacket _null_pack = new BBOPacket(VolSurfaceMDUnit.empty);
public readonly VolSurfaceMDUnit md;
@@ -24,21 +22,6 @@ namespace BCG {
this.md = md;
}
public static bool bid_through_theo(BBOPacket md, mbts.spark.TheoPoint tp, bool is_call) {
return false;
// if (tp != null && md != null && md._md_row != null)
// return get_bid_price(md) > (is_call ? tp.call : tp.put);
// else
// return false;
}
public static bool ask_through_theo(BBOPacket md, mbts.spark.TheoPoint tp, bool is_call) {
// if (tp != null && md != null && md._md_row != null)
// return get_ask_price(md) < (is_call ? tp.call : tp.put);
// else
return false;
}
public static double get_mid_price(BBOPacket md) {
return (get_bid_price(md) + get_ask_price(md)) / 2.0;
}
@@ -47,21 +30,16 @@ namespace BCG {
return md != null ? md._bid_price : Double.NaN;
}
public static UInt32 get_bid_size(BBOPacket md) {
return md != null ? md._bid_size : (UInt32)0;
public static Size get_bid_size(BBOPacket md) {
return md != null ? md._bid_size : (Size)0;
}
public static double get_ask_price(BBOPacket md) {
return md != null ? md._ask_price : Double.NaN;
}
public static UInt32 get_ask_size(BBOPacket md) {
return md != null ? md._ask_size : (UInt32)0;
}
public static Tuple<double, UInt32> get_md(BBOPacket md, bool is_bid) {
double price = is_bid ? BBOPacket.get_bid_price(md) : BBOPacket.get_ask_price(md);
UInt32 sz = is_bid ? BBOPacket.get_bid_size(md) : BBOPacket.get_ask_size(md);
return Tuple.Create(price, sz);
public static Size get_ask_size(BBOPacket md) {
return md != null ? md._ask_size : (Size)0;
}
}
}
@@ -123,15 +123,6 @@ namespace BCG {
#endregion
#region Orders
[System.ComponentModel.Category("Orders / Algo")]
[System.ComponentModel.DisplayName("Default Account")]
public string Account { get; set; }
[System.ComponentModel.Category("Orders / Algo")]
[System.ComponentModel.DisplayName("Default Qty")]
public Nullable<double> DefaultQty { get; set; }
#endregion
public NMSheetSettingLayout() {
show_settings_header = false;
show_exp_navigator = false;
@@ -18,7 +18,6 @@ using mbts.spark.filters;
using System.Collections;
using System.Reactive.Linq;
using System.Threading;
using bts.api.orderman;
namespace BCG {
public partial class NMSheetControl : UserControl {
@@ -556,17 +555,6 @@ namespace BCG {
}
private void _launch_order_entry_window(NMSheetTheoGrid.GridCell cell,
OrderEntryWindow window_type) {
Func<string, bts.AccountTuple> get_account = (string default_acc) => {
string account = _settings.setting_layout.Account;
if (String.IsNullOrEmpty(account)) {
account = default_acc;
}
Nullable<uint> id = _get_account_id(account);
if (!id.HasValue) {
id = 0;
}
return new bts.AccountTuple(id.Value, account);
};
uint key;
var found = _sheet_data.get_security_key(cell.expiration,
cell.strike, cell.call_selected, out key);
@@ -578,20 +566,7 @@ namespace BCG {
if (sc_data.sec_md_map.TryGetValue(key, out var smd)) {
switch (window_type) {
case OrderEntryWindow.Ticket:
bool is_bid = NMSheetSettingColumnOrganization.get_column_info(cell.col).is_bid;
bts.AccountTuple acc_tup = get_account("SMALL");
Nullable<double> qty = _settings.setting_layout.DefaultQty;
OrderDefinition od = new OrderDefinition() {
SecurityKey = smd.id,
TimeInForce = TimeInForce.Gtc,
Account = acc_tup.name,
AccountId = acc_tup.id
};
BBOPacket bbo = _sheet_data.get_bbo(smd.id);
Tuple<double, UInt32> pr_sz = BBOPacket.get_md(bbo, is_bid);
UInt32 effective_qty = qty.HasValue ? Convert.ToUInt32(Math.Abs(qty.Value)) : pr_sz.Item2;
_threaded_bb.post(SparkTags.ShowOrderTicketTag,
(new OrderDefinitionImmutable(od), pr_sz.Item1, effective_qty));
_threaded_bb.post(SparkTags.ShowOrderTicketFromSecurityTag, smd);
break;
case OrderEntryWindow.Ladder:
_threaded_bb.post(SparkTags.ShowLadderTag, smd);
@@ -11,8 +11,6 @@ using System.Reactive.Linq;
using System.Windows.Input;
namespace mbts.spark {
using RegionalTickFactors = user.settings.RegionalTickFactors;
class InlineOrderTicketProperties {
public readonly InlineUserProperty<double> price;
public readonly InlineUserProperty<double> size;
@@ -104,7 +102,6 @@ namespace mbts.spark {
public SecurityImmutable security => _security.Value;
public double price { get; set; }
public RegionalTickFactors price_tick => security.regional_ticks();
public CalculatorLogic<double> size_calculator { get; }
public double display_quantity_divisor {
get => _display_quantity_divisor;
@@ -225,7 +225,10 @@ namespace bts.spark {
var security = try_get_security(order.Def.SecurityKey);
var change_descs = new List<string>(1);
var price_display = TickUtils.get_tick(security, price).to_api_tick_display_settingsi();
TickDisplaySettingsImmutable? price_display =
security.HasValue && security.Value.HasContractSpec ?
security.Value.ContractSpec.tick_factors().to_api_tick_display_settingsi() : null;
var price_qty_change_desc = OrderUtils.update_description(
order, price, size, price_display);
@@ -74,7 +74,7 @@
<usp:PriceEntryControl
Grid.ColumnSpan="2"
usp:OrderModificationControl.IsDirty="{Binding price_dirty, FallbackValue={x:Null}}"
RegionalTick="{Binding price_tick}"
ContractSpec="{Binding security.ContractSpec}"
Focusable="false"
TickDefaultValue="{Binding current_price, Converter={StaticResource TickDefaultPriceConverter}, FallbackValue={u:Double 0}}"
Value="{Binding price}"
+8 -5
View File
@@ -254,14 +254,17 @@ namespace mbts.spark {
$"{(order.HasDef ? order.Def.Side.ToString() : "(unknown-side)")} {(order.HasState ? ($@"{spec.format(order.State.Price)} @ {order.State.Quantity}") : "(unknown-price) @ (unknown-quantity)")}";
public static string short_description(
SecurityImmutable security, Side side, double price, double quantity) {
var price_display = TickUtils.get_tick(security, price).to_api_tick_display_settingsi();
return new StringBuilderEx(
SecurityImmutable security, Side side, double price, double quantity) =>
new StringBuilderEx(
security.ToString(), " ",
side.ToString(), " ",
price_quantity_description(price, quantity, price_display)
price_quantity_description(price, quantity, security.spec_or_default())
).ToString();
}
public static string price_quantity_description(
double price, double quantity, ContractSpecificationImmutable spec) =>
price_quantity_description(
price, quantity, spec.tick_factors().to_api_tick_display_settingsi());
public static string price_quantity_description(
double price,
@@ -190,13 +190,24 @@ namespace mbts.analytics {
}
}
void _compute_swipe_sum() =>
(swipe_sum_puts, swipe_sum_calls, swipe_sum_net) = _visible_selected_cells
.SelectMany(tup =>
tup.exps.Select(exp => {
var (p, c, n) = tup.row.get_cell_values(exp);
return (p.if_nan(0), c.if_nan(0), n.if_nan(0));
}))
.Sum();
void _compute_swipe_sum() {
swipe_sum_puts = 0;
swipe_sum_calls = 0;
swipe_sum_net = 0;
foreach (var (row, exps) in _visible_selected_cells) {
foreach (var exp in exps) {
var (p, c, n) = row.get_cell_values(exp);
if (!p.is_nan()) {
swipe_sum_puts += p;
}
if (!c.is_nan()) {
swipe_sum_calls += c;
}
if (!n.is_nan()) {
swipe_sum_net += n;
}
}
}
}
}
}
@@ -121,7 +121,7 @@ namespace mbts.analytics {
timer.stop();
Log.Ui.info($"PNLReportCalculator n_pos: {output.position_data.Count()} " +
$"n_day_trades: {output.day_trades.Count()} time(msecs): {timer.lastIntervalTime}");
//_log_before_posting(output.position_data);
_log_before_posting(output.position_data);
_bus.post(SparkTags.ReportBlackboardTags.PNL.PNLData, output);
});
}
@@ -115,21 +115,21 @@ namespace mbts.analytics {
}
private void _handle_nonvol_data(VolSurfaceNonVolData vs_nonvol_data) {
// bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_nonvol_data report {0}",
// _report.ReportName));
bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_nonvol_data report {0}",
_report.ReportName));
_vs_nonvol_data = vs_nonvol_data;
}
private void _handle_future_prices(
IReadOnlyDictionary<SecurityExpiration, FutureTheoStructure> future_prices_map) {
// bts.Log.Gen.Diag(String.Format("SlideReportCalculator._handle_future_prices report {0}",
// _report.ReportName));
bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_future_prices report {0}",
_report.ReportName));
_future_prices_map = future_prices_map.ToDictionary(kv => kv.Key, kv => kv.Value.theo);
}
private void _handle_trades(IEnumerable<ITradeLogRow> trades) {
// bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_trades report {0}",
// _report.ReportName));
bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_trades report {0}",
_report.ReportName));
_all_trades = trades.Where(t => t.is_active);
if (_report.CalcMode == Report.Types.ReportCalculationMode.PositionOrTheoChange) {
_compute_dirty = true;
+32 -20
View File
@@ -1,4 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
@@ -21,33 +22,39 @@
<RootNamespace>bts</RootNamespace>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
<Version>$(major).$(minor).$(patch)</Version>
<Version Condition="'$(version)'=='..'">1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="GitVersion.MsBuild" Version="5.12.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Google.Protobuf" Version="3.24.3" />
<PackageReference Include="IronPython" Version="3.4.1" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="NAudio" Version="2.0.1" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="OxyPlot.Core" Version="2.1.2" />
<PackageReference Include="OxyPlot.Wpf" Version="2.1.2" />
<PackageReference Include="protobuf-net" Version="3.2.26" />
<PackageReference Include="System.Composition" Version="7.0.0-preview.2.22152.2" />
<PackageReference Include="System.Composition.AttributedModel" Version="7.0.0-preview.2.22152.2" />
<PackageReference Include="System.Composition.Hosting" Version="7.0.0-preview.2.22152.2" />
<PackageReference Include="System.Composition.TypedParts" Version="7.0.0-preview.2.22152.2" />
<PackageReference Include="System.Reactive.Core" Version="5.0.0" />
<PackageReference Include="System.Reactive.Interfaces" Version="5.0.0" />
<PackageReference Include="System.Reactive.Linq" Version="5.0.0" />
<PackageReference Include="System.Reactive.PlatformServices" Version="5.0.0" />
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<ProjectReference Include="../spark_api/spark_api.csproj" />
<ProjectReference Include="../spark_core/spark_core.csproj" />
<PackageReference Include="Google.Protobuf" Version="3.21.4" />
<PackageReference Include="IronPython" Version="2.7.12" />
<PackageReference Include="NAudio" Version="2.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
<PackageReference Include="protobuf-net" Version="3.1.17" />
<PackageReference Include="OxyPlot.Core" Version="2.1.2" />
<PackageReference Include="OxyPlot.Wpf" Version="2.1.2" />
<PackageReference Include="System.Reactive" Version="6.0.0" />
<PackageReference Include="System.Reactive.Core" Version="6.0.0" />
<PackageReference Include="System.Reactive.Interfaces" Version="6.0.0" />
<PackageReference Include="System.Reactive.Linq" Version="6.0.0" />
<PackageReference Include="System.Reactive.PlatformServices" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\spark_api\spark_api.csproj" />
<ProjectReference Include="..\spark_core\spark_core.csproj" />
</ItemGroup>
<ItemGroup>
@@ -55,15 +62,20 @@
<Resource Include="**/*.png" />
<ConfigDefaults Include="../../lib/config/defaults/*.conf" />
</ItemGroup>
<ItemGroup>
<Resource Remove="logo.ico" />
</ItemGroup>
<ItemGroup>
<Content Include="logo.ico" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Sounds/game_sound_correct.wav" />
<EmbeddedResource Include="Sounds/whistle.wav" />
</ItemGroup>
<Target Name="diagnostic" AfterTargets="PreBuildEvent" />
</Project>
+2 -2
View File
@@ -35,7 +35,7 @@ namespace bts.core {
// TODO: for debugging purposes, remove it later
if (tag != mbts.spark.SparkTags.SparkTimer) {
var tid = System.Threading.Thread.CurrentThread.ManagedThreadId;
//bts.Log.Ui.diag($"SimpleBus.post tag:{tag} tid:{tid}");
bts.Log.Ui.diag($"SimpleBus.post tag:{tag} tid:{tid}");
return;
}
} catch (Exception e) {
@@ -45,7 +45,7 @@ namespace bts.core {
public IObservable<T> subscribe<T>(ITag<T> tag) {
var tid = System.Threading.Thread.CurrentThread.ManagedThreadId;
//bts.Log.Ui.info($"SimpleBus.subscribe tag:{tag} tid:{tid}");
bts.Log.Ui.info($"SimpleBus.post tag:{tag} tid:{tid}");
return subject<T>(tag);
}
}
+1 -7
View File
@@ -169,7 +169,7 @@ namespace mbts.spark {
public static ITag<int> CreateHeatmapTag { get; } = _mk_tag<int>();
// Publisher: VolSurfaceBlackboardManager
// User: VolSurfaceModule
// User: VoLSurfaceModule
public static ITag<IDictionary<int, string>> VolSurfaceListTag { get; } =
_mk_tag<IDictionary<int, string>>();
@@ -202,17 +202,11 @@ namespace mbts.spark {
public static ITag<VolSurfaceCalculationRequest> VolSurfaceCalculationRequestTag(int vs_id) =>
_mk_tag<VolSurfaceCalculationRequest, int>(vs_id);
public static ITag<VolSurfaceCalculationRequest> VolSurfaceAutoCalculationRequestTag(int vs_id) =>
_mk_tag<VolSurfaceCalculationRequest, int>(vs_id);
// Publisher : SingleVolSurfaceRequestCalculator
// Data : VolSurfaceCalculationResponse
public static ITag<VolSurfaceCalculationResponse> VolSurfaceCalculationResponseTag(int vs_id) =>
_mk_tag<VolSurfaceCalculationResponse, int>(vs_id);
public static ITag<VolSurfaceCalculationResponse> VolSurfaceAutoCalculationResponseTag(int vs_id) =>
_mk_tag<VolSurfaceCalculationResponse, int>(vs_id);
// When the UI intends to save the vol surface (data or input)
// Publisher: VolSurfaceDisplayModel
public static ITag<VolSurfaceInput> VolSurfaceCommitTag(int vs_id) =>
@@ -591,9 +591,9 @@ namespace mbts.ui {
// Disabled
public static BTSColor disabled_border => UI.input_disabled_border;
public static BTSColor disabled_background => Button.disabled_background;
public static BTSColor disabled_foreground => Button.disabled_foreground;
public static BTSColor disabled_text => Button.disabled_foreground;
public static BTSColor disabled_background => UI.input_disabled_background;
public static BTSColor disabled_foreground => normal_foreground;
public static BTSColor disabled_text => normal_text;
public static BTSColor disabled_glyph => normal_glyph;
public static BTSColor disabled_button_border => BTSCoreColors.transparent;
public static BTSColor disabled_button_background => BTSCoreColors.transparent;
@@ -647,7 +647,8 @@ namespace mbts.ui {
public static BTSColor pressed_editable_button_background => pressed_button_background;
public static BTSColor pressed_editable_button_border => pressed_button_border;
public static BTSColor disabled_editable_background => Core.transparent;
public static BTSColor disabled_editable_background =>
editable_background.blend(disabled_background);
public static BTSColor disabled_editable_border =>
editable_border.blend(disabled_border);
@@ -1041,21 +1042,6 @@ namespace mbts.ui {
public static BTSColor foreground => UI.default_text;
public static BTSColor footer_foreground => foreground;
}
public static class ExecutionStrategy {
public static BTSColor expander_background => Core.transparent;//Grid.dark_column_background;
public static BTSColor expander_foreground => UI.default_text;
public static BTSColor data_grid_row_header_background => Core.transparent;
public static BTSColor data_grid_row_header_foreground => UI.default_text;
public static BTSColor data_grid_background => Grid.default_row_background;
public static BTSColor default_column_background => Grid.default_row_background;
public static BTSColor alternate_column_background => Grid.alternate_row_background;// 0xff494957;
public static BTSColor arrow_background => UI.default_text;
public static BTSColor arrow_background_selected => UI.alternate_text;
}
public static class ManualFills {
public static BTSColor background => UI.common_background;
}
public static class AlgoStudio {
public static BTSColor background => UI.default_background;
public static BTSColor foreground => UI.default_foreground;
+1 -6
View File
@@ -76,11 +76,6 @@
<Color x:Key="ComboBox.Pressed.Editable.BackgroundColor">#707070</Color>
<Color x:Key="ComboBox.Pressed.Editable.Button.BackgroundColor">#FFC4E0FC</Color>
<Color x:Key="ComboBox.Disabled.Editable.BackgroundColor">#FFE5E5E5</Color>
<Color x:Key="ComboBox.Disabled.Editable.BorderColor">#FFD9D9D9</Color>
<Color x:Key="ComboBox.Disabled.Editable.Button.BackgroundColor">#FFE5E5E5</Color>
<Color x:Key="ComboBox.Disabled.Editable.Button.BorderColor">#FFD9D9D9</Color>
<SolidColorBrush x:Key="ComboBox.Static.Background" Color="{StaticResource ComboBox.Static.BackgroundColor}" />
<SolidColorBrush x:Key="ComboBox.Static.Foreground" Color="{StaticResource ComboBox.Static.ForegroundColor}" />
<SolidColorBrush x:Key="ComboBox.Static.Border" Color="{StaticResource ComboBox.Static.BorderColor}"/>
@@ -103,7 +98,7 @@
<SolidColorBrush x:Key="ComboBox.Disabled.Border" Color="{StaticResource ComboBoxDisabledBorderColor}"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Background" Color="{StaticResource ComboBox.Disabled.Editable.BackgroundColor}" />
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Border" Color="{StaticResource ComboBox.Disabled.Editable.BorderColor}" />
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Background" Color="{StaticResource ComboBox.Disabled.Editable.Button.BackgroundColor}" />
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Background" Color="{StaticResource ComboBox.Disabled.Editable.BackgroundColor}" />
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Border" Color="{StaticResource ComboBox.Disabled.Editable.Button.BorderColor}" />
<SolidColorBrush x:Key="ComboBox.Static.Glyph" Color="#FF606060"/>
@@ -35,7 +35,7 @@
<Setter Property="Focusable" Value="False"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Background" Value="{u:BTSColor UI.ScrollBar.thumb_pressed}"/>
<Setter Property="Background" Value="{u:BTSColor ScrollBar.thumb_pressed}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
@@ -224,18 +224,18 @@
<VisualState Name="Normal"/>
<VisualState Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="{u:BTSColor UI.ScrollBar.thumb_mouse_over}"/>
<ColorAnimation Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="{u:BTSColor ScrollBar.thumb_mouse_over}"/>
</Storyboard>
</VisualState>
<VisualState Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="{u:BTSColor UI.ScrollBar.thumb_pressed}"/>
<ColorAnimation Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="{u:BTSColor ScrollBar.thumb_pressed}"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Thumb.Background>
<SolidColorBrush Color="{u:BTSColor UI.ScrollBar.thumb_static}"/>
<SolidColorBrush Color="{u:BTSColor ScrollBar.thumb_static}"/>
</Thumb.Background>
</Thumb>
</Track.Thumb>
@@ -380,18 +380,18 @@
<VisualState Name="Normal"/>
<VisualState Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="{StaticResource u:BTSColor UI.ScrollBar.thumb_mouse_over}"/>
<ColorAnimation Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="{StaticResource u:BTSColor ScrollBar.thumb_mouse_over}"/>
</Storyboard>
</VisualState>
<VisualState Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="{u:BTSColor UI.ScrollBar.thumb_presseds}"/>
<ColorAnimation Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="{u:BTSColor ScrollBar.thumb_presseds}"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Thumb.Background>
<SolidColorBrush Color="{u:BTSColor UI.ScrollBar.thumb_static}"/>
<SolidColorBrush Color="{u:BTSColor ScrollBar.thumb_static}"/>
</Thumb.Background>
</Thumb>
</Track.Thumb>
@@ -15,16 +15,12 @@ using System.Windows.Controls;
using System.Windows.Input;
namespace mbts.ui.spark {
using RegionalTickFactors = user.settings.RegionalTickFactors;
using TickDisplaySettings = user.settings.TickDisplaySettings;
/// <summary>
/// Interaction logic for PriceEntryControl.xaml
/// </summary>
class PriceEntryControl : Control {
readonly PropertyUpdateGate _price_text_gate = new PropertyUpdateGate();
ContractSpecificationImmutable? _spec;
RegionalTickFactors _tick;
TextBox _text;
IDisposable _text_hooks;
double? _raw_value;
@@ -57,17 +53,16 @@ namespace mbts.ui.spark {
(o, v) => o._update_text(v),
(o, v) => {
o._raw_value = v;
if ((!o._spec.HasValue && o._tick is null) || DoubleHelper.IsNaNOrInfinity(v))
if (!o._spec.HasValue || DoubleHelper.IsNaNOrInfinity(v))
return v;
// Note: the decimal cast here is important bc double-rounding errors can cause the keyboard text to conflict with poorly rounded result, and negate Tick Up/Down commands.
// This happens bc the tick up/down commands flush the current text before inc/decrementing.
var v_dec = (decimal)v;
var rounded = (double)(o._tick?.round_to_tick(v_dec) ?? o._spec.Value.round_to_tick(v_dec));
if (MathUtils.epsilon_equals(rounded, v)) {
double tick = (double)o._spec.Value.round_to_tick((decimal)v);
if (MathUtils.epsilon_equals(tick, v)) {
// this value has already been rounded, don't make it re-enter the CoerceValueCallback delegate.
return v;
}
return rounded;
return tick;
},
double.NaN);
@@ -107,26 +102,6 @@ namespace mbts.ui.spark {
public ContractSpecificationImmutable? ContractSpec { get => GetValue(ContractSpecProperty) as ContractSpecificationImmutable?; set => SetValue(ContractSpecProperty, value); }
#endregion
#region RegionalTick
public static readonly DependencyProperty RegionalTickProperty =
WPFHelper.create_dp<RegionalTickFactors, PriceEntryControl>(
nameof(RegionalTick),
(o, v) => {
o._tick = v;
if (o._raw_value.HasValue) {
o.SetCurrentValue(ValueProperty, o._raw_value.Value);
if (o.AlwaysReformatOnSpecChange)
o._update_text();
}
}, RegionalTickFactors.Default);
public RegionalTickFactors RegionalTick {
get => GetValue(RegionalTickProperty) as RegionalTickFactors;
set => SetValue(RegionalTickProperty, value);
}
#endregion
/// TODO: verify that this control behaves correctly when <see cref="AlwaysReformatOnSpecChange"/> is true (ideally this prop will be removed, and this control will always behave is if the prop is true).
/// Because this control is ubiquitious in our application, such verification will require careful, meticulous analysis.
/// Until such verification is complete, this boolean property will be used to trigger the behavior, so unsuspecting instances of this control will not be affected.
@@ -151,8 +126,6 @@ namespace mbts.ui.spark {
double v = Value;
if (double.IsNaN(v)) {
SetCurrentValue(ValueProperty, TickDefaultValue);
} else if (_tick is not null) {
SetCurrentValue(ValueProperty, _tick.tickup(v));
} else {
var spec = ContractSpec;
if (spec.HasValue) SetCurrentValue(ValueProperty, spec.Value.tickup(v));
@@ -164,8 +137,6 @@ namespace mbts.ui.spark {
double v = Value;
if (double.IsNaN(v)) {
SetCurrentValue(ValueProperty, TickDefaultValue);
} else if (_tick is not null) {
SetCurrentValue(ValueProperty, _tick.tickdown(v));
} else {
var spec = ContractSpec;
if (spec.HasValue) SetCurrentValue(ValueProperty, spec.Value.tickdown(v));
@@ -178,15 +149,7 @@ namespace mbts.ui.spark {
void _update_text(double v) {
if (_text != null)
_price_text_gate.try_update(() => {
if (_tick is not null) {
var settings = TickDisplaySettings.Default;
settings.tick_factors = _tick.get_tick_for(v);
_text.Text = _tick.format(v, settings);
return;
}
_text.Text = _spec.HasValue ? _spec.Value.format(v, double.NaN, false, string.Empty) : v.ToString();
});
_price_text_gate.try_update(() => _text.Text = _spec.HasValue ? _spec.Value.format(v, double.NaN, false, string.Empty) : v.ToString());
}
void _update_value() =>
@@ -485,10 +485,6 @@
Width="{TemplateBinding Width}"
MinWidth="{TemplateBinding MinWidth}"
MaxWidth="{TemplateBinding MaxWidth}"
TickUpCommand="{TemplateBinding TickUpCommand}"
TickUpCommandParameter="{TemplateBinding TickUpCommandParameter}"
TickDownCommand="{TemplateBinding TickDownCommand}"
TickDownCommandParameter="{TemplateBinding TickDownCommandParameter}"
>
<TextBox
Name="PART_TextBox"
@@ -1,7 +1,6 @@
#nullable enable
namespace mbts.ui.controls {
using bts.api.domain;
using bts.utils;
using mbts.ui.extensions;
using mbts.ui.utilities;
@@ -229,12 +228,6 @@ namespace mbts.ui.controls {
get => _explicit_props.value;
set => _explicit_props.value = value;
}
bool _has_valid_bounds =>
Minimum < Maximum;
T _default_value =>
_has_valid_bounds ? T.Zero.within_range(Minimum, Maximum) : T.Zero;
public override void OnApplyTemplate() {
Disposable.dispose(ref _text_hooks);
@@ -279,7 +272,7 @@ namespace mbts.ui.controls {
var v = _value_d;
if (!v.HasValue) {
if (AllowNullTick) {
SetCurrentValue(ValueProperty, _default_value);
SetCurrentValue(ValueProperty, T.Zero);
}
return;
}
@@ -287,10 +280,6 @@ namespace mbts.ui.controls {
TickUpDownHelper.nullable_tick_up(v, _increment_d, out var result_d, _maximum_d) :
TickUpDownHelper.nullable_tick_down(v, _increment_d, out result_d, _minimum_d)) {
var result = NumericCastOutConverter<T>.convert(result_d.assert_value());
// Enforce min/max bounds (if bounds are valid)
if (_has_valid_bounds) {
result = result.within_range(Minimum, Maximum);
}
SetCurrentValue(ValueProperty, result);
CoerceValue(ValueProperty);
}
@@ -8,8 +8,6 @@ using System.Globalization;
using System.Windows.Data;
namespace mbts.ui.converters {
using RegionalTickFactors = user.settings.RegionalTickFactors;
class TickDisplayConverter : IMultiValueConverter {
public static TickDisplayConverter Instance => new();
@@ -26,13 +24,10 @@ namespace mbts.ui.converters {
if (values.Length == 2 && values[1] is bts.api.domain.TickDisplaySettingsImmutable tdsi) {
return tdsi.to_tick_display_settings().format(price);
}
var spec = get_val<ContractSpecificationImmutable?>(values, 1);
var spec = get_val<ContractSpecificationImmutable?>(values, 1) ??
Security.Defaults.ContractSpecificationImmutable;
var settings = get_val<mbts.user.settings.TickDisplaySettings>(values, 2);
if (spec.HasValue) {
return spec.Value.format(price, settings);
}
var rtick = get_val<RegionalTickFactors>(values, 1) ?? RegionalTickFactors.Default;
return rtick.format(price, settings);
return spec.format(price, settings);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
@@ -34,8 +34,7 @@
</Style>
<Style x:Key="DisabledPlaceholderComboBox" TargetType="ComboBox">
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="Foreground" Value="{u:BTSColor UI.ComboBox.disabled_foreground}"/>
<Setter Property="Background" Value="{u:BTSColor UI.ComboBox.disabled_button_background}"/>
<Setter Property="Foreground" Value="Gray"/>
<Setter Property="SelectedIndex" Value="0"/>
<Setter Property="ItemsSource">
<Setter.Value>
@@ -386,9 +386,9 @@
<ControlTemplate x:Key="DataGridRowDefaultTemplate" TargetType="DataGridRow">
<Border
x:Name="DGR_Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<u:StateManager.Groups>
<u:StateGroupsManager AttachToMembers="True">
@@ -408,9 +408,7 @@
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="DGR_Border" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{u:BTSColor ExecutionStrategy.default_column_background}" />
<!-- <DiscreteObjectKeyFrame KeyTime="0" Value="{u:BTSColor Core.red}" /> -->
<!-- <DiscreteObjectKeyFrame KeyTime="1" Value="{u:BTSColor Core.blue}" /> -->
<DiscreteObjectKeyFrame KeyTime="0" Value="{u:BTSColor Grid.alternate_row_background}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
@@ -427,8 +425,7 @@
HeadersVisibility,
ConverterParameter={x:Static DataGridHeadersVisibility.Row},
Converter={x:Static DataGrid.HeadersVisibilityConverter},
RelativeSource={RelativeSource AncestorType=DataGrid}}"
Background="transparent"/>
RelativeSource={RelativeSource AncestorType=DataGrid}}" />
<DataGridCellsPresenter
Grid.Column="1"
ItemsPanel="{TemplateBinding ItemsPanel}"
@@ -454,7 +451,7 @@
<Style TargetType="{x:Type DataGrid}">
<Setter Property="UseLayoutRounding" Value="True" />
<Setter Property="ColumnHeaderStyle" Value="{StaticResource {x:Type DataGridColumnHeader}}" />
<Setter Property="Background" Value="{uc:BTSColor Name=Core.transparent}" />
<Setter Property="Background" Value="{uc:BTSColor Name=UI.default_background}" />
<Setter Property="AlternationCount" Value="2" />
<Setter Property="RowStyle" Value="{StaticResource {x:Type DataGridRow}}" />
<Setter Property="AutoGenerateColumns" Value="False" />
@@ -162,7 +162,7 @@
// Compare bids
if (direct.int_price == implied.int_price) {
bid[x] = new NativePriceSize(direct.int_price, direct.size, implied.size);
bid[x] = new NativePriceSize(direct.int_price, direct.size_f, implied.size_f);
dbid_idx++;
ibid_idx++;
}
@@ -185,7 +185,7 @@
// Compare asks
if (direct.int_price == implied.int_price) {
ask[x] = new NativePriceSize(direct.int_price, direct.size, implied.size);
ask[x] = new NativePriceSize(direct.int_price, direct.size_f, implied.size_f);
dask_idx++;
iask_idx++;
}
@@ -1,14 +1,15 @@
namespace mbts.co2 {
using System.Runtime.InteropServices;
using Size = System.Single;
[StructLayout(LayoutKind.Sequential)]
public readonly struct NativePriceLevel {
readonly IntPrice _bid;
readonly IntPrice _ask;
readonly uint _bid_size;
readonly uint _ask_size;
readonly Size _bid_size;
readonly Size _ask_size;
public NativePriceLevel(IntPrice bid, uint bid_size, IntPrice ask, uint ask_size) {
public NativePriceLevel(IntPrice bid, Size bid_size, IntPrice ask, Size ask_size) {
_bid = bid;
_bid_size = bid_size;
_ask = ask;
@@ -5,27 +5,29 @@ namespace mbts.co2 {
using System;
using System.Runtime.InteropServices;
using Size = System.Single;
public readonly struct NativePriceSize : IEquatable<NativePriceSize> {
readonly IntPrice _price;
readonly uint _size;
readonly uint _direct_size;
readonly Size _size;
readonly Size _direct_size;
static NativePriceSize() =>
Log.assert(Marshal.SizeOf<NativePriceSize>() == 16);
public NativePriceSize(IntPrice price, uint size) {
public NativePriceSize(IntPrice price, Size size) {
_price = price;
_size = size;
_direct_size = size;
}
public NativePriceSize(long mantissa, sbyte exponent, uint size) {
public NativePriceSize(long mantissa, sbyte exponent, Size size) {
_price = new IntPrice(mantissa, exponent);
_size = size;
_direct_size = size;
}
public NativePriceSize(IntPrice price, uint direct_size, uint implied_size) {
public NativePriceSize(IntPrice price, Size direct_size, Size implied_size) {
_price = price;
_size = direct_size + implied_size;
_direct_size = direct_size;
@@ -36,15 +38,18 @@ namespace mbts.co2 {
public IntPrice int_price => _price;
public long mantissa => _price.mantissa;
public sbyte exponent => _price.exponent;
public uint size => _size;
public uint direct_size => _direct_size;
public uint implied_size => _size - _direct_size;
public uint size => (uint)_size;
public uint direct_size => (uint)_direct_size;
public uint implied_size => (uint)(_size - _direct_size);
public Size size_f => _size;
public Size direct_size_f => _direct_size;
public Size implied_size_f => _size - _direct_size;
public double price => _price.price();
public bool is_nan => _price.is_nan;
public override string ToString() =>
$"{price} {size} ({implied_size})";
$"{price} {size_f} ({implied_size})";
public override bool Equals(object obj) =>
obj is NativePriceSize p ? Equals(p) : base.Equals(obj);
+5 -4
View File
@@ -3,13 +3,14 @@ using mbts.co2;
namespace bts.md
{
using Size = System.Single;
public class PriceSize
{
public uint size { get; }
public uint direct_size { get; }
public uint implied_size { get; }
public Size size { get; }
public Size direct_size { get; }
public Size implied_size { get; }
public double price { get; }
public PriceSize(uint sz, uint direct_sz, uint implied_sz, double prx)
public PriceSize(Size sz, Size direct_sz, Size implied_sz, double prx)
{
size = sz;
direct_size = direct_sz;
+3 -15
View File
@@ -1,16 +1,11 @@
#nullable enable
namespace mbts {
using bts;
using bts.api;
using bts.api.domain;
using bts.utils;
using mbts.co2;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
public readonly struct SecurityMD : IEquatable<SecurityMD> {
public static readonly Func<SecurityMD, uint> key_fn = smd => smd.security.Id;
@@ -19,28 +14,21 @@ namespace mbts {
SecurityImmutable security,
IMDConsumerHandle md,
TickDisplaySettingsImmutable? price_display = null,
TickDisplaySettingsImmutable? quantity_display = null,
IEnumerable<TickDisplayRuleImmutable>? price_display_rules = null) {
TickDisplaySettingsImmutable? quantity_display = null) {
this.security = security;
this.md = md;
this.price_display = price_display ?? Security.Defaults.price_display_settingsi;
this.quantity_display = quantity_display ??
Security.Defaults.quantity_display_settingsi;
this.price_display_rules = price_display_rules
.empty_if_null()
.OrderBy(r => r.MaxValue)
.ToImmutableList();
// Validate new 'price rules' config (be strict...this is a hotfix to prod)
Log.assert(this.price_display_rules.All(r => r.MaxValue.is_real()));
Log.assert(this.price_display_rules.Select(r => r.MaxValue).IsDistinct());
unit_quantity = 1;
}
public SecurityImmutable security { get; }
public uint id => security.Id;
public IMDConsumerHandle md { get; }
public double unit_quantity { get; }
public TickDisplaySettingsImmutable price_display { get; }
public TickDisplaySettingsImmutable quantity_display { get; }
public ImmutableList<TickDisplayRuleImmutable> price_display_rules { get; }
public override int GetHashCode() =>
(int)security.Id;
@@ -4,7 +4,6 @@ namespace Spark.Core.Data {
using bts;
using bts.api;
using bts.api.domain;
using bts.collections;
using mbts;
using mbts.config;
@@ -56,21 +55,7 @@ namespace Spark.Core.Data {
}
// Can't possibly be null (the delegate above already checked for this)
raw_meta = Log.notnull(raw_meta);
var meta = convert_raw_metadata(raw_meta);
if (sec.ContractCase == Security.ContractOneofCase.Spread
&& meta.HasValue
&& meta.Value.PriceRules.AtLeast(2)) {
// This is a hack to force Deribit Combos to have uniform ticks
var rules = meta.Value.PriceRules
.Where(r => r.HasTickDisplay && r.TickDisplay.HasTickFactors);
if (rules.TryGetMinElement(
r => r.TickDisplay.TickFactors.tick_unit_frac(), out var min_tick_rule)) {
var mutable_meta = meta.Value.CloneToMutable();
mutable_meta.PriceRules.SetItems(new[] { min_tick_rule.CloneToMutable() });
meta = mutable_meta.immutable();
}
}
return meta;
return convert_raw_metadata(raw_meta);
});
SecurityMetadataImmutable? convert_raw_metadata(SecurityMetadataItem raw_meta) {
@@ -81,8 +66,8 @@ namespace Spark.Core.Data {
/// Don't mutate the actual object stored in <see cref="_security_metadata"/>
var meta = raw_meta.Value.Clone();
meta.PriceDisplay ??= Security.Defaults.price_display_settings;
// meta.PriceDisplay.TickFactors = sec.spec_or_default()
// .tick_factors().to_api_tick_factorsi().CloneToMutable();
meta.PriceDisplay.TickFactors = sec.spec_or_default()
.tick_factors().to_api_tick_factorsi().CloneToMutable();
return new(meta);
}
}
@@ -22,9 +22,6 @@ namespace mbts.user.settings {
_low_tick_inclusive_end = low_tick_inclusive_end;
}
public static RegionalTickFactors Default =>
new(new(10000, 1), new(10000, 1), double.MaxValue);
public TickFactors low_tick => _low_tick;
public TickFactors high_tick => _high_tick;
public double low_tick_inclusive_end => _low_tick_inclusive_end;
+65 -124
View File
@@ -55,16 +55,9 @@ namespace mbts {
this TickFactors tick_factors) =>
new(tick_factors.to_api_tick_display_settings());
public static bool is_valid(this TickFactors tick) {
if (!tick.tick_unit.is_real_positive() && !tick.tick_base.is_real_positive()) {
return false;
}
var ticks_per_base = tick.tick_base / tick.tick_unit;
if (ticks_per_base.is_int()) {
return true;
}
return tick.tick_unit > tick.tick_base && tick.tick_base == 1;
}
public static bool is_valid(this TickFactors tick) =>
(tick.tick_unit > 0 && tick.tick_unit <= (tick.tick_base * 0.5)) ||
tick.tick_unit == tick.tick_base;
public static bool is_valid(this RegionalTickFactors regional_tick) =>
regional_tick.low_tick.is_valid() && regional_tick.high_tick.is_valid();
@@ -72,10 +65,6 @@ namespace mbts {
public static bool is_integer(this TickFactors tick) =>
tick.tick_unit == tick.tick_base;
public static TickFactors tick_factors(this RegionalTickFactors regional_tick, double value) =>
value > regional_tick.low_tick_inclusive_end ?
regional_tick.high_tick : regional_tick.low_tick;
public static double tick_unit_frac(this TickFactors tick) =>
tick.tick_unit / tick.tick_base;
@@ -87,21 +76,6 @@ namespace mbts {
public static double reduced_base(this TickFactors tick) => MathUtils.factor_out(tick.tick_base, 10);
public static bool tick_equals(this RegionalTickFactors regional_tick, double x, double y) {
if (x.is_nan()) {
return y.is_nan();
}
if (y.is_nan()) {
return false;
}
var xtick = regional_tick.tick_factors(x);
var ytick = regional_tick.tick_factors(y);
return xtick is not null
&& ytick is not null
&& xtick.Equals(ytick)
&& xtick.tick_equals(x, y);
}
public static bool tick_equals(this TickFactors tick, double x, double y) {
if (x.is_nan()) {
return y.is_nan();
@@ -118,35 +92,27 @@ namespace mbts {
}
var x_units = MathUtils.round_fractional_units(x, tick.tick_unit_frac<T>());
var y_units = MathUtils.round_fractional_units(y, tick.tick_unit_frac<T>());
return NumericCastConverter<T, long>.convert(x_units) ==
NumericCastConverter<T, long>.convert(y_units);
return NumericCastConverter<T, int>.convert(x_units) ==
NumericCastConverter<T, int>.convert(y_units);
}
public static IEqualityComparer<T> tick_equality_comparer<T>(this TickFactors tick)
where T : IFloatingPoint<T> =>
EqualityComparer.Create<T>(
(a, b) => tick_equals(tick, a, b),
x => NumericCastConverter<T, int>.convert(
MathUtils.round_fractional_units(x, tick.tick_unit_frac<T>())));
x => NumericCastConverter<T, int>.convert(MathUtils.round_fractional_units(x, tick.tick_unit_frac<T>())));
public static IEqualityComparer<double> tick_equality_comparer(this TickFactors tick) =>
EqualityComparer.Create<double>(
(a, b) => tick_equals(tick, a, b),
x => (int)MathUtils.round_fractional_units(x, tick.tick_unit_frac()));
public static TickFactors get_tick_for(this RegionalTickFactors rtick, double value) =>
value > rtick.low_tick_inclusive_end ? rtick.high_tick : rtick.low_tick;
public static string format_without_decimal_part(this TickFactors tick, double value) =>
format(tick, value, double.NaN, false, _nan_text, true);
public static string format<T>(this TickFactors tick, T value) where T : IFloatingPointIeee754<T> =>
format(tick, value, T.NaN, false, _nan_text);
public static string format<T>(this RegionalTickFactors regional_tick, T value, bool show_commas)
where T : IFloatingPointIeee754<T> =>
format(regional_tick, value, T.NaN, show_commas, _nan_text);
public static string format<T>(this TickFactors tick, T value, bool show_commas)
where T : IFloatingPointIeee754<T> =>
format(tick, value, T.NaN, show_commas, _nan_text);
@@ -177,19 +143,6 @@ namespace mbts {
value, settings.display_multiplier.as_type<double, T>(), settings.show_commas);
}
public static string format<T>(
this RegionalTickFactors regional_tick,
T value,
T display_multiplier,
bool show_commas,
string nan_text,
bool show_decimal_part = true
) where T : IFloatingPointIeee754<T> {
var value_d = NumericCastConverter<T, double>.convert(value);
return regional_tick.tick_factors(value_d)
.format(value, display_multiplier, show_commas, nan_text, show_decimal_part);
}
public static string format<T>(
this TickFactors tick,
T value,
@@ -278,44 +231,33 @@ namespace mbts {
_parse(regional_tick, text, display_multiplier, nan_text) :
_parse_no_tick(text, display_multiplier, nan_text);
public static T tick<T>(this TickFactors tick, T value, long count)
public static T tick<T>(this TickFactors tick, T value, int count)
where T : IFloatingPointIeee754<T> =>
count < 0 ? tick.tickdown(value, (uint)-count) : tick.tickup(value, (ulong)count);
count < 0 ? tick.tickdown(value, (uint)-count) : tick.tickup(value, (uint)count);
public static T tick<T>(this bts.api.domain.TickFactorsImmutable tick, T value, long count)
public static T tick<T>(this bts.api.domain.TickFactorsImmutable tick, T value, int count)
where T : IFloatingPointIeee754<T> =>
tick.to_tick_factors().tick(value, count);
public static T tick<T>(
this bts.api.domain.TickDisplaySettingsImmutable settings, T value, long count)
this bts.api.domain.TickDisplaySettingsImmutable settings, T value, int count)
where T : IFloatingPointIeee754<T> =>
settings.HasTickFactors ?
settings.TickFactors.tick(value, count) :
throw new ArgumentException(
"Can't tick because 'settings.HasTickFactors' is false", nameof(settings));
public static double tick(
this RegionalTickFactors regional_tick, double input, long count) =>
tick<double>(regional_tick, input, count);
public static T tick<T>(
this RegionalTickFactors regional_tick, T input, long count)
where T : IFloatingPointIeee754<T> =>
count > 0 ?
tickup(regional_tick, input, (ulong)count) :
tickdown(regional_tick, input, (ulong)-count);
public static T tickup<T>(this TickFactors tick, T input, ulong count = 1)
public static T tickup<T>(this TickFactors tick, T input, uint count = 1)
where T : IFloatingPointIeee754<T> =>
tickup(tick, input, count, TickUtils.precision_const<T>());
public static T tickup<T>(this TickFactors tick, T input, ulong count, T precision)
public static T tickup<T>(this TickFactors tick, T input, uint count, T precision)
where T : IFloatingPointIeee754<T> =>
tickup<T, NumericCastOutConverter<T>>(tick, input, count, precision);
public static T tickup<T, TFromUInt64>(this TickFactors tick, T input, ulong count, T precision)
public static T tickup<T, TFromUInt32>(this TickFactors tick, T input, uint count, T precision)
where T : IFloatingPointIeee754<T>
where TFromUInt64 : IConverter<ulong, T> {
where TFromUInt32 : IConverter<uint, T> {
if (!precision.is_real_positive()) {
throw new ArgumentException("Must be a real positive number.", nameof(precision));
}
@@ -328,33 +270,21 @@ namespace mbts {
if (ieeer < -precision) {
count--;
}
var result = input + tuf * TFromUInt64.convert(count);
var result = input + tuf * TFromUInt32.convert(count);
return _round_tick(tuf, result);
}
public static double tickup(
this RegionalTickFactors regional_tick, double input, long count = 1) =>
count switch {
> 0 => tickup<double>(regional_tick, input, (ulong)count),
< 0 => tickdown<double>(regional_tick, input, (ulong)-count),
0 => input,
};
public static double tickup(
this RegionalTickFactors regional_tick, double input, ulong count) =>
tickup<double>(regional_tick, input, count);
public static T tickup<T>(
this RegionalTickFactors regional_tick, T input) where T : IFloatingPointIeee754<T> =>
regional_tick.tickup(input, 1);
public static T tickup<T>(
this RegionalTickFactors regional_tick, T input, ulong count)
this RegionalTickFactors regional_tick, T input, uint count)
where T : IFloatingPointIeee754<T> =>
regional_tick.tickup(input, count, TickUtils.precision_const<T>());
public static T tickup<T>(
this RegionalTickFactors regional_tick, T input, ulong count, T precision)
this RegionalTickFactors regional_tick, T input, uint count, T precision)
where T : IFloatingPointIeee754<T> {
if (!precision.is_real_positive()) {
throw new ArgumentException("Must be a real positive number.", nameof(precision));
@@ -371,33 +301,33 @@ namespace mbts {
var ieeer = T.Ieee754Remainder(input, tuf);
input -= ieeer;
if (ieeer < -precision) count--;
val = input + tuf * count.as_type<ulong, T>();
val = input + tuf * count.as_type<uint, T>();
if (val > ltie) {
count -= MathUtils.round_up((ltie - input) / tuf, precision).as_type<T, ulong>();
count -= MathUtils.round_up((ltie - input) / tuf, precision).as_type<T, uint>();
tuf = regional_tick.high_tick.tick_unit_frac<T>();
val = ltie + tuf * count.as_type<ulong, T>();
val = ltie + tuf * count.as_type<uint, T>();
}
} else {
tuf = regional_tick.high_tick.tick_unit_frac<T>();
var ieeer = T.Ieee754Remainder(input, tuf);
input -= ieeer;
if (ieeer < -precision) count--;
val = input + tuf * count.as_type<ulong, T>();
val = input + tuf * count.as_type<uint, T>();
}
return _round_tick(tuf, val);
}
public static T tickdown<T>(this TickFactors tick, T input, ulong count = 1)
public static T tickdown<T>(this TickFactors tick, T input, uint count = 1)
where T : IFloatingPointIeee754<T> =>
tickdown(tick, input, count, TickUtils.precision_const<T>());
public static T tickdown<T>(this TickFactors tick, T input, ulong count, T precision)
public static T tickdown<T>(this TickFactors tick, T input, uint count, T precision)
where T : IFloatingPointIeee754<T> =>
tickdown<T, NumericCastOutConverter<T>>(tick, input, count, precision);
public static T tickdown<T, TFromUInt64>(this TickFactors tick, T input, ulong count, T precision)
public static T tickdown<T, TFromUInt32>(this TickFactors tick, T input, uint count, T precision)
where T : IFloatingPointIeee754<T>
where TFromUInt64 : IConverter<ulong, T> {
where TFromUInt32 : IConverter<uint, T> {
if (!input.is_real()) {
return input;
}
@@ -407,42 +337,30 @@ namespace mbts {
if (ieeer > precision) {
count--;
}
var result = input - tuf * TFromUInt64.convert(count);
var result = input - tuf * TFromUInt32.convert(count);
return _round_tick(tuf, result);
}
public static double tickdown(
this RegionalTickFactors regional_tick, double input, long count = 1) =>
count switch {
> 0 => tickdown<double>(regional_tick, input, (ulong)count),
< 0 => tickup<double>(regional_tick, input, (ulong)count),
0 => input,
};
public static double tickdown(
this RegionalTickFactors regional_tick, double input, ulong count) =>
tickdown<double>(regional_tick, input, count);
public static T tickdown<T>(this RegionalTickFactors regional_tick, T input)
where T : IFloatingPointIeee754<T> =>
regional_tick.tickdown(input, 1);
public static T tickdown<T>(this RegionalTickFactors regional_tick, T input, ulong count)
public static T tickdown<T>(this RegionalTickFactors regional_tick, T input, uint count)
where T : IFloatingPointIeee754<T> =>
tickdown(regional_tick, input, count, TickUtils.precision_const<T>());
public static T tickdown<T>(
this RegionalTickFactors regional_tick, T input, ulong count, T precision)
this RegionalTickFactors regional_tick, T input, uint count, T precision)
where T : IFloatingPointIeee754<T> =>
tickdown<T, NumericCastOutConverter<T>, NumericCastOutConverter<T>, NumericCastInConverter<T>>(
regional_tick, input, count, precision);
public static T tickdown<T, TFromUInt32, TFromDouble, TToUInt16>(
this RegionalTickFactors regional_tick, T input, ulong count, T precision)
this RegionalTickFactors regional_tick, T input, uint count, T precision)
where T : IFloatingPointIeee754<T>
where TFromDouble : IConverter<double, T>
where TFromUInt32 : IConverter<ulong, T>
where TToUInt16 : IConverter<T, ulong> {
where TFromUInt32 : IConverter<uint, T>
where TToUInt16 : IConverter<T, uint> {
if (!precision.is_real_positive()) {
throw new ArgumentException("Must be a real positive number.", nameof(precision));
}
@@ -505,6 +423,16 @@ namespace mbts {
CashPerPoint = 1
};
public static bool try_get_tickdiff(this RegionalTickFactors regional_tick, double minuend, double subtrahend, out int n_low, out int n_high) {
if (DoubleHelper.IsNaNOrInfinity(minuend) || DoubleHelper.IsNaNOrInfinity(subtrahend)) {
n_low = default;
n_high = default;
return false;
}
(n_low, n_high) = tickdiff_unchecked(regional_tick, minuend, subtrahend);
return true;
}
/// <summary>
/// Computes the difference, in tick units, between <paramref name="minuend"/> and <paramref name="subtrahend"/>
/// </summary>
@@ -513,14 +441,31 @@ namespace mbts {
/// </remarks>
/// <param name="minuend">The minuend</param>
/// <param name="subtrahend">The subtrahend</param>
public static long? tickdiff(this RegionalTickFactors regional_tick, double minuend, double subtrahend) {
if (!minuend.is_real() || !subtrahend.is_real()) {
return null;
internal static (int n_low, int n_high) tickdiff_unchecked(this RegionalTickFactors regional_tick, double minuend, double subtrahend) {
Log.assert(minuend.is_real() && subtrahend.is_real());
double ltie = regional_tick.low_tick_inclusive_end;
double raw_diff = minuend - subtrahend;
if (minuend < ltie) {
if (subtrahend < ltie) {
return ((int)MathUtils.round_fractional_units(raw_diff, regional_tick.low_tick.tick_unit_frac()), 0);
}
double low_diff = ltie - minuend;
double high_diff = raw_diff - low_diff;
int n_low = (int)MathUtils.round_fractional_units(low_diff, regional_tick.low_tick.tick_unit_frac());
int n_high = (int)MathUtils.round_fractional_units(high_diff, regional_tick.high_tick.tick_unit_frac());
return (n_low, n_high);
}
var minuend_n = TickUtils.count_ticks(regional_tick, minuend);
var subtrahend_n = TickUtils.count_ticks(regional_tick, subtrahend);
var n = minuend_n - subtrahend_n;
return n;
if (subtrahend < ltie) {
double high_diff = minuend - ltie;
double low_diff = raw_diff - high_diff;
int n_low = (int)MathUtils.round_fractional_units(low_diff, regional_tick.high_tick.tick_unit_frac());
int n_high = (int)MathUtils.round_fractional_units(high_diff, regional_tick.high_tick.tick_unit_frac());
return (n_low, n_high);
}
return (0, (int)MathUtils.round_fractional_units(raw_diff, regional_tick.high_tick.tick_unit_frac()));
}
public static double round(this TickFactors tick, double input) =>
@@ -557,10 +502,6 @@ namespace mbts {
new(tick_base ?? tick_factors.tick_base, tick_unit ?? tick_factors.tick_unit);
static char _separator(TickFactors tick) {
if (tick.tick_base == 1 && tick.tick_unit > tick.tick_base &&
MathUtils.is_int(tick.tick_unit / tick.tick_base)) {
return '.';
}
double tb_log10 = Math.Log10(tick.tick_base);
return tb_log10 >= 1 && tb_log10 % 1 == 0 ? '.' : '-';
}
@@ -1,62 +1,34 @@
using bts.api;
using bts.utils;
using mbts;
using mbts.spark;
using mbts.user.settings;
using Spark.Core.Data;
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.data.ticks {
class TickFactorsFactory {
static readonly RegionalTickFactors _default_rtf = new RegionalTickFactors(new TickFactors(1000, 1), new TickFactors(1000, 1), double.MaxValue);
readonly Dictionary<uint, RegionalTickFactors> _sec_ticks = new();
readonly Dictionary<SpecKey, RegionalTickFactors> _spec_ticks = new();
readonly Dictionary<SpecKey, RegionalTickFactors> _exchange_ticks = new Dictionary<SpecKey, RegionalTickFactors>();
public RegionalTickFactors default_rtf => _default_rtf;
public RegionalTickFactors get(ContractSpecification spec) =>
_spec_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
public RegionalTickFactors get(ContractSpecificationImmutable spec) =>
_spec_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
public RegionalTickFactors get(SecurityImmutable security) =>
security.HasContractSpec ? get(security.ContractSpec) : _default_rtf;
public RegionalTickFactors get(SecurityImmutable? security) =>
security.HasValue ?
_sec_ticks.GetOrAdd(security.Value.Id, _ => _create_rtf(security.Value)) :
_default_rtf;
static RegionalTickFactors _create_rtf(SecurityImmutable? security) {
var meta = SecurityMetadataStore.instance.get(security.Value);
return meta?.PriceRules?.Any() ?? false ?
_create_rtf(meta.Value.PriceRules.ToList()) :
_create_rtf(new SpecKey(security.Value.spec_or_default()));
}
static RegionalTickFactors _create_rtf(
IList<api.domain.TickDisplayRuleImmutable> rules) {
if (rules.is_null_or_empty()) {
throw new ArgumentException("Rules is null or empty", nameof(rules));
}
if (rules.Count > 2) {
throw new ArgumentException("Rules cannot have more than 2 items (backwards compatibility)");
}
var low_tick = rules[0].TickDisplay.TickFactors;
var high_tick_idx = rules.Count > 1 ? 1 : 0;
var high_tick = rules[high_tick_idx].TickDisplay.TickFactors;
var low_tick_end = rules.Count > 1 ? rules[0].MaxValue : double.MaxValue;
return new(low_tick.to_tick_factors(), high_tick.to_tick_factors(), low_tick_end);
}
security.HasValue ? get(security.Value) : _default_rtf;
static RegionalTickFactors _create_rtf(SpecKey key) {
double tick_base = key.denominator;
if (tick_base == 0) {
// Tick in pennies if units are < 1 (i.e., fractional)
tick_base = key.low_tick > 1 && key.high_tick > 1 ? 1 : 100;
// indicates tick is a decimal
tick_base = 100;
}
return _create_rtf(key.low_tick, key.high_tick, key.price_break, tick_base, key.display_digits);
}
+77 -165
View File
@@ -1,15 +1,13 @@
using bts;
using bts.api;
using bts.data.ticks;
using bts.utils;
using mbts.co2;
using mbts.user.settings;
using Spark.Core;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Numerics;
@@ -38,9 +36,6 @@ namespace mbts.spark {
public static IComparer<double> price_comparer(this ContractSpecificationImmutable spec) =>
MathUtils.epsilon_comparer.wrap<double, double>(p => spec.round_to_tick(p));
public static IComparer<double> price_comparer(this RegionalTickFactors rtick) =>
MathUtils.epsilon_comparer.wrap<double, double>(p => rtick.round_to_tick(p));
public static double ticksize(this ContractSpecificationImmutable spec, double price) {
if (!spec.LowTick.is_real_positive()) {
return spec.HighTick;
@@ -53,23 +48,8 @@ namespace mbts.spark {
}
}
public static double ticksize(this RegionalTickFactors rtick, double price) {
var low_tick = rtick.low_tick.tick_unit_frac();
var high_tick = rtick.high_tick.tick_unit_frac();
var price_break = rtick.low_tick_inclusive_end;
if (!low_tick.is_real_positive()) {
return high_tick;
} else if (!high_tick.is_real_positive()) {
return low_tick;
} else if (!price.is_real() || !price_break.is_real()) {
return low_tick;
} else {
return price > price_break ? high_tick : low_tick;
}
}
public static string format(this ContractSpecificationImmutable spec, double price, bool in_tick_terms) =>
in_tick_terms ?
in_tick_terms ?
_get(spec).format_without_decimal_part(price / spec.ticksize(price)) :
_get(spec).format(price);
@@ -107,13 +87,18 @@ namespace mbts.spark {
return rv;
}
public static long? tickdiff(this ContractSpecificationImmutable spec, double x, double y) =>
get_regional_ticks(spec).tickdiff(x, y);
public static int? tickdiff(this ContractSpecificationImmutable spec, double x, double y) {
if (get_regional_ticks(spec).try_get_tickdiff(x, y, out int n_low, out int n_high))
return n_low + n_high;
return null;
}
public static long? tickdiff(this SecurityImmutable sec, double x, double y) =>
get_regional_ticks(sec).tickdiff(x, y);
public static int tickdiff_unchecked(this ContractSpecificationImmutable spec, double x, double y) {
(int n_low, int n_high) = get_regional_ticks(spec).tickdiff_unchecked(x, y);
return n_low + n_high;
}
public static double mid_tick(RegionalTickFactors spec, double bid, double ask) {
public static double mid_tick(ContractSpecificationImmutable spec, double bid, double ask) {
double raw_mid;
if (double.IsNaN(bid)) {
if (double.IsNaN(ask)) return double.NaN;
@@ -129,8 +114,8 @@ namespace mbts.spark {
raw_mid - (raw_mid % tick_size);
}
public static TickFactors deduce_ticks(IEnumerable<SecurityImmutable> securities) {
var ticks = new HashSet<TickFactors>(securities.Select(_get).SelectMany(rtf => new[] { rtf.low_tick, rtf.high_tick }));
public static TickFactors deduce_ticks(IEnumerable<ContractSpecification> contract_specs) {
var ticks = new HashSet<TickFactors>(contract_specs.Select(_get).SelectMany(rtf => new[] { rtf.low_tick, rtf.high_tick }));
switch (ticks.Count) {
case 0: return _exchange_ticks.default_rtf.low_tick;
case 1: return ticks.First();
@@ -141,27 +126,30 @@ namespace mbts.spark {
}
}
public static TickFactors tick_factors(this SecurityImmutable sec) =>
_get(sec).low_tick;
public static RegionalTickFactors get_tick_factors(ContractSpecificationImmutable spec) =>
_get(spec);
public static TickFactors tick_factors(this ContractSpecificationImmutable spec) =>
_get(spec).low_tick;
public static double tickup(this ContractSpecificationImmutable spec, double value) =>
_get(spec).tickup(value);
public static double tickup(this ContractSpecificationImmutable spec, double value, long count) {
public static double tickup(this ContractSpecificationImmutable spec, double value, int count) {
uint bl = 3;
var res = value * bl;
if (count == 0) return value;
if (count < 0) return _get(spec).tickdown(value, (ulong)-count);
else return _get(spec).tickup(value, (ulong)count);
if (count < 0) return _get(spec).tickdown(value, (uint)(-count));
else return _get(spec).tickup(value, (uint)count);
}
public static double tickdown(this ContractSpecificationImmutable spec, double value) =>
_get(spec).tickdown(value);
public static double tickdown(this ContractSpecificationImmutable spec, double value, long count) {
public static double tickdown(this ContractSpecificationImmutable spec, double value, int count) {
if (count == 0) return value;
if (count < 0) return _get(spec).tickup(value, (ulong)-count);
else return _get(spec).tickdown(value, (ulong)count);
if (count < 0) return _get(spec).tickup(value, (uint)(-count));
else return _get(spec).tickdown(value, (uint)count);
}
public static bool tick_equals<T>(
@@ -180,7 +168,7 @@ namespace mbts.spark {
x => (int)MathUtils.round_fractional_units(x, contract.LowTick));
public static IEqualityComparer<T> tick_equality_comparer<T>(
this ContractSpecificationImmutable contract) where T : IFloatingPoint<T> =>
this ContractSpecificationImmutable contract) where T : IFloatingPoint<T> =>
EqualityComparer.Create<T>(
(a, b) => tick_equals(contract, a, b),
x => (int)MathUtils.round_fractional_units(
@@ -189,107 +177,85 @@ namespace mbts.spark {
public static int compare_tick_fallback_to_double(this ContractSpecificationImmutable contract, double x, double y) =>
tick_equals(contract, x, y) ? 0 : x.CompareTo(y);
public static double round_to_tick(this ContractSpecificationImmutable spec, double value) =>
round_to_tick(spec.ticksize(value), value);
public static decimal round_to_tick(this ContractSpecificationImmutable spec, decimal value) =>
round_to_tick((decimal)spec.ticksize((double)value), value);
public static double round_to_tick(this RegionalTickFactors rtick, double value) =>
round_to_tick(rtick.ticksize(value), value);
public static decimal round_to_tick(this RegionalTickFactors rtick, decimal value) =>
round_to_tick((decimal)rtick.ticksize((double)value), value);
public static double round_to_tick(double tick_size, double value) {
public static double round_to_tick(this ContractSpecificationImmutable spec, double value) {
double intpart = (long)value;
double floatpart = value % 1;
floatpart = ((int)Math.Round(floatpart / tick_size)) * tick_size;
double tick = spec.ticksize(value);
floatpart = ((int)Math.Round(floatpart / tick)) * tick;
return intpart + floatpart;
}
public static decimal round_to_tick(decimal tick_size, decimal value) {
public static decimal round_to_tick(this ContractSpecificationImmutable spec, decimal value) {
decimal intpart = (long)value;
decimal floatpart = value % 1;
floatpart = ((int)(floatpart / tick_size)) * tick_size;
decimal tick = (decimal)spec.ticksize((double)value);
floatpart = ((int)(floatpart / tick)) * tick;
return intpart + floatpart;
}
public static long? count_ticks(this RegionalTickFactors rtick, double value) {
if (!rtick.is_valid()) {
public static int? full_ticks_from_zero(this ContractSpecificationImmutable spec, double value) =>
full_ticks_from(spec, 0, value);
public static int? full_ticks_from(this ContractSpecificationImmutable spec, double origin, double value) {
if (!value.is_real() || !origin.is_real())
return null;
if (value < origin) {
if (spec.LowTick.is_real_positive()) {
if (spec.HighTick.is_real_positive() && spec.PriceBreak < origin && spec.PriceBreak > value) {
var ticks_to_break = MathUtils.round_up((spec.PriceBreak - origin) / spec.HighTick, _precision_const);
var low_ticks_remainder = MathUtils.round_up((value - spec.PriceBreak) / spec.LowTick, _precision_const);
return (int)(ticks_to_break + low_ticks_remainder);
} else {
return (int)MathUtils.round_up((value - origin) / spec.LowTick, _precision_const);
}
} else if (spec.HighTick.is_real_positive()) {
return (int)MathUtils.round_up((value - origin) / spec.HighTick, _precision_const);
}
} else {
if (spec.HighTick.is_real_positive()) {
if (spec.LowTick.is_real_positive() && spec.PriceBreak > origin && spec.PriceBreak < value) {
var ticks_to_break = MathUtils.round_down((spec.PriceBreak - origin) / spec.LowTick, _precision_const);
var high_ticks_remainder = MathUtils.round_down((value - spec.PriceBreak) / spec.HighTick, _precision_const);
return (int)(ticks_to_break + high_ticks_remainder);
} else {
return (int)MathUtils.round_down(value / spec.HighTick, _precision_const);
}
} else if (spec.LowTick.is_real_positive()) {
return (int)MathUtils.round_down(value / spec.LowTick, _precision_const);
}
}
var n = _count_ticks(
rtick.low_tick.tick_unit_frac(),
rtick.low_tick_inclusive_end,
rtick.high_tick.tick_unit_frac(),
0,
value);
var orig_n = n;
// Enforce proper rounding
var rounded_val = rtick.round_to_tick(value);
var half_tick = rtick.get_tick_for(value).tick_unit_frac() / 2;
if (n < 0 && (value - rounded_val) > half_tick) {
++n;
} else if (n > 0 && (rounded_val - value) > half_tick) {
--n;
}
Log.assert(n == orig_n);
return n;
return null;
}
public static RegionalTickFactors get_regional_ticks(ContractSpecificationImmutable? spec) =>
spec.HasValue ? _get(spec.Value) : _exchange_ticks.default_rtf;
public static RegionalTickFactors regional_ticks(this SecurityImmutable security) =>
_exchange_ticks.get(security);
public static RegionalTickFactors get_regional_ticks(SecurityImmutable security) =>
get_regional_ticks(security.spec_or_default());
public static RegionalTickFactors get_regional_ticks(SecurityImmutable? security) =>
_exchange_ticks.get(security);
public static TickFactors get_tick(SecurityImmutable? security, double value) {
var rtick = get_regional_ticks(security);
var tick = rtick.low_tick_inclusive_end > value ? rtick.high_tick : rtick.low_tick;
return tick;
public static ContractSpecificationImmutable multiply_tick_size(ContractSpecificationImmutable spec, uint factor) {
factor = fit_ticks_per_row_to_dollars(spec, factor);
return spec.with(low_tick: spec.LowTick * factor, high_tick: spec.HighTick * factor);
}
public static RegionalTickFactors multiply_tick_size(RegionalTickFactors spec, uint factor) {
factor = _fit_ticks_per_row_to_dollars(spec, factor) ?? 1;
return new(
new(spec.low_tick.tick_base, spec.low_tick.tick_unit * factor),
new(spec.high_tick.tick_base, spec.high_tick.tick_unit * factor),
spec.low_tick_inclusive_end);
public static double normalize_price(ContractSpecificationImmutable spec, double price) {
var ticks = spec.full_ticks_from_zero(price);
return ticks.HasValue ? spec.tickup(0, ticks.Value) : price;
}
public static double normalize_price(RegionalTickFactors rtick, double price) {
if (!price.is_real()) {
return price;
}
var ticks = rtick.count_ticks(price);
if (!ticks.HasValue) {
return price;
}
var normalized = rtick.tick(0, ticks.Value);
var tick = price > rtick.low_tick_inclusive_end ? rtick.high_tick : rtick.low_tick;
// Diff should always be < 1/2 tick
Log.assert((normalized - price).abs() < tick.tick_unit_frac() * 0.5);
return normalized;
public static double normalize_price(ContractSpecificationImmutable? spec, double price) {
return spec.HasValue ? normalize_price(spec.Value, price) : price;
}
public static uint fit_ticks_per_row_to_dollars(SecurityImmutable sec, uint ticks_per_row) =>
_fit_ticks_per_row_to_dollars(sec, ticks_per_row) ?? 1;
public static uint fit_ticks_per_row_to_dollars(ContractSpecificationImmutable spec, uint ticks_per_row) =>
_fit_ticks_per_row_to_dollars(spec, ticks_per_row) ?? 1;
public static uint fit_ticks_per_row_to_dollars(RegionalTickFactors rtick, uint ticks_per_row) =>
_fit_ticks_per_row_to_dollars(rtick, ticks_per_row) ?? 1;
static uint? _fit_ticks_per_row_to_dollars(SecurityImmutable sec, uint ticks_per_row) =>
_fit_ticks_per_row_to_dollars(sec.regional_ticks(), ticks_per_row);
static uint? _fit_ticks_per_row_to_dollars(RegionalTickFactors tf, uint ticks_per_row) {
static uint? _fit_ticks_per_row_to_dollars(ContractSpecificationImmutable spec, uint ticks_per_row) {
if (ticks_per_row == 0)
return null;
// it is assumed that low tick will be the same as high tick;
// however, best to double check - return null if this isn't the case (let the caller decide what to do).
var tf = get_tick_factors(spec);
if (!tf.low_tick.Equals(tf.high_tick) || !tf.is_valid() || tf.low_tick.is_integer())
return null;
var ticks_per_dollar = (uint)(tf.low_tick.tick_base / tf.low_tick.tick_unit);
@@ -317,61 +283,7 @@ namespace mbts.spark {
return null;
}
static long? _count_ticks(
double low_tick, double price_break, double high_tick, double from, double to) {
if (!from.is_real() || !to.is_real()) {
return null;
}
var has_low = low_tick.is_real_positive();
var has_high = high_tick.is_real_positive();
if (!has_low) {
if (!has_high) {
return null;
}
low_tick = high_tick;
} else if (!has_high) {
high_tick = low_tick;
}
if (price_break.is_nan()) {
// Take price_break out of play
price_break = double.PositiveInfinity;
}
// Allow core impl to assume: 'from <= to'
(from, to) = (from, to).ordered(out var negate);
var n_ticks = _count_asc_ticks(low_tick, high_tick, from, to, price_break);
return negate ? -n_ticks : n_ticks;
}
static long _count_asc_ticks(
double low_tick, double high_tick, double from, double to, double price_break) {
Contract.Requires(from <= to);
Contract.Requires(low_tick.is_real_positive() && high_tick.is_real_positive());
Log.assert(from <= to);
checked {
if (to <= price_break) {
// All ticks are below price_break
return count_single_tick_sz(low_tick, from, to);
}
if (from >= price_break) {
// All ticks are above price_break
return count_single_tick_sz(high_tick, from, to);
}
// Ticks cross the price break. Compute low and high ticks separately
var low_ticks = count_single_tick_sz(low_tick, from, price_break);
var high_ticks = count_single_tick_sz(high_tick, price_break, to);
var total_ticks = low_ticks + high_ticks;
return total_ticks;
static long count_single_tick_sz(
double tick_sz, double from, double to) {
var from_n = (long)MathUtils.round_fractional_units(from, tick_sz);
var to_n = (long)MathUtils.round_fractional_units(to, tick_sz);
return to_n - from_n;
}
}
}
static RegionalTickFactors _get(SecurityImmutable sec) => _exchange_ticks.get(sec);
static RegionalTickFactors _get(ContractSpecification spec) => _exchange_ticks.get(spec);
static RegionalTickFactors _get(ContractSpecificationImmutable spec) => _exchange_ticks.get(spec);
public static class CompareUtils {
@@ -1,6 +1,7 @@
namespace bts.api {
using System;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
public static class SecurityExtensions {
public static Security.ContractOneofCase contract_case(this SecurityType type) =>
@@ -96,6 +97,8 @@ namespace bts.api {
return security.Ice.SecurityDesc;
} else if (security.HasDeribit) {
return security.Deribit.SecurityDesc;
} else if (security.HasFtx) {
return security.Ftx.SecurityDesc;
}
Log.assert(false, "security_desc not implemented for exchange");
return null;
@@ -136,6 +139,7 @@ namespace bts.api {
domain.Venue.Bts => Venue.Bts,
domain.Venue.Sim => Venue.Sim,
domain.Venue.Deribit => Venue.Deribit,
domain.Venue.Ftx => Venue.Ftx,
domain.Venue.Glbx => Venue.Glbx,
domain.Venue.None => Venue.Unset,
_ => throw new NotImplementedException(),
@@ -146,6 +150,7 @@ namespace bts.api {
Venue.Bts => domain.Venue.Bts,
Venue.Sim => domain.Venue.Sim,
Venue.Deribit => domain.Venue.Deribit,
Venue.Ftx => domain.Venue.Ftx,
Venue.Glbx => domain.Venue.Glbx,
Venue.Unset => domain.Venue.None,
_ => throw new NotImplementedException(),
@@ -45,7 +45,7 @@
string.IsNullOrEmpty(exch_desc) ? format_option(symbol, option) : exch_desc;
static string _format(SecurityImmutable? security) =>
security.HasValue ? _format(security.Value) : "Unknown-security";
security.HasValue ? _format(security.Value) : "Unknown-security1";
static string _format(SecurityImmutable security, bool exchange_specific_formatting = true) {
switch (security.ContractCase) {
@@ -71,8 +71,10 @@
return string.Join("", _format_legs(new ContractLegEnumerable(security.Spread.Legs, 1)));
case Security.ContractOneofCase.Synthetic:
return security.Synthetic.Name;
case Security.ContractOneofCase.Spot:
return security.security_desc();
default:
return "Unknown-security";
return "Unknown-security2";
}
}
Loaded 100 of 460 files, more files were not shown because too many files have changed in this diff. Show more