Compare commits
391
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb560bbe33 | ||
|
|
248f2644ca | ||
|
|
b87d86f4d6 | ||
|
|
8515196dcb | ||
|
|
e5e042f016 | ||
|
|
da3593326d | ||
|
|
313d3a2377 | ||
|
|
22f0e126d0 | ||
|
|
c76b061e20 | ||
|
|
0107b65281 | ||
|
|
6b5496b799 | ||
|
|
42f9daf9cb | ||
|
|
bc5ddade7f | ||
|
|
d8d198f7f4 | ||
|
|
af9eb670c1 | ||
|
|
d91837c377 | ||
|
|
7725ae33ef | ||
|
|
9f56abaa38 | ||
|
|
95444d4a4c | ||
|
|
a7e1c18fe2 | ||
|
|
d11fd23cb0 | ||
|
|
8499ac0dfb | ||
|
|
f4afe348e6 | ||
|
|
096424b60b | ||
|
|
b4f5e66466 | ||
|
|
69a0bcc9cd | ||
|
|
d72ebdf421 | ||
|
|
8e93856767 | ||
|
|
19dae6cd34 | ||
|
|
24f42550dd | ||
|
|
c2150ae450 | ||
|
|
40dbfaa068 | ||
|
|
3756a8a8b7 | ||
|
|
46c8dcb18f | ||
|
|
024480f32e | ||
|
|
ba525372bb | ||
|
|
8aa0f1b674 | ||
|
|
fc14215d9b | ||
|
|
091ed5a327 | ||
|
|
30bb9d0837 | ||
|
|
9cb2c580d6 | ||
|
|
2e2150ac1a | ||
|
|
21d9435a67 | ||
|
|
e7e334fb5a | ||
|
|
61f04c4605 | ||
|
|
24fb2e1e39 | ||
|
|
ece141c448 | ||
|
|
617bac8e74 | ||
|
|
ab217401eb | ||
|
|
77e5c1481b | ||
|
|
a0f77f92a3 | ||
|
|
0acdcb150d | ||
|
|
ac2f3b9bc6 | ||
|
|
0e522f90e5 | ||
|
|
40ad1f1b48 | ||
|
|
1cb75d399f | ||
|
|
cddecc0543 | ||
|
|
484e36b330 | ||
|
|
8a9c22fa2a | ||
|
|
a6e8853cb9 | ||
|
|
dafa193283 | ||
|
|
23651970c9 | ||
|
|
043efab5a2 | ||
|
|
5476466d55 | ||
|
|
54cb5a2ab2 | ||
|
|
29bf7d8d7e | ||
|
|
855d2fd790 | ||
|
|
8a6a652882 | ||
|
|
3f288704bf | ||
|
|
98bc78463d | ||
|
|
6d271ad7cd | ||
|
|
2a17d656a5 | ||
|
|
10436f0f3b | ||
|
|
86b19f0ef0 | ||
|
|
502024dd10 | ||
|
|
623012b0b0 | ||
|
|
5859f0f2d1 | ||
|
|
eeffb735ae | ||
|
|
b602997f3a | ||
|
|
3530a77b38 | ||
|
|
afdf635b31 | ||
|
|
4d4aefc0a7 | ||
|
|
960a8af77a | ||
|
|
89828659a4 | ||
|
|
76f83fd7f5 | ||
|
|
d4e40948d2 | ||
|
|
912ab512d2 | ||
|
|
5d3eb92175 | ||
|
|
924386084b | ||
|
|
acab3ac392 | ||
|
|
d06a62f6ee | ||
|
|
14e3eee0bc | ||
|
|
7277bf52f5 | ||
|
|
b81dc7f5cf | ||
|
|
b1a4635345 | ||
|
|
7c7c689170 | ||
|
|
116506c8da | ||
|
|
3feda49ef1 | ||
|
|
10318be7bc | ||
|
|
f7d525a527 | ||
|
|
d2fe9f167a | ||
|
|
696d91a5af | ||
|
|
d575ba0ea8 | ||
|
|
058b131760 | ||
|
|
57f8f3ff58 | ||
|
|
775df2850d | ||
|
|
cb2b15f10b | ||
|
|
60362a4c95 | ||
|
|
c6d7a8f38c | ||
|
|
7931c62b46 | ||
|
|
58518451c8 | ||
|
|
e3bad78aef | ||
|
|
8e9ab8f2eb | ||
|
|
8512f414d6 | ||
|
|
513fc353cf | ||
|
|
4a04beafd2 | ||
|
|
79a3251f26 | ||
|
|
8785e0fecd | ||
|
|
04251c96b6 | ||
|
|
c2d400af1a | ||
|
|
133cc16628 | ||
|
|
16f7aede93 | ||
|
|
2b9488faff | ||
|
|
51b6d592d3 | ||
|
|
1eb4ceba0a | ||
|
|
dbf5cadb44 | ||
|
|
90f1bb0851 | ||
|
|
547d83e1b8 | ||
|
|
e888149c41 | ||
|
|
b944c3980f | ||
|
|
c2a8266106 | ||
|
|
4277ba61b9 | ||
|
|
35cfe9533b | ||
|
|
d49efef93d | ||
|
|
502ed13456 | ||
|
|
716c4dd674 | ||
|
|
4a2b189a07 | ||
|
|
a42edaf9fa | ||
|
|
cbc142ab15 | ||
|
|
3980f2d030 | ||
|
|
00f6d269c9 | ||
|
|
10b2d38de8 | ||
|
|
6d4d8f6ac1 | ||
|
|
5db89e1e8d | ||
|
|
379a46afa2 | ||
|
|
d9eff37184 | ||
|
|
3d055de139 | ||
|
|
648be7dd97 | ||
|
|
2cac9affe4 | ||
|
|
30ff3ba707 | ||
|
|
9d3afd68aa | ||
|
|
8c3de1789b | ||
|
|
b251e1d47e | ||
|
|
6ccd48a8ba | ||
|
|
41d84f7f73 | ||
|
|
795062864d | ||
|
|
1ff72e9444 | ||
|
|
e13b5066a3 | ||
|
|
5ba7901258 | ||
|
|
bc6eb59110 | ||
|
|
5ae5a5dcbf | ||
|
|
7966bcce77 | ||
|
|
ff86b10895 | ||
|
|
ea3c0717d8 | ||
|
|
d0abca3fd9 | ||
|
|
7895fe7357 | ||
|
|
6f5f4f5cc2 | ||
|
|
066a0ba693 | ||
|
|
c2b67b4813 | ||
|
|
ecad456a2d | ||
|
|
2cb3cfdcdd | ||
|
|
0ebc8e5b4b | ||
|
|
2072105c0f | ||
|
|
d663c86aa0 | ||
|
|
4a2dfa46a1 | ||
|
|
6e35db6a24 | ||
|
|
b45dc3bc2d | ||
|
|
a1912907b8 | ||
|
|
b1b3dcdeb8 | ||
|
|
03dc8ef767 | ||
|
|
975bf56c2a | ||
|
|
3f58c2ce84 | ||
|
|
96f28acf91 | ||
|
|
c2c2658e21 | ||
|
|
d6b8e359cd | ||
|
|
84a0850d95 | ||
|
|
4effdc4160 | ||
|
|
b9fe39f6e3 | ||
|
|
b555850015 | ||
|
|
23794fa30d | ||
|
|
90b5a309a3 | ||
|
|
07beda3fc6 | ||
|
|
e3c1ea70f2 | ||
|
|
93b93969f5 | ||
|
|
0a10111846 | ||
|
|
eee00cc407 | ||
|
|
0eb58eaba8 | ||
|
|
12f6733d1e | ||
|
|
93a0984ed1 | ||
|
|
b7e9c393a0 | ||
|
|
aa602b3fd6 | ||
|
|
b4b8cabdae | ||
|
|
cfe06869b3 | ||
|
|
b177ed1cc0 | ||
|
|
ff17a64933 | ||
|
|
81a1fe11f6 | ||
|
|
ad5bf88fcf | ||
|
|
e15f155ac9 | ||
|
|
6d6822cd65 | ||
|
|
ace0c70516 | ||
|
|
e9e5059bc2 | ||
|
|
cde72f51a0 | ||
|
|
e1fd49ae32 | ||
|
|
274f3f98e4 | ||
|
|
a4b7d84318 | ||
|
|
be17205094 | ||
|
|
6ef9bee8e4 | ||
|
|
2c8b3563f1 | ||
|
|
71f7e64ccb | ||
|
|
c31b44ecfb | ||
|
|
19efb77e07 | ||
|
|
76de640421 | ||
|
|
fb030742d3 | ||
|
|
73608f1b45 | ||
|
|
58aebe7002 | ||
|
|
299cb0b29e | ||
|
|
06663f711e | ||
|
|
51479f4885 | ||
|
|
5b520ba241 | ||
|
|
f0651a5341 | ||
|
|
897e0990a2 | ||
|
|
30e1ae5c3c | ||
|
|
efe83f81ec | ||
|
|
5fb21ada97 | ||
|
|
58edd07f6b | ||
|
|
9d81354c3a | ||
|
|
0f37be89a4 | ||
|
|
f6586fe636 | ||
|
|
7827ccc0e7 | ||
|
|
af6a2acc0a | ||
|
|
84abf59a55 | ||
|
|
2fc1d30bf1 | ||
|
|
4dcd5d63a1 | ||
|
|
63e5697b20 | ||
|
|
8e5cb100a7 | ||
|
|
938bee1817 | ||
|
|
d48af15480 | ||
|
|
e3b2e98cd9 | ||
|
|
ee53fb854c | ||
|
|
d36e2d61f6 | ||
|
|
bfff3ea5fe | ||
|
|
059c03d289 | ||
|
|
8c561f7886 | ||
|
|
288567d1eb | ||
|
|
ecfc5405fe | ||
|
|
8db34a2a11 | ||
|
|
91094c4d84 | ||
|
|
dae8c85d73 | ||
|
|
abdd98082f | ||
|
|
2f6aa785ab | ||
|
|
7acf1f7043 | ||
|
|
1c3c7a078a | ||
|
|
93ace52bfa | ||
|
|
b5d97c0b1b | ||
|
|
284aa53ad4 | ||
|
|
8a9952f52d | ||
|
|
748aafaab3 | ||
|
|
3ba30c70de | ||
|
|
e72f465999 | ||
|
|
f3d93f7749 | ||
|
|
8d142b8a74 | ||
|
|
8210b9f35f | ||
|
|
12b20b05ce | ||
|
|
72efbccdbd | ||
|
|
33643e9eab | ||
|
|
0f6dc6f40b | ||
|
|
8c778f5e41 | ||
|
|
f308654200 | ||
|
|
bdf5b980fc | ||
|
|
914ac597cd | ||
|
|
4aa6a77680 | ||
|
|
6b59dd84ee | ||
|
|
4e2c5f2d17 | ||
|
|
9a1b94a1b0 | ||
|
|
13b3c55771 | ||
|
|
d0a6fcdb9c | ||
|
|
1eb13028c1 | ||
|
|
424e5ff16b | ||
|
|
bed389885b | ||
|
|
85cf25a57a | ||
|
|
61471d4675 | ||
|
|
d83435d49d | ||
|
|
582fc10dce | ||
|
|
5969985d24 | ||
|
|
fabe7d665c | ||
|
|
7ce75bb8e7 | ||
|
|
b8e1c3cd60 | ||
|
|
4909944694 | ||
|
|
7258af09b4 | ||
|
|
7f2ce6c1a9 | ||
|
|
4835725e1c | ||
|
|
6a9e8de475 | ||
|
|
4639951acc | ||
|
|
d8de211aac | ||
|
|
87394bc60b | ||
|
|
beba30f65c | ||
|
|
2d58424d8f | ||
|
|
d1bbfa3791 | ||
|
|
aab17f460f | ||
|
|
461ef53322 | ||
|
|
2486bc90b2 | ||
|
|
1c5cbaf60b | ||
|
|
b8de447cfb | ||
|
|
8bfc2d40f9 | ||
|
|
c09fd71c94 | ||
|
|
f3b037553e | ||
|
|
d386d93166 | ||
|
|
5217742c71 | ||
|
|
c59ec95460 | ||
|
|
70f01583ec | ||
|
|
66d2f9eb63 | ||
|
|
4625bed8b8 | ||
|
|
f18fb332b5 | ||
|
|
8746a6f5ca | ||
|
|
702d47abb5 | ||
|
|
bf2a54717c | ||
|
|
9db779fb25 | ||
|
|
f55ddec708 | ||
|
|
6e942dd2d4 | ||
|
|
142310a17a | ||
|
|
7f99a89a95 | ||
|
|
bfce663b4b | ||
|
|
33c743643d | ||
|
|
858e0a389b | ||
|
|
9c3bb85674 | ||
|
|
f4930e8cd4 | ||
|
|
f21e7f3602 | ||
|
|
b56c35aca4 | ||
|
|
e6ac83196e | ||
|
|
7b6f74fc4a | ||
|
|
f754953fac | ||
|
|
2dd1143d9f | ||
|
|
e1ee3275d8 | ||
|
|
2549e406f3 | ||
|
|
d5024f1858 | ||
|
|
3623ac96db | ||
|
|
19834625f9 | ||
|
|
c8fc649b6b | ||
|
|
6b1fc1c413 | ||
|
|
6f086532ac | ||
|
|
dc66282225 | ||
|
|
e2c74c0bb3 | ||
|
|
e89d50eec1 | ||
|
|
8bebbc278d | ||
|
|
2e6d12c9c1 | ||
|
|
102a5e23ea | ||
|
|
582dc791db | ||
|
|
afb35d8c25 | ||
|
|
3a32ed5fe6 | ||
|
|
4e01697327 | ||
|
|
22032c1742 | ||
|
|
0a9bce317c | ||
|
|
40e44db597 | ||
|
|
4a447e44e1 | ||
|
|
b1f4654b3c | ||
|
|
9d350334c0 | ||
|
|
ac597f505f | ||
|
|
147d9ceb1f | ||
|
|
377804dfb9 | ||
|
|
0d26401961 | ||
|
|
889e226151 | ||
|
|
986d53f4e0 | ||
|
|
d13ba2968a | ||
|
|
1a192c8117 | ||
|
|
5e36d4801c | ||
|
|
2cf6411ca7 | ||
|
|
e99df129d5 | ||
|
|
d07c01bd17 | ||
|
|
d69673c524 | ||
|
|
4ee595cdaf | ||
|
|
6fcaf07df0 | ||
|
|
492d48d886 | ||
|
|
b6a56e768b | ||
|
|
3e2b5976a8 | ||
|
|
edd3f6cbd6 | ||
|
|
2e9347341b | ||
|
|
2061e38349 | ||
|
|
650dd57b6d | ||
|
|
82747f7dc0 | ||
|
|
04b26904a3 | ||
|
|
f123e4c72c |
No files matched your search
@@ -65,7 +65,7 @@ RUN \
|
||||
&& apt-get install -y cmake
|
||||
|
||||
# Dotnet
|
||||
RUN apt-get update && apt-get install -y dotnet7
|
||||
RUN apt-get update && apt-get install -y dotnet8
|
||||
|
||||
# Node
|
||||
ARG node_major=18
|
||||
@@ -90,6 +90,12 @@ RUN apt-get update && apt-get install -y libpcap0.8-dev
|
||||
# Databento (deps)
|
||||
RUN apt-get update && apt-get -y install libzstd-dev
|
||||
|
||||
# Docker in Docker
|
||||
RUN apt-get update && apt-get install -y \
|
||||
docker.io \
|
||||
docker-compose \
|
||||
docker-compose-v2
|
||||
|
||||
# Set up default executables (update-alternatives)
|
||||
RUN \
|
||||
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-$clang_major 100; \
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
{
|
||||
"dockerFile": "Dockerfile",
|
||||
// Creates local dir to mount to HOME in docker
|
||||
"initializeCommand": "mkdir -p ${localEnv:HOME}/.bts/devcontainer-mounts/${devcontainerId}",
|
||||
"hostRequirements": {
|
||||
"memory": "4gb",
|
||||
"storage": "6gb"
|
||||
"memory": "8gb",
|
||||
"storage": "10gb"
|
||||
},
|
||||
"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
|
||||
@@ -17,34 +12,20 @@
|
||||
// example, if 8000-8003 is taken, go with 9000-9003, then 10000-10003, etc.
|
||||
"runArgs": [
|
||||
"--shm-size",
|
||||
"2gb",
|
||||
"-p",
|
||||
"8000:8000",
|
||||
"-p",
|
||||
"8001:8001",
|
||||
"-p",
|
||||
"8002:8002",
|
||||
"-p",
|
||||
"8003:8003",
|
||||
"-p",
|
||||
"12345:12345"
|
||||
"8gb"
|
||||
],
|
||||
"mounts": [
|
||||
"source=${localEnv:HOME}/.bts/devcontainer-mounts/${devcontainerId},target=/home/bts,type=bind"
|
||||
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
|
||||
],
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"clangd.arguments": [
|
||||
"--compile-commands-dir=${workspaceFolder}/build/clang/debug"
|
||||
]
|
||||
},
|
||||
"extensions": [
|
||||
"eamodio.gitlens",
|
||||
"llvm-vs-code-extensions.vscode-clangd",
|
||||
"ms-vscode.cmake-tools",
|
||||
"ms-vscode.cpptools"
|
||||
"ms-vscode.cpptools",
|
||||
"xaver.clang-format"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
# This file defines the global default configuration for docker deployments.
|
||||
# It is tracked in source control. For temporary modifications, please create
|
||||
# a separate file called ".env.local" (it's in .gitignore, so won't be tracked).
|
||||
# If a variable is defined in both files, the value in .env.local will be used.
|
||||
#
|
||||
# About the deployment procedure:
|
||||
# We use Docker Compose to build images and launch containers for various services
|
||||
# that make up our software suite:
|
||||
# - sparkd
|
||||
# - TODO: pubsub
|
||||
# - TODO: md-publisher
|
||||
# - TODO: webapp (DSQA on the web)
|
||||
# - TODO: api-server (websocket based)
|
||||
# From inside a devcontainer, you can launch all services in separate containers
|
||||
# on your machine by running:
|
||||
#
|
||||
# tools/deploy.sh
|
||||
#
|
||||
# This script builds release versions for each service, packaged (generally) in
|
||||
# self-contained directories. Next, it runs a few "docker compose" commands to
|
||||
# build docker images loaded with the self-contained binaries from the previous
|
||||
# step, and start containers with those images to run each service. To view the
|
||||
# commands that deploy.sh runs internally (without actually running them), run:
|
||||
#
|
||||
# tools/deploy.sh --dry-run
|
||||
#
|
||||
# For additional information/help, run:
|
||||
#
|
||||
# tools/deploy.sh --help
|
||||
#
|
||||
|
||||
### Sparkd ###
|
||||
# The internal image for sparkd to use. For more info, view:
|
||||
# src/server/sparkd/Dockerfile
|
||||
SPARKD__IMAGE=sparkd
|
||||
# The config dir for this service to use
|
||||
SPARKD__CONFIG_DIR=src/server/sparkd/configurations/dev
|
||||
# Mount a specific volume or dir to "~/sparkdata/data" inside the docker.
|
||||
# Defaults to a volume named "sparkd_<config_port>" (e.g., sparkd_8000).
|
||||
# SPARKD__DATA_DIR=/home/bts/sparkdata/data
|
||||
SPARKD__SOURCE_DIR=install/sparkd-release-gcc
|
||||
SPARKD__RUNTIME_CONF_DIR=/home/ubuntu/8000/sparkdata/conf/
|
||||
SPARKD__CONFIG_PORT=8000
|
||||
SPARKD__QUICKBUS_PORT=8001
|
||||
SPARKD__SIM_FIX_PORT=8002
|
||||
SPARKD__SHM_SIZE=4gb
|
||||
|
||||
# TODO: add config for other services (pubsub, md-publisher, webapp, api-server, etc.)
|
||||
@@ -59,3 +59,5 @@ trace_data/
|
||||
.devcontainer/
|
||||
*.Generated.cs
|
||||
*.lst
|
||||
.env.local
|
||||
.secrets
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[submodule "externals"]
|
||||
path = externals
|
||||
url = git@bitbucket.org:bluetradesys/externals.git
|
||||
url = https://bitbucket.org/bluetradesys/externals.git
|
||||
[submodule "vcpkg"]
|
||||
path = vcpkg
|
||||
url = https://github.com/microsoft/vcpkg.git
|
||||
Vendored
+31
-8
@@ -1,6 +1,14 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python Debugger: Current File with Arguments",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"args": "${command:pickArgs}"
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
@@ -22,7 +30,7 @@
|
||||
"--config",
|
||||
"${workspaceFolder}/src/DataStreamSuite/DataStreamPublisher/Configurations/${input:dataStreamPublisherConfig}.json"
|
||||
],
|
||||
"cwd": "build/Debug/bin/net7.0",
|
||||
"cwd": "build/Debug/bin/net8.0",
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
@@ -55,6 +63,21 @@
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
},
|
||||
{
|
||||
"name": "spreader tests",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/sparkd-debug-clang/server/sparkd",
|
||||
"args": [
|
||||
"test",
|
||||
"[spreader]"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/sparkd-debug-clang/server",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
},
|
||||
{
|
||||
"name": "DataStreamQueryAnalyzer",
|
||||
"type": "coreclr",
|
||||
@@ -67,7 +90,7 @@
|
||||
"--config",
|
||||
"${workspaceFolder}/src/DataStreamSuite/DataStreamQueryAnalyzer/Configurations/DataStreamQueryAnalyzer.json"
|
||||
],
|
||||
"cwd": "build/Debug/bin/net7.0-windows",
|
||||
"cwd": "build/Debug/bin/net8.0-windows",
|
||||
"env": {
|
||||
"PYTHON_EXE": "${workspaceFolder}/.venv/Scripts/python.exe"
|
||||
},
|
||||
@@ -91,7 +114,7 @@
|
||||
"--config",
|
||||
"${workspaceFolder}/src/DataStreamSuite/PubSubServer/Configurations/${input:pubSubServerConfig}.json"
|
||||
],
|
||||
"cwd": "build/Debug/bin/net7.0",
|
||||
"cwd": "build/Debug/bin/net8.0",
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
@@ -103,7 +126,7 @@
|
||||
"windows": {
|
||||
"preLaunchTask": "Build Spark (Debug)",
|
||||
"program": "Spark.exe",
|
||||
"cwd": "build/Debug/bin/net7.0-windows",
|
||||
"cwd": "build/Debug/bin/net8.0-windows",
|
||||
},
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal"
|
||||
@@ -115,7 +138,7 @@
|
||||
"program": "",
|
||||
"windows": {
|
||||
"program": "Spark.exe",
|
||||
"cwd": "build/Debug/bin/net7.0-windows",
|
||||
"cwd": "build/Debug/bin/net8.0-windows",
|
||||
},
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal"
|
||||
@@ -132,7 +155,7 @@
|
||||
"--sparkd",
|
||||
"${input:sparkConfigServer}"
|
||||
],
|
||||
"cwd": "build/Debug/bin/net7.0-windows",
|
||||
"cwd": "build/Debug/bin/net8.0-windows",
|
||||
},
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal"
|
||||
@@ -145,7 +168,7 @@
|
||||
"windows": {
|
||||
"preLaunchTask": "Build Spark (Debug)",
|
||||
"program": "Spark.Console.exe",
|
||||
"cwd": "build/Debug/bin/net7.0",
|
||||
"cwd": "build/Debug/bin/net8.0",
|
||||
"args": [
|
||||
"test",
|
||||
"${input:sparkTest}"
|
||||
@@ -161,7 +184,7 @@
|
||||
"program": "",
|
||||
"windows": {
|
||||
"program": "Spark.Console.exe",
|
||||
"cwd": "build/Debug/bin/net7.0",
|
||||
"cwd": "build/Debug/bin/net8.0",
|
||||
"args": [
|
||||
"test",
|
||||
"${input:sparkTest}"
|
||||
|
||||
Vendored
+62
@@ -699,6 +699,24 @@
|
||||
"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",
|
||||
@@ -740,6 +758,45 @@
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"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",
|
||||
@@ -864,5 +921,10 @@
|
||||
"type": "promptString",
|
||||
"description": "Test to run",
|
||||
},
|
||||
{
|
||||
"id": "sparkdTest",
|
||||
"type": "promptString",
|
||||
"description": "Test to run",
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
project(spark)
|
||||
include(GNUInstallDirs)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/GitVersion.cmake)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
get_version_from_git()
|
||||
if(WIN32)
|
||||
set(platform "windows")
|
||||
else()
|
||||
|
||||
+9
-3
@@ -26,13 +26,14 @@
|
||||
"CMAKE_CXX_FLAGS_DEBUG": "$env{SPARK_CMAKE_CXX_FLAGS} /MTd",
|
||||
"CMAKE_CXX_FLAGS_RELEASE": "$env{SPARK_CMAKE_CXX_FLAGS} /MT",
|
||||
"CMAKE_CXX_STANDARD": "20",
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}",
|
||||
"CMAKE_SHARED_LINKER_FLAGS_DEBUG": "/DEBUG",
|
||||
"CMAKE_SHARED_LINKER_FLAGS_RELEASE": "/OPT:REF /OPT:ICF /LTCG",
|
||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
||||
"EXTERNALS_ROOT": "${sourceDir}/externals/windows",
|
||||
"CONFIG_DEFAULTS_PATH": "config/profiles/default/*.conf",
|
||||
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0",
|
||||
"DOTNET_WINDOWS_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0-windows",
|
||||
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net8.0",
|
||||
"DOTNET_WINDOWS_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net8.0-windows",
|
||||
"EXPORT_MD_CONSUMER": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
@@ -63,6 +64,11 @@
|
||||
"CMAKE_INSTALL_RPATH": ".",
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}",
|
||||
"CMAKE_EXE_LINKER_FLAGS": "-Wno-undef",
|
||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
||||
"DATABENTO_USE_EXTERNAL_JSON": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
},
|
||||
"EXTERNALS_ROOT": "${sourceDir}/externals/linux",
|
||||
"BUILD_SDK": {
|
||||
"type": "BOOL",
|
||||
@@ -181,7 +187,7 @@
|
||||
"CMAKE_CXX_FLAGS_RELEASE": "-fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++20 -fdiagnostics-color -fno-omit-frame-pointer -DNDEBUG -O3",
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}",
|
||||
"CONFIG_DEFAULTS_PATH": "config/profiles/default/*.conf",
|
||||
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0",
|
||||
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net8.0",
|
||||
"EXPORT_MD_CONSUMER": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
function(get_version_from_git)
|
||||
find_package(Git QUIET)
|
||||
if(NOT Git_FOUND)
|
||||
message(WARNING "Git not found")
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} describe --tags --always
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_TAG
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
RESULT_VARIABLE GIT_RESULT
|
||||
)
|
||||
|
||||
if(NOT GIT_RESULT EQUAL 0)
|
||||
message(WARNING "Failed to get git tag")
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_COMMIT_SHORT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
set(PROJECT_VERSION_BUILD_METADATA ${GIT_COMMIT_SHORT_HASH})
|
||||
set(PROJECT_VERSION_BUILD_METADATA ${GIT_COMMIT_SHORT_HASH} PARENT_SCOPE)
|
||||
|
||||
string(REGEX REPLACE "^v" "" CLEAN_TAG "${GIT_TAG}")
|
||||
if(CLEAN_TAG MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-.*)?$")
|
||||
set(PROJECT_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
set(PROJECT_VERSION_MAJOR ${CMAKE_MATCH_1} PARENT_SCOPE)
|
||||
set(PROJECT_VERSION_MINOR ${CMAKE_MATCH_2})
|
||||
set(PROJECT_VERSION_MINOR ${CMAKE_MATCH_2} PARENT_SCOPE)
|
||||
set(PROJECT_VERSION_PATCH ${CMAKE_MATCH_3})
|
||||
set(PROJECT_VERSION_PATCH ${CMAKE_MATCH_3} PARENT_SCOPE)
|
||||
|
||||
set(PROJECT_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
|
||||
set(PROJECT_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE)
|
||||
set(PROJECT_VERSION_FULL "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}+${GIT_COMMIT_SHORT_HASH}")
|
||||
set(PROJECT_VERSION_FULL "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}+${GIT_COMMIT_SHORT_HASH}" PARENT_SCOPE)
|
||||
else()
|
||||
message(WARNING "Tag '${CLEAN_TAG}' does not match semver format")
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef BTS_SPARK_VERSION_H
|
||||
#define BTS_SPARK_VERSION_H
|
||||
|
||||
#define BTS_SPARK_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
||||
#define BTS_SPARK_VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||
#define BTS_SPARK_VERSION_PATCH @PROJECT_VERSION_PATCH@
|
||||
#define BTS_SPARK_VERSION_BUILD_METADATA @PROJECT_VERSION_BUILD_METADATA@
|
||||
#define BTS_SPARK_VERSION "@PROJECT_VERSION@"
|
||||
#define BTS_SPARK_VERSION_FULL "@PROJECT_VERSION_FULL@"
|
||||
|
||||
#endif
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
services:
|
||||
sparkd:
|
||||
volumes:
|
||||
- ${SPARKD__DATA_DIR:-sparkd_data}:/home/bts/sparkdata/data
|
||||
build:
|
||||
dockerfile: src/server/sparkd/Dockerfile
|
||||
args:
|
||||
- IMAGE=${SPARKD__IMAGE}
|
||||
- SOURCE_DIR=${SPARKD__SOURCE_DIR}
|
||||
- CONFIG_DIR=${SPARKD__CONFIG_DIR}
|
||||
shm_size: "${SPARKD__SHM_SIZE:-512mb}"
|
||||
command: >
|
||||
sh -c "
|
||||
echo bts | sudo -S chown -R bts:bts /home/bts/sparkdata/data
|
||||
if [ -e "/home/bts/sparkdata/data/logging.sparkd/log" ]; then
|
||||
mv "/home/bts/sparkdata/data/logging.sparkd/log" "/home/bts/sparkdata/data/logging.sparkd/log_$(date '+%Y%m%d-%H%M')"
|
||||
fi
|
||||
if [ -e "/home/bts/sparkdata/conf/.env.local" ]; then
|
||||
echo 'Sourcing /home/bts/sparkdata/conf/.env.local'
|
||||
set -a
|
||||
. /home/bts/sparkdata/conf/.env.local
|
||||
set +a
|
||||
fi
|
||||
./sparkd run"
|
||||
env_file:
|
||||
- .env
|
||||
- ${SPARKD__CONFIG_DIR}/.env
|
||||
ports:
|
||||
- "${SPARKD__CONFIG_PORT}:${SPARKD__CONFIG_PORT}"
|
||||
- "${SPARKD__QUICKBUS_PORT}:${SPARKD__QUICKBUS_PORT}"
|
||||
- "${SPARKD__SIM_FIX_PORT}:${SPARKD__SIM_FIX_PORT}"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: "${SPARKD__MEMORY:-4gb}"
|
||||
|
||||
sparkd-symbols:
|
||||
volumes:
|
||||
- ${SPARKD__DATA_DIR:-sparkd_data}:/home/bts/sparkdata/data
|
||||
- ${SPARKD__RUNTIME_CONF_DIR:-sparkd_conf}:/home/bts/sparkdata/conf
|
||||
build:
|
||||
dockerfile: src/server/sparkd/Dockerfile
|
||||
args:
|
||||
- IMAGE=${SPARKD__IMAGE}
|
||||
- SOURCE_DIR=${SPARKD__SOURCE_DIR}
|
||||
- CONFIG_DIR=${SPARKD__CONFIG_DIR}
|
||||
shm_size: "${SPARKD__SHM_SIZE:-512mb}"
|
||||
command: >
|
||||
sh -c "
|
||||
echo bts | sudo -S chown -R bts:bts /home/bts/sparkdata
|
||||
if [ -e "/home/bts/sparkdata/data/logging.sparkd/log" ]; then
|
||||
mv "/home/bts/sparkdata/data/logging.sparkd/log" "/home/bts/sparkdata/data/logging.sparkd/log_$(date '+%Y%m%d-%H%M')"
|
||||
fi
|
||||
ls -alh /home/bts/sparkdata/conf/
|
||||
cat /home/bts/sparkdata/conf/.env.local
|
||||
if [ -e "/home/bts/sparkdata/conf/.env.local" ]; then
|
||||
echo 'Sourcing /home/bts/sparkdata/conf/.env.local'
|
||||
set -a
|
||||
. /home/bts/sparkdata/conf/.env.local
|
||||
set +a
|
||||
fi
|
||||
./sparkd run"
|
||||
env_file:
|
||||
- .env
|
||||
- ${SPARKD__CONFIG_DIR}/.env
|
||||
ports:
|
||||
- "${SPARKD__CONFIG_PORT}:${SPARKD__CONFIG_PORT}"
|
||||
- "${SPARKD__QUICKBUS_PORT}:${SPARKD__QUICKBUS_PORT}"
|
||||
- "${SPARKD__SIM_FIX_PORT}:${SPARKD__SIM_FIX_PORT}"
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: any
|
||||
delay: 3s
|
||||
resources:
|
||||
limits:
|
||||
memory: "${SPARKD__MEMORY:-4gb}"
|
||||
|
||||
sparkd-tail:
|
||||
image: bluetradingsystems/linux_dotnet7
|
||||
volumes:
|
||||
- type: volume
|
||||
read_only: true
|
||||
source: sparkd_data
|
||||
target: /home/bts/sparkdata/data/logging.sparkd
|
||||
volume:
|
||||
subpath: logging.sparkd
|
||||
command: ["tail", "-f", "/home/bts/sparkdata/data/logging.sparkd/log"]
|
||||
|
||||
sparkd-ping-test:
|
||||
depends_on:
|
||||
- sparkd
|
||||
profiles:
|
||||
- ping-test
|
||||
build:
|
||||
context: .
|
||||
dockerfile_inline: |
|
||||
FROM bluetradingsystems/linux_dotnet7
|
||||
USER "root"
|
||||
RUN apt-get update && apt-get -y install iputils-ping
|
||||
command: [ "ping", "sparkd" ]
|
||||
|
||||
volumes:
|
||||
sparkd_data:
|
||||
name: "sparkd_${SPARKD__CONFIG_PORT:-8000}"
|
||||
@@ -0,0 +1,38 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get install -y pkg-config
|
||||
RUN apt-get update && apt-get install -y locales
|
||||
RUN apt-get update && apt-get install -y ca-certificates
|
||||
RUN apt-get update && apt-get install -y libssh2-1-dev
|
||||
RUN apt-get update && apt-get install -y software-properties-common
|
||||
RUN apt-get update && apt-get install -y sudo
|
||||
|
||||
# GCC
|
||||
ARG gcc_major=13
|
||||
RUN add-apt-repository ppa:ubuntu-toolchain-r/test \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y g++-$gcc_major
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y dotnet7
|
||||
|
||||
RUN locale-gen en_US.UTF-8
|
||||
|
||||
ARG docker_user="bts"
|
||||
ARG docker_uid="1000"
|
||||
ARG docker_pass="bts"
|
||||
|
||||
RUN \
|
||||
groupadd -g "$docker_uid" "$docker_user" \
|
||||
&& useradd \
|
||||
-m \
|
||||
-u "$docker_uid" \
|
||||
-g "$docker_uid" \
|
||||
-G sudo \
|
||||
--password "$(openssl passwd -1 "$docker_pass")" \
|
||||
"$docker_user"
|
||||
|
||||
USER bts
|
||||
WORKDIR /home/bts
|
||||
+1
-1
Submodule docker/linux updated: 56c61231f3...6537812262.
+1
-1
Submodule externals updated: 8069cc893f...4f6734703c.
@@ -1,11 +1,20 @@
|
||||
astroid==2.7.3
|
||||
autopep8==1.5.7
|
||||
blinker==1.9.0
|
||||
boto3==1.37.9
|
||||
botocore==1.37.9
|
||||
certifi==2023.5.7
|
||||
charset-normalizer==3.1.0
|
||||
click==8.1.8
|
||||
colorama==0.4.4
|
||||
Flask==3.1.0
|
||||
idna==3.4
|
||||
isort==5.9.3
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.6
|
||||
jmespath==1.0.1
|
||||
lazy-object-proxy==1.6.0
|
||||
MarkupSafe==3.0.2
|
||||
mccabe==0.6.1
|
||||
platformdirs==2.3.0
|
||||
protobuf==4.21.12
|
||||
@@ -14,9 +23,14 @@ pycodestyle==2.7.0
|
||||
pycparser==2.20
|
||||
pylint==2.10.2
|
||||
PyMeta3==0.5.1
|
||||
python-dateutil==2.9.0.post0
|
||||
python-dotenv==1.0.1
|
||||
requests==2.31.0
|
||||
s3transfer==0.11.4
|
||||
six==1.17.0
|
||||
termcolor==1.1.0
|
||||
toml==0.10.2
|
||||
urllib3==2.0.3
|
||||
Werkzeug==3.1.3
|
||||
wrapt==1.12.1
|
||||
yapf==0.31.0
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.24.3" />
|
||||
<PackageReference Include="YaccLexTools" Version="1.2.0" />
|
||||
<PackageReference Include="YaccLexTools" Version="1.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -24,8 +24,6 @@ namespace QUT.GplexBuffers
|
||||
public BufferException(string message) : base(message) { }
|
||||
public BufferException(string message, Exception innerException)
|
||||
: base(message, innerException) { }
|
||||
protected BufferException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context) { }
|
||||
}
|
||||
|
||||
internal abstract class ScanBuff
|
||||
@@ -60,12 +58,10 @@ namespace QUT.GplexBuffers
|
||||
return new BuildBuffer(source);
|
||||
}
|
||||
|
||||
#if (!BYTEMODE)
|
||||
public static ScanBuff GetBuffer(Stream source, int fallbackCodePage)
|
||||
{
|
||||
return new BuildBuffer(source, fallbackCodePage);
|
||||
}
|
||||
#endif // !BYTEMODE
|
||||
#endif // !NOFILES
|
||||
}
|
||||
|
||||
@@ -353,17 +349,15 @@ namespace QUT.GplexBuffers
|
||||
{
|
||||
FileStream fStrm = (stream as FileStream);
|
||||
if (fStrm != null) FileName = fStrm.Name;
|
||||
NextBlk = BlockReaderFactory.Raw(stream);
|
||||
NextBlk = BlockReaderFactory.Get(stream);
|
||||
}
|
||||
|
||||
#if (!BYTEMODE)
|
||||
public BuildBuffer(Stream stream, int fallbackCodePage)
|
||||
{
|
||||
FileStream fStrm = (stream as FileStream);
|
||||
if (fStrm != null) FileName = fStrm.Name;
|
||||
NextBlk = BlockReaderFactory.Get(stream, fallbackCodePage);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Marks a conservative lower bound for the buffer,
|
||||
@@ -434,43 +428,19 @@ namespace QUT.GplexBuffers
|
||||
//
|
||||
internal static class BlockReaderFactory
|
||||
{
|
||||
public static BlockReader Raw(Stream stream)
|
||||
{
|
||||
return delegate(char[] block, int index, int number)
|
||||
{
|
||||
byte[] b = new byte[number];
|
||||
int count = stream.Read(b, 0, number);
|
||||
int i = 0;
|
||||
int j = index;
|
||||
for (; i < count; i++, j++)
|
||||
block[j] = (char)b[i];
|
||||
return count;
|
||||
};
|
||||
}
|
||||
|
||||
#if (!BYTEMODE)
|
||||
public static BlockReader Get(Stream stream, int fallbackCodePage)
|
||||
public static BlockReader Get(Stream stream, int fallbackCodePage = -2)
|
||||
{
|
||||
Encoding encoding;
|
||||
int preamble = Preamble(stream);
|
||||
|
||||
if (preamble != 0) // There is a valid BOM here!
|
||||
encoding = Encoding.GetEncoding(preamble);
|
||||
else if (fallbackCodePage == -1) // Fallback is "raw" bytes
|
||||
return Raw(stream);
|
||||
else if (fallbackCodePage != -2) // Anything but "guess"
|
||||
else if (fallbackCodePage >= 0) // Anything but "guess"
|
||||
encoding = Encoding.GetEncoding(fallbackCodePage);
|
||||
else // This is the "guess" option
|
||||
{
|
||||
int guess = new Guesser(stream).GuessCodePage();
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (guess == -1) // ==> this is a 7-bit file
|
||||
encoding = Encoding.ASCII;
|
||||
else if (guess == 65001)
|
||||
encoding = Encoding.UTF8;
|
||||
else // ==> use the machine default
|
||||
encoding = Encoding.Default;
|
||||
}
|
||||
encoding = GuessEncoding(stream);
|
||||
|
||||
StreamReader reader = new StreamReader(stream, encoding);
|
||||
return reader.Read;
|
||||
}
|
||||
@@ -488,6 +458,14 @@ namespace QUT.GplexBuffers
|
||||
int b2 = stream.ReadByte();
|
||||
if (b0 == 0xef && b1 == 0xbb && b2 == 0xbf)
|
||||
return 65001; // UTF8
|
||||
|
||||
int b3 = stream.ReadByte();
|
||||
// Check for UTF-32 BOM (Byte Order Mark)
|
||||
if (b0 == 0x00 && b1 == 0x00 && b2 == 0xFE && b3 == 0xFF)
|
||||
return 12001; // UTF-32BE
|
||||
if (b0 == 0xFF && b1 == 0xFE && b2 == 0x00 && b3 == 0x00)
|
||||
return 12000; // UTF-32LE
|
||||
|
||||
//
|
||||
// There is no unicode preamble, so we
|
||||
// return denoter for the machine default.
|
||||
@@ -495,7 +473,150 @@ namespace QUT.GplexBuffers
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return 0;
|
||||
}
|
||||
#endif // !BYTEMODE
|
||||
|
||||
private static Encoding GuessEncoding(Stream stream)
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (IsUtf16(stream, out Encoding encoding))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return encoding;
|
||||
}
|
||||
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (IsUtf8(stream))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return Encoding.UTF8;
|
||||
}
|
||||
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (IsAscii(stream))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return Encoding.ASCII;
|
||||
}
|
||||
|
||||
#if !NET462_OR_GREATER
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (IsLatin1(stream))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return Encoding.Latin1;
|
||||
}
|
||||
#endif
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return Encoding.Default;
|
||||
}
|
||||
|
||||
private static bool IsUtf8(Stream stream)
|
||||
{
|
||||
int b;
|
||||
while ((b = stream.ReadByte()) != -1)
|
||||
{
|
||||
if ((b & 0x80) == 0) // 0xxxxxxx
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if ((b & 0xE0) == 0xC0) // 110xxxxx 10xxxxxx
|
||||
{
|
||||
if (!IsValidUtf8ContinuationByte(stream)) return false;
|
||||
}
|
||||
else if ((b & 0xF0) == 0xE0) // 1110xxxx 10xxxxxx 10xxxxxx
|
||||
{
|
||||
if (!IsValidUtf8ContinuationByte(stream) || !IsValidUtf8ContinuationByte(stream)) return false;
|
||||
}
|
||||
else if ((b & 0xF8) == 0xF0) // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
{
|
||||
if (!IsValidUtf8ContinuationByte(stream) || !IsValidUtf8ContinuationByte(stream) || !IsValidUtf8ContinuationByte(stream)) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsValidUtf8ContinuationByte(Stream stream)
|
||||
{
|
||||
int b = stream.ReadByte();
|
||||
return b != -1 && (b & 0xC0) == 0x80; // 10xxxxxx
|
||||
}
|
||||
|
||||
private static bool IsUtf16(Stream stream, out Encoding encoding)
|
||||
{
|
||||
encoding = Encoding.Default;
|
||||
|
||||
if (stream.Length >= 2)
|
||||
{
|
||||
// Read the first two bytes
|
||||
int b0 = stream.ReadByte();
|
||||
int b1 = stream.ReadByte();
|
||||
|
||||
// Check for UTF-16 BOM (Byte Order Mark)
|
||||
if (b0 == 0xFF && b1 == 0xFE)
|
||||
{
|
||||
encoding = Encoding.Unicode;
|
||||
return true; // UTF-16LE
|
||||
}
|
||||
if (b0 == 0xFE && b1 == 0xFF)
|
||||
{
|
||||
encoding = Encoding.BigEndianUnicode;
|
||||
return true; // UTF-16BE
|
||||
}
|
||||
|
||||
// Reset the stream position
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Check for common UTF-16 patterns
|
||||
byte[] buffer = new byte[2];
|
||||
while (stream.Read(buffer, 0, 2) == 2)
|
||||
{
|
||||
if (buffer[0] == 0x00 && buffer[1] != 0x00)
|
||||
{
|
||||
encoding = Encoding.BigEndianUnicode;
|
||||
return true; // Likely UTF-16BE
|
||||
}
|
||||
if (buffer[0] != 0x00 && buffer[1] == 0x00)
|
||||
{
|
||||
encoding = Encoding.Unicode;
|
||||
return true; // Likely UTF-16LE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsAscii(Stream stream)
|
||||
{
|
||||
int b;
|
||||
while ((b = stream.ReadByte()) != -1)
|
||||
{
|
||||
// Latin1 includes byte values from 0x00 to 0xFF
|
||||
if (b > 0x7F)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsLatin1(Stream stream)
|
||||
{
|
||||
int b;
|
||||
while ((b = stream.ReadByte()) != -1)
|
||||
{
|
||||
// Latin1 includes byte values from 0x00 to 0xFF
|
||||
if (b > 0xFF)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
#endif // !NOFILES
|
||||
#endregion Buffer classes
|
||||
@@ -540,180 +661,6 @@ namespace QUT.GplexBuffers
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#region guesser
|
||||
#if (!BYTEMODE)
|
||||
// ==============================================================
|
||||
// ============ Encoding Guesser =============
|
||||
// ==============================================================
|
||||
|
||||
/// <summary>
|
||||
/// This class provides a simple finite state automaton that
|
||||
/// scans the file looking for (1) valid UTF-8 byte patterns,
|
||||
/// (2) bytes >= 0x80 which are not part of a UTF-8 sequence.
|
||||
/// The method then guesses whether it is UTF-8 or maybe some
|
||||
/// local machine default encoding. This works well for the
|
||||
/// various Latin encodings.
|
||||
/// </summary>
|
||||
internal class Guesser
|
||||
{
|
||||
ScanBuff buffer;
|
||||
|
||||
public int GuessCodePage() { return Scan(); }
|
||||
|
||||
const int maxAccept = 10;
|
||||
const int initial = 0;
|
||||
const int eofNum = 0;
|
||||
const int goStart = -1;
|
||||
const int INITIAL = 0;
|
||||
const int EndToken = 0;
|
||||
|
||||
#region user code
|
||||
/*
|
||||
* Reads the bytes of a file to determine if it is
|
||||
* UTF-8 or a single-byte code page file.
|
||||
*/
|
||||
public long utfX;
|
||||
public long uppr;
|
||||
#endregion user code
|
||||
|
||||
int state;
|
||||
int currentStart = startState[0];
|
||||
int code;
|
||||
|
||||
#region ScannerTables
|
||||
static int[] startState = new int[] { 11, 0 };
|
||||
|
||||
#region CharacterMap
|
||||
static sbyte[] map = new sbyte[256] {
|
||||
/* '\0' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* '\x10' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* '\x20' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* '0' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* '@' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 'P' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* '`' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 'p' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* '\x80' */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
/* '\x90' */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
/* '\xA0' */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
/* '\xB0' */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
/* '\xC0' */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
/* '\xD0' */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
/* '\xE0' */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
/* '\xF0' */ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5 };
|
||||
#endregion
|
||||
|
||||
static sbyte[][] nextState = new sbyte[][] {
|
||||
new sbyte[] {0, 0, 0, 0, 0, 0},
|
||||
new sbyte[] {-1, -1, 10, -1, -1, -1},
|
||||
new sbyte[] {-1, -1, -1, -1, -1, -1},
|
||||
new sbyte[] {-1, -1, 8, -1, -1, -1},
|
||||
new sbyte[] {-1, -1, 5, -1, -1, -1},
|
||||
new sbyte[] {-1, -1, 6, -1, -1, -1},
|
||||
new sbyte[] {-1, -1, 7, -1, -1, -1},
|
||||
null,
|
||||
new sbyte[] {-1, -1, 9, -1, -1, -1},
|
||||
null,
|
||||
null,
|
||||
new sbyte[] {-1, 1, 2, 3, 4, 2}
|
||||
};
|
||||
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
|
||||
// Reason for suppression: cannot have self-reference in array initializer.
|
||||
static Guesser()
|
||||
{
|
||||
nextState[7] = nextState[2];
|
||||
nextState[9] = nextState[2];
|
||||
nextState[10] = nextState[2];
|
||||
}
|
||||
|
||||
int NextState()
|
||||
{
|
||||
if (code == ScanBuff.EndOfFile)
|
||||
return eofNum;
|
||||
else
|
||||
return nextState[state][map[code]];
|
||||
}
|
||||
#endregion
|
||||
|
||||
public Guesser(System.IO.Stream file) { SetSource(file); }
|
||||
|
||||
public void SetSource(System.IO.Stream source)
|
||||
{
|
||||
this.buffer = new BuildBuffer(source);
|
||||
code = buffer.Read();
|
||||
}
|
||||
|
||||
int Scan()
|
||||
{
|
||||
for (; ; )
|
||||
{
|
||||
int next;
|
||||
state = currentStart;
|
||||
while ((next = NextState()) == goStart)
|
||||
code = buffer.Read();
|
||||
|
||||
state = next;
|
||||
code = buffer.Read();
|
||||
|
||||
while ((next = NextState()) > eofNum)
|
||||
{
|
||||
state = next;
|
||||
code = buffer.Read();
|
||||
}
|
||||
if (state <= maxAccept)
|
||||
{
|
||||
#region ActionSwitch
|
||||
#pragma warning disable 162
|
||||
switch (state)
|
||||
{
|
||||
case eofNum:
|
||||
switch (currentStart)
|
||||
{
|
||||
case 11:
|
||||
if (utfX == 0 && uppr == 0) return -1; /* raw ascii */
|
||||
else if (uppr * 10 > utfX) return 0; /* default code page */
|
||||
else return 65001; /* UTF-8 encoding */
|
||||
break;
|
||||
}
|
||||
return EndToken;
|
||||
case 1: // Recognized '{Upper128}', Shortest string "\xC0"
|
||||
case 2: // Recognized '{Upper128}', Shortest string "\x80"
|
||||
case 3: // Recognized '{Upper128}', Shortest string "\xE0"
|
||||
case 4: // Recognized '{Upper128}', Shortest string "\xF0"
|
||||
uppr++;
|
||||
break;
|
||||
case 5: // Recognized '{Utf8pfx4}{Utf8cont}', Shortest string "\xF0\x80"
|
||||
uppr += 2;
|
||||
break;
|
||||
case 6: // Recognized '{Utf8pfx4}{Utf8cont}{2}', Shortest string "\xF0\x80\x80"
|
||||
uppr += 3;
|
||||
break;
|
||||
case 7: // Recognized '{Utf8pfx4}{Utf8cont}{3}', Shortest string "\xF0\x80\x80\x80"
|
||||
utfX += 3;
|
||||
break;
|
||||
case 8: // Recognized '{Utf8pfx3}{Utf8cont}', Shortest string "\xE0\x80"
|
||||
uppr += 2;
|
||||
break;
|
||||
case 9: // Recognized '{Utf8pfx3}{Utf8cont}{2}', Shortest string "\xE0\x80\x80"
|
||||
utfX += 2;
|
||||
break;
|
||||
case 10: // Recognized '{Utf8pfx2}{Utf8cont}', Shortest string "\xC0\x80"
|
||||
utfX++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#pragma warning restore 162
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end class Guesser
|
||||
|
||||
#endif // !BYTEMODE
|
||||
#endregion
|
||||
#endif // !NOFILES
|
||||
|
||||
// End of code copied from embedded resource
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -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", dt.DayOfWeek.ToString());
|
||||
rv.setValue($"{inp_col}_day_of_week", 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}_min", dt.Minute);
|
||||
rv.setValue($"{inp_col}_minute", 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);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>disable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -36,6 +36,8 @@ 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
|
||||
@@ -113,5 +115,9 @@ 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
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
|
||||
@@ -6,12 +6,18 @@ namespace BCG
|
||||
public interface IPublisher
|
||||
{
|
||||
void publish( IPubItem pi );
|
||||
#pragma warning disable SYSLIB0011 // Type or member is obsolete
|
||||
void publish( IPubItem pi, IFormatter f );
|
||||
#pragma warning restore SYSLIB0011 // Type or member is obsolete
|
||||
void publish( IPubItem[] pi );
|
||||
#pragma warning disable SYSLIB0011 // Type or member is obsolete
|
||||
void publish( IPubItem[] pi, IFormatter f );
|
||||
#pragma warning restore SYSLIB0011 // Type or member is obsolete
|
||||
}
|
||||
|
||||
#pragma warning disable SYSLIB0011 // Type or member is obsolete
|
||||
public interface IPublisher<T> where T : IFormatter
|
||||
#pragma warning restore SYSLIB0011 // Type or member is obsolete
|
||||
{
|
||||
void publish (IPubItem pi);
|
||||
void publish (IPubItem[] pi);
|
||||
|
||||
@@ -5,23 +5,21 @@ using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
namespace BCG
|
||||
{
|
||||
#pragma warning disable SYSLIB0011 // Type or member is obsolete
|
||||
public class GenericFormatter<F> : IGenericFormatter where F : IFormatter, new ()
|
||||
{
|
||||
IFormatter myFormatter = new F ();
|
||||
|
||||
public T Deserialize<T> (Stream serializationStream)
|
||||
{
|
||||
#pragma warning disable
|
||||
return (T) myFormatter.Deserialize (serializationStream);
|
||||
#pragma warning restore SYSLIB0011
|
||||
}
|
||||
public void Serialize<T> (Stream serializationStream, T graph)
|
||||
{
|
||||
#pragma warning disable
|
||||
myFormatter.Serialize (serializationStream, graph);
|
||||
#pragma warning restore SYSLIB0011
|
||||
}
|
||||
}
|
||||
|
||||
public class GenericBinaryFormatter : GenericFormatter<BinaryFormatter> { }
|
||||
#pragma warning restore SYSLIB0011 // Type or member is obsolete
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace bts.SilverPine {
|
||||
using Spark.Api.MarketData;
|
||||
|
||||
using SecurityKey = System.UInt32;
|
||||
using System.Data.Entity.Infrastructure;
|
||||
|
||||
public class FakeBBOStream : ViewStream, IWorker {
|
||||
private string _symbol;
|
||||
@@ -105,10 +104,9 @@ namespace bts.SilverPine {
|
||||
public class BBOStream : ViewStream, IWorker {
|
||||
private readonly TimeSpan _poll_period = TimeSpan.FromMilliseconds(500);
|
||||
private DateTime _last_poll_time = DateTime.MinValue;
|
||||
private ViewStreamDatabase _vsd;
|
||||
private int _id = 0;
|
||||
IReadOnlyDictionary<SecurityKey, BookTuple> _cached_state = null;
|
||||
MarketDataClient _md_client;
|
||||
private ViewStreamDatabase? _vsd;
|
||||
IReadOnlyDictionary<SecurityKey, BookTuple>? _cached_state = null;
|
||||
MarketDataClient? _md_client;
|
||||
internal static IBlueTableSchema _bbo_schema = BlueDataUtility.form_schema("bbo",
|
||||
Tuple.Create("id", TypeCode.Int32),
|
||||
Tuple.Create("sec_key", TypeCode.UInt32),
|
||||
@@ -132,9 +130,8 @@ namespace bts.SilverPine {
|
||||
}
|
||||
|
||||
protected override void myOnFirstSubscriber() {
|
||||
_vsd.addWorker(this);
|
||||
_md_client.books().Subscribe(_on_books);
|
||||
|
||||
_vsd?.addWorker(this);
|
||||
_md_client?.books().Subscribe(_on_books);
|
||||
}
|
||||
|
||||
private void _log_updates(TableUpdate<BookTuple, uint> update) {
|
||||
@@ -156,7 +153,7 @@ namespace bts.SilverPine {
|
||||
}
|
||||
|
||||
protected override void myOnLastSubscriber() {
|
||||
_vsd.removeWorker(this);
|
||||
_vsd?.removeWorker(this);
|
||||
}
|
||||
|
||||
private void _scan_bbo() {
|
||||
@@ -174,18 +171,18 @@ namespace bts.SilverPine {
|
||||
bt.book.asks[0].price, bt.book.asks[0].size);
|
||||
new_state.addRow(r);
|
||||
}
|
||||
DataPacket dp = null;
|
||||
if (myLastValue == null) {
|
||||
dp = new DataPacket(new_state, new_state, new BlueTable(_bbo_schema));
|
||||
} else {
|
||||
DataPacket old_dp = myLastValue as DataPacket;
|
||||
IBlueRowCollection added_rows, del_rows;
|
||||
BlueDataUtility.compute_differences_fast(new_state, old_dp.newRows, out del_rows, out added_rows);
|
||||
dp = new DataPacket(new_state, added_rows, del_rows);
|
||||
}
|
||||
var dp = accumulate(myLastValue as DataPacket, new_state);
|
||||
if (dp.addedRows.Count == 0 && dp.deletedRows.Count == 0)
|
||||
return;
|
||||
myFireEvent(dp);
|
||||
|
||||
static DataPacket accumulate(DataPacket? old_dp, BlueTable new_state) {
|
||||
if (old_dp is null) {
|
||||
return new(new_state, new_state, new BlueTable(_bbo_schema));
|
||||
}
|
||||
BlueDataUtility.compute_differences_fast(new_state, old_dp.newRows, out var del_rows, out var added_rows);
|
||||
return new(new_state, added_rows, del_rows);
|
||||
}
|
||||
}
|
||||
|
||||
public void doWork() {
|
||||
@@ -195,4 +192,4 @@ namespace bts.SilverPine {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,6 +156,14 @@ namespace bts.SilverPine {
|
||||
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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ namespace BCG {
|
||||
private DateTime _last_poll_time = DateTime.MinValue;
|
||||
private ViewStreamDatabase _vsd;
|
||||
private int _id = 0;
|
||||
IReadOnlyDictionary<SecurityKey, BookTuple> _cached_state = null;
|
||||
IReadOnlyDictionary<SecurityKey, BookTuple>? _cached_state = null;
|
||||
MarketDataClient _md_client;
|
||||
readonly Stopwatch _timer = new();
|
||||
private readonly IBlueTableSchema _bbo_schema = BlueDataUtility.form_schema("books",
|
||||
@@ -73,14 +73,8 @@ namespace BCG {
|
||||
|
||||
private Dictionary<SecurityKey, Dictionary<int, int>> _id_map = new Dictionary<SecurityKey, Dictionary<int, int>>();
|
||||
private int _get_id(SecurityKey skey, int depth) {
|
||||
Dictionary<int, int> d_map = null;
|
||||
if (!_id_map.TryGetValue(skey, out d_map)) {
|
||||
d_map = new Dictionary<int, int>();
|
||||
d_map.Add(depth, _id++);
|
||||
_id_map.Add(skey, d_map);
|
||||
}
|
||||
int rv = 0;
|
||||
if (!d_map.TryGetValue(depth, out rv)) {
|
||||
var d_map = _id_map.GetOrAdd(skey, _ => new() { {depth, _id++} });
|
||||
if (!d_map.TryGetValue(depth, out int rv)) {
|
||||
rv = _id++;
|
||||
d_map.Add(depth, rv);
|
||||
}
|
||||
|
||||
@@ -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,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -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.OKX; Venue.GLBX; Venue.ICE; Venue.TAIFEX; Venue.TWSE; Venue.SIM; |]
|
||||
|
||||
let securityTypes = [| SecurityType.SPOT; SecurityType.FUTURE; SecurityType.OPTION; SecurityType.SPREAD; |]
|
||||
|
||||
@@ -74,6 +74,11 @@ type TaifexExchangeField =
|
||||
| Origin of CustomerOrFirm
|
||||
| ClearingAccount of string
|
||||
|
||||
type TwseExchangeField =
|
||||
| CTI of CTICode
|
||||
| Origin of CustomerOrFirm
|
||||
| ClearingAccount of string
|
||||
|
||||
type OkxExchangeField =
|
||||
| CTI of CTICode
|
||||
| Origin of CustomerOrFirm
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>1.0.0</Version>
|
||||
<AssemblyVersion>1.0.0</AssemblyVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net7.0</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
<Nullable>enable</Nullable>
|
||||
<RuntimeIdentifiers>win10-x64;ubuntu.16.04-x64</RuntimeIdentifiers>
|
||||
<Authors>Blue Trading Systems</Authors>
|
||||
<Company>Blue Trading Systems</Company>
|
||||
<Product>Spark</Product>
|
||||
|
||||
@@ -8,6 +8,7 @@ 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).
|
||||
@@ -114,11 +115,53 @@ 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();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
namespace mbts.spark {
|
||||
using bts.api;
|
||||
|
||||
using Spark.Api.Data;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -23,14 +25,14 @@ namespace mbts.spark {
|
||||
foreach (var kv in driver_map.get_price_map()) {
|
||||
var exp = kv.Key;
|
||||
var dmd = kv.Value;
|
||||
IEnumerable<SecurityMD> exp_secs = sc_data.get_securities(
|
||||
(sec_md) => {
|
||||
var s = sec_md.security;
|
||||
return s.security_type() == SecurityType.Option &&
|
||||
s.Symbol == driver_map.option_symbol &&
|
||||
s.earliest_expiration() == exp.expiration;
|
||||
}
|
||||
);
|
||||
IEnumerable<SecurityMD> exp_secs = sc_data.get_securities((sec_md) => {
|
||||
var s = sec_md.security;
|
||||
return s.security_type() == SecurityType.Option &&
|
||||
s.Symbol == driver_map.option_symbol &&
|
||||
s.earliest_expiration() == (Security.is_perpetual_exp(exp.expiration)
|
||||
? ExpirationDate.Perpetual
|
||||
: ExpirationDate.FixedDate(exp.expiration));
|
||||
});
|
||||
foreach (var sec_md in exp_secs) {
|
||||
rv[sec_md.security.Id] = kv.Value;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace mbts.spark {
|
||||
case SecurityType.Future:
|
||||
case SecurityType.Option:
|
||||
rv.GetOrAdd((sec.Exchange, sec.security_type(), sec.Symbol), _ => new())
|
||||
.Add(sec.earliest_expiration());
|
||||
.Add(sec.earliest_expiration().Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,5 +281,16 @@ 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 inout
|
||||
// 2. vs input
|
||||
// 3. exp params(drivers, interst rate etc)
|
||||
// 4. driver prices
|
||||
// 5. vol time
|
||||
|
||||
@@ -133,17 +133,31 @@ namespace bts {
|
||||
|
||||
// set 'unit quantity' for each sec
|
||||
foreach (var smd in exch_secs.Values) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
return (priceDisplay, qtyDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() =>
|
||||
|
||||
@@ -11,8 +11,9 @@ namespace Spark.Trading {
|
||||
|
||||
using mbts.analytics;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
|
||||
using Spark.Api.Data;
|
||||
using Spark.Api.MarketData;
|
||||
using Spark.Api.Orders;
|
||||
using Spark.Api.Reactive;
|
||||
using Spark.Core;
|
||||
@@ -81,6 +82,11 @@ namespace Spark.Trading {
|
||||
pnl = bus.subscribe(SparkTags.ReportBlackboardTags.PNL.PNLData)
|
||||
.Select(d => d.position_data.ToDictionary(u => u.id(), PNLRowData.from_pnl_unit))
|
||||
.Table(PNLRowData.key_fn)
|
||||
.JoinBbo(
|
||||
bus.subscribe(SparkTags.SecuritiesTag),
|
||||
pnlRow => pnlRow.key.security_id,
|
||||
(pnlRow, bbo) => pnlRow.with(bid_price: bbo.BidPrice, ask_price: bbo.AskPrice),
|
||||
PNLRowData.key_fn)
|
||||
.FullJoin(
|
||||
order_data.working_account_security_orders,
|
||||
PNLRowData.key_fn,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
namespace mbts.spark {
|
||||
public enum MarketViewColumnType {
|
||||
None,
|
||||
Venue,
|
||||
Security,
|
||||
Volume,
|
||||
Last,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace bts.shell.market_view {
|
||||
using bts.api;
|
||||
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
|
||||
@@ -8,8 +10,15 @@ 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; }
|
||||
|
||||
@@ -123,6 +132,8 @@ 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());
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -92,6 +92,7 @@ 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,7 +98,14 @@ 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 =new RegexFilterVM<Row>(r => r.currency, nameof(Row.currency));
|
||||
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);
|
||||
// rows
|
||||
_wrapped_rows = _rows.WrapSelector(r => r.security_md, s => {
|
||||
var r = new Row(_bus, s, _orders_model, _position_model);
|
||||
@@ -122,8 +129,9 @@ 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.Regex, ColWidth.Currency);
|
||||
add_col(ColKey.Currency, r => r.currency, nameof(Row.currency), currency_filter, FilterType.DiscreteValues, ColWidth.Currency);
|
||||
add_col(ColKey.Volume, r => r.volume_text, nameof(Row.volume), volume_filter, FilterType.ULongMinMax, ColWidth.Volume);
|
||||
|
||||
// last
|
||||
@@ -388,8 +396,9 @@ 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));
|
||||
@@ -535,6 +544,8 @@ 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(),
|
||||
@@ -580,6 +591,8 @@ 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);
|
||||
@@ -616,16 +629,16 @@ namespace mbts.spark {
|
||||
|
||||
static (string desc, Color fg) _fills_title(ColKey key) =>
|
||||
key switch {
|
||||
ColKey.Position => ("Day Fills", Colors.Black),
|
||||
ColKey.RawDayPosition => ("Raw Day Fills", Colors.Black),
|
||||
_ => ("Fills", Colors.Black),
|
||||
ColKey.Position => ("Day Fills", BTSColors.DayFills.foreground),
|
||||
ColKey.RawDayPosition => ("Raw Day Fills", BTSColors.DayFills.foreground),
|
||||
_ => ("Fills", BTSColors.UI.default_text),
|
||||
};
|
||||
|
||||
static (string desc, Color fg) _orderbook_title(ColKey key) =>
|
||||
key switch {
|
||||
ColKey.WorkingBids => ((string desc, Color fg))("Working Buy Orders", BTSColors.MarketData.bid),
|
||||
ColKey.WorkingAsks => ((string desc, Color fg))("Working Sell Orders", BTSColors.MarketData.ask),
|
||||
_ => ("Working Orders", Colors.Black),
|
||||
_ => ("Working Orders", BTSColors.UI.default_text),
|
||||
};
|
||||
|
||||
static GridSettings _hide_cols(GridSettings grid, IEnumerable<OrderBookGridView.ColumnKeys> columns) =>
|
||||
|
||||
@@ -626,6 +626,22 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<TextBlock
|
||||
Grid.Column="6"
|
||||
x:Name="PART_SpreaderType_Label"
|
||||
VerticalAlignment="Center"
|
||||
Margin="{StaticResource Margin.LHeaderPanelItem}"
|
||||
Text="Type"
|
||||
/>
|
||||
<ComboBox
|
||||
x:Name="PART_SpreaderType_DropDown"
|
||||
Grid.Column="7"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="{StaticResource Margin.LHeaderPanelItem}"
|
||||
ItemsSource="{TemplateBinding SpreaderTypeItemsSource}"
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
SelectedItem="{Binding SpreaderType, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
/>
|
||||
<!-- <TextBlock
|
||||
Grid.Column="6"
|
||||
x:Name="PART_HedgePricing_Label"
|
||||
VerticalAlignment="Center"
|
||||
@@ -642,7 +658,7 @@
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
SelectedItem="{Binding HedgePricing, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Visibility="Collapsed"
|
||||
/>
|
||||
/> -->
|
||||
</Grid>
|
||||
<c:DataGridEx
|
||||
x:Name="PART_DataGrid"
|
||||
|
||||
@@ -237,6 +237,29 @@ namespace mbts.ui.spark {
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SpreaderTypeItemsSource
|
||||
public static readonly DependencyProperty SpreaderTypeItemsSourceProperty =
|
||||
WPFHelper.create_dp<IEnumerable, ExecutionStrategyEditor>(
|
||||
nameof(SpreaderTypeItemsSource));
|
||||
|
||||
public IEnumerable SpreaderTypeItemsSource {
|
||||
get => GetValue(SpreaderTypeItemsSourceProperty) as IEnumerable;
|
||||
set => SetValue(SpreaderTypeItemsSourceProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SpreaderType
|
||||
public static readonly DependencyProperty SpreaderTypeProperty =
|
||||
WPFHelper.create_dp<object, ExecutionStrategyEditor>(
|
||||
nameof(SpreaderType),
|
||||
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
|
||||
|
||||
public object SpreaderType {
|
||||
get => GetValue(SpreaderTypeProperty);
|
||||
set => SetValue(SpreaderTypeProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override void OnApplyTemplate() =>
|
||||
_connect_leg_tuples();
|
||||
|
||||
|
||||
@@ -117,6 +117,14 @@ namespace bts.shell {
|
||||
public static QuoteInsideMarketDistanceThrottle QuoteInsideMarketDistanceThrottle => new QuoteInsideMarketDistanceThrottle();
|
||||
#endregion
|
||||
|
||||
public static SniperQuoteParams SniperQuoteParams => new() {
|
||||
NTicks = 0
|
||||
};
|
||||
|
||||
public static ReverseHedgeQuoteParams ReverseHedgeQuoteParams => new() {
|
||||
NTicks = 0
|
||||
};
|
||||
|
||||
#region PreHedge
|
||||
public static PreHedgeParams PreHedgeParams => new PreHedgeParams {
|
||||
MaxPriceFromInsideMarket = PreHedgeMaxPriceFromInsideMarket,
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
ContractPickerVM="{Binding contract_picker_vm}"
|
||||
HedgePricing="{Binding hedge_pricing}"
|
||||
HedgePricingItemsSource="{Binding hedge_pricing_options}"
|
||||
SpreaderType="{Binding spreader_type}"
|
||||
SpreaderTypeItemsSource="{Binding spreader_type_options}"
|
||||
UseFractionalTicks="{Binding use_fractional_ticks}"
|
||||
IntegerTickSize="{Binding integer_tick_size}"
|
||||
/>
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
using mbts.ui.spark;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SyntheticEditorVM {
|
||||
class IsQuoteModel : RowModelBase<bool> {
|
||||
public override string display_name { get; } = "Enable Quoting";
|
||||
public override string help_text { get; } = "This parameter is in the form of a check box, where if the box is checked the leg will be quoted versus not checked, it will not be shown in the market. If it is not actively quoted in the market, the leg will still be used for calculation of the entire synthetic.";
|
||||
public override string man_page { get; } = string.Empty;
|
||||
|
||||
public override bool get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.IsActiveQuoting;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) =>
|
||||
leg_tup.leg_params.IsActiveQuoting = value;
|
||||
|
||||
public override bool has_errors(IReadOnlyCollection<LegSpreaderParamsTuple> leg_tups) =>
|
||||
!leg_tups.Any(lt => lt.leg_params.IsActiveQuoting);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using mbts.ui.spark;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.spark;
|
||||
|
||||
partial class SyntheticEditorVM {
|
||||
class QuoteEnabledModel : RowModelBase<bool> {
|
||||
public override string display_name => string.Empty;
|
||||
public override string help_text => string.Empty;
|
||||
public override string man_page => string.Empty;
|
||||
|
||||
public override ParameterSection[] section_path { get; } = new[] { Sections.Quote };
|
||||
|
||||
public override bool get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.IsActiveQuoting;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) =>
|
||||
leg_tup.leg_params.IsActiveQuoting = value;
|
||||
|
||||
public override bool has_errors(IReadOnlyCollection<LegSpreaderParamsTuple> leg_tups) =>
|
||||
!leg_tups.Any(lt => lt.leg_params.IsActiveQuoting);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using bts.shell;
|
||||
|
||||
using mbts.ui.spark;
|
||||
|
||||
namespace mbts.spark;
|
||||
|
||||
partial class SyntheticEditorVM {
|
||||
class ReverseHedgeEnabledModel : RowModelBase<bool> {
|
||||
readonly ReverseHedgeTicksModel _ticks_model;
|
||||
|
||||
public ReverseHedgeEnabledModel(ReverseHedgeTicksModel ticks_model) =>
|
||||
_ticks_model = ticks_model;
|
||||
|
||||
public override string display_name => string.Empty;
|
||||
public override string help_text => string.Empty;
|
||||
public override string man_page => string.Empty;
|
||||
|
||||
public override ParameterSection[] section_path { get; } = new[] { Sections.ReverseHedge };
|
||||
|
||||
public override bool get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.ReverseHedgeQuoteParams is not null;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) {
|
||||
if (value) {
|
||||
if (leg_tup.leg_params.ReverseHedgeQuoteParams is null) {
|
||||
leg_tup.leg_params.ReverseHedgeQuoteParams = SyntheticDataHelper.Defaults.ReverseHedgeQuoteParams;
|
||||
_ticks_model.apply_value(leg_tup);
|
||||
}
|
||||
} else {
|
||||
leg_tup.leg_params.ReverseHedgeQuoteParams = null;
|
||||
}
|
||||
_ticks_model.set_enabled(leg_tup, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
using bts.spark;
|
||||
|
||||
using mbts.ui.spark;
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mbts.spark;
|
||||
|
||||
partial class SyntheticEditorVM {
|
||||
internal class ReverseHedgeTicksModel : RowModelBase<int> {
|
||||
readonly Dictionary<int, int> _value_store = new();
|
||||
|
||||
public override string display_name => "Ticks";
|
||||
public override string help_text => string.Empty;
|
||||
public override string man_page => string.Empty;
|
||||
|
||||
public override ParameterSection[] section_path { get; } = new[] { Sections.ReverseHedge };
|
||||
|
||||
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_params(leg_tup).NTicks = _value_from_store(leg_tup);
|
||||
|
||||
public override int get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_params(leg_tup)?.NTicks ?? _value_from_store(leg_tup);
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, int value) {
|
||||
_value_store[leg_tup.id] = value;
|
||||
var s = _get_params(leg_tup);
|
||||
if (s is not null) {
|
||||
s.NTicks = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_params(leg_tup) != null;
|
||||
|
||||
ReverseHedgeQuoteParams _get_params(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.ReverseHedgeQuoteParams;
|
||||
|
||||
int _value_from_store(LegSpreaderParamsTuple leg_tup) =>
|
||||
_value_store.get_value(leg_tup.id, _ => 0);
|
||||
}
|
||||
}
|
||||
@@ -94,7 +94,9 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
private static class Sections {
|
||||
public static ParameterSection Quote => new ParameterSection("Quote");
|
||||
public static ParameterSection Sniper => new("Sniper", true);
|
||||
public static ParameterSection ReverseHedge => new("Reverse Hedge", true);
|
||||
public static ParameterSection Quote => new("Quote", true);
|
||||
public static ParameterSection PreHedge => new ParameterSection("Pre-Hedge");
|
||||
public static ParameterSection PostHedge => new ParameterSection("Post-Hedge");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using bts.shell;
|
||||
|
||||
using mbts.ui.spark;
|
||||
|
||||
namespace mbts.spark;
|
||||
|
||||
partial class SyntheticEditorVM {
|
||||
class SniperEnabledModel : RowModelBase<bool> {
|
||||
readonly SniperTicksModel _ticks_model;
|
||||
|
||||
public SniperEnabledModel(SniperTicksModel ticks_model) =>
|
||||
_ticks_model = ticks_model;
|
||||
|
||||
public override string display_name => string.Empty;
|
||||
public override string help_text => string.Empty;
|
||||
public override string man_page => string.Empty;
|
||||
|
||||
public override ParameterSection[] section_path { get; } = new[] { Sections.Sniper };
|
||||
|
||||
public override bool get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.SniperQuoteParams is not null;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) {
|
||||
if (value) {
|
||||
if (leg_tup.leg_params.SniperQuoteParams is null) {
|
||||
leg_tup.leg_params.SniperQuoteParams = SyntheticDataHelper.Defaults.SniperQuoteParams;
|
||||
_ticks_model.apply_value(leg_tup);
|
||||
}
|
||||
} else {
|
||||
leg_tup.leg_params.SniperQuoteParams = null;
|
||||
}
|
||||
_ticks_model.set_enabled(leg_tup, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using bts.spark;
|
||||
|
||||
using mbts.ui.spark;
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mbts.spark;
|
||||
|
||||
partial class SyntheticEditorVM {
|
||||
internal class SniperTicksModel : RowModelBase<int> {
|
||||
readonly Dictionary<int, int> _value_store = new();
|
||||
|
||||
public override string display_name => "Ticks";
|
||||
public override string help_text => string.Empty;
|
||||
public override string man_page => string.Empty;
|
||||
|
||||
public override ParameterSection[] section_path { get; } = new[] { Sections.Sniper };
|
||||
|
||||
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_params(leg_tup).NTicks = _value_from_store(leg_tup);
|
||||
|
||||
public override int get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_params(leg_tup)?.NTicks ?? _value_from_store(leg_tup);
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, int value) {
|
||||
_value_store[leg_tup.id] = value;
|
||||
var s = _get_params(leg_tup);
|
||||
if (s is not null) {
|
||||
s.NTicks = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_params(leg_tup) != null;
|
||||
|
||||
SniperQuoteParams _get_params(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.SniperQuoteParams;
|
||||
|
||||
int _value_from_store(LegSpreaderParamsTuple leg_tup) =>
|
||||
_value_store.get_value(leg_tup.id, _ => 0);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
using HedgePricing = bts.spark.ExecutionStrategy.Types.HedgePricing;
|
||||
using SpreaderType = bts.spark.ExecutionStrategy.Types.SpreaderType;
|
||||
|
||||
namespace mbts.spark {
|
||||
/// <summary>
|
||||
@@ -47,6 +48,7 @@ namespace mbts.spark {
|
||||
int _integer_tick_size = 1;
|
||||
ExecutionStrategyImmutable _strategy;
|
||||
HedgePricing _hedge_pricing;
|
||||
SpreaderType _spreader_type;
|
||||
|
||||
/// <summary>
|
||||
/// Inititalizes a new instance of SyntheticEditorVM class. This constructor may only be used for already-existing synthetic securities (id must be non-zero).
|
||||
@@ -198,6 +200,8 @@ namespace mbts.spark {
|
||||
} else {
|
||||
_can_add_leg = backing(nameof(can_add_leg), false);
|
||||
}
|
||||
var sniper_ticks_model = new SniperTicksModel();
|
||||
var reverse_hedge_ticks_model = new ReverseHedgeTicksModel();
|
||||
var show_quantity_quantity = new ShowQuantityQuantityModel();
|
||||
|
||||
var quote_throttle_outside_ticks = new QuoteThrottleOutsideTicksModel();
|
||||
@@ -225,15 +229,18 @@ namespace mbts.spark {
|
||||
|
||||
var post_hedge_opposite_market_based_cross_threshold = new PostHedgeOppositeMarketVolumeBasedCrossThresholdModel();
|
||||
var post_hedge_opposite_market_based_cross_cross_ticks = new PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel();
|
||||
var is_quote_model = new IsQuoteModel();
|
||||
rows = new IRowModel[] {
|
||||
_quantity_model,
|
||||
_price_mult_model,
|
||||
is_quote_model,
|
||||
//new EnableHedgeModel(), //TAHA_SPREADER
|
||||
new HedgeRatioModel(),
|
||||
new SniperEnabledModel(sniper_ticks_model),
|
||||
sniper_ticks_model,
|
||||
new ReverseHedgeEnabledModel(reverse_hedge_ticks_model),
|
||||
reverse_hedge_ticks_model,
|
||||
//new MinLeanQtyModel(), //TAHA_SPREADER
|
||||
//new QuoteHiddenModel(),
|
||||
new QuoteEnabledModel(),
|
||||
new QuoteQueueHolderModel(),
|
||||
new MarketDataTypeModel(),
|
||||
new ShowQuantityEnabledModel(show_quantity_quantity),
|
||||
@@ -337,6 +344,13 @@ namespace mbts.spark {
|
||||
public HedgePricing hedge_pricing { get => _hedge_pricing; set => Set(ref _hedge_pricing, value); }
|
||||
public IEnumerable<HedgePricing> hedge_pricing_options => EnumHelper.get_values<HedgePricing>();
|
||||
|
||||
// spreader type
|
||||
public SpreaderType spreader_type {
|
||||
get => _spreader_type;
|
||||
set => Set(ref _spreader_type, value);
|
||||
}
|
||||
public IEnumerable<SpreaderType> spreader_type_options => EnumHelper.get_values<SpreaderType>();
|
||||
|
||||
public ExecutionStrategyImmutable get_strategy() {
|
||||
var mutable_strategy = _strategy.CloneToMutable();
|
||||
mutable_strategy.LegParamsMap.Clear();
|
||||
@@ -352,6 +366,7 @@ namespace mbts.spark {
|
||||
_synthetic.Exchange = Venue.Bts;
|
||||
_strategy = strategy;
|
||||
hedge_pricing = strategy.HedgePricing;
|
||||
spreader_type = strategy.SpreaderType;
|
||||
leg_tuples.SetItems(_get_legs(_synthetic, strategy));
|
||||
if (synthetic.HasContractSpec) {
|
||||
/// assign the fields <see cref="_use_fractional_ticks"/> and <see cref="_integer_tick_size"/>,
|
||||
|
||||
@@ -237,6 +237,17 @@ 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,6 +21,7 @@ namespace mbts.ui.spark {
|
||||
Side,
|
||||
Price,
|
||||
Size,
|
||||
NotionalValue,
|
||||
FillKind,
|
||||
FillStatus,
|
||||
FillKey,
|
||||
|
||||
@@ -8,6 +8,7 @@ using mbts.ui.controls;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using Spark.Api.Data;
|
||||
using Spark.Api.Orders;
|
||||
|
||||
using System;
|
||||
@@ -61,6 +62,10 @@ 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; }
|
||||
@@ -87,7 +92,7 @@ namespace bts.shell.fills {
|
||||
|
||||
// PNL's Expiration group popups use this prop to filtered data by a particular exp (it isn't
|
||||
// serialized bc it's not part of the main window's settings)
|
||||
public DateTime? exp_date { get; set; }
|
||||
public ExpirationDate? exp_date { get; set; }
|
||||
}
|
||||
|
||||
static class FillsFilterStatesExtensions {
|
||||
@@ -113,10 +118,11 @@ 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) {
|
||||
pb.add_date_time(fs.exp_date.Value, f => f.security?.earliest_expiration());
|
||||
pb.add_exp_date(fs.exp_date.Value, f => f.security?.earliest_expiration());
|
||||
}
|
||||
return pb.to_predicate();
|
||||
}
|
||||
|
||||
@@ -408,18 +408,18 @@ namespace mbts.spark {
|
||||
|
||||
[ProtoContract]
|
||||
class LadderPanelPricePointSettings : IEquatable<LadderPanelPricePointSettings> {
|
||||
public static LadderPanelPricePointSettings historic_default =>
|
||||
public static LadderPanelPricePointSettings Default =>
|
||||
new LadderPanelPricePointSettings {
|
||||
day_change = new DaySettings {
|
||||
// (neutral)
|
||||
font = _default_font,
|
||||
background_color = Colors.Transparent.to_uint(),
|
||||
font = _default_font.With(font_color: Colors.Black.to_uint()),
|
||||
background_color = Color.FromRgb(113, 201, 69).to_uint(),
|
||||
// negative
|
||||
negative_font_color = Colors.White.to_uint(),
|
||||
negative_background_color = Colors.Red.to_uint(),
|
||||
// positive
|
||||
positive_font_color = Colors.White.to_uint(),
|
||||
positive_background_color = Colors.Green.to_uint()
|
||||
positive_background_color = Colors.Green.to_uint(),
|
||||
},
|
||||
session_low = _default_item.With(_default_item.font.With(font_color: BTSColors.MarketData.session_low)),
|
||||
session_high = _default_item.With(_default_item.font.With(font_color: BTSColors.MarketData.session_high)),
|
||||
@@ -431,21 +431,9 @@ namespace mbts.spark {
|
||||
theo = _default_item,
|
||||
delta = _default_item,
|
||||
vega = _default_item,
|
||||
volume = _default_item
|
||||
volume = _default_item,
|
||||
};
|
||||
|
||||
public static LadderPanelPricePointSettings tt_default {
|
||||
get {
|
||||
var rv = historic_default;
|
||||
rv.day_change.background_color = Color.FromRgb(113, 201, 69).to_uint();
|
||||
rv.day_change.font.font_color = Colors.Black.to_uint();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
public static LadderPanelPricePointSettings Default => LadderSettings.mimic_tt ?
|
||||
tt_default : historic_default;
|
||||
|
||||
[ProtoMember(1)]
|
||||
public DaySettings day_change { get; set; }
|
||||
[ProtoMember(2)]
|
||||
@@ -607,11 +595,6 @@ namespace mbts.spark {
|
||||
uint _ticks_per_row;
|
||||
List<FairValueLine> _fair_value_lines;
|
||||
|
||||
static LadderSettings() {
|
||||
var knobs = config.Configuration.get<bts.config.LadderKnobs>();
|
||||
mimic_tt = knobs.MimicTt;
|
||||
}
|
||||
|
||||
// Default proto constructor
|
||||
// Added this so that when clients upgrade to a version with new settings,
|
||||
// the default values are applied correctly; thx to John for this tip
|
||||
@@ -643,9 +626,7 @@ namespace mbts.spark {
|
||||
status_message_visible = true;
|
||||
}
|
||||
|
||||
public static bool mimic_tt { get; }
|
||||
|
||||
public static LadderSettings historic_default {
|
||||
public static LadderSettings Default {
|
||||
get {
|
||||
LadderColumnSettings
|
||||
orders_column = LadderColumnSettings.default_orders,
|
||||
@@ -713,11 +694,6 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public static LadderSettings tt_default => historic_default;
|
||||
|
||||
public static LadderSettings Default => mimic_tt ?
|
||||
tt_default : historic_default;
|
||||
|
||||
public enum BadgeStyle { Normal, Alternate }
|
||||
|
||||
// TODO: rename to PriceDisplayFormat (today is merge monday, since this isn't a functional change, it is not worth the potential merge headaches right now)
|
||||
@@ -727,7 +703,11 @@ namespace mbts.spark {
|
||||
[Description("Net Change Dollars")]
|
||||
Dollars,
|
||||
[Description("Net Change Ticks")]
|
||||
Ticks
|
||||
Ticks,
|
||||
[Description("Basis Points")]
|
||||
BasisPoints,
|
||||
[Description("Net Basis Points")]
|
||||
NetBasisPoints,
|
||||
}
|
||||
|
||||
public enum PriceDisplayFormat {
|
||||
@@ -1035,7 +1015,6 @@ namespace mbts.spark {
|
||||
a.ladder_order_limits.safe_equals(b.ladder_order_limits) &&
|
||||
a._ticks_per_row == b._ticks_per_row &&
|
||||
a.panel_price_point_settings.safe_equals(b.panel_price_point_settings) &&
|
||||
a._ticks_per_row == b._ticks_per_row &&
|
||||
EqualityComparer.safe_sequence_equals(a.fair_value_lines, b.fair_value_lines);
|
||||
|
||||
public bool Equals(LadderSettings other) =>
|
||||
|
||||
@@ -66,8 +66,12 @@ namespace mbts.spark {
|
||||
_orders = orders;
|
||||
_real_price = real_price;
|
||||
_text_lines = backing(nameof(text_lines), () => {
|
||||
var fmt = MDDisplayHelper.get_value_formatter(_owner.model.security_md.quantity_display);
|
||||
return BadgeRender.format(side, _quantity, _filled, _current_badge_style, fmt);
|
||||
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);
|
||||
});
|
||||
_clear_phantom_price = new(() => {
|
||||
_phantom_price = double.NaN;
|
||||
|
||||
@@ -705,6 +705,7 @@ 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;
|
||||
|
||||
@@ -41,6 +41,8 @@ namespace mbts.spark {
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
using SpreaderType = bts.spark.ExecutionStrategy.Types.SpreaderType;
|
||||
|
||||
class LadderVM : DisposableViewModelBase, IWillLetYouKnowWhenIDispose, IKeyListener {
|
||||
static readonly Brush _transparent_status_brush = BTSColors.UI.transparent;
|
||||
|
||||
@@ -103,10 +105,12 @@ namespace mbts.spark {
|
||||
ulong _misc_time = 0;
|
||||
ulong _last_trade_time = 0;
|
||||
ulong _last_bbo_time = 0;
|
||||
SpreaderType _spreader_type = SpreaderType.Requote;
|
||||
IReadOnlyDictionary<uint, OrderRecordTuple> _cache = DictionaryHelper.empty<uint, OrderRecordTuple>();
|
||||
ImmutableDictionary<AlgoKey, LadderFavoriteAlgoMetadata> _favorite_algo_metadata_map =
|
||||
ImmutableDictionary<AlgoKey, LadderFavoriteAlgoMetadata>.Empty;
|
||||
RegionalTickFactors? _spec;
|
||||
IReadOnlyDictionary<uint, SecurityMD> _securities = new Dictionary<uint, SecurityMD>(0);
|
||||
|
||||
LadderVM(
|
||||
IBus bus,
|
||||
@@ -125,6 +129,9 @@ namespace mbts.spark {
|
||||
size_calculator.display_settings = model.security_md.quantity_display;
|
||||
_slot_price_vs_orders = new Dictionary<double, LadderOrderVM>(_equality);
|
||||
this.order_properties_vm = order_properties_vm;
|
||||
if (model.security.HasSynthetic) {
|
||||
spreader_type_choices.AddRange(EnumHelper.get_values<SpreaderType>());
|
||||
}
|
||||
_session_high = backing(nameof(session_high), double.NaN);
|
||||
_session_low = backing(nameof(session_low), double.NaN);
|
||||
_day_change = backing(nameof(day_change), double.NaN);
|
||||
@@ -276,9 +283,7 @@ namespace mbts.spark {
|
||||
var orders_source = settings.Select(s => s.ticks_per_row).DistinctUntilChanged()
|
||||
.SelectSeries(tpr => {
|
||||
var spec = TickUtils.multiply_tick_size(security.regional_ticks(), tpr);
|
||||
var price_comp = Comparer<double>.Create((a, b) =>
|
||||
Comparer<long?>.Default.Compare(spec.count_ticks(a), spec.count_ticks(b)));
|
||||
return _order_price_source(model.orders, price_comp, p => spec.round_to_tick(p, RoundingMode.AbsDown));
|
||||
return _order_price_source(model.orders, p => spec.round_to_tick(p, RoundingMode.AbsDown));
|
||||
});
|
||||
_day_change.Changed += (o, n) => RaisePropertyChanged(
|
||||
nameof(day_change_font_color), nameof(day_change_background_color));
|
||||
@@ -329,7 +334,8 @@ namespace mbts.spark {
|
||||
}),
|
||||
WorkLoop.Start($"Ladder '{security}'", _tick),
|
||||
ObservableAutoWrapper.dispose_on_removed(orders, true),
|
||||
Disposable.Create(() => _on_disposed_impl?.Invoke(true)));
|
||||
Disposable.Create(() => _on_disposed_impl?.Invoke(true)),
|
||||
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(secs => _securities = secs));
|
||||
|
||||
PropertyAutoWrapper<LadderSettings, bool> bool_prop(
|
||||
string name, Func<LadderSettings, bool> get_bool) =>
|
||||
@@ -405,7 +411,9 @@ namespace mbts.spark {
|
||||
event Action<bool>? _on_disposed_impl;
|
||||
|
||||
public IMDConsumerHandle md => _model.md;
|
||||
public SecurityMD security_md => _model.security_md;
|
||||
public IBus bus => _bus;
|
||||
public IReadOnlyDictionary<uint, SecurityMD> securities => _securities;
|
||||
|
||||
// we return only entries who have piqs and whose piqs are not uint.Max
|
||||
// uint.Max happens when the server is restarted but the client is still up
|
||||
@@ -576,6 +584,12 @@ namespace mbts.spark {
|
||||
public ObservableCollection<FairValueLineVM> fair_value_lines { get; } =
|
||||
new ObservableCollection<FairValueLineVM>();
|
||||
|
||||
public SpreaderType spreader_type {
|
||||
get => _spreader_type;
|
||||
set => Set(ref _spreader_type, value);
|
||||
}
|
||||
public ObservableCollection<SpreaderType> spreader_type_choices { get; } = new();
|
||||
|
||||
public uint orders_column_width {
|
||||
get => _orders_column_width.Value;
|
||||
set => _orders_column_width.set(value);
|
||||
@@ -755,15 +769,21 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
var order = order_properties_vm.create(side);
|
||||
if (order.CustomDef is not null) {
|
||||
if (order.CustomDef.Stop is not null) {
|
||||
order.CustomDef.Stop.StopPrice = price;
|
||||
} else if (order.CustomDef.StopLimit is not null) {
|
||||
var def = order.CustomDef;
|
||||
if (def is not null) {
|
||||
if (def.Stop is not null) {
|
||||
def.Stop.StopPrice = price;
|
||||
} else if (def.StopLimit is not null) {
|
||||
// swap the prices here so that the level clicked
|
||||
// in the ladder is the StopPrice (trigger) and the Price
|
||||
// is what was specificed in the extra parameter box
|
||||
(price, order.CustomDef.StopLimit.StopPrice) =
|
||||
(order.CustomDef.StopLimit.StopPrice, price);
|
||||
(price, def.StopLimit.StopPrice) =
|
||||
(def.StopLimit.StopPrice, price);
|
||||
}
|
||||
if (security.HasSynthetic) {
|
||||
foreach (var d in OrderDataHelper.enumerate_algo_hierarchy(def).Where(d => d.CustomCase == CustomOrderDefinition.CustomOneofCase.Spreader)) {
|
||||
d.Spreader.ExecutionStrategy.SpreaderType = spreader_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,10 +822,8 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
static IObservable<(ImmutableSortedDictionary<double, ImmutableDictionary<uint, OrderRecordTuple>> price_index, IReadOnlyDictionary<uint, OrderRecordTuple> map)> _order_price_source(
|
||||
IObservable<TableUpdate<OrderRecordTuple, uint>> source,
|
||||
IComparer<double> price_comp,
|
||||
Func<double, double> normalize_price
|
||||
) {
|
||||
IObservable<TableUpdate<OrderRecordTuple, uint>> source, Func<double, double> normalize_price) {
|
||||
var price_comp = MathUtils.error_margin_comparer(1E-9);
|
||||
return Observable.Create<(ImmutableSortedDictionary<double, ImmutableDictionary<uint, OrderRecordTuple>>, IReadOnlyDictionary<uint, OrderRecordTuple>)>(o => {
|
||||
var state = ImmutableSortedDictionary.Create<double, ImmutableDictionary<uint, OrderRecordTuple>>(price_comp);
|
||||
var sub = source.Subscribe(table => {
|
||||
@@ -849,15 +867,18 @@ namespace mbts.spark {
|
||||
return sub;
|
||||
});
|
||||
|
||||
bool price_changed(Change<OrderRecordTuple> ch) =>
|
||||
price_comp.Compare(ch.New.order.ui_price(), ch.Old.order.ui_price()) != 0;
|
||||
|
||||
double get_price(OrderRecordTuple order) {
|
||||
var p = order.order.ui_price();
|
||||
if (!(normalize_price is null))
|
||||
p = normalize_price(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
bool price_changed(Change<OrderRecordTuple> ch) {
|
||||
var oldp = get_price(ch.Old);
|
||||
var newp = get_price(ch.New);
|
||||
return price_comp.Compare(oldp, newp) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
double _normalize_price(double price) =>
|
||||
|
||||
@@ -17,7 +17,6 @@ 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;
|
||||
@@ -38,9 +37,6 @@ 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;
|
||||
@@ -84,6 +80,7 @@ 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,
|
||||
@@ -94,8 +91,10 @@ 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);
|
||||
Func<double, string> _fmt_mkt_quantity_impl = v => v.ToString();
|
||||
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);
|
||||
int _old_tooltip_row = int.MaxValue;
|
||||
NativePriceSize? _old_last_trade;
|
||||
string _direction_arrow = string.Empty;
|
||||
@@ -107,17 +106,7 @@ 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() {
|
||||
@@ -637,10 +626,30 @@ namespace mbts.spark {
|
||||
|
||||
void _send_parameterized_order(Point point, Side side) {
|
||||
Disposable.dispose(ref _parameterized_order_popup);
|
||||
if (side == Side.Undefined)
|
||||
throw new ArgumentException("Side is undefined.", nameof(side));
|
||||
if (!(_vm.size_calculator.value is double size) || size == 0 || !(_vm.order_properties_vm?.selected_algo_definition?.algo_root?.can_expand ?? false))
|
||||
void on_cannot_send(string reason) {
|
||||
Log.Ui.warn($"LadderGridHost cannot send parameterized order. {reason}");
|
||||
}
|
||||
if (_vm is null) {
|
||||
on_cannot_send("ViewModel is null");
|
||||
return;
|
||||
}
|
||||
switch (side) {
|
||||
case Side.Buy:
|
||||
case Side.Sell:
|
||||
break;
|
||||
default:
|
||||
on_cannot_send($"Invalid side '{side}'");
|
||||
return;
|
||||
}
|
||||
double size = _vm?.size_calculator?.value ?? 0;
|
||||
if (size > 0) {
|
||||
return;
|
||||
}
|
||||
bool can_expand = _vm?.order_properties_vm?.selected_algo_definition?.algo_root?.can_expand ?? false;
|
||||
if (!can_expand) {
|
||||
on_cannot_send("Selected algo not parameterized");
|
||||
return;
|
||||
}
|
||||
double price = _price_for_row(_row_for_point(point));
|
||||
var grid = new Grid { DataContext = _vm };
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
|
||||
@@ -712,6 +721,10 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
async Task _show_mbo_popup(Point point) {
|
||||
if (_vm is null) {
|
||||
Log.Ui.warn("Cannot show mbo popup. ViewModel is null.");
|
||||
return;
|
||||
}
|
||||
var query = MBOPopupModel.default_query.with(
|
||||
security_id: _vm.model.security.Id,
|
||||
price_level: price_for_point(point)
|
||||
@@ -737,8 +750,8 @@ namespace mbts.spark {
|
||||
};
|
||||
view.RequestClose += (o, _) => popup.IsOpen = false;
|
||||
}
|
||||
string fmt_mbo_qty(int value) =>
|
||||
value < 0 ? $"-{_fmt_mkt_quantity((uint)value)}" : _fmt_mkt_quantity((uint)value);
|
||||
string fmt_mbo_qty(double value, double price) =>
|
||||
value < 0 ? $"-{_fmt_mkt_quantity(value, price)}" : _fmt_mkt_quantity(value, price);
|
||||
}
|
||||
|
||||
protected override void OnMouseDoubleClick(MouseButtonEventArgs e) {
|
||||
@@ -872,32 +885,13 @@ namespace mbts.spark {
|
||||
switch (column.column_type) {
|
||||
case ColumnType.Order: _ordershost.set_col_width(column.settings.width); break;
|
||||
case ColumnType.Bid:
|
||||
render_market_col(_bids, _bid_market_cell_background);
|
||||
render_market_col(_depth.Bids);
|
||||
break;
|
||||
case ColumnType.Price:
|
||||
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);
|
||||
}
|
||||
_refresh_prices(column);
|
||||
break;
|
||||
case ColumnType.Ask:
|
||||
render_market_col(_asks, _ask_market_cell_background);
|
||||
render_market_col(_depth.Asks);
|
||||
break;
|
||||
case ColumnType.Volume:
|
||||
_volume_container.mark_dirty();
|
||||
@@ -910,9 +904,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(NativePriceSize[] depth, Color? market_cell_background) {
|
||||
void render_market_col(IEnumerable<(double Price, double Volume, double NotionalValue)> depth) {
|
||||
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_mkt_depth_item, _lighten_market_cells, market_cell_background);
|
||||
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);
|
||||
}
|
||||
}
|
||||
_apply_column_visual_offset();
|
||||
@@ -958,6 +952,7 @@ 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;
|
||||
@@ -1016,8 +1011,10 @@ namespace mbts.spark {
|
||||
//System.Diagnostics.Debug.WriteLine(" Tooltip - Previous tooltip is visible, but we've changed rows");
|
||||
_hide_tool_tip();
|
||||
}
|
||||
|
||||
_tooltip_view.Content = _fmt_mkt_quantity(volume);
|
||||
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.IsOpen = true;
|
||||
ToolTip = _tooltip_view;
|
||||
} else {
|
||||
@@ -1104,19 +1101,34 @@ 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 = new LadderPriceList {
|
||||
length = _initial_row_count,
|
||||
regional_tick = _spec
|
||||
};
|
||||
_price_list = _create_price_list();
|
||||
var mid_tick = security.tickdiff(_mkt_break_lines.origin, 0) ?? 0;
|
||||
if (mid_tick > int.MaxValue) {
|
||||
Log.Ui.error(
|
||||
@@ -1153,10 +1165,8 @@ namespace mbts.spark {
|
||||
_scroll_viewer.ScrollToVerticalOffset(new_vertical_offset);
|
||||
}
|
||||
}
|
||||
_refresh_prices();
|
||||
} else {
|
||||
_refresh_prices();
|
||||
}
|
||||
_refresh_prices();
|
||||
scroll_to_pending_target();
|
||||
_refresh_all_visuals();
|
||||
} else {
|
||||
@@ -1165,8 +1175,17 @@ namespace mbts.spark {
|
||||
_refresh_bid_mkt_break();
|
||||
if (_mkt_break_lines.ask_dirty)
|
||||
_refresh_ask_mkt_break();
|
||||
if (_mkt_break_lines.mid_dirty)
|
||||
if (_mkt_break_lines.mid_dirty) {
|
||||
switch (_settings.net_change) {
|
||||
case LadderSettings.NetChangeTerms.BasisPoints:
|
||||
case LadderSettings.NetChangeTerms.NetBasisPoints:
|
||||
_refresh_prices();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_refresh_mid_mkt_break();
|
||||
}
|
||||
_refresh_fair_value_lines(false);
|
||||
if (_mkt_break_lines.session_low_dirty)
|
||||
_refresh_session_low_line();
|
||||
@@ -1176,8 +1195,16 @@ 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_quantity);
|
||||
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 (depth_changed)
|
||||
_refresh_sizes();
|
||||
@@ -1192,25 +1219,27 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
void _refresh_prices() {
|
||||
void _refresh_prices(Column column = null) {
|
||||
if (_settings is null) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
void _refresh_all_visuals() {
|
||||
@@ -1280,21 +1309,36 @@ namespace mbts.spark {
|
||||
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_quantity);
|
||||
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);
|
||||
}
|
||||
|
||||
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, _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);
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1349,7 +1393,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)} ";
|
||||
return $"{_direction_arrow} {_fmt_mkt_quantity(_last_trade.size, _last_trade.price)} ";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1404,7 +1448,7 @@ namespace mbts.spark {
|
||||
double best_ask = _get_top_price(_asks);
|
||||
var v = best_bid
|
||||
.if_nan(_spec.tickdown(best_ask))
|
||||
.if_nan(_vm.settlement);
|
||||
.if_nan(_vm?.settlement ?? double.NaN);
|
||||
return _spec.round_to_tick(v, RoundingMode.AbsDown);
|
||||
}
|
||||
|
||||
@@ -1412,11 +1456,14 @@ namespace mbts.spark {
|
||||
TickUtils.normalize_price(_spec, price);
|
||||
|
||||
double _get_ask_market_break() {
|
||||
if (_vm is null) {
|
||||
return double.NaN;
|
||||
}
|
||||
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);
|
||||
.if_nan(_vm?.settlement ?? double.NaN);
|
||||
return _spec.round_to_tick(v, RoundingMode.AbsDown);
|
||||
}
|
||||
|
||||
@@ -1426,15 +1473,15 @@ namespace mbts.spark {
|
||||
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);
|
||||
.if_nan(_vm?.settlement ?? double.NaN);
|
||||
return _spec.round_to_tick(v, RoundingMode.AbsDown);
|
||||
}
|
||||
|
||||
double _get_session_low() => _normalize_price(_get_session_low_raw());
|
||||
double _get_session_high() => _normalize_price(_get_session_high_raw());
|
||||
|
||||
double _get_session_low_raw() => _vm.md.session_low;
|
||||
double _get_session_high_raw() => _vm.md.session_high;
|
||||
double _get_session_low_raw() => _vm?.md?.session_low ?? double.NaN;
|
||||
double _get_session_high_raw() => _vm?.md?.session_high ?? double.NaN;
|
||||
|
||||
void _render_mkt_break(DrawingVisual vis, double price, uint color) =>
|
||||
LadderRender.render_mkt_break(vis, _row_for_price(price), _settings.row_height, color);
|
||||
@@ -1455,7 +1502,7 @@ namespace mbts.spark {
|
||||
}
|
||||
/// The viewmodel will always have the most up-to-date settings, so ignore <see cref="s"/> and just get it from <see cref="_vm"/>.
|
||||
/// This is a workaround for the global settings-defaults issue: when defaults are updated, some properties are inadvertantly overwritten, such as column-widths, etc.
|
||||
_settings = _vm.get_save_state();
|
||||
_settings = _vm?.get_save_state() ?? s;
|
||||
_pending_scroll_to_origin |= _settings == null;
|
||||
_invalidate_spec();
|
||||
if (_price_list != null) {
|
||||
@@ -1477,9 +1524,13 @@ 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);
|
||||
_fmt_mkt_quantity_impl = MDDisplayHelper.get_value_formatter(_qty_display);
|
||||
_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);
|
||||
_set_columns();
|
||||
_apply_column_visual_offset();
|
||||
if (prev_row_height != _settings.row_height)
|
||||
@@ -1489,18 +1540,16 @@ 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();
|
||||
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);
|
||||
}
|
||||
// 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
|
||||
@@ -1529,19 +1578,19 @@ namespace mbts.spark {
|
||||
_visual_host.AddVisualChild(_mid_mkt_break_vis);
|
||||
_visual_host.AddVisualChild(_ask_mkt_break_vis);
|
||||
_visual_host.AddVisualChild(_bid_mkt_break_vis);
|
||||
_fair_value_lines_sub = ui.utility.ObservableAutoWrapper.connect(
|
||||
_vm.fair_value_lines,
|
||||
_mkt_break_lines.fair_value_line_views,
|
||||
vm => {
|
||||
var with_vis = new FairValueLineWithVis(vm);
|
||||
_visual_host.AddVisualChild(with_vis.vis);
|
||||
return with_vis;
|
||||
},
|
||||
with_vis => _visual_host.RemoveVisualChild(with_vis.vis));
|
||||
if (_vm is not null) {
|
||||
_fair_value_lines_sub = ui.utility.ObservableAutoWrapper.connect(
|
||||
_vm.fair_value_lines,
|
||||
_mkt_break_lines.fair_value_line_views,
|
||||
vm => {
|
||||
var with_vis = new FairValueLineWithVis(vm);
|
||||
_visual_host.AddVisualChild(with_vis.vis);
|
||||
return with_vis;
|
||||
},
|
||||
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
|
||||
@@ -1558,16 +1607,27 @@ 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);
|
||||
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());
|
||||
return impl(value);
|
||||
}
|
||||
|
||||
// This is designed to be a redundant null check for _fmt_mkt_quantity_impl.
|
||||
string _fmt_mkt_quantity(double value) {
|
||||
string _fmt_mkt_quantity(double value, double price) {
|
||||
// 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);
|
||||
return impl is null ? value.ToString() : impl(value, price);
|
||||
}
|
||||
|
||||
void _init_tactile_adorner() {
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -31,19 +31,19 @@ namespace mbts.spark {
|
||||
Side side,
|
||||
double quantity,
|
||||
double filled,
|
||||
double price,
|
||||
LadderSettings.BadgeStyle style,
|
||||
Func<double, string> fmt_value = null
|
||||
Func<double, double, string> fmt_value = null
|
||||
) {
|
||||
if (fmt_value is null)
|
||||
fmt_value = FormattingDelegates.default_string<double>();
|
||||
fmt_value ??= (v, p) => v.ToString();
|
||||
switch (style) {
|
||||
case LadderSettings.BadgeStyle.Alternate:
|
||||
var working_qty = quantity > filled ? quantity - filled : 0;
|
||||
return new[] {
|
||||
$"{side.abbreviate()}:{fmt_value(filled)}",
|
||||
$"W:{fmt_value(working_qty)}"
|
||||
$"{side.abbreviate()}:{fmt_value(filled, price)}",
|
||||
$"W:{fmt_value(working_qty, price)}"
|
||||
};
|
||||
default: return new[] { $"{fmt_value(filled)}/{fmt_value(quantity)}" };
|
||||
default: return new[] { $"{fmt_value(filled, price)}/{fmt_value(quantity, price)}" };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +57,7 @@ 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 => LadderSettings.mimic_tt ?
|
||||
new Thickness(4, 2, 4, 2) : new Thickness(6);
|
||||
public static Thickness row_padding => new Thickness(4, 2, 4, 2);
|
||||
|
||||
const string tick_separator_period = ".";
|
||||
const string tick_separator_dash = "-";
|
||||
@@ -102,17 +101,64 @@ namespace mbts.spark {
|
||||
settings.font_size, settings.width, row_height, HorizontalAlignment.Right,
|
||||
-4, VerticalAlignment.Center, row * row_height, true);
|
||||
|
||||
public static GlyphRun[] format_prices(IList<double> prices, double reference_price, bool format_in_tick_terms, double row_height, LadderColumnSettings settings, GlyphContext glyph_context, RegionalTickFactors spec) {
|
||||
public static 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
|
||||
) {
|
||||
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(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));
|
||||
result[i] = format_text(fmt(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) {
|
||||
@@ -128,51 +174,20 @@ namespace mbts.spark {
|
||||
public static void render_prices(
|
||||
DrawingVisual price,
|
||||
GlyphContext glyph_context,
|
||||
IList<double> price_list,
|
||||
IList<double> dprices,
|
||||
double wrt_price,
|
||||
bool format_in_tick_terms,
|
||||
LadderSettings.NetChangeTerms terms,
|
||||
double row_height,
|
||||
LadderColumnSettings settings,
|
||||
RegionalTickFactors tick) {
|
||||
if (price_list.IsNullOrEmpty()) {
|
||||
RegionalTickFactors tick,
|
||||
LadderSettings.PriceDisplayFormat price_display_format = LadderSettings.PriceDisplayFormat.Default) {
|
||||
if (dprices.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,
|
||||
double wrt_price,
|
||||
bool format_in_tick_terms) {
|
||||
var foreground = settings.foreground.rgb_brush();
|
||||
//var background = new SolidColorBrush(settings.background.rgb_color());
|
||||
var fprices = format_prices(dprices, wrt_price, format_in_tick_terms, row_height, settings, glyph_context, tick);
|
||||
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);
|
||||
var fprices = format_prices(
|
||||
dprices, wrt_price, terms, 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));
|
||||
@@ -198,70 +213,78 @@ 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,
|
||||
NativePriceSize[] rungs,
|
||||
IEnumerable<(double Price, double Volume, double NotionalValue)> rungs,
|
||||
double row_height,
|
||||
RegionalTickFactors spec,
|
||||
LadderColumnSettings settings,
|
||||
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 (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();
|
||||
if (!MathUtils.try_increment(ref curr_index, tick_diff)) {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
Func<LadderPriceLevel, string> fmt) {
|
||||
RenderSizes(
|
||||
dv,
|
||||
glyph_context,
|
||||
origin_index,
|
||||
prices,
|
||||
GetLevels(rungs, spec),
|
||||
row_height,
|
||||
spec,
|
||||
settings,
|
||||
fmt);
|
||||
}
|
||||
|
||||
public static void render_volumes(
|
||||
@@ -274,7 +297,7 @@ namespace mbts.spark {
|
||||
VolumeColumnSettings settings,
|
||||
int last_index,
|
||||
string last_size_and_direction,
|
||||
Func<double, string> fmt_value) {
|
||||
Func<double, double, string> fmt_value) {
|
||||
//
|
||||
if (!data.Any()) {
|
||||
_clear_visual(dv);
|
||||
@@ -297,12 +320,12 @@ namespace mbts.spark {
|
||||
_clear_visual(dv);
|
||||
return;
|
||||
}
|
||||
fmt_value ??= FormattingDelegates.default_string<double>();
|
||||
fmt_value ??= (v, p) => v.ToString();
|
||||
// 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, VolumeAtPriceLevelImmutable vap) {
|
||||
void render_cell(DrawingContext dc, int r, double price, VolumeAtPriceLevelImmutable vap) {
|
||||
Brush text_brush = settings.general.foreground.rgb_brush();
|
||||
double y = row_height * r;
|
||||
var last_trade_here = r == last_index;
|
||||
@@ -329,11 +352,11 @@ namespace mbts.spark {
|
||||
}
|
||||
x_bar += width;
|
||||
if (show_numbers)
|
||||
numbers_text.Add(fmt_value(v));
|
||||
numbers_text.Add(fmt_value(v, vap.Price));
|
||||
}
|
||||
|
||||
if (settings.total.show_numbers) {
|
||||
numbers_text.Add(fmt_value(vap.Volume));
|
||||
numbers_text.Add(fmt_value(vap.Volume, vap.Price));
|
||||
}
|
||||
if (settings.show_ltq && last_trade_here) {
|
||||
numbers_text.Clear();
|
||||
@@ -360,9 +383,17 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void RenderCell<T>(DrawingContext dc, int row, T data_item);
|
||||
public delegate void RenderCell<T>(DrawingContext dc, int row, double price, 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()) {
|
||||
@@ -382,7 +413,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, value);
|
||||
render_cell(dc, idx, price, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -394,10 +425,10 @@ namespace mbts.spark {
|
||||
int index,
|
||||
double row_height,
|
||||
LadderColumnSettings settings,
|
||||
Func<double, string> fmt_value) {
|
||||
fmt_value ??= FormattingDelegates.default_string<double>();
|
||||
Func<NativePriceSize, string> fmt_value) {
|
||||
fmt_value ??= nps => nps.size.ToString();
|
||||
using (var dc = dv.RenderOpen()) {
|
||||
var gr = format_text(fmt_value(last.size), index, row_height, settings, glyph_context);
|
||||
var gr = format_text(fmt_value(last), index, row_height, settings, glyph_context);
|
||||
dc.DrawGlyphRun(settings.foreground.rgb_brush(), gr);
|
||||
}
|
||||
}
|
||||
@@ -411,13 +442,13 @@ namespace mbts.spark {
|
||||
double row_height,
|
||||
RegionalTickFactors spec,
|
||||
LadderColumnSettings settings,
|
||||
Func<double, string> fmt_value
|
||||
Func<double, double, string> fmt_value
|
||||
) {
|
||||
fmt_value ??= FormattingDelegates.default_string<double>();
|
||||
fmt_value ??= (v, p) => v.ToString();
|
||||
var fg = settings.foreground.rgb_brush();
|
||||
_render_cells(dv, row_height, pvp, settings, prices, spec, (dc, row, piq) =>
|
||||
_render_cells(dv, row_height, pvp, settings, prices, spec, (dc, row, price, piq) =>
|
||||
dc.DrawGlyphRun(
|
||||
fg, format_text(fmt_value(piq), row, row_height, settings, glyph_context)));
|
||||
fg, format_text(fmt_value(piq, price), row, row_height, settings, glyph_context)));
|
||||
}
|
||||
|
||||
public static void render_mkt_break(DrawingVisual dv, int index, double row_height, uint color) =>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<UserControl
|
||||
<UserControl
|
||||
x:Class="mbts.ui.spark.LadderSettingsView"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
@@ -457,6 +457,7 @@
|
||||
<Binding Path="sample_side"/>
|
||||
<Binding Path="sample_quantity"/>
|
||||
<Binding Path="sample_filled"/>
|
||||
<Binding Path="sample_price"/>
|
||||
<Binding Path="DataContext.badge_style" RelativeSource="{RelativeSource AncestorType=usp:LadderSettingsView}"/>
|
||||
</MultiBinding>
|
||||
</c:Badge.TextLines>
|
||||
@@ -919,35 +920,55 @@
|
||||
<GroupBox Header="Size Display" DataContext="{Binding size_display}" Padding="4" Grid.Row="0" Grid.RowSpan="2">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush}}"/>
|
||||
<ColumnDefinition Width="220" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalSplit}}"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<c:RadioButtonsControl
|
||||
ItemsSource="{Binding format_options}"
|
||||
SelectedItem="{Binding selected_format}"
|
||||
<TextBlock
|
||||
Text="when implied = 0"
|
||||
VerticalAlignment="Center"
|
||||
FontStyle="Italic"
|
||||
/>
|
||||
<StackPanel Grid.Row="1" Grid.ColumnSpan="2" Orientation="Horizontal" VerticalAlignment="Top">
|
||||
<TextBlock
|
||||
Margin="{StaticResource MarginIndentChild}"
|
||||
Text="when implied is > 0"
|
||||
VerticalAlignment="Center"
|
||||
FontStyle="Italic"
|
||||
Foreground="{Binding can_choose_alternate_implied_format, Converter={StaticResource IsEnabledForegroundConverter}}"
|
||||
/>
|
||||
<ComboBox
|
||||
Margin="{StaticResource MarginLPush}"
|
||||
Width="143"
|
||||
IsEnabled="{Binding can_choose_alternate_implied_format}"
|
||||
ItemsSource="{Binding alternate_implied_format_options}"
|
||||
SelectedItem="{Binding selected_alternate_implied_format}"
|
||||
VerticalAlignment="Top"
|
||||
/>
|
||||
</StackPanel>
|
||||
<Border Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="12,4,0,0" Background="Transparent" BorderBrush="DimGray" BorderThickness="1" ToolTip="Sample Output">
|
||||
<ComboBox
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource MarginLPush}"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding format_options}"
|
||||
Text="{Binding selected_format}"
|
||||
IsEditable="True"
|
||||
/>
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Text="when implied > 0"
|
||||
VerticalAlignment="Center"
|
||||
FontStyle="Italic"
|
||||
/>
|
||||
<ComboBox
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource MarginLPush}"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding alternate_implied_format_options}"
|
||||
Text="{Binding selected_alternate_implied_format}"
|
||||
IsEditable="True"
|
||||
/>
|
||||
<Border
|
||||
Grid.Row="4"
|
||||
Grid.ColumnSpan="3"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Margin="12,0,0,0"
|
||||
Background="Transparent"
|
||||
BorderBrush="DimGray"
|
||||
BorderThickness="1"
|
||||
ToolTip="Sample Output">
|
||||
<Grid Width="164">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
@@ -1000,7 +1021,6 @@
|
||||
Grid.Row="0"
|
||||
VerticalAlignment="Top"
|
||||
Header="Price Display"
|
||||
Visibility="{Binding is_synthetic, Converter={StaticResource NetChangeGroupBoxIsVisibleVisibilityConverter}}"
|
||||
Padding="4"
|
||||
>
|
||||
<Grid>
|
||||
@@ -1021,7 +1041,12 @@
|
||||
TickUpCommand="{Binding DataContext.tick_up_ticks_per_row_cmd, RelativeSource={RelativeSource AncestorType=usp:LadderSettingsView}}"
|
||||
TickDownCommand="{Binding DataContext.tick_down_ticks_per_row_cmd, RelativeSource={RelativeSource AncestorType=usp:LadderSettingsView}}"
|
||||
Value="{Binding DataContext.ticks_per_row, RelativeSource={RelativeSource AncestorType=usp:LadderSettingsView}}" />
|
||||
<c:RadioButtonsControl Grid.Row="2" Grid.ColumnSpan="3" ItemsSource="{Binding price_format_options}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding price_format}"/>
|
||||
<c:RadioButtonsControl
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding price_format_options}"
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
SelectedItem="{Binding price_format}" />
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
<GroupBox
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
</Style>
|
||||
|
||||
<Style x:Key="CancelAll" TargetType="Button" BasedOn="{StaticResource LadderMenuButton}">
|
||||
<Setter Property="Grid.Row" Value="2" />
|
||||
<Setter Property="Grid.Row" Value="0"/>
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
<TextBlock VerticalAlignment="Center">Cancel All</TextBlock>
|
||||
@@ -220,10 +220,7 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<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}">
|
||||
<Style x:Key="CancelBuys" TargetType="Button" BasedOn="{StaticResource CancelAll}">
|
||||
<Setter Property="Grid.Row" Value="1" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
@@ -231,7 +228,7 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="TT_CancelSells" TargetType="Button" BasedOn="{StaticResource CancelAll}">
|
||||
<Style x:Key="CancelSells" TargetType="Button" BasedOn="{StaticResource CancelAll}">
|
||||
<Setter Property="Grid.Row" Value="2" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
@@ -239,83 +236,6 @@
|
||||
</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>
|
||||
@@ -1036,6 +956,13 @@
|
||||
</Style>
|
||||
</uag:OrderDetailsPopupControl.Resources>
|
||||
</uag:OrderDetailsPopupControl>
|
||||
|
||||
<ComboBox
|
||||
Margin="2"
|
||||
ItemsSource="{Binding spreader_type_choices}"
|
||||
SelectedItem="{Binding spreader_type}"
|
||||
Visibility="{Binding HasItems, Mode=OneWay, RelativeSource={RelativeSource Self}, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
/>
|
||||
|
||||
<!-- Position into Qty Button -->
|
||||
<Button
|
||||
@@ -1177,19 +1104,19 @@
|
||||
<Button
|
||||
Command="{Binding cancel_cmd}"
|
||||
CommandParameter="Bids"
|
||||
Style="{StaticResource {x:Static sp:LadderView.CancelBuysButtonStyleKey}}"
|
||||
Style="{StaticResource CancelBuys}"
|
||||
Visibility="{Binding
|
||||
show_cancel_buys, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<Button
|
||||
Command="{Binding cancel_cmd}"
|
||||
CommandParameter="Asks"
|
||||
Style="{StaticResource {x:Static sp:LadderView.CancelSellsButtonStyleKey}}"
|
||||
Style="{StaticResource CancelSells}"
|
||||
Visibility="{Binding
|
||||
show_cancel_sells, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<Button
|
||||
Command="{Binding cancel_cmd}"
|
||||
CommandParameter="All"
|
||||
Style="{StaticResource {x:Static sp:LadderView.CancelAllButtonStyleKey}}"
|
||||
Style="{StaticResource CancelAll}"
|
||||
Visibility="{Binding
|
||||
show_cancel_all, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
</Grid>
|
||||
|
||||
@@ -27,22 +27,6 @@ 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";
|
||||
|
||||
@@ -5,6 +5,7 @@ using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
|
||||
using Spark.Api.Data;
|
||||
using Spark.Core.Data;
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reflection;
|
||||
|
||||
Loaded 100 of 738 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user