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 | ||
|
|
a8646ebb3f | ||
|
|
28fd04293b | ||
|
|
f5457e5ce4 | ||
|
|
26dfd89d6c | ||
|
|
e7b9c3e228 | ||
|
|
ae5db799d4 | ||
|
|
5ba746fd59 | ||
|
|
ab5cb23062 | ||
|
|
4ebdc603c5 | ||
|
|
3de65fb027 | ||
|
|
9de0c4f530 | ||
|
|
ce5998c5dd | ||
|
|
dcbc6e756e | ||
|
|
3c82327f1a | ||
|
|
f4b7fbe6b5 | ||
|
|
9901ef54d4 | ||
|
|
293c13e11d | ||
|
|
be4c69fad4 | ||
|
|
2c4dc8fbe5 | ||
|
|
4ea93bc386 | ||
|
|
1c6333b07a | ||
|
|
26d543af82 | ||
|
|
99b301ad6f | ||
|
|
9cbe83e1a4 | ||
|
|
59369e1b2b | ||
|
|
7c42e75ddd | ||
|
|
b453be40fe | ||
|
|
96709c8121 | ||
|
|
5de9f63665 | ||
|
|
6089b209d1 | ||
|
|
d7a12d0dd9 | ||
|
|
2f87fe31be | ||
|
|
5a9749d551 | ||
|
|
ea950c8786 | ||
|
|
30891bfb49 | ||
|
|
34319c4536 | ||
|
|
196c518ac6 | ||
|
|
f04af5af9d | ||
|
|
8f4da463a7 | ||
|
|
f3adf780d0 | ||
|
|
5078b74f72 | ||
|
|
664c1f28ed | ||
|
|
aae927d773 | ||
|
|
00dba92636 | ||
|
|
5a5de65292 |
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,5 +1,43 @@
|
||||
# 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
|
||||
|
||||
+1
-1
Submodule docker/linux updated: f6d8d59860...8ee9692597.
+1
-1
Submodule externals updated: 5570689de2...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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -335,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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -533,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}" />
|
||||
|
||||
@@ -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,
|
||||
@@ -228,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(),
|
||||
@@ -262,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;
|
||||
@@ -285,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;
|
||||
@@ -306,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;
|
||||
@@ -345,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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -23,6 +23,11 @@ namespace bts.test {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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,6 +12,7 @@ using bts.test;
|
||||
using bts.api.orderman;
|
||||
using mbts;
|
||||
using mbts.tests;
|
||||
using bts.co2;
|
||||
|
||||
namespace bts.tests
|
||||
{
|
||||
@@ -36,6 +37,39 @@ namespace bts.tests
|
||||
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
|
||||
@@ -53,9 +87,9 @@ namespace bts.tests
|
||||
|
||||
//create Orders Here
|
||||
|
||||
bts.md.MarketDataClient mdc = new bts.md.MarketDataClient();
|
||||
|
||||
SecurityImmutable? sec = await mdc.find_security_async(y => y.Symbol == test_symbol && y.Exchange == Venue.Sim);
|
||||
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;
|
||||
@@ -90,10 +124,16 @@ namespace bts.tests
|
||||
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) {
|
||||
@@ -108,6 +148,9 @@ namespace bts.tests
|
||||
}
|
||||
});
|
||||
|
||||
while(waiting_for_orders) {
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
// Restore the Limits to the original values
|
||||
bool success = 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>
|
||||
|
||||
@@ -64,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.
@@ -62,7 +62,7 @@ select
|
||||
fix_reads.channel,
|
||||
fix_reads.sn
|
||||
from
|
||||
(select session_start, channel, sn, hts,ts,rowid entry_id from trace where entry_kind='fix_msg_r') fix_reads,
|
||||
(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
|
||||
@@ -73,14 +73,14 @@ select
|
||||
md_reads.session_start,
|
||||
"requote" event_type,
|
||||
md_reads.hts hts_start,
|
||||
md_reads.entry_id event_id,
|
||||
md_reads.rowid event_id,
|
||||
md_reads.ts start,
|
||||
fix_writes.ts stop,
|
||||
(fix_writes.ts - md_reads.ts) / 1000.0 duration,
|
||||
md_reads.channel,
|
||||
md_reads.sn
|
||||
from
|
||||
(select session_start, channel, sn, hts,ts,rowid entry_id from trace where entry_kind='md') md_reads,
|
||||
(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
|
||||
@@ -302,22 +302,17 @@ 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;
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -123,14 +123,14 @@ message RecordOptions {
|
||||
// Replay options
|
||||
//
|
||||
message ReplayOptions {
|
||||
optional string host = 1 [default = "mdreplay"];
|
||||
optional string base_path = 2 [default = "/spark"];
|
||||
optional string file_name = 3 [default = "futures.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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -182,23 +182,46 @@ void replay_url(const std::string& url, IRelayWriter& writer, int32_t naggle_ms)
|
||||
auto filename = url.substr(last_slash+1);
|
||||
filename = os::sparkdata_path("data/replay/") + filename;
|
||||
utils::download_file_if_newer(host, path, filename);
|
||||
replay_file(filename, writer, naggle_ms, true);
|
||||
if (boost::filesystem::exists(filename)) {
|
||||
replay_file(filename, writer, naggle_ms, true);
|
||||
}
|
||||
}
|
||||
|
||||
void replay_url_with_fallback(const std::string& url, IRelayWriter& writer, int32_t naggle_ms) {
|
||||
// loops forever
|
||||
replay_url(url, writer, naggle_ms);
|
||||
// If it returns it means it failed to download or play cached
|
||||
const auto fallback = config::get<ReplayOptions>().fallback();
|
||||
if (boost::filesystem::exists(fallback)) {
|
||||
replay_file(fallback, writer, naggle_ms);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
shared_ptr<IRelayWriter> mkrelayproducer(bool shared);
|
||||
|
||||
/*this is the public one*/
|
||||
void replay_recording(const string& filename, int32_t naggle_ms) {
|
||||
void replay_recording(const string& filename, int32_t naggle_ms, const std::string fallback) {
|
||||
auto writer = mkrelayproducer(true);
|
||||
if (config::get<ReplayOptions>().interactive()) {
|
||||
naggle_ms = -1;
|
||||
}
|
||||
|
||||
// Try relay_url/replay_file
|
||||
// If it fails resort to the fallback location
|
||||
|
||||
if (filename.find("http://") == 0) {
|
||||
// loops forever
|
||||
replay_url(filename, *writer, naggle_ms);
|
||||
}
|
||||
else {
|
||||
else if (boost::filesystem::exists(filename)) {
|
||||
// loops forever
|
||||
replay_file(filename, *writer, naggle_ms);
|
||||
}
|
||||
// try fallback
|
||||
if (boost::filesystem::exists(fallback)) {
|
||||
replay_file(fallback, *writer, naggle_ms);
|
||||
}
|
||||
}
|
||||
void print_recording(const string& filename) {
|
||||
ConsoleReplayWriter writer;
|
||||
|
||||
@@ -28,8 +28,9 @@ void record_everything(std::string path, std::string filename);
|
||||
void print_recording(const std::string&);
|
||||
void unprint_recording(const std::string& src, const std::string& dest);
|
||||
void trace_recording(const std::string& file, security_key key);
|
||||
void replay_recording(const std::string&, int32_t delay_ms);
|
||||
void replay_recording(const std::string&, int32_t delay_ms, const std::string fallback="");
|
||||
void replay_url(const std::string& url, IRelayWriter& writer, int32_t naggle_ms);
|
||||
void replay_url_with_fallback(const std::string& url, IRelayWriter& writer, int32_t naggle_ms);
|
||||
void replay_file(const std::string& filename, IRelayWriter& writer, int32_t naggle_ms, bool loop=false);
|
||||
|
||||
}}//namespace
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "lib/tracing/PerfLog.h"
|
||||
#include "lib/tracing/bts_trace.h"
|
||||
#include "lib/utils/TimeStamp.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
|
||||
#include <date/date.h>
|
||||
|
||||
@@ -80,6 +81,7 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
switch (ev.md_type) {
|
||||
case MDRequestType::BOOK:
|
||||
on_book_sub(k, *synthetic_count_metric);
|
||||
security_count_metric->set(_handles.size());
|
||||
break;
|
||||
case MDRequestType::TICKER:
|
||||
on_ticker_sub(k, *ticker_channel_cout_metric);
|
||||
@@ -106,6 +108,8 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
book._security_key = SecurityKey(handle.key());
|
||||
trace::CorID trace{h.external_event_channel, handle.sequence()};
|
||||
_perf_log.log(trace, h.last, "md", handle.key());
|
||||
_perf_log.log(trace, "md_r", h.last, h.symbol->c_str(),
|
||||
handle.key());
|
||||
publish(_book_update, book, trace);
|
||||
}
|
||||
}
|
||||
@@ -122,7 +126,7 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
if (trade.key() == per_sec_ev.key().value()) {
|
||||
trade_count++;
|
||||
per_sec_ev.trades.emplace_back(TickerUpdateEvent::Trade{
|
||||
datetime::utc_ll_to_timepoint(trade.time()),
|
||||
datetime::utc_ll_to_timepoint(trade.time()),
|
||||
Price(md::IntPrice(trade.mantissa(),
|
||||
(int8_t)trade.exponent())
|
||||
.price()),
|
||||
@@ -161,7 +165,9 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
auto handle = _md_consumer.get_handle(k);
|
||||
|
||||
_handles.push_back({utils::TimeStamp(), handle,
|
||||
get_external_event_channel(handle.get_usersymbol())});
|
||||
get_external_event_channel(handle.get_usersymbol()),
|
||||
std::make_unique<std::string>(securities::descriptor(
|
||||
handle.get_normalized_security()))});
|
||||
if (handle.is_synthetic())
|
||||
synthetic_count_metric.increment();
|
||||
}
|
||||
@@ -202,10 +208,11 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
"key: {}",
|
||||
user_symbol.to_string(), sk.value());
|
||||
|
||||
// We do not want to get old trades so we drain the trades buffer at
|
||||
// this point. This makes the first subscription to a channel
|
||||
// consistent with subsquent ones in terms of late join semantics.
|
||||
ChannelRecord c {0, _md_consumer.get_channel(user_symbol), {}, user_symbol} ;
|
||||
// We do not want to get old trades so we drain the trades buffer at
|
||||
// this point. This makes the first subscription to a channel
|
||||
// consistent with subsquent ones in terms of late join semantics.
|
||||
ChannelRecord c{
|
||||
0, _md_consumer.get_channel(user_symbol), {}, user_symbol};
|
||||
_trades_buffer.Clear();
|
||||
c.cursor = c.channel.read_trades(_trades_buffer, c.cursor);
|
||||
c.securities.push_back(sk);
|
||||
@@ -215,6 +222,7 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
utils::TimeStamp last;
|
||||
md::MDConsumerHandle handle;
|
||||
const char *external_event_channel;
|
||||
std::unique_ptr<std::string> symbol;
|
||||
};
|
||||
boost::container::small_vector<HandleRecord, 100> _handles;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace orderman {
|
||||
class ChildOrder {
|
||||
public:
|
||||
ChildOrder()
|
||||
: _order(nullptr), _parent_order_services(nullptr), _active(false) {}
|
||||
: _order(nullptr), _parent_order_services(nullptr), _child_order_services(nullptr), _active(false) {}
|
||||
|
||||
ChildOrder(Order &o, OrderServices &services)
|
||||
: _order(&o), _parent_order_services(&services),
|
||||
|
||||
@@ -122,6 +122,12 @@ class OrderStateUpdateEvent : public mercury::Event<OrderStateUpdateEvent> {
|
||||
case api::orderman::OrderState::kOffset:
|
||||
result.mutable_offset();
|
||||
break;
|
||||
case api::orderman::OrderState::kOco:
|
||||
result.mutable_oco();
|
||||
break;
|
||||
case api::orderman::OrderState::kPricer:
|
||||
result.mutable_pricer();
|
||||
break;
|
||||
case api::orderman::OrderState::kDynamic:
|
||||
result.mutable_dynamic();
|
||||
break;
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
#include "lib/orderman/orders/book_algos/JoinOrder.h"
|
||||
#include "lib/orderman/orders/book_algos/LeanOrder.h"
|
||||
#include "lib/orderman/orders/book_algos/OffsetOrder.h"
|
||||
#include "lib/orderman/orders/book_algos/TickOrder.h"
|
||||
#include "lib/orderman/orders/book_algos/OCOOrder.h"
|
||||
#include "lib/orderman/orders/book_algos/PricerOrder.h"
|
||||
#include "lib/orderman/orders/book_algos/TopOrder.h"
|
||||
#include "lib/orderman/orders/spreader_orders/SpreaderOrder.h"
|
||||
|
||||
@@ -148,7 +149,7 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
|
||||
order_ptr.reset(new orderman::TimeBoxOrder());
|
||||
break;
|
||||
case CustomOrderDefinition::kTick:
|
||||
order_ptr.reset(new orderman::TickOrder());
|
||||
bts_assert(false);
|
||||
break;
|
||||
case CustomOrderDefinition::kLean:
|
||||
order_ptr.reset(new orderman::LeanOrder());
|
||||
@@ -167,7 +168,13 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
|
||||
break;
|
||||
case CustomOrderDefinition::kOffset:
|
||||
order_ptr.reset(new orderman::OffsetOrder());
|
||||
break;
|
||||
break;
|
||||
case CustomOrderDefinition::kOco:
|
||||
order_ptr.reset(new orderman::OCOOrder());
|
||||
break;
|
||||
case CustomOrderDefinition::kPricer:
|
||||
order_ptr.reset(new orderman::PricerOrder());
|
||||
break;
|
||||
case CustomOrderDefinition::kDynamic: {
|
||||
order_ptr = me->dynamic_order_registry.build(
|
||||
def.custom_def().dynamic().dynamic_order_type());
|
||||
|
||||
@@ -275,7 +275,7 @@ void NativeOrder::on_init_from_exe_report(
|
||||
|
||||
NativeOrder::~NativeOrder() {}
|
||||
|
||||
void NativeOrder::_on_activate(Price p, Quantity q,
|
||||
bool NativeOrder::_on_activate(Price p, Quantity q,
|
||||
bts::orderman::OrderServices &services,
|
||||
trace::CorID const &trace) {
|
||||
auto scoped_log =
|
||||
@@ -299,7 +299,7 @@ void NativeOrder::_on_activate(Price p, Quantity q,
|
||||
"Dropping order {}",
|
||||
key().value());
|
||||
transition(ORDER_STATUS_REJECTED_BY_SPARK, "Exchange gateway is down");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// can we trade?
|
||||
@@ -315,7 +315,7 @@ void NativeOrder::_on_activate(Price p, Quantity q,
|
||||
|
||||
transition(ORDER_STATUS_REJECTED_BY_SPARK,
|
||||
fmt::format("Unauthorized order: {}", lqr.error_msg));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// can we trade this security, with this size?
|
||||
@@ -332,7 +332,7 @@ void NativeOrder::_on_activate(Price p, Quantity q,
|
||||
|
||||
transition(ORDER_STATUS_REJECTED_BY_SPARK,
|
||||
fmt::format("Order would breach limit: {}", lqr.error_msg));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// are we under the messaging threshold?
|
||||
@@ -343,7 +343,7 @@ void NativeOrder::_on_activate(Price p, Quantity q,
|
||||
key().value());
|
||||
transition(ORDER_STATUS_REJECTED_BY_SPARK,
|
||||
"Messaging threshold breached");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we are here we are good.
|
||||
@@ -382,21 +382,22 @@ void NativeOrder::_on_activate(Price p, Quantity q,
|
||||
auto const &ts = services.get_time_str();
|
||||
me->create_transact_time_bm = encoding.overwrite(
|
||||
me->create_transact_time_bm, &ts.value()[0], ts.value().size());
|
||||
return true;
|
||||
}
|
||||
|
||||
void NativeOrder::warm_up_activate(OrderServicesData&data, OrderServices& services) {
|
||||
void NativeOrder::warm_up_activate(OrderServicesData &data,
|
||||
OrderServices &services) {
|
||||
bts_assert(!me->services);
|
||||
me->services = &services;
|
||||
init_order_services(data,services);
|
||||
_on_activate(Price(0),Quantity(0),services, trace::CorID{});
|
||||
init_order_services(data, services);
|
||||
_on_activate(Price(0), Quantity(0), services, trace::CorID{});
|
||||
|
||||
me->services->unsubscribe_from_exe_report(
|
||||
*me->session_services,me->exe_report_sid);
|
||||
me->services->unsubscribe_from_cancel_reject(
|
||||
*me->session_services,me->cancel_reject_sid);
|
||||
me->services->unsubscribe_from_gateway_status(
|
||||
*me->session_services);
|
||||
me->services = nullptr;
|
||||
me->services->unsubscribe_from_exe_report(*me->session_services,
|
||||
me->exe_report_sid);
|
||||
me->services->unsubscribe_from_cancel_reject(*me->session_services,
|
||||
me->cancel_reject_sid);
|
||||
me->services->unsubscribe_from_gateway_status(*me->session_services);
|
||||
me->services = nullptr;
|
||||
}
|
||||
|
||||
void NativeOrder::on_activate(Price p, Quantity q,
|
||||
@@ -407,22 +408,22 @@ void NativeOrder::on_activate(Price p, Quantity q,
|
||||
|
||||
switch (me->state.status()) {
|
||||
|
||||
case ORDER_STATUS_INACTIVE: {
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
// this is a brand new order
|
||||
if (_on_activate(p, q, services, trace)) {
|
||||
me->services->queue_fix(*me->session_services,
|
||||
jetfix::MessageTypes::NewOrderSingle,
|
||||
me->create_encoding, trace);
|
||||
|
||||
_on_activate(p,q,services,trace);
|
||||
me->services->queue_fix(*me->session_services,
|
||||
jetfix::MessageTypes::NewOrderSingle,
|
||||
me->create_encoding, trace);
|
||||
me->services->governor(*me->session_services).record_sent();
|
||||
me->state.set_status(ORDER_STATUS_PENDING_WORKING);
|
||||
|
||||
me->services->governor(*me->session_services).record_sent();
|
||||
me->state.set_status(ORDER_STATUS_PENDING_WORKING);
|
||||
|
||||
LOG(OrderMan, Info, "Sent new order with key {} to gateway",
|
||||
key().value());
|
||||
// early return so we skip the expensive report_state() call
|
||||
return;
|
||||
}
|
||||
LOG(OrderMan, Info, "Sent new order with key {} to gateway",
|
||||
key().value());
|
||||
// early return so we skip the expensive report_state() call
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
// all cases below are for orders recovered from disk
|
||||
case ORDER_STATUS_WORKING:
|
||||
|
||||
@@ -39,7 +39,7 @@ protected:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
|
||||
void _on_activate(Price, Quantity, bts::orderman::OrderServices &,
|
||||
bool _on_activate(Price, Quantity, bts::orderman::OrderServices &,
|
||||
trace::CorID const &);
|
||||
void on_exe_report(const NewExecutionReportEvent &);
|
||||
void on_cancel_reject(const CancelRejectEvent &);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "BookAlgo.h"
|
||||
#include "lib/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
#include "lib/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include <google/protobuf/timestamp.pb.h>
|
||||
|
||||
namespace bts {
|
||||
@@ -9,113 +9,165 @@ namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct BookAlgo::PImpl {
|
||||
PImpl()
|
||||
: services(nullptr), fake_detached_child(false), supports_hedge(false),
|
||||
has_activated_child(false) {}
|
||||
|
||||
OrderServices *services;
|
||||
|
||||
struct BookAlgo::PImpl
|
||||
{
|
||||
PImpl() : services(nullptr), is_detached(false),
|
||||
supports_hedge(false), has_activated_child(false)
|
||||
{
|
||||
}
|
||||
|
||||
OrderServices* services;
|
||||
|
||||
api::orderman::OrderDefinition def;
|
||||
api::orderman::OrderState state;
|
||||
|
||||
|
||||
OrderBuilder child_builder;
|
||||
ChildOrder child;
|
||||
api::orderman::OrderState child_state;
|
||||
bool fake_detached_child;
|
||||
|
||||
bool supports_hedge;
|
||||
OrderBuilder hedge_builder;
|
||||
std::vector<ChildOrder> hedges;
|
||||
using HedgeStateMap =
|
||||
boost::container::flat_map<OrderKey, api::orderman::OrderState>;
|
||||
HedgeStateMap hedges_state;
|
||||
|
||||
ChildOrder child;
|
||||
api::orderman::OrderState child_state;
|
||||
bool is_detached;
|
||||
|
||||
bool supports_hedge;
|
||||
OrderBuilder hedge_builder;
|
||||
|
||||
BookAlgo::HedgeMap hedges;
|
||||
BookAlgo::HedgeStateMap hedges_state;
|
||||
|
||||
double tick_size;
|
||||
bool has_activated_child;
|
||||
};
|
||||
|
||||
BookAlgo::BookAlgo() : me(new PImpl()) {}
|
||||
BookAlgo::BookAlgo() : me(new PImpl())
|
||||
{
|
||||
}
|
||||
|
||||
BookAlgo::~BookAlgo() {}
|
||||
|
||||
const api::orderman::OrderDefinition &BookAlgo::definition() const {
|
||||
|
||||
|
||||
const api::orderman::OrderDefinition& BookAlgo::definition() const
|
||||
{
|
||||
return me->def;
|
||||
}
|
||||
|
||||
const api::orderman::OrderState &BookAlgo::this_state() const {
|
||||
const api::orderman::OrderState& BookAlgo::this_state() const
|
||||
{
|
||||
return me->state;
|
||||
}
|
||||
|
||||
void BookAlgo::set_price(double p) { me->state.set_price(p); }
|
||||
|
||||
void BookAlgo::set_quantity(uint32_t s) { me->state.set_quantity(s); }
|
||||
|
||||
const api::orderman::OrderState &BookAlgo::child_state() const {
|
||||
return me->child_state;
|
||||
|
||||
void BookAlgo::set_price(double p)
|
||||
{
|
||||
me->state.set_price(p);
|
||||
}
|
||||
|
||||
void BookAlgo::set_quantity(uint32_t s)
|
||||
{
|
||||
me->state.set_quantity(s);
|
||||
}
|
||||
|
||||
void BookAlgo::set_child_price(double p) { me->child_state.set_price(p); }
|
||||
const api::orderman::OrderState& BookAlgo::child_state() const
|
||||
{
|
||||
return me->child_state;
|
||||
}
|
||||
|
||||
void BookAlgo::set_child_price(double p)
|
||||
{
|
||||
me->child_state.set_price(p);
|
||||
}
|
||||
|
||||
void BookAlgo::set_child_quantity(uint32_t s) {
|
||||
void BookAlgo::set_child_quantity(uint32_t s)
|
||||
{
|
||||
me->child_state.set_quantity(s);
|
||||
}
|
||||
|
||||
void BookAlgo::activate_child(Price p, Quantity q) {
|
||||
// per Pedro, activating the child is not a synchronous calls, and he asserts
|
||||
// that this is done only once
|
||||
if (!me->has_activated_child) {
|
||||
void BookAlgo::activate_child(Price p, Quantity q)
|
||||
{
|
||||
//per Pedro, activating the child is not a synchronous calls, and he asserts that this is done only once
|
||||
if(!me->has_activated_child)
|
||||
{
|
||||
me->child.activate(p, q);
|
||||
me->has_activated_child = true;
|
||||
}
|
||||
}
|
||||
|
||||
void BookAlgo::update_child(Price p, Quantity q)
|
||||
{
|
||||
me->child.update(p, q);
|
||||
}
|
||||
|
||||
void BookAlgo::cancel_child()
|
||||
{
|
||||
me->child.cancel();
|
||||
}
|
||||
|
||||
void BookAlgo::build_and_send_hedge(Price p, Quantity q)
|
||||
{
|
||||
auto hedge = me->hedge_builder.build();
|
||||
auto key = hedge.key();
|
||||
me->hedges[key] = std::move(hedge);
|
||||
me->hedges[key].activate(p, q);
|
||||
}
|
||||
|
||||
void BookAlgo::update_child(Price p, Quantity q) { me->child.update(p, q); }
|
||||
|
||||
void BookAlgo::cancel_child() { me->child.cancel(); }
|
||||
|
||||
void BookAlgo::build_and_send_hedge(Price p, Quantity q) {
|
||||
auto hedge = me->hedge_builder.build();
|
||||
me->hedges.push_back(std::move(hedge));
|
||||
me->hedges.back().activate(p, q);
|
||||
double BookAlgo::tick_size() const
|
||||
{
|
||||
return me->tick_size;
|
||||
}
|
||||
|
||||
double BookAlgo::tick_size() const { return me->tick_size; }
|
||||
BookAlgo::HedgeStateMap& BookAlgo::hedges_state()
|
||||
{
|
||||
return me->hedges_state;
|
||||
}
|
||||
|
||||
void BookAlgo::on_init_new(const api::orderman::OrderDefinition &def,
|
||||
const PreActivationOrderServices &services) {
|
||||
BookAlgo::HedgeMap& BookAlgo::hedges()
|
||||
{
|
||||
return me->hedges;
|
||||
}
|
||||
|
||||
bool BookAlgo::is_detached() const
|
||||
{
|
||||
return me->is_detached;
|
||||
}
|
||||
|
||||
|
||||
void BookAlgo::on_init_new(const api::orderman::OrderDefinition& def,
|
||||
const PreActivationOrderServices &services)
|
||||
{
|
||||
me->services = nullptr;
|
||||
me->def = def;
|
||||
|
||||
me->child_state.set_status(ORDER_STATUS_INACTIVE);
|
||||
|
||||
me->child_state.set_status(ORDER_STATUS_INACTIVE);
|
||||
OrderDefinition child_def = def;
|
||||
setup_child(def, services, me->child_state, &child_def);
|
||||
me->child_builder = OrderBuilder(key(), child_def, services, 1);
|
||||
|
||||
OrderDefinition hedge_def = def;
|
||||
|
||||
OrderDefinition hedge_def = def;
|
||||
me->supports_hedge = setup_hedge(def, services, &hedge_def);
|
||||
if (me->supports_hedge)
|
||||
me->hedge_builder = OrderBuilder(key(), hedge_def, services, 4);
|
||||
|
||||
const bts::api::Security *sec =
|
||||
services.sec_db.resolve(SecurityKey(def.security_key()));
|
||||
if (sec) {
|
||||
if(me->supports_hedge)
|
||||
me->hedge_builder = OrderBuilder(key(), hedge_def, services, 4);
|
||||
|
||||
const bts::api::Security* sec = services.sec_db.resolve(SecurityKey(def.security_key()));
|
||||
if(sec)
|
||||
{
|
||||
me->tick_size = sec->contract_spec().low_tick();
|
||||
transition(ORDER_STATUS_INACTIVE);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
auto msg = fmt::format("Unknown security: {}", def.security_key());
|
||||
me->state.set_last_msg(msg);
|
||||
transition(ORDER_STATUS_REJECTED_BY_SPARK, msg);
|
||||
transition(ORDER_STATUS_REJECTED_BY_SPARK, msg);
|
||||
}
|
||||
}
|
||||
|
||||
void BookAlgo::on_init_recovered(const api::orderman::OrderRecord &rec,
|
||||
const PreActivationOrderServices &) {
|
||||
void BookAlgo::on_init_recovered(const api::orderman::OrderRecord& rec,
|
||||
const PreActivationOrderServices &)
|
||||
{
|
||||
auto const &def = rec.def();
|
||||
me->def = def;
|
||||
me->state = rec.state();
|
||||
switch (me->state.status()) {
|
||||
case ORDER_STATUS_WORKING: {
|
||||
switch (me->state.status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
{
|
||||
auto msg = "System restarted with active BookAlgo";
|
||||
me->state.set_last_msg(msg);
|
||||
transition(ORDER_STATUS_ERROR, msg);
|
||||
@@ -139,24 +191,24 @@ void BookAlgo::on_init_recovered(const api::orderman::OrderRecord &rec,
|
||||
}
|
||||
}
|
||||
|
||||
void BookAlgo::on_activate(Price p, Quantity q, OrderServices &services,
|
||||
trace::CorID const &) {
|
||||
void BookAlgo::on_activate(Price p, Quantity q, OrderServices& services, trace::CorID const&)
|
||||
{
|
||||
bts_assert(!me->services);
|
||||
me->services = &services;
|
||||
switch (this_state().status()) {
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
me->child_builder.activate(services);
|
||||
if (me->supports_hedge)
|
||||
me->child_builder.activate(services);
|
||||
if(me->supports_hedge)
|
||||
me->hedge_builder.activate(services);
|
||||
|
||||
|
||||
me->child = me->child_builder.build();
|
||||
set_price(p.value());
|
||||
set_quantity(q.value());
|
||||
services.subscribe_to_child_updates(
|
||||
[this](auto const &os) { this->on_child_order_update(os); });
|
||||
services.subscribe_to_book(
|
||||
SecurityKey(me->def.security_key()),
|
||||
[this](auto const &b) { this->on_book_update(b); });
|
||||
[this](auto const &os) { this->on_child_order_update(os); });
|
||||
services.subscribe_to_book(SecurityKey(me->def.security_key()),
|
||||
[this](auto const &b) { this->on_book_update(b); });
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
break;
|
||||
|
||||
@@ -172,7 +224,7 @@ void BookAlgo::on_activate(Price p, Quantity q, OrderServices &services,
|
||||
// state)
|
||||
case ORDER_STATUS_WORKING:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
@@ -181,11 +233,14 @@ void BookAlgo::on_activate(Price p, Quantity q, OrderServices &services,
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::on_update(Price p, Quantity q, trace::CorID const &) {
|
||||
void BookAlgo::on_update(Price p, Quantity q, trace::CorID const&)
|
||||
{
|
||||
LOG(OrderMan, Info, "Updating BookAlgo {}", key().value());
|
||||
switch (this_state().status()) {
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status()) {
|
||||
switch (child_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
set_price(p.value());
|
||||
set_quantity(q.value());
|
||||
@@ -193,8 +248,8 @@ void BookAlgo::on_update(Price p, Quantity q, trace::CorID const &) {
|
||||
case ORDER_STATUS_WORKING:
|
||||
set_price(p.value());
|
||||
set_quantity(q.value());
|
||||
if (!me->fake_detached_child)
|
||||
me->child.update(p, q);
|
||||
if(!me->is_detached)
|
||||
me->child.update(p,q);
|
||||
break;
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
// we launched our child but we are not yet able
|
||||
@@ -204,7 +259,7 @@ void BookAlgo::on_update(Price p, Quantity q, trace::CorID const &) {
|
||||
|
||||
// if the child was in any of these states we couldn't be
|
||||
// in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
@@ -235,22 +290,25 @@ void BookAlgo::on_update(Price p, Quantity q, trace::CorID const &) {
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::on_crack(trace::CorID const &) {}
|
||||
void BookAlgo::on_crack(trace::CorID const&) {}
|
||||
|
||||
void BookAlgo::on_cancel(trace::CorID const &) {
|
||||
void BookAlgo::on_cancel(trace::CorID const&)
|
||||
{
|
||||
LOG(OrderMan, Info, "Canceling BookAlgo Order {}", key().value());
|
||||
|
||||
switch (this_state().status()) {
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status()) {
|
||||
switch (child_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
transition(ORDER_STATUS_CANCELED);
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
if (!me->fake_detached_child)
|
||||
if(!me->is_detached)
|
||||
me->child.cancel();
|
||||
for (auto &i : me->hedges)
|
||||
i.crack();
|
||||
for(auto& i : me->hedges)
|
||||
i.second.crack();
|
||||
transition(ORDER_STATUS_CANCELED);
|
||||
break;
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
@@ -259,7 +317,7 @@ void BookAlgo::on_cancel(trace::CorID const &) {
|
||||
break;
|
||||
|
||||
// child cannot be in any of these if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
@@ -289,50 +347,103 @@ void BookAlgo::on_cancel(trace::CorID const &) {
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::transition(api::orderman::OrderStatus new_status,
|
||||
std::string msg) {
|
||||
void BookAlgo::transition(api::orderman::OrderStatus new_status, std::string msg)
|
||||
{
|
||||
auto const &from = OrderStatus_Name(me->state.status());
|
||||
auto const &to = OrderStatus_Name(new_status);
|
||||
LOG(OrderMan, Info, "Transitioning BookAlgo {} from {} to {}. Msg: {}",
|
||||
key().value(), from, to, msg);
|
||||
LOG(OrderMan, Info, "Transitioning BookAlgo {} from {} to {}. Msg: {}", key().value(), from, to, msg);
|
||||
me->state.set_status(new_status);
|
||||
}
|
||||
|
||||
void BookAlgo::on_child_order_update(const OrderStateUpdateEvent &ev) {
|
||||
if (me->fake_detached_child)
|
||||
return;
|
||||
if (ev.key != me->child.key())
|
||||
void BookAlgo::on_child_order_update(const OrderStateUpdateEvent& ev)
|
||||
{
|
||||
if(me->is_detached)
|
||||
return;
|
||||
if(ev.key != me->child.key()) //hedge state change
|
||||
{
|
||||
api::orderman::OrderState orig_state = api::orderman::OrderState::default_instance(); //TODO this isn't quite right, should be hedgetype state
|
||||
auto it = me->hedges_state.find(ev.key);
|
||||
if(it != me->hedges_state.end())
|
||||
orig_state = it->second;
|
||||
me->hedges_state[ev.key] = ev.rebuild_state();
|
||||
else {
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (ev.status)
|
||||
{
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
process_hedge_state_change(orig_state, ev);
|
||||
break;
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
process_hedge_state_change(orig_state, ev);
|
||||
transition(ORDER_STATUS_COMPLETED);
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
process_hedge_state_change(orig_state, ev);
|
||||
transition(ORDER_STATUS_ERROR, "Unexpected child order state");
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else //child state change
|
||||
{
|
||||
api::orderman::OrderState orig_state = child_state();
|
||||
me->child_state = ev.rebuild_state();
|
||||
switch (this_state().status()) {
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (ev.status) {
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
if (me->supports_hedge)
|
||||
send_hedge_if_new_fills(orig_state, ev);
|
||||
break;
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
transition(ORDER_STATUS_COMPLETED);
|
||||
if (me->supports_hedge)
|
||||
send_hedge_if_new_fills(orig_state, ev);
|
||||
break;
|
||||
switch (ev.status)
|
||||
{
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
process_child_state_change(orig_state, ev);
|
||||
break;
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
process_child_state_change(orig_state, ev);
|
||||
transition(ORDER_STATUS_COMPLETED);
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
transition(ORDER_STATUS_ERROR, "Unexpected child order state");
|
||||
break;
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
process_child_state_change(orig_state, ev);
|
||||
transition(ORDER_STATUS_ERROR, "Unexpected child order state");
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -354,56 +465,55 @@ void BookAlgo::on_child_order_update(const OrderStateUpdateEvent &ev) {
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::report_state() {
|
||||
auto merged_state = me->state;
|
||||
void BookAlgo::report_state()
|
||||
{
|
||||
OrderState merged_state = me->state;
|
||||
merged_state.set_filled_quantity(child_state().filled_quantity());
|
||||
merged_state.set_last_price(child_state().last_price());
|
||||
merged_state.set_avg_price(child_state().avg_price());
|
||||
merged_state.set_display_status(
|
||||
bts::api::orderman::OrderState_DisplayStatus::
|
||||
OrderState_DisplayStatus_HIDDEN);
|
||||
merged_state.set_display_status(bts::api::orderman::OrderState_DisplayStatus::OrderState_DisplayStatus_HIDDEN);
|
||||
me->services->report_state(merged_state);
|
||||
}
|
||||
|
||||
void BookAlgo::payup(const md::PriceSize &opposite,
|
||||
const bts::orderman::BookUpdateEvent &,
|
||||
uint32_t cross_ticks) {
|
||||
void BookAlgo::payup(const md::PriceSize& opposite, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks)
|
||||
{
|
||||
double new_price = opposite.price() + me->tick_size * cross_ticks;
|
||||
me->child.update(Price(new_price), Quantity(this_state().quantity()));
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::paydown(const md::PriceSize &opposite,
|
||||
const bts::orderman::BookUpdateEvent &,
|
||||
uint32_t cross_ticks) {
|
||||
void BookAlgo::paydown(const md::PriceSize& opposite, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks)
|
||||
{
|
||||
double new_price = opposite.price() - me->tick_size * cross_ticks;
|
||||
me->child.update(Price(new_price), Quantity(this_state().quantity()));
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::detach(bts::api::orderman::OrderStatus status_after_detach) {
|
||||
me->fake_detached_child = true;
|
||||
void BookAlgo::detach(bts::api::orderman::OrderStatus status_after_detach)
|
||||
{
|
||||
me->is_detached = true;
|
||||
me->child.crack();
|
||||
transition(status_after_detach);
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::on_book_update(const bts::orderman::BookUpdateEvent &) {}
|
||||
void BookAlgo::on_book_update(const bts::orderman::BookUpdateEvent&) {}
|
||||
|
||||
bool BookAlgo::setup_hedge(const api::orderman::OrderDefinition &,
|
||||
const PreActivationOrderServices &,
|
||||
api::orderman::OrderDefinition *) {
|
||||
return false;
|
||||
bool BookAlgo::setup_hedge(const api::orderman::OrderDefinition&, const PreActivationOrderServices&, api::orderman::OrderDefinition*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void BookAlgo::send_hedge_if_new_fills(
|
||||
const api::orderman::OrderState &orig_state,
|
||||
const OrderStateUpdateEvent &new_state) {
|
||||
UNUSED(orig_state);
|
||||
UNUSED(new_state);
|
||||
// uint32_t qty = new_state.state.filled_quantity() -
|
||||
// orig_state.filled_quantity(); UNUSED(qty);
|
||||
void BookAlgo::process_child_state_change(const api::orderman::OrderState& orig_state, const OrderStateUpdateEvent& new_state)
|
||||
{
|
||||
UNUSED(orig_state);UNUSED(new_state);
|
||||
}
|
||||
|
||||
void BookAlgo::process_hedge_state_change(const api::orderman::OrderState& orig_state, const OrderStateUpdateEvent& new_state)
|
||||
{
|
||||
UNUSED(orig_state);UNUSED(new_state);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -34,7 +34,8 @@ protected:
|
||||
api::orderman::OrderState&, api::orderman::OrderDefinition*) = 0;
|
||||
virtual bool setup_hedge(const api::orderman::OrderDefinition& parent, const PreActivationOrderServices&,
|
||||
api::orderman::OrderDefinition*);
|
||||
virtual void send_hedge_if_new_fills(const api::orderman::OrderState&, const OrderStateUpdateEvent&);
|
||||
virtual void process_child_state_change(const api::orderman::OrderState&, const OrderStateUpdateEvent&);
|
||||
virtual void process_hedge_state_change(const api::orderman::OrderState&, const OrderStateUpdateEvent&);
|
||||
|
||||
virtual void on_child_order_update(const OrderStateUpdateEvent&);
|
||||
virtual void on_book_update(const bts::orderman::BookUpdateEvent&);
|
||||
@@ -50,6 +51,7 @@ private:
|
||||
std::unique_ptr<PImpl> me;
|
||||
|
||||
protected:
|
||||
double tick_size() const;
|
||||
const api::orderman::OrderState& this_state() const;
|
||||
void set_price(double);
|
||||
void set_quantity(uint32_t);
|
||||
@@ -60,9 +62,14 @@ protected:
|
||||
void activate_child(Price p, Quantity q);
|
||||
void update_child(Price, Quantity);
|
||||
void cancel_child();
|
||||
bool is_detached() const;
|
||||
|
||||
void build_and_send_hedge(Price, Quantity);
|
||||
double tick_size() const;
|
||||
typedef boost::container::flat_map<OrderKey, ChildOrder> HedgeMap;
|
||||
typedef boost::container::flat_map<OrderKey, api::orderman::OrderState> HedgeStateMap;
|
||||
HedgeStateMap& hedges_state();
|
||||
HedgeMap& hedges();
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -10,87 +10,94 @@ namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct CoverOrder::PImpl {
|
||||
struct CoverOrder::PImpl
|
||||
{
|
||||
PImpl() {}
|
||||
|
||||
int hedge_ticks;
|
||||
OrderDefinition hedge_def;
|
||||
|
||||
int hedge_ticks;
|
||||
OrderDefinition hedge_def;
|
||||
};
|
||||
|
||||
|
||||
CoverOrder::CoverOrder() : me(new PImpl()) {}
|
||||
|
||||
CoverOrder::~CoverOrder() {}
|
||||
|
||||
void CoverOrder::setup_child(const api::orderman::OrderDefinition &this_def,
|
||||
const PreActivationOrderServices &,
|
||||
api::orderman::OrderState &initial_state,
|
||||
api::orderman::OrderDefinition *out_child) {
|
||||
void CoverOrder::setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState& initial_state, api::orderman::OrderDefinition* out_child)
|
||||
{
|
||||
initial_state.mutable_cover();
|
||||
auto const &cover_def = this_def.custom_def().cover();
|
||||
auto const &cover_def = this_def.custom_def().cover();
|
||||
*out_child->mutable_custom_def() = cover_def.child();
|
||||
|
||||
me->hedge_ticks = cover_def.hedge_ticks();
|
||||
|
||||
me->hedge_ticks = cover_def.hedge_ticks();
|
||||
}
|
||||
|
||||
bool CoverOrder::setup_hedge(const api::orderman::OrderDefinition &this_def,
|
||||
const PreActivationOrderServices &,
|
||||
api::orderman::OrderDefinition *out_hedge) {
|
||||
bool CoverOrder::setup_hedge(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderDefinition* out_hedge)
|
||||
{
|
||||
auto const &cover_def = this_def.custom_def().cover();
|
||||
if (cover_def.has_hedge()) {
|
||||
*out_hedge->mutable_custom_def() = cover_def.hedge();
|
||||
if (definition().side() == api::SIDE_BUY)
|
||||
if(cover_def.has_hedge())
|
||||
{
|
||||
*out_hedge->mutable_custom_def() = cover_def.hedge();
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
out_hedge->set_side(api::SIDE_SELL);
|
||||
else
|
||||
out_hedge->set_side(api::SIDE_BUY);
|
||||
return true;
|
||||
} else
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void CoverOrder::send_hedge_if_new_fills(
|
||||
const api::orderman::OrderState &orig_state,
|
||||
const OrderStateUpdateEvent &new_state) {
|
||||
void CoverOrder::process_child_state_change(const api::orderman::OrderState& orig_state, const OrderStateUpdateEvent& new_state)
|
||||
{
|
||||
uint32_t qty = new_state.filled_quantity - orig_state.filled_quantity();
|
||||
if (qty > 0) {
|
||||
if(qty > 0)
|
||||
{
|
||||
double prx = new_state.last_price;
|
||||
if (definition().side() ==
|
||||
api::SIDE_BUY) // if this is a buy, I'm going to sell my hedge, nticks
|
||||
// higher than the fill price
|
||||
if(definition().side() == api::SIDE_BUY) //if this is a buy, I'm going to sell my hedge, nticks higher than the fill price
|
||||
prx = prx + tick_size() * me->hedge_ticks;
|
||||
else
|
||||
prx = prx - tick_size() * me->hedge_ticks;
|
||||
|
||||
|
||||
build_and_send_hedge(Price(prx), Quantity(qty));
|
||||
}
|
||||
}
|
||||
|
||||
void CoverOrder::on_book_update(const bts::orderman::BookUpdateEvent &) {
|
||||
switch (this_state().status()) {
|
||||
void CoverOrder::on_book_update(const bts::orderman::BookUpdateEvent& )
|
||||
{
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status()) {
|
||||
case ORDER_STATUS_INACTIVE: {
|
||||
activate_child(Price(this_state().price()),
|
||||
Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
report_state();
|
||||
} break;
|
||||
case ORDER_STATUS_WORKING: {
|
||||
} break;
|
||||
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
switch (child_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
{
|
||||
activate_child(Price(this_state().price()), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
report_state();
|
||||
}
|
||||
break;
|
||||
|
||||
// child cannot be in any of these states if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
case ORDER_STATUS_WORKING:
|
||||
{
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
|
||||
// child cannot be in any of these states if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
@@ -106,8 +113,9 @@ void CoverOrder::on_book_update(const bts::orderman::BookUpdateEvent &) {
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -22,7 +22,7 @@ protected:
|
||||
api::orderman::OrderState&, api::orderman::OrderDefinition*) override;
|
||||
bool setup_hedge(const api::orderman::OrderDefinition& parent, const PreActivationOrderServices&,
|
||||
api::orderman::OrderDefinition*) override;
|
||||
void send_hedge_if_new_fills(const api::orderman::OrderState&, const OrderStateUpdateEvent&) override;
|
||||
void process_child_state_change(const api::orderman::OrderState&, const OrderStateUpdateEvent&) override;
|
||||
void on_book_update(const bts::orderman::BookUpdateEvent&) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
#include "OCOOrder.h"
|
||||
|
||||
#include "lib/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
#include <google/protobuf/timestamp.pb.h>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct OCOOrder::PImpl
|
||||
{
|
||||
PImpl() {}
|
||||
|
||||
int other_ticks;
|
||||
bts::api::RelativeSide other_side;
|
||||
OrderDefinition other_def;
|
||||
bool hedge_sent;
|
||||
};
|
||||
|
||||
|
||||
OCOOrder::OCOOrder() : me(new PImpl()) {}
|
||||
|
||||
OCOOrder::~OCOOrder() {}
|
||||
|
||||
void OCOOrder::setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState& initial_state, api::orderman::OrderDefinition* out_child)
|
||||
{
|
||||
initial_state.mutable_oco();
|
||||
auto const &oco_def = this_def.custom_def().oco();
|
||||
*out_child->mutable_custom_def() = oco_def.child();
|
||||
|
||||
me->other_ticks = oco_def.other_ticks();
|
||||
me->other_side = oco_def.other_side();
|
||||
me->hedge_sent = false;
|
||||
}
|
||||
|
||||
//hedge here means 'other'
|
||||
bool OCOOrder::setup_hedge(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderDefinition* out_hedge)
|
||||
{
|
||||
auto const &oco_def = this_def.custom_def().oco();
|
||||
if(oco_def.has_other())
|
||||
{
|
||||
*out_hedge->mutable_custom_def() = oco_def.other();
|
||||
bts_assert_msg(me->other_side != api::RELATIVESIDE_UNDEFINED, "undefined relativeside");
|
||||
|
||||
if(oco_def.other_side() == api::RELATIVESIDE_BUY)
|
||||
out_hedge->set_side(api::SIDE_BUY);
|
||||
else if(oco_def.other_side() == api::RELATIVESIDE_SELL)
|
||||
out_hedge->set_side(api::SIDE_SELL);
|
||||
else if(oco_def.other_side() == api::RELATIVESIDE_SAME)
|
||||
out_hedge->set_side(definition().side());
|
||||
else if(oco_def.other_side() == api::RELATIVESIDE_OPPOSITE)
|
||||
{
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
out_hedge->set_side(api::SIDE_SELL);
|
||||
else
|
||||
out_hedge->set_side(api::SIDE_BUY);
|
||||
}
|
||||
else
|
||||
bts_assert_msg(false, "unknown relativeside");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void OCOOrder::process_child_state_change(const api::orderman::OrderState& orig_state, const OrderStateUpdateEvent& new_state)
|
||||
{
|
||||
uint32_t fill_qty = new_state.filled_quantity - orig_state.filled_quantity();
|
||||
const auto& the_state = hedges_state();
|
||||
auto& the_hedges = hedges();
|
||||
for(const auto& i : the_state)
|
||||
{
|
||||
const auto& hedge_state = i.second;
|
||||
|
||||
uint32_t working_qty = hedge_state.quantity() - hedge_state.filled_quantity();
|
||||
uint32_t newqty = working_qty - std::min(fill_qty, working_qty);
|
||||
bts_assert(newqty <= working_qty);
|
||||
|
||||
auto it = the_hedges.find(i.first);
|
||||
if(it != the_hedges.end())
|
||||
{
|
||||
if(newqty != working_qty)
|
||||
{
|
||||
if(newqty > 0)
|
||||
{
|
||||
LOG(OrderMan, Info, "OCO reducing hedge qty from {} to {}", working_qty, newqty);
|
||||
it->second.update(Price(hedge_state.price()), Quantity(newqty));
|
||||
}
|
||||
else
|
||||
it->second.cancel();
|
||||
}
|
||||
else if(new_state.status != api::orderman::OrderStatus::ORDER_STATUS_PENDING_WORKING &&
|
||||
new_state.status != api::orderman::OrderStatus::ORDER_STATUS_WORKING)
|
||||
{
|
||||
it->second.cancel();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(OrderMan, Error, "OCO Can't find hedge order {}", i.first);
|
||||
cancel_child();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OCOOrder::process_hedge_state_change(const api::orderman::OrderState& orig_state, const OrderStateUpdateEvent& new_state)
|
||||
{
|
||||
uint32_t fill_qty = new_state.filled_quantity - orig_state.filled_quantity();
|
||||
uint32_t working_qty = child_state().quantity() - child_state().filled_quantity();
|
||||
uint32_t newqty = working_qty - std::min(fill_qty, working_qty);
|
||||
bts_assert(newqty <= working_qty);
|
||||
if(newqty != working_qty)
|
||||
{
|
||||
if(newqty > 0)
|
||||
{
|
||||
LOG(OrderMan, Info, "OCO reducing child qty from {} to {}", working_qty, newqty);
|
||||
update_child(Price(child_state().price()), Quantity(newqty));
|
||||
}
|
||||
else
|
||||
cancel_child();
|
||||
}
|
||||
else if(new_state.status != api::orderman::OrderStatus::ORDER_STATUS_PENDING_WORKING &&
|
||||
new_state.status != api::orderman::OrderStatus::ORDER_STATUS_WORKING)
|
||||
{
|
||||
cancel_child();
|
||||
}
|
||||
}
|
||||
|
||||
void OCOOrder::on_book_update(const bts::orderman::BookUpdateEvent& )
|
||||
{
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
{
|
||||
activate_child(Price(this_state().price()), Quantity(this_state().quantity()));
|
||||
|
||||
if(!me->hedge_sent)
|
||||
{
|
||||
double prx = this_state().price();
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
prx = prx + tick_size() * me->other_ticks;
|
||||
else
|
||||
prx = prx - tick_size() * me->other_ticks;
|
||||
build_and_send_hedge( Price(prx), Quantity(this_state().quantity()) );
|
||||
me->hedge_sent = true;
|
||||
}
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
report_state();
|
||||
}
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
{
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
|
||||
// child cannot be in any of these states if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OCOOrder::on_cancel(trace::CorID const&)
|
||||
{
|
||||
LOG(OrderMan, Info, "Canceling OCOOrder Order {}", key().value());
|
||||
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
transition(ORDER_STATUS_CANCELED);
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
if(!is_detached())
|
||||
{
|
||||
cancel_child();
|
||||
for(auto& i : hedges())
|
||||
i.second.cancel();
|
||||
}
|
||||
transition(ORDER_STATUS_CANCELED);
|
||||
break;
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
// child is running but it cannot be canceled at this point
|
||||
transition(this_state().status(), "Cannot cancel order");
|
||||
break;
|
||||
|
||||
// child cannot be in any of these if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
transition(this_state().status(), "Cannot cancel order");
|
||||
break;
|
||||
|
||||
// these are unreacheable for this order type
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/orderman/orders/book_algos/BookAlgo.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class OrderStateUpdateEvent;
|
||||
class BookUpdateEvent;
|
||||
|
||||
//OCOOrder initializes the child order as normal
|
||||
//it also initializes a 'hedge' or 'other' at the same time, the child and the hedge are managed with OCO semantics:
|
||||
//when one fills the other's quantity is reduced (or cancelled on a full fill)
|
||||
|
||||
class OCOOrder : public BookAlgo
|
||||
{
|
||||
public:
|
||||
OCOOrder();
|
||||
virtual ~OCOOrder();
|
||||
|
||||
void on_cancel(trace::CorID const&) override ;
|
||||
protected:
|
||||
void setup_child(const api::orderman::OrderDefinition&, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState&, api::orderman::OrderDefinition*) override;
|
||||
bool setup_hedge(const api::orderman::OrderDefinition& parent, const PreActivationOrderServices&,
|
||||
api::orderman::OrderDefinition*) override;
|
||||
void process_child_state_change(const api::orderman::OrderState&, const OrderStateUpdateEvent&) override;
|
||||
void process_hedge_state_change(const api::orderman::OrderState&, const OrderStateUpdateEvent&) override;
|
||||
void on_book_update(const bts::orderman::BookUpdateEvent&) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -10,65 +10,75 @@ namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct OffsetOrder::PImpl {
|
||||
struct OffsetOrder::PImpl
|
||||
{
|
||||
PImpl() {}
|
||||
|
||||
uint32_t tick_offset_on_deploy;
|
||||
|
||||
uint32_t tick_offset_on_deploy;
|
||||
};
|
||||
|
||||
|
||||
OffsetOrder::OffsetOrder() : me(new PImpl()) {}
|
||||
|
||||
OffsetOrder::~OffsetOrder() {}
|
||||
|
||||
void OffsetOrder::setup_child(const api::orderman::OrderDefinition &this_def,
|
||||
const PreActivationOrderServices &,
|
||||
api::orderman::OrderState &initial_state,
|
||||
api::orderman::OrderDefinition *out_child) {
|
||||
initial_state.mutable_tick();
|
||||
auto const &offset_def = this_def.custom_def().offset();
|
||||
void OffsetOrder::setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState& initial_state, api::orderman::OrderDefinition* out_child)
|
||||
{
|
||||
initial_state.mutable_offset();
|
||||
auto const &offset_def = this_def.custom_def().offset();
|
||||
*out_child->mutable_custom_def() = offset_def.child();
|
||||
|
||||
me->tick_offset_on_deploy = offset_def.tick_offset_on_deploy();
|
||||
|
||||
me->tick_offset_on_deploy = offset_def.tick_offset_on_deploy();
|
||||
}
|
||||
|
||||
void OffsetOrder::on_book_update(const bts::orderman::BookUpdateEvent &book) {
|
||||
switch (this_state().status()) {
|
||||
|
||||
void OffsetOrder::on_book_update(const bts::orderman::BookUpdateEvent& book)
|
||||
{
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status()) {
|
||||
case ORDER_STATUS_INACTIVE: {
|
||||
if (definition().side() == api::SIDE_BUY) {
|
||||
const auto &same = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
double price =
|
||||
same.price() - tick_size() * me->tick_offset_on_deploy;
|
||||
activate_child(Price(price), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
} else {
|
||||
const auto &same = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
double price =
|
||||
same.price() + tick_size() * me->tick_offset_on_deploy;
|
||||
activate_child(Price(price), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
switch (child_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
{
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
{
|
||||
const auto& same = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
double price = same.price() - tick_size() * me->tick_offset_on_deploy;
|
||||
activate_child(Price(price), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& same = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
double price = same.price() + tick_size() * me->tick_offset_on_deploy;
|
||||
activate_child(Price(price), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
report_state();
|
||||
} break;
|
||||
case ORDER_STATUS_WORKING: {
|
||||
detach();
|
||||
} break;
|
||||
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
|
||||
// child cannot be in any of these states if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
case ORDER_STATUS_WORKING:
|
||||
{
|
||||
detach();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
|
||||
// child cannot be in any of these states if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
@@ -84,8 +94,9 @@ void OffsetOrder::on_book_update(const bts::orderman::BookUpdateEvent &book) {
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "PricerOrder.h"
|
||||
|
||||
#include "lib/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include <google/protobuf/timestamp.pb.h>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct PricerOrder::PImpl
|
||||
{
|
||||
PImpl() {}
|
||||
|
||||
int32_t price_offset_ticks;
|
||||
double price_factor;
|
||||
};
|
||||
|
||||
|
||||
PricerOrder::PricerOrder() : me(new PImpl()) {}
|
||||
|
||||
PricerOrder::~PricerOrder() {}
|
||||
|
||||
void PricerOrder::setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState& initial_state, api::orderman::OrderDefinition* out_child)
|
||||
{
|
||||
initial_state.mutable_pricer();
|
||||
auto const &pricer_def = this_def.custom_def().pricer();
|
||||
*out_child->mutable_custom_def() = pricer_def.child();
|
||||
|
||||
me->price_offset_ticks = pricer_def.price_offset_ticks();
|
||||
me->price_factor = pricer_def.price_factor();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/orderman/orders/book_algos/BookAlgo.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class OrderStateUpdateEvent;
|
||||
class BookUpdateEvent;
|
||||
|
||||
//PricerOrder matches the bid (for buy) or offer (for sell) price of it's target security times a factor plus a constant
|
||||
|
||||
class PricerOrder : public BookAlgo
|
||||
{
|
||||
public:
|
||||
PricerOrder();
|
||||
virtual ~PricerOrder();
|
||||
|
||||
protected:
|
||||
void setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState&, api::orderman::OrderDefinition*) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -1,172 +0,0 @@
|
||||
#include "TickOrder.h"
|
||||
|
||||
#include "lib/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include <google/protobuf/timestamp.pb.h>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct TickOrder::PImpl {
|
||||
PImpl() {}
|
||||
|
||||
uint32_t tick_offset_on_deploy;
|
||||
spark::QuantityMeasure cross_threshold;
|
||||
uint32_t cross_ticks;
|
||||
uint32_t max_chase_ticks;
|
||||
bool detach_on_toward;
|
||||
|
||||
md::PriceSize opposite_at_inception;
|
||||
};
|
||||
|
||||
TickOrder::TickOrder() : me(new PImpl()) {}
|
||||
|
||||
TickOrder::~TickOrder() {}
|
||||
|
||||
void TickOrder::setup_child(const api::orderman::OrderDefinition &this_def,
|
||||
const PreActivationOrderServices &,
|
||||
api::orderman::OrderState &initial_state,
|
||||
api::orderman::OrderDefinition *out_child) {
|
||||
initial_state.mutable_tick();
|
||||
auto const &tick_def = this_def.custom_def().tick();
|
||||
*out_child->mutable_custom_def() = tick_def.child();
|
||||
|
||||
me->tick_offset_on_deploy = tick_def.tick_offset_on_deploy();
|
||||
me->cross_threshold = tick_def.cross_threshold();
|
||||
me->cross_ticks = tick_def.cross_ticks();
|
||||
me->max_chase_ticks = tick_def.max_chase_ticks();
|
||||
me->cross_threshold = tick_def.cross_threshold();
|
||||
me->detach_on_toward = tick_def.detach_on_toward();
|
||||
}
|
||||
|
||||
void TickOrder::payup(const md::PriceSize &opposite,
|
||||
const bts::orderman::BookUpdateEvent &,
|
||||
uint32_t cross_ticks) {
|
||||
double new_price = opposite.price() + tick_size() * cross_ticks;
|
||||
double limit =
|
||||
me->opposite_at_inception.price() + tick_size() * me->max_chase_ticks;
|
||||
if (new_price <= limit)
|
||||
update_child(Price(new_price), Quantity(this_state().quantity()));
|
||||
else {
|
||||
update_child(Price(limit), Quantity(this_state().quantity()));
|
||||
detach();
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
void TickOrder::paydown(const md::PriceSize &opposite,
|
||||
const bts::orderman::BookUpdateEvent &,
|
||||
uint32_t cross_ticks) {
|
||||
double new_price = opposite.price() - tick_size() * cross_ticks;
|
||||
double limit =
|
||||
me->opposite_at_inception.price() - tick_size() * me->max_chase_ticks;
|
||||
if (new_price >= limit)
|
||||
update_child(Price(new_price), Quantity(this_state().quantity()));
|
||||
else {
|
||||
update_child(Price(limit), Quantity(this_state().quantity()));
|
||||
detach();
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
void TickOrder::on_book_update(const bts::orderman::BookUpdateEvent &book) {
|
||||
switch (this_state().status()) {
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status()) {
|
||||
case ORDER_STATUS_INACTIVE: {
|
||||
if (definition().side() == api::SIDE_BUY) {
|
||||
me->opposite_at_inception = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
if (me->tick_offset_on_deploy > 0) {
|
||||
double price = me->opposite_at_inception.price() -
|
||||
tick_size() * me->tick_offset_on_deploy;
|
||||
activate_child(Price(price), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
} else {
|
||||
activate_child(Price(this_state().price()),
|
||||
Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
} else {
|
||||
me->opposite_at_inception = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
if (me->tick_offset_on_deploy > 0) {
|
||||
double price = me->opposite_at_inception.price() +
|
||||
tick_size() * me->tick_offset_on_deploy;
|
||||
activate_child(Price(price), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
} else {
|
||||
activate_child(Price(this_state().price()),
|
||||
Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
}
|
||||
report_state();
|
||||
} break;
|
||||
case ORDER_STATUS_WORKING: {
|
||||
Quantity thresh = SpreaderUtils::_quantity_threshold(
|
||||
me->cross_threshold, Quantity(this_state().quantity()));
|
||||
if (definition().side() == api::SIDE_BUY) {
|
||||
const auto &opposite = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
if (opposite.price() < me->opposite_at_inception.price()) {
|
||||
if (me->detach_on_toward)
|
||||
detach();
|
||||
else
|
||||
me->opposite_at_inception = opposite;
|
||||
} else if (opposite.price() > me->opposite_at_inception.price())
|
||||
payup(opposite, book, me->cross_ticks);
|
||||
else {
|
||||
if (opposite.size() <= thresh.value())
|
||||
payup(opposite, book, me->cross_ticks);
|
||||
}
|
||||
} else {
|
||||
const auto &opposite = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
if (opposite.price() > me->opposite_at_inception.price()) {
|
||||
if (me->detach_on_toward)
|
||||
detach();
|
||||
else
|
||||
me->opposite_at_inception = opposite;
|
||||
} else if (opposite.price() < me->opposite_at_inception.price())
|
||||
paydown(opposite, book, me->cross_ticks);
|
||||
else {
|
||||
if (opposite.size() <= thresh.value())
|
||||
paydown(opposite, book, me->cross_ticks);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
|
||||
// child cannot be in any of these states if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/orderman/orders/book_algos/BookAlgo.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class OrderStateUpdateEvent;
|
||||
class BookUpdateEvent;
|
||||
|
||||
//TickOrder deploys N ticks away from the initial opposite inside market and watches for it to trade below a certain size.
|
||||
//If that size goes below a threshold or the market moves away, TickOrder Pays up once.
|
||||
//The process repeats until the algo has paid up a max of N ticks from the original opposite inside order.
|
||||
//If the market moves toward, the algo either starts watching the new inside market and keeps going or detaches depending on a parameter
|
||||
|
||||
class TickOrder : public BookAlgo
|
||||
{
|
||||
public:
|
||||
TickOrder();
|
||||
virtual ~TickOrder();
|
||||
|
||||
protected:
|
||||
void setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState&, api::orderman::OrderDefinition*) override;
|
||||
void on_book_update(const bts::orderman::BookUpdateEvent&) override;
|
||||
private:
|
||||
void payup(const md::PriceSize& opposite, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks) override;
|
||||
void paydown(const md::PriceSize& opposite, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -22,6 +22,7 @@ _filled_quantity(src._filled_quantity),
|
||||
_last_filled_price(src._last_filled_price),
|
||||
_last_reported_order_price(src._last_reported_order_price),
|
||||
_last_filled_quantity(src._last_filled_quantity),
|
||||
_last_update_trace(src._last_update_trace),
|
||||
_out_price(src._out_price),
|
||||
_out_quantity(src._out_quantity),
|
||||
_order_place_time(src._order_place_time)
|
||||
@@ -54,6 +55,7 @@ void SpreaderChildOrderState::re_init (ChildOrder& child_order){
|
||||
_last_filled_price = Price(0);
|
||||
_last_reported_order_price=Price(0);
|
||||
_last_filled_quantity = Quantity(0);
|
||||
_last_update_trace=trace::CorID();
|
||||
_out_price = Price(0);
|
||||
_out_quantity = Quantity(0);
|
||||
_order_place_time=bts::TimeStamp();
|
||||
|
||||
@@ -44,6 +44,9 @@ private:
|
||||
Price _last_filled_price;
|
||||
Price _last_reported_order_price;
|
||||
Quantity _last_filled_quantity;
|
||||
// this is the trace of the order event
|
||||
// from the last on_order_update call
|
||||
trace::CorID _last_update_trace;
|
||||
// this is latest price, qty sent out
|
||||
Price _out_price;
|
||||
Quantity _out_quantity;
|
||||
@@ -113,6 +116,9 @@ public:
|
||||
bts::TimeStamp order_place_time() const{
|
||||
return _order_place_time;
|
||||
}
|
||||
const trace::CorID& last_update_trace() const{
|
||||
return _last_update_trace;
|
||||
}
|
||||
// init ///////////////////
|
||||
///////////////////////////
|
||||
// this assignes a new order, and subsumes the earlier order's
|
||||
@@ -134,6 +140,7 @@ public:
|
||||
- _filled_quantity.value());
|
||||
_last_filled_price = Price(e.last_price);
|
||||
_last_reported_order_price=Price(e.price);
|
||||
_last_update_trace=e.trace;
|
||||
SpreaderUtils::compute_average_price(_filled_price,
|
||||
_filled_quantity,
|
||||
_last_filled_price,
|
||||
|
||||
+5
-2
@@ -17,7 +17,9 @@ void QuoterWorkingStateWorker::on_transition (){
|
||||
// waiting for the ack, things could have changed (namely
|
||||
// fills in other legs, books, order qty change), hence
|
||||
// we analyze and potentially modify/cxl immediatly
|
||||
_longpath_analyze_and_act ();
|
||||
trace::CorID ack_message_trace_id=
|
||||
_quoter->_child_order_state.last_update_trace();
|
||||
_longpath_analyze_and_act (ack_message_trace_id);
|
||||
}
|
||||
void QuoterWorkingStateWorker::one_time_activate (Price,
|
||||
Quantity){
|
||||
@@ -25,10 +27,11 @@ void QuoterWorkingStateWorker::one_time_activate (Price,
|
||||
}
|
||||
void QuoterWorkingStateWorker::on_order_update (const SpreaderChildOrderState& u){
|
||||
bts_assert (u.acknowledged());
|
||||
trace::CorID ack_message_trace_id=u.last_update_trace();
|
||||
if (u.terminal_status())
|
||||
_quoter->_transform_to_END ();
|
||||
else
|
||||
_longpath_analyze_and_act ();
|
||||
_longpath_analyze_and_act (ack_message_trace_id);
|
||||
}
|
||||
void QuoterWorkingStateWorker::on_book (size_t,
|
||||
const bts::orderman::Book&,
|
||||
|
||||
+1
-3
@@ -544,7 +544,6 @@ uint64_t tick_count() {
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::vector<std::string> g_cmd_args;
|
||||
bool g_cmd_args_set;
|
||||
|
||||
@@ -559,14 +558,13 @@ extern "C" BTS_EXPORT void provide_command_line(const char **argv, int argc) {
|
||||
g_cmd_args_set = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<std::string> get_command_line() {
|
||||
#ifdef _WIN32
|
||||
if (g_cmd_args_set) {
|
||||
// return cached copy
|
||||
return g_cmd_args;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
for (int index = 0; index < __argc; index++) {
|
||||
if (__argv != nullptr) {
|
||||
g_cmd_args.push_back(__argv[index]);
|
||||
|
||||
@@ -32,6 +32,8 @@ void init_plugins(SparkServices &services) {
|
||||
auto &f = *iter;
|
||||
|
||||
auto pl = f.path().string();
|
||||
if(pl.find("tensorflow.so") != std::string::npos)
|
||||
continue;
|
||||
LOG(Plugin, Info, "Attempting to initialize plugin {}", pl);
|
||||
try {
|
||||
auto init_plugin = boost::dll::import<void(SparkServices &)>(
|
||||
|
||||
@@ -65,6 +65,7 @@ HTTPStream::HTTPStream(const std::string& host, const std::string& path, const s
|
||||
HTTPStream::~HTTPStream() { }
|
||||
|
||||
|
||||
// returns true if file was downloaded
|
||||
bool download_file_if_newer(const string& host, const string& path, const string& destination) {
|
||||
struct stat st = boost::initialized_value;
|
||||
stat(destination.c_str(), &st);
|
||||
|
||||
@@ -12,6 +12,10 @@ add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/sdk
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/build_examples.sh
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/md_publisher_example.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/order_plugin_example.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/ai_order_plugin_example.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/tf_test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/tfapi.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/tfapi.h
|
||||
server_layout)
|
||||
|
||||
add_custom_target(sdk_dir ALL DEPENDS ${CMAKE_BINARY_DIR}/sdk)
|
||||
|
||||
+57
-18
@@ -1,8 +1,23 @@
|
||||
# BTS Spark SDK
|
||||
# BTS Spark
|
||||
|
||||
#### Getting Started
|
||||
Our offering consists of a client UI ([Spark Client](https://www.bluetradingsystems.com/bts-spark#ladder-algo-screen)), 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).
|
||||
|
||||
## Docker
|
||||

|
||||
|
||||
|
||||
# BTS Spark Server SDK
|
||||
|
||||
This API extension point is intended for users that require adding functionality to our Spark Server. Using the **Spark Server SDK** users can create [new order types](https://github.com/BlueTradingSystems/spark_sdk/blob/master/examples/order_plugin_example.cpp), [new market data sources](https://github.com/BlueTradingSystems/spark_sdk/blob/master/examples/md_publisher_example.cpp) and soon new execution gateways. In this case users implement the new functionality in C++, producing a shared library that is loaded at run time by our Spark Server.
|
||||
|
||||
If you are interested in sending orders or otherwise controlling a spark server remotely please see the BTS [Spark Client SDK](https://github.com/BlueTradingSystems/spark_client_sdk) repo.
|
||||
|
||||
### In Depth
|
||||
|
||||
* [Spark Algo Descriptions](doc/algos.md)
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Docker
|
||||
|
||||
We use docker to provide a full dev environment with all the tools users need to develop their own algos.
|
||||
|
||||
@@ -35,7 +50,7 @@ $ docker start --attach -i bts_sdk
|
||||
|
||||
* If you prefer to run docker as a daemon, check out [``~/spark_sdk/sdk_shell.sh``](sdk_shell.sh)
|
||||
|
||||
## Git
|
||||
### Git
|
||||
|
||||
* We assume the git you are using is git-lfs compatible; if you're using the git that comes with our docker, you're all set.
|
||||
|
||||
@@ -50,7 +65,7 @@ $ cd spark_sdk
|
||||
```
|
||||
|
||||
|
||||
## Development
|
||||
### Development
|
||||
|
||||
Look at [examples/md_publisher_example.cpp](examples/md_publisher_example.cpp) to look at how to build a custom market data producer
|
||||
|
||||
@@ -62,10 +77,27 @@ run ```build_examples.sh``` to make sure you can build the examples, this will p
|
||||
$ cd ~/spark_sdk/examples
|
||||
$ ./build_examples.sh
|
||||
```
|
||||
#### Server Layout
|
||||
* Spark servers reside on a port, usually 8000, but your production instance could be on a different $PORT
|
||||
|
||||
##### SDK Package Layout
|
||||
* The sdk puts logs in ``~/sparkdata/data/logging/log``
|
||||
* The sdk package has binaries in ``spark_skd/lib``
|
||||
* Put plugins in ``spark_sdk/lib/plugins/`` for debugging
|
||||
|
||||
##### Production layout
|
||||
* Production Logs are in ``~/$PORT/sparkdata/data/logging/log`` on the host
|
||||
* Production binaries are in ``/sparkbin`` inside the docker container
|
||||
* Production algo plugins should go into the ``/sparkbin/plugins`` directory
|
||||
|
||||
#### Recorded market data
|
||||
* We first check for http://mdreplay/spark/futures.coconut
|
||||
|
||||
* Then fall back to ``./data_files/ relative to the sparkd binary``
|
||||
|
||||
## Debugging
|
||||
|
||||
Copy the order_plugin_example.so to ```spark_sdk/lib/plugins/``` and start the sparkd server process inside gdb
|
||||
Copy the order_plugin_example.so to ``spark_sdk/lib/plugins/`` and start the sparkd server process inside gdb
|
||||
```
|
||||
$ cp -f order_plugin_example.so ~/spark_sdk/lib/plugins/
|
||||
$ cd ~/spark_sdk/lib
|
||||
@@ -87,24 +119,31 @@ $ cp -f order_plugin_example.so ~/spark_sdk/lib/plugins/
|
||||
$ cd ~/spark_sdk/lib
|
||||
$ ./sparkd run
|
||||
```
|
||||
|
||||
## Connect to Admin (Risk) UI to set limits or add users
|
||||
* Browse to http://location:$PORT
|
||||
* Login as bts, no password
|
||||
* Add or manage user trading and market data permissions by clicking the Users icon on the left side
|
||||
* Add or manage accounts and risk limits by clicking on the Accounts icon on the left side
|
||||
|
||||
## Other interesting web accessible things
|
||||
|
||||
* When you have a running server you can:
|
||||
* Browse to http://location/$PORT/config to check configuration overrides
|
||||
* Browse to http://location:$PORT/perf_trace to see socket to socket latencies
|
||||
* Send us an email for help: api_support@bluetradesys.com
|
||||
|
||||
|
||||
## Connect Spark windows client
|
||||
|
||||
* To start a spark client, use the -ConfigServer parameter to point at this new server
|
||||
```
|
||||
> spark.exe -ConfigServer http://location/8000/config
|
||||
> spark.exe -ConfigServer http://location:$PORT/config
|
||||
```
|
||||
Or create a file called spark.conf at %APPDATA%\bts\conf\ that contains:
|
||||
```
|
||||
include: "http://location:8000/config/"
|
||||
include: "http://location:$PORT/config/"
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
* When you have a running server you can:
|
||||
* Browse to http://location/8000 to set risk limits and create users
|
||||
* Browse to http://location/8000/config to check configuration overrides
|
||||
* Browse to http://location/8000/perf_trace to see socket to socket latencies
|
||||
* Send us an email for help: api_support@bluetradesys.com
|
||||
|
||||
|
||||
|
||||
## Download the Spark windows client
|
||||
* Download the bts installer here:
|
||||
Loaded 100 of 114 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user