Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbf1fad2dd | ||
|
|
8a3a35a822 | ||
|
|
924b654f5f | ||
|
|
dac01b3004 | ||
|
|
508c542d56 | ||
|
|
9586b72653 | ||
|
|
ee21b4f597 | ||
|
|
964b516504 | ||
|
|
be6124e72f | ||
|
|
578b9e7570 | ||
|
|
fd66ef6993 | ||
|
|
1301130afb | ||
|
|
7daa96d246 | ||
|
|
49e5eab7d3 | ||
|
|
fee4541e38 | ||
|
|
1fbc1584e5 | ||
|
|
02defef93d | ||
|
|
9132b4a178 | ||
|
|
9390fb0639 | ||
|
|
82a8d007bf | ||
|
|
cff32ad09a | ||
|
|
7b9071a1b2 | ||
|
|
99f1d1fa01 | ||
|
|
03fe75cfd2 | ||
|
|
a7d71fe170 | ||
|
|
00ce3adde5 | ||
|
|
6d774d46c9 | ||
|
|
b39812aad0 | ||
|
|
fb059e5571 | ||
|
|
1864c27584 | ||
|
|
8b7f31ccd6 | ||
|
|
b91f998239 | ||
|
|
183916118e | ||
|
|
8d200ee7f3 | ||
|
|
d59d6fd095 | ||
|
|
c53f69bc53 | ||
|
|
e234a76199 | ||
|
|
cf432a82a0 | ||
|
|
5b40b4b4ca | ||
|
|
dd6b415ce7 | ||
|
|
f99717d880 | ||
|
|
b61cf170fe | ||
|
|
7915c84b6a | ||
|
|
3aece4fb97 | ||
|
|
f224b8be43 | ||
|
|
be4dd05ba6 | ||
|
|
570b362a7e | ||
|
|
944714adc5 | ||
|
|
fee7dcedb8 | ||
|
|
0883b91ef8 | ||
|
|
f43269f688 | ||
|
|
20728b7e8c | ||
|
|
b53060d665 | ||
|
|
01d94748a2 | ||
|
|
ee4ebd8ccf | ||
|
|
ef3e3d6edc | ||
|
|
6d4ff5707c | ||
|
|
158b8ac64d | ||
|
|
c11fc37781 | ||
|
|
e3ca2194d0 | ||
|
|
e49c8e32f2 | ||
|
|
63754199bf | ||
|
|
7af568a620 | ||
|
|
dcf509d21b | ||
|
|
b8c3b36a98 | ||
|
|
8e5afbfa40 | ||
|
|
906031d1c3 | ||
|
|
5eedc1a977 | ||
|
|
021c163b64 | ||
|
|
c6c71f0b04 | ||
|
|
bc8c01a789 | ||
|
|
b93f794116 | ||
|
|
5369e67c7a | ||
|
|
65c4b41c74 | ||
|
|
2ce9f7db34 | ||
|
|
ecad4471b3 | ||
|
|
9b4a99dcb7 | ||
|
|
8d805409d5 | ||
|
|
6a100bf3a6 | ||
|
|
4047e5bdee | ||
|
|
5aef712065 | ||
|
|
dd19ac4bce | ||
|
|
ff2102e0cd | ||
|
|
29762512ad | ||
|
|
289d40f24a | ||
|
|
947a905423 | ||
|
|
dfe9ea12c3 | ||
|
|
a9c2261671 | ||
|
|
b8766089f0 | ||
|
|
18d3c268de | ||
|
|
fdf255a7a3 | ||
|
|
995be6cfd0 | ||
|
|
52ac650c58 | ||
|
|
96c7cc1528 | ||
|
|
d81ef8668c | ||
|
|
dab95d7e91 | ||
|
|
88045ea432 | ||
|
|
01113e645a | ||
|
|
17c57878e1 | ||
|
|
4357fbc410 | ||
|
|
7d216978a3 | ||
|
|
fc3d84c02a | ||
|
|
f6fd9155b1 | ||
|
|
7723f1e004 | ||
|
|
041a7988c1 | ||
|
|
daa9800af2 | ||
|
|
dcb10a3484 | ||
|
|
70e826b212 | ||
|
|
af737564aa | ||
|
|
582f97d1ce | ||
|
|
a83c1eb633 | ||
|
|
1f893c73ad | ||
|
|
ef755ea6fb | ||
|
|
ebfc1bc485 | ||
|
|
a8646ebb3f | ||
|
|
28fd04293b | ||
|
|
111767522f | ||
|
|
429e16996a | ||
|
|
1ed85af40e | ||
|
|
f5457e5ce4 | ||
|
|
7465a38022 | ||
|
|
8afa03cc62 | ||
|
|
26dfd89d6c | ||
|
|
fe54d5b973 | ||
|
|
e7b9c3e228 | ||
|
|
ae5db799d4 | ||
|
|
5ba746fd59 | ||
|
|
ab5cb23062 | ||
|
|
4ebdc603c5 | ||
|
|
d573688064 | ||
|
|
14c601034a | ||
|
|
3de65fb027 | ||
|
|
9de0c4f530 | ||
|
|
ce5998c5dd | ||
|
|
dcbc6e756e | ||
|
|
4955c1a792 | ||
|
|
2990710b88 | ||
|
|
280ae81860 | ||
|
|
5e713d9008 | ||
|
|
763c2fa41a | ||
|
|
9d170e32f1 | ||
|
|
b4410f7412 | ||
|
|
b702ce9479 | ||
|
|
abc7b1e2e9 | ||
|
|
f71ff0e306 | ||
|
|
3c82327f1a | ||
|
|
f3ac92c434 | ||
|
|
a8694d6fb2 | ||
|
|
52e6536018 | ||
|
|
42274edbc4 | ||
|
|
6d438b4764 | ||
|
|
3688ec0739 | ||
|
|
ce8d27bb71 | ||
|
|
ee66ddf9a6 | ||
|
|
bea0e5ced7 | ||
|
|
b2d1d11826 | ||
|
|
d075f2ad0c | ||
|
|
409bbdc0f9 | ||
|
|
d4fd3900be | ||
|
|
e45736a692 | ||
|
|
bd8336e22b | ||
|
|
f3dedfc4e3 | ||
|
|
326bcb4544 | ||
|
|
884cdef97b | ||
|
|
5e3467e3b6 | ||
|
|
979c6e078a | ||
|
|
69f38daad7 | ||
|
|
a64d428a89 | ||
|
|
78c89c6677 | ||
|
|
8d0d678c01 | ||
|
|
8b9df305b4 | ||
|
|
02ee5cc6d3 | ||
|
|
207c93c972 | ||
|
|
14012a16da | ||
|
|
f4b7fbe6b5 | ||
|
|
9901ef54d4 | ||
|
|
293c13e11d | ||
|
|
dc264e1063 | ||
|
|
be4c69fad4 | ||
|
|
2e883808b6 | ||
|
|
2c4dc8fbe5 | ||
|
|
4ea93bc386 | ||
|
|
184a900eea | ||
|
|
3bf086c73a | ||
|
|
9838d123c7 | ||
|
|
9d5b80c990 | ||
|
|
040fc3af96 | ||
|
|
1c6333b07a | ||
|
|
cc07e28aca | ||
|
|
d2aec0c7f2 | ||
|
|
a71953b871 | ||
|
|
70e962000f | ||
|
|
f413938943 | ||
|
|
a7250b4734 | ||
|
|
060b88e4c9 | ||
|
|
26d543af82 | ||
|
|
99b301ad6f | ||
|
|
9cbe83e1a4 | ||
|
|
59369e1b2b | ||
|
|
7c42e75ddd | ||
|
|
ae200b374b | ||
|
|
64b94c58e7 | ||
|
|
8e907d7395 | ||
|
|
c567283d3f | ||
|
|
6b84dc433a | ||
|
|
a833944ec5 | ||
|
|
b453be40fe | ||
|
|
97d16115e8 | ||
|
|
96709c8121 | ||
|
|
2e3957a7ac | ||
|
|
b866157bff | ||
|
|
8e9c7383b6 | ||
|
|
5de9f63665 | ||
|
|
930f3b23b8 | ||
|
|
d3fb52246c | ||
|
|
e173fe5515 | ||
|
|
dbe02e44fd | ||
|
|
7453540140 | ||
|
|
9830dcbe31 | ||
|
|
69bc734b6a | ||
|
|
2e69966d44 | ||
|
|
c2070b8e52 | ||
|
|
64e2408b59 | ||
|
|
17a8d47452 | ||
|
|
b4eabf4eb8 | ||
|
|
f12c1d5e74 | ||
|
|
714dfd5038 | ||
|
|
6dfe9b3892 | ||
|
|
15e557a209 | ||
|
|
8075bafb86 | ||
|
|
e2f4a86fcf | ||
|
|
6089b209d1 | ||
|
|
d7a12d0dd9 | ||
|
|
95ae7b780e | ||
|
|
0644d301b2 | ||
|
|
7e68dc4ff7 | ||
|
|
879963b828 | ||
|
|
5320e16088 | ||
|
|
6116e24df0 | ||
|
|
6ba1de8c5c | ||
|
|
0d66054e8e | ||
|
|
daf39324fd | ||
|
|
ab9d3cede3 | ||
|
|
2f87fe31be | ||
|
|
7429c42de2 | ||
|
|
9147b4605a | ||
|
|
5a9749d551 | ||
|
|
ea950c8786 | ||
|
|
6fc8a6c7fe | ||
|
|
30891bfb49 | ||
|
|
8a4d3d4c97 | ||
|
|
7cbf0dc683 | ||
|
|
e05ce79abf | ||
|
|
2ea95e7938 | ||
|
|
641141810e | ||
|
|
34319c4536 | ||
|
|
c3ac2957d2 | ||
|
|
196c518ac6 | ||
|
|
f04af5af9d | ||
|
|
950509e5d6 | ||
|
|
8f4da463a7 | ||
|
|
f3adf780d0 | ||
|
|
9002ca6183 | ||
|
|
b36715a6a0 | ||
|
|
2305c3af32 | ||
|
|
6259f8b03f | ||
|
|
3866e63a70 | ||
|
|
5078b74f72 | ||
|
|
6a1a2d5f0a | ||
|
|
664c1f28ed | ||
|
|
aae927d773 | ||
|
|
b90f6cb45c | ||
|
|
1cc5a77d7f | ||
|
|
fb14a9c3af | ||
|
|
00dba92636 | ||
|
|
e82db39f8c | ||
|
|
5a5de65292 | ||
|
|
dffb9c5320 | ||
|
|
2be4eb1c5c | ||
|
|
175204efdf | ||
|
|
dbd8a7d742 | ||
|
|
8894aea1ad | ||
|
|
43e82fb8c4 |
No files matched your search
Vendored
+3
@@ -30,6 +30,7 @@
|
||||
"${workspaceRoot}/build/ninja_debug/src",
|
||||
"${workspaceRoot}/externals/windows/include",
|
||||
"${workspaceRoot}/externals/linux/include",
|
||||
"${workspaceRoot}/externals/linux/include/tensorflow/contrib/makefile/gen/proto",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
|
||||
@@ -50,6 +51,7 @@
|
||||
"${workspaceRoot}/build/ninja_debug/src",
|
||||
"${workspaceRoot}/externals/windows/include",
|
||||
"${workspaceRoot}/externals/linux/include",
|
||||
"${workspaceRoot}/externals/linux/include/tensorflow/contrib/makefile/gen/proto",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
|
||||
@@ -74,6 +76,7 @@
|
||||
"${workspaceRoot}/build/ninja_debug/src",
|
||||
"${workspaceRoot}/externals/windows/include",
|
||||
"${workspaceRoot}/externals/linux/include",
|
||||
"${workspaceRoot}/externals/linux/include/tensorflow/contrib/makefile/gen/proto",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
|
||||
|
||||
Vendored
+43
-3
@@ -6,7 +6,7 @@
|
||||
"name": "(gdb) Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"program": "${workspaceRoot}/build/ninja_debug/server/sparkd/sparkd",
|
||||
"args": ["run"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceRoot}/build/ninja_debug/src/server/sparkd",
|
||||
@@ -75,10 +75,10 @@
|
||||
"name": "SparkVM Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
|
||||
"args": ["run"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/server/",
|
||||
"environment": [],
|
||||
"externalConsole": true,
|
||||
"MIMode": "gdb",
|
||||
@@ -108,6 +108,46 @@
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "TFTest ",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/sdk/examples/tf_test",
|
||||
"args": [
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/sdk/examples/",
|
||||
"environment": [],
|
||||
"externalConsole": true,
|
||||
"MIMode": "gdb",
|
||||
"preLaunchTask": "",
|
||||
"logging": {
|
||||
"engineLogging": false
|
||||
},
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "enable pretty-printer",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"description": "Skip STL",
|
||||
"text": "skip -gfi /usr/include/c++/7/bits/*.h",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
],
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": [
|
||||
"bts@sparkvm"
|
||||
],
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Spreader Tests",
|
||||
"type": "cppdbg",
|
||||
|
||||
+4
-1
@@ -13,7 +13,7 @@ set (SPARK_BIN "${EXTERNALS_ROOT}/bin")
|
||||
set (SPARK_INCLUDE "${EXTERNALS_ROOT}/include")
|
||||
set (SPARK_LIB "${EXTERNALS_ROOT}/lib/${CMAKE_BUILD_TYPE}")
|
||||
|
||||
include_directories (BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
|
||||
include_directories (BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib ${TF_INCLUDE})
|
||||
link_directories (${SPARK_LIB})
|
||||
|
||||
if (WIN32)
|
||||
@@ -57,6 +57,9 @@ else ()
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/lib/libspark.so ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND mkdir ARGS -p ${CMAKE_BINARY_DIR}/server/plugins
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/sdk/examples/order_plugin_example.so ${CMAKE_BINARY_DIR}/server/plugins/
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/sdk/examples/ai_order_plugin_example.so ${CMAKE_BINARY_DIR}/server/plugins/
|
||||
COMMAND ln ARGS -sf ${EXTERNALS_ROOT}/lib/Release/libtensorflow.so ${CMAKE_BINARY_DIR}/server/plugins
|
||||
|
||||
COMMENT "Creating server runtime layout"
|
||||
DEPENDS sparkd data_files_dir)
|
||||
|
||||
|
||||
+35
-37
@@ -1,11 +1,10 @@
|
||||
# The BTS Development Process #
|
||||
|
||||
## The Big Picture ##
|
||||
|
||||
Our development work is organized in projects. A project is an effort
|
||||
to specifiy, develop and validate a related set of additions to our
|
||||
Spark platform. A project can be assigned to one more developers and a
|
||||
developer might concurrently be assigned to multiple
|
||||
## Projects ##
|
||||
Our development work is organized in projects. A project is a
|
||||
non-trivial effort to specify, develop and validate a related set of
|
||||
additions to our Spark platform. A project can be assigned to one more
|
||||
developers and a developer might concurrently be assigned to multiple
|
||||
projects. Projects typically start out with specification work,
|
||||
proceed to implementation and finish with validation efforts. At any
|
||||
given time there will be multiple projects being worked on
|
||||
@@ -14,16 +13,16 @@ are merged into the product and deployed to customers.
|
||||
|
||||
## The Details ##
|
||||
Typically project ideas come from sales interactions, opposition
|
||||
research and customer feedback, alltough on occasion development or
|
||||
operational requirements will also suggest potentially valuable
|
||||
research and customer feedback, all tough on occasion development or
|
||||
operational requirements can also suggest valuable project
|
||||
ideas. Whatever the source of a project idea the final decision of
|
||||
which projects to pursue is always done by management. Once a project
|
||||
idea has been selected for implementation the corresponding project
|
||||
will go through several phases: inception, specification, development
|
||||
and merge. We detail each of these phases below.
|
||||
will go through several phases: inception, specification, development,
|
||||
validation and merge.
|
||||
|
||||
* Inception. In this phase we create the infrastructure for the
|
||||
project. Typically this means selecting a team, creating a project
|
||||
* Inception. In this phase the infrastructure for the project is
|
||||
created. Typically this means selecting a team, creating a project
|
||||
case in Jira (that is a case assigned to the team@bluetradesys.com
|
||||
Jira user) and possibly creating a new branch for the project. This
|
||||
phase is mostly mechanical and the responsibility of Pedro.
|
||||
@@ -32,11 +31,11 @@ phase is mostly mechanical and the responsibility of Pedro.
|
||||
ill-considered idea project idea into a concrete, implementable
|
||||
specification that attempts to maximize the value/cost ratio of the
|
||||
project. It is hard to exaggerate the impact of a this phase on the
|
||||
rest of the project. *Nothing* has more impact on our ability to
|
||||
quickly deliver value to customers than good specification
|
||||
work. Specification typically take the form of shared google docs,
|
||||
using a mix of text and UI mockups to capture both user requirements
|
||||
and suggested implementation strategies. Taha is responsible for this
|
||||
rest of the project. *Nothing* has more impact on BTS's ability to
|
||||
quickly deliver value to customers than good specification work. A
|
||||
specification typically takes the form of a shared google docs using
|
||||
a mix of text and UI mock-ups to capture both user requirements and
|
||||
suggested implementation strategies. Taha and Kevin are responsible for this
|
||||
phase.
|
||||
|
||||
* Development. Once a specification is complete, a team of developers
|
||||
@@ -44,24 +43,26 @@ will use a project specific branch to implement the
|
||||
specification. Periodically, typically once a week, progress on the
|
||||
project is assessed by Pedro, possibly with the help of others. This
|
||||
assessment takes the form of code reviews, demos, or just one-on-one
|
||||
conversations. To ensure development does not go off on sub-optimal
|
||||
tangents, developers are expected to continuously merge master into
|
||||
the project branch as well as encouraged to create Pull Requests (PRs)
|
||||
early in the project implementation, so that their work is kept
|
||||
visible and integrated with that of the rest of the group. During the
|
||||
conversations. To ensure development does not go off on tangents,
|
||||
developers are expected to continuously merge master into the project
|
||||
branch as well as encouraged to create Pull Requests (PRs) early in
|
||||
the project implementation, so that their work is kept visible and
|
||||
integrated with that of the rest of the group. During the
|
||||
implementation phase developers should reach out to Pedro for UI
|
||||
reviews, architecture and design concerns as well as specification
|
||||
trade off concerns.
|
||||
reviews, architecture and design questions and specification trade off
|
||||
concerns.
|
||||
|
||||
* Verification. Once code is complete think carefully about the best way
|
||||
to verify that it is good before it goes to production. We don't have a
|
||||
dedicated QA cycle to flush out bugs so it's important that a change be
|
||||
fully tested before it's marked "Ready to Merge." For any non-trivial
|
||||
change consider enlisting a 'test buddy' to help review the code and
|
||||
think of test cases that you may not have considered. Stability from
|
||||
release to release is especially critical right now as we develop a
|
||||
reputation in the futures world.
|
||||
- Code Reviews
|
||||
* Verification. Once code is complete it is the project's team job to
|
||||
think carefully about the best way to verify that the work is solid
|
||||
enough to go to production. We do not have a dedicated QA cycle to
|
||||
flush out bugs so it is important for changes to be fully tested
|
||||
before being marked ready to merge (RTM). For any non-trivial change
|
||||
project teams should enlist a 'test buddy' from outside the team to
|
||||
help review the code and think of test cases that they may not have
|
||||
considered. Stability from release to release is especially critical
|
||||
right now as we develop a reputation in the futures
|
||||
world. Additionally, project teams are encouraged to make liberal use
|
||||
of other testing techniques such as
|
||||
- Unit tests
|
||||
- System tests
|
||||
- Manual tests
|
||||
@@ -70,7 +71,7 @@ reputation in the futures world.
|
||||
that includes Pedro should be created (unless, as mentioned above, an
|
||||
early PR was already created in the development phase). Whether the PR
|
||||
already existed or not, its description should be changed to start
|
||||
with the "Ready To Merge" statement. This marks the project as ready
|
||||
with the RTM (Ready To Merge) acronym. This marks the project as ready
|
||||
to be merged into master. Once a week, typically on Mondays, Pedro
|
||||
will go through every PR marked as "Ready To Merge", build it, check
|
||||
it out and if everything looks good merge it to master. At the end of
|
||||
@@ -79,6 +80,3 @@ number. This version will be go through some additional testing by
|
||||
Brian and will then be deployed by Karl and Pete to customers
|
||||
throughout the rest of the week.
|
||||
|
||||
## Tools ##
|
||||
* Pre-Merge checklist
|
||||
* Code Review tutorial
|
||||
@@ -1,4 +1,72 @@
|
||||
# Release Notes
|
||||
|
||||
## 1.5.0
|
||||
### User Visible Changes
|
||||
#### User Interface Improvements
|
||||
- Removed security key field from most grids
|
||||
- Removed trade key field from time and sales grid
|
||||
- Fixed the "Since Open" time filter to use the exchange open time rather than 8:30 AM
|
||||
- Fixed the floating order book so that rows are no longer
|
||||
highlighted after an order is modified
|
||||
#### SDK
|
||||
- Improved SDK documentation and usability
|
||||
### Internal Changes
|
||||
- Improved robustness of limit system test
|
||||
- Extensive market data refactoring allow for security definition updates
|
||||
|
||||
|
||||
## 1.4.11
|
||||
### New Features
|
||||
- Floating Order Book
|
||||
- The floating order book provides a quick view of orders occupying a price level in the ladder
|
||||
- Launch the Floating Order Book by middle-clicking a ladder badge
|
||||
- Cancel orders by clicking on the red X in the Cancel column
|
||||
- Quantity and Price modification
|
||||
- Clicking on a row will select it, providing the option for changing your price and quantity
|
||||
- Enter in a new value or use the up/down buttons to set a desired value
|
||||
- Press the green checkbox to finalize your change and update the order
|
||||
### SDK
|
||||
- Improved SDK documentation and usability
|
||||
### User Interface Improvements
|
||||
- The following spreader quoting parameters have been deprecated:
|
||||
- Hidden parameter
|
||||
- Price Type setting of the Conditional parameter
|
||||
### Bug Fixes
|
||||
- Fixed a limit enforcement problem that could lead to 0 sized orders being sent to the exchange
|
||||
|
||||
## 1.4.10
|
||||
### Bug Fixes
|
||||
- Removed duplicate entries in performance log
|
||||
|
||||
## 1.4.9
|
||||
### SDK
|
||||
- Added ticker events to the order API
|
||||
### Performance Improvements
|
||||
- Significantly improved hedging speed
|
||||
### Bug Fixes
|
||||
- Fixed a visual oddity in the order book UI when using High DPI displays
|
||||
|
||||
## 1.4.8
|
||||
### Marketdata
|
||||
- Additional trade details for CME ticker
|
||||
|
||||
## 1.4.7
|
||||
### Bug Fixes
|
||||
- Corrected FIX tag 1028, ManualOrderIndicator, when orders are triggered by algos
|
||||
|
||||
## 1.4.6
|
||||
### Ladder Usability Improvements
|
||||
- The ladder can now be zoomed in/out according to a user's preference
|
||||
- The zoom slider can be enabled from Ladder Form -> Settings button -> Left Pane tab -> check Zoom Slider checkbox
|
||||
- The zoom functionality can also be performed via hotkey and mouse wheel combinations
|
||||
- Zoom In = CTRL + Mouse wheel up
|
||||
- Zoom Out = CTRL + Mouse wheel down
|
||||
- Reset zoom = CTRL + Mouse wheel click or double click on the zoom slider
|
||||
### Exchange simulator
|
||||
- Improved the exchange simulator to track real market data more closely
|
||||
### Bug Fixes
|
||||
- Fixed a UI crash in some cases when attempting to sort the order book by time
|
||||
|
||||
## 1.4.5
|
||||
- Fixed entitlement validation for ICS spreads
|
||||
- Tuned performance logging
|
||||
|
||||
+1
-1
Submodule docker/linux updated: f6d8d59860...8ee9692597.
+1
-1
Submodule externals updated: 69f8639235...c0941c5650.
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"uri": "sftp://bts:BT5u53r@sparkvm:22/home/bts/",
|
||||
"name": "sparkvm"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"files.associations": {
|
||||
"utility": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"list": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"vector": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xstring": "cpp",
|
||||
"system_error": "cpp",
|
||||
"array": "cpp",
|
||||
"string_view": "cpp",
|
||||
"xutility": "cpp",
|
||||
"*.ipp": "cpp"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"files.associations": {
|
||||
"utility": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"list": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"vector": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xstring": "cpp",
|
||||
"system_error": "cpp",
|
||||
"array": "cpp",
|
||||
"string_view": "cpp",
|
||||
"xutility": "cpp",
|
||||
"*.ipp": "cpp",
|
||||
"atomic": "cpp",
|
||||
"hash_map": "cpp",
|
||||
"hash_set": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"bitset": "cpp",
|
||||
"cctype": "cpp",
|
||||
"cfenv": "cpp",
|
||||
"chrono": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"complex": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"csignal": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"forward_list": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"exception": "cpp",
|
||||
"any": "cpp",
|
||||
"optional": "cpp",
|
||||
"fstream": "cpp",
|
||||
"functional": "cpp",
|
||||
"future": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"memory": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"ostream": "cpp",
|
||||
"numeric": "cpp",
|
||||
"ratio": "cpp",
|
||||
"scoped_allocator": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"thread": "cpp",
|
||||
"tuple": "cpp",
|
||||
"typeindex": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"valarray": "cpp",
|
||||
"variant": "cpp"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,9 @@ namespace mbts.ui {
|
||||
}
|
||||
|
||||
private static ImageSource load_resource_and_freeze(string resource_name) {
|
||||
return ((DrawingImage)_resources[resource_name]).frozen();
|
||||
lock (_resources) {
|
||||
return ((DrawingImage)_resources[resource_name]).frozen();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
@@ -152,7 +154,10 @@ namespace mbts.ui {
|
||||
}
|
||||
|
||||
static IEnumerable<string> _get_geometric_path(string geometric_path_key) {
|
||||
object obj = _resources[geometric_path_key];
|
||||
object obj;
|
||||
lock (_resources) {
|
||||
obj = _resources[geometric_path_key];
|
||||
}
|
||||
if (obj is string path) {
|
||||
if (string.IsNullOrEmpty(path)) {
|
||||
Log.Api.error($"Resource lookup of '{geometric_path_key}' did not return a valid string.", nameof(geometric_path_key));
|
||||
|
||||
@@ -210,36 +210,28 @@ namespace mbts.spark {
|
||||
var order = _orders_model.factory.order(security);
|
||||
switch (col) {
|
||||
case MarketViewView.Columns.Bid:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, 0u));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, 0u));
|
||||
break;
|
||||
case MarketViewView.Columns.BidMergedSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_merged_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, bid_merged_size));
|
||||
break;
|
||||
case MarketViewView.Columns.BidDirectSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_direct_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, bid_direct_size));
|
||||
break;
|
||||
case MarketViewView.Columns.BidImpliedSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_implied_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, bid_implied_size));
|
||||
break;
|
||||
case MarketViewView.Columns.Ask:
|
||||
order.Side = Side.Buy;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, 0u));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Buy), ask_price, 0u));
|
||||
break;
|
||||
case MarketViewView.Columns.AskMergedSize:
|
||||
order.Side = Side.Buy;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_merged_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Buy), ask_price, ask_merged_size));
|
||||
break;
|
||||
case MarketViewView.Columns.AskDirectSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_direct_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), ask_price, ask_direct_size));
|
||||
break;
|
||||
case MarketViewView.Columns.AskImpliedSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_implied_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), ask_price, ask_implied_size));
|
||||
break;
|
||||
case MarketViewView.Columns.Last:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, last_price, 0u));
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace bts.shell {
|
||||
public static GridColumnSettings day_change => new GridColumnSettings(col_id(Columns.DayChange), DefaultColumnHeaders.DayChange, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings session_low => new GridColumnSettings(col_id(Columns.SessionLow), DefaultColumnHeaders.SessionLow, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings session_high => new GridColumnSettings(col_id(Columns.SessionHigh), DefaultColumnHeaders.SessionHigh, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -97,8 +97,11 @@ namespace mbts.spark {
|
||||
public IEnumerable<TradeInfo> read_trades() {
|
||||
var copy = _securities;
|
||||
foreach (var tm in _channel.read_trades(ref _cursor)) {
|
||||
if (copy.TryGetValue(tm.Key, out SecurityImmutable sec))
|
||||
yield return new TradeInfo(sec, tm);
|
||||
if (tm != null) {
|
||||
if (copy.TryGetValue(tm.Key, out SecurityImmutable sec)) {
|
||||
yield return new TradeInfo(sec, new TradeMessageImmutable(tm));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ namespace mbts.spark {
|
||||
price_filter.load_state(filters.price);
|
||||
size_filter.load_state(filters.size);
|
||||
aggressor_filter.load_state(filters.aggressor_sides);
|
||||
trade_key_filter.load_state(filters.trade_key);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
}
|
||||
// Columns
|
||||
@@ -47,7 +46,6 @@ namespace mbts.spark {
|
||||
add_column(TimeAndSalesView.Columns.Price, DefaultColumnHeaders.Price, DefaultColumnWidths.Price, nameof(Row.price), price_filter, DataGridFilterKnownTemplates.DoubleMinMax, System.Windows.HorizontalAlignment.Right);
|
||||
add_column(TimeAndSalesView.Columns.Size, DefaultColumnHeaders.Size, DefaultColumnWidths.Size, nameof(Row.size), size_filter, DataGridFilterKnownTemplates.UIntMinMax, System.Windows.HorizontalAlignment.Right);
|
||||
add_column(TimeAndSalesView.Columns.Aggressor, DefaultColumnHeaders.Aggressor, DefaultColumnWidths.Aggressor, nameof(Row.aggressor_side), aggressor_filter, DataGridFilterKnownTemplates.EnumValues);
|
||||
add_column(TimeAndSalesView.Columns.TradeKey, DefaultColumnHeaders.TradeKey, DefaultColumnWidths.TradeKey, nameof(Row.trade_key), trade_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_column(TimeAndSalesView.Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, DefaultColumnWidths.SecurityKey, nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
void add_column(TimeAndSalesView.Columns col_key, string header, double width, string sort_member_path, IDataGridColumnFilter filter, string filter_template, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
|
||||
columns.Add(new TimeAndSalesColumn(
|
||||
@@ -66,7 +64,7 @@ namespace mbts.spark {
|
||||
_resources.AddRange(
|
||||
show_ladder,
|
||||
DataGridFilterHelper.hook_filters(
|
||||
rows_view, time_filter, security_filter, price_filter, size_filter, aggressor_filter, trade_key_filter, security_key_filter),
|
||||
rows_view, time_filter, security_filter, price_filter, size_filter, aggressor_filter, security_key_filter),
|
||||
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager)
|
||||
);
|
||||
rows_view.set_sort_descriptions(startup_data.sorting_criterion, columns);
|
||||
@@ -103,7 +101,6 @@ namespace mbts.spark {
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> size_filter { get; } = new UIntMinMax<Row>(r => r.size, nameof(Row.size));
|
||||
public IDataGridColumnPersistentFilter<Row, AggressorSide[]> aggressor_filter { get; } = DiscreteValuesFilterVM.Create<Row, AggressorSide>(EnumHelper.get_values<AggressorSide>().ToArray(), r => r.aggressor_side, nameof(Row.aggressor_side));
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
|
||||
public IDataGridColumnPersistentFilter<Row, string> trade_key_filter { get; } = new RegexFilterVM<Row>(r => r.trade_key.ToString(), nameof(Row.trade_key));
|
||||
|
||||
FilterStates _get_filter_states() =>
|
||||
new FilterStates {
|
||||
@@ -111,7 +108,6 @@ namespace mbts.spark {
|
||||
security = security_filter.get_state(),
|
||||
price = price_filter.get_state(),
|
||||
size = size_filter.get_state(),
|
||||
trade_key = trade_key_filter.get_state(),
|
||||
security_key = security_key_filter.get_state(),
|
||||
aggressor_sides = aggressor_filter.get_state()
|
||||
};
|
||||
@@ -175,7 +171,6 @@ namespace mbts.spark {
|
||||
case TimeAndSalesView.Columns.Price: return _price_text;
|
||||
case TimeAndSalesView.Columns.Size: return _size_text;
|
||||
case TimeAndSalesView.Columns.Aggressor: return _aggressor_text;
|
||||
case TimeAndSalesView.Columns.TradeKey: return _trade_key_text;
|
||||
case TimeAndSalesView.Columns.SecurityKey: return _security_key_text;
|
||||
default: return null;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace mbts.ui.spark {
|
||||
/// Interaction logic for TimeAndSalesView.xaml
|
||||
/// </summary>
|
||||
partial class TimeAndSalesView : UserControl {
|
||||
public enum Columns { Time, Security, Price, Size, OrderType, TradeKey, SecurityKey, Aggressor }
|
||||
public enum Columns { Time, Security, Price, Size, OrderType, SecurityKey, Aggressor }
|
||||
|
||||
public TimeAndSalesView() =>
|
||||
InitializeComponent();
|
||||
|
||||
@@ -84,8 +84,7 @@ namespace bts.shell {
|
||||
public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings size => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings aggressor => new GridColumnSettings(col_id(Columns.Aggressor), DefaultColumnHeaders.Aggressor, width: DefaultColumnWidths.Aggressor);
|
||||
public static GridColumnSettings trade_key => new GridColumnSettings(col_id(Columns.TradeKey), DefaultColumnHeaders.TradeKey, width: DefaultColumnWidths.TradeKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible:false, width: DefaultColumnWidths.SecurityKey);
|
||||
|
||||
public static IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
get {
|
||||
@@ -100,7 +99,6 @@ namespace bts.shell {
|
||||
yield return price;
|
||||
yield return size;
|
||||
yield return aggressor;
|
||||
yield return trade_key;
|
||||
yield return security_key;
|
||||
}
|
||||
}
|
||||
@@ -117,8 +115,8 @@ namespace bts.shell {
|
||||
public DoubleMinMaxState price;
|
||||
[ProtoMember(4)]
|
||||
public UIntMinMaxState size;
|
||||
[ProtoMember(5)]
|
||||
public string trade_key;
|
||||
// [ProtoMember(5)]
|
||||
// ProtoMember 5 cannot be used further
|
||||
[ProtoMember(6)]
|
||||
public string security_key;
|
||||
[ProtoMember(7)]
|
||||
|
||||
@@ -53,8 +53,8 @@ namespace mbts.spark {
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<uint?> create_synthetic(SecurityImmutable synthetic) {
|
||||
var result = await _service.create_synthetic_async(synthetic);
|
||||
public async Task<uint?> create_synthetic(SecurityImmutable? synthetic) {
|
||||
var result = await _service.create_synthetic_async(synthetic.Value);
|
||||
return _error.handle_error(result, "Error creating synthetic: {0}", r => r.Error, e => e == CreateResult.Types.Error.Ok, r => new uint?(r.Key), () => null).result;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace bts.shell {
|
||||
public static GridColumnSettings description => new GridColumnSettings(col_id(Columns.Description), DefaultColumnHeaders.Description, width: DefaultColumnWidths.Description);
|
||||
public static GridColumnSettings n_legs => new GridColumnSettings(col_id(Columns.n_legs), DefaultColumnHeaders.N_Legs, width: DefaultColumnWidths.N_Legs);
|
||||
public static GridColumnSettings n_working_orders => new GridColumnSettings(col_id(Columns.n_working_orders), DefaultColumnHeaders.N_Working_Orders, width: DefaultColumnWidths.N_Working_Orders);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
|
||||
public static IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
get {
|
||||
|
||||
@@ -205,7 +205,7 @@ namespace mbts.spark {
|
||||
//new EnableHedgeModel(), //TAHA_SPREADER
|
||||
new HedgeRatioModel(),
|
||||
//new MinLeanQtyModel(), //TAHA_SPREADER
|
||||
new QuoteHiddenModel(),
|
||||
//new QuoteHiddenModel(),
|
||||
new MarketDataTypeModel(),
|
||||
new MarketDataCalculationModel(),
|
||||
new QuoteThrottleEnabledModel(quote_throttle_inside_ticks, quote_throttle_outside_ticks),
|
||||
@@ -213,7 +213,7 @@ namespace mbts.spark {
|
||||
quote_throttle_outside_ticks,
|
||||
new ConditionalQuoteEnabledModel(conditional_quoting_ticks, conditional_quoting_price_type),
|
||||
conditional_quoting_ticks,
|
||||
conditional_quoting_price_type,
|
||||
//conditional_quoting_price_type,
|
||||
//new QuoteInsideMarketThrottleEnabledModel(quote_inside_market_throttle_ticks, quote_inside_market_throttle_min_move),
|
||||
//quote_inside_market_throttle_ticks,
|
||||
//quote_inside_market_throttle_min_move,
|
||||
@@ -294,9 +294,9 @@ namespace mbts.spark {
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
static string _deduce_symbol(SecurityImmutable security) {
|
||||
if (!string.IsNullOrWhiteSpace(security.Symbol)) return security.Symbol;
|
||||
if (security.Synthetic.Legs.Count() > 0) return security.Synthetic.Legs.First().Security.Symbol;
|
||||
static string _deduce_symbol(SecurityImmutable? security) {
|
||||
if (!string.IsNullOrWhiteSpace(security?.Symbol)) return security?.Symbol;
|
||||
if (security?.Synthetic.Legs.Count() > 0) return security?.Synthetic.Legs.First().Security.Symbol;
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace mbts.spark {
|
||||
} else return model.on_fill_tuples;
|
||||
}
|
||||
|
||||
static IConnectableObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> _get_observable(IBus bus, OrdersModel model, IObservable<StartupData> state) =>
|
||||
static IConnectableObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> _get_observable(IBus bus, OrdersModel model, IObservable<StartupData> state) =>
|
||||
state.SelectManyInSeries(s => _get_observable(bus, model, s));
|
||||
|
||||
public class Row : SecurityRow, IEquatable<Row>, IRowVM {
|
||||
@@ -186,7 +186,7 @@ namespace mbts.spark {
|
||||
readonly FillImmutable _fill;
|
||||
readonly OrderRecordImmutable _order;
|
||||
|
||||
public Row(FillTuple fill_tuple, OrdersModel model) : base(fill_tuple.security_md.HasValue ? fill_tuple.security_md.Value.security : SecurityDataHelper.Defaults.Security) {
|
||||
public Row(FillTuple fill_tuple, OrdersModel model) : base(fill_tuple.security_md.HasValue ? fill_tuple.security_md.Value.security : SecurityDataHelper.Defaults.SecurityImmutable) {
|
||||
_fill = fill_tuple.fill;
|
||||
_order = fill_tuple.order ?? OrderDataHelper.Defaults.OrderRecordImmutable;
|
||||
_model = model;
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace bts.shell {
|
||||
public static GridColumnSettings fill_kind => new GridColumnSettings(col_id(Columns.FillKind), DefaultColumnHeaders.FillKind, width: DefaultColumnWidths.FillKind);
|
||||
public static GridColumnSettings fill_key => new GridColumnSettings(col_id(Columns.FillKey), DefaultColumnHeaders.FillKey, width: DefaultColumnWidths.FillKey);
|
||||
public static GridColumnSettings order_key => new GridColumnSettings(col_id(Columns.OrderKey), DefaultColumnHeaders.OrderKey, width: DefaultColumnWidths.OrderKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
public static IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
get {
|
||||
yield return new SortingCriteria(fill_time.id, false);
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
<!-- MaxHeight will force only x rows before vertical scrollbar shows up -->
|
||||
<!-- MinWidth will give enough room beyond the last column so that the the verical scrollbar does not eclipse the column content -->
|
||||
<UserControl x:Class="mbts.spark.MiniOrderBookView" x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:uc="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:order="clr-namespace:bts.api.orderman;assembly=spark_api"
|
||||
mc:Ignorable="d"
|
||||
MaxHeight="240"
|
||||
MinWidth="875"
|
||||
d:DesignHeight="600" d:DesignWidth="1080"
|
||||
FocusManager.IsFocusScope="True"
|
||||
Background="{StaticResource CommonBackground}">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<uspcv:TickDisplayConverter x:Key="TickDisplayConverter"/>
|
||||
<uspcv:OrderSubTypeDisplayTextConverter x:Key="OrderSubTypeLongDisplayTextConverter" UseLongName="True" />
|
||||
<DataTemplate x:Key="{x:Static sp:MiniOrderBookView.SubTypeFilterTemplateKey}">
|
||||
<usp:FilterContentControl
|
||||
ApplyCommandParameter="{Binding}"
|
||||
ApplyCommand="{x:Static c:DataGridFilterProperties.CommitCommand}"
|
||||
ClearCommandParameter="{Binding}"
|
||||
ClearCommand="{x:Static c:DataGridFilterProperties.ClearCommand}"
|
||||
>
|
||||
<c:ListBoxEx
|
||||
ItemsSource="{Binding items}"
|
||||
Selection="{Binding selected_items}"
|
||||
SelectionMode="Multiple"
|
||||
ItemContainerStyle="{StaticResource ListBoxItemCheckBoxStyle}"
|
||||
>
|
||||
<c:ListBoxEx.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={StaticResource OrderSubTypeLongDisplayTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
</c:ListBoxEx.ItemTemplate>
|
||||
</c:ListBoxEx>
|
||||
</usp:FilterContentControl>
|
||||
</DataTemplate>
|
||||
|
||||
<Style x:Key="{x:Static sp:MiniOrderBookView.RowStyleKey}" TargetType="ListBoxItem" BasedOn="{StaticResource DIYGridRowStyle}">
|
||||
<Style.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
|
||||
<Condition Binding="{Binding status}" Value="{uc:Enum Working, Type=order:OrderStatus}"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.Setters>
|
||||
<Setter Property="Background" Value="{x:Static Member=working_order_background_brush, MemberType={uc:Nested uc:BTSColors.Orders}}"/>
|
||||
</MultiDataTrigger.Setters>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<DataTemplate x:Key="{x:Static sp:MiniOrderBookView.RowTemplateKey}">
|
||||
<sp:MiniOrderBookRowView/>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<AdornerDecorator>
|
||||
<Border BorderThickness="4" BorderBrush="LightGray">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<c:DIYGrid
|
||||
x:Name="_grid"
|
||||
Grid.Row="0"
|
||||
Columns="{Binding columns}"
|
||||
CurrentRow="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Rows="{Binding rows}"
|
||||
RowStyle="{StaticResource {x:Static sp:MiniOrderBookView.RowStyleKey}}"
|
||||
RowTemplate="{StaticResource {x:Static sp:MiniOrderBookView.RowTemplateKey}}"
|
||||
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
|
||||
RowsHorizontalScrollBarVisibility="Collapsed"
|
||||
RowHeight="20"
|
||||
>
|
||||
<c:DIYGrid.Resources>
|
||||
<ResourceDictionary>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF27C7F7"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="#FFCCEEFB"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF1CC4F7"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF82DFFB"/>
|
||||
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
|
||||
<Style x:Key="{x:Static sp:OrderBookView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Focusable" Value="False"/>
|
||||
<Setter Property="Width" Value="16"/>
|
||||
<Setter Property="Height" Value="16"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Border Background="Transparent" Height="16" Padding="0,5,5,5" Width="16">
|
||||
<Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="{StaticResource TreeViewItem.TreeArrow.Static.Fill}" Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="135" CenterY="3" CenterX="3"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="RenderTransform" TargetName="ExpandPath">
|
||||
<Setter.Value>
|
||||
<RotateTransform Angle="180" CenterY="3" CenterX="3"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True"/>
|
||||
<Condition Property="IsChecked" Value="True"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ToolTip}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="Content" Value="{x:Static sys:String.Empty}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
<Trigger Property="Content" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</c:DIYGrid.Resources>
|
||||
<c:DIYGrid.InputBindings>
|
||||
<!--<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/> -->
|
||||
</c:DIYGrid.InputBindings>
|
||||
<c:DIYGrid.CommandBindings>
|
||||
<CommandBinding
|
||||
Command="{x:Static c:DIYGrid.DefaultRowCommand}"
|
||||
Executed="{x:Static c:DIYGrid.DefaultRowActionExecuted}"
|
||||
CanExecute="{x:Static c:DIYGrid.DefaultRowActionCanExecute}"
|
||||
/>
|
||||
</c:DIYGrid.CommandBindings>
|
||||
</c:DIYGrid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,26 @@
|
||||
using mbts.ui.controls;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal partial class MiniOrderBookView : UserControl {
|
||||
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
|
||||
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
|
||||
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Cancel, EditableQty, EditablePrice }
|
||||
public const string
|
||||
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
|
||||
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
|
||||
RowStyleKey = "RowStyle",
|
||||
RowTemplateKey = "RowTemplateKey",
|
||||
CVRowStyleKey = "CVRowStyle",
|
||||
CVRowTemplateKey = "CVRowTemplateKey";
|
||||
|
||||
public MiniOrderBookView(OrderBookVM vm) {
|
||||
InitializeComponent();
|
||||
DataContext = vm;
|
||||
}
|
||||
}
|
||||
|
||||
class MiniOrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookView.Columns> {
|
||||
public MiniOrderBookRowView() : base() { }
|
||||
}
|
||||
}
|
||||
@@ -194,6 +194,10 @@ namespace mbts.spark {
|
||||
// quantity
|
||||
Add(qty_column = new SizeColumn(
|
||||
new UIntMinMax<OrderRecordTuple>(ort => ort.order.quantity(), nameof(OrderBookRowVM.quantity))));
|
||||
// edit quantity column
|
||||
// not usually part of the main order book, but it's useful to add it
|
||||
// rather than debugging the popup window
|
||||
//Add(edit_qty_column = new EditableQtyColumn());
|
||||
// filled quantity
|
||||
Add(filled_qty_column = new FilledColumn(
|
||||
new UIntMinMax<OrderRecordTuple>(ort => ort.order.filled(), nameof(OrderBookRowVM.filled_quantity))));
|
||||
@@ -209,6 +213,10 @@ namespace mbts.spark {
|
||||
nameof(OrderBookRowVM.price)),
|
||||
DataGridFilterKnownTemplates.DoubleMinMax,
|
||||
_wrap_comparer(r => r.price)));
|
||||
// edit price column
|
||||
// not usually part of the main order book, but it's useful to add it
|
||||
// rather than debugging the popup window
|
||||
//Add(edit_price_column = new OrderBookVM.EditablePriceColumn());
|
||||
// avg filled price
|
||||
Add(avg_price_column = new SimpleTextColumn<DoubleMinMaxState>(
|
||||
Columns.AvgFilled,
|
||||
@@ -291,6 +299,7 @@ namespace mbts.spark {
|
||||
|
||||
public event Action filter_changed;
|
||||
|
||||
public CancelColumn cancel_column { get; }
|
||||
public SecurityColumn security_column { get; }
|
||||
public SimpleTextColumn<string[]> symbol_column { get; }
|
||||
public SimpleTextColumn<TimeFilterTuple> time_created_column { get; }
|
||||
@@ -303,7 +312,9 @@ namespace mbts.spark {
|
||||
public SimpleTextColumn<OrderSubType[]> sub_type_column { get; }
|
||||
public SideColumn side_column { get; }
|
||||
public SizeColumn qty_column { get; }
|
||||
public EditableQtyColumn edit_qty_column { get; }
|
||||
public SimpleTextColumn<DoubleMinMaxState> price_column { get; }
|
||||
public EditablePriceColumn edit_price_column { get; }
|
||||
public FilledColumn filled_qty_column { get; }
|
||||
public SimpleTextColumn<DoubleMinMaxState> last_price_column { get; }
|
||||
public SimpleTextColumn<DoubleMinMaxState> avg_price_column { get; }
|
||||
@@ -509,7 +520,7 @@ namespace mbts.spark {
|
||||
: base(key, header, get_text, sort_prop, width, filter, filter_template, comparer) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, _get_text(row), position, RowHeight,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, _get_text(row), position, row_height,
|
||||
font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
|
||||
}
|
||||
|
||||
@@ -519,7 +530,7 @@ namespace mbts.spark {
|
||||
: base(Columns.Side, "B/S", r => r.side.ToString(), nameof(OrderBookRowVM.side), DefaultColumnWidths.Side, filter, DataGridFilterKnownTemplates.EnumValues, _wrap_comparer(r => r.side)) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.side.ToString(), position, RowHeight,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.side.ToString(), position, row_height,
|
||||
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
|
||||
}
|
||||
|
||||
@@ -530,7 +541,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.quantity.ToString(), position, RowHeight,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.quantity.ToString(), position, row_height,
|
||||
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
|
||||
}
|
||||
|
||||
@@ -541,7 +552,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.filled_quantity.ToString(), position, RowHeight,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.filled_quantity.ToString(), position, row_height,
|
||||
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
|
||||
}
|
||||
|
||||
@@ -640,6 +651,8 @@ namespace mbts.spark {
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
grid.Children.Add(toggle_button);
|
||||
grid.Children.Add(security_view);
|
||||
grid.HorizontalAlignment = HorizontalAlignment.Center;
|
||||
grid.VerticalAlignment = VerticalAlignment.Center;
|
||||
//
|
||||
var border = new Border { Child = grid };
|
||||
CellRender.set_background_animation_bindings(border, nameof(OrderBookRowVM.security));
|
||||
@@ -651,6 +664,88 @@ namespace mbts.spark {
|
||||
base.OnSortDirectionChanged(new_value);
|
||||
}
|
||||
}
|
||||
|
||||
public class CancelColumn : FrameworkElementColumnBase<Columns> {
|
||||
readonly BindingBase _command_binding;
|
||||
|
||||
public CancelColumn(string command_property) : base(Columns.Cancel) {
|
||||
id = key.ToString();
|
||||
Header = DefaultColumnHeaders.Cancel;
|
||||
_command_binding = new Binding(command_property);
|
||||
}
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var grid = new Grid() {
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
};
|
||||
var l1 = new System.Windows.Shapes.Line() {
|
||||
StrokeThickness = 1,
|
||||
Stroke = Brushes.White,
|
||||
X1 = 0.5,
|
||||
Y1 = 0.5,
|
||||
X2 = 7,
|
||||
Y2 = 7
|
||||
};
|
||||
var l2 = new System.Windows.Shapes.Line() {
|
||||
StrokeThickness = 1,
|
||||
Stroke = Brushes.White,
|
||||
X1 = 0.5,
|
||||
Y1 = 7,
|
||||
X2 = 7,
|
||||
Y2 = 0.5
|
||||
};
|
||||
grid.Children.Add(l1);
|
||||
grid.Children.Add(l2);
|
||||
|
||||
var button = new Button() {
|
||||
Content = grid,
|
||||
Background = Brushes.Red,
|
||||
Foreground = Brushes.White,
|
||||
Margin = new Thickness(2),
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Height = 14,
|
||||
Width = 14
|
||||
};
|
||||
|
||||
button.SetBinding(Button.CommandProperty, _command_binding);
|
||||
return button;
|
||||
}
|
||||
}
|
||||
|
||||
public class EditableQtyColumn:FrameworkElementColumnBase<Columns> {
|
||||
|
||||
public EditableQtyColumn() : base(Columns.EditableQty) {
|
||||
id = key.ToString();
|
||||
Header = "Qty";
|
||||
Width = 64;
|
||||
}
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var tick_up_down = new TickUpDownNullableUIntCommit();
|
||||
tick_up_down.SetBinding(TickUpDownNullableUIntCommit.SourceValueProperty, nameof(OrderBookRowVM.prospective_quantity));
|
||||
tick_up_down.SetBinding(TickUpDownNullableUIntCommit.OnCommitProperty, nameof(OrderBookRowVM.modify_order_quantity));
|
||||
return tick_up_down;
|
||||
}
|
||||
}
|
||||
|
||||
public class EditablePriceColumn : FrameworkElementColumnBase<Columns> {
|
||||
|
||||
public EditablePriceColumn() : base(Columns.EditablePrice) {
|
||||
id = key.ToString();
|
||||
Header = "Price";
|
||||
Width = 96;
|
||||
}
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var price_up_down = new PriceEntryCommitControl();
|
||||
price_up_down.SetBinding(PriceEntryCommitControl.SourceValueProperty, nameof(OrderBookRowVM.prospective_price));
|
||||
price_up_down.SetBinding(PriceEntryCommitControl.SourceContractSpecProperty, nameof(OrderBookRowVM.contract_spec));
|
||||
price_up_down.SetBinding(PriceEntryCommitControl.OnCommitProperty, nameof(OrderBookRowVM.modify_order_price));
|
||||
return price_up_down;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
static class RenderUtils {
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace mbts.spark {
|
||||
class OrderBookRowCollection : ObservableCollection<OrderBookRowVM> {
|
||||
static readonly IEqualityComparer<INode<OrderBookRowVM>> _node_equality_comparer = EqualityComparer.Create<INode<OrderBookRowVM>, uint>(n => n.value.order_key);
|
||||
readonly Func<OrderRecordTuple, OrderBookRowVM> _row_factory;
|
||||
/// Critical: Any and all row mutations <see cref="OrderBookRowVM"/> must be executed through <see cref="SortedTree{T, TKey}.TryReplace(TKey, Func{T, T})"/>.
|
||||
/// Mutations can change the sort-position of a row, and <see cref="_tree"/> will be broken if its elements aren't properly sorted (it relies heavily on binary-search logic).
|
||||
SortedTree<OrderBookRowVM, uint> _tree;
|
||||
IComparer<OrderBookRowVM> _comparer;
|
||||
Func<OrderRecordTuple, bool> _filter;
|
||||
|
||||
@@ -6,6 +6,9 @@ using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
@@ -13,15 +16,34 @@ namespace mbts.spark {
|
||||
const string _unknown_security_price_format_string = "0.#########";
|
||||
const int _unknown_security_price_num_fractional_digits = 9;
|
||||
readonly OrderBookVM _owner;
|
||||
IRaiseCanExecuteChangedCommand _show_order_modify_cmd, _toggle_expanded_cmd;
|
||||
bool _is_parent, _is_expanded;
|
||||
IRaiseCanExecuteChangedCommand _show_order_modify_cmd, _toggle_expanded_cmd, _cancel_cmd, _modify_order_quantity, _modify_order_price;
|
||||
bool _is_parent, _is_expanded, _is_selected;
|
||||
int _depth_level;
|
||||
SecurityMD? _security_md => order_tuple.security_md;
|
||||
|
||||
public OrderBookRowVM(OrderBookVM owner, OrderRecordTuple order_tuple) {
|
||||
|
||||
// for mini-ob
|
||||
uint _prospective_quantity;
|
||||
double _prospective_price;
|
||||
|
||||
|
||||
public OrderBookRowVM(OrderBookVM owner, OrderRecordTuple order_tuple, bool is_expanded) {
|
||||
_owner = owner;
|
||||
this.order_tuple = order_tuple;
|
||||
is_visible = !is_child;
|
||||
_is_expanded = is_expanded;
|
||||
prospective_quantity = this.quantity;
|
||||
prospective_price = this.price;
|
||||
this.invalidated += OrderBookRowVM_invalidated;
|
||||
}
|
||||
|
||||
private void OrderBookRowVM_invalidated() {
|
||||
modify_order_quantity?.TryRaiseCanExecuteChanged();
|
||||
modify_order_price?.TryRaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
this.invalidated -= OrderBookRowVM_invalidated;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public event Action invalidated;
|
||||
@@ -34,6 +56,7 @@ namespace mbts.spark {
|
||||
public uint parent_order_key => order.ParentKey;
|
||||
public uint security_key => order.security_key();
|
||||
public SecurityImmutable? security => _security_md.security();
|
||||
public ContractSpecificationImmutable? contract_spec => security.spec_or_default();
|
||||
public string security_text => security.display_name();
|
||||
public Venue exchange => _security_md.exchange();
|
||||
public string symbol => _security_md.symbol();
|
||||
@@ -72,6 +95,29 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
public int depth_level { get => _depth_level; set => Set(ref _depth_level, value); }
|
||||
public bool is_selected {
|
||||
get => _is_selected;
|
||||
set => Set(ref _is_selected, value);
|
||||
}
|
||||
public uint prospective_quantity {
|
||||
get => _prospective_quantity;
|
||||
set {
|
||||
if (Set(ref _prospective_quantity, value)) {
|
||||
RaisePropertyChanged(nameof(is_qty_dirty));
|
||||
}
|
||||
}
|
||||
}
|
||||
public double prospective_price {
|
||||
get => _prospective_price;
|
||||
set {
|
||||
if (Set(ref _prospective_price, value)) {
|
||||
RaisePropertyChanged(nameof(is_price_dirty));
|
||||
}
|
||||
}
|
||||
}
|
||||
public bool is_qty_dirty => this.prospective_quantity != this.quantity;
|
||||
public bool is_price_dirty => this.prospective_price != this.price;
|
||||
|
||||
// Commands
|
||||
public ICommand toggle_command => _toggle_expanded_cmd ?? (
|
||||
_toggle_expanded_cmd = new DelegateCommand(() => is_expanded = !is_expanded));
|
||||
@@ -79,6 +125,38 @@ namespace mbts.spark {
|
||||
ICommand DIYGrid.IActionableRowVM.default_command => _show_order_modify_cmd ??
|
||||
(_show_order_modify_cmd = new DelegateCommand(() => _owner.show_order_modify(this), () => status == OrderStatus.Working));
|
||||
|
||||
public ICommand cancel_command => _cancel_cmd ??
|
||||
(_cancel_cmd = new DelegateCommand(() => _owner.cancel_order(this), () => status == OrderStatus.Working));
|
||||
|
||||
public ICommand modify_order_quantity => _modify_order_quantity ??
|
||||
(_modify_order_quantity = new DelegateCommand(
|
||||
(desired_quantity) => {
|
||||
_owner.modify_order_quantity(this, (uint)desired_quantity);
|
||||
_unselect_me();
|
||||
},
|
||||
(desired_quantity) =>
|
||||
(desired_quantity != null && this.status == OrderStatus.Working && (uint)desired_quantity != this.quantity)
|
||||
|| is_qty_dirty
|
||||
)
|
||||
);
|
||||
|
||||
public ICommand modify_order_price => _modify_order_price ??
|
||||
(_modify_order_price = new DelegateCommand(
|
||||
(desired_price) => {
|
||||
_owner.modify_order_price(this, (double)desired_price);
|
||||
_unselect_me();
|
||||
},
|
||||
(desired_price) =>
|
||||
(desired_price != null && this.status == OrderStatus.Working && (double)desired_price != this.price)
|
||||
|| is_price_dirty
|
||||
)
|
||||
);
|
||||
|
||||
private void _unselect_me() {
|
||||
this.is_selected = false;
|
||||
_owner.current_row = null;
|
||||
}
|
||||
|
||||
// These fns present a way for the orderbook to avoid noisy, intermittent collection changes during "Expand/Collapse All" ops,
|
||||
// when incremental changes are ignored in favor of a full reset, after 'is_expanded' is updated for all rows.
|
||||
internal void expand_internal() => Set(ref _is_expanded, true);
|
||||
@@ -103,6 +181,14 @@ namespace mbts.spark {
|
||||
// however, the order-type and security cells DO use databindings - if these cells have changed, the PropertyChanged event must be raised regardless of whether or not invalidated is raised
|
||||
if (old_order.order_type() != order.order_type())
|
||||
RaisePropertyChanged(nameof(order_type), nameof(order_metadata));
|
||||
if (old_order.quantity() != order.quantity()) {
|
||||
RaisePropertyChanged(nameof(quantity));
|
||||
RaisePropertyChanged(nameof(is_qty_dirty));
|
||||
}
|
||||
if (old_order.price() != order.price()) {
|
||||
RaisePropertyChanged(nameof(price));
|
||||
RaisePropertyChanged(nameof(is_price_dirty));
|
||||
}
|
||||
if (!had_security && _security_md.HasValue) {
|
||||
RaisePropertyChanged(nameof(security), nameof(security_text));
|
||||
// need to raise invalidated to refresh the price cells (the security.ContractSpec determines how they are formatted - Ticks!)
|
||||
|
||||
@@ -16,6 +16,7 @@ using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using static bts.shell.OrderBookWindowFactory;
|
||||
@@ -33,44 +34,84 @@ namespace mbts.spark {
|
||||
readonly IWindowSettingsManager<OrderBookGridSettings> _settings_manager;
|
||||
readonly List<IDisposable> _resources;
|
||||
OrderBookStartupData _state;
|
||||
bool _is_row_collection_populated;
|
||||
|
||||
public OrderBookVM(IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model, IWindowSettingsManager<OrderBookGridSettings> settings_manager) {
|
||||
public OrderBookVM(IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model, IWindowSettingsManager<OrderBookGridSettings> settings_manager, bool rows_are_initially_expanded = false) {
|
||||
_bus = bus;
|
||||
_model = model;
|
||||
_settings_manager = settings_manager;
|
||||
_columns = new OrderBookColumnCollection(bus, model, model.on_order_securities.Select(u => (u.added.Values.Select(smd => smd.security), u.removed.Values.Select(smd => smd.security))));
|
||||
_row_collection = new OrderBookRowCollection(ort => new OrderBookRowVM(this, ort), _columns.get_sorting_data().comparer) { filter = _columns.get_filter() };
|
||||
// we use an ObservableAppendCollection b/c the mini-ob needs a cancel column at the front
|
||||
columns = new ObservableAppendCollection<DIYGrid.ColumnBase>(_columns);
|
||||
_row_collection = new OrderBookRowCollection(ort => new OrderBookRowVM(this, ort, rows_are_initially_expanded), _columns.get_sorting_data().comparer) { filter = _columns.get_filter() };
|
||||
_row_collection.CollectionChanged += _row_collection_CollectionChanged;
|
||||
_current_row = backing<OrderBookRowVM>(nameof(current_row));
|
||||
_columns.filter_changed += on_filter_changed;
|
||||
collapse_all_rows_command = new DelegateCommand(_row_collection.collapse_all);
|
||||
expand_all_rows_command = new DelegateCommand(_row_collection.expand_all);
|
||||
grid_settings_manager = _create_grid_settings_manager(owner_title, bus, notification_service, model, rows, columns);
|
||||
grid_settings_manager = _create_grid_settings_manager(owner_title, bus, notification_service, model, rows, core_columns);
|
||||
_resources = new List<IDisposable> {
|
||||
model.on_order_tuples_table.OnDispatcher().Subscribe(_row_collection.update),
|
||||
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
|
||||
grid_settings_manager,
|
||||
Disposable.Create(() => {
|
||||
_columns.filter_changed -= on_filter_changed;
|
||||
_row_collection.CollectionChanged -= _row_collection_CollectionChanged;
|
||||
Disposable.dispose(_row_collection);
|
||||
}),
|
||||
_columns
|
||||
};
|
||||
if (settings_manager != null) {
|
||||
_resources.Add(settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager));
|
||||
}
|
||||
void on_filter_changed() => _row_collection.filter = _columns.get_filter();
|
||||
}
|
||||
|
||||
private void _row_collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
|
||||
is_row_collection_populated = _row_collection.Count != 0;
|
||||
}
|
||||
|
||||
public event Action<OrderRecordImmutable?> selected_order_changed;
|
||||
public event Action<bool> OnDisposed;
|
||||
|
||||
public ObservableCollection<DIYGrid.ColumnBase> columns => _columns;
|
||||
public ObservableAppendCollection<DIYGrid.ColumnBase> columns { get; }
|
||||
public ObservableCollection<DIYGrid.ColumnBase> core_columns => _columns;
|
||||
public ObservableCollection<OrderBookRowVM> rows => _row_collection;
|
||||
public OrderBookRowVM current_row { get => _current_row.Value; set => _current_row.Value = value ?? _selected_rows.FirstOrDefault(); }
|
||||
public OrderBookRowVM current_row {
|
||||
get => _current_row.Value;
|
||||
set {
|
||||
_current_row.Value = value;
|
||||
// this is so that when the current_row is set to null from
|
||||
// the row_vm, we propagate the change up to the OrderBookWindow
|
||||
// so that the cancel and modify commands can_execute is updated.
|
||||
// Note that this is really only for if/when the edit_xxx columns
|
||||
// from the mini-ob are present in regular orderbook, else this is
|
||||
// a no-op.
|
||||
selected_order_changed?.Invoke(current_order);
|
||||
}
|
||||
}
|
||||
public OrderRecordImmutable? current_order => current_row?.order;
|
||||
public IEnumerable<OrderRecordImmutable> selected_orders => _selected_rows.Select(r => r.order);
|
||||
public _GridSettingsManager grid_settings_manager { get; }
|
||||
public ICommand collapse_all_rows_command { get; }
|
||||
public ICommand expand_all_rows_command { get; }
|
||||
|
||||
public bool is_row_collection_populated {
|
||||
get => _is_row_collection_populated;
|
||||
set => Set(ref _is_row_collection_populated, value);
|
||||
}
|
||||
|
||||
public void row_selection_changed(SelectionChangedEventArgs e) {
|
||||
if (e.RemovedItems != null) {
|
||||
foreach (var row_vm in e.RemovedItems.Cast<OrderBookRowVM>()) {
|
||||
row_vm.is_selected = false;
|
||||
}
|
||||
}
|
||||
if (e.AddedItems != null) {
|
||||
foreach (var row_vm in e.AddedItems.Cast<OrderBookRowVM>()) {
|
||||
row_vm.is_selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
RowSelectionHelper.apply_changes(_selected_rows, e);
|
||||
selected_order_changed?.Invoke(current_order);
|
||||
}
|
||||
@@ -131,6 +172,15 @@ namespace mbts.spark {
|
||||
internal void show_order_modify(OrderBookRowVM row) =>
|
||||
_bus.post(SparkTags.ShowModifyOrderTag, row.order_key);
|
||||
|
||||
internal void cancel_order(OrderBookRowVM row) =>
|
||||
_bus.post(SparkTags.CancelOrders, new List<uint> { row.order_key });
|
||||
|
||||
internal void modify_order_quantity(OrderBookRowVM row, uint size) =>
|
||||
_bus.post(SparkTags.UpdateOrder, (row.order_key, row.price, size));
|
||||
|
||||
internal void modify_order_price(OrderBookRowVM row, double price) =>
|
||||
_bus.post(SparkTags.UpdateOrder, (row.order_key, price, row.quantity));
|
||||
|
||||
internal void on_expanded_changed(OrderBookRowVM row) {
|
||||
using (_row_collection.defer_refresh(true)) {
|
||||
if (row.is_expanded) {
|
||||
@@ -187,7 +237,8 @@ namespace mbts.spark {
|
||||
return pvm;
|
||||
},
|
||||
(gen, anims) => new OrderBookGridSettings(gen, anims.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_builder, a.cell_animations, a.row_animations, a.sound_notifications))),
|
||||
notification_service.data_cache, col_settings_flags);
|
||||
(notification_service == null) ? null : notification_service.data_cache,
|
||||
col_settings_flags);
|
||||
}
|
||||
|
||||
static OrderRecordTuplePredicate.PredicateTypeOneofCase _corresponding_predicate_type(Columns col) {
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace mbts.spark {
|
||||
internal partial class OrderBookView : UserControl {
|
||||
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
|
||||
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
|
||||
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey }
|
||||
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Cancel, EditableQty, EditablePrice }
|
||||
public const string
|
||||
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
|
||||
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
|
||||
@@ -14,8 +14,6 @@ namespace mbts.spark {
|
||||
CVRowStyleKey = "CVRowStyle",
|
||||
CVRowTemplateKey = "CVRowTemplateKey";
|
||||
|
||||
public const double RowHeight = 17;
|
||||
|
||||
public OrderBookView(OrderBookVM vm) {
|
||||
InitializeComponent();
|
||||
DataContext = vm;
|
||||
@@ -23,6 +21,6 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
class OrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookView.Columns> {
|
||||
public OrderBookRowView() : base(OrderBookView.RowHeight) { }
|
||||
public OrderBookRowView() : base() { }
|
||||
}
|
||||
}
|
||||
@@ -131,7 +131,7 @@ namespace mbts.spark {
|
||||
return _vm.grid_settings_manager.create_vm(
|
||||
// we need to get the most up to date GridSettings when launching b/c users may
|
||||
// have changed some settings on the grid directly, like column widths.
|
||||
s.general.with(_vm.columns.Select(c => c.create())),
|
||||
s.general.with(_vm.core_columns.Select(c => c.create())),
|
||||
this.observe_title()
|
||||
);
|
||||
}
|
||||
@@ -167,11 +167,9 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
private void _vm_selected_order_changed(OrderRecordImmutable? e) {
|
||||
if (e.HasValue) {
|
||||
_modify_order_command.raiseCanExecuteChanged();
|
||||
_cancel_selected_orders_command.raiseCanExecuteChanged();
|
||||
_recreate_selected_orders_command.raiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,8 @@ namespace bts.shell {
|
||||
public static OrderBookGridSettings DefaultGrid => new OrderBookGridSettings(_default_general_grid);
|
||||
|
||||
static GridSettings _default_general_grid => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion);
|
||||
public static GridSettings default_mini_grid => GridSettings.Default.with(DefaultColumns.mini, DefaultColumns.sorting_criterion);
|
||||
|
||||
|
||||
static class DefaultColumns {
|
||||
public static GridColumnSettings security => new GridColumnSettings(Columns.Security.ToString(), DIYGrid.DefaultColumnHeaders.Security, width: DIYGrid.DefaultColumnWidths.Security);
|
||||
@@ -110,7 +112,7 @@ namespace bts.shell {
|
||||
public static GridColumnSettings last_price => new GridColumnSettings(Columns.LastPrice.ToString(), "Last Filled", width: DIYGrid.DefaultColumnWidths.LastPrice, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings status => new GridColumnSettings(Columns.Status.ToString(), "Status", width: 80);
|
||||
public static GridColumnSettings last_message => new GridColumnSettings(Columns.LastMessage.ToString(), "Last Message", width: DIYGrid.DefaultColumnWidths.LastMessage);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, width: DIYGrid.DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, is_visible: false, width: DIYGrid.DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings order_key => new GridColumnSettings(Columns.OrderKey.ToString(), DIYGrid.DefaultColumnHeaders.OrderKey, width: DIYGrid.DefaultColumnWidths.OrderKey);
|
||||
public static GridColumnSettings parent_key => new GridColumnSettings(Columns.ParentKey.ToString(), "Parent Key", width: DIYGrid.DefaultColumnWidths.ParentKey);
|
||||
|
||||
@@ -145,7 +147,23 @@ namespace bts.shell {
|
||||
yield return parent_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<GridColumnSettings> _mini_mode_columns_to_hide = new HashSet<GridColumnSettings> {
|
||||
time_created, exchange, sub_type, quantity, price, avg_price, last_price, status, last_message, security_key, order_key, parent_key
|
||||
};
|
||||
|
||||
public static IEnumerable<GridColumnSettings> mini {
|
||||
get {
|
||||
foreach (var col_setting in all) {
|
||||
if (_mini_mode_columns_to_hide.Contains(col_setting)) {
|
||||
yield return col_setting.with(is_visible: false);
|
||||
} else {
|
||||
yield return col_setting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace mbts.ui.spark {
|
||||
public DataTemplate JoinOrderTemplate { get; set; }
|
||||
public DataTemplate TopOrderTemplate { get; set; }
|
||||
public DataTemplate CoverOrderTemplate { get; set; }
|
||||
public DataTemplate OCOOrderTemplate { get; set; }
|
||||
public DataTemplate PricerOrderTemplate { get; set; }
|
||||
public DataTemplate OffsetOrderTemplate { get; set; }
|
||||
public DataTemplate TriggerOrderTemplate { get; set; }
|
||||
public DataTemplate IcebergOrderTemplate { get; set; }
|
||||
@@ -43,6 +45,8 @@ namespace mbts.ui.spark {
|
||||
case CustomOrderDefinition.CustomOneofCase.Join: return JoinOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Top: return TopOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Cover: return CoverOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Oco : return OCOOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Pricer: return PricerOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Offset: return OffsetOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Trigger: return TriggerOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Iceberg: return IcebergOrderTemplate;
|
||||
|
||||
@@ -139,13 +139,13 @@ namespace mbts.spark {
|
||||
tif => time_in_forces.Contains(tif));
|
||||
_dispose = Disposable.Create(() => _order_type.Changed -= on_selected_order_type_changed);
|
||||
// local methods
|
||||
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd, string id, string display_name) {
|
||||
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable? cd, string id, string display_name) {
|
||||
IOrderTypeVM vm = null;
|
||||
if(cd.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic)
|
||||
vm = OrderTypeVMFactory.create(cd.CustomCase, id, display_name, model, security_md_source, cd.Dynamic.DynamicOrderType);
|
||||
if(cd?.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic)
|
||||
vm = OrderTypeVMFactory.create(cd.Value.CustomCase, id, display_name, model, security_md_source, cd.Value.Dynamic.DynamicOrderType);
|
||||
else
|
||||
vm = OrderTypeVMFactory.create(cd.CustomCase, id, display_name, model, security_md_source);
|
||||
vm.load_from(cd);
|
||||
vm = OrderTypeVMFactory.create(cd.Value.CustomCase, id, display_name, model, security_md_source);
|
||||
vm.load_from(cd.Value);
|
||||
return vm;
|
||||
}
|
||||
InlineUserProperty<IOrderTypeVM> get_order_type_vm_prop(InlineOrderProperties p) {
|
||||
|
||||
@@ -126,6 +126,8 @@ namespace mbts.spark
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public bool has_child => _order_def_tags.Any();
|
||||
|
||||
public IOrderTypeVM child_order
|
||||
{
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.ui;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using static bts.api.orderman.CustomOrderDefinition;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
static partial class OrderTypeVMFactory
|
||||
{
|
||||
class InlineOCOOrderProperties
|
||||
{
|
||||
public InlineOCOOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
|
||||
var othertype = CustomOneofCase.Limit;
|
||||
this.selected_other_order_type = new InlineUserProperty<OrderTemplateHeader>(new OrderTemplateHeader(othertype), true);
|
||||
this.other_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
|
||||
other_ticks = new InlineUserProperty<int>(1, false);
|
||||
other_side = new InlineUserProperty<bts.api.RelativeSide>(bts.api.RelativeSide.Same, false);
|
||||
}
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_other_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> other_order_types;
|
||||
|
||||
public readonly InlineUserProperty<int> other_ticks;
|
||||
public readonly InlineUserProperty<bts.api.RelativeSide> other_side;
|
||||
|
||||
}
|
||||
|
||||
class OCOOrderVM : OrderTypeVMBase<OCOOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineOCOOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _other_order_choices;
|
||||
IOrderTypeVM _other_order;
|
||||
IDisposable _other_order_errors_sub;
|
||||
|
||||
public int other_ticks { get; set; } = 1;
|
||||
public bts.api.RelativeSide other_side { get; set; } = bts.api.RelativeSide.Same;
|
||||
|
||||
public OCOOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineOCOOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name)
|
||||
{ }
|
||||
|
||||
public OCOOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineOCOOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
{
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p =>
|
||||
{
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
{
|
||||
_order_types.SetItems(p.child_order_types.value);
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
if (_other_order_choices != null && (_other_order == null || p.selected_other_order_type.force_override))
|
||||
{
|
||||
_select_other_order(p.selected_other_order_type.value.key.type);
|
||||
}
|
||||
if (p.other_ticks.force_override)
|
||||
{
|
||||
other_ticks = p.other_ticks.value;
|
||||
RaisePropertyChanged(nameof(other_ticks));
|
||||
}
|
||||
if (p.other_side.force_override)
|
||||
{
|
||||
other_side = p.other_side.value;
|
||||
RaisePropertyChanged(nameof(other_side));
|
||||
}
|
||||
_inline_props = p;
|
||||
_validate();
|
||||
})
|
||||
);
|
||||
_validate();
|
||||
}
|
||||
|
||||
public override void assign_to(CustomOrderDefinition value)
|
||||
{
|
||||
if (_child_order == null)
|
||||
_select_child_order(model.factory.default_order_type(_security.Value, true));
|
||||
if (_other_order == null )
|
||||
_select_other_order(model.factory.default_order_type(_security.Value, true));
|
||||
_validate();
|
||||
if (!HasErrors)
|
||||
{
|
||||
var i = factory.oco_order();
|
||||
i.OtherTicks = other_ticks;
|
||||
i.OtherSide = other_side;
|
||||
child_order.assign_to(i.Child);
|
||||
if (other_order != null)
|
||||
other_order.assign_to(i.Other);
|
||||
value.Oco = i;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<bts.api.RelativeSide> other_side_types => EnumHelper.get_values_except<bts.api.RelativeSide>(bts.api.RelativeSide.Undefined);
|
||||
|
||||
public ICollection<IOrderTypeVM> child_order_types
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<IOrderTypeVM> other_order_types
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_other_order_choices == null)
|
||||
_other_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
return _other_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public IOrderTypeVM child_order
|
||||
{
|
||||
get
|
||||
{
|
||||
init();
|
||||
return _child_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
Disposable.dispose(ref _child_order_errors_sub);
|
||||
_child_order = value;
|
||||
_child_order?.init();
|
||||
if (_child_order is INotifyDataErrorInfo ndei)
|
||||
{
|
||||
ndei.ErrorsChanged += on_errors_changed;
|
||||
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
|
||||
_validate();
|
||||
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
|
||||
}
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
}
|
||||
|
||||
public IOrderTypeVM other_order
|
||||
{
|
||||
get
|
||||
{
|
||||
init();
|
||||
return _other_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
Disposable.dispose(ref _other_order_errors_sub);
|
||||
_other_order = value;
|
||||
_other_order?.init();
|
||||
if (_other_order is INotifyDataErrorInfo ndei)
|
||||
{
|
||||
ndei.ErrorsChanged += on_errors_changed;
|
||||
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
|
||||
_validate();
|
||||
_other_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
|
||||
}
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(other_order));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposable.dispose(ref _dispose);
|
||||
if (_child_order_choices != null)
|
||||
_child_order_choices.OfType<IDisposable>().DisposeAll();
|
||||
if (_other_order_choices != null)
|
||||
_other_order_choices.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
|
||||
public override void init()
|
||||
{
|
||||
if (_child_order == null && _security.HasValue)
|
||||
_select_child_order();
|
||||
if (_other_order == null && _security.HasValue)
|
||||
_select_other_order();
|
||||
_validate();
|
||||
}
|
||||
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
void _select_child_order(CustomOneofCase ot)
|
||||
{
|
||||
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
|
||||
if (_child_order == null)
|
||||
_child_order = child_order_types.FirstOrDefault();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
|
||||
void _select_other_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_other_order_type.has_value ?
|
||||
_inline_props.selected_other_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_other_order(ot);
|
||||
}
|
||||
|
||||
void _select_other_order(CustomOneofCase ot)
|
||||
{
|
||||
_other_order = other_order_types.FirstOrDefault(vm => vm.order_type == ot);
|
||||
if (_other_order == null)
|
||||
_other_order = other_order_types.FirstOrDefault();
|
||||
RaisePropertyChanged(nameof(other_order));
|
||||
}
|
||||
|
||||
|
||||
void _validate()
|
||||
{
|
||||
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
|
||||
set_error(nameof(child_order), string.Empty, true);
|
||||
else clear_error(nameof(child_order));
|
||||
|
||||
if ((_other_order == null ) || (_other_order is INotifyDataErrorInfo hdei && hdei.HasErrors))
|
||||
set_error(nameof(other_order), string.Empty, true);
|
||||
else clear_error(nameof(other_order));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,12 @@ namespace mbts.spark {
|
||||
public static IOrderTypeVM stop(OrdersModel model, string id, string display_name) => new StopOrderVM(model, id, display_name);
|
||||
public static IOrderTypeVM stop_limit(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new StopLimitOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TimeBoxOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM tick(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TickOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new LeanOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new JoinOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TopOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM cover(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new CoverOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM oco(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new OCOOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM pricer(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new PricerOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM offset(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new OffsetOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM trigger(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TriggerOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new IcebergOrderVM(model, security_md_source, id, display_name);
|
||||
@@ -34,11 +35,12 @@ namespace mbts.spark {
|
||||
case CustomOneofCase.Stop: return stop(model, id, display_name);
|
||||
case CustomOneofCase.StopLimit: return stop_limit(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.TimeBox: return time_box(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Tick: return tick(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Lean: return lean(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Join: return join(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Top: return top(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Cover: return cover(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Oco : return oco(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Pricer: return pricer(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Offset: return offset(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Trigger: return trigger(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Iceberg: return iceberg(model, security_md_source, id, display_name);
|
||||
|
||||
+25
-53
@@ -15,52 +15,42 @@ namespace mbts.spark
|
||||
{
|
||||
static partial class OrderTypeVMFactory
|
||||
{
|
||||
class InlineTickOrderProperties
|
||||
class InlinePricerOrderProperties
|
||||
{
|
||||
public InlineTickOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
|
||||
public InlinePricerOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
tick_offset_on_deploy = new InlineUserProperty<uint>(0, false);
|
||||
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
|
||||
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
|
||||
cross_ticks = new InlineUserProperty<uint>(0, false);
|
||||
max_chase_ticks = new InlineUserProperty<uint>(10, false);
|
||||
detach_on_toward = new InlineUserProperty<bool>(true, false);
|
||||
price_offset_ticks = new InlineUserProperty<int>(0, false);
|
||||
price_factor = new InlineUserProperty<double>(0, false);
|
||||
}
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
|
||||
public readonly InlineUserProperty<uint> tick_offset_on_deploy;
|
||||
public readonly InlineUserProperty<bts.spark.QuantityMeasure> cross_threshold;
|
||||
public readonly InlineUserProperty<uint> cross_ticks;
|
||||
public readonly InlineUserProperty<uint> max_chase_ticks;
|
||||
public readonly InlineUserProperty<bool> detach_on_toward;
|
||||
public readonly InlineUserProperty<int> price_offset_ticks;
|
||||
public readonly InlineUserProperty<double> price_factor;
|
||||
}
|
||||
|
||||
class TickOrderVM : OrderTypeVMBase<TickOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
class PricerOrderVM : OrderTypeVMBase<PricerOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineTickOrderProperties _inline_props;
|
||||
InlinePricerOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
public uint tick_offset_on_deploy { get; set; } = 0;
|
||||
public bts.spark.QuantityMeasure cross_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
|
||||
public uint cross_ticks { get; set; } = 0;
|
||||
public uint max_chase_ticks { get; set; } = 10;
|
||||
public bool detach_on_toward { get; set; } = true;
|
||||
public int price_offset_ticks { get; set; } = 0;
|
||||
public double price_factor { get; set; } = 1.0;
|
||||
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
public PricerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTickOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { }
|
||||
new InlinePricerOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { }
|
||||
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTickOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
public PricerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlinePricerOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
{
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
@@ -76,31 +66,16 @@ namespace mbts.spark
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
if (p.tick_offset_on_deploy.force_override)
|
||||
if (p.price_offset_ticks.force_override)
|
||||
{
|
||||
tick_offset_on_deploy = p.tick_offset_on_deploy.value;
|
||||
RaisePropertyChanged(nameof(tick_offset_on_deploy));
|
||||
price_offset_ticks = p.price_offset_ticks.value;
|
||||
RaisePropertyChanged(nameof(price_offset_ticks));
|
||||
}
|
||||
if (p.cross_threshold.force_override)
|
||||
if (p.price_factor.force_override)
|
||||
{
|
||||
cross_threshold = p.cross_threshold.value;
|
||||
RaisePropertyChanged(nameof(cross_threshold));
|
||||
}
|
||||
if (p.cross_ticks.force_override)
|
||||
{
|
||||
cross_ticks = p.cross_ticks.value;
|
||||
RaisePropertyChanged(nameof(cross_ticks));
|
||||
}
|
||||
if (p.max_chase_ticks.force_override)
|
||||
{
|
||||
max_chase_ticks = p.max_chase_ticks.value;
|
||||
RaisePropertyChanged(nameof(max_chase_ticks));
|
||||
}
|
||||
if (p.detach_on_toward.force_override)
|
||||
{
|
||||
detach_on_toward = p.detach_on_toward.value;
|
||||
RaisePropertyChanged(nameof(detach_on_toward));
|
||||
}
|
||||
price_factor = p.price_factor.value;
|
||||
RaisePropertyChanged(nameof(price_factor));
|
||||
}
|
||||
_inline_props = p;
|
||||
_validate();
|
||||
})
|
||||
@@ -115,14 +90,11 @@ namespace mbts.spark
|
||||
_validate();
|
||||
if (!HasErrors)
|
||||
{
|
||||
var i = factory.tick_order();
|
||||
i.TickOffsetOnDeploy = tick_offset_on_deploy;
|
||||
i.CrossThreshold = cross_threshold;
|
||||
i.CrossTicks = cross_ticks;
|
||||
i.MaxChaseTicks = max_chase_ticks;
|
||||
i.DetachOnToward = detach_on_toward;
|
||||
var i = factory.pricer_order();
|
||||
i.PriceOffsetTicks = price_offset_ticks;
|
||||
i.PriceFactor = price_factor;
|
||||
child_order.assign_to(i.Child);
|
||||
value.Tick = i;
|
||||
value.Pricer = i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace mbts.spark {
|
||||
var so = model.factory.spreader_order();
|
||||
so.Security = s.CloneToMutable();
|
||||
so.ExecutionStrategy = strategy.CloneToMutable();
|
||||
return new InlineUserProperty<SpreaderOrderDefinitionImmutable>(so, true);
|
||||
var soi = new SpreaderOrderDefinitionImmutable(so);
|
||||
return new InlineUserProperty<SpreaderOrderDefinitionImmutable>(soi, true);
|
||||
})), id, display_name) { }
|
||||
|
||||
public SpreaderOrderVM(
|
||||
|
||||
@@ -93,10 +93,13 @@ namespace mbts.spark {
|
||||
#endregion
|
||||
|
||||
#region Input
|
||||
public async Task<uint?> send_order(OrderDefinitionImmutable order, double price, uint size) {
|
||||
var result = (await send_order_get_error(order, price, size));
|
||||
_activity.on_send_order(order, price, size, result.key, result.error);
|
||||
return result.key;
|
||||
public async Task<uint?> send_order(OrderDefinitionImmutable? order, double price, uint size) {
|
||||
if (order.HasValue) {
|
||||
var result = (await send_order_get_error(order.Value, price, size));
|
||||
_activity.on_send_order(order.Value, price, size, result.key, result.error);
|
||||
return result.key;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
async Task<(uint? key, string error)> send_order_get_error(OrderDefinitionImmutable order, double price, uint size) {
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.4.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
|
||||
<PackageReference Include="IronPython" Version="2.7.7" />
|
||||
<PackageReference Include="NAudio" Version="1.8.4" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
@@ -54,6 +54,7 @@
|
||||
<ItemGroup>
|
||||
<None Remove="Sounds\game_sound_correct.wav" />
|
||||
<None Remove="Sounds\whistle.wav" />
|
||||
<None Remove="view\Controls\TickUpDown\TickUpDownNullableUIntCommit.xaml" />
|
||||
<None Remove="view\Shell\Workspaces\WorkspacesView.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -71,11 +72,20 @@
|
||||
<Folder Include="view\Extensions\ItemsControl\ObservableItemsSource\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="view\Controls\TickUpDown\TickUpDownNullableUIntCommit.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="view\Shell\Workspaces\WorkspacesDialogView.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Modules\Trading\OrderBook\MiniOrderBook\MiniOrderBookView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</None>
|
||||
<None Update="Modules\Trading\OrderBook\MiniOrderBook\OrderBookView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</None>
|
||||
<None Update="view\Controls\DIYGrid\Settings\SimpleGridSettingsDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</None>
|
||||
|
||||
@@ -211,6 +211,8 @@ namespace mbts.ui {
|
||||
public static BTSColor lean_order { get { return new BTSColor(255, 170, 210); } }
|
||||
public static BTSColor join_order { get { return new BTSColor(200, 171, 232); } }
|
||||
public static BTSColor cover_order { get { return new BTSColor(134, 232, 224); } }
|
||||
public static BTSColor oco_order { get { return new BTSColor(110, 94, 11); } }
|
||||
public static BTSColor pricer_order { get { return new BTSColor(170, 132, 150); } }
|
||||
public static BTSColor trigger_order { get { return new BTSColor(172, 255, 179); } }
|
||||
public static BTSColor off_order { get { return new BTSColor(0x8a, 0x00, 0x5f); } }//8a005f
|
||||
public static BTSColor top_order { get { return new BTSColor(0xdd, 0x7a, 0x27); } }//dd7a27
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace bts.proto {
|
||||
static class DateTimeProviderExtensions {
|
||||
public static DateTime get_date_time(this DateTimeProvider dtp) {
|
||||
switch (dtp.mode) {
|
||||
case DateTimeProviderMode.CMEOpen: return MarketSchedule.get_cme_gen_open();
|
||||
case DateTimeProviderMode.CMEOpen: return MarketSchedule.get_start_of_trading_day();
|
||||
case DateTimeProviderMode.Today: return DateTime.Today;
|
||||
case DateTimeProviderMode.Explicit:
|
||||
default: return dtp.explicit_date.GetValueOrDefault();
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using FontFamily = System.Windows.Media.FontFamily;
|
||||
@@ -38,6 +39,7 @@ namespace mbts.ui.controls {
|
||||
CustomOrderDefinition.CustomOneofCase _parent_algo_type;
|
||||
(Point p1, Point p2, Point p3) _current_triangle_vertices;
|
||||
StreamGeometry _triangle_geometry;
|
||||
Popup _popup;
|
||||
|
||||
|
||||
static Badge() {
|
||||
@@ -181,6 +183,7 @@ namespace mbts.ui.controls {
|
||||
_has_multiple_orders_at_level = HasMultipleOrdersAtLevel;
|
||||
_is_hedge_order = IsHedgeOrder;
|
||||
_gcr.glyph_context = GlyphContext.create(FontFamily, FontWeight);
|
||||
_popup = Popup;
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty TextLinesProperty =
|
||||
@@ -264,6 +267,19 @@ namespace mbts.ui.controls {
|
||||
(b, v) => b._is_hedge_order = v,
|
||||
false);
|
||||
|
||||
public Popup Popup {
|
||||
get { return (Popup)GetValue(PopupProperty); }
|
||||
set { SetValue(PopupProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PopupProperty =
|
||||
WPFHelper.create_dp<Popup, Badge>(
|
||||
nameof(Popup),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._popup = v,
|
||||
null);
|
||||
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize) => finalSize;
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize) {
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace mbts.ui.controls {
|
||||
Symbol = 78,
|
||||
Time = 112,
|
||||
TimeInForce = 47,
|
||||
TradeKey = 76,
|
||||
User = 53,
|
||||
Aggressor = 80,
|
||||
Volume = 74,
|
||||
@@ -71,7 +70,6 @@ namespace mbts.ui.controls {
|
||||
ImpliedSize = "Qty (I)",
|
||||
Symbol = "Symbol",
|
||||
TimeInForce = "TIF",
|
||||
TradeKey = "Trade Key",
|
||||
User = "User",
|
||||
Time = "Time",
|
||||
Aggressor = "Aggressor",
|
||||
@@ -97,7 +95,8 @@ namespace mbts.ui.controls {
|
||||
DayPnL = "Day PnL",
|
||||
TotalNetPos = "Total Net Pos",
|
||||
TotalPnL = "Total PnL",
|
||||
PnLPrice = "PnL Price";
|
||||
PnLPrice = "PnL Price",
|
||||
Cancel = "Cancel";
|
||||
}
|
||||
|
||||
public interface IKeyedColumn<TColKey> {
|
||||
|
||||
@@ -139,6 +139,7 @@ namespace mbts.ui.controls {
|
||||
public static readonly Thickness
|
||||
DefaultCellPadding = new Thickness(4, 2, 4, 2),
|
||||
AnimatableCellPadding = new Thickness(8, 2, 4, 2);
|
||||
public static readonly Visibility DefaultRowsHorizontalScrollBarVisibility = Visibility.Visible;
|
||||
|
||||
#region RoutedCommands
|
||||
public static readonly RoutedCommand SortCommand = new RoutedCommand(), DefaultRowCommand = new RoutedCommand();
|
||||
@@ -695,6 +696,14 @@ namespace mbts.ui.controls {
|
||||
|
||||
public static readonly DependencyProperty SelectionModeProperty = ListBox.SelectionModeProperty.AddOwner(typeof(DIYGrid));
|
||||
|
||||
public Visibility RowsHorizontalScrollBarVisibility {
|
||||
get { return (Visibility)GetValue(RowsHorizontalScrollBarVisibilityProperty); }
|
||||
set { SetValue(RowsHorizontalScrollBarVisibilityProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty RowsHorizontalScrollBarVisibilityProperty = DependencyProperty.Register(
|
||||
nameof(RowsHorizontalScrollBarVisibility), typeof(Visibility), typeof(DIYGrid), new PropertyMetadata(DefaultRowsHorizontalScrollBarVisibility));
|
||||
|
||||
#endregion
|
||||
|
||||
// Internal Methods
|
||||
|
||||
@@ -62,7 +62,8 @@
|
||||
<Setter Property="ColumnHeaderStyle">
|
||||
<Setter.Value>
|
||||
<Style TargetType="GridViewColumnHeader">
|
||||
<Setter Property="Background" Value="{mbts:BTSColor header}"/>
|
||||
<!-- Background - note that we're settings this property on the containing stackpanel in which the GridViewHeaderRowPresenter resides -->
|
||||
<Setter Property="Background" Value="{x:Null}"/>
|
||||
<Setter Property="Foreground" Value="{mbts:BTSColor header_text}"/>
|
||||
<Setter Property="BorderBrush" Value="{mbts:BTSColor header}"/>
|
||||
<Setter Property="BorderThickness" Value="0,1,1,0"/>
|
||||
@@ -301,7 +302,9 @@
|
||||
HorizontalScrollBarVisibility="Hidden"
|
||||
VerticalScrollBarVisibility="Hidden"
|
||||
x:Name="_header_scroll_viewer">
|
||||
<StackPanel>
|
||||
<!-- setting the bg color for this stackpanel is a work-around for the vertical white lines that appear on high-dpi displays between some columns -->
|
||||
<!-- in reality, there is some high dpi rounding thing going on, but that's a battle for another day -->
|
||||
<StackPanel Background="{mbts:BTSColor Name=header}">
|
||||
<!-- We don't need super headers over the fixed columns -->
|
||||
<StackPanel Orientation="Horizontal" x:Name="_super_headers" />
|
||||
<GridViewHeaderRowPresenter x:Name="_header_scroll" AllowsColumnReorder="False" ColumnHeaderContainerStyle="{TemplateBinding ColumnHeaderStyle}" UseLayoutRounding="True" />
|
||||
@@ -332,7 +335,7 @@
|
||||
</ControlTemplate>
|
||||
</bts:DIYGridRowsPresenter.Template>
|
||||
</bts:DIYGridRowsPresenter>
|
||||
<ScrollBar Grid.Row="3" Grid.Column="1" Orientation="Horizontal" LargeChange="10" x:Name="_scroll_horiz" />
|
||||
<ScrollBar Grid.Row="3" Grid.Column="1" Orientation="Horizontal" LargeChange="10" x:Name="_scroll_horiz" Visibility="{TemplateBinding RowsHorizontalScrollBarVisibility}" />
|
||||
|
||||
<!-- todo: All edit controls go in a canvas - edit api is called with an edit type. Hide all controls -->
|
||||
<!-- This stole hours of my life. The Text binding down there used to be 'value' and I was encountering -->
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using bts.proto;
|
||||
using bts.utils;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
@@ -18,7 +19,7 @@ namespace mbts.ui.settings {
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class GridColumnSettings {
|
||||
class GridColumnSettings : IEquatable<GridColumnSettings> {
|
||||
const bool
|
||||
_default_is_visible = true,
|
||||
_default_is_collapsed = false;
|
||||
@@ -103,5 +104,43 @@ namespace mbts.ui.settings {
|
||||
width ?? this.width,
|
||||
horizontal_alignment ?? this.horizontal_alignment,
|
||||
horizontal_offset ?? this.horizontal_offset);
|
||||
|
||||
public bool Equals(GridColumnSettings other) =>
|
||||
other != null &&
|
||||
id == other.id &&
|
||||
display_name == other.display_name &&
|
||||
is_visible == other.is_visible &&
|
||||
is_collapsed == other.is_collapsed &&
|
||||
font_weight_override.Equals(other.font_weight_override) &&
|
||||
font_color_override.Equals(other.font_color_override) &&
|
||||
width == other.width &&
|
||||
horizontal_alignment == other.horizontal_alignment &&
|
||||
horizontal_offset == other.horizontal_offset &&
|
||||
display_name == other.display_name;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as GridColumnSettings);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
id.GetHashCode(),
|
||||
display_name.GetHashCode(),
|
||||
is_visible.GetHashCode(),
|
||||
is_collapsed.GetHashCode(),
|
||||
font_weight_override.GetHashCode(),
|
||||
font_color_override.GetHashCode(),
|
||||
width.GetHashCode(),
|
||||
horizontal_alignment.GetHashCode(),
|
||||
horizontal_offset.GetHashCode(),
|
||||
display_name.GetHashCode()
|
||||
);
|
||||
|
||||
public override string ToString() {
|
||||
return $"id: {id} display_name: {display_name}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -126,13 +126,15 @@ namespace mbts.ui {
|
||||
col.set_fallback_font_params(new FontFamily(grid.font_family), grid.font_size, grid.font_color.rgb_color(), grid.font_weight);
|
||||
_predicate_animations_data = value.predicate_animations;
|
||||
_animation_defs.clear();
|
||||
foreach (var pa in _predicate_animations_data) {
|
||||
var predicate = pa.predicate_builder.create_predicate(_arg);
|
||||
foreach (var s in pa.sound_notifications) {
|
||||
_notification_subs.Add(_notification_service.link(predicate, s));
|
||||
}
|
||||
if (pa.predicate_builder is INotificationPredicateBuilder<TRowData, TPredicateBuilderArg> tpb && tpb.is_toast_enabled) {
|
||||
_notification_subs.Add(_notification_service.link(tpb.create_notification_predicate(_arg)));
|
||||
if (_notification_service != null && _arg != null) {
|
||||
foreach (var pa in _predicate_animations_data) {
|
||||
var predicate = pa.predicate_builder.create_predicate(_arg);
|
||||
foreach (var s in pa.sound_notifications) {
|
||||
_notification_subs.Add(_notification_service.link(predicate, s));
|
||||
}
|
||||
if (pa.predicate_builder is INotificationPredicateBuilder<TRowData, TPredicateBuilderArg> tpb && tpb.is_toast_enabled) {
|
||||
_notification_subs.Add(_notification_service.link(tpb.create_notification_predicate(_arg)));
|
||||
}
|
||||
}
|
||||
}
|
||||
// ** in the unfortunate event that we botch backwards compatability, and the columns don't bear a 1:1 relationship with the column-settings
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace mbts.spark.filters {
|
||||
TimeFilterMode _mode;
|
||||
TimeTextTuple _begin, _end;
|
||||
DateTime? _begin_dt, _end_dt;
|
||||
readonly DateTime _cme_open = MarketSchedule.get_cme_gen_open();
|
||||
readonly DateTime _cme_open = MarketSchedule.get_start_of_trading_day();
|
||||
|
||||
public TimeFilterVM(Func<TRow, DateTime> get_row_val, string prop_name) : base(get_row_val, prop_name) =>
|
||||
_set_default_values();
|
||||
|
||||
@@ -34,9 +34,11 @@ namespace bts.view.controls {
|
||||
private void any_button_click(object sender, RoutedEventArgs e) {
|
||||
Close();
|
||||
}
|
||||
internal static void Show(ErrorReportService srv, ErrorReport report) {
|
||||
var window = new SupportRequestWindow(new SupportRequestVM(srv, report));
|
||||
window.Show();
|
||||
internal static void Show(ErrorReportService srv, ErrorReportImmutable? report) {
|
||||
if (report.HasValue) {
|
||||
var window = new SupportRequestWindow(new SupportRequestVM(srv, report.Value));
|
||||
window.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,51 +166,6 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.TimeBoxOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.TickOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="1" VerticalAlignment="Center">Cross Thresh</TextBlock>
|
||||
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding cross_threshold}"/>
|
||||
|
||||
<TextBlock Grid.Row="3" VerticalAlignment="Center">Cross Ticks</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="3" Grid.Column="2" Value="{Binding cross_ticks}"/>
|
||||
<TextBlock Grid.Row="5" VerticalAlignment="Center">Max Chase Ticks</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="5" Grid.Column="2" Value="{Binding max_chase_ticks}"/>
|
||||
<CheckBox Grid.Row="7" Grid.Column="2" VerticalAlignment="Center" Margin="0,0,0,0" IsChecked="{Binding detach_on_toward}">Detach on Toward</CheckBox>
|
||||
<TextBlock Grid.Row="9" VerticalAlignment="Center">Initial Tick Offset</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="9" Grid.Column="2" Value="{Binding tick_offset_on_deploy}"/>
|
||||
<TextBlock Grid.Row="11">Child</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="11" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding child_order_types}"
|
||||
SelectedItem="{Binding child_order}"
|
||||
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.TickOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.LeanOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -379,16 +334,16 @@
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="1"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center" >Hedge Ticks</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the number of ticks to offset the hedge." />
|
||||
<TextBlock VerticalAlignment="Center" >Profit Ticks</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the number of ticks in your favor to offset the profit/hedge order." />
|
||||
</StackPanel>
|
||||
<c:TickUpDownInt Grid.Row="1" Grid.Column="2" Value="{Binding hedge_ticks}"/>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="3"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Hedge</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of the hedge order to send for each fill of Child Order." />
|
||||
<TextBlock VerticalAlignment="Center">Profit Order</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of the profit/hedge order to send for each fill of Child Order." />
|
||||
</StackPanel>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="3" Grid.Column="2"
|
||||
@@ -415,6 +370,68 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.CoverOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.OCOOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="1" >
|
||||
<TextBlock VerticalAlignment="Center" >Stop Ticks</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the number of ticks away from the main order to start the 'stop' order." />
|
||||
</StackPanel>
|
||||
<c:TickUpDownInt Grid.Row="1" Grid.Column="2" Value="{Binding other_ticks}"/>
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="3" >
|
||||
<TextBlock VerticalAlignment="Center" >Stop Side</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="The Side of the 'stop' order" />
|
||||
</StackPanel>
|
||||
<ComboBox Grid.Row="3" Grid.Column="2" ItemsSource="{Binding other_side_types}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding other_side}"/>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="5"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Stop Order</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of the 'stop' order to send." />
|
||||
</StackPanel>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="5" Grid.Column="2"
|
||||
IsOpen="{Binding other_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding other_order_types}"
|
||||
SelectedItem="{Binding other_order}"
|
||||
HasEditableProperties="{Binding other_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="7"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Child</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of order to be placed when the algo is triggered." />
|
||||
</StackPanel>
|
||||
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="7" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding child_order_types}"
|
||||
SelectedItem="{Binding child_order}"
|
||||
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.OCOOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.OffsetOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -773,9 +790,11 @@
|
||||
</usp:DynamicOrderParamTemplateSelector>
|
||||
</ItemsControl.ItemTemplateSelector>
|
||||
</ItemsControl>
|
||||
<TextBlock Grid.Row="3">Child</TextBlock>
|
||||
<TextBlock Grid.Row="3"
|
||||
Visibility="{Binding has_child, Converter={StaticResource BooleanToVisibilityConverter}}" >Child</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="3" Grid.Column="2"
|
||||
Grid.Row="3" Grid.Column="2"
|
||||
Visibility="{Binding has_child, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding child_order_types}"
|
||||
SelectedItem="{Binding child_order}"
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<UserControl x:Class="mbts.ui.controls.PriceEntryCommitControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
d:DesignHeight="100" d:DesignWidth="800" x:Name="_me_price">
|
||||
<UserControl.Resources>
|
||||
<cv:NullableDoubleConverter x:Key="TickDefaultPriceConverter" DefaultValue="0"/>
|
||||
<uspcv:TickDisplayConverter x:Key="TickDisplayConverter" x:Shared="False"/>
|
||||
<ControlTemplate x:Key="ReadTemplate">
|
||||
<Grid HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.ColumnSpan="2">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="SourceValue" ElementName="_me_price"/>
|
||||
<Binding Path="SourceContractSpec" ElementName="_me_price"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
<ControlTemplate x:Key="EditTemplate">
|
||||
<Grid HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<usp:PriceEntryControl x:Name="price_ticker"
|
||||
Margin="0,0,2,0"
|
||||
TickDefaultValue="{Binding SourceValue, ElementName=_me_price, Converter={StaticResource TickDefaultPriceConverter}, FallbackValue={u:Double 0}}"
|
||||
ContractSpec="{Binding SourceContractSpec, ElementName=_me_price}"
|
||||
Value="{Binding SourceValue, ElementName=_me_price}" />
|
||||
<Button Grid.Column="2" x:Name="commit_button" CommandParameter="{Binding Value, ElementName=price_ticker}" Command="{Binding OnCommit, ElementName=_me_price}" Content="✔" >
|
||||
<Button.Style>
|
||||
<Style TargetType="Button">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="True">
|
||||
<Setter Property="Background" Value="Green" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</UserControl.Resources>
|
||||
|
||||
<UserControl.Style>
|
||||
<Style TargetType="UserControl">
|
||||
<Setter Property="Template" Value="{StaticResource ReadTemplate}"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding is_selected}" Value="True">
|
||||
<Setter Property="Template" Value="{StaticResource EditTemplate}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding is_price_dirty}" Value="True">
|
||||
<Setter Property="Template" Value="{StaticResource EditTemplate}"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</UserControl.Style>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,73 @@
|
||||
using bts.api;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
/// <summary>
|
||||
/// Interaction logic for PriceEntryCommitControl.xaml
|
||||
/// </summary>
|
||||
public partial class PriceEntryCommitControl : UserControl {
|
||||
|
||||
|
||||
public static readonly DependencyProperty SourceValueProperty =
|
||||
DependencyProperty.Register("SourceValue",
|
||||
typeof(double),
|
||||
typeof(PriceEntryCommitControl),
|
||||
new FrameworkPropertyMetadata {
|
||||
BindsTwoWayByDefault = true,
|
||||
PropertyChangedCallback = source_value_changed
|
||||
}
|
||||
);
|
||||
|
||||
public double SourceValue {
|
||||
get => (double)GetValue(SourceValueProperty);
|
||||
set => SetValue(SourceValueProperty, value);
|
||||
}
|
||||
private static void source_value_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
if (d is PriceEntryCommitControl _instance_of_me) {
|
||||
_instance_of_me.Dispatcher.BeginInvoke(
|
||||
(Action)(() => _instance_of_me.OnCommit?.TryRaiseCanExecuteChanged())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SourceContractSpecProperty =
|
||||
DependencyProperty.Register("SourceContractSpec", typeof(ContractSpecificationImmutable?), typeof(PriceEntryCommitControl));
|
||||
public ContractSpecificationImmutable? SourceContractSpec {
|
||||
get => (ContractSpecificationImmutable?)GetValue(SourceContractSpecProperty);
|
||||
set => SetValue(SourceContractSpecProperty, value);
|
||||
}
|
||||
|
||||
// public static readonly DependencyProperty PriceTextProperty =
|
||||
//DependencyProperty.Register("PricText", typeof(string), typeof(PriceEntryCommitControl));
|
||||
// public string PriceText {
|
||||
// get => (string)GetValue(PriceTextProperty);
|
||||
// set => SetValue(PriceTextProperty, value);
|
||||
// }
|
||||
|
||||
public static readonly DependencyProperty OnCommitProperty =
|
||||
DependencyProperty.Register("OnCommit", typeof(ICommand), typeof(PriceEntryCommitControl));
|
||||
public ICommand OnCommit {
|
||||
get => (ICommand)GetValue(OnCommitProperty);
|
||||
set => SetValue(OnCommitProperty, value);
|
||||
}
|
||||
|
||||
public PriceEntryCommitControl() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<UserControl x:Class="mbts.ui.controls.TickUpDownNullableUIntCommit"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
d:DesignHeight="100" d:DesignWidth="800" x:Name="_me">
|
||||
<UserControl.Resources>
|
||||
<ControlTemplate x:Key="ReadTemplate">
|
||||
<Grid HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.ColumnSpan="2" Text="{Binding SourceValue, ElementName=_me}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
<ControlTemplate x:Key="EditTemplate">
|
||||
<Grid HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<c:TickUpDownNullableUInt x:Name="qty_ticker" Margin="0,0,2,0" Value="{Binding SourceValue, ElementName=_me}" />
|
||||
<Button Grid.Column="2" x:Name="commit_button" CommandParameter="{Binding Value, ElementName=qty_ticker}" Command="{Binding OnCommit, ElementName=_me}" Content="✔" >
|
||||
<Button.Style>
|
||||
<Style TargetType="Button">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="True">
|
||||
<Setter Property="Background" Value="Green" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</UserControl.Resources>
|
||||
|
||||
<UserControl.Style>
|
||||
<Style TargetType="UserControl">
|
||||
<Setter Property="Template" Value="{StaticResource ReadTemplate}"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding is_selected}" Value="True">
|
||||
<Setter Property="Template" Value="{StaticResource EditTemplate}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding is_qty_dirty}" Value="True">
|
||||
<Setter Property="Template" Value="{StaticResource EditTemplate}"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</UserControl.Style>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
/// <summary>
|
||||
/// Interaction logic for TickUpDownNullableUIntCommit.xaml
|
||||
/// </summary>
|
||||
public partial class TickUpDownNullableUIntCommit : UserControl {
|
||||
|
||||
|
||||
public static readonly DependencyProperty SourceValueProperty =
|
||||
DependencyProperty.Register("SourceValue",
|
||||
typeof(uint?),
|
||||
typeof(TickUpDownNullableUIntCommit),
|
||||
new FrameworkPropertyMetadata {
|
||||
BindsTwoWayByDefault = true,
|
||||
PropertyChangedCallback = source_value_changed
|
||||
}
|
||||
);
|
||||
|
||||
private static void source_value_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
if (d is TickUpDownNullableUIntCommit _instance_of_me) {
|
||||
_instance_of_me.Dispatcher.BeginInvoke(
|
||||
(Action)(() => _instance_of_me.OnCommit?.TryRaiseCanExecuteChanged())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public uint? SourceValue {
|
||||
get => GetValue(SourceValueProperty) as uint?;
|
||||
set => SetValue(SourceValueProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty OnCommitProperty =
|
||||
DependencyProperty.Register("OnCommit", typeof(ICommand), typeof(TickUpDownNullableUIntCommit));
|
||||
public ICommand OnCommit {
|
||||
get => (ICommand)GetValue(OnCommitProperty);
|
||||
set => SetValue(OnCommitProperty, value);
|
||||
}
|
||||
|
||||
public TickUpDownNullableUIntCommit() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ namespace mbts.spark {
|
||||
return intpart + floatpart;
|
||||
}
|
||||
|
||||
public static RegionalTickFactors get_regional_ticks(ContractSpecificationImmutable spec) =>
|
||||
public static RegionalTickFactors get_regional_ticks(ContractSpecificationImmutable? spec) =>
|
||||
TickStore.get(spec);
|
||||
|
||||
public static RegionalTickFactors get_regional_ticks(SecurityImmutable security) =>
|
||||
@@ -177,8 +177,14 @@ namespace mbts.spark {
|
||||
public static RegionalTickFactors get(ContractSpecification spec) =>
|
||||
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
|
||||
|
||||
public static RegionalTickFactors get(ContractSpecificationImmutable spec) =>
|
||||
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
|
||||
public static RegionalTickFactors get(ContractSpecificationImmutable? spec) {
|
||||
if (spec.HasValue) {
|
||||
return _exchange_ticks.GetOrAdd(new SpecKey(spec.Value), _create_rtf);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static RegionalTickFactors get(SecurityImmutable security) =>
|
||||
security.HasContractSpec ? get(security.ContractSpec) : default_rtf;
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace mbts.spark {
|
||||
readonly Property<Brush> _foreground, _background, _hoverbackground, _border, _parent_algo_type_brush;
|
||||
readonly Property<bool> _is_exchange_order, _has_multiple_orders_at_same_level, _is_hedge_order;
|
||||
readonly Property<CustomOrderDefinition.CustomOneofCase> _parent_algo_type;
|
||||
readonly Property<string> _summary, _description;
|
||||
readonly Property<string> _summary;
|
||||
IObservable<LadderSettings.BadgeStyle> _bs;
|
||||
LadderSettings.BadgeStyle _current_badge_style;
|
||||
int _n_active_price_change_ops = 0;
|
||||
@@ -63,7 +63,6 @@ namespace mbts.spark {
|
||||
_hoverbackground = backing(nameof(hoverbackground), _color_bg_brush);
|
||||
_border = backing(nameof(border), _color_bg_brush);
|
||||
_summary = backing(nameof(summary), string.Empty);
|
||||
_description = backing(nameof(description), string.Empty);
|
||||
_is_exchange_order = backing(nameof(is_exchange_order), false);
|
||||
_has_multiple_orders_at_same_level = backing(nameof(has_multiple_orders_at_same_level), false);
|
||||
_is_hedge_order = backing(nameof(is_hedge_order), false);
|
||||
@@ -75,6 +74,7 @@ namespace mbts.spark {
|
||||
Brush get_foreground(LadderSettings s) => s.orders_column.foreground.rgb_brush();
|
||||
//
|
||||
add_order(o);
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
_bs.Subscribe(badge => {
|
||||
_current_badge_style = badge;
|
||||
@@ -122,7 +122,6 @@ namespace mbts.spark {
|
||||
|
||||
if (_orders.Any()) {
|
||||
summary = _orders.Values.Select(order => order.short_description(_parent.security.ContractSpec)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
|
||||
description = _orders.Values.Select(order => order.full_description(_parent.security)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
|
||||
}
|
||||
_set_badge_text();
|
||||
|
||||
@@ -185,11 +184,10 @@ namespace mbts.spark {
|
||||
public Brush hoverbackground { get => _hoverbackground.Value; private set => _hoverbackground.Value = value; }
|
||||
public Brush border { get => _border.Value; private set => _border.Value = value; }
|
||||
public string summary { get => _summary.Value; private set => _summary.Value = value; }
|
||||
public string description { get => _description.Value; private set => _description.Value = value; }
|
||||
public bool is_exchange_order { get => _is_exchange_order.Value; private set => _is_exchange_order.Value = value; }
|
||||
public bool has_multiple_orders_at_same_level { get => _has_multiple_orders_at_same_level.Value; private set => _has_multiple_orders_at_same_level.Value = value; }
|
||||
public bool is_hedge_order { get => _is_hedge_order.Value; private set => _is_hedge_order.Value = value; }
|
||||
public Brush parent_algo_type_brush { get => _parent_algo_type_brush.Value; private set => _parent_algo_type_brush.Value = value; }
|
||||
public Brush parent_algo_type_brush { get => _parent_algo_type_brush.Value; private set => _parent_algo_type_brush.Value = value; }
|
||||
// the setter is public so that the LadderVM can set this field
|
||||
public CustomOrderDefinition.CustomOneofCase parent_algo_type { get => _parent_algo_type.Value; set => _parent_algo_type.Value = value; }
|
||||
|
||||
@@ -224,6 +222,56 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public OrderBookVM create_orderbook_vm() {
|
||||
// We are returning a new OrderBookVM each time b/c after
|
||||
// successive launches of the mini-ob from the same badge, we get an exception
|
||||
// System.InvalidOperationException: Sharing GridViewColumn among multiple GridViewColumnCollections or adding the same GridViewColumn into one GridViewColumnCollection more than once is not allowed.
|
||||
// I checked with Eric and he guesses that what is happening is that the Popup is throwing away the grid, but the binding doesn't remove the columns from that grid
|
||||
//
|
||||
// I think returning a new OrderbookVM is okay at the moment since this Popup window will not be saving any state at this time
|
||||
var price_filter = new filters.DoubleMinMaxState();
|
||||
price_filter.minimum.value = real_price;
|
||||
price_filter.maximum.value = real_price;
|
||||
|
||||
var obvm = new OrderBookVM(null, _parent.bus, null, _parent.model.order_gateway, null, true);
|
||||
var state = new bts.shell.OrderBookWindowFactory.OrderBookStartupData(
|
||||
new bts.shell.OrderBookWindowFactory.FilterStates {
|
||||
security_criteria = new SecurityCriteria(_parent.security),
|
||||
price = price_filter,
|
||||
statuses = new[] { OrderStatus.Working }
|
||||
},
|
||||
bts.shell.OrderBookWindowFactory.OrderBookStartupData.default_mini_grid,
|
||||
use_defaults: false
|
||||
);
|
||||
// NOTE:
|
||||
// we might need the ObservableAppend to InsertAt
|
||||
// if these EditXXX ever support settings.
|
||||
// insert the cancel column
|
||||
obvm.columns.Insert(0, new OrderBookVM.CancelColumn(nameof(OrderBookRowVM.cancel_command)));
|
||||
var qty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.Size.ToString());
|
||||
var qty_index = obvm.columns.indexOf(qty_col);
|
||||
if (qty_index > -1) {
|
||||
// insert the edit column in the same position that the hidden column occupies
|
||||
var existing_editqty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.EditableQty.ToString());
|
||||
var existing_editqty_index = obvm.columns.indexOf(existing_editqty_col);
|
||||
if (existing_editqty_index == -1) {
|
||||
obvm.columns.Insert(qty_index, new OrderBookVM.EditableQtyColumn());
|
||||
}
|
||||
}
|
||||
var price_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.Price.ToString());
|
||||
var price_index = obvm.columns.indexOf(price_col);
|
||||
if (price_index > -1) {
|
||||
var existing_editprice_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.EditablePrice.ToString());
|
||||
var existing_editprice_index = obvm.columns.indexOf(existing_editprice_col);
|
||||
if (existing_editprice_index == -1) {
|
||||
// insert the edit price column immediately after the edit qty
|
||||
obvm.columns.Insert(qty_index + 1, new OrderBookVM.EditablePriceColumn());
|
||||
}
|
||||
}
|
||||
obvm.load_from(state);
|
||||
return obvm;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return $"Price: {this.price} Order Count: {this.orders.Count()}";
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
@@ -91,15 +92,26 @@ namespace mbts.spark {
|
||||
order.PropertyChanged += _order_PropertyChanged;
|
||||
// get the stackpanel it belongs in
|
||||
Panel panel = get_panel(price);
|
||||
//var coords = GetAbsolutePlacement(panel, true);
|
||||
// insert Badge and wire up DataContext
|
||||
if (panel != null) {
|
||||
panel.Children.Add(mkbadge(order));
|
||||
panel.Children.Add(mkbadge(order, panel));
|
||||
}
|
||||
} //else {
|
||||
//Log.Ui.warn($"Failed to add order ({order.id}): order already in {nameof(LadderGridHost)}'s {nameof(_orders)}.");
|
||||
//}
|
||||
}
|
||||
|
||||
public static Rect GetAbsolutePlacement(FrameworkElement element, bool relativeToScreen = false) {
|
||||
var absolutePos = element.PointToScreen(new System.Windows.Point(0, 0));
|
||||
if (relativeToScreen) {
|
||||
return new Rect(absolutePos.X, absolutePos.Y, element.ActualWidth, element.ActualHeight);
|
||||
}
|
||||
var posMW = Application.Current.MainWindow.PointToScreen(new System.Windows.Point(0, 0));
|
||||
absolutePos = new System.Windows.Point(absolutePos.X - posMW.X, absolutePos.Y - posMW.Y);
|
||||
return new Rect(absolutePos.X, absolutePos.Y, element.ActualWidth, element.ActualHeight);
|
||||
}
|
||||
|
||||
void _order_PropertyChanged(object sender, PropertyChangedEventArgs e) {
|
||||
if (e.PropertyName == nameof(LadderOrderVM.price)) {
|
||||
var order = (LadderOrderVM)sender;
|
||||
@@ -108,35 +120,38 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
Badge mkbadge(LadderOrderVM order) {
|
||||
Badge mkbadge(LadderOrderVM order, UIElement popup_placement_target) {
|
||||
Badge b = new Badge();
|
||||
b.DataContext = order;
|
||||
// Hook up the style from xaml
|
||||
b.Style = this.TryFindResource(typeof(Badge)) as Style;
|
||||
b.MouseUp += (s, e) => {
|
||||
b.MouseUp += async (s, e) => {
|
||||
var curr_order = b.DataContext as LadderOrderVM;
|
||||
if (e.ChangedButton == MouseButton.Middle) {
|
||||
b.ContextMenu.DataContext = b.DataContext;
|
||||
b.ContextMenu.IsOpen = true;
|
||||
// close context menu after command executed
|
||||
if (curr_order != null) {
|
||||
Action command_executed = null;
|
||||
command_executed = () => {
|
||||
try {
|
||||
b.ContextMenu.IsOpen = false;
|
||||
curr_order.command_executed -= command_executed;
|
||||
} catch (Exception ex) {
|
||||
Log.Ui.warn($"Error in {typeof(LadderGridHost)}'s {typeof(Badge)} {nameof(LadderOrderVM.command_executed)} local handler: {ex}");
|
||||
}
|
||||
};
|
||||
curr_order.command_executed += command_executed;
|
||||
}
|
||||
// create the new popup window in here
|
||||
var obvm = order.create_orderbook_vm();
|
||||
await Task.Delay(100); // Wait for obvm to load rows collection before showing UI to avoid a flash
|
||||
var view = new MiniOrderBookView(obvm);
|
||||
var popup = new System.Windows.Controls.Primitives.Popup {
|
||||
// we only keep the obvm around assigned to the DataContext so that we can
|
||||
// properly Dispose of it later when the popup is closed.
|
||||
DataContext = obvm,
|
||||
Child = view,
|
||||
StaysOpen = false,
|
||||
PlacementTarget = popup_placement_target,
|
||||
Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom,
|
||||
VerticalOffset = 5
|
||||
};
|
||||
popup.SetBinding(System.Windows.Controls.Primitives.Popup.IsOpenProperty,
|
||||
nameof(OrderBookVM.is_row_collection_populated)
|
||||
);
|
||||
popup.Closed += (sender, event_args) => (popup.DataContext as IDisposable)?.Dispose();
|
||||
b.Popup = popup;
|
||||
} else if (e.ChangedButton == MouseButton.Left) {
|
||||
if (curr_order?.cancel_command?.CanExecute(null) == true)
|
||||
curr_order.cancel_command.Execute(null);
|
||||
}
|
||||
};
|
||||
b.ContextMenuOpening += (s, e) => e.Handled = true;
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
@@ -181,6 +181,7 @@ namespace mbts.spark {
|
||||
[ProtoContract]
|
||||
class LadderSettings : IEquatable<LadderSettings> {
|
||||
const double _default_order_entry_panel_width = 110.0;
|
||||
const double _default_zoom_factor = 1.0;
|
||||
|
||||
// Default proto constructor
|
||||
// Added this so that when clients upgrade to a version with new settings,
|
||||
@@ -203,6 +204,7 @@ namespace mbts.spark {
|
||||
auto_clear_visible = true;
|
||||
clear_visible = true;
|
||||
order_entry_panel_initial_width = _default_order_entry_panel_width;
|
||||
zoom_factor = _default_zoom_factor;
|
||||
use_defaults = true;
|
||||
}
|
||||
|
||||
@@ -260,7 +262,9 @@ namespace mbts.spark {
|
||||
cancel_all_visible = true,
|
||||
auto_clear_visible = true,
|
||||
clear_visible = true,
|
||||
order_entry_panel_initial_width = _default_order_entry_panel_width
|
||||
order_entry_panel_initial_width = _default_order_entry_panel_width,
|
||||
zoom_slider_visible = false,
|
||||
zoom_factor = _default_zoom_factor,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -357,10 +361,19 @@ namespace mbts.spark {
|
||||
public bool clear_visible { get; set; }
|
||||
//
|
||||
[ProtoMember(43), DefaultValue(_default_order_entry_panel_width)]
|
||||
public double order_entry_panel_initial_width { get; set; }
|
||||
public double order_entry_panel_initial_width { get; set; }
|
||||
|
||||
[ProtoMember(44), DefaultValue(true)]
|
||||
public bool use_defaults { get; set; }
|
||||
|
||||
// the zoom slider is the first new widget on the panel
|
||||
// since our initial general release, so its enablement
|
||||
// will be opt-in (default not visible)
|
||||
[ProtoMember(45)]
|
||||
public bool zoom_slider_visible { get; set; }
|
||||
[ProtoMember(46), DefaultValue(_default_zoom_factor)]
|
||||
public double zoom_factor { get; set; }
|
||||
|
||||
public bool Equals(LadderSettings other) =>
|
||||
other != null &&
|
||||
row_height == other.row_height &&
|
||||
@@ -403,6 +416,8 @@ namespace mbts.spark {
|
||||
auto_clear_visible == other.auto_clear_visible &&
|
||||
clear_visible == other.clear_visible &&
|
||||
order_entry_panel_initial_width == other.order_entry_panel_initial_width &&
|
||||
zoom_slider_visible == other.zoom_slider_visible &&
|
||||
zoom_factor == other.zoom_factor &&
|
||||
use_defaults == other.use_defaults;
|
||||
|
||||
public override bool Equals(object obj) => Equals(obj as LadderSettings);
|
||||
@@ -449,16 +464,18 @@ namespace mbts.spark {
|
||||
auto_clear_visible.GetHashCode(),
|
||||
clear_visible.GetHashCode(),
|
||||
order_entry_panel_initial_width.GetHashCode(),
|
||||
zoom_slider_visible.GetHashCode(),
|
||||
zoom_factor.GetHashCode(),
|
||||
use_defaults.GetHashCode());
|
||||
|
||||
public struct WindowSpecifics {
|
||||
public readonly double order_entry_panel_width;
|
||||
public readonly double order_entry_panel_width, zoom_factor;
|
||||
public readonly uint orders_column_width, bid_column_width, price_column_width, ask_column_width, last_column_width, volume_column_width;
|
||||
|
||||
public WindowSpecifics(LadderSettings s)
|
||||
: this(s.order_entry_panel_initial_width, s.orders_column.width, s.bid_column.width, s.price_column.width, s.ask_column.width, s.last_column.width, s.volume_column.general.width) { }
|
||||
: this(s.order_entry_panel_initial_width, s.orders_column.width, s.bid_column.width, s.price_column.width, s.ask_column.width, s.last_column.width, s.volume_column.general.width, s.zoom_factor) { }
|
||||
|
||||
public WindowSpecifics(double order_entry_panel_width, uint orders_column_width, uint bid_column_width, uint price_column_width, uint ask_column_width, uint last_column_width, uint volume_column_width) {
|
||||
public WindowSpecifics(double order_entry_panel_width, uint orders_column_width, uint bid_column_width, uint price_column_width, uint ask_column_width, uint last_column_width, uint volume_column_width, double zoom_factor) {
|
||||
this.order_entry_panel_width = order_entry_panel_width;
|
||||
this.orders_column_width = orders_column_width;
|
||||
this.bid_column_width = bid_column_width;
|
||||
@@ -466,6 +483,7 @@ namespace mbts.spark {
|
||||
this.ask_column_width = ask_column_width;
|
||||
this.last_column_width = last_column_width;
|
||||
this.volume_column_width = volume_column_width;
|
||||
this.zoom_factor = zoom_factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -496,6 +514,7 @@ namespace mbts.spark {
|
||||
source.ask_column.width = specifics.ask_column_width;
|
||||
source.last_column.width = specifics.last_column_width;
|
||||
source.volume_column.general.width = specifics.volume_column_width;
|
||||
source.zoom_factor = specifics.zoom_factor;
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace mbts.spark {
|
||||
readonly Property<LadderSettings.BadgeStyle> _badge_style;
|
||||
readonly Property<uint> _alternate_order_size;
|
||||
readonly DelegateProperty<double> _row_height;
|
||||
Func<double> _get_order_entry_panel_width;
|
||||
Func<double> _get_order_entry_panel_width, _get_zoom_factor;
|
||||
LadderSettings _current;
|
||||
LadderColumnSettingsVM
|
||||
_orders = new OrdersColumnSettingsVM(),
|
||||
@@ -34,6 +34,7 @@ namespace mbts.spark {
|
||||
_session_high_mkt_line = new LadderMarketBreakLineVM("Session High", 2, 1, false),
|
||||
_session_range_fill = new LadderMarketBreakLineVM("Session Range", 0, 0, false);
|
||||
OrderEntryPanelItemVisibilityVM
|
||||
_zoom_slider_visbility = new OrderEntryPanelItemVisibilityVM("Zoom Slider"),
|
||||
_day_visibility = new OrderEntryPanelItemVisibilityVM("Day Change"),
|
||||
_high_visibility = new OrderEntryPanelItemVisibilityVM("High"),
|
||||
_low_visibility = new OrderEntryPanelItemVisibilityVM("Low"),
|
||||
@@ -51,7 +52,7 @@ namespace mbts.spark {
|
||||
_cancel_sells_visibility = new OrderEntryPanelItemVisibilityVM("Cancel Sells"),
|
||||
_cancel_all_visibility = new OrderEntryPanelItemVisibilityVM("Cancel All");
|
||||
|
||||
public LadderSettingsVM(bool is_synthetic, Func<double> get_left_panel_width) {
|
||||
public LadderSettingsVM(bool is_synthetic, Func<double> get_left_panel_width, Func<double> get_zoom_factor) {
|
||||
this.is_synthetic = is_synthetic;
|
||||
size_presets = new ObservableEditableCollection<IMutableValueWrapperVM<uint>>(_create_size_preset_vm);
|
||||
size_presets.CollectionChanged += (s, e) => RaisePropertyChanged(nameof(size_presets_text));
|
||||
@@ -59,6 +60,7 @@ namespace mbts.spark {
|
||||
_badge_style.Changed += _badge_style_Changed;
|
||||
_alternate_order_size = backing<uint>(nameof(alternate_order_size));
|
||||
_get_order_entry_panel_width = get_left_panel_width;
|
||||
_get_zoom_factor = get_zoom_factor;
|
||||
// be sure to initialize backings before this measure_row_height call
|
||||
_row_height = backing(nameof(row_height), () => LadderRender.measure_row_height(get_result()));
|
||||
_orders.font_size_changed += on_font_size_changed;
|
||||
@@ -130,6 +132,7 @@ namespace mbts.spark {
|
||||
|
||||
public IEnumerable<OrderEntryPanelItemVisibilityVM> order_entry_panel_items {
|
||||
get {
|
||||
yield return _zoom_slider_visbility;
|
||||
yield return _day_visibility;
|
||||
yield return _high_visibility;
|
||||
yield return _low_visibility;
|
||||
@@ -169,6 +172,7 @@ namespace mbts.spark {
|
||||
_session_low_mkt_line.assign_from(_current.session_low_line);
|
||||
_session_high_mkt_line.assign_from(_current.session_high_line);
|
||||
_session_range_fill.assign_from(_current.session_range_fill);
|
||||
_zoom_slider_visbility.show = settings.zoom_slider_visible;
|
||||
_day_visibility.show = settings.day_visible;
|
||||
_high_visibility.show = settings.high_visible;
|
||||
_low_visibility.show = settings.low_visible;
|
||||
@@ -227,6 +231,8 @@ namespace mbts.spark {
|
||||
clone.cancel_sells_visible = _cancel_sells_visibility.show;
|
||||
clone.cancel_all_visible = _cancel_all_visibility.show;
|
||||
clone.order_entry_panel_initial_width = _get_order_entry_panel_width();
|
||||
clone.zoom_slider_visible = _zoom_slider_visbility.show;
|
||||
clone.zoom_factor = _get_zoom_factor();
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,12 +24,13 @@ namespace mbts.spark {
|
||||
|
||||
readonly IBus _bus;
|
||||
readonly LadderModel _model;
|
||||
readonly Property<double> _high, _low, _change, _open, _settlement, _position, _left_panel_width;
|
||||
readonly Property<double> _high, _low, _change, _open, _settlement, _position, _left_panel_width, _zoom_factor;
|
||||
//readonly Property<double> _window_width, _window_height;
|
||||
readonly Property<uint> _orders_column_width, _bid_column_width, _price_column_width, _ask_column_width, _last_column_width, _volume_column_width;
|
||||
readonly Property<ulong> _volume;
|
||||
readonly Property<bool> _auto_clear, _show_day, _show_high, _show_low, _show_open, _show_settle, _show_volume, _show_account,
|
||||
_show_tif, _show_order_type, _show_account_position, _show_size_preset, _show_auto_clear, _show_clear, _show_cancel_buys,
|
||||
_show_cancel_sells, _show_cancel_all;
|
||||
_show_cancel_sells, _show_cancel_all, _show_zoom;
|
||||
readonly Property<Brush> _bid_background, _bid_foreground, _ask_background, _ask_foreground;
|
||||
IWindowSettingsManager<LadderSettings> _settings_manager;
|
||||
ulong _misc_time = 0;
|
||||
@@ -58,6 +59,7 @@ namespace mbts.spark {
|
||||
_last_column_width = backing(nameof(last_column_width), ws.last_column_width);
|
||||
_volume_column_width = backing(nameof(volume_column_width), ws.volume_column_width);
|
||||
_left_panel_width = backing(nameof(left_panel_width), ws.order_entry_panel_width);
|
||||
_zoom_factor = backing(nameof(zoom_factor), ws.zoom_factor);
|
||||
// remember: account_positions needs to subscribe to selected_account; both the sub and account_positions need to be disposed
|
||||
var account_positions = new KeyDrivenObservableSeries<string, double>(a => _model.order_gateway.security_total_position(security.Id, a));
|
||||
_position = backing(nameof(position), account_positions.OnDispatcher());
|
||||
@@ -89,11 +91,14 @@ namespace mbts.spark {
|
||||
_show_cancel_buys = bool_prop(nameof(show_cancel_buys), s => s.cancel_buys_visible);
|
||||
_show_cancel_sells = bool_prop(nameof(show_cancel_sells), s => s.cancel_sells_visible);
|
||||
_show_cancel_all = bool_prop(nameof(show_cancel_all), s => s.cancel_all_visible);
|
||||
_show_zoom = bool_prop(nameof(show_zoom), s => s.zoom_slider_visible);
|
||||
|
||||
Property<bool> bool_prop(string name, Func<LadderSettings, bool> get_bool) =>
|
||||
backing(name, settings_manager.subscribe_current().OnDispatcher(), get_bool);
|
||||
|
||||
//
|
||||
//_window_height = backing(nameof(window_height), settings_manager.subscribe_current().OnDispatcher().Select(s => s.zoom_factor), (scale) => scale * 600);
|
||||
//_window_width = backing(nameof(window_width), settings_manager.subscribe_current().OnDispatcher().Select(s => s.zoom_factor), (scale) => scale * 420);
|
||||
position_click_command = new DelegateCommand(() => size_calculator.value = DoubleHelper.safe_convert_to_uint(Math.Abs(position)));
|
||||
send_order_command = new DelegateCommand<(double price, Side? side, bool size_from_alternate)>(send_order);
|
||||
cancel_command = new AsyncCommand<LadderModel.CancelType>(_model.cancel_orders, true);
|
||||
@@ -103,7 +108,7 @@ namespace mbts.spark {
|
||||
var vm = new WindowSettingsDialogVM<LadderSettings, LadderSettingsVM>(
|
||||
settings_manager, s => s.ladder, UserSettings.FileExtensions.LADDER,
|
||||
s => {
|
||||
var svm = new LadderSettingsVM(security.ContractCase == Security.ContractOneofCase.Synthetic, () => left_panel_width);
|
||||
var svm = new LadderSettingsVM(security.ContractCase == Security.ContractOneofCase.Synthetic, () => left_panel_width, () => zoom_factor);
|
||||
svm.load(s);
|
||||
return svm;
|
||||
}, s => s.with_window_specifics(get_save_state().get_window_specifics()));
|
||||
@@ -284,7 +289,11 @@ namespace mbts.spark {
|
||||
public bool show_cancel_buys { get => _show_cancel_buys.Value; set => _show_cancel_buys.set(value); }
|
||||
public bool show_cancel_sells { get => _show_cancel_sells.Value; set => _show_cancel_sells.set(value); }
|
||||
public bool show_cancel_all { get => _show_cancel_all.Value; set => _show_cancel_all.set(value); }
|
||||
public bool show_zoom { get => _show_zoom.Value; set => _show_zoom.set(value); }
|
||||
public double left_panel_width { get => _left_panel_width.Value; set => _left_panel_width.set(value); }
|
||||
public double zoom_factor { get => _zoom_factor.Value; set => _zoom_factor.set(value); }
|
||||
//public double window_height { get => _window_height.Value; set => _window_height.set(value); }
|
||||
//public double window_width { get => _window_width.Value; set => _window_width.set(value); }
|
||||
public uint orders_column_width { get => _orders_column_width.Value; set => _orders_column_width.set(value); }
|
||||
public uint bid_column_width { get => _bid_column_width.Value; set => _bid_column_width.set(value); }
|
||||
public uint price_column_width { get => _price_column_width.Value; set => _price_column_width.set(value); }
|
||||
@@ -296,7 +305,7 @@ namespace mbts.spark {
|
||||
|
||||
public LadderSettings get_save_state() {
|
||||
var state = _settings_manager.current_settings();
|
||||
state = state.with_window_specifics(new LadderSettings.WindowSpecifics(left_panel_width, orders_column_width, bid_column_width, price_column_width, ask_column_width, last_column_width, volume_column_width));
|
||||
state = state.with_window_specifics(new LadderSettings.WindowSpecifics(left_panel_width, orders_column_width, bid_column_width, price_column_width, ask_column_width, last_column_width, volume_column_width, zoom_factor));
|
||||
state.use_defaults = _settings_manager.instance_settings == null;
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
xmlns:uag="clr-namespace:mbts.ui.spark.algo"
|
||||
xmlns:converters="clr-namespace:mbts.ui.converters"
|
||||
xmlns:txt="clr-namespace:mbts.ui.utility.text"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
Height="600"
|
||||
Width="420"
|
||||
Background="{x:Static mbts:BTSColors.common_background_brush}"
|
||||
@@ -273,10 +274,18 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<sys:Double x:Key="MaxZoomFactor">1.5</sys:Double>
|
||||
<sys:Double x:Key="MinZoomFactor">0.5</sys:Double>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<AdornerDecorator>
|
||||
<Grid x:Name="_root">
|
||||
<Grid.LayoutTransform>
|
||||
<ScaleTransform CenterX="0" CenterY="0"
|
||||
ScaleX="{Binding ElementName=_ui_scaler, Path=Value}"
|
||||
ScaleY="{Binding ElementName=_ui_scaler, Path=Value}"
|
||||
/>
|
||||
</Grid.LayoutTransform>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{Binding left_panel_width, Mode=TwoWay, Converter={StaticResource GridLengthPixelConverter}}"
|
||||
MinWidth="60" MaxWidth="150"/>
|
||||
@@ -298,6 +307,13 @@
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</StackPanel.Resources>
|
||||
<StackPanel x:Name="_slider_stack_panel" Margin="5,5,5,0">
|
||||
<Slider x:Name="_ui_scaler" Value="{Binding zoom_factor}"
|
||||
Minimum="{StaticResource MinZoomFactor}"
|
||||
Maximum="{StaticResource MaxZoomFactor}"
|
||||
Width="{Binding Path=ActualWidth, ElementName=_slider_stack_panel}"
|
||||
Visibility="{Binding show_zoom, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
</StackPanel>
|
||||
<Grid Margin="2,4,2,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
@@ -517,29 +533,12 @@
|
||||
<Setter Property="Foreground" Value="{Binding foreground}"/>
|
||||
<Setter Property="TextLines" Value="{Binding text_lines}" />
|
||||
<Setter Property="Width" Value="{x:Static controls:Badge.DefaultWidth}"/>
|
||||
<Setter Property="ToolTip" Value="{Binding description}" />
|
||||
<Setter Property="IsExchangeOrder" Value="{Binding is_exchange_order}" />
|
||||
<Setter Property="HasMultipleOrdersAtLevel" Value="{Binding has_multiple_orders_at_same_level}" />
|
||||
<Setter Property="IsHedgeOrder" Value="{Binding is_hedge_order}" />
|
||||
<Setter Property="ParentAlgoType" Value="{Binding parent_algo_type}" />
|
||||
<Setter Property="ParentAlgoTypeBrush" Value="{Binding parent_algo_type_brush}" />
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="ContextMenu">
|
||||
<Setter.Value>
|
||||
<ContextMenu>
|
||||
<MenuItem Command="{Binding cancel_command}" Header="Cancel">
|
||||
<MenuItem.Icon>
|
||||
<Image Source="{StaticResource CancelSelected}" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Command="{Binding detach_command}" Header="Detach">
|
||||
<MenuItem.Icon>
|
||||
<Image Source="{StaticResource ModifyOrder}" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{Binding hoverbackground}" />
|
||||
|
||||
@@ -5,9 +5,12 @@ using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class LadderView : UserControl {
|
||||
readonly double _max_zoom, _min_zoom;
|
||||
|
||||
internal LadderView(LadderVM vm) {
|
||||
InitializeComponent();
|
||||
_column_headers_presenter.Columns = new GridViewColumnCollection();
|
||||
@@ -16,8 +19,58 @@ namespace mbts.spark {
|
||||
this.vm = vm;
|
||||
DataContext = vm;
|
||||
DataContextChanged += datacontext_changed;
|
||||
if (TryFindResource("MaxZoomFactor") is double found_max) {
|
||||
_max_zoom = found_max;
|
||||
} else {
|
||||
// is there a better way of not specifying the constant twice ?
|
||||
// once in the resource dictionary here and again in the xaml, seems like there has to be a better way
|
||||
_max_zoom = 1.5;
|
||||
}
|
||||
// ditto
|
||||
if (TryFindResource("MinZoomFactor") is double found_min) {
|
||||
_min_zoom = found_min;
|
||||
} else {
|
||||
_min_zoom = 0.5;
|
||||
}
|
||||
_ui_scaler.MouseDoubleClick += _ui_scaler_MouseDoubleClick;
|
||||
this.PreviewMouseWheel += LadderView_PreviewMouseWheel;
|
||||
this.PreviewMouseDown += LadderView_PreviewMouseDown;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// (Left|Right) CTRL + Wheel to zoom
|
||||
// This is neat b/c the user doesn't even need to have the slider visible to zoom
|
||||
private void LadderView_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e) {
|
||||
base.OnPreviewMouseWheel(e);
|
||||
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) {
|
||||
var delta = (e.Delta > 0) ? 0.1 : -0.1;
|
||||
var proposed_scaler_value = _ui_scaler.Value + delta;
|
||||
// Here we enforce the same contraints that are on the gui slider regarding min and max
|
||||
if (_min_zoom < proposed_scaler_value && proposed_scaler_value < _max_zoom) {
|
||||
_ui_scaler.Value = proposed_scaler_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (Left|Right) CTRL + Wheel Click to reset
|
||||
private void LadderView_PreviewMouseDown(object sender, MouseButtonEventArgs e) {
|
||||
base.OnPreviewMouseDown(e);
|
||||
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) {
|
||||
if (e.MiddleButton == MouseButtonState.Pressed) {
|
||||
_ui_scaler_MouseDoubleClick(_ui_scaler, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Double click on scaler itself to reset
|
||||
private void _ui_scaler_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e) {
|
||||
if (sender is Slider _the_slider) {
|
||||
_the_slider.Value = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void datacontext_changed(object sender, DependencyPropertyChangedEventArgs e) {
|
||||
this.vm = DataContext as LadderVM;
|
||||
}
|
||||
|
||||
@@ -28,31 +28,10 @@ namespace mbts.spark {
|
||||
static readonly int sod_hh = (int)config.Configuration.get<bts.config.SODTime>().sod_hh;
|
||||
static readonly int sod_mm = (int)config.Configuration.get<bts.config.SODTime>().sod_mm;
|
||||
static readonly TimeZoneInfo central_tzi = TimeZoneInfo.FindSystemTimeZoneById(cst_id);
|
||||
static readonly DateTime _start_of_trading_day_utc;
|
||||
static readonly DateTime _adjusted_central_sod;
|
||||
|
||||
|
||||
static readonly DateTime unadjusted_central_sod = TimeZoneInfo.ConvertTimeFromUtc(
|
||||
DateTime.UtcNow.Date.Add(new TimeSpan(sod_hh, sod_mm, 0)),
|
||||
central_tzi
|
||||
);
|
||||
|
||||
static MarketSchedule() {
|
||||
var cme_schedule = config.Configuration.get<bts.md.CMETradingSchedule>();
|
||||
_cme_gen_open_hrs = cme_schedule?.gen_open_hour ?? 8.5;
|
||||
|
||||
_adjusted_central_sod = !central_tzi.IsDaylightSavingTime(unadjusted_central_sod) ? unadjusted_central_sod.AddHours(1) : unadjusted_central_sod;
|
||||
|
||||
// Note that this isn't the acutal start of the trading day
|
||||
// but it's close enough for pnl purposes.
|
||||
// In reality, instruments have different schedules
|
||||
|
||||
// if now is less than the current sod, then we me move the sod back a day to account for the trading day start
|
||||
if (_adjusted_central_sod >= TimeZoneInfo.ConvertTime(DateTime.Now, central_tzi)) {
|
||||
_adjusted_central_sod = _adjusted_central_sod.AddDays(-1);
|
||||
}
|
||||
var local_time_zone_sod = TimeZoneInfo.ConvertTime(_adjusted_central_sod, central_tzi, TimeZoneInfo.Local);
|
||||
_start_of_trading_day_utc = local_time_zone_sod.ToUniversalTime();
|
||||
}
|
||||
// for object initializers
|
||||
public MarketSchedule() { }
|
||||
@@ -73,6 +52,27 @@ namespace mbts.spark {
|
||||
return last_open;
|
||||
}
|
||||
|
||||
// DateTime? used here b/c we can't set a default parameter with DateTime.Now
|
||||
// since it's not a compile-time constant
|
||||
public static DateTime get_start_of_trading_day(DateTime? target_datetime = null, DateTimeKind dtk = DateTimeKind.Local ) {
|
||||
if (target_datetime == null) target_datetime = DateTime.Now;
|
||||
var dst_adjusted_central_sod = new DateTimeOffset(
|
||||
new DateTime(target_datetime.Value.Date.Add(new TimeSpan(sod_hh, sod_mm, 0)).Ticks, DateTimeKind.Unspecified),
|
||||
central_tzi.BaseUtcOffset
|
||||
);
|
||||
|
||||
// if we're not currently past the start of the trading day, then the start must be yesterday
|
||||
var temp = dst_adjusted_central_sod;
|
||||
if (temp >= TimeZoneInfo.ConvertTime(target_datetime.Value, central_tzi)) {
|
||||
temp = dst_adjusted_central_sod.AddDays(-1);
|
||||
}
|
||||
var local_time_zone_sod = TimeZoneInfo.ConvertTime(temp.DateTime, central_tzi, TimeZoneInfo.Local);
|
||||
if (dtk == DateTimeKind.Utc) {
|
||||
return local_time_zone_sod.ToUniversalTime();
|
||||
}
|
||||
return local_time_zone_sod;
|
||||
}
|
||||
|
||||
public static DateTime get_utctimestamp_as_localtime(string s) {
|
||||
return DateTimeOffset.ParseExact(s, "yyyyMMddHHmmssffffff", null, System.Globalization.DateTimeStyles.AssumeUniversal).ToLocalTime().LocalDateTime;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace mbts.spark.utilities {
|
||||
static readonly OrderMetadata _default_order_metadata = new OrderMetadata { KeyBinding = "", Color = BTSColors.Orders.spreader_order };
|
||||
static readonly CustomOrderDefinition.CustomOneofCase[]
|
||||
// The following standard types are not yet ready for production use:
|
||||
// CustomOrderDefinition.CustomOneofCase.Tick,
|
||||
// CustomOrderDefinition.CustomOneofCase.Dynamic,
|
||||
_standard_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Market,
|
||||
@@ -37,6 +36,7 @@ namespace mbts.spark.utilities {
|
||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
||||
CustomOrderDefinition.CustomOneofCase.Join,
|
||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
||||
CustomOrderDefinition.CustomOneofCase.Oco,
|
||||
CustomOrderDefinition.CustomOneofCase.Offset,
|
||||
CustomOrderDefinition.CustomOneofCase.Top,
|
||||
CustomOrderDefinition.CustomOneofCase.Trigger,
|
||||
@@ -165,7 +165,8 @@ namespace mbts.spark.utilities {
|
||||
}
|
||||
#endregion
|
||||
|
||||
public OrderDefinition order(SecurityImmutable security) => order(security, _admin_model.get_default_account());
|
||||
public OrderDefinitionImmutable order(SecurityImmutable security) =>
|
||||
new OrderDefinitionImmutable(order(security, _admin_model.get_default_account()));
|
||||
public OrderDefinition order(SecurityImmutable security, AccountTuple? account) {
|
||||
var rv = new OrderDefinition {
|
||||
SecurityKey = security.Id,
|
||||
@@ -227,6 +228,15 @@ namespace mbts.spark.utilities {
|
||||
Child = custom_order(),
|
||||
Hedge = custom_order(),
|
||||
};
|
||||
public OCOOrderDefinition oco_order() => new OCOOrderDefinition
|
||||
{
|
||||
Child = custom_order(),
|
||||
Other = custom_order(),
|
||||
};
|
||||
public PricerOrderDefinition pricer_order() => new PricerOrderDefinition
|
||||
{
|
||||
Child = custom_order(),
|
||||
};
|
||||
public OffsetOrderDefinition offset_order() => new OffsetOrderDefinition
|
||||
{
|
||||
Child = custom_order(),
|
||||
@@ -261,6 +271,8 @@ namespace mbts.spark.utilities {
|
||||
case CustomOrderDefinition.CustomOneofCase.Join: rv.Join = join_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Top: rv.Top = top_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Cover: rv.Cover = cover_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Oco: rv.Oco = oco_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Pricer: rv.Pricer = pricer_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Offset: rv.Offset = offset_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Trigger: rv.Trigger = trigger_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: rv.Spreader = spreader_order(); break;
|
||||
@@ -284,6 +296,8 @@ namespace mbts.spark.utilities {
|
||||
case CustomOrderDefinition.CustomOneofCase.Join: return new OrderMetadata { KeyBinding = "J", Color = BTSColors.Orders.join_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Top: return new OrderMetadata { KeyBinding = "D", Color = BTSColors.Orders.top_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Cover: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.cover_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Oco: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.oco_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Pricer: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.pricer_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Offset: return new OrderMetadata { KeyBinding = "O", Color = BTSColors.Orders.tick_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Trigger: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.trigger_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: goto default;
|
||||
@@ -305,6 +319,8 @@ namespace mbts.spark.utilities {
|
||||
case OrderState.CustomOneofCase.Join: return new OrderMetadata { KeyBinding = "J", Color = BTSColors.Orders.join_order };
|
||||
case OrderState.CustomOneofCase.Top: return new OrderMetadata { KeyBinding = "D", Color = BTSColors.Orders.top_order };
|
||||
case OrderState.CustomOneofCase.Cover: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.cover_order };
|
||||
case OrderState.CustomOneofCase.Oco: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.oco_order };
|
||||
case OrderState.CustomOneofCase.Pricer: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.pricer_order };
|
||||
case OrderState.CustomOneofCase.Offset: return new OrderMetadata { KeyBinding = "O", Color = BTSColors.Orders.tick_order };
|
||||
case OrderState.CustomOneofCase.Trigger: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.trigger_order };
|
||||
case OrderState.CustomOneofCase.Spreader: goto default;
|
||||
@@ -344,6 +360,8 @@ namespace mbts.spark.utilities {
|
||||
case nameof(JoinOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Join;
|
||||
case nameof(TopOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Top;
|
||||
case nameof(CoverOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Cover;
|
||||
case nameof(OCOOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Oco;
|
||||
case nameof(PricerOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Pricer;
|
||||
case nameof(OffsetOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Offset;
|
||||
case nameof(TriggerOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Trigger;
|
||||
case nameof(DynamicOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Dynamic;
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace bts.md
|
||||
{
|
||||
foreach (var v in values)
|
||||
{
|
||||
var smd = new SecurityMD(v.security, v.handle);
|
||||
var smd = new SecurityMD(new SecurityImmutable(v.security), v.handle);
|
||||
_exchange_sec_map.AddOrUpdate(smd.security.Id, smd, (_, __) => smd);
|
||||
}
|
||||
_version += 1;
|
||||
|
||||
@@ -37,17 +37,17 @@ namespace bts.api {
|
||||
$"{symbol}{DateFormatter.Format(future.ExpirationYear, future.ExpirationMonth, future.ExpirationDay, DateFormat.MonthCodeLastDigitYear)}" :
|
||||
exch_desc;
|
||||
|
||||
static string _format(SecurityImmutable security) {
|
||||
switch (security.ContractCase) {
|
||||
case ContractOneofCase.Future: return format_future(security.Symbol, security.glbx_desc(), security.Future);
|
||||
static string _format(SecurityImmutable? security) {
|
||||
switch (security?.ContractCase) {
|
||||
case ContractOneofCase.Future: return format_future(security?.Symbol, security?.glbx_desc(), security.Value.Future);
|
||||
case ContractOneofCase.Spread:
|
||||
if (security.HasGlbx) {
|
||||
if (security?.HasGlbx == true) {
|
||||
// In case we ever bring it back, pretty printed spread names can be found here:
|
||||
// GlbxSecurityFormatter.FormatSpread(security, out string result)
|
||||
return security.Glbx.SecurityDesc;
|
||||
return security?.Glbx.SecurityDesc;
|
||||
}
|
||||
return string.Join("", _format_future_legs(SecurityHelper.get_flat_future_legs(security.Spread.Legs)));
|
||||
case ContractOneofCase.Synthetic: return security.Synthetic.Name;
|
||||
return string.Join("", _format_future_legs(SecurityHelper.get_flat_future_legs(security?.Spread.Legs)));
|
||||
case ContractOneofCase.Synthetic: return security?.Synthetic.Name;
|
||||
default: return "Unknown-security";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,24 +66,10 @@ namespace bts.api {
|
||||
public static readonly IComparer<SecurityImmutable> InstanceImmutable = Comparer<SecurityImmutable>.Create(Compare);
|
||||
|
||||
public static int Compare(Security x, Security y) {
|
||||
if (x.ContractCase == ContractOneofCase.Future) return y.ContractCase == ContractOneofCase.Future ? Compare(x.Symbol, x.Future, y.Symbol, y.Future) : -1;
|
||||
else if (y.ContractCase == ContractOneofCase.Future) return 1;
|
||||
var xlegs = get_flat_future_legs(x).ToList();
|
||||
var ylegs = get_flat_future_legs(y).ToList();
|
||||
int max = Math.Min(xlegs.Count, ylegs.Count);
|
||||
for (int i = 0; i < max; i++) {
|
||||
int rv = Compare(xlegs[i].symbol, xlegs[i].future, ylegs[i].symbol, ylegs[i].future);
|
||||
if (rv != 0) return rv;
|
||||
}
|
||||
for (int i = 0; i < max; i++) {
|
||||
int rv = xlegs[i].quantity.CompareTo(ylegs[i].quantity);
|
||||
if (rv != 0) return rv;
|
||||
}
|
||||
for (int i = 0; i < max; i++) {
|
||||
int rv = xlegs[i].exchange.CompareTo(ylegs[i].exchange);
|
||||
if (rv != 0) return rv;
|
||||
}
|
||||
return xlegs.Count.CompareTo(ylegs.Count);
|
||||
if (ReferenceEquals(x, y)) return 0; // x ==y
|
||||
if (x == null) return 1; // y > x
|
||||
if (y == null) return -1; // x > y
|
||||
return Compare(new SecurityImmutable(x), new SecurityImmutable(y));
|
||||
}
|
||||
|
||||
public static int Compare(SecurityImmutable x, SecurityImmutable y) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.4.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
|
||||
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
|
||||
<PackageReference Include="System.Reactive" Version="3.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace bts {
|
||||
enum Level {
|
||||
NoLog,
|
||||
Diag,
|
||||
Info,
|
||||
Warn,
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace bts.collections {
|
||||
if (_nursery.TryPopValue(k, out SortedList<Node> children)) {
|
||||
// remove orphans from _tree (orphan-nodes must reside in the main _tree until parents arrive)
|
||||
foreach (var n in children)
|
||||
_tree.Remove(n);
|
||||
_assert_remove(_tree, n);
|
||||
node = new Node(item, children);
|
||||
} else node = new Node(item, _comparer);
|
||||
var pk = parent(item);
|
||||
@@ -84,13 +84,10 @@ namespace bts.collections {
|
||||
if (!_index.TryGetValue(k, out Node node)) {
|
||||
return false;
|
||||
} else {
|
||||
var newval = replace(node.value);
|
||||
Log.assert(id(newval).Equals(k));
|
||||
_replace(node, newval);
|
||||
_replace(node, replace);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Remove(T item) => Remove(id(item));
|
||||
|
||||
@@ -185,44 +182,97 @@ namespace bts.collections {
|
||||
}
|
||||
}
|
||||
|
||||
void _replace(Node node, T newval) {
|
||||
TKey pk = parent(newval);
|
||||
TKey old_pk = parent(node.value);
|
||||
if (pk.Equals(old_pk)) {
|
||||
SortedList<Node> node_list;
|
||||
if (pk.Equals(_root_id))
|
||||
node_list = _tree;
|
||||
else if (_index.TryGetValue(pk, out Node pnode)) {
|
||||
node_list = pnode.children;
|
||||
} else if (!_nursery.TryGetValue(pk, out node_list)) {
|
||||
throw new KeyNotFoundException($"Replace was called with a parent-key ({pk}) that was not found in _index or _nursery; item-key was ({id(newval)}).");
|
||||
}
|
||||
node_list.Remove(node);
|
||||
node.value = newval;
|
||||
node_list.Add(node);
|
||||
} else {
|
||||
if (old_pk.Equals(_root_id)) {
|
||||
_tree.Remove(node);
|
||||
} else {
|
||||
if (_index.TryGetValue(old_pk, out Node pnode))
|
||||
pnode.children.Remove(node);
|
||||
else {
|
||||
_remove_from_nursery(old_pk, node);
|
||||
}
|
||||
}
|
||||
node.value = newval;
|
||||
if (pk.Equals(_root_id)) {
|
||||
void _replace(Node node, T new_value) =>
|
||||
_replace(node, _ => new_value);
|
||||
|
||||
void _replace(Node node, Func<T, T> replace_value) {
|
||||
/// STEPS (outline)
|
||||
/// 1) remove node with current value
|
||||
/// 2) replace node.value
|
||||
/// 3) re-insert node with new value
|
||||
|
||||
/// Implementation (details):
|
||||
/// there are 3 scenarios in which step 1 (remove node...) may occur
|
||||
/// a) node is a root node
|
||||
/// - remove it from <see cref="_tree"/>.
|
||||
/// b) node is a child node who's parent is present
|
||||
/// - remove it from the parent <see cref="Node.children"/>
|
||||
/// c) the node is a child node who's parent is NOT present
|
||||
/// - remove it from <see cref="_nursery"/> AND <see cref="_tree"/>.
|
||||
/// In each scenario, after the node is removed, the new parent will be assigned (<see cref="new_pk"/>).
|
||||
/// If the parent has NOT changed, assign the node back to the same list(s), then return.
|
||||
/// If the parent HAS changed, determine the list(s) in which the node needs to be inserted, insert the node, then return.
|
||||
TKey old_pk = parent(node.value), new_pk;
|
||||
if (old_pk.Equals(_root_id)) {
|
||||
// a)
|
||||
_assert_remove(_tree, node);
|
||||
_assert_replace(ref node, replace_value);
|
||||
new_pk = parent(node.value);
|
||||
if (new_pk.Equals(old_pk)) {
|
||||
// the parent has NOT changed; re-insert the node, then return.
|
||||
_tree.Add(node);
|
||||
} else if (_index.TryGetValue(pk, out Node pnode)) {
|
||||
pnode.children.Add(node);
|
||||
} else {
|
||||
if (!_nursery.TryGetValue(pk, out SortedList<Node> nursery_list)) {
|
||||
_nursery.Add(pk, nursery_list = new SortedList<Node>(_comparer));
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (_index.TryGetValue(old_pk, out Node old_pnode)) {
|
||||
// b)
|
||||
_assert_remove(old_pnode.children, node);
|
||||
_assert_replace(ref node, replace_value);
|
||||
new_pk = parent(node.value);
|
||||
if (new_pk.Equals(old_pk)) {
|
||||
// the parent has NOT changed; re-insert the node, then return.
|
||||
old_pnode.children.Add(node);
|
||||
return;
|
||||
}
|
||||
} else if (_nursery.TryGetValue(old_pk, out SortedList<Node> nursery_list)) {
|
||||
// c)
|
||||
_assert_remove(nursery_list, node);
|
||||
_assert_remove(_tree, node);
|
||||
_assert_replace(ref node, replace_value);
|
||||
new_pk = parent(node.value);
|
||||
if (new_pk.Equals(old_pk)) {
|
||||
// the parent has NOT changed; re-insert the node, then return.
|
||||
nursery_list.Add(node);
|
||||
_tree.Add(node);
|
||||
return;
|
||||
} else {
|
||||
// the parent HAS changed; if the old nursery_list is empty, remove it.
|
||||
if (nursery_list.Count == 0)
|
||||
_nursery.Remove(old_pk);
|
||||
}
|
||||
} else {
|
||||
// error)
|
||||
throw new InvalidOperationException($"Unknown parent key '{old_pk}'.");
|
||||
}
|
||||
// the parent HAS changed. Determine the list(s) in which the node needs to inserted.
|
||||
if (new_pk.Equals(_root_id)) {
|
||||
_tree.Add(node);
|
||||
} else if (_index.TryGetValue(new_pk, out Node new_pnode)) {
|
||||
new_pnode.children.Add(node);
|
||||
} else {
|
||||
if (!_nursery.TryGetValue(new_pk, out SortedList<Node> new_nursery_list)) {
|
||||
_nursery.Add(new_pk, new_nursery_list = new SortedList<Node>(_comparer));
|
||||
}
|
||||
new_nursery_list.Add(node);
|
||||
_tree.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to removes the node from the list <see cref="SortedList{T}.Remove"/> and throws an exception if it does not succeed.
|
||||
/// </summary>
|
||||
static void _assert_remove(SortedList<Node> list, Node node) {
|
||||
if (!list.Remove(node))
|
||||
throw new InvalidOperationException("Failed to remove node.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces <see cref="Node.value"/> using the specified delegate, and throws an exception if the new value's id is different from the old value's id.
|
||||
/// </summary>
|
||||
void _assert_replace(ref Node node, Func<T, T> replace_value) {
|
||||
TKey old_key = id(node.value);
|
||||
node.value = replace_value(node.value);
|
||||
if (!id(node.value).Equals(old_key))
|
||||
throw new InvalidOperationException("Replace op changed Node's id.");
|
||||
}
|
||||
|
||||
void _remove_from_nursery(TKey pk, Node node) {
|
||||
|
||||
@@ -9,12 +9,12 @@ using System.Threading.Tasks;
|
||||
namespace bts.test {
|
||||
public static class InProcessTestRunner {
|
||||
|
||||
public static void run_test_syncronously(string v, TestEnumerator enumerator, Action<TestResult> report) {
|
||||
public static void run_test_synchronously(string v, TestEnumerator enumerator, Action<TestResult> report) {
|
||||
foreach (var t in enumerator.find(v)) {
|
||||
run_test_syncronously(t, report);
|
||||
run_test_synchronously(t, report);
|
||||
}
|
||||
}
|
||||
public static void run_test_syncronously(TestMethod test, Action<TestResult> report) {
|
||||
public static void run_test_synchronously(TestMethod test, Action<TestResult> report) {
|
||||
var result = new TestResult() {
|
||||
TestName = test.full_name,
|
||||
Status = TestResult.Types.Step.Setup,
|
||||
|
||||
@@ -2,8 +2,16 @@
|
||||
|
||||
namespace bts.test {
|
||||
|
||||
public enum TestType {
|
||||
Unit, System, Shell
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class TestGroupAttribute : Attribute {
|
||||
public TestGroupAttribute() {
|
||||
this.TestType = TestType.Unit;
|
||||
}
|
||||
public TestType TestType;
|
||||
}
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class TestAttribute : Attribute {
|
||||
|
||||
@@ -7,23 +7,34 @@ namespace bts.test {
|
||||
|
||||
public class TestEnumerator {
|
||||
public TestEnumerator(params string[] assemblies) {
|
||||
TestGroups =
|
||||
TestGroups =
|
||||
assemblies
|
||||
.Select(Assembly.Load)
|
||||
.SelectMany(a => a.GetTypes())
|
||||
.Where(t => t.GetCustomAttribute<TestGroupAttribute>() != null)
|
||||
.Select(t =>
|
||||
new TestClass(t,
|
||||
new TestClass(t,
|
||||
t.GetCustomAttribute<TestGroupAttribute>().TestType,
|
||||
t.GetMethods()
|
||||
.Where(m => m.GetCustomAttribute<TestAttribute>() != null)
|
||||
.Select(m => new TestMethod(t, m))
|
||||
.ToList()))
|
||||
.ToList();
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<TestClass> TestGroups { get; }
|
||||
|
||||
public IEnumerable<TestClass> get_groups_of_type(TestType tt) {
|
||||
return TestGroups.Where(wt => wt.test_type == tt);
|
||||
}
|
||||
|
||||
public IEnumerable<TestMethod> Tests => TestGroups.SelectMany(t => t.methods);
|
||||
|
||||
public IEnumerable<TestMethod> get_of_type(TestType tt) {
|
||||
return TestGroups.Where(wt => wt.test_type == tt).SelectMany(t => t.methods);
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<TestMethod> find(string name) {
|
||||
foreach (var g in TestGroups) {
|
||||
foreach (var t in g.methods) {
|
||||
@@ -41,13 +52,15 @@ namespace bts.test {
|
||||
|
||||
|
||||
public class TestClass {
|
||||
public TestClass(Type type, IEnumerable<TestMethod> methods) {
|
||||
public TestClass(Type type, TestType test_type, IEnumerable<TestMethod> methods) {
|
||||
this.type = type;
|
||||
this.test_type = test_type;
|
||||
this.methods = methods;
|
||||
}
|
||||
public string name => type.Name;
|
||||
public Assembly assembly => type.Assembly;
|
||||
public Type type { get; }
|
||||
public TestType test_type { get; }
|
||||
public IEnumerable<TestMethod> methods { get; }
|
||||
public override string ToString() {
|
||||
return $"[TestGroup:{name}]";
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.4.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
|
||||
<PackageReference Include="System.Reactive" Version="3.1.1" />
|
||||
<PackageReference Include="System.Reactive.PlatformServices" Version="3.1.1" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
|
||||
@@ -1,8 +1,31 @@
|
||||
# BTS Spark
|
||||
|
||||
|
||||
Our offering consists of a client UI [Spark Client](https://www.bluetradingsystems.com/images/Images/spark/02.06.19/Ladder.png), a high performance professional futures front end, connected via our proprietary protocol over TCP to our Spark Server, which in turn connects to execution venues via their native protocols (typically FIX/TCP for execution and some proprietary protocol over UDP multicast for market data).
|
||||
|
||||

|
||||
|
||||
|
||||
# BTS Spark Client SDK
|
||||
|
||||
#### Getting Started
|
||||
We currently offer two API extension points. This repository, our BTS **Spark Client SDK** allows users to control our Spark Server externally, that is, out of process, using the exact same protocol our own Spark Client does. In other words, a client application developed via the Spark Client SDK, say a VB .NET UI, would be able to provide any functionality our own UI provides.
|
||||
|
||||
## Git
|
||||
We currently offer bindings for the Spark protocol to C++, C# and Excel clients. Since the underlying transport is TCP, clients developed via the Spark Client SDK can be located anywhere in the world, as long as they can reach the Spark Server via TCP. We do require a VPN or a secure network, since the Spark Protocol is currently not yet encrypted.
|
||||
|
||||
Our second API extension point is intended for users that require adding functionality to our Spark Server. If you are interested in extending the server process through custom order types, market data sources, or execution gateways, please see the [Spark Server SDK](https://github.com/BlueTradingSystems/spark_sdk).
|
||||
|
||||
|
||||
### In Depth
|
||||
|
||||
* [Interface Overview](doc/Overview.md)
|
||||
* [Threading Notes](doc/Threading.md)
|
||||
|
||||
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Git
|
||||
|
||||
* First clone the spark_client_sdk repo
|
||||
|
||||
@@ -11,7 +34,7 @@
|
||||
```
|
||||
|
||||
|
||||
## Build
|
||||
### Build
|
||||
* Use msbuild to target windows / .net 4.71
|
||||
* Install studio here if you don't have it: https://visualstudio.microsoft.com/downloads/
|
||||
* Use VSCode for editing, it's better: https://code.visualstudio.com/
|
||||
@@ -24,7 +47,7 @@ spark_client_sdk\src\spark_sdk> msbuild
|
||||
|
||||
* spark_sdk binaries will be produced in the bin\Debug directory
|
||||
|
||||
## Connecting to Spark Server
|
||||
### Connecting to Spark Server
|
||||
|
||||
* Start your exe using the -ConfigServer parameter to point at a running Spark Server
|
||||
```
|
||||
@@ -35,7 +58,7 @@ spark_client_sdk\src\spark_sdk> msbuild
|
||||
include: "http://location:8000/config/"
|
||||
```
|
||||
|
||||
## Examples
|
||||
### Examples
|
||||
* Make sure you can connect to a running server
|
||||
* run spark_sdk.exe with no parameters to see help
|
||||
```
|
||||
@@ -58,22 +81,15 @@ Arguments:
|
||||
* [Driver](src/spark_sdk/samples/SDKMain.cs)
|
||||
|
||||
|
||||
## Excel
|
||||
### Excel
|
||||
```
|
||||
spark_sdk\bin\Debug\net471> start spark_excel-addin64-packed.xll
|
||||
```
|
||||
* This will start excel and load the bts addin
|
||||
* Check the [```excel```](excel/) directory for an example spreadsheet
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
* Our client side API is based on Google's protocol buffers; the message types are all protos. https://developers.google.com/protocol-buffers/
|
||||
|
||||
* We extended the protoc compiler to generate c#, c++, excel and js bindings to the services provided by our server process (https://github.com/BlueTradingSystems/spark_sdk)
|
||||
|
||||
|
||||
* ### Dotnet core
|
||||
### Dotnet core
|
||||
|
||||
* Right now we only support c# Windows 10 clients, but the package targets both net471 and .net core 2.1.6, so in theory one could run a .net core or c++ client on linux with a little work.
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
|
||||
## Spark Client SDK Overview
|
||||
|
||||
The high level description of our order entry interface is as follows:
|
||||
|
||||
```
|
||||
service OrderService {
|
||||
// RPCs
|
||||
rpc create_order(Create) returns (Response)
|
||||
rpc cancel_order(Cancel) returns (Response)
|
||||
rpc update_order(Update) returns (Response)
|
||||
// Streams
|
||||
rpc orders (OrdersSub) returns (stream OrderRecord);
|
||||
rpc fills (FillsSub) returns (stream Fill);
|
||||
}
|
||||
```
|
||||
|
||||
This description uses the Google Protocol Buffers syntax to define the server interface (we use our own little compiler to generate bindings to this interface in C#, C++ and Excel). Using this server interface we can create, cancel or update orders, using the Remote Procedure Calls (RPCs) defined these orders can be exchange native orders or our own high level algo orders, or new order types created by users via the Spark Server SDK). We can monitor order state by subscribing to the orders stream above, which produces a a stream of OrderRecord objects, each capturing the latest state of a particular order, as it goes through it life cycle.
|
||||
|
||||
An OrderRecord looks like this:
|
||||
|
||||
```
|
||||
message OrderRecord {
|
||||
uint32 key;
|
||||
uint32 parent_key;
|
||||
OrderDefinition def;
|
||||
OrderState state;
|
||||
}
|
||||
```
|
||||
|
||||
We have an order key, a possibly null parent order key, an OrderDefinition record, which defines the basic elements of the order, namely its type, side, security, account, etc, and an OrderState record, which captures the latest state of the order:
|
||||
|
||||
```
|
||||
message OrderState {
|
||||
// Standard order state, shared by all order types
|
||||
OrderStatus status ;
|
||||
double price ;
|
||||
uint32 quantity ;
|
||||
uint32 filled_quantity;
|
||||
double last_price;
|
||||
double avg_price;
|
||||
string last_msg;
|
||||
google.protobuf.Timestamp created;
|
||||
google.protobuf.Timestamp last_change;
|
||||
// Custom order state, specific to the order type in question
|
||||
oneof custom {
|
||||
// native order types
|
||||
MarketOrderState market;
|
||||
LimitOrderState limit;
|
||||
StopOrderState stop;
|
||||
StopLimitOrderState stop_limit;
|
||||
// algo order types
|
||||
IcebergOrderState iceberg;
|
||||
SpreaderOrderState spreader;
|
||||
TimeBoxOrderState time_box;
|
||||
TickOrderState tick;
|
||||
LeanOrderState lean;
|
||||
JoinOrderState join;
|
||||
TopOrderState top;evident
|
||||
TriggerOrderState trigger;
|
||||
CoverOrderState cover;
|
||||
OffsetOrderState offset;
|
||||
DynamicOrderState dynamic;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This OrderState record consists of a common part, applicable to all order types, and a custom part that is specific to the particular order type the record is associated with. Most of the standard fields should be self-explanatory, the latest price, the latest quantity, the filled quantity, etc. The status enumerate field tells you which state the order is in. We currently define the following states:
|
||||
|
||||
```
|
||||
enum OrderStatus {
|
||||
// The order has been constructed but not acted on. Inactive orders
|
||||
// can be abandoned without consequences. This state should only be
|
||||
// used internally by the order manager, i.e. it should never appear
|
||||
// in externally visible orders.
|
||||
ORDER_STATUS_INACTIVE = 0;
|
||||
// The order is currently working, i.e. its algorithm is operating.
|
||||
ORDER_STATUS_WORKING = 1;
|
||||
// The order has completed successfully and accomplished its
|
||||
// goal. This is a terminal state.
|
||||
ORDER_STATUS_COMPLETED = 2;
|
||||
// The order was terminated orderly before it had a chance to
|
||||
// complete. This is a terminal state.
|
||||
ORDER_STATUS_CANCELED = 3;
|
||||
// The order has encountered an unexpected condition from which it
|
||||
// cannot recover. This a terminal state.
|
||||
ORDER_STATUS_ERROR = 4;
|
||||
// The last known state for this order was ORDER_STATUS_WORKING, but
|
||||
// since then the execution gateway has signaled a disconnect. This
|
||||
// is not a terminal state.
|
||||
ORDER_STATUS_DISCONNECTED = 5;
|
||||
// The order is initializiYou can ng. It cannot be modified at this
|
||||
point.
|
||||
ORDER_STATUS_PENDING_WORKING = 6;
|
||||
// The order is invalid and was rejected by Spark (not the
|
||||
// exchange), on creation. This is a terminal state.
|
||||
ORDER_STATUS_REJECTED_BY_SPARK= 7;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Threading
|
||||
Check out our API Threading Doc [here](Threading.md).
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
# Client API Threading
|
||||
|
||||
BTS API provides two types of calls. There are RPC (remote procedure calls) which accept an argument and return a value and there are streams of tabular data which we call subscriptions. All communication to and from the server happens on a connection thread owned by the ServiceContext object and data must be marshaled back to the right context when the call is complete.
|
||||
|
||||
## RPC
|
||||
|
||||
### C#
|
||||
|
||||
All API calls provide both a synchronous and [asynchronous](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/async) version. If you are calling the API from a WPF or Windows Forms application you should always prefer the async version of the method because it will efficiently make the call and resume execution on the correct thread without blocking or lagging user interactions. The synchronous calls are intended for use in console applications where nothing else needs use of the thread until the data is available.
|
||||
|
||||
The async version of the method will return a `Result<T>` type which is used to signal if an error occurred during the call. You should test the Result.success to see if the call succeeded before using Result.value.
|
||||
|
||||
```csharp
|
||||
var s = context.locate<api.user.AuthService>();
|
||||
var result = await s.get_users_async(new GetUsersRequest());
|
||||
if (result.success) {
|
||||
Console.WriteLine(result.value);
|
||||
}
|
||||
else {
|
||||
Console.WriteLine($"Error {result.error_message}");
|
||||
}
|
||||
```
|
||||
|
||||
### C++
|
||||
|
||||
All C++ API calls also provide both a blocking synchronous version and a callback based asynchronous version. In the asynchronous case the callback will execute on the ServiceContext thread and you will need to handle marshaling that data to the appropriate thread. Just like in c# the callback will receive a `Result<T>` which will indicate success or failure. `Result.value` returns an `std::shared_ptr` so you have control over the lifetime of the value.
|
||||
|
||||
## Subscriptions
|
||||
|
||||
BTS provides streaming updates for some types of data. These updates are incremental in nature and will provide a snapshot of the current state on a new subscription. Streaming updates use the [ReactiveX library](http://reactivex.io) to manage subscribing and unsubscribing. This library provides a rich set of operators that you can use to slide and dice the data streams based on your specific application requirements.
|
||||
|
||||
### C#
|
||||
|
||||
The subscription callback will arrive on the ServiceContext thread but it can be easily marshaled to the thread of your choice using the [ObserveOn](http://reactivex.io/documentation/operators/observeon.html) operator. For a WPF or Windows Forms application the most common use of this will be [ObserveOnDispatcher]() which will simply execute the callback in the context of the original calling thread. By your choice of where to place the `ObserveOnDispatcher` call in your reactive call chain you can choose whether other operators execute before after switching threads.
|
||||
|
||||
In this example the `Where` and `SelectMany` operators will execute on `ServiceContext` connection thread and only the resulting matches will be marshaled across to the calling thread. It is safe to unsubscribe by disposing `sub` from any thread.
|
||||
|
||||
```csharp
|
||||
var orders = context.locate<OrderService>();
|
||||
ObservableCollection<OrderRecord> orders_with_errors;
|
||||
IDisposable sub =
|
||||
orders
|
||||
.subscribe_orders()
|
||||
.SelectMany(update => update.added)
|
||||
.Where(o => o.State.Status == OrderStatus.Error)
|
||||
.ObserveOnDispatcher()
|
||||
.Subscribe(o => {
|
||||
orders_with_errors.Add(o);
|
||||
});
|
||||
```
|
||||
|
||||
### C++
|
||||
|
||||
The C++ subscription callbacks use the [RxCpp](https://github.com/ReactiveX/RxCpp) library which implements the [ReactiveX](http://reactivex.io) operators. The subscribe call will return an `rxcpp::composite_subscription` which implements [RAII](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization) and will unsubscribe when it is destroyed.
|
||||
|
||||
```cpp
|
||||
#include "lib/api/public/ServiceContext.h"
|
||||
#include "lib/api/services/OrderService.client.pb.h"
|
||||
#include <rxcpp/rx.hpp>
|
||||
#include <google/protobuf/util/json_util.h>
|
||||
//...
|
||||
bts::api::ServiceContext ctx;
|
||||
auto client = ctx.locate<bts::api::orderman::OrderService>();
|
||||
auto sub =
|
||||
client.subscribe_orders().subscribe([=](const bts::api::orderman::OrdersUpdate& update) {
|
||||
std::string result;
|
||||
for(const auto& order : update.added()) {
|
||||
auto status = google::protobuf::util::MessageToJsonString(*order, &result, {});
|
||||
std::cout << "New order: " << result << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
@@ -22,7 +22,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.4.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
|
||||
<PackageReference Include="System.Reactive" Version="3.1.1" />
|
||||
<PackageReference Include="System.Reactive.PlatformServices" Version="3.1.1" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
@@ -61,6 +61,7 @@
|
||||
<None Include="$(OutputPath)\net471\spark_excel_example_2018.xlsm" pack="true" PackagePath=".\excel\spark_excel_example_2018.xlsm" />
|
||||
<None Include="$(OutputPath)\net471\spark_excel-addin64-packed.xll" pack="true" PackagePath=".\excel\spark_excel-addin64-packed.xll" />
|
||||
<None Include="$(OutputPath)\net471\spark_excel-addin64.dna" pack="true" PackagePath=".\excel\spark_excel-addin64.dna" />
|
||||
<None Include="doc/**" pack="true" PackagePath=".\doc\" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences" Condition= " '$(SparkRoot)' != '' ">
|
||||
|
||||
@@ -12,9 +12,10 @@ namespace bts.mcore
|
||||
class SecurityTests {
|
||||
[Test]
|
||||
public void test_security_equal() {
|
||||
SecurityImmutable a = new Security() { Id = 42 };
|
||||
SecurityImmutable b = new Security() { Id = 42 };
|
||||
SecurityImmutable c = new Security() { Id = 4 };
|
||||
|
||||
SecurityImmutable a = new SecurityImmutable(new Security() { Id = 42 });
|
||||
SecurityImmutable b = new SecurityImmutable(new Security() { Id = 42 });
|
||||
SecurityImmutable c = new SecurityImmutable(new Security() { Id = 4 });
|
||||
|
||||
Assert.IsTrue(a == b);
|
||||
Assert.IsTrue(a.Equals(b));
|
||||
|
||||
@@ -25,9 +25,9 @@ namespace bts.tests {
|
||||
parent_id = parent;
|
||||
}
|
||||
|
||||
public char name { get; }
|
||||
public char name { get; set; }
|
||||
public int id { get; }
|
||||
public int parent_id { get; }
|
||||
public int parent_id { get; set; }
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
Order other = (Order)obj;
|
||||
@@ -133,6 +133,9 @@ namespace bts.tests {
|
||||
/// 2) Insert - all items are properly added via <see cref="SortedTree{T, TKey}.Insert(T)"/>
|
||||
/// 3) Reset - all items are properly added via <see cref="SortedTree{T, TKey}.Reset(IEnumerable{T})"/>
|
||||
/// 4) Replace - all items are properly updated with a new value and/or parent via <see cref="SortedTree{T, TKey}.TryReplace(TKey, Func{T, T})"/>
|
||||
/// a) delegate returns new value
|
||||
/// b) delegate mutates existing value
|
||||
/// - failing to account for this usage led to a bug in the OrderBook ca. 12/20/2018 (https://bluetradesys.atlassian.net/browse/SPARK-1903)
|
||||
|
||||
// 1) Constructor Initialization
|
||||
var tree_asc = new SortedTree<Order, int>(Order.name_asc, id, pid, input);
|
||||
@@ -154,6 +157,7 @@ namespace bts.tests {
|
||||
// 4) Replace
|
||||
tree_asc.Reset(input);
|
||||
tree_desc.Reset(input);
|
||||
//// a) delegate returns new value
|
||||
var modified_input = _randomize_values(input);
|
||||
foreach (var v in modified_input) {
|
||||
Assert.IsTrue(tree_asc.TryReplace(id(v), _ => v));
|
||||
@@ -162,7 +166,31 @@ namespace bts.tests {
|
||||
// verify asc/desc
|
||||
assert_state_then_clear(tree_asc, modified_input, Order.name_asc);
|
||||
assert_state_then_clear(tree_desc, modified_input, Order.name_desc);
|
||||
|
||||
// reset with modified_input since we are mutating next ('input' is essentially a global variable, shared across the test iterations).
|
||||
tree_asc.Reset(modified_input);
|
||||
tree_desc.Reset(modified_input);
|
||||
//// b) delegate mutates existing value
|
||||
for (int i = 0; i < modified_input.Count; i++) {
|
||||
var a = modified_input[i];
|
||||
var b = modified_input[modified_input.Count - i - 1];
|
||||
var swap_name = _swap_name_del(b.name);
|
||||
var swap_parent = _swap_parent_del(b.parent_id);
|
||||
var swap_both = _swap_name_and_parent_del(b.name, b.parent_id);
|
||||
var reset = _swap_name_and_parent_del(a.name, a.parent_id);
|
||||
foreach (var tree in new[] { tree_asc, tree_desc }) {
|
||||
Assert.IsTrue(tree.TryReplace(id(a), swap_name));
|
||||
Assert.IsTrue(tree.TryReplace(id(a), swap_parent));
|
||||
// reset 'a' so swap_both will actually change values (we already swapped name and parent individually above).
|
||||
Assert.IsTrue(tree.TryReplace(id(a), reset));
|
||||
Assert.IsTrue(tree.TryReplace(id(a), swap_both));
|
||||
// reset 'a' to finish. Since tree_asc/desc share the same actual orders, this protects the other tree from becoming unsorted as a result of the mutations.
|
||||
Assert.IsTrue(tree.TryReplace(id(a), reset));
|
||||
}
|
||||
}
|
||||
// verify asc/desc
|
||||
assert_state_then_clear(tree_asc, modified_input, Order.name_asc);
|
||||
assert_state_then_clear(tree_desc, modified_input, Order.name_desc);
|
||||
// local methods
|
||||
void assert_state_then_clear(SortedTree<Order, int> tree, IEnumerable<Order> vals, IComparer<Order> comp) {
|
||||
Assert.IsTrue(tree.SequenceEqual(_hierarchically_sorted_list(vals, comp)));
|
||||
clear_action(tree);
|
||||
@@ -265,6 +293,20 @@ namespace bts.tests {
|
||||
return reshuffled;
|
||||
}
|
||||
|
||||
Func<Order, Order> _swap_name_del(char name) => o => {
|
||||
o.name = name;
|
||||
return o;
|
||||
};
|
||||
Func<Order, Order> _swap_parent_del(int parent) => o => {
|
||||
o.parent_id = parent;
|
||||
return o;
|
||||
};
|
||||
Func<Order, Order> _swap_name_and_parent_del(char name, int parent) => o => {
|
||||
o.name = name;
|
||||
o.parent_id = parent;
|
||||
return o;
|
||||
};
|
||||
|
||||
void _write_tree_to_console(SortedTree<Order, int> tree) {
|
||||
foreach (var str in _fmt_output(tree.RootNodes))
|
||||
Console.WriteLine(str);
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using bts.api;
|
||||
using bts.api.admin;
|
||||
using bts.api.core;
|
||||
using bts.api.test;
|
||||
using bts.test;
|
||||
using bts.api.orderman;
|
||||
using mbts;
|
||||
using mbts.tests;
|
||||
using bts.co2;
|
||||
|
||||
namespace bts.tests
|
||||
{
|
||||
[TestGroup(TestType = TestType.System)]
|
||||
public class LimitsTests : IDisposable {
|
||||
ServiceContext _context;
|
||||
OrderService _order_service;
|
||||
AdminService _admin_service;
|
||||
public LimitsTests (){
|
||||
_context = new ServiceContext();
|
||||
|
||||
_order_service = _context.locate<OrderService>();
|
||||
_admin_service = _context.locate<AdminService>();
|
||||
|
||||
var auth = _context.login("bts","");
|
||||
auth.Wait(3000);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Disposable.dispose(ref _order_service);
|
||||
Disposable.dispose(ref _admin_service);
|
||||
Disposable.dispose(ref _context);
|
||||
}
|
||||
|
||||
async Task<IEnumerable<SecurityImmutable>> all_futures(string symbol) {
|
||||
var request = new MDRequest() {
|
||||
Host = "system_test",
|
||||
Channels = {
|
||||
new SecurityChannel() {
|
||||
Time = 0,
|
||||
Flags = 0,
|
||||
Type = SecurityType.Future,
|
||||
Exchange = Venue.Sim,
|
||||
Symbol = symbol,
|
||||
TradeCursor = 0,
|
||||
RfqCursor = 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var relay = _context.locate<MarketDataRelay>();
|
||||
await Task.Delay(100);
|
||||
var result = await relay.requestmd_async(request);
|
||||
|
||||
List<SecurityImmutable> securities = new List<SecurityImmutable>();
|
||||
if (result.success) {
|
||||
foreach (var ch in result.value.Channels) {
|
||||
foreach (var update in ch.Updates) {
|
||||
if (update.Security != null) {
|
||||
securities.Add(new SecurityImmutable(update.Security));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return securities;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task test_clip_limits() {
|
||||
//Get and set limit values from the admin service
|
||||
|
||||
uint test_limit_value = 10u;
|
||||
string test_symbol = "CL";
|
||||
|
||||
var test_user = (name: "bts", id: 1u);
|
||||
var test_account = (name: "ACC A", id: 1u);
|
||||
|
||||
var account_data = await AdminUtilities.get_account_data(_admin_service, test_account.name);
|
||||
Assert.IsTrue(account_data.HasValue);
|
||||
|
||||
var old_clip_limit = AdminUtilities.get_clip_limit(account_data, test_symbol);
|
||||
|
||||
//create Orders Here
|
||||
|
||||
var all = await all_futures(test_symbol);
|
||||
SecurityImmutable? sec = all.FirstOrDefault();
|
||||
Assert.IsTrue(sec.HasValue);
|
||||
|
||||
uint under_limit_order_key;
|
||||
uint over_limit_order_key;
|
||||
uint no_limit_order_key;
|
||||
|
||||
var order_def = create_order_def(sec.Value.Id, Side.Buy, test_account, test_user);
|
||||
|
||||
Assert.IsTrue(sec.HasValue, "Unable to create order without security");
|
||||
await AdminUtilities.remove_clip_limit(_admin_service, test_account.name, test_symbol);
|
||||
Result<Response> no_limit_order_result = await _order_service.create_order_async(new Create() {
|
||||
NewOrder = order_def,
|
||||
Price = 5,
|
||||
Quantity = test_limit_value
|
||||
});
|
||||
|
||||
var set_success = await AdminUtilities.set_clip_limit(_admin_service, test_limit_value, account_data, test_symbol);
|
||||
Assert.IsTrue(set_success, "Failed to set limit");
|
||||
|
||||
Result<Response> under_limit_order_result = await _order_service.create_order_async(new Create() {
|
||||
NewOrder = order_def,
|
||||
Price = 5,
|
||||
Quantity = test_limit_value
|
||||
});
|
||||
|
||||
Result<Response> over_limit_order_result = await _order_service.create_order_async(new Create() {
|
||||
NewOrder = order_def,
|
||||
Price = 5,
|
||||
Quantity = test_limit_value + 1
|
||||
});
|
||||
|
||||
under_limit_order_key = under_limit_order_result.value.OrderKey;
|
||||
over_limit_order_key = over_limit_order_result.value.OrderKey;
|
||||
no_limit_order_key = no_limit_order_result.value.OrderKey;
|
||||
|
||||
bool waiting_for_orders = true;
|
||||
|
||||
var orders = _order_service.subscribei_orders().Subscribe(ords =>
|
||||
{
|
||||
waiting_for_orders = false;
|
||||
|
||||
IEnumerable<OrderRecordImmutable> filtered_orders = ords.all.Where(x => x.Key == under_limit_order_key || x.Key == over_limit_order_key || x.Key == no_limit_order_key);
|
||||
|
||||
Assert.IsTrue(filtered_orders.Count() == 3, "There should be 3 matching orders... there are not");
|
||||
|
||||
foreach(var o in filtered_orders)
|
||||
{
|
||||
if(o.Key == under_limit_order_key) {
|
||||
Assert.IsTrue(o.State.Status == OrderStatus.Working, "Under Limit Order is not 'Working'");
|
||||
}
|
||||
else if(o.Key == over_limit_order_key) {
|
||||
Assert.IsTrue(o.State.Status == OrderStatus.RejectedBySpark, "Over Limit Order is not 'RejectedBySpark'");
|
||||
}
|
||||
else if(o.Key == no_limit_order_key) {
|
||||
Assert.IsTrue(o.State.Status == OrderStatus.RejectedBySpark, "No Limit Order is not 'RejectedBySpark'");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
while(waiting_for_orders) {
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
// Restore the Limits to the original values
|
||||
bool success = false;
|
||||
|
||||
if(old_clip_limit.HasValue) {
|
||||
success = await AdminUtilities.set_clip_limit(_admin_service, old_clip_limit.Value, test_account.name, test_symbol);
|
||||
}
|
||||
else {
|
||||
success = await AdminUtilities.remove_clip_limit(_admin_service, test_account.name, test_symbol);
|
||||
}
|
||||
|
||||
Assert.IsTrue(success, "Failed to restore Limit");
|
||||
|
||||
|
||||
}
|
||||
|
||||
public OrderDefinition create_order_def(uint security_id, Side side, (String name, uint id) account, (String name, uint id) user){
|
||||
return new OrderDefinition {
|
||||
SecurityKey = security_id,
|
||||
Side = Side.Buy,
|
||||
TimeInForce = TimeInForce.Day,
|
||||
CustomDef = new CustomOrderDefinition { Limit = new LimitOrderDefinition { } },
|
||||
CtiCode = CTICode.CtiCode1,
|
||||
CustomerOrFirm = CustomerOrFirm.Firm,
|
||||
ManualOrderIndicator = ManualOrderIndicator.Yes,
|
||||
Account = account.name,
|
||||
AccountId = account.id,
|
||||
User = user.name,
|
||||
UserId = user.id
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class AdminUtilities {
|
||||
|
||||
public static async Task<BTSAccountImmutable?> get_account_data (AdminService admin_service, string account_name)
|
||||
{
|
||||
Update<AdminDataImmutable, int> update = await admin_service.subscribei_admin_data().FirstAsync();
|
||||
|
||||
foreach (var acc in update.all.First().Accounts) {
|
||||
if(acc.Name == account_name) {
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AccountSecurityClipLimitUnitImmutable? get_clip_limit_row(BTSAccountImmutable? account_data, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
|
||||
IEnumerable<AccountSecurityClipLimitUnitImmutable> clip_limit_rows = account_data.Value.SecurityClipLimits.Limit.Where(
|
||||
r => r.Security.SecuritySymbol == symbol
|
||||
&& r.Security.Exchange == venue
|
||||
&& r.Security.SecurityType == security_type);
|
||||
return clip_limit_rows.Any() ? clip_limit_rows.First() : (AccountSecurityClipLimitUnitImmutable?) null;
|
||||
}
|
||||
|
||||
public static uint? get_clip_limit(BTSAccountImmutable? account_data, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
|
||||
var row = get_clip_limit_row(account_data,symbol,security_type,venue);
|
||||
return row.HasValue ? row.Value.ClipSize : (uint?) null;
|
||||
}
|
||||
|
||||
public static async Task<uint?> get_clip_limit(AdminService admin_service, string account_name, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
|
||||
var account_data = await get_account_data(admin_service, account_name);
|
||||
return get_clip_limit(account_data, symbol, security_type, venue);
|
||||
}
|
||||
|
||||
public static async Task<bool> set_clip_limit(AdminService admin_service, uint clip_value, string account_name, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
|
||||
var account_data = await get_account_data(admin_service, account_name);
|
||||
return await set_clip_limit(admin_service, clip_value,account_data, symbol,security_type, venue);
|
||||
}
|
||||
|
||||
public static async Task<bool> set_clip_limit(AdminService admin_service, uint clip_value, BTSAccountImmutable? account_data, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
|
||||
|
||||
AccountSecurityClipLimitUnitImmutable? origin_clip_limit_row = get_clip_limit_row(account_data,symbol,security_type,venue);
|
||||
AccountSecurityClipLimitUnitImmutable new_clip_limit_row;
|
||||
|
||||
List<AccountSecurityClipLimitUnitImmutable> clip_limits_list = account_data.Value.SecurityClipLimits.Limit.ToList();
|
||||
|
||||
if(origin_clip_limit_row.HasValue) {
|
||||
clip_limits_list.Remove(origin_clip_limit_row.Value);
|
||||
}
|
||||
|
||||
new_clip_limit_row = (AccountSecurityClipLimitUnitImmutable) new AccountSecurityClipLimitUnit {
|
||||
Security = new SecurityScope(){ SecuritySymbol = symbol, Exchange = venue, SecurityType = security_type },
|
||||
ClipSize = clip_value
|
||||
};
|
||||
|
||||
clip_limits_list.Add(new_clip_limit_row);
|
||||
|
||||
AccountSecurityClipLimits temp_scl = new AccountSecurityClipLimits();
|
||||
temp_scl.Limit.AddRange(clip_limits_list.Select(x => x.CloneToMutable()));
|
||||
|
||||
var new_account_data = account_data.Value.with(security_clip_limits: temp_scl);
|
||||
|
||||
var update_ret = await admin_service.update_account_async(new AccountUpdateAPIArgument {
|
||||
CompleteData = new_account_data.CloneToMutable()
|
||||
});
|
||||
|
||||
return update_ret.success;
|
||||
}
|
||||
|
||||
public static async Task<bool> remove_clip_limit(AdminService admin_service, string account_name, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
|
||||
var account_data = await get_account_data(admin_service, account_name);
|
||||
return await remove_clip_limit(admin_service, account_data, symbol, security_type, venue);
|
||||
}
|
||||
|
||||
public static async Task<bool> remove_clip_limit(AdminService admin_service, BTSAccountImmutable? account_data, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
|
||||
|
||||
AccountSecurityClipLimitUnitImmutable? origin_clip_limit_row = get_clip_limit_row(account_data,symbol,security_type,venue);
|
||||
List<AccountSecurityClipLimitUnitImmutable> clip_limits_list = account_data.Value.SecurityClipLimits.Limit.ToList();
|
||||
|
||||
if(origin_clip_limit_row.HasValue) {
|
||||
clip_limits_list.Remove(origin_clip_limit_row.Value);
|
||||
AccountSecurityClipLimits temp_scl = new AccountSecurityClipLimits();
|
||||
temp_scl.Limit.AddRange(clip_limits_list.Select(x => x.CloneToMutable()));
|
||||
var new_account_data = account_data.Value.with(security_clip_limits: temp_scl);
|
||||
|
||||
var update_ret = await admin_service.update_account_async(new AccountUpdateAPIArgument {
|
||||
CompleteData = new_account_data.CloneToMutable()
|
||||
});
|
||||
|
||||
return update_ret.success;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net471</TargetFramework>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Platform>x64</Platform>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -15,12 +15,17 @@ namespace sparkcmd {
|
||||
uint _parallel = 1;
|
||||
bool _list;
|
||||
|
||||
TestType test_type = TestType.Unit;
|
||||
|
||||
public TestCommand() {
|
||||
IsCommand("test", "Run managed unit tests");
|
||||
// i comes thorugh as "t" which seems daft.
|
||||
// i comes thorugh as "t" which seems daft.
|
||||
HasOption("t|timeout", "Timeout unit tests", i => _timeout = 10);
|
||||
HasOption<uint>("n|parallel", "How many tests to run in parallel", i => _parallel = i);
|
||||
HasOption("l|list", "List all tests", _ => _list = true);
|
||||
HasOption("unit","Run unit tests", _ => test_type = TestType.Unit);
|
||||
HasOption("system","Run system tests", _ => test_type = TestType.System);
|
||||
HasOption("shell","Run shell tests", _ => test_type = TestType.Shell);
|
||||
HasAdditionalArguments(null, " [Test List]");
|
||||
}
|
||||
|
||||
@@ -35,7 +40,7 @@ namespace sparkcmd {
|
||||
if (_timeout == 0 && tests.Length == 1) {
|
||||
//System.Threading.Thread.Sleep(5000);
|
||||
using (var trr = new TestResultReceiver(true)) {
|
||||
InProcessTestRunner.run_test_syncronously(tests[0], Enumerator, trr.get_reporter());
|
||||
InProcessTestRunner.run_test_synchronously(tests[0], Enumerator, trr.get_reporter());
|
||||
trr.print_summary();
|
||||
return trr.ErrorCount;
|
||||
}
|
||||
@@ -44,7 +49,7 @@ namespace sparkcmd {
|
||||
return run_tests(Enumerator.match(tests).Select(t => t.name));
|
||||
}
|
||||
else {
|
||||
return run_tests(Enumerator.Tests.Select(t => t.full_name));
|
||||
return run_tests(Enumerator.get_of_type(test_type).Select(t => t.full_name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +64,7 @@ namespace sparkcmd {
|
||||
|
||||
void list_tests() {
|
||||
Console.WriteLine("All tests: ");
|
||||
foreach (var t in Enumerator.TestGroups) {
|
||||
foreach (var t in Enumerator.get_groups_of_type(test_type)) {
|
||||
ConsoleEx.WriteLine(t.name, ConsoleColor.Green);
|
||||
foreach (var test in t.methods) {
|
||||
Console.Write($" {test.name}");
|
||||
|
||||
@@ -8,10 +8,10 @@ using System.Linq;
|
||||
|
||||
namespace sparkcmd {
|
||||
class Program {
|
||||
static void Main(string[] args) {
|
||||
static int Main(string[] args) {
|
||||
mbts.config.Configuration.initialize();
|
||||
var commands = ConsoleCommandDispatcher.FindCommandsInSameAssemblyAs(typeof(Program));
|
||||
ConsoleCommandDispatcher.DispatchCommand(commands, args, Console.Out);
|
||||
return;
|
||||
return ConsoleCommandDispatcher.DispatchCommand(commands, args, Console.Out);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net471</TargetFramework>
|
||||
<!--<TargetFrameworks>net471;netcoreapp2.1</TargetFrameworks>-->
|
||||
<TargetFrameworks>net471;netcoreapp2.1</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;ubuntu.16.04-x64</RuntimeIdentifiers>
|
||||
<Authors>Blue Trading Systems</Authors>
|
||||
<Company>Blue Trading Systems</Company>
|
||||
@@ -18,7 +17,7 @@
|
||||
<ProjectReference Include="../spark_api/spark_api.csproj" />
|
||||
<!--<ProjectReference Include="..\spark\Spark.csproj" />-->
|
||||
<ProjectReference Include="..\spark_core\spark_core.csproj" />
|
||||
<ProjectReference Include="..\spark_test\spark_test.csproj" Condition="'$(Configuration)'=='Debug'" />
|
||||
<ProjectReference Include="..\spark_test\spark_test.csproj" />
|
||||
<!--<Proto3 Include="**/*.proto" />-->
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -3,3 +3,9 @@
|
||||
rm -rf $1
|
||||
mkdir $1
|
||||
cp -r $2/files/* $1
|
||||
|
||||
for filename in $1/*; do
|
||||
if [[ $filename == *.xz ]]; then
|
||||
unxz $filename
|
||||
fi
|
||||
done
|
||||
Binary file not shown.
@@ -1,3 +1,6 @@
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* Main tables */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
create table if not exists
|
||||
session (
|
||||
version TEXT,
|
||||
@@ -5,6 +8,7 @@ session (
|
||||
build_type TEXT,
|
||||
session_start TIMESTAMP PRIMARY KEY
|
||||
);
|
||||
create index if not exists session_idx on session(session_start);
|
||||
|
||||
create table if not exists
|
||||
trace (
|
||||
@@ -20,55 +24,46 @@ trace (
|
||||
sn INTEGER,
|
||||
uint32_1 INTEGER
|
||||
);
|
||||
|
||||
drop view if exists recent_trace;
|
||||
create view recent_trace as
|
||||
select * from trace order by rowid desc limit 140123;
|
||||
|
||||
create index if not exists trace_idx on trace(session_start,channel,sn,ts);
|
||||
create index if not exists trace_t_idx on trace(session_start,ts);
|
||||
|
||||
drop view if exists ordered_trace;
|
||||
create view ordered_trace as
|
||||
select
|
||||
recent_trace.*,
|
||||
row_number() over (order by session_start,ts) entry_rank
|
||||
from
|
||||
recent_trace;
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* Main Views */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
drop view if exists delta_trace;
|
||||
create view delta_trace as
|
||||
/* An ordered view of the traces, with extra columns for the delta
|
||||
betwen consecutive traces as well as the delta between related
|
||||
(scoped) traces */
|
||||
drop view if exists log_view;
|
||||
create view log_view as
|
||||
select
|
||||
current.*,
|
||||
(current.ts - previous.ts) / 1000.0 delta
|
||||
from
|
||||
ordered_trace current,
|
||||
ordered_trace previous
|
||||
where
|
||||
current.entry_rank = previous.entry_rank+1;
|
||||
|
||||
drop view if exists log;
|
||||
create view log as
|
||||
select
|
||||
delta_trace.*,
|
||||
trace.*,
|
||||
trace.rowid entry_id,
|
||||
(ts-lag(ts) over by_session_ts)/1000.0 delta,
|
||||
case cor_event>0 when 1 then (ts-cor_event)/1000.0 else 0 end scope_delta,
|
||||
case cor_event>0 when 1 then "end of " || entry_kind else entry_kind end scoped_entry
|
||||
from
|
||||
delta_trace;
|
||||
|
||||
drop view if exists events;
|
||||
create view events as
|
||||
trace
|
||||
window
|
||||
by_session_ts as (order by session_start,ts);
|
||||
|
||||
/* A view identifying each high level event (requote or hedge) and
|
||||
annotating it with its latency */
|
||||
drop view if exists event_view;
|
||||
create view event_view as
|
||||
select
|
||||
fix_reads.session_start,
|
||||
"hedge" event_type,
|
||||
fix_reads.hts hts_start,
|
||||
fix_reads.entry_id event_id,
|
||||
fix_reads.ts start,
|
||||
fix_writes.ts stop,
|
||||
(fix_writes.ts - fix_reads.ts) / 1000.0 duration,
|
||||
row_number() over (order by fix_reads.session_start,fix_reads.ts,fix_writes.ts) event_rank ,
|
||||
fix_reads.channel,
|
||||
fix_reads.sn
|
||||
from
|
||||
(select session_start, channel, sn, ts from recent_trace where entry_kind='fix_msg_r') fix_reads,
|
||||
(select session_start, channel, sn, ts from recent_trace where entry_kind='before_fix_socket_w') fix_writes
|
||||
(select session_start, channel, sn, hts,ts,rowid entry_id from trace where entry_kind='fix_msg_r' order by ts) fix_reads,
|
||||
(select session_start, channel, sn, hts,ts,rowid entry_id from trace where entry_kind='before_fix_socket_w') fix_writes
|
||||
where
|
||||
fix_reads.session_start = fix_writes.session_start and
|
||||
fix_reads.channel = fix_writes.channel and
|
||||
@@ -77,35 +72,168 @@ union
|
||||
select
|
||||
md_reads.session_start,
|
||||
"requote" event_type,
|
||||
md_reads.hts hts_start,
|
||||
md_reads.rowid event_id,
|
||||
md_reads.ts start,
|
||||
fix_writes.ts stop,
|
||||
(fix_writes.ts - md_reads.ts) / 1000.0 duration,
|
||||
row_number() over (order by md_reads.session_start,md_reads.ts,fix_writes.ts) event_rank ,
|
||||
md_reads.channel,
|
||||
md_reads.sn
|
||||
from
|
||||
(select session_start, channel, sn, ts from recent_trace where entry_kind='md') md_reads,
|
||||
(select session_start, channel, sn, ts from recent_trace where entry_kind='before_fix_socket_w') fix_writes
|
||||
(select distinct session_start, channel, sn, hts,ts from trace where entry_kind='md' order by ts) md_reads,
|
||||
(select session_start, channel, sn, hts,ts,rowid entry_id from trace where entry_kind='before_fix_socket_w') fix_writes
|
||||
where
|
||||
md_reads.session_start = fix_writes.session_start and
|
||||
md_reads.channel = fix_writes.channel and
|
||||
md_reads.sn = fix_writes.sn;
|
||||
|
||||
drop view if exists event_log;
|
||||
create view event_log as
|
||||
|
||||
/* A view on hedge events, identifying each order sent as part of the event*/
|
||||
drop view if exists hedge_trace_view;
|
||||
create view hedge_trace_view as
|
||||
select
|
||||
log.*,
|
||||
event_type,
|
||||
event_rank,
|
||||
events.duration event_duration,
|
||||
events.start event_start,
|
||||
events.stop event_stop
|
||||
session.version,
|
||||
session.host,
|
||||
session.build_type,
|
||||
session.session_start,
|
||||
trace.hts trace_time,
|
||||
uint64_1 order_key,
|
||||
duration total,
|
||||
event_id
|
||||
from
|
||||
log,
|
||||
events
|
||||
event_view,
|
||||
trace,
|
||||
session
|
||||
where
|
||||
log.session_start = events.session_start and
|
||||
log.ts >= events.start and log.ts <= events.stop;
|
||||
session.session_start = trace.session_start and
|
||||
session.session_start = event_view.session_start and
|
||||
event_view.channel=trace.channel and event_view.sn=trace.sn and
|
||||
event_type='hedge' and
|
||||
entry_kind='NativeOrder::on_activate' and
|
||||
cor_event=0;
|
||||
|
||||
/* A view on requote events, identifying each order sent as part of the event*/
|
||||
drop view if exists requote_trace_view;
|
||||
create view requote_trace_view as
|
||||
select
|
||||
session.version,
|
||||
session.host,
|
||||
session.build_type,
|
||||
session.session_start,
|
||||
trace.hts trace_time,
|
||||
uint64_1 order_key,
|
||||
duration total,
|
||||
event_id
|
||||
from
|
||||
event_view,
|
||||
trace,
|
||||
session
|
||||
where
|
||||
session.session_start = trace.session_start and
|
||||
session.session_start = event_view.session_start and
|
||||
event_view.channel=trace.channel and event_view.sn=trace.sn and
|
||||
event_type='requote' and
|
||||
entry_kind='NativeOrder::on_update' and
|
||||
cor_event=0;
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* Archive tables/view */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
/* Auxiliary tables and views, used to archive the results of the main
|
||||
views to speed up analysis as data accumulates */
|
||||
|
||||
create table if not exists
|
||||
event_archive (
|
||||
session_start TIMESTAMP,
|
||||
event_type TEXT,
|
||||
hts_start TIMESTAMP,
|
||||
event_id INTEGER,
|
||||
start INTEGER,
|
||||
stop INTEGER,
|
||||
duration float,
|
||||
channel TEXT,
|
||||
sn INTEGER
|
||||
);
|
||||
create index if not exists event_archive_idx on event_archive(session_start,start,stop);
|
||||
|
||||
create table if not exists
|
||||
log_archive(
|
||||
session_start TIMESTAMP,
|
||||
ts INTEGER,
|
||||
hts timestamp,
|
||||
channel TEXT,
|
||||
entry_kind TEXT,
|
||||
txt_1 TEXT,
|
||||
uint64_1 INTEGER,
|
||||
thread TEXT,
|
||||
cor_event INTEGER,
|
||||
sn INTEGER,
|
||||
uint32_1 INTEGER,
|
||||
entry_id INTEGER,
|
||||
delta float,
|
||||
scope_delta float,
|
||||
scoped_entry TEXT
|
||||
);
|
||||
create index if not exists log_archive_idx on log_archive(session_start,ts);
|
||||
|
||||
create table if not exists
|
||||
hedge_trace_archive (
|
||||
version TEXT,
|
||||
host TEXT,
|
||||
build_type TEXT,
|
||||
session_start TIMESTAMP,
|
||||
trace_time TIMESTAMP,
|
||||
order_key INTEGER,
|
||||
total float,
|
||||
event_id integer
|
||||
);
|
||||
drop view if exists hedge_trace;
|
||||
create view hedge_trace as
|
||||
select * from hedge_trace_view union select * from hedge_trace_archive;
|
||||
|
||||
create table if not exists
|
||||
requote_trace_archive (
|
||||
version TEXT,
|
||||
host TEXT,
|
||||
build_type TEXT,
|
||||
session_start TIMESTAMP,
|
||||
trace_time TIMESTAMP,
|
||||
order_key INTEGER,
|
||||
total float,
|
||||
event_id integer
|
||||
);
|
||||
drop view if exists requote_trace;
|
||||
create view requote_trace as
|
||||
select * from requote_trace_view union select * from requote_trace_archive;
|
||||
|
||||
|
||||
/* a synthetis of the log and events, focusing only on traces that are
|
||||
part of hedge/requote events */
|
||||
create table if not exists
|
||||
event_log_archive (
|
||||
session_start TIMESTAMP,
|
||||
ts INTEGER,
|
||||
hts timestamp,
|
||||
channel TEXT,
|
||||
entry_kind TEXT,
|
||||
txt_1 TEXT,
|
||||
uint64_1 INTEGER,
|
||||
thread TEXT,
|
||||
cor_event INTEGER,
|
||||
sn INTEGER,
|
||||
uint32_1 INTEGER,
|
||||
entry_id INTEGER,
|
||||
delta float,
|
||||
scope_delta float,
|
||||
scoped_entry TEXT,
|
||||
event_type TEXT,
|
||||
event_id INTEGER,
|
||||
event_duration float,
|
||||
hts_start TIMESTAMP,
|
||||
event_start INTEGER,
|
||||
event_stop INTEGER
|
||||
);
|
||||
create index if not exists event_log_archive_idx on event_log_archive(session_start);
|
||||
|
||||
drop view if exists pretty_log;
|
||||
create view pretty_log as
|
||||
@@ -115,69 +243,76 @@ select
|
||||
scope_delta ,
|
||||
event_duration,
|
||||
event_type,
|
||||
event_rank,
|
||||
event_id,
|
||||
hts,
|
||||
ts,
|
||||
session_start,
|
||||
entry_rank
|
||||
entry_id
|
||||
from
|
||||
event_log;
|
||||
event_log_archive;
|
||||
|
||||
drop view if exists last_pretty_log;
|
||||
create view last_pretty_log as
|
||||
select * from pretty_log where session_start=(select max(session_start) from session);
|
||||
select
|
||||
*
|
||||
from
|
||||
pretty_log
|
||||
where
|
||||
session_start=(select max(session_start) from session);
|
||||
|
||||
-- drop view if exists summary;
|
||||
-- create view summary as
|
||||
-- select session_start,event_type,count(*) count, avg(duration) avg, max(duration) max, min(duration) min, median(duration) median, stdev(duration) stdev from events group by session_start,event_type;
|
||||
/*
|
||||
drop view if exists event_stats;
|
||||
create view event_stats as
|
||||
select
|
||||
session_start,
|
||||
event_type,
|
||||
count(*) count,
|
||||
avg(duration) avg,
|
||||
max(duration) max,
|
||||
min(duration) min,
|
||||
median(duration) median,
|
||||
stdev(duration) stdev
|
||||
from
|
||||
event_archive
|
||||
group by
|
||||
session_start,
|
||||
event_type;
|
||||
*/
|
||||
|
||||
drop view if exists event_log_aux;
|
||||
create view event_log_aux as
|
||||
select
|
||||
log_archive.*,
|
||||
event_type,
|
||||
event_id,
|
||||
event_archive.duration event_duration,
|
||||
event_archive.hts_start hts_start,
|
||||
event_archive.start event_start,
|
||||
event_archive.stop event_stop
|
||||
from
|
||||
log_archive,
|
||||
event_archive
|
||||
where
|
||||
log_archive.session_start = event_archive.session_start and
|
||||
log_archive.ts >= event_archive.start and log_archive.ts <= event_archive.stop;
|
||||
|
||||
begin transaction;
|
||||
insert into hedge_trace_archive select * from hedge_trace_view;
|
||||
insert into requote_trace_archive select * from requote_trace_view;
|
||||
insert into event_archive select * from event_view;
|
||||
insert into log_archive select * from log_view;
|
||||
insert into event_log_archive select * from event_log_aux;
|
||||
delete from trace;
|
||||
delete from event_log_archive where
|
||||
rowid in (select rowid from event_log_archive order by rowid desc limit -1 offset 10000123);
|
||||
delete from hedge_trace_archive where
|
||||
rowid in (select rowid from hedge_trace_archive order by rowid desc limit -1 offset 10000123);
|
||||
delete from requote_trace_archive where
|
||||
rowid in (select rowid from requote_trace_archive order by rowid desc limit -1 offset 10000123);
|
||||
delete from log_archive where
|
||||
rowid in (select rowid from log_archive order by rowid desc limit -1 offset 10000123);
|
||||
delete from event_log_archive where
|
||||
rowid in (select rowid from event_log_archive order by rowid desc limit -1 offset 10000123);
|
||||
commit;
|
||||
|
||||
|
||||
drop view if exists hedge_trace;
|
||||
create view hedge_trace as
|
||||
select
|
||||
session.version,
|
||||
session.host,
|
||||
session.build_type,
|
||||
session.session_start,
|
||||
recent_trace.hts trace_time,
|
||||
recent_trace.uint64_1 order_key,
|
||||
hedge_events.duration total,
|
||||
hedge_events.event_rank rank
|
||||
from
|
||||
recent_trace,
|
||||
(select * from events where event_type='hedge') hedge_events,
|
||||
session
|
||||
where
|
||||
entry_kind='NativeOrder::on_activate' and
|
||||
recent_trace.cor_event = 0 and
|
||||
recent_trace.channel = hedge_events.channel and
|
||||
recent_trace.session_start = hedge_events.session_start and
|
||||
recent_trace.sn = hedge_events.sn and
|
||||
recent_trace.session_start = session.session_start;
|
||||
|
||||
drop view if exists requote_trace;
|
||||
create view requote_trace as
|
||||
select
|
||||
session.version,
|
||||
session.host,
|
||||
session.build_type,
|
||||
session.session_start,
|
||||
recent_trace.hts trace_time,
|
||||
recent_trace.uint64_1 order_key,
|
||||
requote_events.duration total,
|
||||
requote_events.event_rank rank
|
||||
from
|
||||
recent_trace,
|
||||
(select * from events where event_type='requote') requote_events,
|
||||
session
|
||||
where
|
||||
entry_kind='NativeOrder::on_update' and
|
||||
recent_trace.cor_event = 0 and
|
||||
recent_trace.channel = requote_events.channel and
|
||||
recent_trace.session_start = requote_events.session_start and
|
||||
recent_trace.sn = requote_events.sn and
|
||||
recent_trace.session_start = session.session_start ;
|
||||
|
||||
delete from trace where
|
||||
rowid in (select rowid from trace order by rowid desc limit -1 offset 10000123);
|
||||
|
||||
@@ -1 +1 @@
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;mercury/actors/IPollableActor.cpp;mercury/broker/Broker.cpp;mercury/broker/BrokerProxy.cpp;mercury/broker/ILocalBroker.cpp;mercury/broker/IRemoteBroker.cpp;mercury/common/mercury_common.cpp;mercury/events/TimerEvent.cpp;mercury/executor/Executor.cpp;mercury/topics/Topic.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.cpp;orderman/actors/MDSupplementarySource.cpp;orderman/actors/OrderStore.cpp;orderman/actors/SODPosition.cpp;orderman/actors/SecuritySrc.cpp;orderman/actors/ServiceBridge.cpp;orderman/actors/order_manager/OrderBuilder.cpp;orderman/actors/order_manager/OrderManager.cpp;orderman/actors/order_manager/OrderServicesData.cpp;orderman/events/CancelRejectEvent.cpp;orderman/events/NewExecutionReportEvent.cpp;orderman/limiter/LimitStore.cpp;orderman/limiter/Limiter.cpp;orderman/limiter/ProductLimitNode.cpp;orderman/limiter/ProductLimitTree.cpp;orderman/limiter/SecurityLimitNode.cpp;orderman/limiter/SecurityLimitTree.cpp;orderman/limiter/tests/limit_tests.cpp;orderman/order_factory/DynaLine truncated
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp;utils/stats.cpp;utils/tests/AuthTokenTests.cpp;utils/thread_manager.cpp)
|
||||
@@ -0,0 +1 @@
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp;utils/stats.cpp;utils/tests/AuthTokenTests.cpp;utils/thread_manager.cpp)
|
||||
@@ -141,4 +141,4 @@ if (WIN32)
|
||||
include(${CMAKE_SOURCE_DIR}/tools/vs_filters.cmake)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
@@ -31,8 +31,9 @@ namespace {{package}} {
|
||||
public {{name}} CloneToMutable() { return _impl.Clone(); }
|
||||
|
||||
// Implicit conversion operator
|
||||
public static implicit operator {{name}}Immutable({{name}} other) {
|
||||
return new {{name}}Immutable(other, false);
|
||||
public static implicit operator Nullable<{{name}}Immutable>({{name}} other) {
|
||||
if (other == null) return null;
|
||||
else return new {{name}}Immutable(other, false);
|
||||
}
|
||||
|
||||
public override bool Equals(object other) {
|
||||
|
||||
@@ -73,6 +73,8 @@ message CustomOrderDefinition
|
||||
TriggerOrderDefinition trigger = 22;
|
||||
CoverOrderDefinition cover = 23;
|
||||
OffsetOrderDefinition offset = 24;
|
||||
OCOOrderDefinition oco = 25;
|
||||
PricerOrderDefinition pricer = 26;
|
||||
|
||||
DynamicOrderDefinition dynamic = 1000;
|
||||
}
|
||||
@@ -154,6 +156,17 @@ message OffsetOrderDefinition {
|
||||
uint32 tick_offset_on_deploy = 1;
|
||||
CustomOrderDefinition child = 6;
|
||||
}
|
||||
message OCOOrderDefinition {
|
||||
CustomOrderDefinition other = 1;
|
||||
int32 other_ticks = 2;
|
||||
api.RelativeSide other_side = 4;
|
||||
CustomOrderDefinition child = 3;
|
||||
}
|
||||
message PricerOrderDefinition {
|
||||
int32 price_offset_ticks = 1;
|
||||
double price_factor = 2;
|
||||
CustomOrderDefinition child = 3;
|
||||
}
|
||||
message DynamicOrderDefinition {
|
||||
string dynamic_order_type = 1;
|
||||
map<string, DynamicOrderParam> order_params = 2;
|
||||
@@ -203,6 +216,8 @@ message OrderState {
|
||||
TriggerOrderState trigger = 21;
|
||||
CoverOrderState cover = 22;
|
||||
OffsetOrderState offset = 24;
|
||||
OCOOrderState oco = 25;
|
||||
PricerOrderState pricer = 26;
|
||||
|
||||
DynamicOrderState dynamic = 255; // do not use anything bigger than 255, or things will break
|
||||
}
|
||||
@@ -276,6 +291,8 @@ message TopOrderState {}
|
||||
message TriggerOrderState {}
|
||||
message CoverOrderState {}
|
||||
message OffsetOrderState {}
|
||||
message OCOOrderState {}
|
||||
message PricerOrderState {}
|
||||
message DynamicOrderState {}
|
||||
|
||||
enum OrderStatus {
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace internal {
|
||||
using namespace ::google::protobuf;
|
||||
using namespace bts::expression;
|
||||
using namespace bts::util::proto;
|
||||
using std::vector;
|
||||
|
||||
ConfigValueQueue::ConfigValueQueue(IPropertyEvaluator& evaluator) :
|
||||
_eval(evaluator),
|
||||
|
||||
@@ -110,6 +110,7 @@ message CMEOptions
|
||||
|
||||
// Prevents parsing trades/rfqs/doing anything not required for quoting
|
||||
optional bool quoter_mode = 6;
|
||||
optional bool verbose_ticker = 7 [default=false];
|
||||
}
|
||||
|
||||
message RecordOptions {
|
||||
@@ -122,14 +123,14 @@ message RecordOptions {
|
||||
// Replay options
|
||||
//
|
||||
message ReplayOptions {
|
||||
optional string host = 1 [default = "mdreplay"];
|
||||
optional string base_path = 2 [default = "/md"];
|
||||
optional string file_name = 3 [default = "options.coconut"];
|
||||
optional string url = 1 [default = "http://mdreplay.bluetradesys.local/spark/futures.coconut"];
|
||||
|
||||
// 2.0 = double speed, 0.5 = half speed
|
||||
optional double speed = 11 [default = 1.0];
|
||||
|
||||
optional bool interactive = 12 [default = false];
|
||||
|
||||
optional string fallback = 20 [default ="data_files/futures.coconut"];
|
||||
}
|
||||
|
||||
message RelayOptions {
|
||||
|
||||
@@ -3,7 +3,6 @@ package bts.config;
|
||||
|
||||
message PerfLogCfg {
|
||||
optional bool active = 1 [default=true];
|
||||
optional uint32 recent_traces_size = 2 [default=140123];
|
||||
optional uint32 max_rows = 3 [default=10000123];
|
||||
optional uint32 entry_batch_size = 4 [default=200];
|
||||
}
|
||||
@@ -3,4 +3,14 @@ package bts.config;
|
||||
|
||||
message Spreader{
|
||||
optional uint32 n_failed_iocs = 10 [default=20];
|
||||
//if this is set to true, the spreader internally
|
||||
//generates fake fills to keep the cache (data and
|
||||
//instruction) warm
|
||||
message CacheWarming{
|
||||
optional bool enable_fills = 10 [default=true];
|
||||
optional bool enable_child_order_activate = 15 [default=true];
|
||||
optional double fill_period_low = 20 [default=10];
|
||||
optional double fill_period_high = 30 [default=50];
|
||||
}
|
||||
optional CacheWarming cache_warming = 20;
|
||||
}
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
using std::vector;
|
||||
#include <google/protobuf/stubs/common.h>
|
||||
#include <google/protobuf/stubs/stringpiece.h>
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include <stack>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
using std::vector;
|
||||
|
||||
#include "text_format.h"
|
||||
|
||||
|
||||
@@ -66,6 +66,14 @@ enum Side {
|
||||
SIDE_SELL = 50;
|
||||
}
|
||||
|
||||
enum RelativeSide {
|
||||
RELATIVESIDE_UNDEFINED = 0;
|
||||
RELATIVESIDE_OPPOSITE = 1;
|
||||
RELATIVESIDE_SAME = 2;
|
||||
RELATIVESIDE_BUY = 49;
|
||||
RELATIVESIDE_SELL = 50;
|
||||
}
|
||||
|
||||
enum PutOrCall {
|
||||
PUTORCALL_PUT = 0;
|
||||
PUTORCALL_CALL = 1;
|
||||
|
||||
Loaded 100 of 209 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user