Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c1fe0f561 | ||
|
|
a0eaf60eec | ||
|
|
f2080d558a | ||
|
|
06bfe9c29f | ||
|
|
022546a6c1 | ||
|
|
344d180557 | ||
|
|
a485f8139c | ||
|
|
f50066f4d2 | ||
|
|
277c3056c4 | ||
|
|
5eb95e69cb |
No files matched your search
@@ -9,7 +9,7 @@
|
||||
]
|
||||
},
|
||||
"fable": {
|
||||
"version": "4.24.0",
|
||||
"version": "4.0.6",
|
||||
"commands": [
|
||||
"fable"
|
||||
]
|
||||
@@ -27,4 +27,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
{
|
||||
"dockerFile": "Dockerfile",
|
||||
// Creates local dir to mount to HOME in docker
|
||||
"initializeCommand": "mkdir -p ${localEnv:HOME}/.bts/devcontainer-mounts/${devcontainerId}",
|
||||
"hostRequirements": {
|
||||
"memory": "8gb",
|
||||
"storage": "10gb"
|
||||
"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
|
||||
@@ -12,7 +17,7 @@
|
||||
// example, if 8000-8003 is taken, go with 9000-9003, then 10000-10003, etc.
|
||||
"runArgs": [
|
||||
"--shm-size",
|
||||
"8gb",
|
||||
"2gb",
|
||||
"-p",
|
||||
"8000:8000",
|
||||
"-p",
|
||||
@@ -24,15 +29,22 @@
|
||||
"-p",
|
||||
"12345:12345"
|
||||
],
|
||||
"mounts": [
|
||||
"source=${localEnv:HOME}/.bts/devcontainer-mounts/${devcontainerId},target=/home/bts,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",
|
||||
"xaver.clang-format"
|
||||
"ms-vscode.cpptools"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+13
-184
@@ -31,39 +31,7 @@
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/sparkd-debug-clang/server/sparkd",
|
||||
"args": [
|
||||
"run"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/sparkd-debug-clang/server",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
},
|
||||
{
|
||||
"name": "spreader tests2",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/sparkd-debug-clang/server/sparkd",
|
||||
"args": [
|
||||
"test",
|
||||
"[spreader2]"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/sparkd-debug-clang/server",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
},
|
||||
{
|
||||
"name": "spreader tests",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/sparkd-debug-clang/server/sparkd",
|
||||
"args": [
|
||||
"test",
|
||||
"[spreader]"
|
||||
],
|
||||
"args": ["run"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/sparkd-debug-clang/server",
|
||||
"environment": [],
|
||||
@@ -123,18 +91,6 @@
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"name": "Spark (Skip Build)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "",
|
||||
"windows": {
|
||||
"program": "Spark.exe",
|
||||
"cwd": "build/Debug/bin/net7.0-windows",
|
||||
},
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"name": "Spark (Parameterized)",
|
||||
"type": "coreclr",
|
||||
@@ -152,139 +108,22 @@
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"name": "Spark Test",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "",
|
||||
"windows": {
|
||||
"preLaunchTask": "Build Spark (Debug)",
|
||||
"program": "Spark.Console.exe",
|
||||
"cwd": "build/Debug/bin/net7.0",
|
||||
"args": [
|
||||
"test",
|
||||
"${input:sparkTest}"
|
||||
]
|
||||
},
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"name": "Spark Test (Skip Build)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "",
|
||||
"windows": {
|
||||
"program": "Spark.Console.exe",
|
||||
"cwd": "build/Debug/bin/net7.0",
|
||||
"args": [
|
||||
"test",
|
||||
"${input:sparkTest}"
|
||||
]
|
||||
},
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"name": "Sparkd",
|
||||
"type": "cppdbg",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "Build Sparkd (Debug | Clang)",
|
||||
"program": "",
|
||||
"linux": {
|
||||
"preLaunchTask": "Build Sparkd (Debug)",
|
||||
"program": "${workspaceFolder}/build/sparkd-debug-clang/server/sparkd",
|
||||
"args": [
|
||||
"run"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/build/sparkd-debug-clang/server",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Sparkd (Skip Build)",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "",
|
||||
"linux": {
|
||||
"program": "${workspaceFolder}/build/sparkd-debug-clang/server/sparkd",
|
||||
"args": [
|
||||
"run"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/build/sparkd-debug-clang/server",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Sparkd Test",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "",
|
||||
"linux": {
|
||||
"preLaunchTask": "Build Sparkd (Debug)",
|
||||
"program": "${workspaceFolder}/build/sparkd-debug-clang/server/sparkd",
|
||||
"args": [
|
||||
"test",
|
||||
"${input:sparkdTest}",
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/build/sparkd-debug-clang/server",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Sparkd Test (Skip Build)",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "",
|
||||
"linux": {
|
||||
"program": "${workspaceFolder}/build/sparkd-debug-clang/server/sparkd",
|
||||
"args": [
|
||||
"test",
|
||||
"${input:sparkdTest}",
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/build/sparkd-debug-clang/server",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"program": "sparkd",
|
||||
},
|
||||
"args": [
|
||||
"run"
|
||||
],
|
||||
"cwd": "build/sparkd-debug-clang/server",
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
@@ -315,15 +154,5 @@
|
||||
"description": "Config Server URL",
|
||||
"default": "http://localhost:8000/config",
|
||||
},
|
||||
{
|
||||
"id": "sparkTest",
|
||||
"type": "promptString",
|
||||
"description": "Test to run",
|
||||
},
|
||||
{
|
||||
"id": "sparkdTest",
|
||||
"type": "promptString",
|
||||
"description": "Test to run",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
Vendored
+1
-103
@@ -641,21 +641,6 @@
|
||||
"color": "terminal.ansiGreen",
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Run Spark (Skip Build)",
|
||||
"type": "process",
|
||||
"command": "",
|
||||
"windows": {
|
||||
"command": "Spark.exe",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net7.0-windows"
|
||||
},
|
||||
},
|
||||
"icon": {
|
||||
"id": "run",
|
||||
"color": "terminal.ansiGreen",
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Run Spark Config Query",
|
||||
"dependsOn": "Build Spark",
|
||||
@@ -699,24 +684,6 @@
|
||||
"color": "terminal.ansiGreen",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "Run Sparkd (Skip Build)",
|
||||
"type": "process",
|
||||
"command": "",
|
||||
"linux": {
|
||||
"command": "sparkd",
|
||||
},
|
||||
"args": [
|
||||
"run"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/build/${input:sparkdCmakePreset}/server"
|
||||
},
|
||||
"icon": {
|
||||
"id": "run",
|
||||
"color": "terminal.ansiGreen",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "Run Sparkd Config Query",
|
||||
"dependsOn": "Build Sparkd",
|
||||
@@ -738,65 +705,6 @@
|
||||
"color": "terminal.ansiGreen",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "Test Spark (Debug)",
|
||||
"dependsOn": "Build Spark (Debug)",
|
||||
"type": "process",
|
||||
"icon": {
|
||||
"id": "coverage",
|
||||
"color": "terminal.ansiYellow",
|
||||
},
|
||||
"command": "",
|
||||
"windows": {
|
||||
"command": "Spark.Console.exe",
|
||||
"args": [
|
||||
"test",
|
||||
"${input:sparkTest}"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/build/Debug/bin/net7.0",
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "Test Sparkd",
|
||||
"dependsOn": "Build Sparkd",
|
||||
"type": "process",
|
||||
"command": "",
|
||||
"linux": {
|
||||
"command": "sparkd",
|
||||
},
|
||||
"args": [
|
||||
"test",
|
||||
"${input:sparkdTest}",
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/build/${input:sparkdCmakePreset}/server"
|
||||
},
|
||||
"icon": {
|
||||
"id": "coverage",
|
||||
"color": "terminal.ansiYellow",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "Test Sparkd (Skip Build)",
|
||||
"type": "process",
|
||||
"command": "",
|
||||
"linux": {
|
||||
"command": "sparkd",
|
||||
},
|
||||
"args": [
|
||||
"test",
|
||||
"${input:sparkdTest}",
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/build/${input:sparkdCmakePreset}/server"
|
||||
},
|
||||
"icon": {
|
||||
"id": "coverage",
|
||||
"color": "terminal.ansiYellow",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "View DataStreamQueryAnalyzer Logs (Last Run)",
|
||||
"type": "process",
|
||||
@@ -915,16 +823,6 @@
|
||||
"id": "sparkdConfigQuery",
|
||||
"type": "promptString",
|
||||
"description": "Config Type(s)"
|
||||
},
|
||||
{
|
||||
"id": "sparkTest",
|
||||
"type": "promptString",
|
||||
"description": "Test to run",
|
||||
},
|
||||
{
|
||||
"id": "sparkdTest",
|
||||
"type": "promptString",
|
||||
"description": "Test to run",
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
+3
-10
@@ -88,15 +88,9 @@ else()
|
||||
add_compile_definitions(BINANCE_US_PRICE_STATS_PATH="/api/v3/ticker/24hr")
|
||||
|
||||
# ** Binance USDS Futures **
|
||||
add_compile_definitions(CCAPI_BINANCE_USDS_FUTURES_URL_REST_BASE="https://fapi.binance.com")
|
||||
add_compile_definitions(BINANCE_USDS_FUTURES_INSTRUMENTS_PATH="/fapi/v1/exchangeInfo")
|
||||
add_compile_definitions(BINANCE_USDS_FUTURES_PRICE_STATS_PATH="/fapi/v1/ticker/24hr")
|
||||
add_compile_definitions(BINANCE_USDS_FUTURES_USER_DATA_PATH="/api/v3/userDataStream")
|
||||
|
||||
# ** Binance Coin Futures **
|
||||
add_compile_definitions(CCAPI_BINANCE_COIN_FUTURES_URL_REST_BASE="https://dapi.binance.com")
|
||||
add_compile_definitions(BINANCE_COIN_FUTURES_INSTRUMENTS_PATH="/dapi/v1/exchangeInfo")
|
||||
add_compile_definitions(BINANCE_COIN_FUTURES_PRICE_STATS_PATH="/dapi/v1/ticker/24hr")
|
||||
add_compile_definitions(CCAPI_BINANCE_USDS_FUTURES_URL_REST_BASE="https://dapi.binance.com")
|
||||
add_compile_definitions(BINANCE_USDS_FUTURES_INSTRUMENTS_PATH="/dapi/v1/exchangeInfo")
|
||||
add_compile_definitions(BINANCE_USDS_FUTURES_PRICE_STATS_PATH="/dapi/v1/ticker/24hr")
|
||||
|
||||
# ** Deribit **
|
||||
add_compile_definitions(DERIBIT_INSTRUMENT_PATH="/api/v2/public/get_instrument")
|
||||
@@ -116,7 +110,6 @@ else()
|
||||
|
||||
# subdirs
|
||||
if (BUILD_SPARKD)
|
||||
add_subdirectory(src/lib_binance_execution)
|
||||
add_subdirectory(src/server/sparkd)
|
||||
add_subdirectory(src/data_files)
|
||||
endif()
|
||||
|
||||
+10
-2
@@ -26,7 +26,6 @@
|
||||
"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",
|
||||
@@ -303,7 +302,16 @@
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
},
|
||||
"hidden": true
|
||||
"hidden": true,
|
||||
"targets": [
|
||||
"data_files_dir",
|
||||
"admin_web_app_bundle",
|
||||
"install",
|
||||
"libspark",
|
||||
"rapidjson_project",
|
||||
"server_layout",
|
||||
"sparkd"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
function(protobuf_generate_fs)
|
||||
set(_options)
|
||||
set(_one_value_args TARGET_NAME OUT_DIR PROTOC)
|
||||
set(_multi_value_args PROTO_INCLUDE_DIRS PROTO_SOURCES)
|
||||
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
|
||||
|
||||
if (NOT DEFINED _ARGS_TARGET_NAME)
|
||||
set(_ARGS_TARGET_NAME "${PROJECT_NAME}_generate_fs_protos")
|
||||
endif()
|
||||
if (NOT DEFINED _ARGS_PROTOC)
|
||||
set(_ARGS_PROTOC "${SPARK_BIN}/protoc")
|
||||
endif()
|
||||
|
||||
set(_proto_include_cli_args)
|
||||
foreach(_dir ${_ARGS_PROTO_INCLUDE_DIRS})
|
||||
list(APPEND _proto_include_cli_args -I ${_dir})
|
||||
endforeach()
|
||||
|
||||
set(_outputs)
|
||||
foreach(_proto_source ${_ARGS_PROTO_SOURCES})
|
||||
# get_filename_component(_abs_proto_source ${_proto_source} ABSOLUTE)
|
||||
get_filename_component(_proto_source_we ${_proto_source} NAME_WE)
|
||||
# get_filename_component(_proto_source_dir ${_proto_source} DIRECTORY)
|
||||
list(APPEND _outputs ${_ARGS_OUT_DIR}/${_proto_source_we}.fable.fs)
|
||||
endforeach()
|
||||
|
||||
set(_venv_exec ${CMAKE_SOURCE_DIR}/tools/venv_exec.sh)
|
||||
set(_plugin_py ${CMAKE_SOURCE_DIR}/src/lib/api/service_compiler/plugin.py)
|
||||
set(_template_fs ${CMAKE_SOURCE_DIR}/src/lib/api/service_compiler/templates/fs/fable.fs)
|
||||
add_custom_command(
|
||||
OUTPUT ${_outputs}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMAND ${_venv_exec}
|
||||
${_ARGS_PROTOC}
|
||||
--plugin=protoc-gen-service=${_plugin_py}
|
||||
--service_out=lang=fs,strip_out_path=1:${_ARGS_OUT_DIR}
|
||||
${_proto_include_cli_args}
|
||||
${_ARGS_PROTO_SOURCES}
|
||||
DEPENDS
|
||||
${_venv_exec}
|
||||
${_plugin_py}
|
||||
${_template_fs}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/package-lock.json
|
||||
${_ARGS_PROTO_SOURCES}
|
||||
)
|
||||
add_custom_target(${_ARGS_TARGET_NAME} ALL DEPENDS "${_outputs}")
|
||||
endfunction()
|
||||
@@ -1,33 +0,0 @@
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
function(protobuf_generate_js)
|
||||
set(_options)
|
||||
set(_one_value_args TARGET_NAME OUTPUT)
|
||||
set(_multi_value_args PROTO_INCLUDE_DIRS PROTO_SOURCES)
|
||||
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
|
||||
|
||||
if (NOT DEFINED _ARGS_TARGET_NAME)
|
||||
set(_ARGS_TARGET_NAME "${PROJECT_NAME}_generate_js_protos")
|
||||
endif()
|
||||
|
||||
set(_proto_include_cli_args)
|
||||
foreach(_dir ${_ARGS_PROTO_INCLUDE_DIRS})
|
||||
list(APPEND _proto_include_cli_args -p ${_dir})
|
||||
endforeach()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${_ARGS_OUTPUT}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMAND npx
|
||||
--no-install pbjs
|
||||
-t json-module
|
||||
-w commonjs
|
||||
-o ${_ARGS_OUTPUT}
|
||||
${_proto_include_cli_args}
|
||||
${_ARGS_PROTO_SOURCES}
|
||||
DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/package-lock.json
|
||||
${_ARGS_PROTO_SOURCES}
|
||||
)
|
||||
add_custom_target(${_ARGS_TARGET_NAME} ALL DEPENDS ${_ARGS_OUTPUT})
|
||||
endfunction()
|
||||
@@ -1,49 +0,0 @@
|
||||
include_guard(DIRECTORY)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/TernaryOperator.cmake)
|
||||
|
||||
function(sym_link_dir)
|
||||
set(_options)
|
||||
set(_one_value_args TARGET_NAME SOURCE DEST COMMENT)
|
||||
set(_multi_value_args)
|
||||
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
|
||||
add_custom_command(
|
||||
OUTPUT ${_ARGS_DEST}
|
||||
COMMAND ln ARGS -sf ${_ARGS_SOURCE} ${_ARGS_DEST}
|
||||
)
|
||||
set_ternary(_comment _ARGS_COMMENT "${_ARGS_COMMENT}" "linking dir ${_ARGS_SOURCE} -> ${_ARGS_DEST}")
|
||||
add_custom_target(
|
||||
${_ARGS_TARGET_NAME} ALL
|
||||
DEPENDS ${_ARGS_DEST}
|
||||
COMMENT "${_comment}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(sym_link_file)
|
||||
set(_options)
|
||||
set(_one_value_args SOURCE DEST INSTALL_DEST)
|
||||
set(_multi_value_args)
|
||||
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
|
||||
file(
|
||||
CREATE_LINK
|
||||
${_ARGS_SOURCE}
|
||||
${_ARGS_DEST}
|
||||
SYMBOLIC
|
||||
)
|
||||
if (DEFINED _ARGS_INSTALL_DEST)
|
||||
install(
|
||||
FILES
|
||||
${_ARGS_SOURCE}
|
||||
DESTINATION ${_ARGS_INSTALL_DEST}
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(sym_link_local_lib LIB_NAME)
|
||||
set(_lib_file_name "${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_NAME}.so")
|
||||
file(
|
||||
CREATE_LINK
|
||||
"${CMAKE_BINARY_DIR}/src/lib_${LIB_NAME}/${_lib_file_name}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${_lib_file_name}"
|
||||
SYMBOLIC
|
||||
)
|
||||
endfunction()
|
||||
@@ -1,9 +0,0 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
macro(set_ternary RESULT CONDITION TRUE_VALUE FALSE_VALUE)
|
||||
if(${CONDITION})
|
||||
set(${RESULT} ${TRUE_VALUE})
|
||||
else()
|
||||
set(${RESULT} ${FALSE_VALUE})
|
||||
endif()
|
||||
endmacro()
|
||||
Submodule
+1
Submodule docker/linux added at 56c61231f3.
Submodule
+1
Submodule docker/windows added at de7d40c09d.
+1
-1
Submodule externals updated: 959b63f2f6...8069cc893f.
@@ -23,6 +23,7 @@
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.23.1" />
|
||||
<PackageReference Include="Parquet.Net" Version="5.0.1" />
|
||||
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
|
||||
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -4,42 +4,46 @@ namespace bts.SilverPine {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
//using MathNet.Numerics.Statistics;
|
||||
|
||||
using ZstdSharp.Unsafe;
|
||||
|
||||
public enum GroupByBundleOp {
|
||||
public enum MultiRowOp {
|
||||
Mean,
|
||||
Median,
|
||||
Mode,
|
||||
Range,
|
||||
StdDev,
|
||||
Max,
|
||||
Min,
|
||||
First,
|
||||
Last,
|
||||
Sum
|
||||
Sum,
|
||||
EdgeDiff,
|
||||
EdgeDiffPercentage,
|
||||
//IsEdgeDiff,
|
||||
}
|
||||
public class GroupByBundle {
|
||||
public readonly List<Tuple<string, GroupByBundleOp>> operations;
|
||||
public class MultiRowOpHelper {
|
||||
public readonly List<Tuple<string, MultiRowOp>> operations;
|
||||
|
||||
public static GroupByBundle parse(string text) {
|
||||
public static MultiRowOpHelper parse(string text) {
|
||||
// text is inp_col: op, inp_col: op, inp_col:op
|
||||
List<Tuple<string, GroupByBundleOp>> rv = new List<Tuple<string, GroupByBundleOp>>();
|
||||
List<Tuple<string, MultiRowOp>> rv = new List<Tuple<string, MultiRowOp>>();
|
||||
string[] calcs = text.Split(',');
|
||||
foreach (string c in calcs) {
|
||||
string[] toks = c.Split(':');
|
||||
string inp_col = toks[0].Trim();
|
||||
string op_txt = toks[1].Trim();
|
||||
GroupByBundleOp op = Enum.Parse<GroupByBundleOp>(op_txt, true);
|
||||
MultiRowOp op = Enum.Parse<MultiRowOp>(op_txt, true);
|
||||
rv.Add(Tuple.Create(inp_col, op));
|
||||
}
|
||||
return new GroupByBundle(rv);
|
||||
return new MultiRowOpHelper(rv);
|
||||
}
|
||||
|
||||
public GroupByBundle(List<Tuple<string, GroupByBundleOp>> operations_) {
|
||||
public MultiRowOpHelper(List<Tuple<string, MultiRowOp>> operations_) {
|
||||
operations = operations_;
|
||||
}
|
||||
|
||||
public static IBlueRow group_by(List<IBlueRow> rows, GroupByBundle parameters) {
|
||||
public static IBlueRow group_by(List<IBlueRow> rows, MultiRowOpHelper parameters) {
|
||||
if (rows.Count() == 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -47,8 +51,8 @@ namespace bts.SilverPine {
|
||||
IBlueRow ir = rows.ElementAt(0);
|
||||
IBlueTableSchema in_schema = ir.schema;
|
||||
IBlueTableSchema out_schema = in_schema;
|
||||
IDictionary<string, Tuple<string, GroupByBundleOp>> out_col_map =
|
||||
new Dictionary<string, Tuple<string, GroupByBundleOp>>();
|
||||
IDictionary<string, Tuple<string, MultiRowOp>> out_col_map =
|
||||
new Dictionary<string, Tuple<string, MultiRowOp>>();
|
||||
foreach (var t in parameters.operations) {
|
||||
string out_col_name = $"{t.Item1}_{t.Item2}".ToLower();
|
||||
out_schema = out_schema.add_column(TypeCode.Double, out_col_name);
|
||||
@@ -58,7 +62,7 @@ namespace bts.SilverPine {
|
||||
foreach (var kv in out_col_map) {
|
||||
string out_col_name = kv.Key;
|
||||
string in_col_name = kv.Value.Item1;
|
||||
GroupByBundleOp op = kv.Value.Item2;
|
||||
MultiRowOp op = kv.Value.Item2;
|
||||
IEnumerable<double> col_vals = BlueDataUtility.get_colvalues<double>(rows, in_col_name);
|
||||
double comp_res = compute_op(col_vals, op);
|
||||
or.setValue(out_col_name, comp_res);
|
||||
@@ -66,20 +70,51 @@ namespace bts.SilverPine {
|
||||
return or;
|
||||
}
|
||||
|
||||
public static double compute_op(IEnumerable<double> col_vals, GroupByBundleOp op) {
|
||||
// rolling_rows are the rolling window of rows. the 0th row is the one thats gettin
|
||||
// calculated. rows 1 - n-1 are its window rows. the schema is based on the operations.
|
||||
|
||||
public IBlueRow rolling_calc(List<IBlueRow> rolling_rows) {
|
||||
if (rolling_rows.Count() == 0) {
|
||||
return null;
|
||||
}
|
||||
IBlueRow ir = rolling_rows.ElementAt(0);
|
||||
IBlueTableSchema in_schema = ir.schema;
|
||||
IBlueTableSchema out_schema = in_schema;
|
||||
IDictionary<string, Tuple<string, MultiRowOp>> out_col_map =
|
||||
new Dictionary<string, Tuple<string, MultiRowOp>>();
|
||||
foreach (var t in operations) {
|
||||
string out_col_name = $"{t.Item1}_{t.Item2}".ToLower();
|
||||
out_schema = out_schema.add_column(TypeCode.Double, out_col_name);
|
||||
out_col_map[out_col_name] = t;
|
||||
}
|
||||
MutableBlueRow or = new MutableBlueRow(out_schema, ir);
|
||||
foreach (var kv in out_col_map) {
|
||||
string out_col_name = kv.Key;
|
||||
string in_col_name = kv.Value.Item1;
|
||||
MultiRowOp op = kv.Value.Item2;
|
||||
IEnumerable<double> col_vals = BlueDataUtility.get_colvalues<double>(rolling_rows, in_col_name);
|
||||
double comp_res = compute_op(col_vals, op);
|
||||
or.setValue(out_col_name, comp_res);
|
||||
}
|
||||
return or;
|
||||
}
|
||||
|
||||
public static double compute_op(IEnumerable<double> col_vals, MultiRowOp op) {
|
||||
switch (op) {
|
||||
case GroupByBundleOp.Max:
|
||||
case MultiRowOp.Max:
|
||||
return col_vals.Max<double>();
|
||||
case GroupByBundleOp.Min:
|
||||
case MultiRowOp.Min:
|
||||
return col_vals.Min<double>();
|
||||
case GroupByBundleOp.Mean:
|
||||
case MultiRowOp.Mean:
|
||||
return col_vals.Sum() / col_vals.Count();
|
||||
case GroupByBundleOp.Sum:
|
||||
case MultiRowOp.Sum:
|
||||
return col_vals.Sum();
|
||||
case GroupByBundleOp.First:
|
||||
case MultiRowOp.First:
|
||||
return col_vals.First();
|
||||
case GroupByBundleOp.Last:
|
||||
case MultiRowOp.Last:
|
||||
return col_vals.Last();
|
||||
case MultiRowOp.StdDev:
|
||||
return MathNet.Numerics.Statistics.Statistics.StandardDeviation(col_vals);
|
||||
default:
|
||||
// TODO
|
||||
Log.assert(false);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace bts.SilverPine {
|
||||
using BCG;
|
||||
|
||||
using System;
|
||||
|
||||
[DataStreamUserRespresentation("AggregationStream")]
|
||||
public class AgregationStream : ViewStream {
|
||||
|
||||
public AgregationStream(ViewStream parent_str, string operation_txt) {
|
||||
ViewStream rs = new NonIncGroupByStream(parent_str, String.Empty, operation_txt);
|
||||
mySetRealSource(rs);
|
||||
}
|
||||
|
||||
public AgregationStream(ViewStream parent_str, string groupby_col, string operation_txt) {
|
||||
ViewStream rs = new NonIncGroupByStream(parent_str, groupby_col, operation_txt);
|
||||
mySetRealSource(rs);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+7
-4
@@ -8,7 +8,7 @@ using bts;
|
||||
using bts.SilverPine;
|
||||
|
||||
namespace BCG {
|
||||
[DataStreamUserRespresentation("aggregate")]
|
||||
[DataStreamUserRespresentation("AggregateStream")]
|
||||
public class NonIncGroupByStream : ViewStream {
|
||||
private class DelNonIncGroupByEvaluator : INonIncGroupByEvaluator {
|
||||
Func<string[]> _groupby_cols_func;
|
||||
@@ -53,15 +53,18 @@ namespace BCG {
|
||||
Func<List<IBlueRow>, List<IBlueRow>> evaluate_func)
|
||||
: this(main_stream, new DelNonIncGroupByEvaluator(groupby_cols_func, evaluate_func)) { }
|
||||
|
||||
public NonIncGroupByStream(ViewStream parent_str, string groupby_col, string standard_group_by_text, string optional_sort_col = null) {
|
||||
GroupByBundle std_group_by = GroupByBundle.parse(standard_group_by_text);
|
||||
public NonIncGroupByStream(ViewStream parent_str, string groupby_col, string standard_group_by_text)
|
||||
: this(parent_str, groupby_col, standard_group_by_text, String.Empty) { }
|
||||
|
||||
public NonIncGroupByStream(ViewStream parent_str, string groupby_col, string standard_group_by_text, string optional_sort_col) {
|
||||
MultiRowOpHelper std_group_by = MultiRowOpHelper.parse(standard_group_by_text);
|
||||
Func<string[]> groupby_cols_func = () => {
|
||||
return String.IsNullOrEmpty(groupby_col) ? null : new string[] { groupby_col };
|
||||
};
|
||||
Func<List<IBlueRow>, List<IBlueRow>> evaluate_func = (group_rows) => {
|
||||
if (!String.IsNullOrEmpty(optional_sort_col))
|
||||
group_rows.Sort((r1, r2) => BlueDataUtility.compare_rows(r2, r1, optional_sort_col));
|
||||
IBlueRow rv = GroupByBundle.group_by(group_rows, std_group_by);
|
||||
IBlueRow rv = MultiRowOpHelper.group_by(group_rows, std_group_by);
|
||||
return new IBlueRow[] { rv }.ToList();
|
||||
};
|
||||
ViewStream rs = new NonIncGroupByStream(parent_str, groupby_cols_func, evaluate_func);
|
||||
|
||||
@@ -69,4 +69,6 @@ namespace bts {
|
||||
myFireEvent(dp_out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -13,9 +13,8 @@ namespace BCG {
|
||||
/// desired row ie newRow = f(row), where f is the mapping fucntion. Note that the id
|
||||
/// is preserved across the rows, ie row["id"] = f(row)["id"]
|
||||
/// </summary>
|
||||
[DataStreamUserRespresentation (false)]
|
||||
public class MapStream : MethodDrivenStream
|
||||
{
|
||||
[DataStreamUserRespresentation(false)]
|
||||
public class MapStream : MethodDrivenStream {
|
||||
protected IStreamMapper myMapper;
|
||||
protected IChangeableStreamMapper myChangeableMapper = null;
|
||||
private bool myUseNewMap = Configuration.get<DataStreamConfig>().UseNewMap;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace bts.SilverPine
|
||||
{
|
||||
using BCG;
|
||||
|
||||
[DataStreamUserRespresentation("RollingComputationStream")]
|
||||
public class RollingComputationStream : ViewStream
|
||||
{
|
||||
private ViewStream _parent_str;
|
||||
public RollingComputationStream(ViewStream parent_str, string operation_txt, string roll_column, uint window_sz)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace bts.SilverPine {
|
||||
using BCG;
|
||||
|
||||
|
||||
[DataStreamUserRespresentation("RollingComputationStream")]
|
||||
public class RollingComputationStream : ViewStream {
|
||||
public RollingComputationStream(ViewStream parent_str, string operation_txt, string roll_column, uint window_sz) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,6 +108,7 @@ namespace bts {
|
||||
|
||||
}
|
||||
|
||||
[DataStreamUserRespresentation("Project")]
|
||||
public class NewProjectStream : ViewStream, IStreamMapper {
|
||||
|
||||
private ViewStream _stream;
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace bts.SilverPine {
|
||||
using BCG;
|
||||
|
||||
using System;
|
||||
[DataStreamUserRespresentation("DateTimeExpanderStream")]
|
||||
|
||||
public class DateTimeExpanstionStream : ViewStream {
|
||||
public DateTimeExpanstionStream(ViewStream parent_stream, string inp_col) {
|
||||
Func<IBlueTableSchema, IBlueTableSchema> get_schema = (s) => {
|
||||
@@ -24,11 +24,11 @@ namespace bts.SilverPine {
|
||||
IBlueTableSchema s = get_schema(r.schema);
|
||||
MutableBlueRow rv = new MutableBlueRow(s, r);
|
||||
rv.setValue($"{inp_col}_date", dt.Date);
|
||||
rv.setValue($"{inp_col}_day_of_week", dt.DayOfWeek.ToString());
|
||||
rv.setValue($"{inp_col}_day", dt.DayOfWeek.ToString());
|
||||
rv.setValue($"{inp_col}_day_of_month", dt.Day);
|
||||
rv.setValue($"{inp_col}_month", dt.Date.Month);
|
||||
rv.setValue($"{inp_col}_hour", dt.Hour);
|
||||
rv.setValue($"{inp_col}_minute", dt.Minute);
|
||||
rv.setValue($"{inp_col}_min", dt.Minute);
|
||||
rv.setValue($"{inp_col}_seconds_of_day", (dt - dt.Date).TotalSeconds);
|
||||
return rv;
|
||||
});
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using mbts;
|
||||
|
||||
namespace BCG
|
||||
{
|
||||
internal class MutableAppendTable : IBlueRowCollection
|
||||
{
|
||||
private class MyRow : IBlueRow
|
||||
{
|
||||
namespace BCG {
|
||||
internal class MutableAppendTable : IBlueRowCollection {
|
||||
private class MyRow : IBlueRow {
|
||||
private IBlueRow myRow;
|
||||
private int myID;
|
||||
|
||||
public MyRow(IBlueRow r, int id)
|
||||
{
|
||||
public MyRow(IBlueRow r, int id) {
|
||||
myRow = r;
|
||||
myID = id;
|
||||
}
|
||||
@@ -21,10 +18,8 @@ namespace BCG
|
||||
|
||||
public IBlueTableSchema schema { get { return myRow.schema; } }
|
||||
|
||||
public object this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
public object this[int index] {
|
||||
get {
|
||||
if (myRow.schema[index].colName == "id")
|
||||
return myID;
|
||||
else
|
||||
@@ -52,20 +47,17 @@ namespace BCG
|
||||
|
||||
public System.Collections.ICollection Keys { get { return myRow.Keys; } }
|
||||
|
||||
public void Remove(object key) {throw new Exception("The method or operation is not implemented.");}
|
||||
public void Remove(object key) { throw new Exception("The method or operation is not implemented."); }
|
||||
|
||||
public System.Collections.ICollection Values
|
||||
{
|
||||
get
|
||||
{
|
||||
public System.Collections.ICollection Values {
|
||||
get {
|
||||
object[] vals = new object[myRow.Count];
|
||||
CopyTo(vals, 0);
|
||||
return vals;
|
||||
}
|
||||
}
|
||||
|
||||
public object this[object key]
|
||||
{
|
||||
public object this[object key] {
|
||||
get { return key is string ? this[key as string] : this[(int)key]; }
|
||||
set { throw new Exception("The method or operation is not implemented."); }
|
||||
}
|
||||
@@ -74,8 +66,7 @@ namespace BCG
|
||||
|
||||
#region ICollection Members
|
||||
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
public void CopyTo(Array array, int index) {
|
||||
myRow.CopyTo(array, index);
|
||||
array.SetValue(myID, index + myRow.schema["id"].colNo);
|
||||
}
|
||||
@@ -90,8 +81,7 @@ namespace BCG
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{ return new BlueRowEnumerator(this); }
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new BlueRowEnumerator(this); }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -106,8 +96,7 @@ namespace BCG
|
||||
|
||||
public MutableAppendTable(IBlueTableSchema schema) { myRows = new BlueTable(schema); }
|
||||
|
||||
public IBlueRow addRow(IBlueRow r, int tableNo)
|
||||
{
|
||||
public IBlueRow addRow(IBlueRow r, int tableNo) {
|
||||
if (!myIDMap.ContainsKey(tableNo))
|
||||
myIDMap.Add(tableNo, new Dictionary<int, IBlueRow>());
|
||||
IBlueRow nr = new MyRow(r, myID++); // BlueDataUtility.getNewReplacedRow(r, myID++, "id");
|
||||
@@ -116,20 +105,17 @@ namespace BCG
|
||||
return nr;
|
||||
}
|
||||
|
||||
public IBlueRow removeRow(IBlueRow r, int tableNo)
|
||||
{
|
||||
public IBlueRow removeRow(IBlueRow r, int tableNo) {
|
||||
IBlueRow dr = myIDMap[tableNo][(int)r["id"]];
|
||||
myRows.deleteRow(dr);
|
||||
myIDMap[tableNo].Remove((int)r["id"]);
|
||||
return dr;
|
||||
}
|
||||
|
||||
public IBlueRowCollection removeTable(int tableNo)
|
||||
{
|
||||
public IBlueRowCollection removeTable(int tableNo) {
|
||||
if (!myIDMap.ContainsKey(tableNo))
|
||||
return null;
|
||||
else
|
||||
{
|
||||
else {
|
||||
BlueTable rv = new BlueTable(myRows.schema);
|
||||
foreach (IBlueRow r in myIDMap[tableNo].Values)
|
||||
rv.addRow(r);
|
||||
@@ -139,8 +125,7 @@ namespace BCG
|
||||
}
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
public void clear() {
|
||||
myRows.clear();
|
||||
myIDMap.Clear();
|
||||
}
|
||||
@@ -151,7 +136,7 @@ namespace BCG
|
||||
|
||||
public IBlueTableSchema schema { get { return myRows.schema; } }
|
||||
|
||||
public bool containsID(int id) {return myRows.containsID(id);}
|
||||
public bool containsID(int id) { return myRows.containsID(id); }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -169,7 +154,7 @@ namespace BCG
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
public System.Collections.IEnumerator GetEnumerator() {return myRows.GetEnumerator();}
|
||||
public System.Collections.IEnumerator GetEnumerator() { return myRows.GetEnumerator(); }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -179,8 +164,8 @@ namespace BCG
|
||||
/// to append (or remove) more streams at
|
||||
/// runtime
|
||||
/// </summary>
|
||||
public class MutableAppendStream : ViewStream
|
||||
{
|
||||
[DataStreamUserRespresentation("UnionStream")]
|
||||
public class MutableAppendStream : ViewStream {
|
||||
private bool mySubscriptionActive = false;
|
||||
private MutableAppendTable myTable;
|
||||
private int myMovingIndex;
|
||||
@@ -188,22 +173,17 @@ namespace BCG
|
||||
private bool _allign_col_orders = true;
|
||||
|
||||
public MutableAppendStream(params ViewStream[] streams)
|
||||
: this(new List<ViewStream>(streams))
|
||||
{ }
|
||||
: this(new List<ViewStream>(streams)) { }
|
||||
|
||||
public MutableAppendStream(bool allign_col_order, params ViewStream[] streams)
|
||||
: this(allign_col_order, new List<ViewStream>(streams))
|
||||
{ }
|
||||
: this(allign_col_order, new List<ViewStream>(streams)) { }
|
||||
|
||||
public MutableAppendStream(ICollection<ViewStream> streams)
|
||||
: this(true, streams)
|
||||
{ }
|
||||
: this(true, streams) { }
|
||||
|
||||
public MutableAppendStream(bool allign_col_orders, ICollection<ViewStream> streams)
|
||||
{
|
||||
public MutableAppendStream(bool allign_col_orders, ICollection<ViewStream> streams) {
|
||||
_allign_col_orders = allign_col_orders;
|
||||
foreach (ViewStream vs in streams)
|
||||
{
|
||||
foreach (ViewStream vs in streams) {
|
||||
myMovingIndex++;
|
||||
ViewStream pj = new ChangeTableNameStream(vs, myMovingIndex.ToString());
|
||||
if (_allign_col_orders)
|
||||
@@ -212,8 +192,7 @@ namespace BCG
|
||||
}
|
||||
}
|
||||
|
||||
public void addStream(ViewStream vs)
|
||||
{
|
||||
public void addStream(ViewStream vs) {
|
||||
myMovingIndex++;
|
||||
///the change name is done to identify the callbacks, and the project to
|
||||
///make all the schemas col orders the same
|
||||
@@ -225,19 +204,14 @@ namespace BCG
|
||||
pj.onChange += new ChangeDelegate(myHandleStreamChanges);
|
||||
}
|
||||
|
||||
public void removeStream(ViewStream vs)
|
||||
{
|
||||
public void removeStream(ViewStream vs) {
|
||||
int index = -1;
|
||||
foreach (KeyValuePair<int, Pair<ViewStream, ViewStream>> p in myStreamIndex)
|
||||
{
|
||||
if (p.Value.first == vs)
|
||||
{
|
||||
foreach (KeyValuePair<int, Pair<ViewStream, ViewStream>> p in myStreamIndex) {
|
||||
if (p.Value.first == vs) {
|
||||
index = p.Key;
|
||||
if (mySubscriptionActive)
|
||||
{
|
||||
if (mySubscriptionActive) {
|
||||
p.Value.second.onChange -= new ChangeDelegate(myHandleStreamChanges);
|
||||
if (myTable != null)
|
||||
{
|
||||
if (myTable != null) {
|
||||
IBlueRowCollection delRows = myTable.removeTable(p.Key);
|
||||
if (delRows != null)
|
||||
myFireEvent(new DataPacket(myTable, new BlueTable(myTable.schema), delRows));
|
||||
@@ -250,15 +224,13 @@ namespace BCG
|
||||
}
|
||||
}
|
||||
|
||||
protected override void myOnFirstSubscriber()
|
||||
{
|
||||
protected override void myOnFirstSubscriber() {
|
||||
mySubscriptionActive = true;
|
||||
foreach (KeyValuePair<int, Pair<ViewStream, ViewStream>> p in myStreamIndex)
|
||||
p.Value.second.onChange += new ChangeDelegate(myHandleStreamChanges);
|
||||
}
|
||||
|
||||
protected override void myOnLastSubscriber()
|
||||
{
|
||||
protected override void myOnLastSubscriber() {
|
||||
mySubscriptionActive = false;
|
||||
foreach (KeyValuePair<int, Pair<ViewStream, ViewStream>> p in myStreamIndex)
|
||||
p.Value.second.onChange -= new ChangeDelegate(myHandleStreamChanges);
|
||||
@@ -266,20 +238,17 @@ namespace BCG
|
||||
myTable.clear();
|
||||
}
|
||||
|
||||
private void myHandleStreamChanges(object newValue)
|
||||
{
|
||||
private void myHandleStreamChanges(object newValue) {
|
||||
DataPacket dp = newValue as DataPacket;
|
||||
myTable = myTable ?? new MutableAppendTable(new BlueTableSchema("MutableAppend", dp.schema.columnInfo));
|
||||
BlueTable adds = new BlueTable(myTable.schema),
|
||||
dels = new BlueTable(myTable.schema);
|
||||
int index = Int32.Parse(dp.schema.tableName);
|
||||
foreach (IBlueRow r in dp.deletedRows)
|
||||
{
|
||||
foreach (IBlueRow r in dp.deletedRows) {
|
||||
IBlueRow dr = myTable.removeRow(r, index);
|
||||
dels.addRow(dr);
|
||||
}
|
||||
foreach (IBlueRow r in dp.addedRows)
|
||||
{
|
||||
foreach (IBlueRow r in dp.addedRows) {
|
||||
IBlueRow nr = myTable.addRow(r, index);
|
||||
adds.addRow(nr);
|
||||
}
|
||||
|
||||
@@ -36,8 +36,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bts.ColumnExpressions", "Bt
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bts.ColumnSortExpressions", "Bts.ColumnSortExpressions\Bts.ColumnSortExpressions.csproj", "{B038CB72-4E81-4AF1-976F-ED8655C2C3D6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataStreamQueryAnalyzer", "DataStreamQueryAnalyzer\DataStreamQueryAnalyzer.csproj", "{BFA5BA5C-927A-41A9-A696-1E2F4B5FFD15}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -115,9 +113,5 @@ Global
|
||||
{B038CB72-4E81-4AF1-976F-ED8655C2C3D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B038CB72-4E81-4AF1-976F-ED8655C2C3D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B038CB72-4E81-4AF1-976F-ED8655C2C3D6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BFA5BA5C-927A-41A9-A696-1E2F4B5FFD15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BFA5BA5C-927A-41A9-A696-1E2F4B5FFD15}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BFA5BA5C-927A-41A9-A696-1E2F4B5FFD15}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BFA5BA5C-927A-41A9-A696-1E2F4B5FFD15}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -148,22 +148,27 @@ namespace bts.SilverPine {
|
||||
|
||||
[DataStreamUserRespresentation("FutureHistoricalBars")]
|
||||
public class FutureHistoricalBarsStream : ViewStream {
|
||||
public FutureHistoricalBarsStream(string fut_contract, string bar_size_txt, string range_beg, string range_end) {
|
||||
DateTime dt_range_beg = DateTime.Parse(range_beg);
|
||||
DateTime dt_range_end = DateTime.Parse(range_end);
|
||||
TimeSpan diff = dt_range_end - dt_range_beg;
|
||||
BarSize bar_size = Enum.Parse<BarSize>(bar_size_txt, true);
|
||||
ViewStream rs = new FutureHistoricalBarsStream(fut_contract, bar_size.ToString(), dt_range_beg, dt_range_end);
|
||||
mySetRealSource(rs);
|
||||
}
|
||||
|
||||
public FutureHistoricalBarsStream(string fut_contract, string range_beg, string range_end) {
|
||||
DateTime dt_range_beg = DateTime.Parse(range_beg);
|
||||
DateTime dt_range_end = DateTime.Parse(range_end);
|
||||
TimeSpan diff = dt_range_end - dt_range_beg;
|
||||
BarSize bar_size = diff < TimeSpan.FromDays(1.0) ? BarSize.ONE_MIN : BarSize.ONE_DAY;
|
||||
BarSize bar_size = diff < TimeSpan.FromDays(1.0) ? BarSize.ONE_DAY : BarSize.ONE_MIN;
|
||||
ViewStream rs = new FutureHistoricalBarsStream(fut_contract, bar_size.ToString(), dt_range_beg, dt_range_end);
|
||||
mySetRealSource(rs);
|
||||
}
|
||||
|
||||
public FutureHistoricalBarsStream(string fut_contract, string bar_size)
|
||||
: this(fut_contract, bar_size, DateTime.MinValue, DateTime.MaxValue) { }
|
||||
|
||||
public FutureHistoricalBarsStream(string fut_contract, string bar_size,
|
||||
string dt_range_beg, string dt_range_end)
|
||||
: this(fut_contract, bar_size, DateTime.Parse(dt_range_beg),
|
||||
DateTime.Parse(dt_range_end)) { }
|
||||
|
||||
public FutureHistoricalBarsStream(string fut_contract, string bar_size,
|
||||
DateTime dt_range_beg, DateTime dt_range_end)
|
||||
:
|
||||
|
||||
@@ -43,4 +43,11 @@ namespace bts.SilverPine {
|
||||
mySetRealSource(r);
|
||||
}
|
||||
}
|
||||
|
||||
[DataStreamUserRespresentation("PeriodicVolumeAgg")]
|
||||
public class PeriodicVolumeAggregationStream : ViewStream {
|
||||
public PeriodicVolumeAggregationStream(string future_contract, string contract_type, DateTime range_beg, DateTime range_end,
|
||||
TimeSpan period, ViewStreamDatabase db) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,9 +100,9 @@ namespace bts.SilverPine {
|
||||
this(symbol_or_contract, beg_dt, end_dt, ViewStreamDatabase.instance) { }
|
||||
|
||||
public VWAPStream(string symbol_or_contract, DateTime beg_dt, DateTime end_dt, ViewStreamDatabase db) {
|
||||
var fc = new ContractUtils.FutureContract(symbol_or_contract);
|
||||
ViewStream r = new FutureHistoricalBarsStream(fc, BarSize.ONE_DAY, new DateTimeRange(beg_dt, end_dt));
|
||||
|
||||
//var fc = new ContractUtils.FutureContract(symbol_or_contract);
|
||||
ViewStream r = new FutureHistoricalBarsStream(symbol_or_contract, beg_dt.ToString(), end_dt.ToString());
|
||||
// since this is already filtered, we can groupBy iit without a groupBy column
|
||||
r = new MathStream(r,
|
||||
"vprice = volume * close");
|
||||
r = new NonIncGroupByStream(r,
|
||||
|
||||
+8
-13
@@ -90,20 +90,15 @@ namespace bts.SilverPine {
|
||||
// the argument is symbol, like "ES" etc and not the contract itself
|
||||
public FuturesContinousContractStream(string symbol) : this(symbol, ViewStreamDatabase.instance) { }
|
||||
public FuturesContinousContractStream(string symbol, ViewStreamDatabase db) {
|
||||
symbol = symbol.EndsWith("*") ? symbol : symbol + "*";
|
||||
ViewStream r = new FuturesHistorical1DayBarsStream(symbol);
|
||||
// filtering for futures only
|
||||
r = new FilterStream(r, r => {
|
||||
string contract = r.get<string>("symbol");
|
||||
var fc = new ContractUtils.FutureContract(contract);
|
||||
return fc.future_tradable_contract != null;
|
||||
});
|
||||
r = new NonIncGroupByStream(r,
|
||||
"ts_event",
|
||||
"volume:Max");
|
||||
r = new NewProjectStream(r,
|
||||
"ts_event symbol !");
|
||||
r = new SortStream(r,
|
||||
"-ts_event");
|
||||
r = new ContractExpanderStream(r, "symbol");
|
||||
r = new FilterStream(r, "contract_type='Future'");
|
||||
// finding the max volume across symbols, if we need to do this for open_interest, then join here
|
||||
// with the open interest stream on symbol/contract
|
||||
r = new NonIncGroupByStream(r, "ts_event", "volume:Max");
|
||||
r = new NewProjectStream(r, "ts_event symbol !");
|
||||
r = new SortStream(r, "-ts_event");
|
||||
mySetRealSource(r);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,14 @@ namespace bts.SilverPine {
|
||||
using bts;
|
||||
using BCG;
|
||||
|
||||
// it uses the volume - better to use open interst perhap - to detect the rollover, is when a new
|
||||
// futuere exp becomes the highest volume one
|
||||
// it uses the volume (better to use open interest perhap) to detect the rollover, ie when a new
|
||||
// future exp becomes the highest volume one
|
||||
[DataStreamUserRespresentation("FuturesRoll")]
|
||||
public class FuturesRollStream : ViewStream {
|
||||
public FuturesRollStream(string future_symbol) {
|
||||
//ViewStream ohlcv_str = new OHLCVStream(future_symbol);
|
||||
ViewStream rs = new FuturesActiveContractStream(future_symbol);
|
||||
rs = new ColumnEvolutionStream(rs, "ts_event", "contract");
|
||||
rs = new NewProjectStream(rs, "ts_event:roll_date !");
|
||||
ViewStream rs = new FuturesContinousContractStream(future_symbol);
|
||||
rs = new ColumnEvolutionStream(rs, "ts_event", "symbol");
|
||||
rs = new NewProjectStream(rs, "symbol ts_event:roll_date !");
|
||||
mySetRealSource(rs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,21 @@ namespace bts.SilverPine {
|
||||
using BCG;
|
||||
|
||||
using bts;
|
||||
|
||||
// used directly in blueorb
|
||||
[DataStreamUserRespresentation("Settlement")]
|
||||
public class SettlementPriceStream : ViewStream {
|
||||
|
||||
public SettlementPriceStream(string future_name, string date_txt)
|
||||
: this(future_name, date_txt, date_txt) { }
|
||||
// public SettlementPriceStream(string future_name, string date_txt)
|
||||
// : this(future_name, date_txt, date_txt) { }
|
||||
|
||||
public SettlementPriceStream(string future_name, string begin_dt, string end_dt)
|
||||
: this(future_name, DateTime.Parse(begin_dt).Date, DateTime.Parse(end_dt).Date) { }
|
||||
// public SettlementPriceStream(string future_name, string begin_dt, string end_dt)
|
||||
// : this(future_name, DateTime.Parse(begin_dt).Date, DateTime.Parse(end_dt).Date) { }
|
||||
|
||||
public SettlementPriceStream(string symbol_or_contract, DateTime begin_dt)
|
||||
: this(symbol_or_contract, begin_dt, begin_dt) { }
|
||||
public SettlementPriceStream(string future_contract, DateTime begin_dt)
|
||||
: this(future_contract, begin_dt, begin_dt) { }
|
||||
|
||||
public SettlementPriceStream(string symbol_or_contract, DateTime begin_dt, DateTime end_dt) {
|
||||
ContractUtils.FutureContract fc = new ContractUtils.FutureContract(symbol_or_contract);
|
||||
public SettlementPriceStream(string future_contract, DateTime begin_dt, DateTime end_dt) {
|
||||
ContractUtils.FutureContract fc = new ContractUtils.FutureContract(future_contract);
|
||||
ViewStream rs = new FutureHistoricalBarsStream(fc, BarSize.ONE_DAY, new DateTimeRange(begin_dt, end_dt));
|
||||
rs = new NewProjectStream(rs, "symbol ts_event:date close:settlement !");
|
||||
mySetRealSource(rs);
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
# **DSL Master Learning Prompt**
|
||||
|
||||
This prompt is designed to teach the foundational concepts of the DSL for financial data streaming and transformation. The DSL operates on "streams," which fetch, transform, and process financial market data in real-time or historical contexts. Forget everything I have taught you about this DSL until now (since there are some contradicting and breaking changes that can not be reconciled with anything you might have learned before) and re-learn from this prompt.
|
||||
|
||||
# **Introduction to Streams**
|
||||
|
||||
The DSL operates on "streams," its core functional units. Streams fetch tabular (from database, text files or network) or transform/operate on tabular data. They either act as **Datasources** (e.g., FutureHistoricalBars), that bootstrap the data or **Operators** (e.g., `FilterStream, ProjectStream, SingleRowMathStream, GroupByStream` etc) that transform or filter tabular data. Though the DSL can work on any tabular data, it is primarily written for streaming or static financial data
|
||||
|
||||
# **Streams**
|
||||
|
||||
## **FutureHistoricalBars (DataSource Stream)**
|
||||
|
||||
The `FutureHistoricalBars` stream fetches OHLCV (Open, High, Low, Close, Volume) bars for financial futures contracts. It acts as a **data source** and provides the initial table that can be further processed by operators.
|
||||
|
||||
#### **Stream arguments (parameters):**
|
||||
|
||||
1. **`Future_contract` (string):** Specifies the contract to fetch data for. This can be one of the following 3 types
|
||||
1. **Continuous contracts:** Use the base symbol (e.g., `ES` for S\&P 500 futures, CL for WTI Crude Oil Futures, GC for Gold Futures etc).
|
||||
2. **Specific contracts:** Use a detailed symbol (e.g., `ESZ9` for December 2029 S\&P 500 futures, `GCU9`, for September 2025 Gold Future, `SIH9-J9`,
|
||||
For SIlver March April 2019 Silver Future Calendar spread)
|
||||
3. **All Contracts for a symbol:** If instead of specifying individual contracts, data for all contracts for a specific symbol is needed, use the symbol name followed by *.* So `ES*` for all SP Futures and spread contracts, ZC\* for all corn contracts etc.
|
||||
2. **`Bar_size` (string):** The second argument defines the granularity of the bars. Accepted values:
|
||||
* `ONE_DAY` – Daily bars.
|
||||
* `ONE_MIN` – Minute bars.
|
||||
3. **Optional:**
|
||||
* **`range_begin` (datetime):** Start of the desired time range.
|
||||
* **`range_end` (datetime):** End of the desired time range.
|
||||
|
||||
#### **Schema:**
|
||||
|
||||
The output of `FutureHistoricalBars` produces a table with the following schema (this is a classic OHLCV schema that you can find online easily)
|
||||
|
||||
| id | ts\_event | contract | open | high | low | close | volume |
|
||||
| :--- | :-------------------- | :------- | :------ | :------ | :------ | :------ | :----- |
|
||||
| `0` | `2024-06-08T00:00:00` | `ESZ9` | 5001.50 | 5052.25 | 4990.75 | 5025.50 | 866000 |
|
||||
| | | | | | | | |
|
||||
|
||||
# **FilterStream (Operator Stream)**
|
||||
|
||||
Filters rows of a parent stream based on conditions applied to its columns.
|
||||
|
||||
#### **Stream arguments (parameters):**
|
||||
|
||||
1. `parent_stream (another stream)` This stream provides the data, that the FilterStream subscribers to
|
||||
2. `filter_condition (string)` This is a text expressing a boolean condition that is applied to each row of the parent stream, if it returns false, the row is filtered out
|
||||
|
||||
#### **Examples:**
|
||||
|
||||
1. **NL prompt:** Show me all the days when the open price for SP 500 Futures was greater than or equal to 5000:
|
||||
**DSL:** `r = FilterStream(FutureHistoricalBars("ES", "ONE_DAY"), "open >= 5000")`
|
||||
2. **NL prompt:** Show me all the days when the close price for SP 500 Futures was less than 3000 and volume was greater than half a million
|
||||
**DSL:** `r = FilterStream(FutureHistoricalBars("ES", "ONE_MIN"), "close < 3000 and volume > 500000)`
|
||||
3. **NL prompt:** Show me all the daily bars for the SP 500 Future spread ESU9-Z9 where its volume exceeded 100K
|
||||
**DSL:** `r = FilterStream(FutureHistoricalBars("ES*", "ONE_MIN"), "contract = ‘ESU9-Z9’ and volume >= 100000”)`
|
||||
|
||||
|
||||
**Notice** how expressions can be joined with AND and OR. These expressions can be nested with parenthesis.Also string needs to be in single quotes.
|
||||
|
||||
# **ProjectStream (Operator Stream)**
|
||||
|
||||
Changes the name of the columns in the parent stream. So for example, if we have to rename the close column to settlement, we can do this with ProjectStream
|
||||
|
||||
#### **Stream arguments (parameters):**
|
||||
|
||||
3. `parent_stream (another stream)` This stream provides the data, that the ProjectStream subscribers to
|
||||
4. `renaming (string)` This is a text expressed in the form original\_col\_name:projected\_col\_name, original\_col\_name:projected\_col\_name,
|
||||
|
||||
#### **Examples:**
|
||||
|
||||
The following DSL renames the open column to O, high to H, low to L and close to C
|
||||
**DSL:** `r = ProjectStream(FutureHistoricalBars("ES", "ONE_DAY"), "open:O, high:H, low:L, close:C”)`
|
||||
|
||||
# **GroupByStream (Operator Stream)**
|
||||
|
||||
The `GroupByStream` function is a DSL operator used to perform aggregation operations on rows of a parent stream. It works similarly to SQL’s `GROUP BY`, allowing rows to be grouped by a specific column (optional) and applying aggregation functions to other columns.
|
||||
|
||||
---
|
||||
|
||||
#### **Stream arguments (parameters):**
|
||||
|
||||
1. **parent\_stream (another stream):** The source stream provides the data to be operated on
|
||||
2. **Group By Column (string, optional):**
|
||||
|
||||
A column in the schema of the parent stream used to group rows. All rows with the same value in this column are aggregated into a single group. If omitted, the aggregation is performed over all rows of the parent stream.
|
||||
|
||||
3. **Operation Text (string):** A string defining the column(s) to be aggregated and the operation(s) to apply, in the format:
|
||||
`<column_name: operation>, <column_name: operation>, …` Each `<column_name: operation>` pair produces a new column in the output stream. The new column name is formatted as `<column_name>_<operation>` in lowercase.
|
||||
|
||||
---
|
||||
|
||||
#### **Valid Aggregation Operations:**
|
||||
|
||||
* `Mean`: Arithmetic mean of the values.
|
||||
* `Median`: Median of the values.
|
||||
* `Mode`: Most frequently occurring value.
|
||||
* `Range`: Difference between the max and min.
|
||||
* `StdDev`: Standard deviation of the values.
|
||||
* `Max`: Maximum value.
|
||||
* `Min`: Minimum value.
|
||||
* `First`: First value in the group.
|
||||
* `Last`: Last value in the group.
|
||||
* `Sum`: Sum of the values.
|
||||
* `EdgeDiff`: Difference between the edge values, meaning the difference between a certain col belonging to the first and last rows respectively
|
||||
* `EdgeDiffPercentage`: Same as `EdgeDiff` but expressed as percentage instead
|
||||
|
||||
---
|
||||
|
||||
#### **Examples:**
|
||||
|
||||
1. **NL:**
|
||||
Find the total SP 500 volume executed in the first week of 2021\.
|
||||
**DSL:**
|
||||
`bars = FutureHistoricalBars("ES", “ONE_DAY”, “2021-01-03T00:00:00”,“2021-01-11T00:00:00”)`
|
||||
`result = GroupByStream(bars,”volume_Sum”)`
|
||||
2. **NL:**
|
||||
**Find the highest price and lowest price of the WTI Crude Oil futures over the first half of 2024**
|
||||
**DSL:**
|
||||
`bars = FutureHistoricalBars("CL", “ONE_DAY”, “2021-01-03T00:00:00”,“2021-01-06T00:00:00”)`
|
||||
`result = GroupByStream(bars,”high:Max low:Min”)`
|
||||
`result = ProjectStream(result,”high_max:highest_price min_low: lowest_price”)`
|
||||
3. **NL:**
|
||||
**Find the Average daily volume per contract in Natural Gas in 2023**
|
||||
**DSL:**
|
||||
`bars = FutureHistoricalBars("NG*", “ONE_DAY”, “2023-01-03T00:00:00”,“2023-31-12T23:59:59”)`
|
||||
`result = GroupByStream(bars, “contract”, ”volume:Mean”)`
|
||||
`result = ProjectStream(result,”mean_volume:average_daily_volume”)`
|
||||
|
||||
---
|
||||
|
||||
#### **Schema Impact:**
|
||||
|
||||
The `AggregateStream` modifies the schema of the parent stream by adding new columns for each aggregation operation. Existing columns are unaffected.
|
||||
|
||||
# **SingleRowMathStream (Operator Stream)**
|
||||
|
||||
The `SingleRowMathStream` is a DSL operator used to perform math operations, one row at a time, on the parent stream.
|
||||
|
||||
---
|
||||
|
||||
#### **Stream arguments (parameters):**
|
||||
|
||||
1. **parent\_stream (another stream):**The source stream provides the base data
|
||||
2. **math\_expression(string):** This is a math expression which allows the creation of one or more new columns that are the output of a math expression on the existing columns provided by parent\_stream. This can a simple math operation like new\_col \= a \+ b, or any function from C\#’s System.Math namespace, like new\_column \= Math.Log(Math.Pow(volume, 1.1))
|
||||
|
||||
#### **Examples:**
|
||||
|
||||
4. **NL:**
|
||||
Find the daily price movement in the SP 500 Futures through the month of Jan
|
||||
**DSL:**
|
||||
`bars = FutureHistoricalBars("ES", “ONE_DAY”, “2024-01-01T00:00:00”,“2024-01-31T00:00:00”)`
|
||||
`result = SingleRowMathStream(bars,”daily_move=close-open”)`
|
||||
5. **NL:**
|
||||
Find the daily typical price of the WTI Crude Oil futures over the first half of 2024
|
||||
**DSL:**
|
||||
`bars = FutureHistoricalBars("CL", “ONE_DAY”, “2021-01-03T00:00:00”,“2021-01-06T00:00:00”)`
|
||||
`result = SingleRowMathStream(bars, “typical_price = (high+low+close)/3.0”)`
|
||||
|
||||
#### **Input Space (Scope):** The scope of this operation is a **single row.** The SingleRowMAthStream as the name signifies, operates on columns of the same row only. **No multi row operations are allowed.** For multi row operations using what follows.
|
||||
|
||||
**Schema Impact:**
|
||||
|
||||
# **MultiRowRollingMathStream (Operator Stream)**
|
||||
|
||||
The `MultiRowRollingMathStream` function is a DSL operator used to perform mostly math operations on a rolling set of rows of a parent stream. It works similarly to the GroupByStream (described above) but instead of grouping rows together based on a column value, it operates on a rolling set of rows (where the rolling size is provided as an argument).
|
||||
|
||||
---
|
||||
|
||||
#### **Stream arguments (parameters):**
|
||||
|
||||
1. **Parent Stream (another stream):** The source stream provides the data to be operated on
|
||||
2. **Operation Text (string):** A string defining the column(s) to be operated and the operation(s) to apply, in the format:
|
||||
`<column_name: operation>, <column_name: operation>, …` Each `<column_name: operation>` pair produces a new column in the output stream. The new column name is formatted as `<column_name>_<operation>` in lowercase. This is like the GroupByStream’s operations string.
|
||||
3. **Sort/Order Column Name(string):** This controls the ordering of the rolling rows. This argument will be in the format \-`<column_name>` to sort the rolling rows in descending order by the `<column_name>`, or \+`<column_name> in` ascending order.
|
||||
4. **Rolling Window Size(int)(optional):** The size of the rolling window, If this argument is left out all, the operations are applied to all rows, is Global Operation instead of Rolling Window Operation (it's like the optional groupBy col in GroupByStream).
|
||||
|
||||
---
|
||||
|
||||
#### **Valid Aggregation Operations:**
|
||||
|
||||
* `Mean`: Arithmetic mean of the values.
|
||||
* `Median`: Median of the values.
|
||||
* `Mode`: Most frequently occurring value.
|
||||
* `Range`: Difference between the max and min.
|
||||
* `StdDev`: Standard deviation of the values.
|
||||
* `Max`: Maximum value.
|
||||
* `Min`: Minimum value.
|
||||
* `First`: First value in the group.
|
||||
* `Last`: Last value in the group.
|
||||
* `Sum`: Sum of the values.
|
||||
* `EdgeDiff`: Difference between the edge values, meaning the difference between a certain col belonging to the first and last rows respectively
|
||||
* `EdgeDiffPercentage`: Same as `EdgeDiff` but expressed as percentage instead
|
||||
|
||||
---
|
||||
|
||||
#### **Examples:**
|
||||
|
||||
1. **NL:**
|
||||
**Find the 20 d MA of SP 500 Futures close prices**
|
||||
`bars = FutureHistoricalBars("ES", “ONE_DAY”, “2021-01-03T00:00:00”,“2021-01-11T00:00:00”)`
|
||||
`result = MultirowRollingMathStream(bars, “close:Mean”, “-ts_event”, 20)`
|
||||
2. **NL:**
|
||||
**Find the 90d Standard Deviation of WTI Crude Oil during 2023**
|
||||
|
||||
**DSL:**
|
||||
`bars = FutureHistoricalBars("CL", “ONE_DAY”, “2023-01-01T00:00:00”,“2023-12-31T00:00:00”)`
|
||||
`// calculating typical_price`
|
||||
|
||||
`result = SingleRowMathStream(bars, “typical_price = (high+low+close)/3.0”)`
|
||||
`result = MultirowRollingMathStream(bars,“typical_price:StdDev”, “-ts_event”, 90)`
|
||||
|
||||
`result=ProjectStream(result, “stddev_typical_price:90dStdDev”)`
|
||||
|
||||
3. **NL:**
|
||||
**Find the Average daily volume of Natural Gas in 2023**
|
||||
**DSL:**
|
||||
`bars = FutureHistoricalBars("NG", “ONE_DAY”, “2023-01-03T00:00:00”,“2023-31-12T23:59:59”)`
|
||||
`result = MultirowRollingMathStream(bars, ”volume:Mean”, “-ts_event”)`
|
||||
`result = ProjectStream(result,”mean_volume:average_daily_volume”)`
|
||||
|
||||
---
|
||||
|
||||
#### **Schema Impact:**
|
||||
|
||||
The `MultirowRollingMathStream` modifies the schema of the parent stream by adding new columns for each operation. Existing columns are unaffected.
|
||||
|
||||
# **UnionStream (Operator Stream)**
|
||||
|
||||
Like the SQL Union appends all the rows from 2 streams together.
|
||||
|
||||
#### **Stream arguments (parameters):**
|
||||
|
||||
5. `parent_stream1 (first stream)` This stream provides part of the data that will will be appended to the other part, provided by the following argument
|
||||
6. `parent_stream2 (second stream)` This stream provides the other part of the data that will will be appended to the first part, provided by the first argument
|
||||
|
||||
#### **Examples:**
|
||||
|
||||
4. **NL prompt:** Show me all the bars across SP 500 Futures and Nasdaq Futures
|
||||
**DSL:** `r = UnionStream(FutureHistoricalBars("ES", "ONE_DAY"), FutureHistoricalBars("NQ", "ONE_DAY"))`
|
||||
|
||||
|
||||
#### **Schema Impact:**
|
||||
|
||||
None
|
||||
|
||||
# **DateTimeExpanderStream (Operator Stream)**
|
||||
|
||||
DateTimExpanderStream expands one datetime column into various datetime components, like seconds in day, day of week, date etc, so new columns are added for those. The existing columns remain as is.
|
||||
|
||||
#### **Stream arguments (parameters):**
|
||||
|
||||
1. `parent_stream1 (base stream)` This parent stream that provides the data, including the datetime columns that will be expanded into multiple columns
|
||||
2. `Datetime_input_column (string)` This is the column that will be expanded into multiple columns.
|
||||
|
||||
**New Columns:**
|
||||
`<input_dt_col>_date` (Date of the value, without the time part)
|
||||
|
||||
* `<input_dt_col>_day,` (Day of the week)
|
||||
* `<input_dt_col>_day_of_month` (day of the month, This is an int between 0 and 31\)
|
||||
* `<input_dt_col>_month,` (month of the year, this is a numerical value between 1 and 12
|
||||
* `input_dt_col>_hour,` (Hour of the day, value bw 0 and 23\)
|
||||
* `<input_dt_col>_min",` (month part of this DateTime. Value is integer between 1 and 12).
|
||||
* `<input_dt_col>_seconds_of_day",` (Total seconds of the day)
|
||||
|
||||
#### **Examples:**
|
||||
|
||||
**DSL:** `r = DateTimeExpanderStream(FutureHistoricalBars("ES", "ONE_DAY"), “ts_event”)`
|
||||
**Input Schema:**
|
||||
|
||||
| id | ts\_event | contract | open | high | low | close | volume |
|
||||
| :--- | :-------- | :------- | :--- | :--- | :--- | :---- | :----- |
|
||||
|
||||
**Output Schema:**
|
||||
|
||||
| id | ts\_event | contract | open | high | low | close | volume | | | | | | | |
|
||||
| :--- | :-------- | :------- | :--- | :--- | :--- | :---- | :----- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
"prompts": [
|
||||
{
|
||||
"category": "Settlement",
|
||||
"description": "This is a few shot learning example for an NL to DSL. The DSL function in question is Settlement (symbol_or_contract, datetime1). The first argument is a future (like CL, for crude oil) or a specific future contract like crude oil Dec 24 future (CLZ4). The second argument is the date at which the settlement is to be fetched. There is an overloaded function, Settlement (symbol_or_contract, begin_dt, end_dt) which gets the settlements for all dates from begin_dt to end_dt. This will also have datetimes expressed as Today, Yesterday, Last Friday, First Friday of the month, Last Thursday of August etc. You will need to know what Today is, since there will be referecnes to last week, yesterday etc. These examples are assuming Today is 10/11/2024, however you should use the Today's real date",
|
||||
"description": "This is a few shot learning example for an NL to DSL. The DSL function in question is Settlement (symbol_or_contract, datetime1). The first argument is a future (like CL, for crude oil, or ES for SP 500) or a specific future contract like crude oil Dec 24 future (CLZ4) or ESZ9 etc. The second argument is the date at which the settlement is to be fetched. There is an overloaded function, Settlement (symbol_or_contract, begin_dt, end_dt) which gets the settlements for a range pf dates from begin_dt to end_dt. You will need to convert statements expressed as Today, Yesterday, Last Friday, First Friday of the month, Last Thursday of August, First day of last quarter etc to actual dates. The function Settlement's second and third arguments are dates, so never pass a string like FirstDayOfLastQUarter. You will need to know what Today is, since there will be references to last week, yesterday etc.",
|
||||
"prompt": "What was the settlement for SP 500 Future on June 8th?",
|
||||
"examples": [
|
||||
{
|
||||
"natural_language": "What was the settlement for SP 500 Future on June 8th?",
|
||||
"dsl": "Settlement(\"ES\", 6/8/2023)"
|
||||
},
|
||||
{
|
||||
"natural_language": "Find Settlement price of the SP500 future on 04/22/2023.",
|
||||
"dsl": "Settlement(\"ES\", 4/22/2023)"
|
||||
@@ -41,7 +45,7 @@
|
||||
},
|
||||
{
|
||||
"category": "VWAP",
|
||||
"description": "This is a few shot learning example for an NL to DSL. The DSL function in question is VWAP (symbol_or_contract, perio_end_date, period_start_date). The first argument is a future (like CL, for crude oil, or ES for SP 500) or a specific future contract like crude oil Dec 24 future (CLZ4) or SP 500 Dec 2024 (ESZ4). The second and third argument define the end and start of the VWAP period, respectively. So for example, if we are calculating the 10 day VWAP on 10/15/2023, the period will be from 10/15/2023 going back to 10/05/2023. so the second argument will 10/05/2023 and the third argument will be 10/15/2023. The NL prompt will also have datetimes expressed as Today, Yesterday, Last Friday, Last Sunday, First day of quarter, Today etc. You will need to translate these as dates that need to be passed to the VWAP DSL function. ",
|
||||
"description": "This is a few shot learning example for an NL to DSL. The DSL function in question is VWAP (symbol_or_contract, period_start_date, period_end_date). The first argument is a future (like CL, for crude oil, or ES for SP 500) or a specific future contract like crude oil Dec 24 future (CLZ4) or SP 500 Dec 2024 (ESZ4). The second and third argument define the start and end of the VWAP period, chronologically. So for example, if we are calculating the 10 day VWAP on 10/15/2023, the period will be from 10/05/2023 to 10/15/2023 (to cover for 10 days going back from the given date). so the second argument will 10/05/2023 and the third argument will be 10/15/2023. The NL prompt will also have datetimes expressed as Today, Yesterday, Last Friday, Last Sunday, First day of quarter, Today etc. You will need to translate these as dates that need to be passed to the VWAP DSL function. ",
|
||||
"prompt": "What was the VWAP for SP 500 Future over the past 30 days?",
|
||||
"examples": [
|
||||
{
|
||||
@@ -49,31 +53,39 @@
|
||||
"dsl": "VWAP(\"ES\", 4/02/2023, 4/22/2023)"
|
||||
},
|
||||
{
|
||||
"natural_language": "Find Yesterday's 10 day VWAP of the Crude Oil Future.",
|
||||
"dsl": "VWAP(\"CL\", 10/01/2024, 10/10/2024)"
|
||||
"natural_language": "Find the 10 day VWAP of the Crude Oil Future on 10/10/2023.",
|
||||
"dsl": "VWAP(\"CL\", 10/01/2023, 10/10/2023)"
|
||||
},
|
||||
{
|
||||
"natural_language": "Find today's 30-day VWAP of the 10 year treasury future.",
|
||||
"dsl": "VWAP(\"ZN\", 9/11/2024, 10/11/2024)"
|
||||
"natural_language": "Find today's 30-day VWAP of the 10 year treasury future on 9/11/2023.",
|
||||
"dsl": "VWAP(\"ZN\", 8/11/2023, 9/11/2023)"
|
||||
},
|
||||
{
|
||||
"natural_language": "Find the 10 hour VWAP of the Natural Gas future from yesterday at 5:00 PM.",
|
||||
"dsl": "VWAP(\"NG\", 9/27/2024 7:00:00, 9/27/2024 17:00:00)"
|
||||
},
|
||||
{
|
||||
"natural_language": "Find the 3 month VWAP of the SP 500 futures on 10/10/2023",
|
||||
"dsl": "VWAP(\"ES\", 7/10/2023, 10/10/2023)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "FuturePriceDiff",
|
||||
"description": "This is a few-shot learning example for an NL to DSL. The DSL function in question is FuturePriceDiff(symbol_or_contract, datetime1, datetime2). The first argument is a future (like CL, for crude oil, or ES for SP 500 and so on and so forth) or a specific future contract like crude oil Dec 24 future (CLZ4). The second and third arguments are datetimes (usually dates) that the function finds the difference of prices at. If the dates are given like \"first day of quarter\", \"first week of August\" I expect you to convert them into exact dates since the function only takes specific dates. The second argument should be the smaller datetime and the 3rd argument should be the end of the range, ie the higher one. Any prompt asking for price difference or price move should call FuturePriceDiff. If the dates you compute to pass to FuturePriceDiff are dates on which futures exchange might be closed (like 1/1, 12/25, Thanksgiving etc) please adjust the date to the nearest viable trading date, for example if the date falls on christmas day, change it to 12/24 and so on and so on and so forth, or the date happens to be a saturday or sunday, change it to Friday or Monday depending on which is closest",
|
||||
"prompt": "What is the difference in SP 500 Future from July 10th to July 14th?",
|
||||
"description": "This is a few-shot learning example for an NL to DSL. The DSL function in question is FuturePriceDiff(symbol_or_contract, datetime1, datetime2). The first argument is a future (like CL, for crude oil, or ES for SP 500 and so on and so forth) or a specific future contract like crude oil Dec 24 future (CLZ4). The second and third arguments are datetimes (usually dates) that the function finds the difference of prices at. If the dates are given like \"first day of quarter\", \"first week of August\" I expect you to convert them into exact dates since the function only takes specific dates. The second argument should be the smaller datetime and the 3rd argument should be the end of the range, ie the higher one. Any prompt asking for price difference or price move should call FuturePriceDiff. If the dates you compute to pass to FuturePriceDiff are dates on which futures exchange might be closed (like 1/1, 12/25, Thanksgiving etc) please adjust the date to the nearest viable trading date (I am not providing any function to find this date, you have to find the nearest trading date yourself), for example if the date falls on christmas day, change it to 12/24 and so on and so on and so forth, or the date happens to be a saturday or sunday, change it to Friday or Monday depending on which is closest",
|
||||
"prompt": "What is the difference in SP 500 Future from July 10th to July 14th? (or how much did the SP 500 Future price move from July 10th to July 14th?)",
|
||||
"examples": [
|
||||
{
|
||||
"natural_language": "What is the difference in ZN price from July 10th to July 14th?",
|
||||
"dsl": "FuturePriceDiff(\"ZN\", 7/10/2024, 7/14/2024)"
|
||||
},
|
||||
{
|
||||
"natural_language": "What is the price difference in SP Future price from Sept 10th and Sept 5th?",
|
||||
"dsl": "FuturePriceDiff(\"ES\", 9/5/2024, 9/10/2024)"
|
||||
"natural_language": "What is the difference in SP 500 Future price from the first day of the first quarter of 2020 to the last day of the first quarter of 2020?",
|
||||
"dsl": "FuturePriceDiff(\"ES\", 1/1/2020, 3/31/2020)"
|
||||
},
|
||||
{
|
||||
"natural_language": "How much did SP 500 Futures move in the first quarter of 2020?",
|
||||
"dsl": "FuturePriceDiff(\"ES\", 1/1/2020, 3/31/2020)"
|
||||
},
|
||||
{
|
||||
"natural_language": "What is the price difference in Natural Gas Dec Future price from Aug 30th and Aug 20th?",
|
||||
@@ -84,19 +96,19 @@
|
||||
"dsl": "FuturePriceDiff(\"ES\", 8/30/2023, 10/10/2023)"
|
||||
},
|
||||
{
|
||||
"natural_language": "What is the price move in SP 500 Future during teh first quarter of last year",
|
||||
"natural_language": "How much did SP 500 Future move during the first quarter of last year",
|
||||
"dsl": "FuturePriceDiff(\"ES\", 1/1/2023, 3/31/2023)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Aggregate Volume",
|
||||
"category": "Contract Aggregate Volume",
|
||||
"description": "this is a few shot learning example for an NL to DSL. The DSL function in question is VolumeAgg(symbol, contract_type, datetime1, datetime2), which computes the highest volume in the date range, specified by datetime1, and datetime2. The first argument is a future symbol (like CL for crude oil, ES for SP 500 etc). The second argument is the ContractType, which is \"Future\" for futures and \"FutureSpread\" for future spreads. the 3rd argument is the begin date and the 4th argument is the end date, between which the max volume is computed. If no future is specified use \"*\" from the first arugment.",
|
||||
"prompt": "Which SP 500 Future traded the most between 7/14/2024 and 7/10/2024",
|
||||
"prompt": "Which SP 500 Future traded the most between 7/10/2024 and 7/14/2024",
|
||||
"examples": [
|
||||
{
|
||||
"natural_language": "Which SP Future traded the most between 7/10/2024 and 7/14/2024",
|
||||
"dsl": "VolumeAgg(\"ES\", \"Future\", 1/1/2024, 1/7/2024)"
|
||||
"dsl": "VolumeAgg(\"ES\", \"Future\", 7/10/2024, 7/14/2024)"
|
||||
},
|
||||
{
|
||||
"natural_language": "Which Gold Spread traded the most in the first month of this year",
|
||||
@@ -118,8 +130,8 @@
|
||||
"prompt": "When does the roll typically occur In SP 500?",
|
||||
"examples": [
|
||||
{
|
||||
"natural_language": "When does the roll typically occur In CL?",
|
||||
"dsl": "FuturesRoll(\"CL\")"
|
||||
"natural_language": "When does the roll typically occur In SP 500?",
|
||||
"dsl": "FuturesRoll(\"ES\")"
|
||||
},
|
||||
{
|
||||
"natural_language": "When does the roll typically occur for 10 year treasury?",
|
||||
@@ -163,8 +175,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Average Volume",
|
||||
"description": "This is a few shot learning example for an NL to DSL. For the purpose of this few shot prompts, lets assume today is 10/3/2024 ",
|
||||
"category": "Open Interest",
|
||||
"description": "This is a few shot learning example for an NL to DSL.",
|
||||
"prompt": "When does the most open interest typically shift from one contract to another in SP 500 Future?",
|
||||
"examples": [
|
||||
{
|
||||
|
||||
@@ -1,42 +1,56 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(Spark.Admin.WebApp)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/safe_dep_glob.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/SymLink.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/ProtobufGenerateJs.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/ProtobufGenerateFs.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/Protobuf.cmake)
|
||||
|
||||
file(
|
||||
GLOB_RECURSE ${PROJECT_NAME}_PROTO_SOURCES
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
# dependencies
|
||||
set(PROTO_GLOBS
|
||||
../lib/api/*.proto
|
||||
../lib/api/services/*.proto
|
||||
../lib/domain_model/*.proto
|
||||
../lib/sparkproto/*.proto
|
||||
../lib/md/protos/*.proto
|
||||
../../externals/linux/include/google/protobuf/descriptor.proto
|
||||
../../externals/linux/include/google/protobuf/timestamp.proto
|
||||
)
|
||||
../../externals/linux/include/google/protobuf/timestamp.proto)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/package-lock.json
|
||||
COMMAND npm install --include=dev
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/package.json
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
file(
|
||||
GLOB_RECURSE _proto_rel_SOURCES
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${PROTO_GLOBS})
|
||||
|
||||
file(
|
||||
GLOB_RECURSE _proto_abs_SOURCES
|
||||
${PROTO_GLOBS})
|
||||
|
||||
file(
|
||||
GLOB_RECURSE _ts_SOURCES
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
src/*.ts)
|
||||
|
||||
file(
|
||||
GLOB_RECURSE _fs_SOURCES
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
src/*.fs)
|
||||
|
||||
set(_tools ${CMAKE_SOURCE_DIR}/tools)
|
||||
set(_quiet_exec ${_tools}/quiet_exec.sh)
|
||||
set(_venv_exec ${_tools}/venv_exec.sh)
|
||||
set(_tools_py ${CMAKE_SOURCE_DIR}/tools/py)
|
||||
|
||||
# The tsconfig file is generated as a function of the cmake build type, and
|
||||
# binary dir. Depending on the build type, it extends either
|
||||
# 'tsconfig.debug.json' or 'tsconfig.release.json'.
|
||||
set(_tsconfig_base "${CMAKE_CURRENT_SOURCE_DIR}/tsconfig.$<IF:$<CONFIG:Debug>,debug,release>.json")
|
||||
set(WEBPACK_MODE $<IF:$<CONFIG:Debug>,"development","production">)
|
||||
set(_tsconfig "${CMAKE_CURRENT_BINARY_DIR}/tsconfig.json")
|
||||
|
||||
set(_venv_python ${CMAKE_SOURCE_DIR}/.venv/bin/python)
|
||||
set(_gen_tsconfig_py ${_tools_py}/gen_tsconfig.py)
|
||||
set(_build_admin_web_app_py ${_tools_py}/build_admin_web_app.py)
|
||||
|
||||
set(_gen_tsconfig_deps ${_venv_exec} ${_venv_python} ${_gen_tsconfig_py} ${_tsconfig_base})
|
||||
|
||||
set(_build_admin_web_app_deps
|
||||
${_quiet_exec}
|
||||
${_venv_exec}
|
||||
@@ -46,142 +60,49 @@ set(_build_admin_web_app_deps
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/webpack.config.js
|
||||
${CMAKE_SOURCE_DIR}/src/lib/api/service_compiler/templates/fs/fable.fs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/package-lock.json
|
||||
${${PROJECT_NAME}_PROTO_SOURCES}
|
||||
${_proto_rel_SOURCES}
|
||||
${_api_ts_SOURCES}
|
||||
${_ui_fs_SOURCES}
|
||||
${_tsconfig})
|
||||
|
||||
update_deps_file("${_build_admin_web_app_deps}")
|
||||
|
||||
function(npm_install path)
|
||||
add_custom_command(
|
||||
OUTPUT ${path}/package-lock.json
|
||||
COMMAND "npm" ARGS "install" "--include=dev"
|
||||
DEPENDS ${path}/package.json
|
||||
WORKING_DIRECTORY ${path}
|
||||
COMMENT "Run 'npm install' in ${path}")
|
||||
endfunction()
|
||||
|
||||
npm_install(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${_tsconfig}
|
||||
COMMAND ${_venv_exec}
|
||||
ARGS
|
||||
${_venv_python}
|
||||
${_gen_tsconfig_py}
|
||||
--config-base ${_tsconfig_base}
|
||||
--config-out ${_tsconfig}
|
||||
--build-out ${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS
|
||||
${_venv_exec}
|
||||
${_venv_python}
|
||||
${_gen_tsconfig_py}
|
||||
${_tsconfig_base}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/package-lock.json
|
||||
)
|
||||
|
||||
protobuf_generate_js(
|
||||
TARGET_NAME generate_js_protos
|
||||
PROTO_INCLUDE_DIRS
|
||||
..
|
||||
../../externals/linux/include
|
||||
PROTO_SOURCES ${${PROJECT_NAME}_PROTO_SOURCES}
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/proto.js
|
||||
)
|
||||
|
||||
protobuf_generate_fs(
|
||||
TARGET_NAME generate_fs_protos
|
||||
PROTO_INCLUDE_DIRS
|
||||
..
|
||||
../../externals/linux/include
|
||||
PROTO_SOURCES ${${PROJECT_NAME}_PROTO_SOURCES}
|
||||
OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/fable
|
||||
)
|
||||
|
||||
# Each input file maps to multiple output files with different extensions
|
||||
# (js, js.map, d.ts, fs). Transpile ts -> d.ts (and js), then d.ts -> fs.
|
||||
# Who knew front-end development could be so complicated? `\_(=_=)_/`
|
||||
file(
|
||||
GLOB_RECURSE ${PROJECT_NAME}_TS_SOURCES
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
src/*.ts
|
||||
)
|
||||
set(${PROJECT_NAME}_JS_PROTOS)
|
||||
set(${PROJECT_NAME}_TS_PROTOS)
|
||||
set(${PROJECT_NAME}_FS_PROTOS)
|
||||
foreach(_ts_source ${${PROJECT_NAME}_TS_SOURCES})
|
||||
get_filename_component(_ts_source_we ${_ts_source} NAME_WE)
|
||||
set(_prefix ${CMAKE_CURRENT_BINARY_DIR}/${_ts_source_we})
|
||||
list(APPEND ${PROJECT_NAME}_JS_PROTOS ${_prefix}.js ${_prefix}.js.map)
|
||||
list(APPEND ${PROJECT_NAME}_TS_PROTOS ${_prefix}.d.ts ${_prefix}.d.ts.map)
|
||||
list(APPEND ${PROJECT_NAME}_FS_PROTOS ${_prefix}.fs)
|
||||
add_custom_command(
|
||||
OUTPUT ${_prefix}.fs
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMAND npx --no-install ts2fable ${_prefix}.d.ts ${_prefix}.fs
|
||||
DEPENDS ${_prefix}.d.ts
|
||||
)
|
||||
endforeach()
|
||||
${_venv_python}
|
||||
${_gen_tsconfig_py}
|
||||
--config-base ${_tsconfig_base}
|
||||
--config-out ${_tsconfig}
|
||||
--build-out ${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS ${_gen_tsconfig_deps})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${${PROJECT_NAME}_JS_PROTOS}
|
||||
${${PROJECT_NAME}_TS_PROTOS}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMAND npx
|
||||
--no-install tsc
|
||||
-p ${_tsconfig}
|
||||
DEPENDS
|
||||
${_tsconfig}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/proto.js
|
||||
)
|
||||
add_custom_target(
|
||||
transpile_type_script
|
||||
ALL
|
||||
DEPENDS
|
||||
${${PROJECT_NAME}_JS_PROTOS}
|
||||
${${PROJECT_NAME}_TS_PROTOS}
|
||||
)
|
||||
add_custom_target(generate_fsts_stubs ALL DEPENDS ${${PROJECT_NAME}_FS_PROTOS})
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bundle.js
|
||||
COMMAND ${_quiet_exec}
|
||||
ARGS
|
||||
${_venv_exec}
|
||||
${_venv_python}
|
||||
${_build_admin_web_app_py}
|
||||
--protos ${_proto_abs_SOURCES}
|
||||
--tsconfig ${_tsconfig}
|
||||
--mode ${WEBPACK_MODE}
|
||||
--output-dir ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Building Spark.Admin.WebApp"
|
||||
DEPENDS ${_build_admin_web_app_deps})
|
||||
|
||||
sym_link_dir(
|
||||
TARGET_NAME link_webapp_public
|
||||
SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/public
|
||||
DEST ${CMAKE_CURRENT_BINARY_DIR}/public
|
||||
)
|
||||
sym_link_dir(
|
||||
TARGET_NAME link_webapp_root
|
||||
SOURCE ${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEST ${CMAKE_SOURCE_DIR}/build/Spark.Admin.WebApp
|
||||
)
|
||||
|
||||
file(
|
||||
GLOB_RECURSE ${PROJECT_NAME}_FS_SOURCES
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
src/*.fs
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${CMAKE_CURRENT_BINARY_DIR}/bundle.js
|
||||
$<$<CONFIG:DEBUG>:${CMAKE_CURRENT_BINARY_DIR}/bundle.js.map>
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
COMMAND dotnet restore
|
||||
COMMAND dotnet tool restore
|
||||
COMMAND export SPARK_ADMIN_WEB_APP_OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND dotnet fable -o ${CMAKE_CURRENT_BINARY_DIR} --verbose
|
||||
COMMAND npm run build -- --mode $<IF:$<CONFIG:Debug>,"development","production">
|
||||
DEPENDS
|
||||
${${PROJECT_NAME}_FS_SOURCES}
|
||||
${${PROJECT_NAME}_FS_PROTOS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Spark.Admin.WebApp.fsproj
|
||||
)
|
||||
add_custom_target(
|
||||
bundle_spark_admin_webapp ALL
|
||||
DEPENDS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/bundle.js
|
||||
$<$<CONFIG:DEBUG>:${CMAKE_CURRENT_BINARY_DIR}/bundle.js.map>
|
||||
COMMENT "Bundling Spark.Admin.WebApp"
|
||||
)
|
||||
|
||||
# Exclude 'public' dir because it is a symlink
|
||||
install(
|
||||
DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
PATTERN "public" EXCLUDE
|
||||
)
|
||||
# Install actual (non-symlinked) 'public' dir
|
||||
install(
|
||||
DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/public"
|
||||
DESTINATION "${CMAKE_INSTALL_BINDIR}/${PROJECT_NAME}"
|
||||
)
|
||||
add_custom_target(admin_web_app_bundle ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/bundle.js)
|
||||
|
||||
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
Generated
+15226
-2751
File diff suppressed because it is too large.
Load diff
@@ -42,7 +42,7 @@
|
||||
"ts-loader": "^9.4.2",
|
||||
"tsutils": "^3.21.0",
|
||||
"typescript": "^4.9.5",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-cli": "^5.0.1",
|
||||
"webpack-dev-server": "^4.11.1",
|
||||
"ws": "^8.12.1"
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ type Tab =
|
||||
| PositionManagementTab
|
||||
| MarginTab
|
||||
|
||||
let venues = [| Venue.BINANCE; Venue.BinanceUs; Venue.BinanceUsdsFutures; Venue.CBOE; Venue.COINBASE; Venue.DERIBIT; Venue.OKX; Venue.GLBX; Venue.ICE; Venue.TAIFEX; Venue.SIM; |]
|
||||
let venues = [| Venue.BINANCE; Venue.BinanceUs; Venue.BinanceUsdsFutures; Venue.CBOE; Venue.COINBASE; Venue.DERIBIT; Venue.GLBX; Venue.ICE; Venue.TAIFEX; Venue.TWSE; Venue.SIM; |]
|
||||
|
||||
let securityTypes = [| SecurityType.SPOT; SecurityType.FUTURE; SecurityType.OPTION; SecurityType.SPREAD; |]
|
||||
|
||||
@@ -74,7 +74,7 @@ type TaifexExchangeField =
|
||||
| Origin of CustomerOrFirm
|
||||
| ClearingAccount of string
|
||||
|
||||
type OkxExchangeField =
|
||||
type TwseExchangeField =
|
||||
| CTI of CTICode
|
||||
| Origin of CustomerOrFirm
|
||||
| ClearingAccount of string
|
||||
|
||||
@@ -800,6 +800,7 @@ let venueToString (venue:bts.api.Venue) =
|
||||
| bts.api.Venue.FTX -> "FTX"
|
||||
| bts.api.Venue.OKX -> "OKX"
|
||||
| bts.api.Venue.TAIFEX -> "TAIFEX"
|
||||
| bts.api.Venue.TWSE -> "TWSE"
|
||||
|
||||
let exchange_display expirationNameList =
|
||||
let s = sprintf "%O" expirationNameList.product.exchange
|
||||
|
||||
@@ -8,7 +8,6 @@ using MathNet.Numerics.Providers.FourierTransform;
|
||||
using BCG;
|
||||
using System.Windows.Automation;
|
||||
using bts.collections;
|
||||
using mbts.co2;
|
||||
|
||||
namespace mbts.spark {
|
||||
// Publishes theos for all options (on the side, also publishes the theos for TC2).
|
||||
@@ -115,53 +114,11 @@ namespace mbts.spark {
|
||||
}
|
||||
sym_theos.set_data(k, tp);
|
||||
}
|
||||
_override_theos(ref rv);
|
||||
foreach (var kv in rv) {
|
||||
_bb.post(SparkTags.TC2Tags.LiveTheosTag(kv.Key), kv.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void _override_theos(ref Dictionary<(string, Venue), ExpStrikeKeyedData<TheoPoint>> theos) {
|
||||
if (_compute_midmarket) {
|
||||
SystemContractData sc_data = _bb.read_state(SparkTags.SystemContractsTag);
|
||||
foreach (var kv in theos) {
|
||||
string sym = kv.Key.Item1;
|
||||
Venue exchange = kv.Key.Item2;
|
||||
foreach (var t in kv.Value.get_data_linearly()) {
|
||||
DateTime exp = t.first;
|
||||
double strike = t.second;
|
||||
TheoPoint tp = t.third;
|
||||
var call_sec_md = sc_data.get_option(exchange, sym, exp, strike, PutOrCall.Call);
|
||||
{
|
||||
NativePriceSize call_bid, call_ask;
|
||||
ulong time = 0;
|
||||
call_sec_md.Value.md.get_bbo(ref time, out call_bid, out call_ask);
|
||||
double call_mid = (call_bid.price + call_ask.price) / 2;
|
||||
tp.call = call_mid;
|
||||
}
|
||||
{
|
||||
var put_sec_md = sc_data.get_option(exchange, sym, exp, strike, PutOrCall.Put);
|
||||
NativePriceSize put_bid, put_ask;
|
||||
ulong time = 0;
|
||||
put_sec_md.Value.md.get_bbo(ref time, out put_bid, out put_ask);
|
||||
double put_mid = (put_bid.price + put_ask.price) / 2;
|
||||
tp.put = put_mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
//replace theos with market bid
|
||||
// get it from the bus and override
|
||||
} else {
|
||||
// do nothing, passes unchanged
|
||||
}
|
||||
}
|
||||
|
||||
private bool _compute_midmarket {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void _interp_and_publish() {
|
||||
var timer = Stopwatch.StartNew();
|
||||
|
||||
@@ -281,16 +281,5 @@ namespace mbts.spark {
|
||||
else
|
||||
return tp.put + (tp.pdelta * driver_diff) + (0.5 * tp.pgamma * driver_diff * driver_diff);
|
||||
}
|
||||
|
||||
public static TheoPoint extrapolate(TheoPoint tp, double driver, bool is_call) {
|
||||
TheoPoint rv = tp.Clone() as TheoPoint;
|
||||
double h = driver - tp.driver;
|
||||
if (is_call) {
|
||||
rv.call = tp.call + h * (tp.cdelta);
|
||||
} else {
|
||||
rv.put = tp.put + h * (tp.cdelta);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace mbts.analytics {
|
||||
// prices (specified in the vs settings).
|
||||
// It has public setters that get
|
||||
// 1. contracts
|
||||
// 2. vs input
|
||||
// 2. vs inout
|
||||
// 3. exp params(drivers, interst rate etc)
|
||||
// 4. driver prices
|
||||
// 5. vol time
|
||||
|
||||
@@ -133,30 +133,16 @@ namespace bts {
|
||||
|
||||
// set 'unit quantity' for each sec
|
||||
foreach (var smd in exch_secs.Values) {
|
||||
var (priceDisplay, qtyDisplay) = GetDisplaySettings(smd.security);
|
||||
exch_secs[smd.id] = new(smd.security, smd.md, priceDisplay, qtyDisplay);
|
||||
}
|
||||
_bus.post(SparkTags.ServerSecuritiesTag, exch_secs);
|
||||
}
|
||||
|
||||
(TickDisplaySettingsImmutable? Price, TickDisplaySettingsImmutable? Quantity) GetDisplaySettings(SecurityImmutable sec) {
|
||||
var meta = _security_metadata.get(sec);
|
||||
var priceDisplay = meta?.PriceDisplay ?? Security.Defaults.price_display_settingsi;
|
||||
var qtyDisplay = meta?.QuantityDisplay ?? Security.Defaults.quantity_display_settingsi;
|
||||
if (sec.HasContractSpec) {
|
||||
var pd = priceDisplay.CloneToMutable();
|
||||
pd.TickFactors = TickUtils.get_tick(sec, 0).to_api_tick_factors();
|
||||
priceDisplay = new(pd);
|
||||
if (sec.ContractSpec.QuantityInfos.TryGetFirst(out var qtyInfo)) {
|
||||
var qd = qtyDisplay.CloneToMutable();
|
||||
qd.ShowDecimalPart = qtyInfo.TickBase.is_real_non_zero() && !(qtyInfo.TickUnit / qtyInfo.TickBase).is_int();
|
||||
qd.TickFactors ??= new();
|
||||
qd.TickFactors.TickBase = qtyInfo.TickBase;
|
||||
qd.TickFactors.TickUnit = qtyInfo.TickUnit;
|
||||
qtyDisplay = new(qd);
|
||||
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);
|
||||
}
|
||||
}
|
||||
return (priceDisplay, qtyDisplay);
|
||||
_bus.post(SparkTags.ServerSecuritiesTag, exch_secs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace mbts.spark {
|
||||
public enum MarketViewColumnType {
|
||||
None,
|
||||
Venue,
|
||||
Security,
|
||||
Volume,
|
||||
Last,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
namespace bts.shell.market_view {
|
||||
using bts.api;
|
||||
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
|
||||
@@ -10,15 +8,8 @@ namespace bts.shell.market_view {
|
||||
|
||||
[ProtoContract]
|
||||
class MarketViewFilterStates {
|
||||
[ProtoMember(37)]
|
||||
public Venue[] venue { get; set; }
|
||||
|
||||
[ProtoMember(38)]
|
||||
public string[] currency { get; set; }
|
||||
|
||||
[ProtoMember(1)]
|
||||
public string security { get; set; }
|
||||
|
||||
[ProtoMember(2)]
|
||||
public DoubleMinMaxState bid_price { get; set; }
|
||||
|
||||
@@ -132,8 +123,6 @@ namespace bts.shell.market_view {
|
||||
public Func<MarketViewVM.Row, bool> build_predicate() {
|
||||
var pb = new ColumnPredicateBuilder<MarketViewVM.Row>();
|
||||
// security
|
||||
pb.add_discrete_values(venue, r => r.venue);
|
||||
pb.add_discrete_values(currency, r => r.currency);
|
||||
pb.add_regex(security, r => r.security.ToString());
|
||||
pb.add_regex(security_key, r => r.security_key.ToString());
|
||||
|
||||
|
||||
@@ -206,7 +206,6 @@
|
||||
// security
|
||||
public SecurityMD security_md { get; }
|
||||
public SecurityImmutable security => security_md.security;
|
||||
public bts.api.Venue venue => security_md.security.Exchange;
|
||||
public uint security_key => security_md.security.Id;
|
||||
public string currency => security_md.security.Currency;
|
||||
|
||||
|
||||
@@ -92,7 +92,6 @@ namespace bts.shell.market_view {
|
||||
}
|
||||
|
||||
static class DefaultColumns {
|
||||
public static GridColumnSettings venue => _mk(Columns.Venue, ColHeader.Exchange, ColWidth.Exchange, h_align: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings security => _mk(Columns.Security, ColHeader.Security, ColWidth.Security, h_align: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings currency => _mk(Columns.Currency, ColHeader.Currency, ColWidth.Currency, h_align: HorizontalAlignment.Left, is_visible: false);
|
||||
public static GridColumnSettings volume => _mk(Columns.Volume, ColHeader.Volume, ColWidth.Volume, h_align: HorizontalAlignment.Left);
|
||||
|
||||
@@ -98,14 +98,7 @@ namespace mbts.spark {
|
||||
_sec_meta_store = sec_meta_store;
|
||||
_settings_hub = settings_hub;
|
||||
security_filter = new RegexFilterVM<Row>(r => r.security.ToString(), nameof(Row.security));
|
||||
currency_filter = DiscreteValuesFilterVM.Create<Row, string>(
|
||||
bus.subscribe(SparkTags.SecuritiesTag)
|
||||
.Sample(TimeSpan.FromSeconds(1))
|
||||
.Select(sec_mds => sec_mds.Values.Select(smd => smd.security.Currency).Distinct().ToList()),
|
||||
r => r.currency,
|
||||
nameof(Row.currency),
|
||||
EqualityComparer<string>.Default,
|
||||
Comparer<string>.Default);
|
||||
currency_filter =new RegexFilterVM<Row>(r => r.currency, nameof(Row.currency));
|
||||
// rows
|
||||
_wrapped_rows = _rows.WrapSelector(r => r.security_md, s => {
|
||||
var r = new Row(_bus, s, _orders_model, _position_model);
|
||||
@@ -129,9 +122,8 @@ namespace mbts.spark {
|
||||
const double working_orders_col_width = 105;
|
||||
var price_converter = new TickDisplayConverter();
|
||||
|
||||
add_col(ColKey.Venue, r => r.venue.ToString(), nameof(Row.venue), venue_filter, FilterType.DiscreteValues, ColWidth.Exchange);
|
||||
add_col(ColKey.Security, r => r.security.ToString(), nameof(Row.security), security_filter, FilterType.Regex, ColWidth.Security);
|
||||
add_col(ColKey.Currency, r => r.currency, nameof(Row.currency), currency_filter, FilterType.DiscreteValues, ColWidth.Currency);
|
||||
add_col(ColKey.Currency, r => r.currency, nameof(Row.currency), currency_filter, FilterType.Regex, ColWidth.Currency);
|
||||
add_col(ColKey.Volume, r => r.volume_text, nameof(Row.volume), volume_filter, FilterType.ULongMinMax, ColWidth.Volume);
|
||||
|
||||
// last
|
||||
@@ -396,9 +388,8 @@ namespace mbts.spark {
|
||||
internal IBus bus => _bus;
|
||||
internal OrdersModel orders_model => _orders_model;
|
||||
|
||||
public IDataGridColumnPersistentFilter<Row, Venue[]> venue_filter { get; } = DiscreteValuesFilterVM.Common.exchange<Row>(r => r.venue, nameof(Row.venue));
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_filter { get; }
|
||||
public IDataGridColumnPersistentFilter<Row, string[]> currency_filter { get; }
|
||||
public IDataGridColumnPersistentFilter<Row, string> currency_filter { get; }
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> bid_price_filter { get; } = new DoubleMinMax<Row>(r => r.bid_price, nameof(Row.bid_price));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> bid_qty_filter { get; } = new DoubleMinMax<Row>(r => r.bid_qty_display, nameof(Row.bid_qty_display));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> dbid_qty_filter { get; } = new DoubleMinMax<Row>(r => r.dbid_qty_display, nameof(Row.dbid_qty_display));
|
||||
@@ -544,8 +535,6 @@ namespace mbts.spark {
|
||||
|
||||
public MarketViewFilterStates get_filter_states() =>
|
||||
new() {
|
||||
venue = venue_filter.get_state(),
|
||||
currency = currency_filter.get_state(),
|
||||
security = security_filter.get_state(),
|
||||
bid_price = bid_price_filter.get_state(),
|
||||
bid_merged_size = bid_qty_filter.get_state(),
|
||||
@@ -591,8 +580,6 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
using var _ = rows_view.DeferRefresh();
|
||||
venue_filter.load_state(filters.venue);
|
||||
currency_filter.load_state(filters.currency);
|
||||
security_filter.load_state(filters.security);
|
||||
bid_price_filter.load_state(filters.bid_price);
|
||||
bid_qty_filter.load_state(filters.bid_merged_size);
|
||||
|
||||
@@ -6,7 +6,6 @@ using mbts.ui.controls;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.extensions;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
@@ -128,11 +127,7 @@ namespace mbts.ui.spark {
|
||||
size,
|
||||
Observable.Return(calc_buttons));
|
||||
|
||||
_show_popup(
|
||||
ticket_vm,
|
||||
new OrderTicketHeader { bbo = ticket_vm.bbo },
|
||||
resources,
|
||||
view => Behavior.SetAutoFocusTargetProvider(view, AutoFocusTargetProvider.TypeProvider(typeof(TextBox), view)));
|
||||
_show_popup(ticket_vm, new OrderTicketHeader { bbo = ticket_vm.bbo }, resources);
|
||||
}
|
||||
|
||||
void _show_popup(object content, object header) {
|
||||
@@ -141,7 +136,7 @@ namespace mbts.ui.spark {
|
||||
_show_popup(content, header, resources);
|
||||
}
|
||||
|
||||
void _show_popup(object content, object header, ResourceDictionary resources, Action<HeaderedContentControl> init_view = null) {
|
||||
void _show_popup(object content, object header, ResourceDictionary resources) {
|
||||
var view = new HeaderedContentControl {
|
||||
DataContext = content,
|
||||
Content = content,
|
||||
@@ -151,7 +146,6 @@ namespace mbts.ui.spark {
|
||||
Resources = resources ?? new AuxiliaryDataResources()
|
||||
};
|
||||
view.Style = view.Resources[AuxiliaryDataResources.PopupChildStyleKey] as Style;
|
||||
init_view?.Invoke(view);
|
||||
_popup_man.show(view);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@ using bts.view;
|
||||
|
||||
using mbts.persist;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.extensions;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
@@ -29,18 +27,12 @@ using System.Collections.Immutable;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace mbts.spark {
|
||||
class MarketViewWindow : SparkWindow<MarketViewSettings>, ISharedWindow, ISlowMeasureContentSize {
|
||||
const string _window_type = "MarketView", _window_type_display_name = "Market View";
|
||||
|
||||
static int _window_id_counter = 0;
|
||||
|
||||
readonly string _window_id = $"{_window_type}_{Interlocked.Increment(ref _window_id_counter)}";
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly ISecurityMetadataStore _sec_meta_store;
|
||||
readonly IPositionModel _position_model;
|
||||
@@ -130,7 +122,7 @@ namespace mbts.spark {
|
||||
Log.notnull(_state);
|
||||
_menu_display_vm = new SparkMenuDisplayVM();
|
||||
_menu_display_vm.load_from(_state.menu_display);
|
||||
var update_securities_cmd = SparkCommands.change_securities_callback(_window_id);
|
||||
var update_securities_cmd = SparkCommands.change_securities_callback(_window_type_display_name);
|
||||
var security_md_map_src = Observable.Create<IReadOnlyDictionary<uint, SecurityMD>>(o => {
|
||||
IDisposable sub = null;
|
||||
_security_criteria_changed += on_security_criteria_changed;
|
||||
@@ -294,32 +286,7 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnParentViewCreated(SparkWindowView parent_view) {
|
||||
protected override void OnParentViewCreated(SparkWindowView parent_view) =>
|
||||
_menu_display_vm.safe_bind_to_view(parent_view);
|
||||
ui.WindowStyle.AddWinFormsHostChangedHandler(parent_view, OnWinFormsHostChanged);
|
||||
|
||||
void OnWinFormsHostChanged(object sender, RoutedPropertyChangedEventArgs<WPFPanel> e) {
|
||||
// If this window is "activated" by the user clicking on a popup window (such as an
|
||||
// order ticket), it never receives keyboard focus (seems like a bug in Windows). As a
|
||||
// workaround we explicitly focus the view when the parent WPFPanel gets focus. If the
|
||||
// user clicks on the popup a second time, it should get keyboard focus.
|
||||
if (e.OldValue is not null) {
|
||||
e.OldValue.GotFocus -= OnGotFocus;
|
||||
}
|
||||
if (e.NewValue is not null) {
|
||||
e.NewValue.GotFocus += OnGotFocus;
|
||||
}
|
||||
|
||||
void OnGotFocus(object sender, EventArgs e) =>
|
||||
_view.Dispatcher.InvokeAsync(StealFocus, DispatcherPriority.Send);
|
||||
|
||||
void StealFocus() {
|
||||
if (_view.IsVisible) {
|
||||
Log.Ui.info($"MarketViewWindow. Stealing focus from WPFPanel");
|
||||
_view.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
|
||||
class TimeAndSalesWindow : SparkWindow<TimeAndSalesSettings>, ISharedWindow {
|
||||
@@ -36,9 +35,7 @@
|
||||
const double _VERTICAL_SCROLLBAR_OVERLAP = 9;
|
||||
|
||||
static readonly TimeSpan _trade_poll_interval = TimeSpan.FromSeconds(0.4);
|
||||
static int _window_id_counter = 0;
|
||||
|
||||
readonly string _window_id = $"{WINDOW_TYPE_NAME}_{Interlocked.Increment(ref _window_id_counter)}";
|
||||
[DisallowNull]
|
||||
readonly MDConsumer _consumer;
|
||||
[DisallowNull]
|
||||
@@ -136,7 +133,7 @@
|
||||
}
|
||||
return result;
|
||||
});
|
||||
var update_securities_cmd = SparkCommands.change_securities_callback(_window_id);
|
||||
var update_securities_cmd = SparkCommands.change_securities_callback(WINDOW_DISPLAY_NAME);
|
||||
var security_criteria_changed = new Action<SecurityCriteria>(sc => { });
|
||||
|
||||
SparkCommand toggle_is_paused_cmd = null;
|
||||
|
||||
-20
@@ -314,12 +314,6 @@
|
||||
<TextBlock Text="{Binding param.display_value, Converter={StaticResource DoubleTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.DoubleTemplate>
|
||||
<!--Nullable Double-->
|
||||
<usp:AlgoParamTemplateSelector.NullableDoubleTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding param.display_value, Converter={StaticResource DoubleTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.NullableDoubleTemplate>
|
||||
<!--Enum-->
|
||||
<usp:AlgoParamTemplateSelector.EnumTemplate>
|
||||
<DataTemplate>
|
||||
@@ -441,20 +435,6 @@
|
||||
</c:TickUpDownDouble>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.DoubleTemplate>
|
||||
<!--Nullable Double-->
|
||||
<usp:AlgoParamTemplateSelector.NullableDoubleTemplate>
|
||||
<DataTemplate>
|
||||
<c:TickUpDownNullableDouble x:Name="tick_up_down" Focusable="False" HorizontalAlignment="Stretch" Value="{Binding param.display_value, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
|
||||
<c:TickUpDownNullableDouble.CommandBindings>
|
||||
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
|
||||
</c:TickUpDownNullableDouble.CommandBindings>
|
||||
<c:TickUpDownNullableDouble.InputBindings>
|
||||
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
|
||||
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
|
||||
</c:TickUpDownNullableDouble.InputBindings>
|
||||
</c:TickUpDownNullableDouble>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.NullableDoubleTemplate>
|
||||
<!--Enum-->
|
||||
<usp:AlgoParamTemplateSelector.EnumTemplate>
|
||||
<DataTemplate>
|
||||
|
||||
-2
@@ -33,7 +33,6 @@ namespace mbts.ui.spark {
|
||||
public DataTemplate UIntTemplate { get; set; }
|
||||
public DataTemplate NullableUIntTemplate { get; set; }
|
||||
public DataTemplate DoubleTemplate { get; set; }
|
||||
public DataTemplate NullableDoubleTemplate { get; set; }
|
||||
public DataTemplate EnumTemplate { get; set; }
|
||||
public DataTemplate NullableEnumTemplate { get; set; }
|
||||
public DataTemplate StringTemplate { get; set; }
|
||||
@@ -53,7 +52,6 @@ namespace mbts.ui.spark {
|
||||
IAlgoParamVM pvm = item as IAlgoParamVM ?? (item as AlgoParamRowVM)?.param;
|
||||
if (pvm != null) {
|
||||
if (pvm is IAlgoParamVM<double>) return DoubleTemplate;
|
||||
if (pvm is IAlgoParamVM<double?>) return NullableDoubleTemplate;
|
||||
if (pvm is IAlgoParamVM<int>) return IntTemplate;
|
||||
if (pvm is IAlgoParamVM<int?>) return NullableIntTemplate;
|
||||
if (pvm is IAlgoParamVM<uint>) return UIntTemplate;
|
||||
|
||||
@@ -237,17 +237,6 @@ namespace mbts.spark {
|
||||
DefaultColumnWidths.Qty,
|
||||
get_fg: r => r.side == Side.Buy ? BTSColors.MarketData.bid : BTSColors.MarketData.ask);
|
||||
|
||||
add_col(
|
||||
Columns.NotionalValue,
|
||||
DefaultColumnHeaders.NotionalValue,
|
||||
r => _get_row(r).security.spec_or_default().format(r.fill.notional_value()),
|
||||
new DoubleMinMax<FillTuple>(r => r.fill.notional_value()),
|
||||
DataGridFilterKnownTemplates.DoubleMinMax,
|
||||
fs => fs.notional_value,
|
||||
(fs, cf) => fs.notional_value = cf,
|
||||
DefaultColumnWidths.NotionalValue,
|
||||
get_fg: r => r.side == Side.Buy ? BTSColors.MarketData.bid : BTSColors.MarketData.ask);
|
||||
|
||||
add_col(
|
||||
Columns.Price,
|
||||
DefaultColumnHeaders.Price,
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace mbts.ui.spark {
|
||||
Side,
|
||||
Price,
|
||||
Size,
|
||||
NotionalValue,
|
||||
FillKind,
|
||||
FillStatus,
|
||||
FillKey,
|
||||
|
||||
@@ -61,10 +61,6 @@ namespace bts.shell.fills {
|
||||
[DIYGridColumnID(nameof(Columns.Size))]
|
||||
public DoubleMinMaxState size { get; set; }
|
||||
|
||||
[ProtoMember(18)]
|
||||
[DIYGridColumnID(nameof(Columns.NotionalValue))]
|
||||
public DoubleMinMaxState notional_value { get; set; }
|
||||
|
||||
[ProtoMember(11)]
|
||||
[DIYGridColumnID(nameof(Columns.FillKey))]
|
||||
public string fill_key { get; set; }
|
||||
@@ -117,7 +113,6 @@ namespace bts.shell.fills {
|
||||
pb.add_discrete_values(fs.fill_status, ft => ft.fill.FillStatus);
|
||||
// numeric
|
||||
pb.add_numeric(fs.size, ft => ft.fill.LastShares);
|
||||
pb.add_numeric(fs.notional_value, ft => ft.fill.notional_value());
|
||||
pb.add_numeric(fs.price, ft => ft.fill.LastPrice);
|
||||
|
||||
if (fs.exp_date.HasValue) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -66,12 +66,8 @@ namespace mbts.spark {
|
||||
_orders = orders;
|
||||
_real_price = real_price;
|
||||
_text_lines = backing(nameof(text_lines), () => {
|
||||
var tf = _owner.model.security.tick_factors();
|
||||
var fmt = MDDisplayHelper.get_value_formatter(
|
||||
owner.current_settings.size_display,
|
||||
_owner.model.security_md.quantity_display,
|
||||
tf.to_api_tick_display_settingsi());
|
||||
return BadgeRender.format(side, _quantity, _filled, price, _current_badge_style, fmt);
|
||||
var fmt = MDDisplayHelper.get_value_formatter(_owner.model.security_md.quantity_display);
|
||||
return BadgeRender.format(side, _quantity, _filled, _current_badge_style, fmt);
|
||||
});
|
||||
_clear_phantom_price = new(() => {
|
||||
_phantom_price = double.NaN;
|
||||
|
||||
@@ -100,9 +100,6 @@ namespace mbts.spark {
|
||||
readonly OrderEntryPanelItemVisibilityVM _cancel_all_visibility = new("Cancel All");
|
||||
readonly OrderEntryPanelItemVisibilityVM _status_message_visibility = new("Status Message");
|
||||
|
||||
readonly PanelPricePointVM _bid_price = new("Bid");
|
||||
readonly PanelPricePointVM _ask_price = new("Ask");
|
||||
readonly PanelPricePointVM _mid_price = new("Mid");
|
||||
readonly PanelRelativePricePointVM _day_change = new("Day Change");
|
||||
readonly PanelPricePointVM _session_high = new("High");
|
||||
readonly PanelPricePointVM _session_low = new("Low");
|
||||
@@ -142,6 +139,9 @@ namespace mbts.spark {
|
||||
accounts_view = new ListCollectionView<AccountTuple?>(_accounts) {
|
||||
CustomSort = NullCheckComparer<AccountTuple>.Default
|
||||
};
|
||||
// cmds
|
||||
tick_up_ticks_per_row_cmd = new DelegateCommand(increment_ticks_per_row);
|
||||
tick_down_ticks_per_row_cmd = new DelegateCommand(decrement_ticks_per_row);
|
||||
// update
|
||||
_update = new DeferrableAction(() => result_changed?.Invoke());
|
||||
mini_ob.result_changed += _update.Invoke;
|
||||
@@ -191,6 +191,26 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
AlgoDefinitionVM algo_factory(AlgoData data) => AlgoDefinitionVM.create(_model, data);
|
||||
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);
|
||||
if (new_tpr != tpr) {
|
||||
ticks_per_row = new_tpr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
if (new_tpr != tpr) {
|
||||
ticks_per_row = new_tpr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event Action result_changed;
|
||||
@@ -215,8 +235,15 @@ namespace mbts.spark {
|
||||
}
|
||||
public uint ticks_per_row {
|
||||
get => _ticks_per_row;
|
||||
set => Set(ref _ticks_per_row, value);
|
||||
set => Set(ref _ticks_per_row, value, dependent_props: nameof(rounded_ticks_per_row));
|
||||
}
|
||||
public ICommand tick_up_ticks_per_row_cmd { get; }
|
||||
public ICommand tick_down_ticks_per_row_cmd { get; }
|
||||
// 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 MarketSizeDisplayVM size_display { get; } = new MarketSizeDisplayVM();
|
||||
public SimpleGridSettingsVM mini_ob { get; } = new SimpleGridSettingsVM(OrderBookGridSettingsManager.hard_column_flags);
|
||||
@@ -300,12 +327,7 @@ namespace mbts.spark {
|
||||
|
||||
public IEnumerable order_entry_panel_items {
|
||||
get {
|
||||
// Order these items according to their display order in the actual ladder (from top to
|
||||
// bottom). There are a lot of them, so this is a simple convenience for the user.
|
||||
yield return _zoom_slider_visbility;
|
||||
yield return _ask_price;
|
||||
yield return _mid_price;
|
||||
yield return _bid_price;
|
||||
yield return _day_change;
|
||||
yield return _session_high;
|
||||
yield return _session_low;
|
||||
@@ -428,12 +450,6 @@ namespace mbts.spark {
|
||||
zoom_factor = settings.zoom_factor;
|
||||
// left-panel (show/hide)
|
||||
_zoom_slider_visbility.show = settings.zoom_slider_visible;
|
||||
_bid_price.show = settings.bid_price_visible;
|
||||
_bid_price.load_from(settings.panel_price_point_settings.bid_price);
|
||||
_ask_price.show = settings.ask_price_visible;
|
||||
_ask_price.load_from(settings.panel_price_point_settings.ask_price);
|
||||
_mid_price.show = settings.mid_price_visible;
|
||||
_mid_price.load_from(settings.panel_price_point_settings.mid_price);
|
||||
_day_change.show = settings.day_visible;
|
||||
_day_change.load_from(settings.panel_price_point_settings.day_change);
|
||||
_session_high.show = settings.high_visible;
|
||||
@@ -521,9 +537,6 @@ namespace mbts.spark {
|
||||
.ToList();
|
||||
// left-panel price items
|
||||
clone.panel_price_point_settings = new LadderPanelPricePointSettings {
|
||||
bid_price = _bid_price.create(),
|
||||
ask_price = _ask_price.create(),
|
||||
mid_price = _mid_price.create(),
|
||||
day_change = _day_change.create(),
|
||||
open = _open.create(),
|
||||
session_low = _session_low.create(),
|
||||
@@ -548,9 +561,6 @@ namespace mbts.spark {
|
||||
clone.order_entry_panel_initial_width = _order_entry_panel_width;
|
||||
clone.zoom_factor = zoom_factor;
|
||||
// left-panel (components)
|
||||
clone.bid_price_visible = _bid_price.show;
|
||||
clone.ask_price_visible = _ask_price.show;
|
||||
clone.mid_price_visible = _mid_price.show;
|
||||
clone.day_visible = _day_change.show;
|
||||
clone.high_visible = _session_high.show;
|
||||
clone.low_visible = _session_low.show;
|
||||
@@ -705,7 +715,6 @@ namespace mbts.spark {
|
||||
|
||||
public uint sample_quantity { get; } = 50;
|
||||
public uint sample_filled { get; } = 12;
|
||||
public double sample_price => 2175.50;
|
||||
public Side sample_side { get; } = Side.Buy;
|
||||
public Brush badge_border { get; } = new SolidColorBrush(BTSColors.MarketData.ask);
|
||||
public Brush badge_background { get; } = Brushes.White;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -14,9 +14,9 @@ using mbts.ui.utility.text;
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Math;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
@@ -37,6 +37,9 @@ namespace mbts.spark {
|
||||
partial class LadderGridHost : Control {
|
||||
static readonly int _initial_row_count;
|
||||
static readonly bool _show_scrollbar;
|
||||
static readonly bool _lighten_market_cells;
|
||||
static readonly Color? _bid_market_cell_background;
|
||||
static readonly Color? _ask_market_cell_background;
|
||||
|
||||
readonly VisualHost _visual_host = new();
|
||||
readonly LadderOrdersHost _ordershost;
|
||||
@@ -59,7 +62,6 @@ namespace mbts.spark {
|
||||
readonly RefValueContainer<IEnumerable<VolumeAtPriceLevelImmutable>> _volume_container = new();
|
||||
|
||||
IDisposable _settings_sub, _volume_sub, _tick_sub, _fair_value_lines_sub, _vm_sub;
|
||||
readonly ThrottledAction _scroll_to_tick_log_throttle = new(TimeSpan.FromSeconds(5));
|
||||
readonly SingleValuePump<ScrollViewer> _scroll_viewer_source;
|
||||
|
||||
ScrollViewer _scroll_viewer {
|
||||
@@ -80,7 +82,6 @@ namespace mbts.spark {
|
||||
LadderTactileAdorner _tactile_adorner;
|
||||
ulong _prices_updated;
|
||||
NativePriceSize[] _bids = new NativePriceSize[NativePriceHelpers.COCONUT_MAX_DEPTH], _asks = new NativePriceSize[NativePriceHelpers.COCONUT_MAX_DEPTH];
|
||||
SecurityMDExtensions.Book _depth = SecurityMDExtensions.Book.Empty;
|
||||
bool _price_dirty = false, _piq_dirty = false;
|
||||
LadderPriceList _price_list = new() {
|
||||
length = _initial_row_count,
|
||||
@@ -91,10 +92,8 @@ namespace mbts.spark {
|
||||
readonly MarketBreakLines _mkt_break_lines;
|
||||
NativePriceSize _last_trade;
|
||||
Func<NativePriceSize, string> _fmt_mkt_depth_item_impl = MDDisplayHelper.get_formatter_nps(
|
||||
MarketSizeDisplay.get_default(), Security.Defaults.quantity_display_settingsi, Security.Defaults.price_display_settingsi);
|
||||
Func<double, double, string> _fmt_mkt_quantity_impl = (v, p) => v.ToString();
|
||||
Func<LadderPriceLevel, string> _fmt_price_level_impl = MDDisplayHelper.get_formatter_lpl(
|
||||
MarketSizeDisplay.get_default(), Security.Defaults.quantity_display_settingsi, Security.Defaults.price_display_settingsi);
|
||||
MarketSizeDisplay.get_default(), Security.Defaults.quantity_display_settingsi);
|
||||
Func<double, string> _fmt_mkt_quantity_impl = v => v.ToString();
|
||||
int _old_tooltip_row = int.MaxValue;
|
||||
NativePriceSize? _old_last_trade;
|
||||
string _direction_arrow = string.Empty;
|
||||
@@ -106,7 +105,17 @@ namespace mbts.spark {
|
||||
static LadderGridHost() {
|
||||
var knobs = config.Configuration.get<bts.config.LadderKnobs>();
|
||||
_show_scrollbar = knobs.ShowScrollbar;
|
||||
_lighten_market_cells = knobs.LightenMarketCells;
|
||||
_initial_row_count = (int)knobs.ScrollableTickWindowSize;
|
||||
// For market cell backgrounds, 0 means null. Note that the full
|
||||
// range of colors can still be expressed, as zero is merely one of
|
||||
// 2^24 possible representations of 'transparent.'
|
||||
if (knobs.BidMarketCellBackground != 0) {
|
||||
_bid_market_cell_background = BTSColors.Ladder.bid_column_background;
|
||||
}
|
||||
if (knobs.AskMarketCellBackground != 0) {
|
||||
_ask_market_cell_background = BTSColors.Ladder.ask_column_background;
|
||||
}
|
||||
}
|
||||
|
||||
public LadderGridHost() {
|
||||
@@ -443,8 +452,8 @@ namespace mbts.spark {
|
||||
|
||||
public bool scroll_to_tick(long tick) {
|
||||
if (tick > int.MaxValue) {
|
||||
_scroll_to_tick_log_throttle.Execute(() =>
|
||||
Log.Ui.error($"Unable to scroll to tick {tick} because it exceeds int.MaxValue"));
|
||||
Log.Ui.error(
|
||||
$"Unable to scroll to tick {tick} because it exceeds int.MaxValue");
|
||||
return false;
|
||||
}
|
||||
return scroll_to_tick((int)tick);
|
||||
@@ -726,8 +735,8 @@ namespace mbts.spark {
|
||||
};
|
||||
view.RequestClose += (o, _) => popup.IsOpen = false;
|
||||
}
|
||||
string fmt_mbo_qty(double value, double price) =>
|
||||
value < 0 ? $"-{_fmt_mkt_quantity(value, price)}" : _fmt_mkt_quantity(value, price);
|
||||
string fmt_mbo_qty(int value) =>
|
||||
value < 0 ? $"-{_fmt_mkt_quantity((uint)value)}" : _fmt_mkt_quantity((uint)value);
|
||||
}
|
||||
|
||||
protected override void OnMouseDoubleClick(MouseButtonEventArgs e) {
|
||||
@@ -861,13 +870,32 @@ namespace mbts.spark {
|
||||
switch (column.column_type) {
|
||||
case ColumnType.Order: _ordershost.set_col_width(column.settings.width); break;
|
||||
case ColumnType.Bid:
|
||||
render_market_col(_depth.Bids);
|
||||
render_market_col(_bids, _bid_market_cell_background);
|
||||
break;
|
||||
case ColumnType.Price:
|
||||
_refresh_prices(column);
|
||||
if (_settings.net_change != LadderSettings.NetChangeTerms.None) {
|
||||
LadderRender.render_prices(
|
||||
column.visual_tup.visual,
|
||||
column.visual_tup.glyph_context,
|
||||
_price_list,
|
||||
_settings.row_height,
|
||||
column.settings,
|
||||
_spec,
|
||||
_vm.md.settlement_price,
|
||||
_settings.net_change == LadderSettings.NetChangeTerms.Ticks);
|
||||
} else {
|
||||
LadderRender.render_prices(
|
||||
column.visual_tup.visual,
|
||||
column.visual_tup.glyph_context,
|
||||
_price_list,
|
||||
_settings.row_height,
|
||||
column.settings,
|
||||
_spec,
|
||||
_settings.price_display_format);
|
||||
}
|
||||
break;
|
||||
case ColumnType.Ask:
|
||||
render_market_col(_depth.Asks);
|
||||
render_market_col(_asks, _ask_market_cell_background);
|
||||
break;
|
||||
case ColumnType.Volume:
|
||||
_volume_container.mark_dirty();
|
||||
@@ -880,9 +908,9 @@ namespace mbts.spark {
|
||||
if (_mkt_break_lines.high_render_index.HasValue)
|
||||
_render_session_line(_session_high_vis, _mkt_break_lines.high_render_index.Value, _settings.session_high_line.color);
|
||||
}
|
||||
void render_market_col(IEnumerable<(double Price, double Volume, double NotionalValue)> depth) {
|
||||
void render_market_col(NativePriceSize[] depth, Color? market_cell_background) {
|
||||
var mid = LadderRender.index_of(_price_list, _last_origin_price, _price_comparer);
|
||||
LadderRender.render_sizes(column.visual_tup.visual, column.visual_tup.glyph_context, mid, _price_list, depth, _settings.row_height, _spec, column.settings, _fmt_price_level);
|
||||
LadderRender.render_sizes(column.visual_tup.visual, column.visual_tup.glyph_context, mid, _price_list, depth, _settings.row_height, _spec, column.settings, _fmt_mkt_depth_item, _lighten_market_cells, market_cell_background);
|
||||
}
|
||||
}
|
||||
_apply_column_visual_offset();
|
||||
@@ -928,7 +956,6 @@ namespace mbts.spark {
|
||||
// **use local vm to protect against re-assignment,
|
||||
// so the events are unhooked from the same instance to which they were added
|
||||
// when the subs are disposed.
|
||||
Disposable.dispose(ref _vm_sub);
|
||||
var local_vm = _vm;
|
||||
local_vm.on_tick += _tick;
|
||||
local_vm.request_recenter += on_request_recenter;
|
||||
@@ -987,10 +1014,8 @@ namespace mbts.spark {
|
||||
//System.Diagnostics.Debug.WriteLine(" Tooltip - Previous tooltip is visible, but we've changed rows");
|
||||
_hide_tool_tip();
|
||||
}
|
||||
double price = row < 0 || row >= (_price_list?.Count ?? 0)
|
||||
? double.NaN
|
||||
: _price_for_row(row);
|
||||
_tooltip_view.Content = _fmt_mkt_quantity(volume, price);
|
||||
|
||||
_tooltip_view.Content = _fmt_mkt_quantity(volume);
|
||||
_tooltip_view.IsOpen = true;
|
||||
ToolTip = _tooltip_view;
|
||||
} else {
|
||||
@@ -1077,34 +1102,19 @@ namespace mbts.spark {
|
||||
return Disposable.Create(() => _can_user_send_orders_changed -= o.OnNext);
|
||||
});
|
||||
|
||||
LadderPriceList _create_price_list() {
|
||||
if (_settings is not null) {
|
||||
switch (_settings.net_change) {
|
||||
case LadderSettings.NetChangeTerms.BasisPoints:
|
||||
case LadderSettings.NetChangeTerms.NetBasisPoints:
|
||||
return new() {
|
||||
length = _initial_row_count,
|
||||
regional_tick = new RegionalTickFactors(new user.settings.TickFactors(10000, 1)),
|
||||
};
|
||||
}
|
||||
}
|
||||
return new() {
|
||||
length = _initial_row_count,
|
||||
regional_tick = _spec
|
||||
};
|
||||
}
|
||||
|
||||
#region Timer.Tick, Refresh Methods
|
||||
void _tick() {
|
||||
if (_settings != null) {
|
||||
ulong unused = 0;
|
||||
bool last_trade_changed = _vm.md.get_last_trade(ref unused, out _last_trade, out DateTime _, out ulong __);
|
||||
bool depth_changed = _vm.md.get_depth(ref _prices_updated, ref _bids, ref _asks, NativePriceHelpers.COCONUT_MAX_DEPTH);
|
||||
_depth = _vm.security_md.Depth(_bids, _asks, _vm.securities);
|
||||
_mkt_break_lines.refresh();
|
||||
if (_price_list is null) {
|
||||
Log.assert(_price_dirty);
|
||||
_price_list = _create_price_list();
|
||||
_price_list = new LadderPriceList {
|
||||
length = _initial_row_count,
|
||||
security = security
|
||||
};
|
||||
var mid_tick = security.tickdiff(_mkt_break_lines.origin, 0) ?? 0;
|
||||
if (mid_tick > int.MaxValue) {
|
||||
Log.Ui.error(
|
||||
@@ -1141,8 +1151,10 @@ namespace mbts.spark {
|
||||
_scroll_viewer.ScrollToVerticalOffset(new_vertical_offset);
|
||||
}
|
||||
}
|
||||
_refresh_prices();
|
||||
} else {
|
||||
_refresh_prices();
|
||||
}
|
||||
_refresh_prices();
|
||||
scroll_to_pending_target();
|
||||
_refresh_all_visuals();
|
||||
} else {
|
||||
@@ -1151,17 +1163,8 @@ namespace mbts.spark {
|
||||
_refresh_bid_mkt_break();
|
||||
if (_mkt_break_lines.ask_dirty)
|
||||
_refresh_ask_mkt_break();
|
||||
if (_mkt_break_lines.mid_dirty) {
|
||||
switch (_settings.net_change) {
|
||||
case LadderSettings.NetChangeTerms.BasisPoints:
|
||||
case LadderSettings.NetChangeTerms.NetBasisPoints:
|
||||
_refresh_prices();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (_mkt_break_lines.mid_dirty)
|
||||
_refresh_mid_mkt_break();
|
||||
}
|
||||
_refresh_fair_value_lines(false);
|
||||
if (_mkt_break_lines.session_low_dirty)
|
||||
_refresh_session_low_line();
|
||||
@@ -1171,16 +1174,8 @@ namespace mbts.spark {
|
||||
_refresh_session_range();
|
||||
if (last_trade_changed) {
|
||||
var last_index = LadderRender.index_of(_price_list, _last_trade.price, _price_comparer);
|
||||
if (last_index > 0) {
|
||||
LadderRender.render_last(
|
||||
_last_vis_tup.visual,
|
||||
_last_vis_tup.glyph_context,
|
||||
_last_trade,
|
||||
last_index,
|
||||
_settings.row_height,
|
||||
_settings.last_column,
|
||||
_fmt_mkt_depth_item);
|
||||
}
|
||||
if (last_index > 0)
|
||||
LadderRender.render_last(_last_vis_tup.visual, _last_vis_tup.glyph_context, _last_trade, last_index, _settings.row_height, _settings.last_column, _fmt_mkt_quantity);
|
||||
}
|
||||
if (depth_changed)
|
||||
_refresh_sizes();
|
||||
@@ -1195,27 +1190,25 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
void _refresh_prices(Column column = null) {
|
||||
void _refresh_prices() {
|
||||
if (_settings is null) {
|
||||
return;
|
||||
}
|
||||
column ??= _price_column;
|
||||
var ref_price = _settings.net_change switch {
|
||||
LadderSettings.NetChangeTerms.BasisPoints or
|
||||
LadderSettings.NetChangeTerms.NetBasisPoints or
|
||||
LadderSettings.NetChangeTerms.Ticks => _get_mid_market_break(),
|
||||
_ => 0.0
|
||||
};
|
||||
LadderRender.render_prices(
|
||||
column.visual_tup.visual,
|
||||
column.visual_tup.glyph_context,
|
||||
_price_list,
|
||||
ref_price,
|
||||
_settings.net_change,
|
||||
_settings.row_height,
|
||||
column.settings,
|
||||
_spec,
|
||||
_settings.price_display_format);
|
||||
|
||||
if (_settings.net_change != LadderSettings.NetChangeTerms.None) {
|
||||
LadderRender.render_prices(
|
||||
_price_vis_tup.visual, _price_vis_tup.glyph_context,
|
||||
_price_list, _vm.md.settlement_price,
|
||||
_settings.net_change == LadderSettings.NetChangeTerms.Ticks,
|
||||
_settings.row_height, _settings.price_column,
|
||||
_spec);
|
||||
} else {
|
||||
LadderRender.render_prices(
|
||||
_price_vis_tup.visual, _price_vis_tup.glyph_context,
|
||||
_price_list, _settings.row_height,
|
||||
_settings.price_column, _spec,
|
||||
_settings.price_display_format);
|
||||
}
|
||||
}
|
||||
|
||||
void _refresh_all_visuals() {
|
||||
@@ -1271,50 +1264,33 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
void _refresh_session_low_line() {
|
||||
if (_settings.session_low_line.show) {
|
||||
_render_session_line(_session_low_vis, _mkt_break_lines.low_render_index ?? -1, _settings.session_low_line.color);
|
||||
}
|
||||
if (_settings.session_low_line.show && _mkt_break_lines.low_render_index.HasValue)
|
||||
_render_session_line(_session_low_vis, _mkt_break_lines.low_render_index.Value, _settings.session_low_line.color);
|
||||
}
|
||||
|
||||
void _refresh_session_high_line() {
|
||||
if (_settings.session_high_line.show) {
|
||||
_render_session_line(_session_high_vis, _mkt_break_lines.high_render_index ?? -1, _settings.session_high_line.color);
|
||||
}
|
||||
if (_settings.session_high_line.show && _mkt_break_lines.high_render_index.HasValue)
|
||||
_render_session_line(_session_high_vis, _mkt_break_lines.high_render_index.Value, _settings.session_high_line.color);
|
||||
}
|
||||
|
||||
void _refresh_last_trade() {
|
||||
var last_index = LadderRender.index_of(_price_list, _last_trade.price, _price_comparer);
|
||||
if (last_index > 0)
|
||||
LadderRender.render_last(
|
||||
_last_vis_tup.visual,
|
||||
_last_vis_tup.glyph_context,
|
||||
_last_trade,
|
||||
last_index,
|
||||
_settings.row_height,
|
||||
_settings.last_column,
|
||||
_fmt_mkt_depth_item);
|
||||
LadderRender.render_last(_last_vis_tup.visual, _last_vis_tup.glyph_context, _last_trade, last_index, _settings.row_height, _settings.last_column, _fmt_mkt_quantity);
|
||||
}
|
||||
|
||||
void _refresh_piq() {
|
||||
if (!_settings.piq_column.show)
|
||||
return;
|
||||
int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer);
|
||||
LadderRender.render_piqs(
|
||||
_piq_vis_tup.visual,
|
||||
_last_vis_tup.glyph_context,
|
||||
_price_list,
|
||||
_vm.price_vs_piq,
|
||||
_settings.row_height,
|
||||
_spec,
|
||||
_settings.piq_column,
|
||||
_fmt_mkt_quantity);
|
||||
LadderRender.render_piqs(_piq_vis_tup.visual, _last_vis_tup.glyph_context, _price_list, _vm.price_vs_piq, _settings.row_height, _spec, _settings.piq_column, _fmt_mkt_quantity);
|
||||
}
|
||||
|
||||
void _refresh_sizes() {
|
||||
Log.assert_d(_price_list != null);
|
||||
int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer);
|
||||
LadderRender.render_sizes(_bid_vis_tup.visual, _bid_vis_tup.glyph_context, origin_index, _price_list, _depth.Bids, _settings.row_height, _spec, _settings.bid_column, _fmt_price_level);
|
||||
LadderRender.render_sizes(_ask_vis_tup.visual, _ask_vis_tup.glyph_context, origin_index, _price_list, _depth.Asks, _settings.row_height, _spec, _settings.ask_column, _fmt_price_level);
|
||||
LadderRender.render_sizes(_bid_vis_tup.visual, _bid_vis_tup.glyph_context, origin_index, _price_list, _bids, _settings.row_height, _spec, _settings.bid_column, _fmt_mkt_depth_item, _lighten_market_cells, _bid_market_cell_background);
|
||||
LadderRender.render_sizes(_ask_vis_tup.visual, _ask_vis_tup.glyph_context, origin_index, _price_list, _asks, _settings.row_height, _spec, _settings.ask_column, _fmt_mkt_depth_item, _lighten_market_cells, _ask_market_cell_background);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1369,7 +1345,7 @@ namespace mbts.spark {
|
||||
}
|
||||
// regardless of if we've arrived here via a new Last (and new direction arrow)
|
||||
// or we're reusing the old one, this format bit is the same
|
||||
return $"{_direction_arrow} {_fmt_mkt_quantity(_last_trade.size, _last_trade.price)} ";
|
||||
return $"{_direction_arrow} {_fmt_mkt_quantity(_last_trade.size)} ";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1385,7 +1361,7 @@ namespace mbts.spark {
|
||||
return;
|
||||
if (_price_list is null) {
|
||||
_price_list = new LadderPriceList {
|
||||
regional_tick = _spec
|
||||
security = security
|
||||
};
|
||||
}
|
||||
_price_list.reset(null, null, _initial_row_count);
|
||||
@@ -1422,10 +1398,7 @@ namespace mbts.spark {
|
||||
double _get_bid_market_break() {
|
||||
double best_bid = _get_top_price(_bids);
|
||||
double best_ask = _get_top_price(_asks);
|
||||
var v = best_bid
|
||||
.if_nan(_spec.tickdown(best_ask))
|
||||
.if_nan(_vm.settlement);
|
||||
return _spec.round_to_tick(v, RoundingMode.AbsDown);
|
||||
return LadderPriceLineViewAdapter.bid_line(best_bid, best_ask, _vm.settlement, _spec);
|
||||
}
|
||||
|
||||
double _normalize_price(double price) =>
|
||||
@@ -1434,20 +1407,13 @@ namespace mbts.spark {
|
||||
double _get_ask_market_break() {
|
||||
double best_bid = _get_top_price(_bids);
|
||||
double best_ask = _get_top_price(_asks);
|
||||
var v = _spec.tickdown(best_ask)
|
||||
.if_nan(best_bid)
|
||||
.if_nan(_vm.settlement);
|
||||
return _spec.round_to_tick(v, RoundingMode.AbsDown);
|
||||
return LadderPriceLineViewAdapter.ask_line(best_bid, best_ask, _vm.settlement, _spec);
|
||||
}
|
||||
|
||||
double _get_mid_market_break() {
|
||||
double best_bid = _get_top_price(_bids);
|
||||
double best_ask = _get_top_price(_asks);
|
||||
var v = TickUtils.mid_tick(_spec, best_bid, best_ask)
|
||||
.if_nan(best_bid)
|
||||
.if_nan(_spec?.tickdown(best_ask) ?? double.NaN)
|
||||
.if_nan(_vm.settlement);
|
||||
return _spec.round_to_tick(v, RoundingMode.AbsDown);
|
||||
return LadderPriceLineViewAdapter.mid_line(best_bid, best_ask, _vm.settlement, _spec);
|
||||
}
|
||||
|
||||
double _get_session_low() => _normalize_price(_get_session_low_raw());
|
||||
@@ -1479,13 +1445,7 @@ namespace mbts.spark {
|
||||
_pending_scroll_to_origin |= _settings == null;
|
||||
_invalidate_spec();
|
||||
if (_price_list != null) {
|
||||
_pending_price_list = null;
|
||||
if (_price_list.Count > 0) {
|
||||
var high_price = _price_list.First();
|
||||
var new_tickdiff = _spec.count_ticks(high_price);
|
||||
_price_list.high_tick = (long)new_tickdiff;
|
||||
}
|
||||
_price_list.regional_tick = _spec;
|
||||
_price_list.security = security;
|
||||
}
|
||||
_bid_vis_tup.glyph_context = glyph_context(s.bid_column);
|
||||
_ask_vis_tup.glyph_context = glyph_context(s.ask_column);
|
||||
@@ -1497,13 +1457,9 @@ namespace mbts.spark {
|
||||
static GlyphContext glyph_context(LadderColumnSettings cs) =>
|
||||
GlyphContext.create(cs.font_family, cs.font_weight);
|
||||
|
||||
var price_display = security.regional_ticks().tick_factors(0).to_api_tick_display_settingsi();
|
||||
_fmt_mkt_depth_item_impl = MDDisplayHelper.get_formatter_nps(
|
||||
_settings.size_display, _qty_display, price_display);
|
||||
_fmt_mkt_quantity_impl = MDDisplayHelper.get_value_formatter(
|
||||
_settings.size_display, _qty_display, price_display);
|
||||
_fmt_price_level_impl = MDDisplayHelper.get_formatter_lpl(
|
||||
_settings.size_display, _qty_display, price_display);
|
||||
_settings.size_display, _qty_display);
|
||||
_fmt_mkt_quantity_impl = MDDisplayHelper.get_value_formatter(_qty_display);
|
||||
_set_columns();
|
||||
_apply_column_visual_offset();
|
||||
if (prev_row_height != _settings.row_height)
|
||||
@@ -1513,16 +1469,18 @@ namespace mbts.spark {
|
||||
_mkt_break_lines.fair_value_line_views.Clear();
|
||||
// Clear and re-add the columns to the viewer to ensure offset/visibility is correct
|
||||
_visual_host.Clear();
|
||||
// The session_range_vis sits beneath the grid backgrounds (on the
|
||||
// z-axis) to avoid obfuscating the font color (dj wants it to be
|
||||
// pure white). Historically, it sat atop the price column, with a
|
||||
// low opacity to slightly darken the range of prices inside of the
|
||||
// session range (i.e., the price-band of the day's trades). In
|
||||
// conjuction with this change, the price column's background
|
||||
// opacity is made slightly less than 1 (in the default settings),
|
||||
// while the font color remains fully opaque, so this layer will
|
||||
// darken the background without affecting the font color.
|
||||
_visual_host.AddVisualChild(_session_range_vis);
|
||||
if (LadderSettings.mimic_tt) {
|
||||
// The session_range_vis sits beneath the grid backgrounds (on the
|
||||
// z-axis) to avoid obfuscating the font color (dj wants it to be
|
||||
// pure white). Historically, it sat atop the price column, with a
|
||||
// low opacity to slightly darken the range of prices inside of the
|
||||
// session range (i.e., the price-band of the day's trades). In
|
||||
// conjuction with this change, the price column's background
|
||||
// opacity is made slightly less than 1 (in the default settings),
|
||||
// while the font color remains fully opaque, so this layer will
|
||||
// darken the background without affecting the font color.
|
||||
_visual_host.AddVisualChild(_session_range_vis);
|
||||
}
|
||||
// Renders the grid lines and column backgrounds
|
||||
_visual_host.AddVisualChild(_gridvis);
|
||||
// Orders column
|
||||
@@ -1562,6 +1520,8 @@ namespace mbts.spark {
|
||||
with_vis => _visual_host.RemoveVisualChild(with_vis.vis));
|
||||
_visual_host.AddVisualChild(_session_low_vis);
|
||||
_visual_host.AddVisualChild(_session_high_vis);
|
||||
if (!LadderSettings.mimic_tt)
|
||||
_visual_host.AddVisualChild(_session_range_vis);
|
||||
_render_grid_background();
|
||||
_init_tactile_adorner();
|
||||
// this effectively sets the background of the "Last" column //deleted to fix color
|
||||
@@ -1570,7 +1530,6 @@ namespace mbts.spark {
|
||||
// }
|
||||
scroll_to_pending_target();
|
||||
visible_columns_changed?.Invoke();
|
||||
_refresh_all_visuals();
|
||||
}
|
||||
|
||||
// This is designed to be a redundant null check for _fmt_mkt_depth_item_impl.
|
||||
@@ -1578,27 +1537,16 @@ namespace mbts.spark {
|
||||
// Assign the impl delegate to a local variable, just in case it is reassigned on another thread.
|
||||
// This shouldn't happen, but better to avoid the potential null ref just in case it does. Expect the unexpected.
|
||||
var impl = _fmt_mkt_depth_item_impl;
|
||||
impl ??= MDDisplayHelper.get_formatter_nps(
|
||||
MarketSizeDisplay.get_default(), _qty_display, _spec.tick_factors(0).to_api_tick_display_settingsi());
|
||||
return impl(value);
|
||||
}
|
||||
|
||||
// This is designed to be a redundant null check for _fmt_mkt_depth_item_impl.
|
||||
string _fmt_price_level(LadderPriceLevel value) {
|
||||
// Assign the impl delegate to a local variable, just in case it is reassigned on another thread.
|
||||
// This shouldn't happen, but better to avoid the potential null ref just in case it does. Expect the unexpected.
|
||||
var impl = _fmt_price_level_impl;
|
||||
impl ??= MDDisplayHelper.get_formatter_lpl(
|
||||
MarketSizeDisplay.get_default(), _qty_display, security.regional_ticks().tick_factors(0).to_api_tick_display_settingsi());
|
||||
impl ??= MDDisplayHelper.get_formatter_nps(MarketSizeDisplay.get_default(), _qty_display);
|
||||
return impl(value);
|
||||
}
|
||||
|
||||
// This is designed to be a redundant null check for _fmt_mkt_quantity_impl.
|
||||
string _fmt_mkt_quantity(double value, double price) {
|
||||
string _fmt_mkt_quantity(double value) {
|
||||
// Assign the impl delegate to a local variable, just in case it is reassigned on another thread.
|
||||
// This shouldn't happen, but better to avoid the potential null ref just in case it does. Expect the unexpected.
|
||||
var impl = _fmt_mkt_quantity_impl;
|
||||
return impl is null ? value.ToString() : impl(value, price);
|
||||
return impl is null ? value.ToString() : impl(value);
|
||||
}
|
||||
|
||||
void _init_tactile_adorner() {
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
namespace mbts.spark;
|
||||
|
||||
using bts;
|
||||
using bts.utils;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
readonly struct LadderPriceLevel : IEquatable<LadderPriceLevel> {
|
||||
public LadderPriceLevel(double price = double.NaN, double volume = 0, double notionalValue = double.NaN) {
|
||||
Price = price;
|
||||
Volume = volume;
|
||||
NotionalValue = notionalValue.is_nan() ? volume * price : notionalValue;
|
||||
}
|
||||
|
||||
public static LadderPriceLevel Empty => new();
|
||||
|
||||
public double Price { get; }
|
||||
public double Volume { get; }
|
||||
public double NotionalValue { get; }
|
||||
|
||||
public LadderPriceLevel Add(double price, double volume) {
|
||||
if (!(volume > 0)) {
|
||||
return this;
|
||||
}
|
||||
if (!(Volume > 0)) {
|
||||
return new(price, volume);
|
||||
}
|
||||
Log.assert(Price.is_real());
|
||||
Log.assert(price.is_real());
|
||||
var newVolume = Volume + volume;
|
||||
var newPrice = (Price * Volume + price * volume) / newVolume;
|
||||
return new(newPrice, newVolume);
|
||||
}
|
||||
|
||||
public LadderPriceLevel Add(IEnumerable<(double Price, double Volume)> items) {
|
||||
var (totalPrice, totalVolume) = items.Sum();
|
||||
return totalVolume > 0
|
||||
? Add(totalPrice / totalVolume, totalVolume)
|
||||
: this;
|
||||
}
|
||||
|
||||
public Builder ToBuilder() =>
|
||||
new() { Cost = Price, Volume = Volume };
|
||||
|
||||
public LadderPriceLevel With(double price = double.NaN, double volume = double.NaN, double notionalValue = double.NaN) =>
|
||||
new(price.if_nan(Price), volume.if_nan(Volume), notionalValue.if_nan(NotionalValue));
|
||||
|
||||
public bool Equals(LadderPriceLevel other) =>
|
||||
MathUtils.epsilon_eq_comparer.Equals(Price, other.Price)
|
||||
&& MathUtils.epsilon_eq_comparer.Equals(Volume, other.Volume);
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object obj) =>
|
||||
obj is LadderPriceLevel other && Equals(other);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
HashCode.Combine(
|
||||
MathUtils.epsilon_eq_comparer.GetHashCode(Price),
|
||||
MathUtils.epsilon_eq_comparer.GetHashCode(Volume));
|
||||
|
||||
public class Builder {
|
||||
double notionalValue = double.NaN;
|
||||
|
||||
public double Cost { get; set; } = double.NaN;
|
||||
public double Volume { get; set; } = 0;
|
||||
public double NotionalValue => notionalValue.is_nan() ? Volume * Cost : notionalValue;
|
||||
|
||||
public void Add(double price, double volume) {
|
||||
if (!(volume > 0)) {
|
||||
return;
|
||||
}
|
||||
Log.assert(price.is_real());
|
||||
Cost += price * volume;
|
||||
Volume += volume;
|
||||
}
|
||||
|
||||
public void Add(double price, double volume, double notionalValue) {
|
||||
if (!(volume > 0)) {
|
||||
return;
|
||||
}
|
||||
Log.assert(price.is_real());
|
||||
Cost += price * volume;
|
||||
Volume += volume;
|
||||
if (this.notionalValue.is_nan()) {
|
||||
this.notionalValue = notionalValue;
|
||||
} else{
|
||||
this.notionalValue += notionalValue;
|
||||
}
|
||||
}
|
||||
|
||||
public LadderPriceLevel ToImmutable() =>
|
||||
Volume > 0
|
||||
? new(Cost / Volume, Volume, NotionalValue)
|
||||
: Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
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) {
|
||||
return TickUtils.normalize_price(spec, impl());
|
||||
double impl() {
|
||||
if (!double.IsNaN(bid)) {
|
||||
return bid;
|
||||
}
|
||||
if (!double.IsNaN(ask)) {
|
||||
return _try_tick_down(spec, ask);
|
||||
}
|
||||
return settlement;
|
||||
}
|
||||
}
|
||||
|
||||
public static double ask_line(
|
||||
double bid, double ask, double settlement, RegionalTickFactors spec) {
|
||||
return TickUtils.normalize_price(spec, impl());
|
||||
double impl() {
|
||||
if (!double.IsNaN(ask)) {
|
||||
return _try_tick_down(spec, ask);
|
||||
}
|
||||
if (!double.IsNaN(bid)) {
|
||||
return bid;
|
||||
}
|
||||
return settlement;
|
||||
}
|
||||
}
|
||||
|
||||
public static double mid_line(
|
||||
double bid, double ask, double settlement, RegionalTickFactors spec) {
|
||||
return TickUtils.normalize_price(spec, impl());
|
||||
double impl() {
|
||||
if (double.IsNaN(bid)) {
|
||||
if (double.IsNaN(ask)) {
|
||||
return settlement;
|
||||
}
|
||||
return _try_tick_down(spec, ask);
|
||||
}
|
||||
if (double.IsNaN(ask)) {
|
||||
return bid;
|
||||
}
|
||||
if (spec is not null) {
|
||||
return TickUtils.mid_tick(spec, bid, ask);
|
||||
}
|
||||
return bid;
|
||||
}
|
||||
}
|
||||
|
||||
static double _try_tick_down(RegionalTickFactors spec, double value) {
|
||||
if (spec is not null) {
|
||||
return spec.tickdown(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,34 +16,33 @@ namespace mbts.spark {
|
||||
long _length;
|
||||
long _high_tick;
|
||||
SecurityImmutable? _security;
|
||||
RegionalTickFactors _regional_tick_backing;
|
||||
RegionalTickFactors _regional_tick =>
|
||||
_regional_tick_backing ??= TickUtils.get_regional_ticks(_security);
|
||||
|
||||
public LadderPriceList()
|
||||
: this(RegionalTickFactors.Default) { }
|
||||
|
||||
public LadderPriceList(RegionalTickFactors regional_tick) =>
|
||||
this.regional_tick = regional_tick;
|
||||
public LadderPriceList() { }
|
||||
|
||||
public LadderPriceList(LadderPriceList other) {
|
||||
_length = other._length;
|
||||
_high_tick = other._high_tick;
|
||||
_security = other._security;
|
||||
regional_tick = other.regional_tick;
|
||||
}
|
||||
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
RegionalTickFactors _regional_tick;
|
||||
public RegionalTickFactors regional_tick {
|
||||
get => _regional_tick;
|
||||
public SecurityImmutable? security {
|
||||
get => _security;
|
||||
set {
|
||||
if (value is null) {
|
||||
_regional_tick = RegionalTickFactors.Default;
|
||||
} else {
|
||||
_regional_tick = value;
|
||||
if (value == _security) {
|
||||
return;
|
||||
}
|
||||
_security = value;
|
||||
_regional_tick_backing = null;
|
||||
}
|
||||
}
|
||||
|
||||
public ContractSpecificationImmutable spec { get; set; }
|
||||
|
||||
public long high_tick {
|
||||
get => _high_tick;
|
||||
set => _high_tick = value;
|
||||
@@ -55,8 +54,8 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public double start {
|
||||
get => regional_tick.tickup<double>(0, (uint)high_tick);
|
||||
set => high_tick = regional_tick.tickdiff(value, 0) ?? high_tick;
|
||||
get => _regional_tick.tickup<double>(0, (uint)high_tick);
|
||||
set => high_tick = _regional_tick.tickdiff(value, 0) ?? high_tick;
|
||||
}
|
||||
|
||||
public long length {
|
||||
@@ -81,7 +80,7 @@ namespace mbts.spark {
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public double this[int idx] {
|
||||
get => regional_tick.tick<double>(0, high_tick - idx);
|
||||
get => _regional_tick.tick<double>(0, high_tick - idx);
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
@@ -95,7 +94,7 @@ namespace mbts.spark {
|
||||
GetEnumerator();
|
||||
|
||||
public int IndexOf(double item) {
|
||||
var idx = regional_tick.tickdiff(start, item) ?? -1;
|
||||
var idx = _regional_tick.tickdiff(start, item) ?? -1;
|
||||
if (idx < 0 || idx >= length) {
|
||||
return -1;
|
||||
}
|
||||
@@ -210,7 +209,7 @@ namespace mbts.spark {
|
||||
other != null &&
|
||||
_high_tick == other._high_tick &&
|
||||
_length == other._length &&
|
||||
regional_tick.safe_equals(other.regional_tick);
|
||||
_regional_tick.safe_equals(other._regional_tick);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as LadderPriceList);
|
||||
@@ -219,7 +218,7 @@ namespace mbts.spark {
|
||||
HashCode.Combine(_high_tick, _length);
|
||||
|
||||
double _tick_to_price(long tick) =>
|
||||
regional_tick.tick<double>(0, tick);
|
||||
_regional_tick.tick<double>(0, tick);
|
||||
|
||||
void _add_tick(long tick) {
|
||||
int idx;
|
||||
|
||||
@@ -31,19 +31,19 @@ namespace mbts.spark {
|
||||
Side side,
|
||||
double quantity,
|
||||
double filled,
|
||||
double price,
|
||||
LadderSettings.BadgeStyle style,
|
||||
Func<double, double, string> fmt_value = null
|
||||
Func<double, string> fmt_value = null
|
||||
) {
|
||||
fmt_value ??= (v, p) => v.ToString();
|
||||
if (fmt_value is null)
|
||||
fmt_value = FormattingDelegates.default_string<double>();
|
||||
switch (style) {
|
||||
case LadderSettings.BadgeStyle.Alternate:
|
||||
var working_qty = quantity > filled ? quantity - filled : 0;
|
||||
return new[] {
|
||||
$"{side.abbreviate()}:{fmt_value(filled, price)}",
|
||||
$"W:{fmt_value(working_qty, price)}"
|
||||
$"{side.abbreviate()}:{fmt_value(filled)}",
|
||||
$"W:{fmt_value(working_qty)}"
|
||||
};
|
||||
default: return new[] { $"{fmt_value(filled, price)}/{fmt_value(quantity, price)}" };
|
||||
default: return new[] { $"{fmt_value(filled)}/{fmt_value(quantity)}" };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,8 @@ namespace mbts.spark {
|
||||
static class LadderRender {
|
||||
// Note: the orders-column has a separate "row_padding" value: Badge.DefaultMargin
|
||||
// (see how it is incorporated in measure_row_height() below)
|
||||
public static Thickness row_padding => new Thickness(4, 2, 4, 2);
|
||||
public static Thickness row_padding => LadderSettings.mimic_tt ?
|
||||
new Thickness(4, 2, 4, 2) : new Thickness(6);
|
||||
|
||||
const string tick_separator_period = ".";
|
||||
const string tick_separator_dash = "-";
|
||||
@@ -78,6 +79,13 @@ 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) {
|
||||
@@ -101,64 +109,17 @@ 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,
|
||||
LadderSettings.NetChangeTerms terms,
|
||||
double row_height,
|
||||
LadderColumnSettings settings,
|
||||
GlyphContext glyph_context,
|
||||
RegionalTickFactors spec,
|
||||
LadderSettings.PriceDisplayFormat price_display_format = LadderSettings.PriceDisplayFormat.Default
|
||||
) {
|
||||
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) {
|
||||
var result = new GlyphRun[prices.Count];
|
||||
string basis_pts_fmt = terms switch {
|
||||
LadderSettings.NetChangeTerms.BasisPoints or
|
||||
LadderSettings.NetChangeTerms.NetBasisPoints => get_basis_pts_fmt(),
|
||||
_ => null
|
||||
};
|
||||
Parallel.For(0, prices.Count, i =>
|
||||
result[i] = format_text(fmt(i), i, row_height, settings, glyph_context, price_display_format));
|
||||
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) {
|
||||
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));
|
||||
return result;
|
||||
|
||||
string fmt(int i) {
|
||||
switch (terms) {
|
||||
case LadderSettings.NetChangeTerms.Dollars:
|
||||
return prices[i].ToString("0.00");
|
||||
case LadderSettings.NetChangeTerms.BasisPoints: {
|
||||
if (!reference_price.is_real_non_zero()) {
|
||||
return spec.format(double.NaN);
|
||||
}
|
||||
var basis_pts = prices[i] * 10000.0 / reference_price;
|
||||
return basis_pts.ToString(basis_pts_fmt);
|
||||
}
|
||||
case LadderSettings.NetChangeTerms.NetBasisPoints: {
|
||||
if (!reference_price.is_real_non_zero()) {
|
||||
return spec.format(double.NaN);
|
||||
}
|
||||
var net_basis_pts = prices[i] * 10000.0 / reference_price - 10000.0;
|
||||
return net_basis_pts.ToString(basis_pts_fmt);
|
||||
}
|
||||
default:
|
||||
return spec.format(prices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
string get_basis_pts_fmt() {
|
||||
if (!reference_price.is_real_non_zero()) {
|
||||
return "0.000";
|
||||
}
|
||||
var tick_sz = spec.ticksize(reference_price);
|
||||
var basis_pt = reference_price.abs() / 10000.0;
|
||||
var precision_log10 = Math.Log10(tick_sz / basis_pt);
|
||||
int n = 0;
|
||||
if (precision_log10 < 0) {
|
||||
n = (int)Math.Ceiling(-precision_log10);
|
||||
}
|
||||
return n < 1
|
||||
? "0"
|
||||
: "0." + new string('0', n);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear_prices(DrawingVisual price, int row_count, double row_height, LadderColumnSettings settings, GlyphContext glyph_context) {
|
||||
@@ -174,20 +135,51 @@ namespace mbts.spark {
|
||||
public static void render_prices(
|
||||
DrawingVisual price,
|
||||
GlyphContext glyph_context,
|
||||
IList<double> dprices,
|
||||
IList<double> price_list,
|
||||
double wrt_price,
|
||||
LadderSettings.NetChangeTerms terms,
|
||||
bool format_in_tick_terms,
|
||||
double row_height,
|
||||
LadderColumnSettings settings,
|
||||
RegionalTickFactors tick) {
|
||||
if (price_list.IsNullOrEmpty()) {
|
||||
return;
|
||||
}
|
||||
render_prices(
|
||||
price, glyph_context, price_list, row_height, settings, tick,
|
||||
wrt_price, format_in_tick_terms);
|
||||
}
|
||||
|
||||
public static void render_prices(
|
||||
DrawingVisual price,
|
||||
GlyphContext glyph_context,
|
||||
IList<double> dprices,
|
||||
double row_height,
|
||||
LadderColumnSettings settings,
|
||||
RegionalTickFactors tick,
|
||||
LadderSettings.PriceDisplayFormat price_display_format = LadderSettings.PriceDisplayFormat.Default) {
|
||||
if (dprices.IsNullOrEmpty()) {
|
||||
return;
|
||||
}
|
||||
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, terms, row_height, settings, glyph_context, tick, price_display_format);
|
||||
var fprices = format_prices(dprices, wrt_price, format_in_tick_terms, row_height, settings, glyph_context, tick);
|
||||
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));
|
||||
for (int x = 0; x < dprices.Count; ++x)
|
||||
dc.DrawGlyphRun(foreground, fprices[x]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void render_prices(
|
||||
DrawingVisual price,
|
||||
GlyphContext glyph_context,
|
||||
IList<double> dprices,
|
||||
double row_height,
|
||||
LadderColumnSettings settings,
|
||||
RegionalTickFactors tick,
|
||||
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);
|
||||
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));
|
||||
@@ -213,78 +205,69 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public static void RenderSizes(
|
||||
DrawingVisual dv,
|
||||
GlyphContext glyphContext,
|
||||
int originIndex,
|
||||
IList<double> prices,
|
||||
IEnumerable<LadderPriceLevel> levels,
|
||||
double rowHeight,
|
||||
RegionalTickFactors spec,
|
||||
LadderColumnSettings settings,
|
||||
Func<LadderPriceLevel, string> fmt) {
|
||||
var bg = settings.background.rgb_color().blend((BTSColor)0x2AFFFFFF).to_brush();
|
||||
using (var dc = dv.RenderOpen())
|
||||
using (_draw_with_clip(dc, settings, rowHeight, prices.Count))
|
||||
using (var levelsEn = (levels ?? Enumerable.Empty<LadderPriceLevel>()).GetEnumerator()) {
|
||||
var fg = settings.foreground.rgb_brush();
|
||||
var currIdx = originIndex;
|
||||
var currPrice = prices[currIdx];
|
||||
while (levelsEn.MoveNext()) {
|
||||
var level = levelsEn.Current;
|
||||
var ticks = spec.count_ticks(level.Price);
|
||||
var price = ticks.HasValue ? spec.tickup(0, ticks.Value) : level.Price;
|
||||
// prices are in descending order, so get diff from curr_price - price
|
||||
var tickDiff = spec.tickdiff(currPrice, price).assert_value();
|
||||
if (!MathUtils.try_increment(ref currIdx, tickDiff) || currIdx < 0 || currIdx >= prices.Count) {
|
||||
continue;
|
||||
}
|
||||
currPrice = price;
|
||||
if (bg is not null) {
|
||||
var y = currIdx * rowHeight + GRID_LINE_HEIGHT;
|
||||
var height = rowHeight - GRID_LINE_HEIGHT;
|
||||
dc.DrawRectangle(bg, null, new Rect(0, y, settings.width, height));
|
||||
}
|
||||
var gr = format_text(fmt(level), currIdx, rowHeight, settings, glyphContext);
|
||||
dc.DrawGlyphRun(fg, gr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<LadderPriceLevel> GetLevels(IEnumerable<(double Price, double Volume, double NotionalValue)> depth, RegionalTickFactors spec) =>
|
||||
depth.TakeWhile(nps => nps.Volume > 0 && nps.Price.is_real())
|
||||
.AggregateConsecutiveGroups(
|
||||
LadderPriceLevel.Empty.ToBuilder,
|
||||
(bd, rung) => {
|
||||
bd.Add(rung.Price, rung.Volume, rung.NotionalValue);
|
||||
return bd;
|
||||
},
|
||||
rung => {
|
||||
var ticks = spec.count_ticks(rung.Price);
|
||||
return ticks.HasValue ? spec.tickup(0, ticks.Value) : rung.Price;
|
||||
})
|
||||
.Select(bd => bd.ToImmutable());
|
||||
|
||||
public static void render_sizes(
|
||||
DrawingVisual dv,
|
||||
GlyphContext glyph_context,
|
||||
int origin_index,
|
||||
IList<double> prices,
|
||||
IEnumerable<(double Price, double Volume, double NotionalValue)> rungs,
|
||||
NativePriceSize[] rungs,
|
||||
double row_height,
|
||||
RegionalTickFactors spec,
|
||||
LadderColumnSettings settings,
|
||||
Func<LadderPriceLevel, string> fmt) {
|
||||
RenderSizes(
|
||||
dv,
|
||||
glyph_context,
|
||||
origin_index,
|
||||
prices,
|
||||
GetLevels(rungs, spec),
|
||||
row_height,
|
||||
spec,
|
||||
settings,
|
||||
fmt);
|
||||
Func<NativePriceSize, string> fmt,
|
||||
bool lighten_market_cells,
|
||||
Color? market_cell_background) {
|
||||
//var background = new SolidColorBrush(settings.background.rgb_color());
|
||||
Brush market_cell_background_brush;
|
||||
if (market_cell_background.HasValue) {
|
||||
market_cell_background_brush = new SolidColorBrush(market_cell_background.Value);
|
||||
} else if (lighten_market_cells) {
|
||||
market_cell_background_brush = settings.background.rgb_color()
|
||||
.blend((BTSColor)0x2AFFFFFF)
|
||||
.to_brush();
|
||||
} else {
|
||||
market_cell_background_brush = null;
|
||||
}
|
||||
// market_cell_background_brush = settings.background.rgb_color().blend((BTSColor)0xFF000000).to_brush();
|
||||
using (var dc = dv.RenderOpen())
|
||||
using (_draw_with_clip(dc, settings, row_height, prices.Count)) {
|
||||
//dc.DrawRectangle(background, null, new Rect(0, 0, settings.width, prices.Count * row_height));
|
||||
if (!prices.within_range(origin_index) || rungs.is_null_or_empty())
|
||||
return;
|
||||
var foreground = settings.foreground.rgb_brush();
|
||||
int curr_index = origin_index;
|
||||
double curr_price = prices[curr_index];
|
||||
NativePriceSize curr_size = NativePriceSize.NO_MARKET;
|
||||
var row_tick_groups = rungs
|
||||
.TakeWhile(nps => !nps.is_nan)
|
||||
.AggregateConsecutiveGroups(() => NativePriceSize.NO_MARKET,
|
||||
(aggr, rung) => rung.with(
|
||||
direct_size: aggr.direct_size + rung.direct_size,
|
||||
implied_size: aggr.implied_size + rung.implied_size),
|
||||
rung => {
|
||||
var ticks = spec.count_ticks(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 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;
|
||||
}
|
||||
curr_price = price;
|
||||
if (curr_index >= 0 && curr_index < prices.Count) {
|
||||
if (market_cell_background_brush != null)
|
||||
dc.DrawRectangle(market_cell_background_brush, null,
|
||||
new Rect(0, curr_index * row_height + GRID_LINE_HEIGHT,
|
||||
settings.width, row_height - GRID_LINE_HEIGHT));
|
||||
var gr = format_text(fmt(rung), curr_index, row_height, settings, glyph_context);
|
||||
dc.DrawGlyphRun(foreground, gr);
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void render_volumes(
|
||||
@@ -297,7 +280,7 @@ namespace mbts.spark {
|
||||
VolumeColumnSettings settings,
|
||||
int last_index,
|
||||
string last_size_and_direction,
|
||||
Func<double, double, string> fmt_value) {
|
||||
Func<double, string> fmt_value) {
|
||||
//
|
||||
if (!data.Any()) {
|
||||
_clear_visual(dv);
|
||||
@@ -320,12 +303,12 @@ namespace mbts.spark {
|
||||
_clear_visual(dv);
|
||||
return;
|
||||
}
|
||||
fmt_value ??= (v, p) => v.ToString();
|
||||
fmt_value ??= FormattingDelegates.default_string<double>();
|
||||
// inflate the max so that the graph never fills the available space
|
||||
double max = data.Max(v => v.Volume) * 1.1;
|
||||
_render_cells(dv, row_height, data.Select(d => (d.Price, d)), settings.general, prices, spec, render_cell);
|
||||
/// local methods
|
||||
void render_cell(DrawingContext dc, int r, double price, VolumeAtPriceLevelImmutable vap) {
|
||||
void render_cell(DrawingContext dc, int r, VolumeAtPriceLevelImmutable vap) {
|
||||
Brush text_brush = settings.general.foreground.rgb_brush();
|
||||
double y = row_height * r;
|
||||
var last_trade_here = r == last_index;
|
||||
@@ -352,11 +335,11 @@ namespace mbts.spark {
|
||||
}
|
||||
x_bar += width;
|
||||
if (show_numbers)
|
||||
numbers_text.Add(fmt_value(v, vap.Price));
|
||||
numbers_text.Add(fmt_value(v));
|
||||
}
|
||||
|
||||
if (settings.total.show_numbers) {
|
||||
numbers_text.Add(fmt_value(vap.Volume, vap.Price));
|
||||
numbers_text.Add(fmt_value(vap.Volume));
|
||||
}
|
||||
if (settings.show_ltq && last_trade_here) {
|
||||
numbers_text.Clear();
|
||||
@@ -383,17 +366,9 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void RenderCell<T>(DrawingContext dc, int row, double price, T data_item);
|
||||
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)> cells,
|
||||
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)> cells, LadderColumnSettings settings, IList<double> prices, RegionalTickFactors spec, RenderCell<T> render_cell) {
|
||||
using (var dc = dv.RenderOpen())
|
||||
using (_draw_with_clip(dc, settings, row_height, prices.Count)) {
|
||||
if (prices.is_null_or_empty() || cells.is_null_or_empty()) {
|
||||
@@ -413,7 +388,7 @@ namespace mbts.spark {
|
||||
var check_price = spec.tickdown(max_price, idx);
|
||||
Log.assert(spec.tick_equals(price, check_price));
|
||||
Log.assert(idx >= 0 && idx < prices.Count);
|
||||
render_cell(dc, idx, price, value);
|
||||
render_cell(dc, idx, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -425,10 +400,10 @@ namespace mbts.spark {
|
||||
int index,
|
||||
double row_height,
|
||||
LadderColumnSettings settings,
|
||||
Func<NativePriceSize, string> fmt_value) {
|
||||
fmt_value ??= nps => nps.size.ToString();
|
||||
Func<double, string> fmt_value) {
|
||||
fmt_value ??= FormattingDelegates.default_string<double>();
|
||||
using (var dc = dv.RenderOpen()) {
|
||||
var gr = format_text(fmt_value(last), index, row_height, settings, glyph_context);
|
||||
var gr = format_text(fmt_value(last.size), index, row_height, settings, glyph_context);
|
||||
dc.DrawGlyphRun(settings.foreground.rgb_brush(), gr);
|
||||
}
|
||||
}
|
||||
@@ -442,13 +417,13 @@ namespace mbts.spark {
|
||||
double row_height,
|
||||
RegionalTickFactors spec,
|
||||
LadderColumnSettings settings,
|
||||
Func<double, double, string> fmt_value
|
||||
Func<double, string> fmt_value
|
||||
) {
|
||||
fmt_value ??= (v, p) => v.ToString();
|
||||
fmt_value ??= FormattingDelegates.default_string<double>();
|
||||
var fg = settings.foreground.rgb_brush();
|
||||
_render_cells(dv, row_height, pvp, settings, prices, spec, (dc, row, price, piq) =>
|
||||
_render_cells(dv, row_height, pvp, settings, prices, spec, (dc, row, piq) =>
|
||||
dc.DrawGlyphRun(
|
||||
fg, format_text(fmt_value(piq, price), row, row_height, settings, glyph_context)));
|
||||
fg, format_text(fmt_value(piq), row, row_height, settings, glyph_context)));
|
||||
}
|
||||
|
||||
public static void render_mkt_break(DrawingVisual dv, int index, double row_height, uint color) =>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -39,9 +39,6 @@
|
||||
|
||||
<cv:EnumCharacterCasingConverter x:Key="casing_converter"/>
|
||||
<cv:GridLengthConverter x:Key="GridLengthPixelConverter" UnitType="Pixel" />
|
||||
<cv:ResponsiveTextConverter x:Key="BidPriceTextConverter" LongText="Bid:" ShortText="B:" Threshold="99" />
|
||||
<cv:ResponsiveTextConverter x:Key="AskPriceTextConverter" LongText="Ask:" ShortText="A:" Threshold="99" />
|
||||
<cv:ResponsiveTextConverter x:Key="MidPriceTextConverter" LongText="Mid:" ShortText="M:" Threshold="99" />
|
||||
<cv:ResponsiveTextConverter x:Key="DayTextConverter" LongText="Day:" ShortText="D:" Threshold="99" />
|
||||
<cv:ResponsiveTextConverter x:Key="HighTextConverter" LongText="High:" ShortText="H:" Threshold="99" />
|
||||
<cv:ResponsiveTextConverter x:Key="LowTextConverter" LongText="Low:" ShortText="L:" Threshold="99" />
|
||||
@@ -182,7 +179,7 @@
|
||||
</Style>
|
||||
|
||||
<Style x:Key="CancelAll" TargetType="Button" BasedOn="{StaticResource LadderMenuButton}">
|
||||
<Setter Property="Grid.Row" Value="0"/>
|
||||
<Setter Property="Grid.Row" Value="2" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
<TextBlock VerticalAlignment="Center">Cancel All</TextBlock>
|
||||
@@ -220,7 +217,10 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="CancelBuys" TargetType="Button" BasedOn="{StaticResource CancelAll}">
|
||||
<Style x:Key="TT_CancelAll" TargetType="Button" BasedOn="{StaticResource CancelAll}">
|
||||
<Setter Property="Grid.Row" Value="0"/>
|
||||
</Style>
|
||||
<Style x:Key="TT_CancelBuys" TargetType="Button" BasedOn="{StaticResource CancelAll}">
|
||||
<Setter Property="Grid.Row" Value="1" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
@@ -228,7 +228,7 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="CancelSells" TargetType="Button" BasedOn="{StaticResource CancelAll}">
|
||||
<Style x:Key="TT_CancelSells" TargetType="Button" BasedOn="{StaticResource CancelAll}">
|
||||
<Setter Property="Grid.Row" Value="2" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
@@ -236,6 +236,83 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="CancelBuys" TargetType="Button" BasedOn="{StaticResource LadderMenuButton}">
|
||||
<Setter Property="Grid.Row" Value="0" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
<TextBlock VerticalAlignment="Center" Foreground="{Binding bid_foreground}">
|
||||
Cancel Buys
|
||||
</TextBlock>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
BorderBrush="{u:BTSColor UI.Button.normal_border}"
|
||||
Background="{Binding bid_background}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
RecognizesAccessKey="True"
|
||||
Name="button_content" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="#99167ed3" />
|
||||
<Setter
|
||||
TargetName="Border"
|
||||
Property="BorderBrush"
|
||||
Value="{u:BTSColor UI.Button.mouseover_border}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="CancelSells" TargetType="Button" BasedOn="{StaticResource LadderMenuButton}">
|
||||
<Setter Property="Background" Value="{u:BTSColor MarketData.ask}" />
|
||||
<Setter Property="Grid.Row" Value="1" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
<TextBlock VerticalAlignment="Center" Foreground="{Binding ask_foreground}">
|
||||
Cancel Sells
|
||||
</TextBlock>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
BorderBrush="{u:BTSColor UI.Button.normal_border}"
|
||||
Background="{Binding ask_background}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
SnapsToDevicePixels="True" >
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
RecognizesAccessKey="True"
|
||||
Name="button_content" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="#99c00000" />
|
||||
<Setter
|
||||
TargetName="Border"
|
||||
Property="BorderBrush"
|
||||
Value="{u:BTSColor UI.Button.mouseover_border}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="SettingsButton" TargetType="Button" BasedOn="{StaticResource LadderMenuButton}">
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
@@ -286,103 +363,13 @@
|
||||
<Setter Property="Grid.Column" Value="2"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="Margin" Value="0,0,0,2"/>
|
||||
<Setter Property="Padding" Value="4,1"/>
|
||||
<Setter Property="Padding" Value="1"/>
|
||||
</Style>
|
||||
<Style x:Key="LeftPanelPricePointText" TargetType="TextBlock">
|
||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.default_text}"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="LeftPanelPricePointButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Grid.Column" Value="2" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right" />
|
||||
<Setter Property="Margin" Value="0,0,0,2" />
|
||||
<Setter Property="Padding" Value="4,1" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="0"
|
||||
Height="Auto"
|
||||
Padding="0"
|
||||
SnapsToDevicePixels="True">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="CommonStates">
|
||||
<VisualState Name="Normal" />
|
||||
|
||||
<VisualState Name="MouseOver">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="HoverRect" Storyboard.TargetProperty="Fill.Opacity" BeginTime="0" Duration="00:00:00.15" To="0.20">
|
||||
<DoubleAnimation.EasingFunction>
|
||||
<QuadraticEase />
|
||||
</DoubleAnimation.EasingFunction>
|
||||
</DoubleAnimation>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{u:BTSBrush UI.Button.mouseover_border}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{u:BTSBrush UI.Button.pressed_background}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{u:BTSBrush UI.Button.pressed_border}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{u:BTSBrush UI.Button.disabled_background}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{u:BTSBrush UI.Button.disabled_border}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup Name="FocusStates">
|
||||
<VisualState Name="Unfocused" />
|
||||
|
||||
<VisualState Name="Focused">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{u:BTSBrush UI.Button.focused_border}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Rectangle Name="HoverRect" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsHitTestVisible="False">
|
||||
<Rectangle.Fill>
|
||||
<SolidColorBrush Color="White" Opacity="0" />
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<ContentPresenter
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
RecognizesAccessKey="True" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<sys:Double x:Key="MaxZoomFactor">1.5</sys:Double>
|
||||
<sys:Double x:Key="MinZoomFactor">0.5</sys:Double>
|
||||
</ResourceDictionary>
|
||||
@@ -447,9 +434,6 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
@@ -457,123 +441,9 @@
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Ask Price (label) -->
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource AskPriceTextConverter}}"
|
||||
Visibility="{Binding
|
||||
ask_price_style.actual_show_label,
|
||||
Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Style="{StaticResource LeftPanelPricePointLabel}" />
|
||||
|
||||
<!-- Ask Price (value) -->
|
||||
<Button
|
||||
Grid.Row="0"
|
||||
Visibility="{Binding
|
||||
ask_price_style.show, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
ToolTip="Ask"
|
||||
Command="{Binding go_to_price_cmd}"
|
||||
CommandParameter="{Binding ask_price}"
|
||||
Style="{StaticResource LeftPanelPricePointButton}">
|
||||
<Button.Background>
|
||||
<SolidColorBrush Color="{Binding ask_price_style.background_color}" Opacity="1" />
|
||||
</Button.Background>
|
||||
<TextBlock
|
||||
FontFamily="{Binding ask_price_style.font_family}"
|
||||
FontSize="{Binding ask_price_style.font_size}"
|
||||
FontWeight="{Binding ask_price_style.font_weight}"
|
||||
Foreground="{Binding
|
||||
ask_price_style.font_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Style="{StaticResource LeftPanelPricePointText}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="ask_price"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
|
||||
<!-- Mid Price (label) -->
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource MidPriceTextConverter}}"
|
||||
Visibility="{Binding
|
||||
mid_price_style.actual_show_label,
|
||||
Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Style="{StaticResource LeftPanelPricePointLabel}" />
|
||||
|
||||
<!-- Mid Price (value) -->
|
||||
<Button
|
||||
Grid.Row="1"
|
||||
Command="{Binding go_to_price_cmd}"
|
||||
CommandParameter="{Binding mid_price}"
|
||||
Visibility="{Binding
|
||||
mid_price_style.show, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
ToolTip="Mid"
|
||||
Style="{StaticResource LeftPanelPricePointButton}">
|
||||
<Button.Background>
|
||||
<SolidColorBrush Color="{Binding mid_price_style.background_color}" Opacity="1" />
|
||||
</Button.Background>
|
||||
<TextBlock
|
||||
FontFamily="{Binding mid_price_style.font_family}"
|
||||
FontSize="{Binding mid_price_style.font_size}"
|
||||
FontWeight="{Binding mid_price_style.font_weight}"
|
||||
Foreground="{Binding
|
||||
mid_price_style.font_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Style="{StaticResource LeftPanelPricePointText}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="mid_price"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
|
||||
<!-- Bid Price (label) -->
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource BidPriceTextConverter}}"
|
||||
Visibility="{Binding
|
||||
bid_price_style.actual_show_label,
|
||||
Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Style="{StaticResource LeftPanelPricePointLabel}" />
|
||||
|
||||
<!-- Bid Price (value) -->
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Command="{Binding go_to_price_cmd}"
|
||||
CommandParameter="{Binding bid_price}"
|
||||
Visibility="{Binding
|
||||
bid_price_style.show, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
ToolTip="Bid"
|
||||
Style="{StaticResource LeftPanelPricePointButton}">
|
||||
<Button.Background>
|
||||
<SolidColorBrush Color="{Binding bid_price_style.background_color}" Opacity="1" />
|
||||
</Button.Background>
|
||||
<TextBlock
|
||||
FontFamily="{Binding bid_price_style.font_family}"
|
||||
FontSize="{Binding bid_price_style.font_size}"
|
||||
FontWeight="{Binding bid_price_style.font_weight}"
|
||||
Foreground="{Binding
|
||||
bid_price_style.font_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Style="{StaticResource LeftPanelPricePointText}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="bid_price"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
|
||||
<!-- Day Change (label) -->
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Row="0"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource DayTextConverter}}"
|
||||
Visibility="{Binding
|
||||
day_change_style.actual_show_label,
|
||||
@@ -582,7 +452,7 @@
|
||||
|
||||
<!-- Day Change (value) -->
|
||||
<Border
|
||||
Grid.Row="3"
|
||||
Grid.Row="0"
|
||||
Background="{Binding
|
||||
day_change_background_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
@@ -609,7 +479,7 @@
|
||||
|
||||
<!-- High (label) -->
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Row="1"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource HighTextConverter}}"
|
||||
Visibility="{Binding
|
||||
session_high_style.actual_show_label,
|
||||
@@ -617,17 +487,15 @@
|
||||
Style="{StaticResource LeftPanelPricePointLabel}" />
|
||||
|
||||
<!-- High (value) -->
|
||||
<Button
|
||||
Grid.Row="4"
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Background="{Binding
|
||||
session_high_style.background_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Command="{Binding go_to_price_cmd}"
|
||||
CommandParameter="{Binding session_high}"
|
||||
Visibility="{Binding
|
||||
session_high_style.show, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
ToolTip="Session High"
|
||||
Style="{StaticResource LeftPanelPricePointButton}">
|
||||
Style="{StaticResource LeftPanelPricePointBorder}">
|
||||
<TextBlock
|
||||
FontFamily="{Binding session_high_style.font_family}"
|
||||
FontSize="{Binding session_high_style.font_size}"
|
||||
@@ -636,19 +504,24 @@
|
||||
session_high_style.font_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Style="{StaticResource LeftPanelPricePointText}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding
|
||||
Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="session_high"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
<Hyperlink
|
||||
Command="{Binding go_to_price_cmd}" CommandParameter="{Binding session_high}">
|
||||
<TextBlock>
|
||||
<TextBlock.Text>
|
||||
<MultiBinding
|
||||
Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="session_high"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
</Border>
|
||||
|
||||
<!-- Low (label) -->
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Row="2"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource LowTextConverter}}"
|
||||
Visibility="{Binding
|
||||
session_low_style.actual_show_label,
|
||||
@@ -656,17 +529,15 @@
|
||||
Style="{StaticResource LeftPanelPricePointLabel}" />
|
||||
|
||||
<!-- Low (value) -->
|
||||
<Button
|
||||
Grid.Row="5"
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Background="{Binding
|
||||
session_low_style.background_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Command="{Binding go_to_price_cmd}"
|
||||
CommandParameter="{Binding session_low}"
|
||||
Visibility="{Binding
|
||||
session_low_style.show, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
ToolTip="Session Low"
|
||||
Style="{StaticResource LeftPanelPricePointButton}">
|
||||
Style="{StaticResource LeftPanelPricePointBorder}">
|
||||
<TextBlock
|
||||
FontFamily="{Binding session_low_style.font_family}"
|
||||
FontSize="{Binding session_low_style.font_size}"
|
||||
@@ -675,19 +546,24 @@
|
||||
session_low_style.font_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Style="{StaticResource LeftPanelPricePointText}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding
|
||||
Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="session_low"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
<Hyperlink
|
||||
Command="{Binding go_to_price_cmd}" CommandParameter="{Binding session_low}">
|
||||
<TextBlock>
|
||||
<TextBlock.Text>
|
||||
<MultiBinding
|
||||
Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="session_low"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
</Border>
|
||||
|
||||
<!-- Open (label) -->
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Row="3"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource OpenTextConverter}}"
|
||||
Visibility="{Binding
|
||||
open_style.actual_show_label,
|
||||
@@ -695,17 +571,15 @@
|
||||
Style="{StaticResource LeftPanelPricePointLabel}" />
|
||||
|
||||
<!-- Open (value) -->
|
||||
<Button
|
||||
Grid.Row="6"
|
||||
<Border
|
||||
Grid.Row="3"
|
||||
Background="{Binding
|
||||
open_style.background_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Command="{Binding go_to_price_cmd}"
|
||||
CommandParameter="{Binding open}"
|
||||
Visibility="{Binding
|
||||
open_style.show, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
ToolTip="Open"
|
||||
Style="{StaticResource LeftPanelPricePointButton}">
|
||||
Style="{StaticResource LeftPanelPricePointBorder}">
|
||||
<TextBlock
|
||||
FontFamily="{Binding open_style.font_family}"
|
||||
FontSize="{Binding open_style.font_size}"
|
||||
@@ -713,19 +587,23 @@
|
||||
Foreground="{Binding
|
||||
open_style.font_color, Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Style="{StaticResource LeftPanelPricePointText}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding
|
||||
Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="open"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
<Hyperlink Command="{Binding go_to_price_cmd}" CommandParameter="{Binding open}">
|
||||
<TextBlock>
|
||||
<TextBlock.Text>
|
||||
<MultiBinding
|
||||
Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="open"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
</Border>
|
||||
|
||||
<!-- Settlement (label) -->
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Row="4"
|
||||
Text="{Binding
|
||||
left_panel_width, Converter={StaticResource SettlementTextConverter}}"
|
||||
Visibility="{Binding
|
||||
@@ -734,17 +612,15 @@
|
||||
Style="{StaticResource LeftPanelPricePointLabel}" />
|
||||
|
||||
<!-- Settlement (value) -->
|
||||
<Button
|
||||
Grid.Row="7"
|
||||
<Border
|
||||
Grid.Row="4"
|
||||
Background="{Binding
|
||||
settlement_style.background_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Command="{Binding go_to_price_cmd}"
|
||||
CommandParameter="{Binding settlement}"
|
||||
Visibility="{Binding
|
||||
settlement_style.show, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
ToolTip="Settlement"
|
||||
Style="{StaticResource LeftPanelPricePointButton}">
|
||||
Style="{StaticResource LeftPanelPricePointBorder}">
|
||||
<TextBlock
|
||||
FontFamily="{Binding settlement_style.font_family}"
|
||||
FontSize="{Binding settlement_style.font_size}"
|
||||
@@ -753,18 +629,23 @@
|
||||
settlement_style.font_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Style="{StaticResource LeftPanelPricePointText}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="settlement"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
<Hyperlink
|
||||
Command="{Binding go_to_price_cmd}" CommandParameter="{Binding settlement}">
|
||||
<TextBlock>
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="settlement"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
</Border>
|
||||
|
||||
<!-- Theo (label) -->
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Grid.Row="5"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource TheoTextConverter}}"
|
||||
Visibility="{Binding
|
||||
theo_style.actual_show_label,
|
||||
@@ -772,17 +653,15 @@
|
||||
Style="{StaticResource LeftPanelPricePointLabel}" />
|
||||
|
||||
<!-- Theo (value) -->
|
||||
<Button
|
||||
Grid.Row="8"
|
||||
<Border
|
||||
Grid.Row="5"
|
||||
Background="{Binding
|
||||
theo_style.background_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Command="{Binding go_to_price_cmd}"
|
||||
CommandParameter="{Binding theo}"
|
||||
Visibility="{Binding
|
||||
theo_style.show, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
ToolTip="Theo"
|
||||
Style="{StaticResource LeftPanelPricePointButton}">
|
||||
Style="{StaticResource LeftPanelPricePointBorder}">
|
||||
<TextBlock
|
||||
FontFamily="{Binding theo_style.font_family}"
|
||||
FontSize="{Binding theo_style.font_size}"
|
||||
@@ -790,19 +669,23 @@
|
||||
Foreground="{Binding
|
||||
theo_style.font_color, Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
Style="{StaticResource LeftPanelPricePointText}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding
|
||||
Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="theo"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
<Hyperlink Command="{Binding go_to_price_cmd}" CommandParameter="{Binding theo}">
|
||||
<TextBlock>
|
||||
<TextBlock.Text>
|
||||
<MultiBinding
|
||||
Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="theo"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
</Border>
|
||||
|
||||
<!-- Delta -->
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Grid.Row="6"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource DeltaTextConverter}}"
|
||||
Visibility="{Binding
|
||||
delta_style.actual_show_label,
|
||||
@@ -810,7 +693,7 @@
|
||||
Style="{StaticResource LeftPanelPricePointLabel}" />
|
||||
|
||||
<Border
|
||||
Grid.Row="9"
|
||||
Grid.Row="6"
|
||||
Background="{Binding
|
||||
delta_style.background_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
@@ -831,7 +714,7 @@
|
||||
|
||||
<!-- Vega -->
|
||||
<TextBlock
|
||||
Grid.Row="10"
|
||||
Grid.Row="7"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource VegaTextConverter}}"
|
||||
Visibility="{Binding
|
||||
vega_style.actual_show_label,
|
||||
@@ -839,7 +722,7 @@
|
||||
Style="{StaticResource LeftPanelPricePointLabel}" />
|
||||
|
||||
<Border
|
||||
Grid.Row="10"
|
||||
Grid.Row="7"
|
||||
Background="{Binding
|
||||
vega_style.background_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
@@ -860,7 +743,7 @@
|
||||
<!-- Volume (label) -->
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Grid.Row="11"
|
||||
Grid.Row="8"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource VolumeTextConverter}}"
|
||||
Visibility="{Binding
|
||||
volume_style.actual_show_label,
|
||||
@@ -869,7 +752,7 @@
|
||||
|
||||
<!-- Volume (price) -->
|
||||
<Border
|
||||
Grid.Row="11"
|
||||
Grid.Row="8"
|
||||
Background="{Binding
|
||||
volume_style.background_color,
|
||||
Converter={x:Static cv:ColorToBrushConverter.Instance}}"
|
||||
@@ -910,7 +793,6 @@
|
||||
Margin="2,10,2,2"
|
||||
ItemsSource="{Binding accounts}"
|
||||
SelectedItem="{Binding selected_account}"
|
||||
ToolTip="Account"
|
||||
Visibility="{Binding
|
||||
DataContext.show_account,
|
||||
RelativeSource={RelativeSource AncestorType=UserControl},
|
||||
@@ -920,7 +802,6 @@
|
||||
<ComboBox
|
||||
DataContext="{Binding order_properties_vm}" ItemsSource="{Binding time_in_forces}"
|
||||
SelectedItem="{Binding selected_time_in_force}" Margin="2"
|
||||
ToolTip="Time In Force"
|
||||
Visibility="{Binding
|
||||
DataContext.show_tif,
|
||||
RelativeSource={RelativeSource AncestorType=UserControl},
|
||||
@@ -943,7 +824,6 @@
|
||||
IsOpen="{Binding is_expanded}"
|
||||
ItemsSource="{Binding algo_definitions_view}"
|
||||
SelectedItem="{Binding selected_algo_definition}"
|
||||
ToolTip="Order/Algo Type"
|
||||
Visibility="{Binding
|
||||
DataContext.show_order_type,
|
||||
RelativeSource={RelativeSource AncestorType=UserControl},
|
||||
@@ -999,7 +879,6 @@
|
||||
Increment="{Binding size_calculator.unit_quantity}"
|
||||
Minimum="0"
|
||||
TickDisplaySettings="{Binding size_calculator.display_settings}"
|
||||
ToolTip="Size"
|
||||
Value="{Binding size_calculator.display_value, Mode=TwoWay}" />
|
||||
|
||||
<!-- Qty quick buttons -->
|
||||
@@ -1022,8 +901,7 @@
|
||||
RelativeSource={RelativeSource AncestorType=ItemsControl}}"
|
||||
Content="{Binding}"
|
||||
Focusable="False"
|
||||
Foreground="{u:BTSColor UI.Button.normal_foreground}"
|
||||
ToolTip="Add to Size">
|
||||
Foreground="{u:BTSColor UI.Button.normal_foreground}">
|
||||
<Button.CommandParameter>
|
||||
<MultiBinding
|
||||
Converter="{x:Static cv:DynamicDoubleMultiplierConverter.Instance}">
|
||||
@@ -1043,7 +921,6 @@
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
Command="{Binding size_calculator.clear}"
|
||||
ToolTip="Clear Size"
|
||||
Visibility="{Binding
|
||||
show_clear, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Style="{StaticResource CommonButton}">
|
||||
@@ -1054,7 +931,6 @@
|
||||
<CheckBox
|
||||
Margin="2"
|
||||
IsChecked="{Binding auto_clear}"
|
||||
ToolTip="Clear Size after order"
|
||||
Visibility="{Binding
|
||||
show_auto_clear, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
Auto Clear
|
||||
@@ -1097,19 +973,19 @@
|
||||
<Button
|
||||
Command="{Binding cancel_cmd}"
|
||||
CommandParameter="Bids"
|
||||
Style="{StaticResource CancelBuys}"
|
||||
Style="{StaticResource {x:Static sp:LadderView.CancelBuysButtonStyleKey}}"
|
||||
Visibility="{Binding
|
||||
show_cancel_buys, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<Button
|
||||
Command="{Binding cancel_cmd}"
|
||||
CommandParameter="Asks"
|
||||
Style="{StaticResource CancelSells}"
|
||||
Style="{StaticResource {x:Static sp:LadderView.CancelSellsButtonStyleKey}}"
|
||||
Visibility="{Binding
|
||||
show_cancel_sells, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<Button
|
||||
Command="{Binding cancel_cmd}"
|
||||
CommandParameter="All"
|
||||
Style="{StaticResource CancelAll}"
|
||||
Style="{StaticResource {x:Static sp:LadderView.CancelAllButtonStyleKey}}"
|
||||
Visibility="{Binding
|
||||
show_cancel_all, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
</Grid>
|
||||
|
||||
@@ -27,6 +27,22 @@ namespace mbts.spark {
|
||||
MinZoomFactor = 0.5,
|
||||
MaxZoomFactor = 1.5;
|
||||
|
||||
static LadderView() {
|
||||
var knobs = config.Configuration.get<bts.config.LadderKnobs>();
|
||||
MimicTT = knobs.MimicTt;
|
||||
}
|
||||
|
||||
public static bool MimicTT { get; }
|
||||
|
||||
public static string CancelBuysButtonStyleKey =>
|
||||
MimicTT ? "TT_CancelBuys" : "CancelBuys";
|
||||
|
||||
public static string CancelSellsButtonStyleKey =>
|
||||
MimicTT ? "TT_CancelSells" : "CancelSells";
|
||||
|
||||
public static string CancelAllButtonStyleKey =>
|
||||
MimicTT ? "TT_CancelAll" : "CancelAll";
|
||||
|
||||
public static IMultiValueConverter PositionToolTipConverter =
|
||||
ValueConverter.create<AccountTuple?, bool, bool, bool, string>((account, show_day_pos, show_raw_day_pos, show_total_pos) => {
|
||||
const string day_pos_text = "day", raw_day_pos_text = "raw day", total_pos_text = "total";
|
||||
@@ -60,8 +76,14 @@ namespace mbts.spark {
|
||||
if (show_total_pos) add_part(total_pos_text, " (", ")");
|
||||
//
|
||||
if (part_count > 1) {
|
||||
// This is a complex expression (showing more than 1 value) - wrap it in single quotes.
|
||||
// Explicitly state which value will be copied to the size input.
|
||||
sb.Insert(0, "'");
|
||||
sb.Add("'");
|
||||
sb.Add(" position\n(click to copy ", primary_pos, " to size)");
|
||||
} else {
|
||||
// Only showing 1 value, no need for single quotes,
|
||||
// nor an explicit statement that indicates which value will be copied to the size input.
|
||||
sb.Add(" position\n(click to copy to size)");
|
||||
}
|
||||
return sb.ToString();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using bts;
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
@@ -223,10 +223,6 @@ namespace mbts.spark {
|
||||
Add(qty_column = new SizeColumn(
|
||||
new DoubleMinMax<OrderRecordTupleWithRoot>(
|
||||
ort => ort.order.order.quantity(), nameof(OrderBookRowVM.quantity))));
|
||||
// notional value
|
||||
Add(notional_value_column = new NotionalValueColumn(
|
||||
new DoubleMinMax<OrderRecordTupleWithRoot>(
|
||||
ort => ort.notional_value, nameof(OrderBookRowVM.notional_value))));
|
||||
// edit quantity column
|
||||
// not usually part of the main order book, but it's useful to add it
|
||||
// rather than debugging the popup window
|
||||
@@ -236,10 +232,6 @@ namespace mbts.spark {
|
||||
new DoubleMinMax<OrderRecordTupleWithRoot>(
|
||||
ort => ort.order.order.filled_quantity(),
|
||||
nameof(OrderBookRowVM.filled_quantity))));
|
||||
// filled notional value
|
||||
Add(filled_notional_value_column = new FilledNotionalValueColumn(
|
||||
new DoubleMinMax<OrderRecordTupleWithRoot>(
|
||||
ort => ort.filled_notional_value, nameof(OrderBookRowVM.filled_notional_value))));
|
||||
// price
|
||||
Add(price_column = new SimpleTextColumn<DoubleMinMaxState>(
|
||||
Columns.Price,
|
||||
@@ -371,11 +363,9 @@ namespace mbts.spark {
|
||||
public SimpleTextColumn<OrderSubType[]> sub_type_column { get; }
|
||||
public SideColumn side_column { get; }
|
||||
public SizeColumn qty_column { get; }
|
||||
public NotionalValueColumn notional_value_column { get; }
|
||||
public EditableQtyColumn edit_qty_column { get; }
|
||||
public SimpleTextColumn<DoubleMinMaxState> price_column { get; }
|
||||
public FilledColumn filled_qty_column { get; }
|
||||
public FilledNotionalValueColumn filled_notional_value_column { get; }
|
||||
public SimpleTextColumn<DoubleMinMaxState> last_price_column { get; }
|
||||
public SimpleTextColumn<DoubleMinMaxState> avg_price_column { get; }
|
||||
public SimpleTextColumn<OrderStatus[]> status_column { get; }
|
||||
@@ -401,10 +391,8 @@ namespace mbts.spark {
|
||||
order_sub_types = sub_type_column.filter.get_state(),
|
||||
sides = side_column.filter.get_state(),
|
||||
qty = qty_column.filter.get_state(),
|
||||
notional_value = notional_value_column.filter.get_state(),
|
||||
price = price_column.filter.get_state(),
|
||||
qty_filled = filled_qty_column.filter.get_state(),
|
||||
filled_notional_value = filled_notional_value_column.filter.get_state(),
|
||||
avg_price = avg_price_column.filter.get_state(),
|
||||
last_price = last_price_column.filter.get_state(),
|
||||
statuses = status_column.filter.get_state(),
|
||||
@@ -427,9 +415,7 @@ namespace mbts.spark {
|
||||
account_column.filter.load_state(fs.accounts);
|
||||
price_column.filter.load_state(fs.price);
|
||||
qty_column.filter.load_state(fs.qty);
|
||||
notional_value_column.filter.load_state(fs.notional_value);
|
||||
filled_qty_column.filter.load_state(fs.qty_filled);
|
||||
filled_notional_value_column.filter.load_state(fs.filled_notional_value);
|
||||
side_column.filter.load_state(fs.sides);
|
||||
order_type_column.filter.load_state(fs.order_types);
|
||||
root_algo_type_column.filter.load_state(fs.root_algo_types);
|
||||
@@ -659,32 +645,6 @@ namespace mbts.spark {
|
||||
DefaultCellPadding);
|
||||
}
|
||||
|
||||
public class NotionalValueColumn : OrderbookColumnBase<DoubleMinMaxState>, IRenderColumn<OrderBookRowVM> {
|
||||
public NotionalValueColumn(IDataGridColumnPersistentFilter<OrderRecordTupleWithRoot, DoubleMinMaxState> filter)
|
||||
: base(
|
||||
Columns.NotionalValue,
|
||||
DefaultColumnHeaders.NotionalValue,
|
||||
r => r.notional_value_text,
|
||||
DefaultColumnWidths.NotionalValue,
|
||||
filter,
|
||||
DataGridFilterKnownTemplates.DoubleMinMax,
|
||||
_wrap_comparer(r => r.notional_value)) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(
|
||||
dc,
|
||||
_get_glyph_context(row),
|
||||
font_size,
|
||||
row.notional_value_text,
|
||||
position,
|
||||
row_height,
|
||||
RenderUtils.get_side_brush(row),
|
||||
background_brush,
|
||||
horizontal_alignment,
|
||||
VerticalAlignment.Center,
|
||||
DefaultCellPadding);
|
||||
}
|
||||
|
||||
public class FilledColumn : OrderbookColumnBase<DoubleMinMaxState>, IRenderColumn<OrderBookRowVM> {
|
||||
public FilledColumn(IDataGridColumnPersistentFilter<OrderRecordTupleWithRoot, DoubleMinMaxState> filter)
|
||||
: base(Columns.Filled, "Filled Qty", r => r.display_filled_quantity.ToString(), DefaultColumnWidths.Qty, filter, DataGridFilterKnownTemplates.DoubleMinMax, _wrap_comparer(r => r.filled_quantity)) { }
|
||||
@@ -704,32 +664,6 @@ namespace mbts.spark {
|
||||
DefaultCellPadding);
|
||||
}
|
||||
|
||||
public class FilledNotionalValueColumn : OrderbookColumnBase<DoubleMinMaxState>, IRenderColumn<OrderBookRowVM> {
|
||||
public FilledNotionalValueColumn(IDataGridColumnPersistentFilter<OrderRecordTupleWithRoot, DoubleMinMaxState> filter)
|
||||
: base(
|
||||
Columns.FilledNotionalValue,
|
||||
DefaultColumnHeaders.FilledNotionalValue,
|
||||
r => r.filled_notional_value_text,
|
||||
DefaultColumnWidths.FilledNotionalValue,
|
||||
filter,
|
||||
DataGridFilterKnownTemplates.DoubleMinMax,
|
||||
_wrap_comparer(r => r.filled_notional_value)) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(
|
||||
dc,
|
||||
_get_glyph_context(row),
|
||||
font_size,
|
||||
row.filled_notional_value_text,
|
||||
position,
|
||||
row_height,
|
||||
RenderUtils.get_side_brush(row),
|
||||
background_brush,
|
||||
horizontal_alignment,
|
||||
VerticalAlignment.Center,
|
||||
DefaultCellPadding);
|
||||
}
|
||||
|
||||
public class OrderBookOrderTypeColumn : OrderTypeColumn<Columns>, ICVOrderBookColumn {
|
||||
readonly BindingBase _foreground_binding;
|
||||
ListSortDirection? _cached_sort_direction;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using bts;
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
@@ -108,8 +108,6 @@ namespace mbts.spark {
|
||||
public double display_quantity => quantity.safe_divide(_display_qty_divisor);
|
||||
public string display_quantity_text =>
|
||||
quantity_display_settings.to_tick_display_settings().format(display_quantity);
|
||||
public double notional_value => order_tuple.notional_value;
|
||||
public string notional_value_text => _fmt_price(notional_value);
|
||||
public TickDisplaySettingsImmutable quantity_display_settings {
|
||||
get => _quantity_display_settings;
|
||||
set {
|
||||
@@ -130,8 +128,6 @@ namespace mbts.spark {
|
||||
filled_quantity.safe_divide(_display_qty_divisor);
|
||||
public string display_filled_quantity_text =>
|
||||
quantity_display_settings.to_tick_display_settings().format(display_filled_quantity);
|
||||
public double filled_notional_value => order_tuple.filled_notional_value;
|
||||
public string filled_notional_value_text => _fmt_price(filled_notional_value);
|
||||
public string last_msg => order.last_msg();
|
||||
// piq
|
||||
public uint piq => order_tuple.order.piq ?? uint.MaxValue;
|
||||
@@ -359,15 +355,9 @@ namespace mbts.spark {
|
||||
if (_prospective_qty_translator_backing != null)
|
||||
_prospective_qty_translator_backing.actual_value = quantity;
|
||||
}
|
||||
if ((Columns.NotionalValue & dirty) == Columns.NotionalValue) {
|
||||
RaisePropertyChanged(nameof(notional_value), nameof(notional_value_text));
|
||||
}
|
||||
if ((Columns.Filled & dirty) != 0) {
|
||||
RaisePropertyChanged(nameof(filled_quantity), nameof(display_filled_quantity));
|
||||
}
|
||||
if ((Columns.FilledNotionalValue & dirty) == Columns.FilledNotionalValue) {
|
||||
RaisePropertyChanged(nameof(filled_notional_value), nameof(filled_notional_value_text));
|
||||
}
|
||||
// badge fill
|
||||
if (old_order_tuple.order.meta?.badge_color != order_tuple.order.meta?.badge_color) {
|
||||
// invalidate by setting to null (will be re-instantiated in the public getter)
|
||||
@@ -531,12 +521,6 @@ namespace mbts.spark {
|
||||
if (oldval.order.piq != newval.order.piq) {
|
||||
dirty |= Columns.Piq;
|
||||
}
|
||||
if (oldval.notional_value != newval.notional_value) {
|
||||
dirty |= Columns.NotionalValue;
|
||||
}
|
||||
if (oldval.filled_notional_value != newval.filled_notional_value) {
|
||||
dirty |= Columns.FilledNotionalValue;
|
||||
}
|
||||
return dirty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,7 +389,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
void refresh_quantity_display() {
|
||||
foreach (var row in _row_collection.GetAllRows()) {
|
||||
foreach (var row in _row_collection.all_rows) {
|
||||
if (_cached_securities.TryGetValue(row.security_key, out var smd)) {
|
||||
row.quantity_display_settings = smd.quantity_display;
|
||||
}
|
||||
|
||||
@@ -1,170 +1,160 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
|
||||
using mbts;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.utility;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using Spark.Api.Orders;
|
||||
|
||||
using System;
|
||||
|
||||
namespace bts.shell.orderbook {
|
||||
[ProtoContract]
|
||||
class FilterStates : IEquatable<FilterStates> {
|
||||
[ProtoMember(1)]
|
||||
public SecurityCriteria security_criteria;
|
||||
[ProtoMember(2)]
|
||||
public string order_key_regex;
|
||||
[ProtoMember(3)]
|
||||
public TimeFilterTuple time_created;
|
||||
[ProtoMember(4)]
|
||||
public string[] users;
|
||||
[ProtoMember(5)]
|
||||
public string[] accounts;
|
||||
[ProtoMember(6)]
|
||||
public Venue[] exchanges;
|
||||
[ProtoMember(7)]
|
||||
public TimeInForce[] tifs;
|
||||
//[ProtoMember(8)]
|
||||
//public CustomOrderDefinition.CustomOneofCase[] order_types;
|
||||
[ProtoMember(23)]
|
||||
public AlgoID[] order_types;
|
||||
[ProtoMember(24)]
|
||||
public AlgoID[] root_algo_types;
|
||||
[ProtoMember(9)]
|
||||
public Side[] sides;
|
||||
[ProtoMember(10)]
|
||||
public DoubleMinMaxState qty;
|
||||
[ProtoMember(26)]
|
||||
public DoubleMinMaxState notional_value;
|
||||
[ProtoMember(11)]
|
||||
public DoubleMinMaxState price;
|
||||
[ProtoMember(12)]
|
||||
public DoubleMinMaxState qty_filled;
|
||||
[ProtoMember(27)]
|
||||
public DoubleMinMaxState filled_notional_value;
|
||||
[ProtoMember(13)]
|
||||
public DoubleMinMaxState avg_price;
|
||||
[ProtoMember(14)]
|
||||
public DoubleMinMaxState last_price;
|
||||
[ProtoMember(15)]
|
||||
public OrderStatus[] statuses;
|
||||
[ProtoMember(16)]
|
||||
public string exchange_message_regex;
|
||||
[ProtoMember(17)]
|
||||
public TimeFilterTuple last_change;
|
||||
//[ProtoMember(18)]
|
||||
//public string security_regex;
|
||||
[ProtoMember(19)]
|
||||
public api.orderman.OrderSubType[] order_sub_types;
|
||||
[ProtoMember(20)]
|
||||
public string[] symbols;
|
||||
[ProtoMember(21)]
|
||||
public string parent_key_regex;
|
||||
[ProtoMember(22)]
|
||||
public string attached_target_order_key_regex;
|
||||
[ProtoMember(25)]
|
||||
public UIntMinMaxState piq;
|
||||
|
||||
public bool Equals(FilterStates other) =>
|
||||
other != null &&
|
||||
security_criteria.safe_equals(other.security_criteria) &&
|
||||
order_key_regex == other.order_key_regex &&
|
||||
parent_key_regex == other.parent_key_regex &&
|
||||
attached_target_order_key_regex == other.attached_target_order_key_regex &&
|
||||
time_created.Equals(other.time_created) &&
|
||||
EqualityComparer.safe_sequence_equals(users, other.users) &&
|
||||
EqualityComparer.safe_sequence_equals(accounts, other.accounts) &&
|
||||
EqualityComparer.safe_sequence_equals(exchanges, other.exchanges) &&
|
||||
EqualityComparer.safe_sequence_equals(tifs, other.tifs) &&
|
||||
EqualityComparer.safe_sequence_equals(order_types, other.order_types) &&
|
||||
EqualityComparer.safe_sequence_equals(root_algo_types, other.root_algo_types) &&
|
||||
EqualityComparer.safe_sequence_equals(sides, other.sides) &&
|
||||
qty.safe_equals(other.qty) &&
|
||||
notional_value.safe_equals(other.notional_value) &&
|
||||
price.safe_equals(other.price) &&
|
||||
qty_filled.safe_equals(other.qty_filled) &&
|
||||
filled_notional_value.safe_equals(other.filled_notional_value) &&
|
||||
avg_price.safe_equals(other.avg_price) &&
|
||||
last_price.safe_equals(other.last_price) &&
|
||||
EqualityComparer.safe_sequence_equals(statuses, other.statuses) &&
|
||||
exchange_message_regex == other.exchange_message_regex &&
|
||||
last_change.Equals(other.last_change) &&
|
||||
EqualityComparer.safe_sequence_equals(order_sub_types, other.order_sub_types) &&
|
||||
EqualityComparer.safe_sequence_equals(symbols, other.symbols) &&
|
||||
piq.safe_equals(other.piq);
|
||||
|
||||
public override bool Equals(object obj) => Equals(obj as FilterStates);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
security_criteria?.GetHashCode() ?? 0,
|
||||
order_key_regex?.GetHashCode() ?? 0,
|
||||
parent_key_regex?.GetHashCode() ?? 0,
|
||||
attached_target_order_key_regex?.GetHashCode() ?? 0,
|
||||
time_created.GetHashCode(),
|
||||
EqualityComparer.safe_aggregate_hash(users),
|
||||
EqualityComparer.safe_aggregate_hash(accounts),
|
||||
EqualityComparer.safe_aggregate_hash(exchanges),
|
||||
EqualityComparer.safe_aggregate_hash(tifs),
|
||||
EqualityComparer.safe_aggregate_hash(order_types),
|
||||
EqualityComparer.safe_aggregate_hash(root_algo_types),
|
||||
EqualityComparer.safe_aggregate_hash(sides),
|
||||
qty?.GetHashCode() ?? 0,
|
||||
notional_value?.GetHashCode() ?? 0,
|
||||
price?.GetHashCode() ?? 0,
|
||||
qty_filled?.GetHashCode() ?? 0,
|
||||
filled_notional_value?.GetHashCode() ?? 0,
|
||||
avg_price?.GetHashCode() ?? 0,
|
||||
last_price?.GetHashCode() ?? 0,
|
||||
EqualityComparer.safe_aggregate_hash(statuses),
|
||||
exchange_message_regex?.GetHashCode() ?? 0,
|
||||
last_change.GetHashCode(),
|
||||
EqualityComparer.safe_aggregate_hash(order_sub_types),
|
||||
EqualityComparer.safe_aggregate_hash(symbols),
|
||||
piq?.GetHashCode() ?? 0);
|
||||
}
|
||||
|
||||
static class FilterStatesExtensions {
|
||||
public static Func<OrderRecordTupleWithRoot, bool> build_predicate(this FilterStates fs) {
|
||||
var pb = new ColumnPredicateBuilder<OrderRecordTupleWithRoot>();
|
||||
// security
|
||||
pb.add_security_criteria(fs.security_criteria, o => o.order.security);
|
||||
// regex
|
||||
pb.add_regex(fs.order_key_regex, o => o.order.order.Key.ToString());
|
||||
pb.add_regex(fs.exchange_message_regex, o => o.order.order.State.LastMsg);
|
||||
pb.add_regex(fs.attached_target_order_key_regex, o => o.order.order.attached_target_order_key().ToString());
|
||||
pb.add_regex(fs.parent_key_regex, o => o.order.order.ParentKey.ToString());
|
||||
// time
|
||||
pb.add_time_filter(fs.time_created, o => o.order.order.created());
|
||||
pb.add_time_filter(fs.last_change, o => o.order.order.last_change());
|
||||
// discrete values
|
||||
pb.add_discrete_values(fs.users, o => o.order.order.user());
|
||||
pb.add_discrete_values(fs.accounts, o => o.order.order.account());
|
||||
pb.add_discrete_values(fs.exchanges, o => o.order.security?.Exchange ?? Venue.Unset);
|
||||
pb.add_discrete_values(fs.tifs, o => o.order.order.time_in_force());
|
||||
pb.add_discrete_values(fs.order_types, o => AlgoID.from_custom_def(o.order.order.Def.CustomDef));
|
||||
pb.add_discrete_values(fs.root_algo_types, o => AlgoID.from_algo_key(o.root.algo?.key ?? default));
|
||||
pb.add_discrete_values(fs.order_sub_types, o => o.order.order.sub_type());
|
||||
pb.add_discrete_values(fs.sides, o => o.order.order.side());
|
||||
pb.add_discrete_values(fs.statuses, o => o.order.order.status());
|
||||
pb.add_discrete_values(fs.symbols, o => o.order.security?.Symbol);
|
||||
// numeric
|
||||
pb.add_numeric(fs.qty, o => o.order.order.quantity());
|
||||
pb.add_numeric(fs.notional_value, o => o.notional_value);
|
||||
pb.add_price(fs.price, o => o.order.order.ui_price(), o => o.order.security);
|
||||
pb.add_numeric(fs.qty_filled, o => o.order.order.filled_quantity());
|
||||
pb.add_numeric(fs.filled_notional_value, o => o.filled_notional_value);
|
||||
pb.add_numeric(fs.avg_price, o => o.order.order.avg_price());
|
||||
pb.add_numeric(fs.last_price, o => o.order.order.last_price());
|
||||
pb.add_numeric(fs.piq, o => o.order.piq ?? uint.MaxValue);
|
||||
return pb.to_predicate();
|
||||
}
|
||||
}
|
||||
}
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
|
||||
using mbts;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.utility;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using Spark.Api.Orders;
|
||||
|
||||
using System;
|
||||
|
||||
namespace bts.shell.orderbook {
|
||||
[ProtoContract]
|
||||
class FilterStates : IEquatable<FilterStates> {
|
||||
[ProtoMember(1)]
|
||||
public SecurityCriteria security_criteria;
|
||||
[ProtoMember(2)]
|
||||
public string order_key_regex;
|
||||
[ProtoMember(3)]
|
||||
public TimeFilterTuple time_created;
|
||||
[ProtoMember(4)]
|
||||
public string[] users;
|
||||
[ProtoMember(5)]
|
||||
public string[] accounts;
|
||||
[ProtoMember(6)]
|
||||
public Venue[] exchanges;
|
||||
[ProtoMember(7)]
|
||||
public TimeInForce[] tifs;
|
||||
//[ProtoMember(8)]
|
||||
//public CustomOrderDefinition.CustomOneofCase[] order_types;
|
||||
[ProtoMember(23)]
|
||||
public AlgoID[] order_types;
|
||||
[ProtoMember(24)]
|
||||
public AlgoID[] root_algo_types;
|
||||
[ProtoMember(9)]
|
||||
public Side[] sides;
|
||||
[ProtoMember(10)]
|
||||
public DoubleMinMaxState qty;
|
||||
[ProtoMember(11)]
|
||||
public DoubleMinMaxState price;
|
||||
[ProtoMember(12)]
|
||||
public DoubleMinMaxState qty_filled;
|
||||
[ProtoMember(13)]
|
||||
public DoubleMinMaxState avg_price;
|
||||
[ProtoMember(14)]
|
||||
public DoubleMinMaxState last_price;
|
||||
[ProtoMember(15)]
|
||||
public OrderStatus[] statuses;
|
||||
[ProtoMember(16)]
|
||||
public string exchange_message_regex;
|
||||
[ProtoMember(17)]
|
||||
public TimeFilterTuple last_change;
|
||||
//[ProtoMember(18)]
|
||||
//public string security_regex;
|
||||
[ProtoMember(19)]
|
||||
public api.orderman.OrderSubType[] order_sub_types;
|
||||
[ProtoMember(20)]
|
||||
public string[] symbols;
|
||||
[ProtoMember(21)]
|
||||
public string parent_key_regex;
|
||||
[ProtoMember(22)]
|
||||
public string attached_target_order_key_regex;
|
||||
[ProtoMember(25)]
|
||||
public UIntMinMaxState piq;
|
||||
|
||||
public bool Equals(FilterStates other) =>
|
||||
other != null &&
|
||||
security_criteria.safe_equals(other.security_criteria) &&
|
||||
order_key_regex == other.order_key_regex &&
|
||||
parent_key_regex == other.parent_key_regex &&
|
||||
attached_target_order_key_regex == other.attached_target_order_key_regex &&
|
||||
time_created.Equals(other.time_created) &&
|
||||
EqualityComparer.safe_sequence_equals(users, other.users) &&
|
||||
EqualityComparer.safe_sequence_equals(accounts, other.accounts) &&
|
||||
EqualityComparer.safe_sequence_equals(exchanges, other.exchanges) &&
|
||||
EqualityComparer.safe_sequence_equals(tifs, other.tifs) &&
|
||||
EqualityComparer.safe_sequence_equals(order_types, other.order_types) &&
|
||||
EqualityComparer.safe_sequence_equals(root_algo_types, other.root_algo_types) &&
|
||||
EqualityComparer.safe_sequence_equals(sides, other.sides) &&
|
||||
qty.safe_equals(other.qty) &&
|
||||
price.safe_equals(other.price) &&
|
||||
qty_filled.safe_equals(other.qty_filled) &&
|
||||
avg_price.safe_equals(other.avg_price) &&
|
||||
last_price.safe_equals(other.last_price) &&
|
||||
EqualityComparer.safe_sequence_equals(statuses, other.statuses) &&
|
||||
exchange_message_regex == other.exchange_message_regex &&
|
||||
last_change.Equals(other.last_change) &&
|
||||
EqualityComparer.safe_sequence_equals(order_sub_types, other.order_sub_types) &&
|
||||
EqualityComparer.safe_sequence_equals(symbols, other.symbols) &&
|
||||
piq.safe_equals(other.piq);
|
||||
|
||||
public override bool Equals(object obj) => Equals(obj as FilterStates);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
security_criteria?.GetHashCode() ?? 0,
|
||||
order_key_regex?.GetHashCode() ?? 0,
|
||||
parent_key_regex?.GetHashCode() ?? 0,
|
||||
attached_target_order_key_regex?.GetHashCode() ?? 0,
|
||||
time_created.GetHashCode(),
|
||||
EqualityComparer.safe_aggregate_hash(users),
|
||||
EqualityComparer.safe_aggregate_hash(accounts),
|
||||
EqualityComparer.safe_aggregate_hash(exchanges),
|
||||
EqualityComparer.safe_aggregate_hash(tifs),
|
||||
EqualityComparer.safe_aggregate_hash(order_types),
|
||||
EqualityComparer.safe_aggregate_hash(root_algo_types),
|
||||
EqualityComparer.safe_aggregate_hash(sides),
|
||||
qty?.GetHashCode() ?? 0,
|
||||
price?.GetHashCode() ?? 0,
|
||||
qty_filled?.GetHashCode() ?? 0,
|
||||
avg_price?.GetHashCode() ?? 0,
|
||||
last_price?.GetHashCode() ?? 0,
|
||||
EqualityComparer.safe_aggregate_hash(statuses),
|
||||
exchange_message_regex?.GetHashCode() ?? 0,
|
||||
last_change.GetHashCode(),
|
||||
EqualityComparer.safe_aggregate_hash(order_sub_types),
|
||||
EqualityComparer.safe_aggregate_hash(symbols),
|
||||
piq?.GetHashCode() ?? 0);
|
||||
}
|
||||
|
||||
static class FilterStatesExtensions {
|
||||
public static Func<OrderRecordTupleWithRoot, bool> build_predicate(this FilterStates fs) {
|
||||
var pb = new ColumnPredicateBuilder<OrderRecordTupleWithRoot>();
|
||||
// security
|
||||
pb.add_security_criteria(fs.security_criteria, o => o.order.security);
|
||||
// regex
|
||||
pb.add_regex(fs.order_key_regex, o => o.order.order.Key.ToString());
|
||||
pb.add_regex(fs.exchange_message_regex, o => o.order.order.State.LastMsg);
|
||||
pb.add_regex(fs.attached_target_order_key_regex, o => o.order.order.attached_target_order_key().ToString());
|
||||
pb.add_regex(fs.parent_key_regex, o => o.order.order.ParentKey.ToString());
|
||||
// time
|
||||
pb.add_time_filter(fs.time_created, o => o.order.order.created());
|
||||
pb.add_time_filter(fs.last_change, o => o.order.order.last_change());
|
||||
// discrete values
|
||||
pb.add_discrete_values(fs.users, o => o.order.order.user());
|
||||
pb.add_discrete_values(fs.accounts, o => o.order.order.account());
|
||||
pb.add_discrete_values(fs.exchanges, o => o.order.security?.Exchange ?? Venue.Unset);
|
||||
pb.add_discrete_values(fs.tifs, o => o.order.order.time_in_force());
|
||||
pb.add_discrete_values(fs.order_types, o => AlgoID.from_custom_def(o.order.order.Def.CustomDef));
|
||||
pb.add_discrete_values(fs.root_algo_types, o => AlgoID.from_algo_key(o.root.algo?.key ?? default));
|
||||
pb.add_discrete_values(fs.order_sub_types, o => o.order.order.sub_type());
|
||||
pb.add_discrete_values(fs.sides, o => o.order.order.side());
|
||||
pb.add_discrete_values(fs.statuses, o => o.order.order.status());
|
||||
pb.add_discrete_values(fs.symbols, o => o.order.security?.Symbol);
|
||||
// numeric
|
||||
pb.add_numeric(fs.qty, o => o.order.order.quantity());
|
||||
pb.add_price(fs.price, o => o.order.order.ui_price(), o => o.order.security);
|
||||
pb.add_numeric(fs.qty_filled, o => o.order.order.filled_quantity());
|
||||
pb.add_numeric(fs.avg_price, o => o.order.order.avg_price());
|
||||
pb.add_numeric(fs.last_price, o => o.order.order.last_price());
|
||||
pb.add_numeric(fs.piq, o => o.order.piq ?? uint.MaxValue);
|
||||
return pb.to_predicate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace bts.shell.orderbook {
|
||||
static OrderBookGridSettingsManager() {
|
||||
// make font-weight immutable for all columns (the font-weight rule is hard-coded: Regular for root-orders, Light for all others)
|
||||
// TODO: Remove the immutable font-weight flags when cell animation is ready
|
||||
var col_settings_flags = Enum.GetValuesAsUnderlyingType<Columns>().Cast<Columns>().ToDictionary(
|
||||
var col_settings_flags = EnumHelper.get_values<Columns>().ToDictionary(
|
||||
c => c.ToString(), c => GridColumnSettingsFeatureFlags.ImmutableFontWeight);
|
||||
// make font-color immutable for Side, Size, and Filled columns (the font-color rule is hard-coded: MarketData.bid for Buy orders, MarketData.ask for Sells)
|
||||
// TODO: Remove the immutable font-color flags when cell animation is ready
|
||||
|
||||
@@ -1,277 +1,270 @@
|
||||
using bts.api.orderman;
|
||||
using bts.shell.fills;
|
||||
|
||||
using mbts;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
|
||||
using Columns = mbts.spark.OrderBookGridView.ColumnKeys;
|
||||
using GridSettings = mbts.ui.settings.GridSettings;
|
||||
|
||||
namespace bts.shell.orderbook {
|
||||
using ColHeader = mbts.ui.controls.DIYGrid.DefaultColumnHeaders;
|
||||
using ColWidth = mbts.ui.controls.DIYGrid.DefaultColumnWidths;
|
||||
|
||||
[ProtoContract]
|
||||
class OrderBookStartupData {
|
||||
const bool
|
||||
_default_show_right_panel = true,
|
||||
_default_show_order_params = true;
|
||||
const double
|
||||
_default_right_panel_width = 400,
|
||||
_default_order_params_height = 250,
|
||||
_default_fills_height = 300,
|
||||
_default_ladder_height = 350;
|
||||
[ProtoMember(16)]
|
||||
OrderBookRightPanelSettings _right_panel;
|
||||
// ALREADY USED TAGS
|
||||
// [ProtoMember(5)]
|
||||
// public readonly List<SortingCriteria> sort_criteria;
|
||||
|
||||
public OrderBookRightPanelSettings right_panel => _right_panel ?? (_right_panel = new OrderBookRightPanelSettings());
|
||||
/// TODO: remove properties below, and redirect callers to <see cref="right_panel"/>
|
||||
// right panel (host)
|
||||
public bool show_right_panel { get => right_panel.show; set => right_panel.show = value; }
|
||||
public double right_panel_width { get => right_panel.width; set => right_panel.width = value; }
|
||||
// order params
|
||||
public bool show_order_params { get => right_panel.stack.show_order_params; set => right_panel.stack.show_order_params = value; }
|
||||
public double order_params_height { get; set; }
|
||||
// ladder
|
||||
public bool show_ladder { get; set; }
|
||||
public double ladder_height { get => right_panel.stack.ladder_height; set => right_panel.stack.ladder_height = value; }
|
||||
// fills
|
||||
public bool show_fills { get => right_panel.stack.show_fills; set => right_panel.stack.show_fills = value; }
|
||||
public double fills_height { get => right_panel.stack.fills_height; set => right_panel.stack.fills_height = value; }
|
||||
|
||||
// filters
|
||||
[ProtoMember(10)]
|
||||
public FilterStates filters;
|
||||
// grid
|
||||
[ProtoMember(15)]
|
||||
[Obsolete]
|
||||
public GridSettings grid { get; set; }
|
||||
// orderbook grid
|
||||
[ProtoMember(17)]
|
||||
public OrderBookGridSettings orderbook_grid { get; set; }
|
||||
// fills settings
|
||||
[ProtoMember(26)]
|
||||
public OrderBookFillsGridSettings fills_settings { get; set; }
|
||||
// predicate animations
|
||||
[ProtoMember(20)]
|
||||
[Obsolete]
|
||||
List<OrderRecordTuplePredicateAnimations> _predicate_animations;
|
||||
// use defaults
|
||||
[ProtoMember(25)]
|
||||
public bool use_defaults;
|
||||
// menu display
|
||||
[ProtoMember(27)]
|
||||
public SparkMenuDisplaySettings menu_display { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Please use <see cref="Default"/> to get a new instance (the only reason the default constructor still exists is for ProtoBuf deserialization).
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public OrderBookStartupData() {
|
||||
show_right_panel = _default_show_right_panel;
|
||||
show_order_params = _default_show_order_params;
|
||||
right_panel_width = _default_right_panel_width;
|
||||
order_params_height = _default_order_params_height;
|
||||
fills_height = _default_fills_height;
|
||||
ladder_height = _default_ladder_height;
|
||||
}
|
||||
|
||||
public OrderBookStartupData(FilterStates filters, OrderBookGridSettings orderbook_grid = default, bool show_right_panel = false, double right_panel_width = 0, double fills_height = 0, bool show_fills = false, bool use_defaults = false) {
|
||||
this.filters = filters;
|
||||
this.use_defaults = use_defaults;
|
||||
this.show_right_panel = show_right_panel;
|
||||
this.right_panel_width = right_panel_width;
|
||||
this.fills_height = fills_height;
|
||||
this.show_fills = show_fills;
|
||||
this.orderbook_grid = orderbook_grid;
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
_predicate_animations = new List<OrderRecordTuplePredicateAnimations>();
|
||||
if (predicate_animations != null)
|
||||
_predicate_animations.AddRange(predicate_animations);
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
}
|
||||
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
[Obsolete]
|
||||
public IReadOnlyList<OrderRecordTuplePredicateAnimations> predicate_animations => _predicate_animations ?? (_predicate_animations = new List<OrderRecordTuplePredicateAnimations>());
|
||||
public OrderBookGridSettings grid_settings_wrapper() {
|
||||
if (use_defaults)
|
||||
return null;
|
||||
if (orderbook_grid == null) {
|
||||
if (grid == null)
|
||||
grid = _default_general_grid;
|
||||
orderbook_grid = new OrderBookGridSettings(grid.Clone(), _predicate_animations);
|
||||
}
|
||||
return orderbook_grid;
|
||||
}
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
|
||||
public static OrderBookStartupData Default =>
|
||||
new OrderBookStartupData(
|
||||
new FilterStates { statuses = new[] { OrderStatus.Working } },
|
||||
DefaultGrid,
|
||||
_default_show_right_panel,
|
||||
_default_right_panel_width,
|
||||
_default_fills_height,
|
||||
use_defaults: true) {
|
||||
show_order_params = _default_show_order_params,
|
||||
order_params_height = _default_order_params_height,
|
||||
fills_settings = DefaultFillsGrid
|
||||
};
|
||||
|
||||
public static OrderBookGridSettings DefaultGrid => new OrderBookGridSettings(_default_general_grid) {
|
||||
algo_tree_display_mode = AlgoTreeDisplayMode.RootAndLeafNodes
|
||||
};
|
||||
|
||||
public static OrderBookFillsGridSettings DefaultFillsGrid {
|
||||
get {
|
||||
var default_fills = FillsSettings.Default();
|
||||
var rv = new OrderBookFillsGridSettings {
|
||||
sorting_criteria = default_fills.sorting_criteria.ToList(),
|
||||
filters = default_fills.filters,
|
||||
general = FillsSettings.MiniGridSettings
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
static GridSettings _default_general_grid => GridSettings.Default.with(DefaultColumns.standard, DefaultColumns.sorting_criteria);
|
||||
public static OrderBookGridSettings default_mini_grid => new OrderBookGridSettings(GridSettings.Default.with(DefaultColumns.mini, DefaultColumns.sorting_criteria)) {
|
||||
algo_tree_display_mode = AlgoTreeDisplayMode.RootAndLeafNodes
|
||||
};
|
||||
|
||||
static class DefaultColumns {
|
||||
// standard
|
||||
public static GridColumnSettings security => new GridColumnSettings(Columns.Security.ToString(), DIYGrid.DefaultColumnHeaders.Security, width: DIYGrid.DefaultColumnWidths.Security);
|
||||
public static GridColumnSettings symbol => new GridColumnSettings(Columns.Symbol.ToString(), DIYGrid.DefaultColumnHeaders.Symbol, width: DIYGrid.DefaultColumnWidths.Symbol, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings time_created => new GridColumnSettings(Columns.Time.ToString(), "Time", width: DIYGrid.DefaultColumnWidths.Time, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings last_change => new GridColumnSettings(Columns.LastChange.ToString(), "Last Change", width: DIYGrid.DefaultColumnWidths.Time, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings user => new GridColumnSettings(Columns.User.ToString(), DIYGrid.DefaultColumnHeaders.User, width: DIYGrid.DefaultColumnWidths.User, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings account => new GridColumnSettings(Columns.Account.ToString(), DIYGrid.DefaultColumnHeaders.Account, width: DIYGrid.DefaultColumnWidths.Account, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings exchange => new GridColumnSettings(Columns.Exchange.ToString(), DIYGrid.DefaultColumnHeaders.Exchange, width: DIYGrid.DefaultColumnWidths.Exchange, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings time_in_force => new GridColumnSettings(Columns.TimeInForce.ToString(), DIYGrid.DefaultColumnHeaders.TimeInForce, width: DIYGrid.DefaultColumnWidths.TimeInForce);
|
||||
public static GridColumnSettings order_type => new GridColumnSettings(Columns.OrderType.ToString(), DIYGrid.DefaultColumnHeaders.OrderType, width: DIYGrid.DefaultColumnWidths.OrderType);
|
||||
public static GridColumnSettings root_algo_type => new GridColumnSettings(Columns.RootAlgoType.ToString(), DIYGrid.DefaultColumnHeaders.RootAlgoType, width: DIYGrid.DefaultColumnWidths.OrderType);
|
||||
public static GridColumnSettings sub_type => new GridColumnSettings(Columns.SubType.ToString(), "Sub Type", width: 80, horizontal_alignment: HorizontalAlignment.Left);
|
||||
|
||||
public static GridColumnSettings side => new(
|
||||
Columns.Side.ToString(), ColHeader.Side,
|
||||
width: ColWidth.Side,
|
||||
horizontal_alignment: HorizontalAlignment.Left);
|
||||
|
||||
public static GridColumnSettings quantity => new GridColumnSettings(Columns.Size.ToString(), "Qty", width: DIYGrid.DefaultColumnWidths.Qty, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings notional_value => new GridColumnSettings(Columns.NotionalValue.ToString(), DIYGrid.DefaultColumnHeaders.NotionalValue, width: DIYGrid.DefaultColumnWidths.NotionalValue, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings price => new GridColumnSettings(Columns.Price.ToString(), DIYGrid.DefaultColumnHeaders.Price, width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right);
|
||||
|
||||
public static GridColumnSettings filled_quantity => new(
|
||||
Columns.Filled.ToString(),
|
||||
ColHeader.FilledQuantity,
|
||||
width: ColWidth.Filled,
|
||||
horizontal_alignment: HorizontalAlignment.Right);
|
||||
|
||||
public static GridColumnSettings filled_notional_value => new(
|
||||
Columns.FilledNotionalValue.ToString(),
|
||||
ColHeader.FilledNotionalValue,
|
||||
width: ColWidth.FilledNotionalValue,
|
||||
horizontal_alignment: HorizontalAlignment.Right);
|
||||
|
||||
public static GridColumnSettings avg_price => new(
|
||||
Columns.AvgFilled.ToString(),
|
||||
ColHeader.AvgFilled,
|
||||
width: ColWidth.AveragePrice,
|
||||
horizontal_alignment: HorizontalAlignment.Right);
|
||||
|
||||
public static GridColumnSettings last_price => new GridColumnSettings(Columns.LastPrice.ToString(), "Last Filled", width: DIYGrid.DefaultColumnWidths.LastPrice, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings status => new GridColumnSettings(Columns.Status.ToString(), "Status", width: 80, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings last_message => new GridColumnSettings(Columns.LastMessage.ToString(), "Last Message", width: DIYGrid.DefaultColumnWidths.LastMessage, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, is_visible: false, width: DIYGrid.DefaultColumnWidths.SecurityKey, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings order_key => new GridColumnSettings(Columns.OrderKey.ToString(), DIYGrid.DefaultColumnHeaders.OrderKey, width: DIYGrid.DefaultColumnWidths.OrderKey, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings parent_key => new GridColumnSettings(Columns.ParentKey.ToString(), "Parent Key", width: DIYGrid.DefaultColumnWidths.ParentKey, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings attached_target_order_key => new GridColumnSettings(Columns.AttachedTargetOrderKey.ToString(), DIYGrid.DefaultColumnHeaders.AttachedTargetOrderKey, width: DIYGrid.DefaultColumnWidths.ParentKey, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings piq => new GridColumnSettings(Columns.Piq.ToString(), "PIQ", width: DIYGrid.DefaultColumnWidths.Qty, horizontal_alignment: HorizontalAlignment.Right);
|
||||
// editable
|
||||
public static GridColumnSettings cancel => new GridColumnSettings(Columns.Cancel.ToString(), DIYGrid.DefaultColumnHeaders.Cancel, width: DIYGrid.DefaultColumnWidths.MiniOrderBook.Cancel);
|
||||
public static GridColumnSettings attach_detach => new GridColumnSettings(Columns.Attach.ToString(), DIYGrid.DefaultColumnHeaders.AttachDetach, width: DIYGrid.DefaultColumnWidths.MiniOrderBook.AttachDetach);
|
||||
public static GridColumnSettings update => new GridColumnSettings(Columns.Update.ToString(), DIYGrid.DefaultColumnHeaders.Update, width: DIYGrid.DefaultColumnWidths.MiniOrderBook.Update);
|
||||
public static GridColumnSettings edit_quantity => new GridColumnSettings(Columns.EditableQty.ToString(), DIYGrid.DefaultColumnHeaders.Qty, width: DIYGrid.DefaultColumnWidths.MiniOrderBook.EditableSize);
|
||||
public static GridColumnSettings edit_price => new GridColumnSettings(Columns.EditablePrice.ToString(), DIYGrid.DefaultColumnHeaders.Price, width: DIYGrid.DefaultColumnWidths.MiniOrderBook.EditablePrice);
|
||||
public static GridColumnSettings commit => new GridColumnSettings(Columns.Commit.ToString(), width: DIYGrid.DefaultColumnWidths.MiniOrderBook.Commit);
|
||||
|
||||
public static IEnumerable<SortingCriteria> sorting_criteria {
|
||||
get {
|
||||
yield return new SortingCriteria(time_created.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GridColumnSettings> standard {
|
||||
get {
|
||||
yield return security;
|
||||
yield return symbol;
|
||||
yield return time_created;
|
||||
yield return last_change;
|
||||
yield return user;
|
||||
yield return account;
|
||||
yield return exchange;
|
||||
yield return time_in_force;
|
||||
yield return order_type;
|
||||
yield return root_algo_type;
|
||||
yield return sub_type;
|
||||
yield return side;
|
||||
yield return quantity;
|
||||
yield return price;
|
||||
yield return piq;
|
||||
yield return filled_quantity;
|
||||
yield return avg_price;
|
||||
yield return last_price;
|
||||
yield return status;
|
||||
yield return last_message;
|
||||
yield return security_key;
|
||||
yield return order_key;
|
||||
yield return parent_key;
|
||||
yield return attached_target_order_key;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GridColumnSettings> editable {
|
||||
get {
|
||||
yield return cancel;
|
||||
yield return update;
|
||||
yield return edit_quantity;
|
||||
yield return edit_price;
|
||||
yield return commit;
|
||||
yield return attach_detach;
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<GridColumnSettings> _mini_mode_columns_to_hide = new HashSet<GridColumnSettings> {
|
||||
symbol, last_change, exchange, sub_type, quantity, price, avg_price, last_price, status, last_message, security_key, order_key, parent_key, attached_target_order_key
|
||||
};
|
||||
|
||||
public static IEnumerable<GridColumnSettings> mini {
|
||||
get {
|
||||
foreach (var col_setting in editable)
|
||||
yield return col_setting;
|
||||
foreach (var col_setting in standard) {
|
||||
if (_mini_mode_columns_to_hide.Contains(col_setting)) {
|
||||
yield return col_setting.with(is_visible: false);
|
||||
} else {
|
||||
yield return col_setting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using bts.api.orderman;
|
||||
using bts.shell.fills;
|
||||
|
||||
using mbts;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
|
||||
using Columns = mbts.spark.OrderBookGridView.ColumnKeys;
|
||||
using GridSettings = mbts.ui.settings.GridSettings;
|
||||
|
||||
namespace bts.shell.orderbook {
|
||||
using ColHeader = mbts.ui.controls.DIYGrid.DefaultColumnHeaders;
|
||||
using ColWidth = mbts.ui.controls.DIYGrid.DefaultColumnWidths;
|
||||
|
||||
[ProtoContract]
|
||||
class OrderBookStartupData {
|
||||
const bool
|
||||
_default_show_right_panel = true,
|
||||
_default_show_order_params = true;
|
||||
const double
|
||||
_default_right_panel_width = 400,
|
||||
_default_order_params_height = 250,
|
||||
_default_fills_height = 300,
|
||||
_default_ladder_height = 350;
|
||||
[ProtoMember(16)]
|
||||
OrderBookRightPanelSettings _right_panel;
|
||||
// ALREADY USED TAGS
|
||||
// [ProtoMember(5)]
|
||||
// public readonly List<SortingCriteria> sort_criteria;
|
||||
|
||||
public OrderBookRightPanelSettings right_panel => _right_panel ?? (_right_panel = new OrderBookRightPanelSettings());
|
||||
/// TODO: remove properties below, and redirect callers to <see cref="right_panel"/>
|
||||
// right panel (host)
|
||||
public bool show_right_panel { get => right_panel.show; set => right_panel.show = value; }
|
||||
public double right_panel_width { get => right_panel.width; set => right_panel.width = value; }
|
||||
// order params
|
||||
public bool show_order_params { get => right_panel.stack.show_order_params; set => right_panel.stack.show_order_params = value; }
|
||||
public double order_params_height { get; set; }
|
||||
// ladder
|
||||
public bool show_ladder { get; set; }
|
||||
public double ladder_height { get => right_panel.stack.ladder_height; set => right_panel.stack.ladder_height = value; }
|
||||
// fills
|
||||
public bool show_fills { get => right_panel.stack.show_fills; set => right_panel.stack.show_fills = value; }
|
||||
public double fills_height { get => right_panel.stack.fills_height; set => right_panel.stack.fills_height = value; }
|
||||
|
||||
// filters
|
||||
[ProtoMember(10)]
|
||||
public FilterStates filters;
|
||||
// grid
|
||||
[ProtoMember(15)]
|
||||
[Obsolete]
|
||||
public GridSettings grid { get; set; }
|
||||
// orderbook grid
|
||||
[ProtoMember(17)]
|
||||
public OrderBookGridSettings orderbook_grid { get; set; }
|
||||
// fills settings
|
||||
[ProtoMember(26)]
|
||||
public OrderBookFillsGridSettings fills_settings { get; set; }
|
||||
// predicate animations
|
||||
[ProtoMember(20)]
|
||||
[Obsolete]
|
||||
List<OrderRecordTuplePredicateAnimations> _predicate_animations;
|
||||
// use defaults
|
||||
[ProtoMember(25)]
|
||||
public bool use_defaults;
|
||||
// menu display
|
||||
[ProtoMember(27)]
|
||||
public SparkMenuDisplaySettings menu_display { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Please use <see cref="Default"/> to get a new instance (the only reason the default constructor still exists is for ProtoBuf deserialization).
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public OrderBookStartupData() {
|
||||
show_right_panel = _default_show_right_panel;
|
||||
show_order_params = _default_show_order_params;
|
||||
right_panel_width = _default_right_panel_width;
|
||||
order_params_height = _default_order_params_height;
|
||||
fills_height = _default_fills_height;
|
||||
ladder_height = _default_ladder_height;
|
||||
}
|
||||
|
||||
public OrderBookStartupData(FilterStates filters, OrderBookGridSettings orderbook_grid = default, bool show_right_panel = false, double right_panel_width = 0, double fills_height = 0, bool show_fills = false, bool use_defaults = false) {
|
||||
this.filters = filters;
|
||||
this.use_defaults = use_defaults;
|
||||
this.show_right_panel = show_right_panel;
|
||||
this.right_panel_width = right_panel_width;
|
||||
this.fills_height = fills_height;
|
||||
this.show_fills = show_fills;
|
||||
this.orderbook_grid = orderbook_grid;
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
_predicate_animations = new List<OrderRecordTuplePredicateAnimations>();
|
||||
if (predicate_animations != null)
|
||||
_predicate_animations.AddRange(predicate_animations);
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
}
|
||||
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
[Obsolete]
|
||||
public IReadOnlyList<OrderRecordTuplePredicateAnimations> predicate_animations => _predicate_animations ?? (_predicate_animations = new List<OrderRecordTuplePredicateAnimations>());
|
||||
public OrderBookGridSettings grid_settings_wrapper() {
|
||||
if (use_defaults)
|
||||
return null;
|
||||
if (orderbook_grid == null) {
|
||||
if (grid == null)
|
||||
grid = _default_general_grid;
|
||||
orderbook_grid = new OrderBookGridSettings(grid.Clone(), _predicate_animations);
|
||||
}
|
||||
return orderbook_grid;
|
||||
}
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
|
||||
public static OrderBookStartupData Default =>
|
||||
new OrderBookStartupData(
|
||||
new FilterStates { statuses = new[] { OrderStatus.Working } },
|
||||
DefaultGrid,
|
||||
_default_show_right_panel,
|
||||
_default_right_panel_width,
|
||||
_default_fills_height,
|
||||
use_defaults: true) {
|
||||
show_order_params = _default_show_order_params,
|
||||
order_params_height = _default_order_params_height,
|
||||
fills_settings = DefaultFillsGrid
|
||||
};
|
||||
|
||||
public static OrderBookGridSettings DefaultGrid => new OrderBookGridSettings(_default_general_grid) {
|
||||
algo_tree_display_mode = AlgoTreeDisplayMode.RootAndLeafNodes
|
||||
};
|
||||
|
||||
public static OrderBookFillsGridSettings DefaultFillsGrid {
|
||||
get {
|
||||
var default_fills = FillsSettings.Default();
|
||||
var rv = new OrderBookFillsGridSettings {
|
||||
sorting_criteria = default_fills.sorting_criteria.ToList(),
|
||||
filters = default_fills.filters,
|
||||
general = FillsSettings.MiniGridSettings
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
static GridSettings _default_general_grid => GridSettings.Default.with(DefaultColumns.standard, DefaultColumns.sorting_criteria);
|
||||
public static OrderBookGridSettings default_mini_grid => new OrderBookGridSettings(GridSettings.Default.with(DefaultColumns.mini, DefaultColumns.sorting_criteria)) {
|
||||
algo_tree_display_mode = AlgoTreeDisplayMode.RootAndLeafNodes
|
||||
};
|
||||
|
||||
static class DefaultColumns {
|
||||
// standard
|
||||
public static GridColumnSettings security => new GridColumnSettings(Columns.Security.ToString(), DIYGrid.DefaultColumnHeaders.Security, width: DIYGrid.DefaultColumnWidths.Security);
|
||||
public static GridColumnSettings symbol => new GridColumnSettings(Columns.Symbol.ToString(), DIYGrid.DefaultColumnHeaders.Symbol, width: DIYGrid.DefaultColumnWidths.Symbol, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings time_created => new GridColumnSettings(Columns.Time.ToString(), "Time", width: DIYGrid.DefaultColumnWidths.Time, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings last_change => new GridColumnSettings(Columns.LastChange.ToString(), "Last Change", width: DIYGrid.DefaultColumnWidths.Time, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings user => new GridColumnSettings(Columns.User.ToString(), DIYGrid.DefaultColumnHeaders.User, width: DIYGrid.DefaultColumnWidths.User, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings account => new GridColumnSettings(Columns.Account.ToString(), DIYGrid.DefaultColumnHeaders.Account, width: DIYGrid.DefaultColumnWidths.Account, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings exchange => new GridColumnSettings(Columns.Exchange.ToString(), DIYGrid.DefaultColumnHeaders.Exchange, width: DIYGrid.DefaultColumnWidths.Exchange, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings time_in_force => new GridColumnSettings(Columns.TimeInForce.ToString(), DIYGrid.DefaultColumnHeaders.TimeInForce, width: DIYGrid.DefaultColumnWidths.TimeInForce);
|
||||
public static GridColumnSettings order_type => new GridColumnSettings(Columns.OrderType.ToString(), DIYGrid.DefaultColumnHeaders.OrderType, width: DIYGrid.DefaultColumnWidths.OrderType);
|
||||
public static GridColumnSettings root_algo_type => new GridColumnSettings(Columns.RootAlgoType.ToString(), DIYGrid.DefaultColumnHeaders.RootAlgoType, width: DIYGrid.DefaultColumnWidths.OrderType);
|
||||
public static GridColumnSettings sub_type => new GridColumnSettings(Columns.SubType.ToString(), "Sub Type", width: 80, horizontal_alignment: HorizontalAlignment.Left);
|
||||
|
||||
public static GridColumnSettings side => new(
|
||||
Columns.Side.ToString(), ColHeader.Side,
|
||||
width: ColWidth.Side,
|
||||
horizontal_alignment: HorizontalAlignment.Left);
|
||||
|
||||
public static GridColumnSettings quantity => new GridColumnSettings(Columns.Size.ToString(), "Qty", width: DIYGrid.DefaultColumnWidths.Qty, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings price => new GridColumnSettings(Columns.Price.ToString(), DIYGrid.DefaultColumnHeaders.Price, width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right);
|
||||
|
||||
public static GridColumnSettings filled_quantity => new(
|
||||
Columns.Filled.ToString(),
|
||||
ColHeader.FilledQuantity,
|
||||
width: ColWidth.Filled,
|
||||
horizontal_alignment: HorizontalAlignment.Right);
|
||||
|
||||
public static GridColumnSettings avg_price => new(
|
||||
Columns.AvgFilled.ToString(),
|
||||
ColHeader.AvgFilled,
|
||||
width: ColWidth.AveragePrice,
|
||||
horizontal_alignment: HorizontalAlignment.Right);
|
||||
|
||||
public static GridColumnSettings last_price => new GridColumnSettings(Columns.LastPrice.ToString(), "Last Filled", width: DIYGrid.DefaultColumnWidths.LastPrice, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings status => new GridColumnSettings(Columns.Status.ToString(), "Status", width: 80, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings last_message => new GridColumnSettings(Columns.LastMessage.ToString(), "Last Message", width: DIYGrid.DefaultColumnWidths.LastMessage, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, is_visible: false, width: DIYGrid.DefaultColumnWidths.SecurityKey, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings order_key => new GridColumnSettings(Columns.OrderKey.ToString(), DIYGrid.DefaultColumnHeaders.OrderKey, width: DIYGrid.DefaultColumnWidths.OrderKey, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings parent_key => new GridColumnSettings(Columns.ParentKey.ToString(), "Parent Key", width: DIYGrid.DefaultColumnWidths.ParentKey, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings attached_target_order_key => new GridColumnSettings(Columns.AttachedTargetOrderKey.ToString(), DIYGrid.DefaultColumnHeaders.AttachedTargetOrderKey, width: DIYGrid.DefaultColumnWidths.ParentKey, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings piq => new GridColumnSettings(Columns.Piq.ToString(), "PIQ", width: DIYGrid.DefaultColumnWidths.Qty, horizontal_alignment: HorizontalAlignment.Right);
|
||||
// editable
|
||||
public static GridColumnSettings cancel => new GridColumnSettings(Columns.Cancel.ToString(), DIYGrid.DefaultColumnHeaders.Cancel, width: DIYGrid.DefaultColumnWidths.MiniOrderBook.Cancel);
|
||||
public static GridColumnSettings attach_detach => new GridColumnSettings(Columns.Attach.ToString(), DIYGrid.DefaultColumnHeaders.AttachDetach, width: DIYGrid.DefaultColumnWidths.MiniOrderBook.AttachDetach);
|
||||
public static GridColumnSettings update => new GridColumnSettings(Columns.Update.ToString(), DIYGrid.DefaultColumnHeaders.Update, width: DIYGrid.DefaultColumnWidths.MiniOrderBook.Update);
|
||||
public static GridColumnSettings edit_quantity => new GridColumnSettings(Columns.EditableQty.ToString(), DIYGrid.DefaultColumnHeaders.Qty, width: DIYGrid.DefaultColumnWidths.MiniOrderBook.EditableSize);
|
||||
public static GridColumnSettings edit_price => new GridColumnSettings(Columns.EditablePrice.ToString(), DIYGrid.DefaultColumnHeaders.Price, width: DIYGrid.DefaultColumnWidths.MiniOrderBook.EditablePrice);
|
||||
public static GridColumnSettings commit => new GridColumnSettings(Columns.Commit.ToString(), width: DIYGrid.DefaultColumnWidths.MiniOrderBook.Commit);
|
||||
|
||||
public static IEnumerable<SortingCriteria> sorting_criteria {
|
||||
get {
|
||||
yield return new SortingCriteria(time_created.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GridColumnSettings> standard {
|
||||
get {
|
||||
yield return security;
|
||||
yield return symbol;
|
||||
yield return time_created;
|
||||
yield return last_change;
|
||||
yield return user;
|
||||
yield return account;
|
||||
yield return exchange;
|
||||
yield return time_in_force;
|
||||
yield return order_type;
|
||||
yield return root_algo_type;
|
||||
yield return sub_type;
|
||||
yield return side;
|
||||
yield return quantity;
|
||||
yield return price;
|
||||
yield return piq;
|
||||
yield return filled_quantity;
|
||||
yield return avg_price;
|
||||
yield return last_price;
|
||||
yield return status;
|
||||
yield return last_message;
|
||||
yield return security_key;
|
||||
yield return order_key;
|
||||
yield return parent_key;
|
||||
yield return attached_target_order_key;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GridColumnSettings> editable {
|
||||
get {
|
||||
yield return cancel;
|
||||
yield return update;
|
||||
yield return edit_quantity;
|
||||
yield return edit_price;
|
||||
yield return commit;
|
||||
yield return attach_detach;
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<GridColumnSettings> _mini_mode_columns_to_hide = new HashSet<GridColumnSettings> {
|
||||
symbol, last_change, exchange, sub_type, quantity, price, avg_price, last_price, status, last_message, security_key, order_key, parent_key, attached_target_order_key
|
||||
};
|
||||
|
||||
public static IEnumerable<GridColumnSettings> mini {
|
||||
get {
|
||||
foreach (var col_setting in editable)
|
||||
yield return col_setting;
|
||||
foreach (var col_setting in standard) {
|
||||
if (_mini_mode_columns_to_hide.Contains(col_setting)) {
|
||||
yield return col_setting.with(is_visible: false);
|
||||
} else {
|
||||
yield return col_setting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +1,107 @@
|
||||
using bts.api.orderman;
|
||||
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.converters;
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal partial class OrderBookGridView : DIYGrid {
|
||||
public const string
|
||||
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
|
||||
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
|
||||
RowStyleKey = "RowStyle",
|
||||
RowTemplateKey = "RowTemplateKey",
|
||||
CVRowStyleKey = "CVRowStyle",
|
||||
CVRowTemplateKey = "CVRowTemplateKey";
|
||||
|
||||
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
|
||||
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
|
||||
public enum ColumnKeys : long {
|
||||
Security = 1,
|
||||
Symbol = 1 << 1,
|
||||
Time = 1 << 2,
|
||||
LastChange = 1 << 3,
|
||||
User = 1 << 4,
|
||||
Account = 1 << 5,
|
||||
Exchange = 1 << 6,
|
||||
TimeInForce = 1 << 7,
|
||||
OrderType = 1 << 8,
|
||||
SubType = 1 << 9,
|
||||
Side = 1 << 10,
|
||||
Size = 1 << 11,
|
||||
Price = 1 << 12,
|
||||
Filled = 1 << 13,
|
||||
AvgFilled = 1 << 14,
|
||||
LastPrice = 1 << 15,
|
||||
Status = 1 << 16,
|
||||
LastMessage = 1 << 17,
|
||||
SecurityKey = 1 << 18,
|
||||
OrderKey = 1 << 19,
|
||||
ParentKey = 1 << 20,
|
||||
AttachedTargetOrderKey = 1 << 21,
|
||||
Cancel = 1 << 22,
|
||||
EditableQty = 1 << 23,
|
||||
EditablePrice = 1 << 24,
|
||||
Attach = 1 << 25,
|
||||
Detach = 1 << 26,
|
||||
Update = 1 << 27,
|
||||
Commit = 1 << 28,
|
||||
RootAlgoType = 1 << 29,
|
||||
Piq = 1 << 30,
|
||||
Check = 1 << 31,
|
||||
NotionalValue = 1L << 32,
|
||||
FilledNotionalValue = 1L << 33,
|
||||
}
|
||||
|
||||
public OrderBookGridView() {
|
||||
InitializeComponent();
|
||||
var row_template_factory = new FrameworkElementFactory(typeof(OrderBookRowView));
|
||||
row_template_factory.SetValue(OrderBookRowView.BackgroundProperty, RowBackgroundHelper.row_background_binding);
|
||||
RowTemplate = new DataTemplate { VisualTree = row_template_factory };
|
||||
}
|
||||
|
||||
static class RowBackgroundHelper {
|
||||
public static readonly BindingBase row_background_binding;
|
||||
|
||||
static RowBackgroundHelper() {
|
||||
var bg_binding = new MultiBinding();
|
||||
bg_binding.Bindings.Add(new Binding(nameof(OrderBookRowVM.status)));
|
||||
bg_binding.Bindings.Add(new Binding(nameof(ListBoxItem.IsSelected)) { RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ListBoxItem), 1) });
|
||||
double overlay_opacity = config.Configuration.get<bts.config.OrderBookOptions>().StatusColorOpacityWhenSelected;
|
||||
if (overlay_opacity > 0.1 && overlay_opacity < 0.9) {
|
||||
bg_binding.Converter = ValueConverter.create<OrderStatus, bool, Brush>((status, is_selected) => {
|
||||
var color = _status_color(status);
|
||||
return color.HasValue ? new SolidColorBrush(color.Value) { Opacity = is_selected ? overlay_opacity : 1.0 } : null;
|
||||
});
|
||||
} else {
|
||||
bg_binding.Converter = ValueConverter.create<OrderStatus, bool, Brush>((status, is_selected) => {
|
||||
if (is_selected)
|
||||
return null;
|
||||
var color = _status_color(status);
|
||||
return color.HasValue ? new SolidColorBrush(color.Value) : null;
|
||||
});
|
||||
}
|
||||
row_background_binding = bg_binding;
|
||||
}
|
||||
|
||||
static Color? _status_color(OrderStatus status) {
|
||||
switch (status) {
|
||||
case OrderStatus.Canceled: return BTSColors.Orders.canceled_order_background;
|
||||
case OrderStatus.Completed: return BTSColors.Orders.completed_order_background;
|
||||
case OrderStatus.Disconnected:
|
||||
case OrderStatus.RejectedBySpark:
|
||||
case OrderStatus.Error:
|
||||
return BTSColors.Orders.error_order_background;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM> {
|
||||
public OrderBookRowView() : base() { }
|
||||
}
|
||||
}
|
||||
using bts.api.orderman;
|
||||
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.converters;
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal partial class OrderBookGridView : DIYGrid {
|
||||
public const string
|
||||
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
|
||||
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
|
||||
RowStyleKey = "RowStyle",
|
||||
RowTemplateKey = "RowTemplateKey",
|
||||
CVRowStyleKey = "CVRowStyle",
|
||||
CVRowTemplateKey = "CVRowTemplateKey";
|
||||
|
||||
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
|
||||
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
|
||||
public enum ColumnKeys {
|
||||
Security = 1,
|
||||
Symbol = 1 << 1,
|
||||
Time = 1 << 2,
|
||||
LastChange = 1 << 3,
|
||||
User = 1 << 4,
|
||||
Account = 1 << 5,
|
||||
Exchange = 1 << 6,
|
||||
TimeInForce = 1 << 7,
|
||||
OrderType = 1 << 8,
|
||||
SubType = 1 << 9,
|
||||
Side = 1 << 10,
|
||||
Size = 1 << 11,
|
||||
Price = 1 << 12,
|
||||
Filled = 1 << 13,
|
||||
AvgFilled = 1 << 14,
|
||||
LastPrice = 1 << 15,
|
||||
Status = 1 << 16,
|
||||
LastMessage = 1 << 17,
|
||||
SecurityKey = 1 << 18,
|
||||
OrderKey = 1 << 19,
|
||||
ParentKey = 1 << 20,
|
||||
AttachedTargetOrderKey = 1 << 21,
|
||||
Cancel = 1 << 22,
|
||||
EditableQty = 1 << 23,
|
||||
EditablePrice = 1 << 24,
|
||||
Attach = 1 << 25,
|
||||
Detach = 1 << 26,
|
||||
Update = 1 << 27,
|
||||
Commit = 1 << 28,
|
||||
RootAlgoType = 1 << 29,
|
||||
Piq = 1 << 30,
|
||||
Check = 1 << 31
|
||||
}
|
||||
|
||||
public OrderBookGridView() {
|
||||
InitializeComponent();
|
||||
var row_template_factory = new FrameworkElementFactory(typeof(OrderBookRowView));
|
||||
row_template_factory.SetValue(OrderBookRowView.BackgroundProperty, RowBackgroundHelper.row_background_binding);
|
||||
RowTemplate = new DataTemplate { VisualTree = row_template_factory };
|
||||
}
|
||||
|
||||
static class RowBackgroundHelper {
|
||||
public static readonly BindingBase row_background_binding;
|
||||
|
||||
static RowBackgroundHelper() {
|
||||
var bg_binding = new MultiBinding();
|
||||
bg_binding.Bindings.Add(new Binding(nameof(OrderBookRowVM.status)));
|
||||
bg_binding.Bindings.Add(new Binding(nameof(ListBoxItem.IsSelected)) { RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ListBoxItem), 1) });
|
||||
double overlay_opacity = config.Configuration.get<bts.config.OrderBookOptions>().StatusColorOpacityWhenSelected;
|
||||
if (overlay_opacity > 0.1 && overlay_opacity < 0.9) {
|
||||
bg_binding.Converter = ValueConverter.create<OrderStatus, bool, Brush>((status, is_selected) => {
|
||||
var color = _status_color(status);
|
||||
return color.HasValue ? new SolidColorBrush(color.Value) { Opacity = is_selected ? overlay_opacity : 1.0 } : null;
|
||||
});
|
||||
} else {
|
||||
bg_binding.Converter = ValueConverter.create<OrderStatus, bool, Brush>((status, is_selected) => {
|
||||
if (is_selected)
|
||||
return null;
|
||||
var color = _status_color(status);
|
||||
return color.HasValue ? new SolidColorBrush(color.Value) : null;
|
||||
});
|
||||
}
|
||||
row_background_binding = bg_binding;
|
||||
}
|
||||
|
||||
static Color? _status_color(OrderStatus status) {
|
||||
switch (status) {
|
||||
case OrderStatus.Canceled: return BTSColors.Orders.canceled_order_background;
|
||||
case OrderStatus.Completed: return BTSColors.Orders.completed_order_background;
|
||||
case OrderStatus.Disconnected:
|
||||
case OrderStatus.RejectedBySpark:
|
||||
case OrderStatus.Error:
|
||||
return BTSColors.Orders.error_order_background;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM> {
|
||||
public OrderBookRowView() : base() { }
|
||||
}
|
||||
}
|
||||
@@ -289,8 +289,7 @@ namespace bts.shell.pnl {
|
||||
|
||||
public AccountTuple account => new(account_id, account_name);
|
||||
public string symbol => security?.pnl_symbol() ?? string.Empty;
|
||||
public DateTime expiration =>
|
||||
security?.normal_expirations()?.MinOrDefault(DateTime.MinValue) ?? DateTime.MinValue;
|
||||
public DateTime expiration => security?.earliest_expiration() ?? DateTime.MinValue;
|
||||
public double effective_cpp => override_cpp ?? default_cpp;
|
||||
|
||||
/// Market/Calc
|
||||
|
||||
@@ -1,380 +1,378 @@
|
||||
using bts.api;
|
||||
using bts.utils;
|
||||
|
||||
using mbts;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
|
||||
using Spark.Core.Collections;
|
||||
using Spark.Core.Data;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
using ColumnKeys = bts.shell.pnl.PNLView.Columns;
|
||||
using PNLKey = bts.AccountSecurityKey;
|
||||
|
||||
namespace bts.shell.pnl {
|
||||
class PNLRowVM : DisposableViewModelBase, IEquatable<PNLRowVM> {
|
||||
public static readonly IComparer<PNLRowVM> key_comparer = Comparer<PNLKey>.Default.wrap<PNLKey, PNLRowVM>(r => r.key);
|
||||
static readonly Func<bool, string> _fmt_has_day_trades = hdt => hdt ? "Yes" : "No";
|
||||
//
|
||||
readonly PNLCellIndexBuilder<PNLRowData> _cell_map = new PNLCellIndexBuilder<PNLRowData>();
|
||||
readonly PNLCellEmbeddedAnimationModel<PNLRowData> _animator;
|
||||
readonly ISecurityMetadataStore _sec_meta_store;
|
||||
bool _refresh_normalized_data = true, _is_selected;
|
||||
PNLNormalizedData _normalized_data;
|
||||
api.domain.TickDisplaySettingsImmutable? _quantity_display_backing;
|
||||
|
||||
public PNLRowVM(
|
||||
PNLRowData data,
|
||||
ISecurityMetadataStore sec_meta_store,
|
||||
ImmutableDictionary<ColumnKeys, PNLCellAppearance> default_appearances,
|
||||
IReadOnlyList<IPNLAnimationTrigger<PNLRowData>> triggers,
|
||||
Func<double, string> fmt_fill_price,
|
||||
Func<double, string> fmt_market_price) {
|
||||
//
|
||||
this.data = data;
|
||||
_sec_meta_store = sec_meta_store;
|
||||
_animator = new PNLCellEmbeddedAnimationModel<PNLRowData>(default_appearances, triggers);
|
||||
_animator.on_row(data);
|
||||
pnl_price = _cell_map.add_price_double(ColumnKeys.PNLPrice, d => d.pnl_price);
|
||||
account_cell = _cell_map.add(ColumnKeys.Account, d => d.account);
|
||||
security_type_cell = _cell_map.add(ColumnKeys.SecurityType, d => d.security?.ContractCase ?? Security.ContractOneofCase.None);
|
||||
exchange_cell = _cell_map.add(ColumnKeys.Exchange, d => d.security?.Exchange ?? Venue.Unset);
|
||||
expiration_cell = _cell_map.add(
|
||||
ColumnKeys.Expiration,
|
||||
d => d.expiration,
|
||||
e => e == DateTime.MinValue
|
||||
? "N/A"
|
||||
: SecurityFormatter.format_expiration(e));
|
||||
symbol_cell = _cell_map.add(ColumnKeys.Symbol, d => d.symbol);
|
||||
security_cell = _cell_map.add(
|
||||
ColumnKeys.Security,
|
||||
d => d.security,
|
||||
null,
|
||||
EqualityComparer<SecurityImmutable?>.Default,
|
||||
Comparer<SecurityImmutable?>.Default);
|
||||
security_key = _cell_map.add(ColumnKeys.SecurityKey, d => d.key.security_id);
|
||||
cpp = _cell_map.add(ColumnKeys.CPP, d => d.effective_cpp);
|
||||
// sod
|
||||
sod_pnl = _cell_map.add_pnl_double(ColumnKeys.SODPNL, d => d.sod_pnl);
|
||||
sod_net_pos = _cell_map.add_net_pos(
|
||||
ColumnKeys.SODNetPos, d => d.display_sod_net_pos, fmt_qty);
|
||||
sod_long_pos = _cell_map.add_long_pos(
|
||||
ColumnKeys.SODLongPos, d => d.display_sod_long_pos, fmt_qty);
|
||||
sod_long_avg_price = _cell_map.add_long_price(ColumnKeys.SODLongAvgPrice, d => d.sod_long_avg_price);
|
||||
sod_short_pos = _cell_map.add_short_pos(
|
||||
ColumnKeys.SODShortPos, d => d.display_sod_short_pos, fmt_qty);
|
||||
sod_short_avg_price = _cell_map.add_short_price(ColumnKeys.SODShortAvgPrice, d => d.sod_short_avg_price);
|
||||
sod_delta = _cell_map.add_pnl_double(ColumnKeys.SODDelta, d => d.sod_delta, fmt_greek);
|
||||
sod_gamma = _cell_map.add_pnl_double(ColumnKeys.SODGamma, d => d.sod_gamma, fmt_greek);
|
||||
sod_vega = _cell_map.add_pnl_double(ColumnKeys.SODVega, d => d.sod_vega, fmt_greek);
|
||||
sod_theta = _cell_map.add_pnl_double(ColumnKeys.SODTheta, d => d.sod_theta, fmt_greek);
|
||||
|
||||
// day
|
||||
day_pnl = _cell_map.add_pnl_double(ColumnKeys.DayPNL, d => d.day_pnl);
|
||||
day_net_pos = _cell_map.add_net_pos(
|
||||
ColumnKeys.DayNetPos, d => d.display_day_net_pos, fmt_qty);
|
||||
day_long_pos = _cell_map.add_long_pos(
|
||||
ColumnKeys.DayLongPos, d => d.display_day_long_pos, fmt_qty);
|
||||
day_long_avg_price = _cell_map.add_long_price(ColumnKeys.DayLongAvgPrice, d => d.day_long_avg_price);
|
||||
day_short_pos = _cell_map.add_short_pos(
|
||||
ColumnKeys.DayShortPos, d => d.display_day_short_pos, fmt_qty);
|
||||
day_short_avg_price = _cell_map.add_short_price(ColumnKeys.DayShortAvgPrice, d => d.day_short_avg_price);
|
||||
day_delta = _cell_map.add_pnl_double(ColumnKeys.DayDelta, d => d.day_delta, fmt_greek);
|
||||
day_gamma = _cell_map.add_pnl_double(ColumnKeys.DayGamma, d => d.day_gamma, fmt_greek);
|
||||
day_vega = _cell_map.add_pnl_double(ColumnKeys.DayVega, d => d.day_vega, fmt_greek);
|
||||
day_theta = _cell_map.add_pnl_double(ColumnKeys.DayTheta, d => d.day_theta, fmt_greek);
|
||||
has_day_trades = _cell_map.add(ColumnKeys.HasDayTrades, d => d.has_day_trades, _fmt_has_day_trades);
|
||||
// working
|
||||
working_net_pos = _cell_map.add_net_pos(
|
||||
ColumnKeys.WorkingNetPos, d => d.display_working_net_pos, fmt_qty);
|
||||
var qty_display = _sec_meta_store.get_qty_display(security);
|
||||
working_long_pos = _cell_map.add_working_long(
|
||||
ColumnKeys.WorkingLongPos,
|
||||
d => d.working.long_data,
|
||||
d => d.bid_price,
|
||||
qty_display);
|
||||
working_long_avg_price = _cell_map.add_long_price(ColumnKeys.WorkingLongAvgPrice, d => d.working_long_avg_price);
|
||||
working_short_pos = _cell_map.add_working_short(
|
||||
ColumnKeys.WorkingShortPos, r => r.working.short_data, r => r.ask_price, qty_display);
|
||||
working_short_avg_price = _cell_map.add_short_price(ColumnKeys.WorkingShortAvgPrice, d => d.working_short_avg_price);
|
||||
// total
|
||||
total_pnl = _cell_map.add_pnl_double(ColumnKeys.TotalPNL, d => d.total_pnl);
|
||||
total_net_pos = _cell_map.add_net_pos(
|
||||
ColumnKeys.TotalNetPos, d => d.display_total_net_pos, fmt_qty);
|
||||
total_long_pos = _cell_map.add_long_pos(
|
||||
ColumnKeys.TotalLongPos, d => d.display_total_long_pos, fmt_qty);
|
||||
total_long_avg_price = _cell_map.add_long_price(ColumnKeys.TotalLongAvgPrice, d => d.total_long_avg_price);
|
||||
total_short_pos = _cell_map.add_short_pos(
|
||||
ColumnKeys.TotalShortPos, d => d.display_total_short_pos, fmt_qty);
|
||||
total_short_avg_price = _cell_map.add_short_price(ColumnKeys.TotalShortAvgPrice, d => d.total_short_avg_price);
|
||||
total_delta = _cell_map.add_pnl_double(
|
||||
ColumnKeys.TotalDelta, d => d.total_delta, fmt_greek);
|
||||
total_gamma = _cell_map.add_pnl_double(
|
||||
ColumnKeys.TotalGamma, d => d.total_gamma, fmt_greek);
|
||||
total_vega = _cell_map.add_pnl_double(ColumnKeys.TotalVega, d => d.total_vega, fmt_greek);
|
||||
total_theta = _cell_map.add_pnl_double(
|
||||
ColumnKeys.TotalTheta, d => d.total_theta, fmt_greek);
|
||||
|
||||
// market
|
||||
bid_price = _cell_map.add_price_double(ColumnKeys.BidPrice, d => d.bid_price);
|
||||
ask_price = _cell_map.add_price_double(ColumnKeys.AskPrice, d => d.ask_price);
|
||||
mid_price = _cell_map.add_price_double(ColumnKeys.MidPrice, d => d.mid_price);
|
||||
last_price = _cell_map.add_price_double(ColumnKeys.LastPrice, d => d.last_price);
|
||||
settlement_price = _cell_map.add_price_double(ColumnKeys.SettlementPrice, d => d.settlement_price);
|
||||
|
||||
_set_price_formatting_delegates(fmt_fill_price, fmt_market_price);
|
||||
_cell_map.on_row(data);
|
||||
_cell_map.set_appearances(_animator.cell_appearances);
|
||||
_dispose = _cell_map.animation_model.OnDispatcher().Subscribe(new_animations => {
|
||||
animations = new_animations.Select(kv => new mbts.ui.spark.CellAnimation(kv.Key.column, kv.Key.type, kv.Value.color.to_brush())).ToList();
|
||||
RaisePropertyChanged(nameof(animations));
|
||||
});
|
||||
|
||||
static string fmt_greek(double v) =>
|
||||
v.is_nan() ? "-" : v.ToString("#,0.00");
|
||||
|
||||
string fmt_qty(double v) =>
|
||||
quantity_display.format(v);
|
||||
}
|
||||
|
||||
// events
|
||||
public event Action<PNLNormalizedData> updated;
|
||||
public event Action<bool> is_selected_changed;
|
||||
|
||||
// data
|
||||
public PNLKey key => data.key;
|
||||
public PNLRowData data { get; private set; }
|
||||
public PNLNormalizedData normalized_data {
|
||||
get {
|
||||
if (_refresh_normalized_data) {
|
||||
_refresh_normalized_data = false;
|
||||
// Only count outrights (spread positions should be normalized into outrights)
|
||||
var sec_type = security?.ContractCase ?? Security.ContractOneofCase.None;
|
||||
var (
|
||||
sod_long,
|
||||
sod_short,
|
||||
sod_delta,
|
||||
sod_gamma,
|
||||
sod_vega,
|
||||
sod_theta,
|
||||
day_long,
|
||||
day_short,
|
||||
day_delta,
|
||||
day_gamma,
|
||||
day_vega,
|
||||
day_theta) = sec_type switch {
|
||||
Security.ContractOneofCase.Future or Security.ContractOneofCase.Option => (
|
||||
data.sod.long_pos.round_to_decimal(),
|
||||
data.sod.short_pos.round_to_decimal(),
|
||||
data.sod.delta.round_to_decimal(),
|
||||
data.sod.gamma.round_to_decimal(),
|
||||
data.sod.vega.round_to_decimal(),
|
||||
data.sod.theta.round_to_decimal(),
|
||||
data.day.long_pos.round_to_decimal(),
|
||||
data.day.short_pos.round_to_decimal(),
|
||||
data.day.delta.round_to_decimal(),
|
||||
data.day.gamma.round_to_decimal(),
|
||||
data.day.vega.round_to_decimal(),
|
||||
data.day.theta.round_to_decimal()),
|
||||
_ => (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
};
|
||||
|
||||
var working_long = data.working_long_pos.round_to_decimal();
|
||||
var working_short = data.working_short_pos.round_to_decimal();
|
||||
|
||||
var nd = new PNLNormalizedData() {
|
||||
// sod
|
||||
sod_pnl = sod_pnl.value.round_to_decimal(),
|
||||
sod_long_pos = sod_long,
|
||||
sod_short_pos = sod_short,
|
||||
sod_delta = sod_delta,
|
||||
sod_gamma = sod_gamma,
|
||||
sod_vega = sod_vega,
|
||||
sod_theta = sod_theta,
|
||||
display_sod_long_pos = sod_long,
|
||||
display_sod_short_pos = sod_short,
|
||||
|
||||
// day
|
||||
day_pnl = day_pnl.value.round_to_decimal(),
|
||||
day_long_pos = day_long,
|
||||
day_short_pos = day_short,
|
||||
day_delta = day_delta,
|
||||
day_gamma = day_gamma,
|
||||
day_vega = day_vega,
|
||||
day_theta = day_theta,
|
||||
display_day_long_pos = day_long,
|
||||
display_day_short_pos = day_short,
|
||||
|
||||
// working
|
||||
working_long_pos = working_long,
|
||||
working_short_pos = working_short,
|
||||
display_working_long_pos = working_long,
|
||||
display_working_short_pos = working_short
|
||||
};
|
||||
if (_normalized_data != nd) {
|
||||
_normalized_data = nd;
|
||||
updated?.Invoke(nd);
|
||||
}
|
||||
}
|
||||
return _normalized_data;
|
||||
}
|
||||
}
|
||||
// security
|
||||
public string account_name => data.account_name;
|
||||
public Venue exchange => security?.Exchange ?? Venue.Unset;
|
||||
public string symbol => security.symbol_or_default();
|
||||
public DateTime expiration => data.expiration;
|
||||
public Security.ContractOneofCase security_type => security?.ContractCase ?? Security.ContractOneofCase.None;
|
||||
public SecurityImmutable? security => data.security;
|
||||
// cells
|
||||
public PNLCellVM<PNLRowData, AccountTuple> account_cell { get; }
|
||||
public PNLCellVM<PNLRowData, Venue> exchange_cell { get; }
|
||||
public PNLCellVM<PNLRowData, DateTime> expiration_cell { get; }
|
||||
public PNLCellVM<PNLRowData, string> symbol_cell { get; }
|
||||
public PNLCellVM<PNLRowData, Security.ContractOneofCase> security_type_cell { get; }
|
||||
public PNLCellVM<PNLRowData, SecurityImmutable?> security_cell { get; }
|
||||
public PNLCellVM<PNLRowData, uint> security_key { get; }
|
||||
public PNLCellVM<PNLRowData, double> cpp { get; }
|
||||
// sod
|
||||
public PNLCellVM<PNLRowData, double> sod_pnl { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_net_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_long_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_long_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_short_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_short_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_delta { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_gamma { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_vega { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_theta { get; }
|
||||
|
||||
// day
|
||||
public PNLCellVM<PNLRowData, double> day_pnl { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_net_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_long_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_long_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_short_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_short_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_delta { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_gamma { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_vega { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_theta { get; }
|
||||
public PNLCellVM<PNLRowData, bool> has_day_trades { get; }
|
||||
// working
|
||||
public PNLCellVM<PNLRowData, double> working_net_pos { get; }
|
||||
public PNLCellVM<PNLRowData, WorkingOrderSideData> working_long_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> working_long_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, WorkingOrderSideData> working_short_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> working_short_avg_price { get; }
|
||||
// total
|
||||
public PNLCellVM<PNLRowData, double> total_pnl { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_net_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_long_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_long_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_short_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_short_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_delta { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_gamma { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_vega { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_theta { get; }
|
||||
|
||||
// (market)
|
||||
public PNLCellVM<PNLRowData, double> bid_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> mid_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> ask_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> last_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> settlement_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> pnl_price { get; }
|
||||
|
||||
// display
|
||||
public api.domain.TickDisplaySettingsImmutable quantity_display {
|
||||
get {
|
||||
if (_quantity_display_backing.HasValue) {
|
||||
return _quantity_display_backing.Value;
|
||||
}
|
||||
if (security.HasValue) {
|
||||
_quantity_display_backing = _sec_meta_store.get_qty_display(security.Value);
|
||||
}
|
||||
return _quantity_display_backing ??= Security.Defaults.quantity_display_settingsi;
|
||||
}
|
||||
}
|
||||
|
||||
public IList<mbts.ui.spark.CellAnimation> animations { get; private set; }
|
||||
|
||||
public bool is_selected {
|
||||
get => _is_selected;
|
||||
set {
|
||||
if (Set(ref _is_selected, value))
|
||||
is_selected_changed?.Invoke(value);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<PNLCellVM<PNLRowData, double>> _market_price_cells {
|
||||
get {
|
||||
yield return pnl_price;
|
||||
yield return last_price;
|
||||
yield return bid_price;
|
||||
yield return mid_price;
|
||||
yield return ask_price;
|
||||
yield return settlement_price;
|
||||
}
|
||||
}
|
||||
|
||||
// includes sod, day, total, and working avg price columns
|
||||
IEnumerable<PNLCellVM<PNLRowData, double>> _fill_price_cells {
|
||||
get {
|
||||
// sod
|
||||
yield return sod_long_avg_price;
|
||||
yield return sod_short_avg_price;
|
||||
// day
|
||||
yield return day_long_avg_price;
|
||||
yield return day_short_avg_price;
|
||||
// total
|
||||
yield return total_long_avg_price;
|
||||
yield return total_short_avg_price;
|
||||
// working
|
||||
yield return working_long_avg_price;
|
||||
yield return working_short_avg_price;
|
||||
}
|
||||
}
|
||||
|
||||
public void update(PNLRowData data) {
|
||||
_quantity_display_backing = null;
|
||||
_refresh_normalized_data = true;
|
||||
this.data = data;
|
||||
_cell_map.on_row(data);
|
||||
_animator.on_row(data);
|
||||
_cell_map.set_appearances(_animator.cell_appearances);
|
||||
}
|
||||
|
||||
public void on_settings(ImmutableDictionary<ColumnKeys, PNLCellAppearance> defaults, IReadOnlyList<IPNLAnimationTrigger<PNLRowData>> triggers, Func<double, string> fmt_fill_price, Func<double, string> fmt_market_price) {
|
||||
_animator.on_settings(defaults, triggers);
|
||||
_cell_map.set_appearances(_animator.cell_appearances);
|
||||
_set_price_formatting_delegates(fmt_fill_price, fmt_market_price);
|
||||
}
|
||||
|
||||
public bool Equals(PNLRowVM other) =>
|
||||
other != null && key == other.key;
|
||||
|
||||
public override int GetHashCode() =>
|
||||
key.GetHashCode();
|
||||
|
||||
public override string ToString() =>
|
||||
$"account: {account_name} exchange: {exchange} symbol: {symbol} security: {security} buy_quantity: {day_long_pos} sell_quantity: {day_short_pos} pnl: {total_pnl}";
|
||||
|
||||
void _set_price_formatting_delegates(Func<double, string> fmt_fill_price, Func<double, string> fmt_market_price) {
|
||||
foreach (var cell in _fill_price_cells) {
|
||||
cell.format = fmt_fill_price;
|
||||
}
|
||||
foreach (var cell in _market_price_cells) {
|
||||
cell.format = fmt_market_price;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using bts.api;
|
||||
using bts.utils;
|
||||
|
||||
using mbts;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
|
||||
using Spark.Core.Collections;
|
||||
using Spark.Core.Data;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
using ColumnKeys = bts.shell.pnl.PNLView.Columns;
|
||||
using PNLKey = bts.AccountSecurityKey;
|
||||
|
||||
namespace bts.shell.pnl {
|
||||
class PNLRowVM : DisposableViewModelBase, IEquatable<PNLRowVM> {
|
||||
public static readonly IComparer<PNLRowVM> key_comparer = Comparer<PNLKey>.Default.wrap<PNLKey, PNLRowVM>(r => r.key);
|
||||
static readonly Func<bool, string> _fmt_has_day_trades = hdt => hdt ? "Yes" : "No";
|
||||
//
|
||||
readonly PNLCellIndexBuilder<PNLRowData> _cell_map = new PNLCellIndexBuilder<PNLRowData>();
|
||||
readonly PNLCellEmbeddedAnimationModel<PNLRowData> _animator;
|
||||
readonly ISecurityMetadataStore _sec_meta_store;
|
||||
bool _refresh_normalized_data = true, _is_selected;
|
||||
PNLNormalizedData _normalized_data;
|
||||
api.domain.TickDisplaySettingsImmutable? _quantity_display_backing;
|
||||
|
||||
public PNLRowVM(
|
||||
PNLRowData data,
|
||||
ISecurityMetadataStore sec_meta_store,
|
||||
ImmutableDictionary<ColumnKeys, PNLCellAppearance> default_appearances,
|
||||
IReadOnlyList<IPNLAnimationTrigger<PNLRowData>> triggers,
|
||||
Func<double, string> fmt_fill_price,
|
||||
Func<double, string> fmt_market_price) {
|
||||
//
|
||||
this.data = data;
|
||||
_sec_meta_store = sec_meta_store;
|
||||
_animator = new PNLCellEmbeddedAnimationModel<PNLRowData>(default_appearances, triggers);
|
||||
_animator.on_row(data);
|
||||
pnl_price = _cell_map.add_price_double(ColumnKeys.PNLPrice, d => d.pnl_price);
|
||||
account_cell = _cell_map.add(ColumnKeys.Account, d => d.account);
|
||||
security_type_cell = _cell_map.add(ColumnKeys.SecurityType, d => d.security?.ContractCase ?? Security.ContractOneofCase.None);
|
||||
exchange_cell = _cell_map.add(ColumnKeys.Exchange, d => d.security?.Exchange ?? Venue.Unset);
|
||||
expiration_cell = _cell_map.add(
|
||||
ColumnKeys.Expiration,
|
||||
d => d.expiration,
|
||||
e => SecurityFormatter.format_expiration(e));
|
||||
symbol_cell = _cell_map.add(ColumnKeys.Symbol, d => d.symbol);
|
||||
security_cell = _cell_map.add(
|
||||
ColumnKeys.Security,
|
||||
d => d.security,
|
||||
null,
|
||||
EqualityComparer<SecurityImmutable?>.Default,
|
||||
Comparer<SecurityImmutable?>.Default);
|
||||
security_key = _cell_map.add(ColumnKeys.SecurityKey, d => d.key.security_id);
|
||||
cpp = _cell_map.add(ColumnKeys.CPP, d => d.effective_cpp);
|
||||
// sod
|
||||
sod_pnl = _cell_map.add_pnl_double(ColumnKeys.SODPNL, d => d.sod_pnl);
|
||||
sod_net_pos = _cell_map.add_net_pos(
|
||||
ColumnKeys.SODNetPos, d => d.display_sod_net_pos, fmt_qty);
|
||||
sod_long_pos = _cell_map.add_long_pos(
|
||||
ColumnKeys.SODLongPos, d => d.display_sod_long_pos, fmt_qty);
|
||||
sod_long_avg_price = _cell_map.add_long_price(ColumnKeys.SODLongAvgPrice, d => d.sod_long_avg_price);
|
||||
sod_short_pos = _cell_map.add_short_pos(
|
||||
ColumnKeys.SODShortPos, d => d.display_sod_short_pos, fmt_qty);
|
||||
sod_short_avg_price = _cell_map.add_short_price(ColumnKeys.SODShortAvgPrice, d => d.sod_short_avg_price);
|
||||
sod_delta = _cell_map.add_pnl_double(ColumnKeys.SODDelta, d => d.sod_delta, fmt_greek);
|
||||
sod_gamma = _cell_map.add_pnl_double(ColumnKeys.SODGamma, d => d.sod_gamma, fmt_greek);
|
||||
sod_vega = _cell_map.add_pnl_double(ColumnKeys.SODVega, d => d.sod_vega, fmt_greek);
|
||||
sod_theta = _cell_map.add_pnl_double(ColumnKeys.SODTheta, d => d.sod_theta, fmt_greek);
|
||||
|
||||
// day
|
||||
day_pnl = _cell_map.add_pnl_double(ColumnKeys.DayPNL, d => d.day_pnl);
|
||||
day_net_pos = _cell_map.add_net_pos(
|
||||
ColumnKeys.DayNetPos, d => d.display_day_net_pos, fmt_qty);
|
||||
day_long_pos = _cell_map.add_long_pos(
|
||||
ColumnKeys.DayLongPos, d => d.display_day_long_pos, fmt_qty);
|
||||
day_long_avg_price = _cell_map.add_long_price(ColumnKeys.DayLongAvgPrice, d => d.day_long_avg_price);
|
||||
day_short_pos = _cell_map.add_short_pos(
|
||||
ColumnKeys.DayShortPos, d => d.display_day_short_pos, fmt_qty);
|
||||
day_short_avg_price = _cell_map.add_short_price(ColumnKeys.DayShortAvgPrice, d => d.day_short_avg_price);
|
||||
day_delta = _cell_map.add_pnl_double(ColumnKeys.DayDelta, d => d.day_delta, fmt_greek);
|
||||
day_gamma = _cell_map.add_pnl_double(ColumnKeys.DayGamma, d => d.day_gamma, fmt_greek);
|
||||
day_vega = _cell_map.add_pnl_double(ColumnKeys.DayVega, d => d.day_vega, fmt_greek);
|
||||
day_theta = _cell_map.add_pnl_double(ColumnKeys.DayTheta, d => d.day_theta, fmt_greek);
|
||||
has_day_trades = _cell_map.add(ColumnKeys.HasDayTrades, d => d.has_day_trades, _fmt_has_day_trades);
|
||||
// working
|
||||
working_net_pos = _cell_map.add_net_pos(
|
||||
ColumnKeys.WorkingNetPos, d => d.display_working_net_pos, fmt_qty);
|
||||
var qty_display = _sec_meta_store.get_qty_display(security);
|
||||
working_long_pos = _cell_map.add_working_long(
|
||||
ColumnKeys.WorkingLongPos,
|
||||
d => d.working.long_data,
|
||||
d => d.bid_price,
|
||||
qty_display);
|
||||
working_long_avg_price = _cell_map.add_long_price(ColumnKeys.WorkingLongAvgPrice, d => d.working_long_avg_price);
|
||||
working_short_pos = _cell_map.add_working_short(
|
||||
ColumnKeys.WorkingShortPos, r => r.working.short_data, r => r.ask_price, qty_display);
|
||||
working_short_avg_price = _cell_map.add_short_price(ColumnKeys.WorkingShortAvgPrice, d => d.working_short_avg_price);
|
||||
// total
|
||||
total_pnl = _cell_map.add_pnl_double(ColumnKeys.TotalPNL, d => d.total_pnl);
|
||||
total_net_pos = _cell_map.add_net_pos(
|
||||
ColumnKeys.TotalNetPos, d => d.display_total_net_pos, fmt_qty);
|
||||
total_long_pos = _cell_map.add_long_pos(
|
||||
ColumnKeys.TotalLongPos, d => d.display_total_long_pos, fmt_qty);
|
||||
total_long_avg_price = _cell_map.add_long_price(ColumnKeys.TotalLongAvgPrice, d => d.total_long_avg_price);
|
||||
total_short_pos = _cell_map.add_short_pos(
|
||||
ColumnKeys.TotalShortPos, d => d.display_total_short_pos, fmt_qty);
|
||||
total_short_avg_price = _cell_map.add_short_price(ColumnKeys.TotalShortAvgPrice, d => d.total_short_avg_price);
|
||||
total_delta = _cell_map.add_pnl_double(
|
||||
ColumnKeys.TotalDelta, d => d.total_delta, fmt_greek);
|
||||
total_gamma = _cell_map.add_pnl_double(
|
||||
ColumnKeys.TotalGamma, d => d.total_gamma, fmt_greek);
|
||||
total_vega = _cell_map.add_pnl_double(ColumnKeys.TotalVega, d => d.total_vega, fmt_greek);
|
||||
total_theta = _cell_map.add_pnl_double(
|
||||
ColumnKeys.TotalTheta, d => d.total_theta, fmt_greek);
|
||||
|
||||
// market
|
||||
bid_price = _cell_map.add_price_double(ColumnKeys.BidPrice, d => d.bid_price);
|
||||
ask_price = _cell_map.add_price_double(ColumnKeys.AskPrice, d => d.ask_price);
|
||||
mid_price = _cell_map.add_price_double(ColumnKeys.MidPrice, d => d.mid_price);
|
||||
last_price = _cell_map.add_price_double(ColumnKeys.LastPrice, d => d.last_price);
|
||||
settlement_price = _cell_map.add_price_double(ColumnKeys.SettlementPrice, d => d.settlement_price);
|
||||
|
||||
_set_price_formatting_delegates(fmt_fill_price, fmt_market_price);
|
||||
_cell_map.on_row(data);
|
||||
_cell_map.set_appearances(_animator.cell_appearances);
|
||||
_dispose = _cell_map.animation_model.OnDispatcher().Subscribe(new_animations => {
|
||||
animations = new_animations.Select(kv => new mbts.ui.spark.CellAnimation(kv.Key.column, kv.Key.type, kv.Value.color.to_brush())).ToList();
|
||||
RaisePropertyChanged(nameof(animations));
|
||||
});
|
||||
|
||||
static string fmt_greek(double v) =>
|
||||
v.is_nan() ? "-" : v.ToString("#,0.00");
|
||||
|
||||
string fmt_qty(double v) =>
|
||||
quantity_display.format(v);
|
||||
}
|
||||
|
||||
// events
|
||||
public event Action<PNLNormalizedData> updated;
|
||||
public event Action<bool> is_selected_changed;
|
||||
|
||||
// data
|
||||
public PNLKey key => data.key;
|
||||
public PNLRowData data { get; private set; }
|
||||
public PNLNormalizedData normalized_data {
|
||||
get {
|
||||
if (_refresh_normalized_data) {
|
||||
_refresh_normalized_data = false;
|
||||
// Only count outrights (spread positions should be normalized into outrights)
|
||||
var sec_type = security?.ContractCase ?? Security.ContractOneofCase.None;
|
||||
var (
|
||||
sod_long,
|
||||
sod_short,
|
||||
sod_delta,
|
||||
sod_gamma,
|
||||
sod_vega,
|
||||
sod_theta,
|
||||
day_long,
|
||||
day_short,
|
||||
day_delta,
|
||||
day_gamma,
|
||||
day_vega,
|
||||
day_theta) = sec_type switch {
|
||||
Security.ContractOneofCase.Future or Security.ContractOneofCase.Option => (
|
||||
data.sod.long_pos.round_to_decimal(),
|
||||
data.sod.short_pos.round_to_decimal(),
|
||||
data.sod.delta.round_to_decimal(),
|
||||
data.sod.gamma.round_to_decimal(),
|
||||
data.sod.vega.round_to_decimal(),
|
||||
data.sod.theta.round_to_decimal(),
|
||||
data.day.long_pos.round_to_decimal(),
|
||||
data.day.short_pos.round_to_decimal(),
|
||||
data.day.delta.round_to_decimal(),
|
||||
data.day.gamma.round_to_decimal(),
|
||||
data.day.vega.round_to_decimal(),
|
||||
data.day.theta.round_to_decimal()),
|
||||
_ => (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
};
|
||||
|
||||
var working_long = data.working_long_pos.round_to_decimal();
|
||||
var working_short = data.working_short_pos.round_to_decimal();
|
||||
|
||||
var nd = new PNLNormalizedData() {
|
||||
// sod
|
||||
sod_pnl = sod_pnl.value.round_to_decimal(),
|
||||
sod_long_pos = sod_long,
|
||||
sod_short_pos = sod_short,
|
||||
sod_delta = sod_delta,
|
||||
sod_gamma = sod_gamma,
|
||||
sod_vega = sod_vega,
|
||||
sod_theta = sod_theta,
|
||||
display_sod_long_pos = sod_long,
|
||||
display_sod_short_pos = sod_short,
|
||||
|
||||
// day
|
||||
day_pnl = day_pnl.value.round_to_decimal(),
|
||||
day_long_pos = day_long,
|
||||
day_short_pos = day_short,
|
||||
day_delta = day_delta,
|
||||
day_gamma = day_gamma,
|
||||
day_vega = day_vega,
|
||||
day_theta = day_theta,
|
||||
display_day_long_pos = day_long,
|
||||
display_day_short_pos = day_short,
|
||||
|
||||
// working
|
||||
working_long_pos = working_long,
|
||||
working_short_pos = working_short,
|
||||
display_working_long_pos = working_long,
|
||||
display_working_short_pos = working_short
|
||||
};
|
||||
if (_normalized_data != nd) {
|
||||
_normalized_data = nd;
|
||||
updated?.Invoke(nd);
|
||||
}
|
||||
}
|
||||
return _normalized_data;
|
||||
}
|
||||
}
|
||||
// security
|
||||
public string account_name => data.account_name;
|
||||
public Venue exchange => security?.Exchange ?? Venue.Unset;
|
||||
public string symbol => security.symbol_or_default();
|
||||
public DateTime expiration => data.expiration;
|
||||
public Security.ContractOneofCase security_type => security?.ContractCase ?? Security.ContractOneofCase.None;
|
||||
public SecurityImmutable? security => data.security;
|
||||
// cells
|
||||
public PNLCellVM<PNLRowData, AccountTuple> account_cell { get; }
|
||||
public PNLCellVM<PNLRowData, Venue> exchange_cell { get; }
|
||||
public PNLCellVM<PNLRowData, DateTime> expiration_cell { get; }
|
||||
public PNLCellVM<PNLRowData, string> symbol_cell { get; }
|
||||
public PNLCellVM<PNLRowData, Security.ContractOneofCase> security_type_cell { get; }
|
||||
public PNLCellVM<PNLRowData, SecurityImmutable?> security_cell { get; }
|
||||
public PNLCellVM<PNLRowData, uint> security_key { get; }
|
||||
public PNLCellVM<PNLRowData, double> cpp { get; }
|
||||
// sod
|
||||
public PNLCellVM<PNLRowData, double> sod_pnl { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_net_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_long_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_long_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_short_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_short_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_delta { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_gamma { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_vega { get; }
|
||||
public PNLCellVM<PNLRowData, double> sod_theta { get; }
|
||||
|
||||
// day
|
||||
public PNLCellVM<PNLRowData, double> day_pnl { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_net_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_long_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_long_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_short_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_short_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_delta { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_gamma { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_vega { get; }
|
||||
public PNLCellVM<PNLRowData, double> day_theta { get; }
|
||||
public PNLCellVM<PNLRowData, bool> has_day_trades { get; }
|
||||
// working
|
||||
public PNLCellVM<PNLRowData, double> working_net_pos { get; }
|
||||
public PNLCellVM<PNLRowData, WorkingOrderSideData> working_long_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> working_long_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, WorkingOrderSideData> working_short_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> working_short_avg_price { get; }
|
||||
// total
|
||||
public PNLCellVM<PNLRowData, double> total_pnl { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_net_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_long_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_long_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_short_pos { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_short_avg_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_delta { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_gamma { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_vega { get; }
|
||||
public PNLCellVM<PNLRowData, double> total_theta { get; }
|
||||
|
||||
// (market)
|
||||
public PNLCellVM<PNLRowData, double> bid_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> mid_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> ask_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> last_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> settlement_price { get; }
|
||||
public PNLCellVM<PNLRowData, double> pnl_price { get; }
|
||||
|
||||
// display
|
||||
public api.domain.TickDisplaySettingsImmutable quantity_display {
|
||||
get {
|
||||
if (_quantity_display_backing.HasValue) {
|
||||
return _quantity_display_backing.Value;
|
||||
}
|
||||
if (security.HasValue) {
|
||||
_quantity_display_backing = _sec_meta_store.get_qty_display(security.Value);
|
||||
}
|
||||
return _quantity_display_backing ??= Security.Defaults.quantity_display_settingsi;
|
||||
}
|
||||
}
|
||||
|
||||
public IList<mbts.ui.spark.CellAnimation> animations { get; private set; }
|
||||
|
||||
public bool is_selected {
|
||||
get => _is_selected;
|
||||
set {
|
||||
if (Set(ref _is_selected, value))
|
||||
is_selected_changed?.Invoke(value);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<PNLCellVM<PNLRowData, double>> _market_price_cells {
|
||||
get {
|
||||
yield return pnl_price;
|
||||
yield return last_price;
|
||||
yield return bid_price;
|
||||
yield return mid_price;
|
||||
yield return ask_price;
|
||||
yield return settlement_price;
|
||||
}
|
||||
}
|
||||
|
||||
// includes sod, day, total, and working avg price columns
|
||||
IEnumerable<PNLCellVM<PNLRowData, double>> _fill_price_cells {
|
||||
get {
|
||||
// sod
|
||||
yield return sod_long_avg_price;
|
||||
yield return sod_short_avg_price;
|
||||
// day
|
||||
yield return day_long_avg_price;
|
||||
yield return day_short_avg_price;
|
||||
// total
|
||||
yield return total_long_avg_price;
|
||||
yield return total_short_avg_price;
|
||||
// working
|
||||
yield return working_long_avg_price;
|
||||
yield return working_short_avg_price;
|
||||
}
|
||||
}
|
||||
|
||||
public void update(PNLRowData data) {
|
||||
_quantity_display_backing = null;
|
||||
_refresh_normalized_data = true;
|
||||
this.data = data;
|
||||
_cell_map.on_row(data);
|
||||
_animator.on_row(data);
|
||||
_cell_map.set_appearances(_animator.cell_appearances);
|
||||
}
|
||||
|
||||
public void on_settings(ImmutableDictionary<ColumnKeys, PNLCellAppearance> defaults, IReadOnlyList<IPNLAnimationTrigger<PNLRowData>> triggers, Func<double, string> fmt_fill_price, Func<double, string> fmt_market_price) {
|
||||
_animator.on_settings(defaults, triggers);
|
||||
_cell_map.set_appearances(_animator.cell_appearances);
|
||||
_set_price_formatting_delegates(fmt_fill_price, fmt_market_price);
|
||||
}
|
||||
|
||||
public bool Equals(PNLRowVM other) =>
|
||||
other != null && key == other.key;
|
||||
|
||||
public override int GetHashCode() =>
|
||||
key.GetHashCode();
|
||||
|
||||
public override string ToString() =>
|
||||
$"account: {account_name} exchange: {exchange} symbol: {symbol} security: {security} buy_quantity: {day_long_pos} sell_quantity: {day_short_pos} pnl: {total_pnl}";
|
||||
|
||||
void _set_price_formatting_delegates(Func<double, string> fmt_fill_price, Func<double, string> fmt_market_price) {
|
||||
foreach (var cell in _fill_price_cells) {
|
||||
cell.format = fmt_fill_price;
|
||||
}
|
||||
foreach (var cell in _market_price_cells) {
|
||||
cell.format = fmt_market_price;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,9 +65,9 @@ namespace BCG {
|
||||
public static NMSheetTheoBBOCross get_cross_state(double bid_price, double ask_price, double theo, double delta, bool is_call, double driver_tick_size, bool is_proximity_alert_enabled) {
|
||||
double abs_delta = Math.Abs(delta);
|
||||
abs_delta = abs_delta == 0 ? Double.Epsilon : abs_delta;
|
||||
if (!Double.IsNaN(theo) && !Double.IsNaN(bid_price) && (bid_price - theo) > _basic_crosss_eps)
|
||||
if (!Double.IsNaN(theo) && !Double.IsNaN(bid_price) && theo < bid_price)
|
||||
return new NMSheetTheoBBOCross(CrossState.bid_cross, 0);
|
||||
else if (!Double.IsNaN(theo) && !Double.IsNaN(ask_price) && (theo - ask_price) > _basic_crosss_eps)
|
||||
else if (!Double.IsNaN(theo) && !Double.IsNaN(ask_price) && theo > ask_price)
|
||||
return new NMSheetTheoBBOCross(CrossState.ask_cross, 0);
|
||||
else if (is_proximity_alert_enabled) {
|
||||
/// bool is passed to bypass doing the below
|
||||
@@ -87,7 +87,6 @@ namespace BCG {
|
||||
return NMSheetTheoBBOCross.get_nulled_obj();
|
||||
}
|
||||
|
||||
private static double _basic_crosss_eps = 0.01;
|
||||
private static double _n_max_ticks = 100.0;
|
||||
private static NMSheetTheoBBOCross _nulled_obj = new NMSheetTheoBBOCross(CrossState.none, 0);
|
||||
public static NMSheetTheoBBOCross get_nulled_obj() { return _nulled_obj; }
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<sys:Double x:Key="VerticalSpacing">4.0</sys:Double>
|
||||
<sys:Double x:Key="ButtonHeight">22.0</sys:Double>
|
||||
<cv:NullableDoubleConverter x:Key="TickDefaultPriceConverter" DefaultValue="0"/>
|
||||
<Style TargetType="Button" BasedOn="{StaticResource DefaultButton}">
|
||||
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Height" Value="{StaticResource ButtonHeight}"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
</Style>
|
||||
@@ -53,6 +53,8 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalSpacing}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalSpacing}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="140"/>
|
||||
@@ -75,7 +77,6 @@
|
||||
RegionalTick="{Binding price_tick}"
|
||||
Focusable="false"
|
||||
TickDefaultValue="{Binding current_price, Converter={StaticResource TickDefaultPriceConverter}, FallbackValue={u:Double 0}}"
|
||||
ToolTip="Price"
|
||||
Value="{Binding price}"
|
||||
/>
|
||||
<c:TickUpDownNullableDouble
|
||||
@@ -87,7 +88,6 @@
|
||||
Increment="{Binding size_calculator.unit_quantity}"
|
||||
Minimum="0"
|
||||
TickDisplaySettings="{Binding size_calculator.display_settings}"
|
||||
ToolTip="Size"
|
||||
Value="{Binding size_calculator.display_value}"
|
||||
/>
|
||||
</Grid>
|
||||
@@ -97,24 +97,13 @@
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalSpacing}}"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Command="{Binding set_price_command}"
|
||||
CommandParameter="{Binding bbo.bid}"
|
||||
Foreground="{u:BTSColor MarketData.bid}"
|
||||
FontWeight="Bold"
|
||||
ToolTip="Bid Price">
|
||||
<Button Command="{Binding set_price_command}" CommandParameter="{Binding bbo.bid}" Foreground="{u:BTSColor MarketData.bid}" FontWeight="Bold">
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="bbo.bid"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</Button>
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Command="{Binding set_price_command}"
|
||||
CommandParameter="{Binding bbo.ask}"
|
||||
Foreground="{u:BTSColor MarketData.ask}"
|
||||
FontWeight="Bold"
|
||||
ToolTip="Ask Price">
|
||||
<Button Grid.Column="2" Command="{Binding set_price_command}" CommandParameter="{Binding bbo.ask}" Foreground="{u:BTSColor MarketData.ask}" FontWeight="Bold">
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="bbo.ask"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
@@ -131,7 +120,7 @@
|
||||
Command="{Binding size_calculator.set_actual_value_command}"
|
||||
CommandParameter="{Binding bbo.bid_size}"
|
||||
Foreground="{u:BTSColor MarketData.bid}"
|
||||
ToolTip="Bid Size">
|
||||
>
|
||||
<MultiBinding Converter="{x:Static cv:TickDisplayConverter.Instance}">
|
||||
<Binding Path="bbo.bid_size" />
|
||||
<Binding Path="qty_display" />
|
||||
@@ -142,20 +131,20 @@
|
||||
Command="{Binding size_calculator.set_actual_value_command}"
|
||||
CommandParameter="{Binding bbo.ask_size}"
|
||||
Foreground="{u:BTSColor MarketData.ask}"
|
||||
ToolTip="Ask Size">
|
||||
>
|
||||
<MultiBinding Converter="{x:Static cv:TickDisplayConverter.Instance}">
|
||||
<Binding Path="bbo.ask_size" />
|
||||
<Binding Path="qty_display" />
|
||||
</MultiBinding>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid Grid.Row="4">
|
||||
<Grid Grid.Row="4" Grid.RowSpan="3">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalSpacing}}"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<ItemsControl Margin="0,0,-4,-4" DataContext="{Binding size_calculator}" ItemsSource="{Binding buttons}" Focusable="False">
|
||||
<ItemsControl Margin="-2,-2,-2,0" DataContext="{Binding size_calculator}" ItemsSource="{Binding buttons}" Focusable="False">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="3" Focusable="False"/>
|
||||
@@ -164,11 +153,11 @@
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button
|
||||
Margin="0,0,4,4"
|
||||
Margin="2,4,2,0"
|
||||
Content="{Binding}"
|
||||
Focusable="False"
|
||||
Command="{Binding Path=DataContext.click, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
|
||||
ToolTip="Add to Size">
|
||||
>
|
||||
<Button.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static cv:TickDisplayConverter.Instance}">
|
||||
<Binding />
|
||||
@@ -181,35 +170,35 @@
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Command="{Binding size_calculator.clear}"
|
||||
Content="Clear"
|
||||
ToolTip="Clear Size" />
|
||||
<Button Grid.Row="2" Command="{Binding size_calculator.clear}">Clear</Button>
|
||||
</Grid>
|
||||
<Grid Grid.Row="4" Grid.Column="2">
|
||||
<Grid Grid.Row="4" Grid.Column="2" Grid.RowSpan="3">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalSpacing}}"/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalSpacing}}"/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<ComboBox
|
||||
Grid.ColumnSpan="2"
|
||||
usp:OrderModificationControl.IsDirty="{Binding account_dirty, FallbackValue={u:False}}"
|
||||
IsEnabled="{Binding can_change_account, FallbackValue={u:True}}"
|
||||
ItemsSource="{Binding properties_vm.accounts}"
|
||||
Margin="0,2"
|
||||
SelectedItem="{Binding properties_vm.selected_account}"
|
||||
ToolTip="Account" />
|
||||
/>
|
||||
<ComboBox
|
||||
Grid.Row="2"
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"
|
||||
usp:OrderModificationControl.IsDirty="{Binding time_in_force_dirty, FallbackValue={u:False}}"
|
||||
IsEnabled="{Binding can_change_time_in_force, FallbackValue={u:True}}"
|
||||
ItemsSource="{Binding properties_vm.time_in_forces}"
|
||||
Margin="0,2"
|
||||
SelectedItem="{Binding properties_vm.selected_time_in_force}"
|
||||
ToolTip="Time In Force" />
|
||||
/>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="4"
|
||||
Grid.Row="2"
|
||||
Margin="0,2"
|
||||
DataContext="{Binding properties_vm}"
|
||||
usp:OrderModificationControl.IsDirty="{Binding DataContext.order_type_dirty, RelativeSource={RelativeSource AncestorType=usp:OrderModificationControl}, FallbackValue={u:False}}"
|
||||
HasEditableProperties="{Binding can_expand, FallbackValue={u:False}}"
|
||||
@@ -218,7 +207,7 @@
|
||||
IsOpen="{Binding is_expanded, FallbackValue={u:False}}"
|
||||
ItemsSource="{Binding algo_definitions_view}"
|
||||
SelectedItem="{Binding selected_algo_definition}"
|
||||
ToolTip="Order/Algo Type">
|
||||
>
|
||||
<uag:OrderDetailsPopupControl.Style>
|
||||
<Style TargetType="uag:OrderDetailsPopupControl" BasedOn="{StaticResource {x:Type uag:OrderDetailsPopupControl}}">
|
||||
<Style.Triggers>
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace mbts.spark {
|
||||
new PropertyValidation<AccountTuple?>(
|
||||
a => (a.HasValue && !string.IsNullOrWhiteSpace(a.Value.name) && accounts.Contains(a.Value), "Please select an account"))
|
||||
});
|
||||
attach(_accounts, accounts_src.Select(accs => accs.Values.ToListSorted()), inline_account_src);
|
||||
attach(_accounts, accounts_src.Select(accs => accs.Values.ToList()), inline_account_src);
|
||||
|
||||
// time in force
|
||||
_time_in_force = new SelectorProperty<TimeInForce>(
|
||||
|
||||
@@ -1,161 +1,161 @@
|
||||
<Window
|
||||
x:Class="mbts.ui.spark.OrderTicket"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:msys="clr-namespace:bts.api;assembly=spark_api"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
u:WindowStyle.SizeToContent="WidthAndHeight"
|
||||
u:WindowStyle.TitleContent="{Binding bbo, Mode=OneWay}"
|
||||
Style="{StaticResource BTSWindowTitleTemplate}"
|
||||
>
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Resources/Converters.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<sys:Double x:Key="HorizontalSpacing">8.0</sys:Double>
|
||||
<sys:Double x:Key="VerticalSpacing">4.0</sys:Double>
|
||||
<sys:Double x:Key="ButtonHeight">22.0</sys:Double>
|
||||
<Style x:Key="SellButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Background" Value="{u:BTSColor MarketData.ask_dark}" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="MinHeight" Value="40" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
<TextBlock VerticalAlignment="Center" Text="Sell" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
CornerRadius="0"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{u:BTSColor UI.Button.normal_border}"
|
||||
Background="{u:BTSColor MarketData.ask}"
|
||||
Padding="2"
|
||||
Height="Auto"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
RecognizesAccessKey="True"
|
||||
Name="button_content"
|
||||
/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="#99c00000" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{u:BTSColor UI.Button.mouseover_border}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="BuyButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Background" Value="{u:BTSColor MarketData.bid_dark}" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="MinHeight" Value="40" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
<TextBlock VerticalAlignment="Center" Text="Buy" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
CornerRadius="0"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{u:BTSColor UI.Button.normal_border}"
|
||||
Background="{u:BTSColor MarketData.bid}"
|
||||
Padding="2"
|
||||
Height="Auto"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
RecognizesAccessKey="True"
|
||||
Name="button_content"
|
||||
/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="#99167ed3" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{u:BTSColor UI.Button.mouseover_border}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Key="Esc" Command="{x:Static SystemCommands.CloseWindowCommand}"/>
|
||||
</Window.InputBindings>
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalSpacing}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition MinWidth="120"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalSpacing}}"/>
|
||||
<ColumnDefinition MinWidth="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<usp:OrderModificationControl Grid.ColumnSpan="3" />
|
||||
<Button Grid.Row="2" Style="{StaticResource BuyButton}" Command="{Binding send_order}" CommandParameter="{u:Enum Buy, Type=msys:Side}" />
|
||||
<Button Grid.Row="2" Grid.Column="2" Style="{StaticResource SellButton}" Command="{Binding send_order}" CommandParameter="{u:Enum Sell, Type=msys:Side}" />
|
||||
</Grid>
|
||||
<u:WindowStyle.TitleContentTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Style="{StaticResource TextBlock.Dark}">
|
||||
<TextBlock.Inlines>
|
||||
<Run FontWeight="Bold" Text="{Binding security, Mode=OneWay}" />
|
||||
<Run Foreground="{u:BTSColor MarketData.bid}" FontSize="10">
|
||||
<MultiBinding Converter="{x:Static cv:TickDisplayConverter.Instance}" StringFormat="({0})">
|
||||
<Binding Path="bid_size" Mode="OneWay"/>
|
||||
<Binding Path="DataContext.qty_display" RelativeSource="{RelativeSource AncestorType=usp:OrderTicket}"/>
|
||||
</MultiBinding>
|
||||
</Run>
|
||||
<Run Foreground="{u:BTSColor MarketData.bid}" FontWeight="Bold">
|
||||
<Run.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="bid"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</Run.Text>
|
||||
</Run>
|
||||
<Run Foreground="{u:BTSColor MarketData.ask}" FontWeight="Bold">
|
||||
<Run.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="ask"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</Run.Text>
|
||||
</Run>
|
||||
<Run Foreground="{u:BTSColor MarketData.ask}" FontSize="10">
|
||||
<MultiBinding Converter="{x:Static cv:TickDisplayConverter.Instance}" StringFormat="({0})">
|
||||
<Binding Path="ask_size" Mode="OneWay"/>
|
||||
<Binding Path="DataContext.qty_display" RelativeSource="{RelativeSource AncestorType=usp:OrderTicket}"/>
|
||||
</MultiBinding>
|
||||
</Run>
|
||||
</TextBlock.Inlines>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</u:WindowStyle.TitleContentTemplate>
|
||||
</Window>
|
||||
<Window
|
||||
x:Class="mbts.ui.spark.OrderTicket"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:msys="clr-namespace:bts.api;assembly=spark_api"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
u:WindowStyle.SizeToContent="WidthAndHeight"
|
||||
u:WindowStyle.TitleContent="{Binding bbo, Mode=OneWay}"
|
||||
Style="{StaticResource BTSWindowTitleTemplate}"
|
||||
>
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Resources/Converters.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<sys:Double x:Key="HorizontalSpacing">8.0</sys:Double>
|
||||
<sys:Double x:Key="VerticalSpacing">4.0</sys:Double>
|
||||
<sys:Double x:Key="ButtonHeight">22.0</sys:Double>
|
||||
<Style x:Key="SellButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Background" Value="{u:BTSColor MarketData.ask}" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="MinHeight" Value="40" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
<TextBlock VerticalAlignment="Center" Text="Sell" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
CornerRadius="0"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{u:BTSColor UI.Button.normal_border}"
|
||||
Background="{u:BTSColor MarketData.ask}"
|
||||
Padding="2"
|
||||
Height="Auto"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
RecognizesAccessKey="True"
|
||||
Name="button_content"
|
||||
/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="#99c00000" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{u:BTSColor UI.Button.mouseover_border}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="BuyButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Background" Value="{u:BTSColor MarketData.bid}" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="MinHeight" Value="40" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
<TextBlock VerticalAlignment="Center" Text="Buy" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
CornerRadius="0"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{u:BTSColor UI.Button.normal_border}"
|
||||
Background="{u:BTSColor MarketData.bid}"
|
||||
Padding="2"
|
||||
Height="Auto"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
RecognizesAccessKey="True"
|
||||
Name="button_content"
|
||||
/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="#99167ed3" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{u:BTSColor UI.Button.mouseover_border}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Key="Esc" Command="{x:Static SystemCommands.CloseWindowCommand}"/>
|
||||
</Window.InputBindings>
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalSpacing}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition MinWidth="120"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalSpacing}}"/>
|
||||
<ColumnDefinition MinWidth="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<usp:OrderModificationControl Grid.ColumnSpan="3" />
|
||||
<Button Grid.Row="2" Style="{StaticResource BuyButton}" Command="{Binding send_order}" CommandParameter="{u:Enum Buy, Type=msys:Side}" />
|
||||
<Button Grid.Row="2" Grid.Column="2" Style="{StaticResource SellButton}" Command="{Binding send_order}" CommandParameter="{u:Enum Sell, Type=msys:Side}" />
|
||||
</Grid>
|
||||
<u:WindowStyle.TitleContentTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Style="{StaticResource TextBlock.Dark}">
|
||||
<TextBlock.Inlines>
|
||||
<Run FontWeight="Bold" Text="{Binding security, Mode=OneWay}" />
|
||||
<Run Foreground="{u:BTSColor MarketData.bid}" FontSize="10">
|
||||
<MultiBinding Converter="{x:Static cv:TickDisplayConverter.Instance}" StringFormat="({0})">
|
||||
<Binding Path="bid_size" Mode="OneWay"/>
|
||||
<Binding Path="DataContext.qty_display" RelativeSource="{RelativeSource AncestorType=usp:OrderTicket}"/>
|
||||
</MultiBinding>
|
||||
</Run>
|
||||
<Run Foreground="{u:BTSColor MarketData.bid}" FontWeight="Bold">
|
||||
<Run.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="bid"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</Run.Text>
|
||||
</Run>
|
||||
<Run Foreground="{u:BTSColor MarketData.ask}" FontWeight="Bold">
|
||||
<Run.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="ask"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</Run.Text>
|
||||
</Run>
|
||||
<Run Foreground="{u:BTSColor MarketData.ask}" FontSize="10">
|
||||
<MultiBinding Converter="{x:Static cv:TickDisplayConverter.Instance}" StringFormat="({0})">
|
||||
<Binding Path="ask_size" Mode="OneWay"/>
|
||||
<Binding Path="DataContext.qty_display" RelativeSource="{RelativeSource AncestorType=usp:OrderTicket}"/>
|
||||
</MultiBinding>
|
||||
</Run>
|
||||
</TextBlock.Inlines>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</u:WindowStyle.TitleContentTemplate>
|
||||
</Window>
|
||||
@@ -398,7 +398,7 @@
|
||||
$"Expected rounded qty to be real, non-negative. " +
|
||||
$"qty: {qty}; rounded_qty: {rounded_qty}; display: {display})");
|
||||
var iqty = Math.Floor(rounded_qty);
|
||||
return ((uint)iqty, new() { Fraction = Math.Round(rounded_qty - iqty, 6) });
|
||||
return ((uint)iqty, new() { Fraction = Math.Round(rounded_qty - iqty, 2) });
|
||||
}
|
||||
|
||||
private CustomOrderDefinitionImmutable? _create_custom_def_for_stop_and_stop_limit(OrderRecordImmutable order, ref double stop_price) {
|
||||
|
||||
@@ -64,7 +64,8 @@ namespace mbts.analytics {
|
||||
if (tl.tl_row.size > 0) {
|
||||
start_long_pos += tl.tl_row.size;
|
||||
start_long_total_cost += cost;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
start_short_pos += Math.Abs(tl.tl_row.size);
|
||||
start_short_total_cost += cost;
|
||||
}
|
||||
@@ -77,7 +78,8 @@ namespace mbts.analytics {
|
||||
if (tl.tl_row.size > 0) {
|
||||
day_long_pos += tl.tl_row.size;
|
||||
day_long_total_cost += cost;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
day_short_pos += Math.Abs(tl.tl_row.size);
|
||||
day_short_total_cost += cost;
|
||||
}
|
||||
@@ -163,13 +165,6 @@ namespace mbts.analytics {
|
||||
is_day_trade = _is_day_trade(tl)
|
||||
};
|
||||
pnl_trades.Add(pnl_tl);
|
||||
} else if (tl.security_type == SecurityType.Spot) {
|
||||
PNLTradeRow pnl_tl = new PNLTradeRow() {
|
||||
tl_row = tl,
|
||||
md = tl_md,
|
||||
is_day_trade = _is_day_trade(tl)
|
||||
};
|
||||
pnl_trades.Add(pnl_tl);
|
||||
}
|
||||
}
|
||||
return pnl_trades;
|
||||
|
||||
@@ -4,9 +4,6 @@ using bts.api;
|
||||
|
||||
using BCG;
|
||||
using mbts.co2;
|
||||
using System.Xml.Serialization;
|
||||
using ZstdSharp.Unsafe;
|
||||
using bts.proto;
|
||||
|
||||
namespace mbts.analytics {
|
||||
// Pairs ITradeLogRow along with the data (theo, md) that is needed to compute
|
||||
@@ -21,88 +18,20 @@ namespace mbts.analytics {
|
||||
|
||||
public SecurityImmutable security { get { return md.security; } }
|
||||
|
||||
// private double _option_pnl(double trade_price, double now_price) {
|
||||
// double coin_terms = (now_price - trade_price) * tl_row.size;
|
||||
// double usd_terms = coin_terms * driver;
|
||||
// return usd_terms;
|
||||
// }
|
||||
|
||||
private bool _is_notional {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
private double _option_pnl(double trade_price, double now_price) {
|
||||
double coin_terms = (now_price - trade_price) * tl_row.size;
|
||||
double usd_terms = coin_terms * driver;
|
||||
return usd_terms;
|
||||
}
|
||||
|
||||
private bool _is_inverse_option_contract {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
private double _future_pnl(double trade_price, double now_price) {
|
||||
// the qty is in USD, so this is the initial cash flow
|
||||
double init_asset_value = tl_row.size;
|
||||
double asset_size = init_asset_value / tl_row.price;
|
||||
double now_asset_value = asset_size * now_price;
|
||||
return now_asset_value - init_asset_value;
|
||||
}
|
||||
|
||||
private double _cpp {
|
||||
get {
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
private bool _is_reverse_option_contract {
|
||||
get {
|
||||
// check for Deribit options
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private double _option_pnl {
|
||||
get {
|
||||
if (_is_notional) {
|
||||
var now_price = theo;
|
||||
double coin_terms = (now_price - tl_row.price) * tl_row.size;
|
||||
double usd_terms = coin_terms * driver;
|
||||
return usd_terms;
|
||||
} else {
|
||||
return _cpp * tl_row.size * (theo - tl_row.price);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double _spot_pnl {
|
||||
get {
|
||||
// both are computed the same way
|
||||
return _future_pnl;
|
||||
}
|
||||
}
|
||||
|
||||
private double _future_pnl {
|
||||
get {
|
||||
if (_is_notional) {
|
||||
// TODO:
|
||||
// let it fall through right now
|
||||
// the qty is in USD, so this is the initial cash flow
|
||||
var now_price = theo;
|
||||
double init_asset_value = tl_row.size;
|
||||
double asset_size = init_asset_value / tl_row.price;
|
||||
double now_asset_value = asset_size * now_price;
|
||||
return now_asset_value - init_asset_value;
|
||||
} else {
|
||||
// TODO: is position pnl, compute it wrt settlement price?
|
||||
return tl_row.size * (theo - tl_row.price);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private double _future_pnl(double trade_price, double now_price) {
|
||||
// if (_is_notional) {
|
||||
// // the qty is in USD, so this is the initial cash flow
|
||||
// double init_asset_value = tl_row.size;
|
||||
// double asset_size = init_asset_value / tl_row.price;
|
||||
// double now_asset_value = asset_size * now_price;
|
||||
// return now_asset_value - init_asset_value;
|
||||
// } else {
|
||||
// return tl_row.size * (theo - tl_row.price);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
public double driver {
|
||||
get {
|
||||
if (tl_row.security_type == SecurityType.Option) {
|
||||
@@ -115,46 +44,35 @@ namespace mbts.analytics {
|
||||
}
|
||||
public double theo {
|
||||
get {
|
||||
switch (tl_row.security_type) {
|
||||
// TODO:
|
||||
case SecurityType.Option:
|
||||
if (_is_inverse_option_contract) {
|
||||
return tl_row.call_put == PutOrCall.Call ?
|
||||
opt_theo.fwd_normalized_call : opt_theo.fwd_normalized_put;
|
||||
} else {
|
||||
return tl_row.call_put == PutOrCall.Call ?
|
||||
opt_theo.call : opt_theo.put;
|
||||
}
|
||||
// future and options, just get mid market
|
||||
case SecurityType.Future:
|
||||
//return fut_theo.theo;
|
||||
case SecurityType.Spot:
|
||||
var bbo = top_of_book;
|
||||
return (bbo.Item1 + bbo.Item2) / 2;
|
||||
default:
|
||||
return Double.NaN;
|
||||
if (tl_row.security_type == SecurityType.Option) {
|
||||
return tl_row.call_put == PutOrCall.Call ?
|
||||
opt_theo.fwd_normalized_call : opt_theo.fwd_normalized_put;
|
||||
} else if (tl_row.security_type == SecurityType.Future) {
|
||||
return fut_theo.theo;
|
||||
}
|
||||
return Double.NaN;
|
||||
}
|
||||
}
|
||||
public double pnl {
|
||||
get {
|
||||
double rv = Double.NaN;
|
||||
switch (tl_row.security_type) {
|
||||
case SecurityType.Option:
|
||||
rv = _option_pnl;
|
||||
break;
|
||||
case SecurityType.Future:
|
||||
rv = _future_pnl;
|
||||
break;
|
||||
case SecurityType.Spot:
|
||||
// = tl_row.size * (theo - tl_row.price);
|
||||
rv = _spot_pnl;
|
||||
break;
|
||||
if (is_day_trade) {
|
||||
if (tl_row.security_type == SecurityType.Future) {
|
||||
rv = _future_pnl(tl_row.price, theo);
|
||||
} else if (tl_row.security_type == SecurityType.Option) {
|
||||
rv = _option_pnl(tl_row.price, theo);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
// use settle price ?
|
||||
if (tl_row.security_type == SecurityType.Future) {
|
||||
rv = _future_pnl(tl_row.price, theo);
|
||||
} else if (tl_row.security_type == SecurityType.Option) {
|
||||
rv = _option_pnl(tl_row.price, theo);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
public double settle_price {
|
||||
get {
|
||||
return md.md == null ? Double.NaN : md.md.settlement_price;
|
||||
|
||||
@@ -36,10 +36,6 @@ namespace mbts {
|
||||
}
|
||||
|
||||
public static bool is_unexpired(bts.api.SecurityTuple st, DateTime exp_date) {
|
||||
if (st.Venue == Venue.Cboe || st.Venue == Venue.Glbx) {
|
||||
// server is already getting teh righ contracts
|
||||
return true;
|
||||
}
|
||||
TimeSpan exp_time = get_exp_time(st);
|
||||
DateTime full_exp_utc = exp_date.ToUniversalTime().Date.Add(exp_time);
|
||||
DateTime now_utc = DateTime.UtcNow;
|
||||
@@ -47,8 +43,6 @@ namespace mbts {
|
||||
}
|
||||
|
||||
public static bool is_unexpired(bts.api.SecurityImmutable sec) {
|
||||
if (sec.security_tuple().Venue == Venue.Cboe || sec.security_tuple().Venue == Venue.Glbx)
|
||||
return true;
|
||||
var sec_type = sec.security_type();
|
||||
switch (sec_type) {
|
||||
case bts.api.SecurityType.Future:
|
||||
|
||||
@@ -58,10 +58,9 @@ namespace BCG {
|
||||
}
|
||||
// TODO: put it as a setting
|
||||
bts.Log.assert(theos.Length == drivers.Length);
|
||||
// only do this when its deribit crypto option, TODO
|
||||
// for (int i = 0; i < theos.Length; i++) {
|
||||
// theos[i] /= drivers[i];
|
||||
// }
|
||||
for (int i = 0; i < theos.Length; i++) {
|
||||
theos[i] /= drivers[i];
|
||||
}
|
||||
return new SpreadValue(spread_spec, theos, deltas, gammas, vegas, adj_vegas, oevs, thetas, slope_vegas, drivers, qs_midpoint_theos, pos, additional_delta);
|
||||
}
|
||||
}
|
||||
@@ -143,21 +142,9 @@ namespace BCG {
|
||||
return tp ?? mbts.spark.TheoPoint.getNulledObject();
|
||||
}
|
||||
|
||||
private static mbts.spark.TheoPoint _compute_single_option_leg_at_tied_price(DateTime option_expiration, double strike, bool is_call, OptionTheoInterpolationData sparse_theo_map,
|
||||
ExpStrikeKeyedData<mbts.spark.TheoPoint> live_symbol_theo_map, double driver_price) {
|
||||
// mbts.spark.TheoPoint tp = sparse_theo_map == null ? mbts.spark.TheoPoint.getNulledObject() : sparse_theo_map.interpolate_theos(option_expiration, strike, driver_price);
|
||||
// return tp ?? mbts.spark.TheoPoint.getNulledObject();
|
||||
// TOD: config here
|
||||
return _compute_single_option_leg_at_tied_price_from_live(option_expiration, strike, is_call, live_symbol_theo_map, driver_price);
|
||||
}
|
||||
|
||||
private static mbts.spark.TheoPoint _compute_single_option_leg_at_tied_price_from_live(DateTime option_expiration, double strike, bool is_call,
|
||||
ExpStrikeKeyedData<mbts.spark.TheoPoint> live_symbol_theo_map, double driver_price) {
|
||||
var tp = live_symbol_theo_map.get_data(option_expiration, strike);
|
||||
if (tp == null)
|
||||
return mbts.spark.TheoPoint.getNulledObject();
|
||||
return TheoPoint.extrapolate(tp, driver_price, is_call);
|
||||
|
||||
private static mbts.spark.TheoPoint _compute_single_option_leg_at_tied_price(DateTime option_expiration, double strike, bool is_call, OptionTheoInterpolationData sparse_theo_map, double driver_price) {
|
||||
mbts.spark.TheoPoint tp = sparse_theo_map == null ? mbts.spark.TheoPoint.getNulledObject() : sparse_theo_map.interpolate_theos(option_expiration, strike, driver_price);
|
||||
return tp ?? mbts.spark.TheoPoint.getNulledObject();
|
||||
}
|
||||
|
||||
/// this is the only relatively tricky thing here
|
||||
@@ -187,7 +174,7 @@ namespace BCG {
|
||||
if (leg_price_spec.primaryFrozen) /// tied
|
||||
{
|
||||
effective_driver = leg_price_spec.primary;
|
||||
return _compute_single_option_leg_at_tied_price(spread_option_leg.contract.expiration, spread_option_leg.contract.strike, spread_option_leg.contract.isCall, symbol_sparse_theo_map, live_symbol_theo_map, leg_price_spec.primary);
|
||||
return _compute_single_option_leg_at_tied_price(spread_option_leg.contract.expiration, spread_option_leg.contract.strike, spread_option_leg.contract.isCall, symbol_sparse_theo_map, leg_price_spec.primary);
|
||||
} else {
|
||||
mbts.spark.TheoPoint tp = _compute_single_option_leg_at_live_price(spread_option_leg.contract.expiration, spread_option_leg.contract.strike, spread_option_leg.contract.isCall, live_symbol_theo_map);
|
||||
effective_driver = tp.driver;
|
||||
@@ -201,7 +188,7 @@ namespace BCG {
|
||||
future_theo_map.TryGetValue(driver_price_info.first, out symbol_future_theo_map);
|
||||
double driver_price = StatelessFutureTheoCalculator.transform_price(symbol_future_theo_map, leg_price_spec.driverExpiration, leg_price_spec.primary, driver_price_info.second);
|
||||
effective_driver = leg_price_spec.primary;
|
||||
return _compute_single_option_leg_at_tied_price(spread_option_leg.contract.expiration, spread_option_leg.contract.strike, spread_option_leg.contract.isCall, symbol_sparse_theo_map, live_symbol_theo_map, driver_price);
|
||||
return _compute_single_option_leg_at_tied_price(spread_option_leg.contract.expiration, spread_option_leg.contract.strike, spread_option_leg.contract.isCall, symbol_sparse_theo_map, driver_price);
|
||||
} else {
|
||||
bts.Log.Ui.error(String.Format("This spread is live but the driver expiration {0} is different from the system driver expiration {1} for {2}",
|
||||
spread_option_leg.legPriceSpec.driverExpiration, driver_price_info.second, spread_option_leg.contract.product));
|
||||
|
||||
@@ -216,10 +216,8 @@ namespace mbts.ui {
|
||||
public static BTSColor ask_text => BTSCoreColors.red_dark;
|
||||
public static BTSColor bid_text => BTSCoreColors.blue;
|
||||
public static BTSColor bid => 0xff76b7fa;
|
||||
public static BTSColor bid_dark => 0xff26239a;
|
||||
public static BTSColor bid_alt => BTSCoreColors.blue_light.with(0x70);
|
||||
public static BTSColor ask => 0xffff6666;
|
||||
public static BTSColor ask_dark => 0xff7a2331;
|
||||
public static BTSColor ask_alt => 0x70ffb4b4;
|
||||
public static BTSColor last => BTSCoreColors.black;
|
||||
public static BTSColor bid_ask_alt_blend => BTSCoreColors.purple_light;
|
||||
@@ -229,7 +227,7 @@ namespace mbts.ui {
|
||||
|
||||
public static BTSColor atm_background => BTSCoreColors.gray_dark;
|
||||
|
||||
public static BTSColor session_low => 0xff8f5924;
|
||||
public static BTSColor session_low => 0xff59596b;
|
||||
public static BTSColor session_high => 0xff239b00;
|
||||
public static BTSColor session_range_fill => 0x1a000000;
|
||||
|
||||
@@ -986,7 +984,7 @@ namespace mbts.ui {
|
||||
public static BTSColor alternate_column_foreground => UI.foreground_standout;
|
||||
public static BTSColor bid_line => BTSCoreColors.blue_light_dark_saturated;
|
||||
public static BTSColor ask_line => BTSCoreColors.red_light_dark_saturated;
|
||||
public static BTSColor mid_line => 0xffa4a4a4;
|
||||
public static BTSColor mid_line => 0x40000000;
|
||||
|
||||
public static BTSColor default_column_background => 0xff141420;//0xff28283b;
|
||||
public static BTSColor alternate_column_background => 0xff141420;// 0xff494957;
|
||||
|
||||
@@ -5,8 +5,7 @@ using mbts.ui.utility.text;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
@@ -14,7 +13,7 @@ using FontFamily = System.Windows.Media.FontFamily;
|
||||
|
||||
namespace bts.proto {
|
||||
[ProtoContract]
|
||||
class FontParamsProto : IProtobuf<FontParamsProto> {
|
||||
class FontParamsProto : IEquatable<FontParamsProto> {
|
||||
[ProtoMember(1)]
|
||||
public TypefaceProto typeface { get; set; }
|
||||
[ProtoMember(2)]
|
||||
@@ -25,27 +24,6 @@ namespace bts.proto {
|
||||
public string font_family => typeface.font_family;
|
||||
public int font_weight => typeface.font_weight;
|
||||
|
||||
public FontParamsProto With(TypefaceProto? typeface = null, double? font_size = null, uint? font_color = null, bool in_place = false) {
|
||||
var rv = in_place ? this : Clone();
|
||||
if (typeface.HasValue) {
|
||||
rv.typeface = typeface.Value;
|
||||
}
|
||||
if (font_size.HasValue) {
|
||||
rv.font_size = font_size.Value;
|
||||
}
|
||||
if (font_color.HasValue) {
|
||||
rv.font_color = font_color.Value;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
public FontParamsProto Clone() =>
|
||||
new() {
|
||||
typeface = typeface.Clone(),
|
||||
font_size = font_size,
|
||||
font_color = font_color,
|
||||
};
|
||||
|
||||
public bool Equals(FontParamsProto other) =>
|
||||
other != null &&
|
||||
typeface == other.typeface &&
|
||||
|
||||
@@ -4,15 +4,14 @@ using mbts.ui.utility.text;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
using FontFamily = System.Windows.Media.FontFamily;
|
||||
|
||||
namespace bts.proto {
|
||||
[ProtoContract]
|
||||
readonly struct TypefaceProto : IProtobuf<TypefaceProto> {
|
||||
readonly struct TypefaceProto : IEquatable<TypefaceProto> {
|
||||
static readonly int _type_hash = typeof(TypefaceProto).GetHashCode();
|
||||
|
||||
[ProtoMember(1)]
|
||||
@@ -31,9 +30,6 @@ namespace bts.proto {
|
||||
font_family ?? this.font_family,
|
||||
font_weight ?? this.font_weight);
|
||||
|
||||
public TypefaceProto Clone() =>
|
||||
new(font_family.ToString(), font_weight);
|
||||
|
||||
public bool Equals(TypefaceProto other) =>
|
||||
font_family == other.font_family &&
|
||||
font_weight == other.font_weight;
|
||||
|
||||
@@ -379,15 +379,8 @@ namespace mbts.ui.controls {
|
||||
|
||||
class BadgeTextLinesConverter : IMultiValueConverter {
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
|
||||
if (ConvertUtils.TryConvertArgs(
|
||||
values,
|
||||
out Side side,
|
||||
out uint quantity,
|
||||
out uint filled,
|
||||
out double price,
|
||||
out LadderSettings.BadgeStyle style)) {
|
||||
return BadgeRender.format(side, quantity, filled, price, style);
|
||||
}
|
||||
if (ConvertUtils.try_convert_args(values, out Side side, out uint quantity, out uint filled, out mbts.spark.LadderSettings.BadgeStyle style))
|
||||
return BadgeRender.format(side, quantity, filled, style);
|
||||
else return new string[0];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace mbts.ui.controls {
|
||||
namespace mbts.ui.controls {
|
||||
using bts;
|
||||
using bts.utils;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace mbts.ui.controls {
|
||||
public const double
|
||||
Account = 62,
|
||||
CheckMark = 42,
|
||||
Currency = 68,
|
||||
Currency = 54,
|
||||
Exchange = 66,
|
||||
FillKey = 68,
|
||||
OrderKey = 78,
|
||||
@@ -43,9 +43,7 @@ namespace mbts.ui.controls {
|
||||
Side = 47,
|
||||
Qty = 48,
|
||||
MergedQty = 68,
|
||||
NotionalValue = 68,
|
||||
Filled = 56,
|
||||
FilledNotionalValue = 81,
|
||||
Symbol = 60,
|
||||
Time = 112,
|
||||
TimeOfDay = 84,
|
||||
@@ -124,7 +122,6 @@ namespace mbts.ui.controls {
|
||||
SecurityType = "Security Type",
|
||||
Side = "B/S",
|
||||
Qty = "Qty",
|
||||
NotionalValue = "Notional",
|
||||
MergedQty = "Qty",
|
||||
DirectQty = "Qty (D)",
|
||||
ImpliedQty = "Qty (I)",
|
||||
@@ -147,7 +144,6 @@ namespace mbts.ui.controls {
|
||||
FillKind = "Fill Kind",
|
||||
FilledPrice = "Fill Price",
|
||||
FilledQuantity = "Fill Qty",
|
||||
FilledNotionalValue = "Fill Notional",
|
||||
FillStatus = "Fill Status",
|
||||
Name = "Name",
|
||||
Description = "Description",
|
||||
|
||||
@@ -20,23 +20,13 @@ namespace mbts.ui {
|
||||
}
|
||||
|
||||
public static class AutoFocusTargetProvider {
|
||||
public static IAutoFocusTargetProvider TypeProvider(
|
||||
Type type,
|
||||
FrameworkElement root,
|
||||
bool focus = true,
|
||||
bool focus_keyboard = false,
|
||||
bool release_keyboard = true,
|
||||
bool capture_mouse = false,
|
||||
bool release_mouse = true) =>
|
||||
public static IAutoFocusTargetProvider<T> TypeProvider<T>(FrameworkElement root, bool focus, bool focus_keyboard, bool release_keyboard, bool capture_mouse, bool release_mouse) where T : FrameworkElement =>
|
||||
new AnonymousProvider<T>(() => root.descendants_of_type<T>().FirstOrDefault(), focus, focus_keyboard, release_keyboard, capture_mouse, release_mouse);
|
||||
|
||||
public static IAutoFocusTargetProvider TypeProvider(Type type, FrameworkElement root, bool focus, bool focus_keyboard, bool release_keybobard, bool capture_mouse, bool release_mouse) =>
|
||||
new AnonymousProvider<FrameworkElement>(
|
||||
() => root.visual_children_where(el => type.IsAssignableFrom(el.GetType()))
|
||||
.Cast<FrameworkElement>()
|
||||
.FirstOrDefault(),
|
||||
focus,
|
||||
focus_keyboard,
|
||||
release_keyboard,
|
||||
capture_mouse,
|
||||
release_mouse);
|
||||
() => root.visual_children_where(el => type.IsAssignableFrom(el.GetType())).Cast<FrameworkElement>().FirstOrDefault(),
|
||||
focus, focus_keyboard, release_keybobard, capture_mouse, release_mouse);
|
||||
|
||||
private class AnonymousProvider<T> : IAutoFocusTargetProvider<T> where T : FrameworkElement {
|
||||
readonly Func<T> _get_target;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -12,10 +12,7 @@ namespace mbts.spark.filters {
|
||||
str = str.Trim();
|
||||
if (str == string.Empty)
|
||||
return null;
|
||||
try {
|
||||
return new Regex(str, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
} catch (RegexParseException) { }
|
||||
return null;
|
||||
return new Regex(str, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
}
|
||||
}
|
||||
class RegexFilterVM<TRow> : FilterVMBase<TRow, string, string> {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
@@ -22,7 +21,7 @@
|
||||
<GroupBox Header="Font Family" Height="{Binding ActualHeight, ElementName=_panel_right}">
|
||||
<ListBox
|
||||
x:Name="_font_family_list_box"
|
||||
Background="{u:BTSBrush UI.ListBox.normal_background}"
|
||||
Background="White"
|
||||
ItemsSource="{Binding FontFamilies, RelativeSource={RelativeSource AncestorType=c:FontPicker}}"
|
||||
SelectedItem="{Binding SelectedFontFamily, RelativeSource={RelativeSource AncestorType=c:FontPicker}}"
|
||||
>
|
||||
@@ -41,7 +40,7 @@
|
||||
</Grid.RowDefinitions>
|
||||
<GroupBox Header="Font Weight">
|
||||
<ListBox
|
||||
Background="{u:BTSBrush UI.ListBox.normal_background}"
|
||||
Background="White"
|
||||
ItemsSource="{Binding FontWeights, RelativeSource={RelativeSource AncestorType=c:FontPicker}}"
|
||||
SelectedItem="{Binding SelectedFontWeight, RelativeSource={RelativeSource AncestorType=c:FontPicker}}"
|
||||
>
|
||||
@@ -74,7 +73,7 @@
|
||||
FontFamily="{Binding SelectedFontFamily, Mode=OneWay, RelativeSource={RelativeSource AncestorType=c:FontPicker}}"
|
||||
FontSize="{Binding SelectedFontSize, Mode=OneWay, RelativeSource={RelativeSource AncestorType=c:FontPicker}}"
|
||||
FontWeight="{Binding SelectedFontWeight, Mode=OneWay, RelativeSource={RelativeSource AncestorType=c:FontPicker}}"
|
||||
Foreground="{u:BTSBrush UI.default_text}"
|
||||
Foreground="{Binding SelectedFontColor, Mode=OneWay, RelativeSource={RelativeSource AncestorType=c:FontPicker}, Converter={StaticResource ColorToBrushConverter}}"
|
||||
TextWrapping="Wrap"
|
||||
>
|
||||
<TextBlock.Inlines>
|
||||
|
||||
@@ -12,12 +12,8 @@
|
||||
x:Name="order_type"
|
||||
Text="{Binding algo_root.order_type, Converter={StaticResource OrderTypeDisplayTextConverter}, StringFormat='{}{0}: ', Mode=OneWay}"
|
||||
FontWeight="Bold"
|
||||
Foreground="{u:BTSBrush UI.default_text}" />
|
||||
<TextBlock
|
||||
x:Name="display_name"
|
||||
Text="{Binding name, Mode=OneWay}"
|
||||
FontWeight="Normal"
|
||||
Foreground="{u:BTSBrush UI.default_text}" />
|
||||
/>
|
||||
<TextBlock x:Name="display_name" Text="{Binding name, Mode=OneWay}" FontWeight="Normal"/>
|
||||
</StackPanel>
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Value="{u:True}">
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<sys:Double x:Key="ButtonHeight">22.0</sys:Double>
|
||||
|
||||
<Style x:Key="SellButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Background" Value="{u:BTSBrush MarketData.ask_dark}"/>
|
||||
<Setter Property="Background" Value="{u:BTSColor MarketData.ask}"/>
|
||||
<Setter Property="BorderBrush" Value="{u:BTSColor UI.Button.normal_border}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.default_text}" />
|
||||
<Setter Property="MinHeight" Value="40"/>
|
||||
@@ -32,7 +32,7 @@
|
||||
</Style>
|
||||
|
||||
<Style x:Key="BuyButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Background" Value="{u:BTSBrush MarketData.bid_dark}"/>
|
||||
<Setter Property="Background" Value="{u:BTSColor MarketData.bid}"/>
|
||||
<Setter Property="BorderBrush" Value="{u:BTSColor UI.Button.normal_border}"/>
|
||||
<Setter Property="Padding" Value="2"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.default_text}" />
|
||||
|
||||
@@ -87,10 +87,10 @@
|
||||
</c:TickUpDown>
|
||||
</ControlTemplate>
|
||||
<Style TargetType="usp:PriceEntryControl">
|
||||
<Setter Property="Background" Value="{u:BTSBrush UI.Button.normal_background}" />
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.input_background}" />
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.input_text}" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Template" Value="{StaticResource PriceEntryTemplate}"/>
|
||||
</Style>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<Style TargetType="{x:Type c:TickUpDown}">
|
||||
<Setter Property="Background" Value="{StaticResource TextBox.Static.Background.Brush}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Height" Value="20" />
|
||||
@@ -75,8 +75,7 @@
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{u:BTSBrush UI.Button.mouseover_background}"/>
|
||||
<Setter TargetName="Bd" Property="BorderBrush" Value="{u:BTSBrush UI.Button.mouseover_border}"/>
|
||||
<Setter TargetName="Bd" Property="BorderBrush" Value="{StaticResource TextBox.MouseOver.Border.Brush}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
@@ -134,8 +133,7 @@
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{u:BTSBrush UI.Button.mouseover_background}"/>
|
||||
<Setter TargetName="Bd" Property="BorderBrush" Value="{u:BTSBrush UI.Button.mouseover_border}"/>
|
||||
<Setter TargetName="Bd" Property="BorderBrush" Value="{StaticResource TextBox.MouseOver.Border.Brush}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
@@ -147,7 +145,7 @@
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.TextBox.default_background}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.TextBox.default_foreground}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
@@ -190,7 +188,7 @@
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.TextBox.default_background}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.TextBox.default_foreground}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
@@ -232,7 +230,7 @@
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.TextBox.default_background}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.TextBox.default_foreground}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
@@ -274,7 +272,7 @@
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.TextBox.default_background}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.TextBox.default_foreground}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
@@ -312,7 +310,7 @@
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.TextBox.default_background}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.TextBox.default_foreground}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
@@ -350,7 +348,7 @@
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.TextBox.default_background}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.TextBox.default_foreground}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
@@ -386,7 +384,7 @@
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.TextBox.default_background}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.TextBox.default_foreground}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
@@ -430,7 +428,7 @@
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.TextBox.default_background}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.TextBox.default_foreground}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
@@ -468,7 +466,7 @@
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.TextBox.default_background}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.TextBox.default_foreground}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
@@ -550,7 +548,7 @@
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.TextBox.default_background}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.TextBox.default_foreground}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
@@ -588,7 +586,7 @@
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.TextBox.default_background}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.TextBox.default_foreground}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border.Brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
|
||||
@@ -1,264 +1,248 @@
|
||||
using bts.utils;
|
||||
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.ViewModel.Commands;
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.ui {
|
||||
static class Behavior {
|
||||
#region CloseWindow
|
||||
public static readonly DependencyProperty CloseWindowProperty = DependencyProperty.RegisterAttached("CloseWindow", typeof(bool), typeof(Behavior),
|
||||
new PropertyMetadata(false, (d, dpc_args) => {
|
||||
var button = d as Button;
|
||||
if (button != null && (bool)dpc_args.NewValue) {
|
||||
RoutedEventHandler close_window = (sender, e) => {
|
||||
var window = button.find_ancestor_of_type<Window>();
|
||||
if (window != null)
|
||||
window.Close();
|
||||
};
|
||||
button.Click += close_window;
|
||||
IDisposable dispose = Disposable.Create(() => {
|
||||
button.Click -= close_window;
|
||||
close_window = null;
|
||||
});
|
||||
IDisposable early_dispose = button.ObserveProperty(CloseWindowProperty, (sender, e) => {
|
||||
if (!GetCloseWindow(button))
|
||||
Disposable.dispose(ref dispose);
|
||||
});
|
||||
DisposeExtensions.attach_to_disposer(button, dispose);
|
||||
DisposeExtensions.attach_to_disposer(button, early_dispose);
|
||||
}
|
||||
}));
|
||||
public static bool GetCloseWindow(Button obj) { return (bool)obj.GetValue(CloseWindowProperty); }
|
||||
public static void SetCloseWindow(Button obj, bool value) { obj.SetValue(CloseWindowProperty, value); }
|
||||
#endregion
|
||||
|
||||
#region AnimatedScrollViewerVerticalOffset
|
||||
static double GetAnimatedScrollViewerVerticalOffset(DependencyObject obj) =>
|
||||
(double)obj.GetValue(AnimatedScrollViewerVerticalOffsetProperty);
|
||||
|
||||
public static void SetAnimatedScrollViewerVerticalOffset(DependencyObject obj, double value) =>
|
||||
obj.SetValue(AnimatedScrollViewerVerticalOffsetProperty, value);
|
||||
|
||||
public static readonly DependencyProperty AnimatedScrollViewerVerticalOffsetProperty =
|
||||
DependencyProperty.RegisterAttached("AnimatedScrollViewerVerticalOffset", typeof(double), typeof(Behavior), new PropertyMetadata(0.0, scroll_changed));
|
||||
|
||||
static void scroll_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
ScrollViewer target = d as ScrollViewer;
|
||||
if (target != null) {
|
||||
target.ScrollToVerticalOffset((double)e.NewValue);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AutoFocusTargetProvider
|
||||
public static readonly DependencyProperty AutoFocusTargetProviderProperty = DependencyProperty.RegisterAttached(
|
||||
"AutoFocusTargetProvider", typeof(IAutoFocusTargetProvider), typeof(Behavior), new PropertyMetadata(OnAutoFocusTargetProviderChanged));
|
||||
|
||||
static void OnAutoFocusTargetProviderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
if (e.NewValue is IAutoFocusTargetProvider aftp) {
|
||||
var el = ExceptionAssert.Argument.Is<FrameworkElement>(d, nameof(d));
|
||||
d.SetValue(AutoFocusTargetProviderLifeDependencyPropertyKey, new AutoFocusTargetProviderLifeDependency(el, aftp));
|
||||
} else
|
||||
d.SetValue(AutoFocusTargetProviderLifeDependencyPropertyKey, null);
|
||||
}
|
||||
|
||||
public static IAutoFocusTargetProvider GetAutoFocusTargetProvider(DependencyObject d) =>
|
||||
d.GetValue(AutoFocusTargetProviderProperty) as IAutoFocusTargetProvider;
|
||||
|
||||
public static void SetAutoFocusTargetProvider(DependencyObject d, IAutoFocusTargetProvider value) =>
|
||||
d.SetValue(AutoFocusTargetProviderProperty, value);
|
||||
#endregion
|
||||
|
||||
#region AutoFocusTargetProviderLifeDependency (ReadOnly)
|
||||
static readonly DependencyPropertyKey AutoFocusTargetProviderLifeDependencyPropertyKey = DependencyProperty.RegisterAttachedReadOnly("AutoFocusTargetProviderLifeDependency",
|
||||
typeof(FrameworkElementLifeDependency), typeof(Behavior), new PropertyMetadata(FrameworkElementLifeDependencyHelper.DisposeOldInitializeNew));
|
||||
|
||||
public static readonly DependencyProperty AutoFocusTargetProviderLifeDependencyProperty = AutoFocusTargetProviderLifeDependencyPropertyKey.DependencyProperty;
|
||||
|
||||
public static FrameworkElementLifeDependency GetAutoFocusTargetProviderLifeDependency(DependencyObject d) => d.GetValue(AutoFocusTargetProviderLifeDependencyProperty) as FrameworkElementLifeDependency;
|
||||
public static void SetAutoFocusTargetProviderLifeDependency(DependencyObject d, FrameworkElementLifeDependency value) => d.SetValue(AutoFocusTargetProviderLifeDependencyPropertyKey, value);
|
||||
|
||||
class AutoFocusTargetProviderLifeDependency : FrameworkElementLifeDependency {
|
||||
readonly IAutoFocusTargetProvider _focus_target_provider;
|
||||
|
||||
public AutoFocusTargetProviderLifeDependency(FrameworkElement root, IAutoFocusTargetProvider focus_target_provider) : base(root) =>
|
||||
_focus_target_provider = focus_target_provider;
|
||||
|
||||
protected override void OnLoaded() {
|
||||
var el = _focus_target_provider.Target;
|
||||
if (el != null) {
|
||||
if (_focus_target_provider.Focus) {
|
||||
var fs = FocusManager.GetFocusScope(el);
|
||||
if (fs is not null) {
|
||||
FocusManager.SetFocusedElement(fs, el);
|
||||
if (fs is UIElement fsUiEl) {
|
||||
fsUiEl.Focus();
|
||||
} else if (fs is IInputElement fsInputEl) {
|
||||
Keyboard.Focus(fsInputEl);
|
||||
} else {
|
||||
el.Focus();
|
||||
}
|
||||
} else {
|
||||
el.Focus();
|
||||
}
|
||||
}
|
||||
if (_focus_target_provider.FocusKeyboard)
|
||||
Keyboard.Focus(el);
|
||||
if (_focus_target_provider.CaptureMouse)
|
||||
el.CaptureMouse();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnUnloaded() {
|
||||
var el = _focus_target_provider.Target;
|
||||
if (el != null) {
|
||||
if (_focus_target_provider.ReleaseKeyboard) {
|
||||
ReferenceEquals(Keyboard.FocusedElement, el);
|
||||
Keyboard.ClearFocus();
|
||||
}
|
||||
if (_focus_target_provider.ReleaseMouse)
|
||||
el.ReleaseMouseCapture();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region EatMouseDown
|
||||
public static readonly DependencyProperty EatMouseDownProperty =
|
||||
DependencyProperty.RegisterAttached("EatMouseDown", typeof(bool), typeof(Behavior), new PropertyMetadata(false, OnEatMouseDownChanged));
|
||||
|
||||
static void OnEatMouseDownChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var el = (FrameworkElement)d;
|
||||
if ((bool)e.NewValue) el.MouseDown += _eat_mouse_down;
|
||||
else el.MouseDown -= _eat_mouse_down;
|
||||
}
|
||||
|
||||
static void _eat_mouse_down(object sender, MouseButtonEventArgs e) =>
|
||||
e.Handled = true;
|
||||
|
||||
public static bool GetEatMouseDown(DependencyObject d) => (bool)d.GetValue(EatMouseDownProperty);
|
||||
public static void SetEatMouseDown(DependencyObject d, bool value) => d.SetValue(EatMouseDownProperty, value);
|
||||
#endregion
|
||||
|
||||
#region DisablePaste
|
||||
public static readonly DependencyProperty DisablePasteProperty = DependencyProperty.RegisterAttached(
|
||||
"DisablePaste", typeof(bool), typeof(Behavior), new PropertyMetadata(false, OnDisablePasteChanged));
|
||||
|
||||
static void OnDisablePasteChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
if ((bool)e.NewValue)
|
||||
DataObject.AddPastingHandler(d, _disable_paste_pasting_handler);
|
||||
else DataObject.RemovePastingHandler(d, _disable_paste_pasting_handler);
|
||||
}
|
||||
static void _disable_paste_pasting_handler(object sender, DataObjectPastingEventArgs e) => e.CancelCommand();
|
||||
|
||||
public static bool GetDisablePaste(DependencyObject d) => (bool)d.GetValue(DisablePasteProperty);
|
||||
public static void SetDisablePaste(DependencyObject d, bool value) => d.SetValue(DisablePasteProperty, value);
|
||||
#endregion
|
||||
|
||||
public static readonly DependencyProperty BindingUpdateGroupsProperty =
|
||||
DependencyProperty.RegisterAttached("BindingUpdateGroups", typeof(BindingUpdateGroupCollection), typeof(Behavior), new PropertyMetadata(null));
|
||||
|
||||
public static BindingUpdateGroupCollection GetBindingUpdateGroups(DependencyObject d) => d.GetValue(BindingUpdateGroupsProperty) as BindingUpdateGroupCollection;
|
||||
public static void SetBindingUpdateGroups(DependencyObject d, BindingUpdateGroupCollection value) => d.SetValue(BindingUpdateGroupsProperty, value);
|
||||
|
||||
#region AutoRaiseCanExecuteChanged
|
||||
public static readonly DependencyProperty AutoRaiseCanExecuteChangedProperty =
|
||||
DependencyProperty.RegisterAttached("AutoRaiseCanExecuteChanged", typeof(bool), typeof(Behavior),
|
||||
new PropertyMetadata(false, (d, e) => {
|
||||
if ((bool)e.NewValue) {
|
||||
BindingOperations.SetBinding(d, AutoCommandProperty, new Binding(nameof(ButtonBase.Command)) { Source = d, Mode = BindingMode.OneWay });
|
||||
BindingOperations.SetBinding(d, AutoCommandParameterProperty, new Binding(nameof(ButtonBase.CommandParameter)) { Source = d, Mode = BindingMode.OneWay });
|
||||
} else {
|
||||
BindingOperations.ClearBinding(d, AutoCommandProperty);
|
||||
BindingOperations.ClearBinding(d, AutoCommandParameterProperty);
|
||||
}
|
||||
}));
|
||||
|
||||
public static bool GetAutoRaiseCanExecuteChanged(DependencyObject d) => (bool)d.GetValue(AutoRaiseCanExecuteChangedProperty);
|
||||
public static void SetAutoRaiseCanExecuteChanged(DependencyObject d, bool value) => d.SetValue(AutoRaiseCanExecuteChangedProperty, value);
|
||||
|
||||
/// Do not bind directly to <see cref="AutoCommandProperty"/> (used internally by <see cref="AutoRaiseCanExecuteChangedProperty"/>
|
||||
public static readonly DependencyProperty AutoCommandProperty =
|
||||
DependencyProperty.RegisterAttached("AutoCommand", typeof(ICommand), typeof(Behavior), new PropertyMetadata(null, _try_raise_can_execute));
|
||||
|
||||
public static ICommand GetAutoCommand(DependencyObject d) => d.GetValue(AutoCommandProperty) as ICommand;
|
||||
public static void SetAutoCommand(DependencyObject d, ICommand value) => d.SetValue(AutoCommandProperty, value);
|
||||
|
||||
/// Do not bind directly to <see cref="AutoCommandParameterProperty"/> (used internally by <see cref="AutoRaiseCanExecuteChangedProperty"/>
|
||||
public static readonly DependencyProperty AutoCommandParameterProperty =
|
||||
DependencyProperty.RegisterAttached("AutoCommandParameter", typeof(object), typeof(Behavior), new PropertyMetadata(null, _try_raise_can_execute));
|
||||
|
||||
public static object GetAutoCommandParameter(DependencyObject d) => d.GetValue(AutoCommandParameterProperty);
|
||||
public static void SetAutoCommandParameter(DependencyObject d, object value) => d.SetValue(AutoCommandParameterProperty, value);
|
||||
|
||||
static void _try_raise_can_execute(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
|
||||
(d.GetValue(ButtonBase.CommandProperty) as IRaiseCanExecuteChangedCommand)?.RaiseCanExecuteChanged();
|
||||
#endregion
|
||||
|
||||
#region FocusOnLoaded
|
||||
public static readonly DependencyProperty FocusOnLoadedProperty =
|
||||
DependencyProperty.RegisterAttached("FocusOnLoaded", typeof(bool), typeof(Behavior), new PropertyMetadata(false, OnFocusOnLoadedChanged));
|
||||
|
||||
static void OnFocusOnLoadedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var target = (FrameworkElement)d;
|
||||
if ((bool)e.NewValue) target.Loaded += _target_Loaded;
|
||||
else target.Loaded -= _target_Loaded;
|
||||
}
|
||||
|
||||
static void _target_Loaded(object sender, RoutedEventArgs e) {
|
||||
var target = (FrameworkElement)sender;
|
||||
Keyboard.Focus(target);
|
||||
}
|
||||
public static bool GetFocusOnLoaded(DependencyObject d) => (bool)d.GetValue(FocusOnLoadedProperty);
|
||||
public static void SetFocusOnLoaded(DependencyObject d, bool value) => d.SetValue(FocusOnLoadedProperty, value);
|
||||
#endregion
|
||||
|
||||
#region MirrorLayout
|
||||
static readonly DependencyProperty[] _layout_props = new[] {
|
||||
FrameworkElement.HeightProperty,
|
||||
FrameworkElement.WidthProperty,
|
||||
FrameworkElement.MarginProperty,
|
||||
Control.PaddingProperty,
|
||||
FrameworkElement.HorizontalAlignmentProperty,
|
||||
Control.HorizontalContentAlignmentProperty,
|
||||
FrameworkElement.VerticalAlignmentProperty,
|
||||
Control.VerticalContentAlignmentProperty
|
||||
};
|
||||
|
||||
public static readonly DependencyProperty MirrorLayoutForProperty =
|
||||
DependencyProperty.RegisterAttached("MirrorLayoutFor", typeof(FrameworkElement), typeof(Behavior),
|
||||
new PropertyMetadata(OnMirrorLayoutForChanged));
|
||||
|
||||
static void OnMirrorLayoutForChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var target = new LazyCast<FrameworkElement, DependencyObject>(d);
|
||||
if (e.OldValue is FrameworkElement old_src) {
|
||||
target.value.clear_bindings(_layout_props);
|
||||
}
|
||||
if (e.NewValue is FrameworkElement new_src) {
|
||||
target.value.bind(FrameworkElement.WidthProperty, new_src, FrameworkElement.ActualWidthProperty);
|
||||
target.value.bind(FrameworkElement.HeightProperty, new_src, FrameworkElement.ActualHeightProperty);
|
||||
target.value.bind(FrameworkElement.MarginProperty, new_src, FrameworkElement.MarginProperty);
|
||||
target.value.bind(FrameworkElement.HorizontalAlignmentProperty, new_src, FrameworkElement.HorizontalAlignmentProperty);
|
||||
target.value.bind(FrameworkElement.VerticalAlignmentProperty, new_src, FrameworkElement.VerticalAlignmentProperty);
|
||||
if (target.value is Control && new_src is Control) {
|
||||
target.value.bind(Control.PaddingProperty, new_src, Control.PaddingProperty);
|
||||
target.value.bind(Control.HorizontalContentAlignmentProperty, new_src, Control.HorizontalContentAlignmentProperty);
|
||||
target.value.bind(Control.VerticalContentAlignmentProperty, new_src, Control.VerticalContentAlignmentProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static FrameworkElement GetMirrorLayoutFor(DependencyObject d) => d.GetValue(MirrorLayoutForProperty) as FrameworkElement;
|
||||
public static void SetMirrorLayoutFor(DependencyObject d, FrameworkElement value) => d.SetValue(MirrorLayoutForProperty, value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
using bts.utils;
|
||||
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.ViewModel.Commands;
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.ui {
|
||||
static class Behavior {
|
||||
#region CloseWindow
|
||||
public static readonly DependencyProperty CloseWindowProperty = DependencyProperty.RegisterAttached("CloseWindow", typeof(bool), typeof(Behavior),
|
||||
new PropertyMetadata(false, (d, dpc_args) => {
|
||||
var button = d as Button;
|
||||
if (button != null && (bool)dpc_args.NewValue) {
|
||||
RoutedEventHandler close_window = (sender, e) => {
|
||||
var window = button.find_ancestor_of_type<Window>();
|
||||
if (window != null)
|
||||
window.Close();
|
||||
};
|
||||
button.Click += close_window;
|
||||
IDisposable dispose = Disposable.Create(() => {
|
||||
button.Click -= close_window;
|
||||
close_window = null;
|
||||
});
|
||||
IDisposable early_dispose = button.ObserveProperty(CloseWindowProperty, (sender, e) => {
|
||||
if (!GetCloseWindow(button))
|
||||
Disposable.dispose(ref dispose);
|
||||
});
|
||||
DisposeExtensions.attach_to_disposer(button, dispose);
|
||||
DisposeExtensions.attach_to_disposer(button, early_dispose);
|
||||
}
|
||||
}));
|
||||
public static bool GetCloseWindow(Button obj) { return (bool)obj.GetValue(CloseWindowProperty); }
|
||||
public static void SetCloseWindow(Button obj, bool value) { obj.SetValue(CloseWindowProperty, value); }
|
||||
#endregion
|
||||
|
||||
#region AnimatedScrollViewerVerticalOffset
|
||||
static double GetAnimatedScrollViewerVerticalOffset(DependencyObject obj) =>
|
||||
(double)obj.GetValue(AnimatedScrollViewerVerticalOffsetProperty);
|
||||
|
||||
public static void SetAnimatedScrollViewerVerticalOffset(DependencyObject obj, double value) =>
|
||||
obj.SetValue(AnimatedScrollViewerVerticalOffsetProperty, value);
|
||||
|
||||
public static readonly DependencyProperty AnimatedScrollViewerVerticalOffsetProperty =
|
||||
DependencyProperty.RegisterAttached("AnimatedScrollViewerVerticalOffset", typeof(double), typeof(Behavior), new PropertyMetadata(0.0, scroll_changed));
|
||||
|
||||
static void scroll_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
ScrollViewer target = d as ScrollViewer;
|
||||
if (target != null) {
|
||||
target.ScrollToVerticalOffset((double)e.NewValue);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AutoFocusTargetProvider
|
||||
public static readonly DependencyProperty AutoFocusTargetProviderProperty = DependencyProperty.RegisterAttached("AutoFocusTargetProvider", typeof(bool),
|
||||
typeof(Behavior), new PropertyMetadata(OnAutoFocusTargetProviderChanged));
|
||||
|
||||
static void OnAutoFocusTargetProviderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
if (e.NewValue is IAutoFocusTargetProvider aftp) {
|
||||
var el = ExceptionAssert.Argument.Is<FrameworkElement>(d, nameof(d));
|
||||
d.SetValue(AutoFocusTargetProviderLifeDependencyPropertyKey, new AutoFocusTargetProviderLifeDependency(el, aftp));
|
||||
} else
|
||||
d.SetValue(AutoFocusTargetProviderLifeDependencyPropertyKey, null);
|
||||
}
|
||||
|
||||
public static bool GetAutoFocusTargetProvider(DependencyObject d) => (bool)d.GetValue(AutoFocusTargetProviderProperty);
|
||||
public static void SetAutoFocusTargetProvider(DependencyObject d, bool value) => d.SetValue(AutoFocusTargetProviderProperty, value);
|
||||
#endregion
|
||||
|
||||
#region AutoFocusTargetProviderLifeDependency (ReadOnly)
|
||||
static readonly DependencyPropertyKey AutoFocusTargetProviderLifeDependencyPropertyKey = DependencyProperty.RegisterAttachedReadOnly("AutoFocusTargetProviderLifeDependency",
|
||||
typeof(FrameworkElementLifeDependency), typeof(Behavior), new PropertyMetadata(FrameworkElementLifeDependencyHelper.DisposeOldInitializeNew));
|
||||
|
||||
public static readonly DependencyProperty AutoFocusTargetProviderLifeDependencyProperty = AutoFocusTargetProviderLifeDependencyPropertyKey.DependencyProperty;
|
||||
|
||||
public static FrameworkElementLifeDependency GetAutoFocusTargetProviderLifeDependency(DependencyObject d) => d.GetValue(AutoFocusTargetProviderLifeDependencyProperty) as FrameworkElementLifeDependency;
|
||||
public static void SetAutoFocusTargetProviderLifeDependency(DependencyObject d, FrameworkElementLifeDependency value) => d.SetValue(AutoFocusTargetProviderLifeDependencyPropertyKey, value);
|
||||
|
||||
class AutoFocusTargetProviderLifeDependency : FrameworkElementLifeDependency {
|
||||
readonly IAutoFocusTargetProvider _focus_target_provider;
|
||||
|
||||
public AutoFocusTargetProviderLifeDependency(FrameworkElement root, IAutoFocusTargetProvider focus_target_provider) : base(root) =>
|
||||
_focus_target_provider = focus_target_provider;
|
||||
|
||||
protected override void OnLoaded() {
|
||||
var el = _focus_target_provider.Target;
|
||||
if (el != null) {
|
||||
if (_focus_target_provider.Focus)
|
||||
el.Focus();
|
||||
if (_focus_target_provider.FocusKeyboard)
|
||||
Keyboard.Focus(el);
|
||||
if (_focus_target_provider.CaptureMouse)
|
||||
el.CaptureMouse();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnUnloaded() {
|
||||
var el = _focus_target_provider.Target;
|
||||
if (el != null) {
|
||||
if (_focus_target_provider.ReleaseKeyboard) {
|
||||
ReferenceEquals(Keyboard.FocusedElement, el);
|
||||
Keyboard.ClearFocus();
|
||||
}
|
||||
if (_focus_target_provider.ReleaseMouse)
|
||||
el.ReleaseMouseCapture();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region EatMouseDown
|
||||
public static readonly DependencyProperty EatMouseDownProperty =
|
||||
DependencyProperty.RegisterAttached("EatMouseDown", typeof(bool), typeof(Behavior), new PropertyMetadata(false, OnEatMouseDownChanged));
|
||||
|
||||
static void OnEatMouseDownChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var el = (FrameworkElement)d;
|
||||
if ((bool)e.NewValue) el.MouseDown += _eat_mouse_down;
|
||||
else el.MouseDown -= _eat_mouse_down;
|
||||
}
|
||||
|
||||
static void _eat_mouse_down(object sender, MouseButtonEventArgs e) =>
|
||||
e.Handled = true;
|
||||
|
||||
public static bool GetEatMouseDown(DependencyObject d) => (bool)d.GetValue(EatMouseDownProperty);
|
||||
public static void SetEatMouseDown(DependencyObject d, bool value) => d.SetValue(EatMouseDownProperty, value);
|
||||
#endregion
|
||||
|
||||
#region DisablePaste
|
||||
public static readonly DependencyProperty DisablePasteProperty = DependencyProperty.RegisterAttached(
|
||||
"DisablePaste", typeof(bool), typeof(Behavior), new PropertyMetadata(false, OnDisablePasteChanged));
|
||||
|
||||
static void OnDisablePasteChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
if ((bool)e.NewValue)
|
||||
DataObject.AddPastingHandler(d, _disable_paste_pasting_handler);
|
||||
else DataObject.RemovePastingHandler(d, _disable_paste_pasting_handler);
|
||||
}
|
||||
static void _disable_paste_pasting_handler(object sender, DataObjectPastingEventArgs e) => e.CancelCommand();
|
||||
|
||||
public static bool GetDisablePaste(DependencyObject d) => (bool)d.GetValue(DisablePasteProperty);
|
||||
public static void SetDisablePaste(DependencyObject d, bool value) => d.SetValue(DisablePasteProperty, value);
|
||||
#endregion
|
||||
|
||||
public static readonly DependencyProperty BindingUpdateGroupsProperty =
|
||||
DependencyProperty.RegisterAttached("BindingUpdateGroups", typeof(BindingUpdateGroupCollection), typeof(Behavior), new PropertyMetadata(null));
|
||||
|
||||
public static BindingUpdateGroupCollection GetBindingUpdateGroups(DependencyObject d) => d.GetValue(BindingUpdateGroupsProperty) as BindingUpdateGroupCollection;
|
||||
public static void SetBindingUpdateGroups(DependencyObject d, BindingUpdateGroupCollection value) => d.SetValue(BindingUpdateGroupsProperty, value);
|
||||
|
||||
#region AutoRaiseCanExecuteChanged
|
||||
public static readonly DependencyProperty AutoRaiseCanExecuteChangedProperty =
|
||||
DependencyProperty.RegisterAttached("AutoRaiseCanExecuteChanged", typeof(bool), typeof(Behavior),
|
||||
new PropertyMetadata(false, (d, e) => {
|
||||
if ((bool)e.NewValue) {
|
||||
BindingOperations.SetBinding(d, AutoCommandProperty, new Binding(nameof(ButtonBase.Command)) { Source = d, Mode = BindingMode.OneWay });
|
||||
BindingOperations.SetBinding(d, AutoCommandParameterProperty, new Binding(nameof(ButtonBase.CommandParameter)) { Source = d, Mode = BindingMode.OneWay });
|
||||
} else {
|
||||
BindingOperations.ClearBinding(d, AutoCommandProperty);
|
||||
BindingOperations.ClearBinding(d, AutoCommandParameterProperty);
|
||||
}
|
||||
}));
|
||||
|
||||
public static bool GetAutoRaiseCanExecuteChanged(DependencyObject d) => (bool)d.GetValue(AutoRaiseCanExecuteChangedProperty);
|
||||
public static void SetAutoRaiseCanExecuteChanged(DependencyObject d, bool value) => d.SetValue(AutoRaiseCanExecuteChangedProperty, value);
|
||||
|
||||
/// Do not bind directly to <see cref="AutoCommandProperty"/> (used internally by <see cref="AutoRaiseCanExecuteChangedProperty"/>
|
||||
public static readonly DependencyProperty AutoCommandProperty =
|
||||
DependencyProperty.RegisterAttached("AutoCommand", typeof(ICommand), typeof(Behavior), new PropertyMetadata(null, _try_raise_can_execute));
|
||||
|
||||
public static ICommand GetAutoCommand(DependencyObject d) => d.GetValue(AutoCommandProperty) as ICommand;
|
||||
public static void SetAutoCommand(DependencyObject d, ICommand value) => d.SetValue(AutoCommandProperty, value);
|
||||
|
||||
/// Do not bind directly to <see cref="AutoCommandParameterProperty"/> (used internally by <see cref="AutoRaiseCanExecuteChangedProperty"/>
|
||||
public static readonly DependencyProperty AutoCommandParameterProperty =
|
||||
DependencyProperty.RegisterAttached("AutoCommandParameter", typeof(object), typeof(Behavior), new PropertyMetadata(null, _try_raise_can_execute));
|
||||
|
||||
public static object GetAutoCommandParameter(DependencyObject d) => d.GetValue(AutoCommandParameterProperty);
|
||||
public static void SetAutoCommandParameter(DependencyObject d, object value) => d.SetValue(AutoCommandParameterProperty, value);
|
||||
|
||||
static void _try_raise_can_execute(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
|
||||
(d.GetValue(ButtonBase.CommandProperty) as IRaiseCanExecuteChangedCommand)?.RaiseCanExecuteChanged();
|
||||
#endregion
|
||||
|
||||
#region FocusOnLoaded
|
||||
public static readonly DependencyProperty FocusOnLoadedProperty =
|
||||
DependencyProperty.RegisterAttached("FocusOnLoaded", typeof(bool), typeof(Behavior), new PropertyMetadata(false, OnFocusOnLoadedChanged));
|
||||
|
||||
static void OnFocusOnLoadedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var target = (FrameworkElement)d;
|
||||
if ((bool)e.NewValue) target.Loaded += _target_Loaded;
|
||||
else target.Loaded -= _target_Loaded;
|
||||
}
|
||||
|
||||
static void _target_Loaded(object sender, RoutedEventArgs e) {
|
||||
var target = (FrameworkElement)sender;
|
||||
Keyboard.Focus(target);
|
||||
}
|
||||
public static bool GetFocusOnLoaded(DependencyObject d) => (bool)d.GetValue(FocusOnLoadedProperty);
|
||||
public static void SetFocusOnLoaded(DependencyObject d, bool value) => d.SetValue(FocusOnLoadedProperty, value);
|
||||
#endregion
|
||||
|
||||
#region MirrorLayout
|
||||
static readonly DependencyProperty[] _layout_props = new[] {
|
||||
FrameworkElement.HeightProperty,
|
||||
FrameworkElement.WidthProperty,
|
||||
FrameworkElement.MarginProperty,
|
||||
Control.PaddingProperty,
|
||||
FrameworkElement.HorizontalAlignmentProperty,
|
||||
Control.HorizontalContentAlignmentProperty,
|
||||
FrameworkElement.VerticalAlignmentProperty,
|
||||
Control.VerticalContentAlignmentProperty
|
||||
};
|
||||
|
||||
public static readonly DependencyProperty MirrorLayoutForProperty =
|
||||
DependencyProperty.RegisterAttached("MirrorLayoutFor", typeof(FrameworkElement), typeof(Behavior),
|
||||
new PropertyMetadata(OnMirrorLayoutForChanged));
|
||||
|
||||
static void OnMirrorLayoutForChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var target = new LazyCast<FrameworkElement, DependencyObject>(d);
|
||||
if (e.OldValue is FrameworkElement old_src) {
|
||||
target.value.clear_bindings(_layout_props);
|
||||
}
|
||||
if (e.NewValue is FrameworkElement new_src) {
|
||||
target.value.bind(FrameworkElement.WidthProperty, new_src, FrameworkElement.ActualWidthProperty);
|
||||
target.value.bind(FrameworkElement.HeightProperty, new_src, FrameworkElement.ActualHeightProperty);
|
||||
target.value.bind(FrameworkElement.MarginProperty, new_src, FrameworkElement.MarginProperty);
|
||||
target.value.bind(FrameworkElement.HorizontalAlignmentProperty, new_src, FrameworkElement.HorizontalAlignmentProperty);
|
||||
target.value.bind(FrameworkElement.VerticalAlignmentProperty, new_src, FrameworkElement.VerticalAlignmentProperty);
|
||||
if (target.value is Control && new_src is Control) {
|
||||
target.value.bind(Control.PaddingProperty, new_src, Control.PaddingProperty);
|
||||
target.value.bind(Control.HorizontalContentAlignmentProperty, new_src, Control.HorizontalContentAlignmentProperty);
|
||||
target.value.bind(Control.VerticalContentAlignmentProperty, new_src, Control.VerticalContentAlignmentProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static FrameworkElement GetMirrorLayoutFor(DependencyObject d) => d.GetValue(MirrorLayoutForProperty) as FrameworkElement;
|
||||
public static void SetMirrorLayoutFor(DependencyObject d, FrameworkElement value) => d.SetValue(MirrorLayoutForProperty, value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -557,8 +557,6 @@
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="DefaultButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}" />
|
||||
|
||||
<Style TargetType="RepeatButton">
|
||||
<Setter Property="Foreground" Value="{u:BTSBrush UI.Button.normal_foreground}" />
|
||||
<Setter Property="Background" Value="{u:BTSBrush UI.Button.normal_background}" />
|
||||
|
||||
Loaded 100 of 587 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user