Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd5c680155 | ||
|
|
5a1cc4cf2b | ||
|
|
9be3a09749 | ||
|
|
61745d0ed7 | ||
|
|
db1610591e | ||
|
|
bb1571540a | ||
|
|
342d89724e | ||
|
|
07d3903d11 | ||
|
|
d08b134d7f | ||
|
|
92f67d1d2d | ||
|
|
43a18941a4 | ||
|
|
b102b271b0 | ||
|
|
b129bea129 | ||
|
|
38d79eb58b | ||
|
|
dfd0cfc217 | ||
|
|
a123ac2ed8 | ||
|
|
ad4fd13189 | ||
|
|
8a1180835c | ||
|
|
f65f34c2fe | ||
|
|
34401960f9 | ||
|
|
34b7c99c81 | ||
|
|
044ef0b812 | ||
|
|
9cc4b5aa87 | ||
|
|
821274f544 | ||
|
|
ca6c0f984c | ||
|
|
80e337c599 | ||
|
|
5b71918a1f | ||
|
|
d2a9d21700 | ||
|
|
16fee6e478 | ||
|
|
3cf701a211 | ||
|
|
5392d05ec8 | ||
|
|
7838bdf04f | ||
|
|
546553406b | ||
|
|
02d0f6191c | ||
|
|
36722e27ad | ||
|
|
dcfbb6e62f | ||
|
|
ff2d150dc4 | ||
|
|
6692797c3f | ||
|
|
b059aedbce | ||
|
|
73c9be3bec | ||
|
|
bc35e773ce | ||
|
|
4c8d0a4217 | ||
|
|
05abbc4a50 | ||
|
|
95500187f0 | ||
|
|
8716099ac6 | ||
|
|
d7850d6b70 | ||
|
|
905c1cab2c | ||
|
|
6181548902 | ||
|
|
c32eada3ac | ||
|
|
42feedfd36 | ||
|
|
d76c2a30ce | ||
|
|
c312a880a7 | ||
|
|
b2ed88fb4a | ||
|
|
567edafe18 | ||
|
|
9a39d84739 | ||
|
|
d9b4e21c61 | ||
|
|
fa287b14ef | ||
|
|
096bf3338c | ||
|
|
a646f28831 | ||
|
|
391397692c | ||
|
|
8130d5a0f8 | ||
|
|
d89f93c323 | ||
|
|
a376fe9e22 | ||
|
|
7381088d6d | ||
|
|
5f9a421490 | ||
|
|
f7fd81f3af | ||
|
|
f6c0f393bd | ||
|
|
adec1bdfd0 | ||
|
|
a3de83dd04 | ||
|
|
5ba7cd561c | ||
|
|
5ff9f13e7e | ||
|
|
f4b65b21bb | ||
|
|
c1789bb4a1 | ||
|
|
0cf4b5c2c8 | ||
|
|
a101a267ed | ||
|
|
d76b098c51 | ||
|
|
06aa540110 | ||
|
|
9fd7db55ca | ||
|
|
5b118d8fa0 | ||
|
|
b570762054 | ||
|
|
b664c42cae | ||
|
|
a25c17306f | ||
|
|
b0c52f542a | ||
|
|
dabeb63eac | ||
|
|
f0d3cabd14 | ||
|
|
418a1078c2 | ||
|
|
3d37dc481f | ||
|
|
d24b7a3707 | ||
|
|
12362d9719 | ||
|
|
01128639ce | ||
|
|
71d2e01187 | ||
|
|
c923be6ced | ||
|
|
4cf9d544aa | ||
|
|
79f37f7130 | ||
|
|
19c0b94803 | ||
|
|
57fc50ce61 | ||
|
|
3cff5343a3 | ||
|
|
a6ec93e87e | ||
|
|
25acf771ab | ||
|
|
22cc946f52 | ||
|
|
6d0eb62712 | ||
|
|
369b89b3a7 | ||
|
|
8793c6c527 | ||
|
|
df14522433 | ||
|
|
086e90acbe | ||
|
|
16acc4f5b0 | ||
|
|
b8a94f2540 | ||
|
|
3af7e561b9 | ||
|
|
467dba98f8 | ||
|
|
f871dfe142 | ||
|
|
d44de204ee | ||
|
|
04beea61eb | ||
|
|
2181b92970 | ||
|
|
693c730f51 | ||
|
|
0ccba5bb0b | ||
|
|
3458be59d7 | ||
|
|
7dc9aa0710 | ||
|
|
88d6dd0f00 | ||
|
|
ce4c46da04 | ||
|
|
59c1f6a511 | ||
|
|
938caf9a4f | ||
|
|
df4f061b6c | ||
|
|
8afee21df7 | ||
|
|
f34014eb25 | ||
|
|
f513f7d9f2 | ||
|
|
b07eec08b4 | ||
|
|
964274f5c4 | ||
|
|
c16941977c | ||
|
|
6b19119ab2 | ||
|
|
ea42086e96 | ||
|
|
292873b189 | ||
|
|
8545a5ef38 | ||
|
|
6a52fff3d5 | ||
|
|
4ec6fafbc2 | ||
|
|
b08e2c2ee6 | ||
|
|
8bdd033501 | ||
|
|
76466e83f0 | ||
|
|
69f9f2791e | ||
|
|
ad6fc6d23b | ||
|
|
a4575e3fa3 | ||
|
|
2540dc0510 | ||
|
|
dc1a80afd2 | ||
|
|
e1cfa7bf0b | ||
|
|
2020221e65 | ||
|
|
ce438e93a6 | ||
|
|
f0132c84b6 | ||
|
|
57ca9596c8 | ||
|
|
bad5bc3097 | ||
|
|
e9da338402 | ||
|
|
bf4953f6dc | ||
|
|
c277fcd503 | ||
|
|
dff57a43dd | ||
|
|
39c71d0c16 | ||
|
|
be6c916691 | ||
|
|
009e72953c | ||
|
|
635163c895 | ||
|
|
95e96a6afc | ||
|
|
c7f0e2dd58 | ||
|
|
f5e79a65a3 | ||
|
|
c958f4bf1a | ||
|
|
95939a5534 | ||
|
|
9a7164b42b | ||
|
|
f347fda47f | ||
|
|
d75ab3a63f | ||
|
|
b30f59227e | ||
|
|
ef60d4200a | ||
|
|
ed6f0b8bdb | ||
|
|
766116eac4 | ||
|
|
8d49de3f61 | ||
|
|
16cf4554c5 | ||
|
|
6b17efee60 | ||
|
|
5d64213455 | ||
|
|
0ef44e92aa | ||
|
|
4d60376cb1 | ||
|
|
ee2f76553b | ||
|
|
c1887b9786 | ||
|
|
1fd5e51b9e | ||
|
|
56d3a27e7f | ||
|
|
1c65b1d87a | ||
|
|
693910f093 | ||
|
|
498b38ebcb | ||
|
|
fb91206e0f | ||
|
|
91f4de00f8 | ||
|
|
28980f10ae | ||
|
|
04e006b812 | ||
|
|
02ed327ec7 | ||
|
|
e9162263d6 | ||
|
|
3b3cc9a51c | ||
|
|
5b959ca5bd | ||
|
|
54f03c4a03 | ||
|
|
c4d297969b | ||
|
|
1f86456437 | ||
|
|
e24e04ca4f | ||
|
|
08a1e07ab6 | ||
|
|
038e6d16af | ||
|
|
fe2e1e9945 | ||
|
|
20d5276874 | ||
|
|
b2f3d093d2 | ||
|
|
4860f706ad | ||
|
|
29ddd6621c | ||
|
|
e8e0f01fb7 | ||
|
|
41a04de31b | ||
|
|
b3b1af68d0 | ||
|
|
7cbf729607 | ||
|
|
6e8851d92c | ||
|
|
460e747f55 | ||
|
|
20978b10d4 | ||
|
|
42e0cfba70 | ||
|
|
728c485013 | ||
|
|
b83f88e098 | ||
|
|
32b1a38655 | ||
|
|
24934f6607 | ||
|
|
90b56277ca | ||
|
|
20ca8a7ffe | ||
|
|
6ed4adbc14 | ||
|
|
9117e3e238 | ||
|
|
c0f079b7f6 | ||
|
|
751f22645d | ||
|
|
47daf439d0 | ||
|
|
19ce3a7dd4 | ||
|
|
5686b57d47 | ||
|
|
cb456a439c | ||
|
|
7ac54242e0 | ||
|
|
1ebf8219c2 | ||
|
|
f0b14cb8dc | ||
|
|
7259f81ab5 | ||
|
|
3b902b61dd | ||
|
|
6cfef51e3d | ||
|
|
76edb94905 | ||
|
|
e4bf9fc747 | ||
|
|
d488ff23ba | ||
|
|
c1d4b9cd9f | ||
|
|
dce856e4af | ||
|
|
6b6fd9c40a | ||
|
|
f7265740e7 | ||
|
|
f006d4ffdb | ||
|
|
31dd7849b9 | ||
|
|
bcf9b97420 | ||
|
|
697ffd71eb | ||
|
|
b56fc2445c | ||
|
|
a4d162c3a2 | ||
|
|
527bc05b6f | ||
|
|
a52d65859b | ||
|
|
363b9e3e7a | ||
|
|
3a06684146 | ||
|
|
8643feabf2 | ||
|
|
e7f1c59210 | ||
|
|
c9f403973b | ||
|
|
d6bc6fa289 | ||
|
|
7557ca38cb | ||
|
|
ef59511a01 | ||
|
|
8dfbb106a6 | ||
|
|
3e9a931c5f | ||
|
|
71fac664e8 | ||
|
|
1b39ffc323 | ||
|
|
bc69c669ae | ||
|
|
170f7426aa | ||
|
|
16d30e450f | ||
|
|
a3787ee65a | ||
|
|
14d5d36067 | ||
|
|
1af4b97b97 | ||
|
|
faf60af9c3 | ||
|
|
109fee099f | ||
|
|
747b44c48d | ||
|
|
4b091a1259 | ||
|
|
9626bee38e | ||
|
|
776bc71c22 | ||
|
|
7c0e310f6a | ||
|
|
761657b56b | ||
|
|
2988967f1f | ||
|
|
724da31316 | ||
|
|
2ae5b50445 | ||
|
|
dfcf38289f | ||
|
|
09436473ed | ||
|
|
5524e89a10 | ||
|
|
d615dcc7d6 | ||
|
|
d582ed8436 | ||
|
|
c5768c4022 | ||
|
|
1c54b0140b | ||
|
|
6c652df3eb | ||
|
|
c4e684f934 | ||
|
|
51ac39c166 | ||
|
|
f04a48d4f8 | ||
|
|
f7baf6c05a | ||
|
|
1eaf3d8629 | ||
|
|
62ecd1a721 | ||
|
|
e034fd2623 | ||
|
|
a3b8d579a1 | ||
|
|
cabc84a577 | ||
|
|
9d2cdb702d | ||
|
|
394c7fd954 | ||
|
|
b2d1969e9f | ||
|
|
744797b91c | ||
|
|
6446e38aea | ||
|
|
32df88914b | ||
|
|
628490ac2d | ||
|
|
ef475f7003 | ||
|
|
d0784feddd | ||
|
|
76a1367510 | ||
|
|
fef5ae4aff | ||
|
|
2c9e83c6f0 | ||
|
|
35738c25f1 | ||
|
|
e64567f8ad | ||
|
|
5865ab21bc | ||
|
|
78b6169750 | ||
|
|
e2c0d81f19 | ||
|
|
b811b960ec | ||
|
|
bf0507e434 | ||
|
|
1205a35b69 | ||
|
|
2f4d2a9c0d | ||
|
|
08e5088d72 | ||
|
|
e7b371aa6f | ||
|
|
3094eee327 | ||
|
|
80ff61c9b2 | ||
|
|
2a42c1fab0 | ||
|
|
4a64e45d36 | ||
|
|
97f09768bb | ||
|
|
2b4add85a2 | ||
|
|
4c798f6db4 | ||
|
|
b2e275124f | ||
|
|
36e4f62a53 |
No files matched your search
@@ -75,6 +75,7 @@
|
||||
"name": "SparkVM Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
|
||||
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
|
||||
"args": ["run"],
|
||||
"stopAtEntry": false,
|
||||
@@ -96,7 +97,12 @@
|
||||
"description": "Skip STL",
|
||||
"text": "skip -gfi /usr/include/c++/7/bits/*.h",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "LD LIBRARY",
|
||||
"text": "set env LD_LIBRARY_PATH=.",
|
||||
"ignoreFailures": false
|
||||
}
|
||||
],
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
@@ -208,10 +214,10 @@
|
||||
"name": "MD Tests",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"args": ["test", "[md]"],
|
||||
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
|
||||
"args": ["test", "[mbo]"],
|
||||
"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",
|
||||
|
||||
@@ -55,6 +55,8 @@ else ()
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/web ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/data_files ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/lib/libspark.so ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND ln ARGS -sf ${EXTERNALS_ROOT}/lib/Release/libOnixS.IceImpactMulticastPriceFeedHandlerCpp.so.3.13.0.0 ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND ln ARGS -sf ${EXTERNALS_ROOT}/lib/Release/libACE-5.7.5.so ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND mkdir ARGS -p ${CMAKE_BINARY_DIR}/server/plugins
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/sdk/examples/order_plugin_example.so ${CMAKE_BINARY_DIR}/server/plugins/
|
||||
COMMENT "Creating server runtime layout"
|
||||
|
||||
@@ -1 +1 @@
|
||||
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;/home/ppinto/Dropbox/bts/spark/doc/user/mkdocs.yml;bts-help-theme/404.html;bts-help-theme/base.html;bts-help-theme/content.html;bts-help-theme/css/base.css;bts-help-theme/css/bts.css;bts-help-theme/css/font-awesome.min.css;bts-help-theme/css/highlight.css;bts-help-theme/css/local.css;bts-help-theme/fonts/fontawesome-webfont.eot;bts-help-theme/fonts/glyphicons-halflings-regular.eot;bts-help-theme/img/favicon.ico;bts-help-theme/js/base.js;bts-help-theme/js/bootstrap-3.0.3.min.js;bts-help-theme/js/highlight.pack.js;bts-help-theme/js/jquery-1.10.2.min.js;bts-help-theme/keyboard-modal.html;bts-help-theme/main.html;bts-help-theme/nav-sub.html;bts-help-theme/nav.html;bts-help-theme/search-modal.html;bts-help-theme/toc.html;docs/Help.md;docs/Launcher.md;docs/Logging-In-to-the-System.md;docs/Market.md;docs/My-Windows.md;docs/Settings.md;docs/Spreader.md;docs/Trading.md;docs/images/image1.png;docs/images/image10.png;docs/images/image11.png;docs/images/image12.png;docs/images/image13.png;docs/images/image14.png;docs/images/image15.png;docs/images/image16.png;docs/images/image17.png;docs/images/image18.png;docs/images/image19.png;docs/images/image2.png;docs/images/image20.png;docs/images/image21.png;docs/images/image22.png;docs/images/image23.png;docs/images/image24.png;docs/images/image25.png;docs/images/image26.png;docs/images/image27.png;docs/images/image28.png;docs/images/image29.png;docs/images/image3.png;docs/images/image30.png;docs/images/image31.png;docs/images/image32.png;docs/images/image33.png;docs/images/image34.png;docs/images/image35.png;docs/images/image36.png;docs/images/image37.png;docs/images/image38.png;docs/images/image39.png;docs/images/image4.png;docs/images/image40.png;docs/images/image41.png;docs/images/image42.png;docs/images/image43.png;docs/images/image44.png;docs/images/image45.png;docs/images/image46.png;docs/images/image47.png;docs/images/image48.png;docs/images/image49.png;docs/images/image5.png;docs/images/image50.png;docs/images/image51.png;docs/images/image52.png;docs/images/image53.png;docs/images/image54.png;docs/images/image55.png;docs/images/image56.png;docs/images/image57.png;docs/images/image58.png;docs/images/image59.png;docs/images/image6.png;docs/images/image60.png;docs/images/image61.png;docs/images/image62.png;docs/images/image63.png;docs/images/image64.png;docs/images/image65.png;docs/images/image66.png;docs/images/image67.png;docs/images/image68.png;docs/images/image69.png;docs/images/image7.png;docs/images/image70.png;docs/images/image71.png;docs/images/image72.png;docs/images/image73.png;docs/images/image74.png;docs/images/image76.png;docs/images/image8.png;docs/images/image9.png;docs/index.md;docs/release_notes.md)
|
||||
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Glossary/index.md;content/bts_spark/Help/about_spark.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Help/index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Launcher/index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Logging-In-to-the-System/index.md;content/bts_spark/Market/change_securities.png;content/bts_spark/Market/image11.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/index.md;content/bts_spark/Market/market_view.png;content/bts_spark/Market/settings_icon.png;content/bts_spark/Market/time_and_sales.png;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/My-Windows/index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Settings/index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Spreader/index.md;content/bts_spark/Trading/algo_studio.png;content/bts_spark/Trading/collapse_all_icon.png;content/bts_spark/Trading/cover_algo_control.png;content/bts_spark/Trading/expand_all_icon.png;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/index.md;content/bts_spark/Trading/join_algo_control.png;content/bts_spark/Trading/ladder.png;content/bts_spark/Trading/ladder_badges.png;content/bts_spark/Trading/ladder_main_part.png;content/bts_spark/Trading/lean_algo_control.png;content/bts_spark/Trading/mini_orderbook.png;content/bts_spark/Trading/oco_algo_control.png;content/bts_spark/Trading/offset_algo_control.png;content/bts_spark/Trading/pnl_calculation.png;content/bts_spark/Trading/pnl_window.png;content/bts_spark/Trading/recreate_icon.png;content/bts_spark/Trading/settings_icon.png;content/bts_spark/Trading/spark_menu_order_book.png;content/bts_spark/Trading/top_algo_control.png;content/bts_spark/Trading/trigger_algo_control.png;content/bts_spark/_index.md;content/bts_spark_sdk/Client/Overview.md;content/bts_spark_sdk/Client/Threading.md;content/bts_spark_sdk/Client/_index.md;content/bts_spark_sdk/Server/_index.md;content/bts_spark_sdk/_index.md;content/release_notes/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
|
||||
@@ -22,7 +22,7 @@ update_deps_file("${All_Deps}")
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/web/docs/index.html
|
||||
COMMAND /bin/bash ${CMAKE_CURRENT_SOURCE_DIR}/build.sh
|
||||
ARGS ${CMAKE_BINARY_DIR}/web/docs/
|
||||
ARGS ${CMAKE_BINARY_DIR}/web/docs/ ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Building Manual UI"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${All_Deps})
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
#!/bin/bash
|
||||
tmpfile=$(mktemp /tmp/quiet_build_log.XXXX)
|
||||
cp -r ../../src/sdk/doc/* ./content/bts_spark_sdk/Server/
|
||||
./run_zola.sh build -u '/docs/' -o $1 >$tmpfile 2>&1
|
||||
rm ./content/bts_spark_sdk/Server/algos.md
|
||||
|
||||
cur_dir=$(pwd)
|
||||
temp_path="$2/web_temp"
|
||||
zola_path=$(readlink -f "../../externals/linux/bin/zola/zola")
|
||||
|
||||
mkdir -p $temp_path
|
||||
cp -r ./* $temp_path
|
||||
cp -r ../../src/sdk/doc/* $temp_path/content/bts_spark_sdk/Server/
|
||||
|
||||
cd $temp_path
|
||||
$zola_path build -u '/docs/' -o $1 >$tmpfile 2>&1
|
||||
|
||||
cd $cur_dir
|
||||
rm -r $temp_path
|
||||
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
rm $tmpfile
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# The URL the site will be built for
|
||||
base_url = "https://www.bluetradingsystems.com/docs"
|
||||
base_url = "https://www.bluetradingsystems.com/documentation"
|
||||
|
||||
title = "BTS Spark Documentation"
|
||||
|
||||
@@ -15,4 +15,4 @@ build_search_index = true
|
||||
|
||||
[extra]
|
||||
# Put all your custom variables here
|
||||
book_numbered_chapters = false
|
||||
book_numbered_chapters = false
|
||||
@@ -0,0 +1,46 @@
|
||||
+++
|
||||
title = "BTS Basic Algos"
|
||||
weight = 10
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
|
||||
|
||||
## Lean
|
||||
|
||||
The goal of the lean order is to watch the opposite-inside market size and cross when that size goes below the threshold (this also implies a cross if that size goes to zero and the market trades away from you) If the market trades toward you, you can opt into several different behaviors
|
||||
|
||||
##### Parameters
|
||||
* Cross Thresh - size thresh in absolute or ratio terms
|
||||
* Max Chase Ticks - max levels to pay up, in ticks
|
||||
|
||||
|
||||
## Join
|
||||
The goal of the join order is to watch the opposite-inside market and join when the market flips (that is, when the best offer becomes the best bid) and has at least threshold size.
|
||||
|
||||
##### Parameters
|
||||
* Join Thresh - size thresh in absolute or ratio terms
|
||||
|
||||
|
||||
## Top
|
||||
|
||||
The goal of the top order is to not trade by yourself, but the intent at the time of order entry is to get filled.If the order is on the market (say current bid price) AND the quantity on that bid price is below top thresh then cancel the order else remain working.
|
||||
|
||||
##### Parameters
|
||||
* Top Thresh - size thresh in absolute or ratio terms
|
||||
|
||||
|
||||
## Cover
|
||||
A Cover order’s purpose is to fire a hedge order (of any type) in response to fills from it’s child order on the opposite side of the market N ticks away
|
||||
|
||||
##### Parameters
|
||||
* Profit Ticks - ticks above or below fill price to place profit / cover order
|
||||
|
||||
|
||||
## OCO
|
||||
An OCO order creates a relationship between a child order and a 'stop' or 'other' order such that when one of these is cancelled, both cancel, or when one is filled, the other order's size is debited in the amount of the fill.
|
||||
|
||||
Composing this order with Cover simulates scalping functionality
|
||||
|
||||
##### Parameters
|
||||
* Stop Ticks - ticks below or above main order to place stop / other order
|
||||
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 29 KiB |
@@ -25,7 +25,7 @@ insert_anchor_links = "right"
|
||||
which will list what version you are running as well as who is
|
||||
currently logged in.
|
||||
|
||||

|
||||

|
||||
|
||||
## Activity log
|
||||
This is an overall log viewer which will show
|
||||
|
||||
|
After Width: | Height: | Size: 942 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 42 KiB |
@@ -51,7 +51,7 @@ insert_anchor_links = "right"
|
||||
securities you have chosen, in this example, it appears like the
|
||||
following:
|
||||
|
||||

|
||||

|
||||
|
||||
- Once a market view is launched and you are viewing data, you can
|
||||
customize the view further by the following:
|
||||
@@ -88,31 +88,34 @@ insert_anchor_links = "right"
|
||||
|
||||
- Change securities
|
||||
|
||||
- Click on the settings icon which will launch the market view security selection. You can add or remove Securities you have already selected and are shown in the market view.
|
||||
- Click on the settings icon  which will launch the market view security selection. You can add or remove Securities you have already selected and are shown in the market view.
|
||||
|
||||
- Launch an order ticket
|
||||
|
||||
- This is done 1 of 2 ways:
|
||||
|
||||
- This is done 1 of 3 ways:
|
||||
- Single left clicking on either the Bid Qty, Bid Price, Ask Price,
|
||||
or Ask Qty which will bring up an order ticket pre-populated with
|
||||
either the Bid or Ask price and volume.
|
||||
- Highlighting a single security and clicking on the order ticket
|
||||
 icon
|
||||
on the left-hand browser window of the market view
|
||||
- Highlighting a single security and right clicking bringing up the
|
||||
menu options to launch an order ticket.
|
||||
|
||||
- Launch a ladder for 1 security
|
||||
- You can double-click on any security in the market view, besides
|
||||
the Bid Qty, Bid Price, Ask Price, or Ask Qty columns to launch a
|
||||
ladder for that Security.
|
||||
- This is done 1 of 2 ways:
|
||||
- You can double-click on any security in the market view, besides
|
||||
the Bid Qty, Bid Price, Ask Price, or Ask Qty columns to launch a
|
||||
ladder for that Security.
|
||||
- Highlighting a single security and right clicking bringing up the
|
||||
menu options to launch a ladder.
|
||||
|
||||
- Launching multiple ladders
|
||||
|
||||
- If you hold down the Ctrl key and select multiple securities, you
|
||||
can then click on the ladder icon
|
||||
 on
|
||||
the left-hand side to bring up the selected ladders all at
|
||||
once.
|
||||
- This is done 1 of 2 ways:
|
||||
- If you hold down the Ctrl key and select multiple securities, you
|
||||
can then click on the ladder icon on the left hand side to bring
|
||||
up the selected ladders all at once.
|
||||
- Highlighting multiple securities and right clicking to bring up
|
||||
the menu options to launch a ladder.
|
||||
|
||||
- Launching a Fills window for 1 or many securities
|
||||
|
||||
@@ -158,8 +161,9 @@ insert_anchor_links = "right"
|
||||
- On launching a security browser will appear, allowing you to choose
|
||||
the products you would like to view.
|
||||
|
||||

|
||||

|
||||
|
||||
- From time and sales, you can launch the same items as the market
|
||||
view with the exception of a launching the synthetic window to
|
||||
create a synthetic product
|
||||
create a synthetic product
|
||||
- From the settings icon  you can customize the time and sales modifying grid; the colors of the aggressor, status of trades coming in, as well as font sizes and which columns to see.
|
||||
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 99 KiB |
@@ -5,6 +5,7 @@ sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
|
||||
{{ youtube(id="RZU9Wj0cETo") }}
|
||||
|
||||
<div>
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/XEZpuPAgMC0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 70 KiB |
@@ -59,7 +59,7 @@ insert_anchor_links = "right"
|
||||
functionality to quickly perform various tasks with live orders as
|
||||
well as launch other modules. It appears like the below:
|
||||
|
||||
- 
|
||||

|
||||
|
||||
- Fills -
|
||||
clicking on this will launch a fills window
|
||||
@@ -79,6 +79,18 @@ insert_anchor_links = "right"
|
||||
- Cancel
|
||||
all - clicking on this will cancel all working orders you have the
|
||||
order book
|
||||
-  Recreate - clicking this will recreate already cancelled orders, sending them back to market with the same price and quantity as they were previously at before being cancelled.
|
||||
-  Collapse all - This will collapse all trees inside the order book to show only the top level of the order.
|
||||
-  Expand all - This will expand to show everything inside the order book.
|
||||
-  Settings - This launches the settings for the orderbook.
|
||||
- Furthermore, there is a “floating” or “mini orderbook” feature as well
|
||||
integrated within the ladder itself.
|
||||
- To access this, you need to center click with your mouse wheel over
|
||||
working orders on the ladder.
|
||||
- From here, you can change the price and quantity of any working order
|
||||
within the mini orderbook straight from the window
|
||||
- This looks like below:
|
||||

|
||||
|
||||
## Fills
|
||||
|
||||
@@ -89,6 +101,8 @@ insert_anchor_links = "right"
|
||||
like
|
||||
- All fill windows are brought up as tabs and can be combined with
|
||||
all other windows within BTS
|
||||
- There is also a settings button in the left hand tab which can be
|
||||
launched to customize the fills window
|
||||
|
||||
## P&L
|
||||
|
||||
@@ -104,10 +118,20 @@ insert_anchor_links = "right"
|
||||
other windows within BTS
|
||||
- All columns are sortable and either searchable or can be further
|
||||
filtered by entering in min and max values.
|
||||
- Below is an example P&L window showing a user’s multiple accounts
|
||||
and various positions in each:
|
||||
- There is also a settings button in the left hand tab which can be
|
||||
launched to customize the P&L window
|
||||
- In regards to the PnL specifically, you can choose how you want the
|
||||
PnL calculated based on Last, MidPoint, or Settle price.
|
||||
- This is accomplished on the top bar, which when clicked on, displays
|
||||
the choices for calculation, this appears like below:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
- Below is an example P&L window showing a user’s multiple accounts and
|
||||
various positions in each:
|
||||
|
||||

|
||||
|
||||
|
||||
## Ladder
|
||||
@@ -125,7 +149,7 @@ insert_anchor_links = "right"
|
||||
market with a single click
|
||||
- Below is an example ladder:
|
||||
|
||||

|
||||

|
||||
|
||||
- The ladder consists of 2 major parts, the Ladder itself and the
|
||||
information and control section on the left-hand side
|
||||
@@ -213,37 +237,59 @@ insert_anchor_links = "right"
|
||||
|
||||

|
||||
|
||||
- Lean
|
||||
|
||||
- This is a specialized algo order which allows the user to
|
||||
- Lean
|
||||
- This is a specialized algo order which allows the user to
|
||||
essentially watch the opposite-inside market size and cross when
|
||||
that size goes below the threshold defined.
|
||||
- “Crossing” means to take an order working on the bid and 'cross' by
|
||||
buying into the asks. This is often referred to as “paying up” as
|
||||
well
|
||||
- “Cross” means to take an order working on the bid and 'cross' by
|
||||
buying into the asks. This is often referred to as “paying up” as well
|
||||
|
||||

|
||||

|
||||
|
||||
- Join
|
||||
|
||||
- This is a specialized algo order which allows the user to enter an
|
||||
- Join
|
||||
- This is a specialized algo order which allows the user to enter an
|
||||
order which will watch the opposite-inside market and join when the
|
||||
market flips and has at least threshold size as defined.
|
||||
- “Flip” refers to the event where the best offer becomes the best
|
||||
bid or the market trades away from you, i.e. the line moves up in
|
||||
the GUI. The reverse is true if selling.
|
||||
|
||||

|
||||

|
||||
|
||||
- Top
|
||||
|
||||
- This is a specialized algo order which allows the user to
|
||||
- Top
|
||||
- This is a specialized algo order which allows the user to
|
||||
essentially not trade by themselves
|
||||
- If the order is on the market (current bid price) and the quantity
|
||||
- If the order is on the market (current bid price) and the quantity
|
||||
on that bid price is below the top threshold defined the order will
|
||||
cancel and do nothing else
|
||||

|
||||
|
||||

|
||||
- Offset
|
||||
|
||||
- The goal of the offset order is to send your order X amount of ticks
|
||||
from where you actually place it on the ladder. This is sometimes
|
||||
known as a "fat finger" safety.
|
||||

|
||||
|
||||
- OCO
|
||||
|
||||
- An OCO order creates a relationship between a child order and a 'stop'
|
||||
or 'other' order such that when one of these is cancelled, both cancel,
|
||||
or when one is filled, the other order's size is debited in the amount
|
||||
of the fill. Composing this order with Cover simulates scalping
|
||||
functionality.
|
||||
|
||||

|
||||
|
||||
- Cover
|
||||
|
||||
- A Cover order’s purpose is to fire a hedge order (of any algo-type) in response to fills from it’s child order on the opposite side of the market N ticks away.
|
||||
|
||||

|
||||
|
||||
- Trigger
|
||||
|
||||
- The Trigger order is exactly as it sounds, where it will trigger an order based on conditions defined.
|
||||
- This order also has exit strategy built in where a user can define when the trigger order can auto-cancel based on time as well as price conditional.
|
||||
|
||||

|
||||
|
||||
- The middle of the control section is where you can click on
|
||||
predefined values to quickly populate the volume at which you wish
|
||||
@@ -264,20 +310,36 @@ insert_anchor_links = "right"
|
||||
- The bottom section of the control area of a ladder has 4 aspects
|
||||
to it
|
||||
|
||||
- Cancel Buys, which will cancel all buys currently working on the
|
||||
ladder
|
||||
- Cancel Sells, which will cancel all sells currently working on the
|
||||
ladder
|
||||
- Cancel All, which will cancel all orders on the ladder
|
||||
- Settings, which will launch the ladder settings window which
|
||||
appears like below
|
||||
- Cancel Buys, which will cancel all buys currently working on the
|
||||
ladder
|
||||
- Cancel Sells, which will cancel all sells currently working on the
|
||||
ladder
|
||||
- Cancel All, which will cancel all orders on the ladder
|
||||
- Settings, which will launch the ladder settings window which
|
||||
appears like below
|
||||

|
||||
- Fonts and Colors - This is where you can customize the overall main
|
||||
aspects of the ladder.
|
||||
- Market Break Lines - These are where can control the viewing of what
|
||||
has been traded through various shading.
|
||||
- Market Size Display - This is where you can define if you wish
|
||||
to see implieds or not and how exactly you would like to see them.
|
||||
- Volume Display - Where you can choose to show the volume traded at price
|
||||
graph and what details you would like to see.
|
||||
- Size Presets - Here you can define what volume of predefined quick buttons
|
||||
you would like to see or any at all. You can remove these by clicking on the “X” next to the volume number to remove one or many.
|
||||
- Algo Favorites - Here you can set up which “quick button” or user defined
|
||||
algos to see. You can customize the amount of columns to be
|
||||
displayed as well.
|
||||
- Left Panel - This is where you can remove predefined information on the
|
||||
left panel to save room and get rid of data that you might not require
|
||||
to see.
|
||||
|
||||

|
||||
|
||||
- The other, main part of the ladder, is the ladder itself as
|
||||
displayed below
|
||||
|
||||

|
||||

|
||||
|
||||
- There are 5 main columns
|
||||
|
||||
@@ -291,14 +353,10 @@ insert_anchor_links = "right"
|
||||
where the last trade occurred
|
||||
|
||||
- Functionality of the ladder
|
||||
- To enter an order directly by clicking in the ladder, left click on either the bid or ask volume with a volume greater than 0 on the predefined value box on the left-hand section
|
||||
- A badge will be displayed within the orders column of the value you have chosen and the quantity filled as displayed below
|
||||
|
||||
- To enter an order directly by clicking in the ladder, left click on
|
||||
either the bid or ask volume with a volume greater than 0 on the
|
||||
predefined value box on the left-hand section
|
||||
- A badge will be displayed within the orders column of the value you
|
||||
have chosen and the quantity filled as displayed below
|
||||
|
||||

|
||||

|
||||
|
||||
- To change the price level you wish to work the order, you can right-click and hold on the badge to move it up or down the ladder for the
|
||||
desired price
|
||||
@@ -344,3 +402,44 @@ insert_anchor_links = "right"
|
||||
- Please note you can also launch the required parameters for algo or
|
||||
synthetic orders by clicking on the right arrow next to the algo or
|
||||
synthetic
|
||||
|
||||
|
||||
## Algo Studio
|
||||
|
||||
{{ youtube(id="DHIa5tnifKc") }}
|
||||
|
||||
- The BTS Algo Studio was built for users to experience algo composability
|
||||
never seen before in a professional futures trading system.
|
||||
|
||||
- There is no limit for putting both “canned algos” already present in the
|
||||
system as well as user defined algos fed into the system via the API.
|
||||
|
||||
- This is all in place in a very user friendly GUI as shown below:
|
||||
|
||||

|
||||
|
||||
|
||||
- There are 3 main areas of the Algo Studio:
|
||||
- Left Pane (from top to bottom)
|
||||
- Add Algo
|
||||
- Clone Algo
|
||||
- Save Algo
|
||||
- Remove Algo
|
||||
- Middle Pane
|
||||
- This is where your customized algo will be displayed in a running list
|
||||
- Right Pane
|
||||
- This is where your complete customization happens of combining various
|
||||
algos together and saving them to fit your exact needs
|
||||
- On the very top are the name and abbreviation boxes
|
||||
- For the name you can simply name the algo whatever you would like
|
||||
- For the abbreviation section (which is optional), you can put in an
|
||||
abbreviated name for the algo which will be displayed on the Algo
|
||||
buttons on the ladder. (Example - Algo name = PROFIT_OCO_LEAN,
|
||||
Abbrev. = POL)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -1,6 +0,0 @@
|
||||
+++
|
||||
title = "Client"
|
||||
weight = 1
|
||||
sort_by = "weight"
|
||||
redirect_to = "bts_spark_sdk/Client/overview/"
|
||||
+++
|
||||
@@ -1,6 +0,0 @@
|
||||
+++
|
||||
title = "Server"
|
||||
weight = 2
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
@@ -1,6 +0,0 @@
|
||||
+++
|
||||
title = "BTS Spark SDK"
|
||||
weight = 2
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
@@ -4,6 +4,57 @@ weight = 3
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
## 1.6.0
|
||||
### User Visible Changes
|
||||
#### New Features
|
||||
- You can now view your order's position in queue in the ladder
|
||||
#### Bug Fixes
|
||||
- Fix a bug in the order popup in the ladder which could lead to a crash.
|
||||
|
||||
## 1.5.9
|
||||
### User Visible Changes
|
||||
- Fix for stunnel connections on Windows 7
|
||||
|
||||
## 1.5.8
|
||||
### User Visible Changes
|
||||
- Fixed a bug where it was possible to crash the UI by entering an order through the order ticket if Enter was used to dismiss the dialog.
|
||||
- Fixed a spreader reporting bug which could cause the server to crash if an algo was used to control certain types of synthetics
|
||||
- Improved user message when the UI fails to login
|
||||
- Integrated stunnel into UI to make VPN setup unnecessary in unsecured environments.
|
||||
## 1.5.7
|
||||
### User Visible Changes
|
||||
- New videos have been added to the manual describing the [Algo Studio](./bts_spark/Trading/index.md#algo-studio) and [Spreader](./bts_spark/Spreader/index.md)
|
||||
- Fixed a bug that occasionally would cause windows to be position outside the visible screen on startup
|
||||
- Fixed a bug in the order book form that would occasionally cause the Recreate Order option to be unavailable
|
||||
- Added a confirmation step to Cancel All, Cancel Buys and Cancel Sells commands
|
||||
- Fixed Aggressor coloring and ladder high/low coloring in the SIM execution gateway
|
||||
- Added initial support for ICE market data
|
||||
- In an effort to streamline the PnL report we've made the following changes:
|
||||
- The PnL report will by default filter for Futures contracts only
|
||||
- If you wish to see Spread contracts again you may change Security Type column filter
|
||||
- The PnL report will now only show rows in which there is a position (net and/or day)
|
||||
- The Exchange, Start PnL, Total Net Pos, and Total Net PnL columns are now hidden by default
|
||||
- These columns can be restored by toggling their visibility within the PnL's Settings form
|
||||
- A new Traded Today column has been added to the PnL
|
||||
- Filtering on this column will allow a user to restrict the report to only show today's position
|
||||
### Internal changes
|
||||
- Improved organization of the shell notification architecture
|
||||
- Added logging to help troubleshoot shell exit hang
|
||||
|
||||
## 1.5.6
|
||||
### User Visible Changes
|
||||
- Fixed a bug that could cause limits to be erroneously enforced
|
||||
- Improved algo documentation
|
||||
- Changed how the spreader uses net change
|
||||
- Simplified trigger algo user interface
|
||||
### Internal changes
|
||||
- Defaulted to not recording sim market data
|
||||
- Improved documentation build
|
||||
- Added additional performance tracing
|
||||
|
||||
## 1.5.5
|
||||
- Fixed Market View 'stale working orders' bug
|
||||
|
||||
## 1.5.4
|
||||
- Avoided logging when warming up cache
|
||||
|
||||
|
||||
@@ -54,3 +54,8 @@ img {
|
||||
background: #ccc;
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
|
||||
iframe {
|
||||
width:560px;
|
||||
height:315px;
|
||||
}
|
||||
@@ -1,223 +1,197 @@
|
||||
function initToggleMenu() {
|
||||
var $menu = document.querySelector(".menu");
|
||||
var $menuIcon = document.querySelector(".menu-icon");
|
||||
var $page = document.querySelector(".page");
|
||||
$menuIcon.addEventListener("click", function() {
|
||||
$menu.classList.toggle("menu-hidden");
|
||||
$page.classList.toggle("page-without-menu");
|
||||
});
|
||||
}
|
||||
|
||||
function debounce(func, wait) {
|
||||
var timeout;
|
||||
|
||||
return function () {
|
||||
var context = this;
|
||||
var args = arguments;
|
||||
clearTimeout(timeout);
|
||||
|
||||
timeout = setTimeout(function () {
|
||||
timeout = null;
|
||||
func.apply(context, args);
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
|
||||
// Taken from mdbook
|
||||
// The strategy is as follows:
|
||||
// First, assign a value to each word in the document:
|
||||
// Words that correspond to search terms (stemmer aware): 40
|
||||
// Normal words: 2
|
||||
// First word in a sentence: 8
|
||||
// Then use a sliding window with a constant number of words and count the
|
||||
// sum of the values of the words within the window. Then use the window that got the
|
||||
// maximum sum. If there are multiple maximas, then get the last one.
|
||||
// Enclose the terms in <b>.
|
||||
function makeTeaser(body, terms) {
|
||||
var TERM_WEIGHT = 40;
|
||||
var NORMAL_WORD_WEIGHT = 2;
|
||||
var FIRST_WORD_WEIGHT = 8;
|
||||
var TEASER_MAX_WORDS = 30;
|
||||
|
||||
var stemmedTerms = terms.map(function (w) {
|
||||
return elasticlunr.stemmer(w.toLowerCase());
|
||||
});
|
||||
var termFound = false;
|
||||
var index = 0;
|
||||
var weighted = []; // contains elements of ["word", weight, index_in_document]
|
||||
|
||||
// split in sentences, then words
|
||||
var sentences = body.toLowerCase().split(". ");
|
||||
|
||||
for (var i in sentences) {
|
||||
var words = sentences[i].split(" ");
|
||||
var value = FIRST_WORD_WEIGHT;
|
||||
|
||||
for (var j in words) {
|
||||
var word = words[j];
|
||||
|
||||
if (word.length > 0) {
|
||||
for (var k in stemmedTerms) {
|
||||
if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) {
|
||||
value = TERM_WEIGHT;
|
||||
termFound = true;
|
||||
}
|
||||
}
|
||||
weighted.push([word, value, index]);
|
||||
value = NORMAL_WORD_WEIGHT;
|
||||
}
|
||||
|
||||
index += word.length;
|
||||
index += 1; // ' ' or '.' if last word in sentence
|
||||
}
|
||||
|
||||
index += 1; // because we split at a two-char boundary '. '
|
||||
}
|
||||
|
||||
if (weighted.length === 0) {
|
||||
return body;
|
||||
}
|
||||
|
||||
var windowWeights = [];
|
||||
var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS);
|
||||
// We add a window with all the weights first
|
||||
var curSum = 0;
|
||||
for (var i = 0; i < windowSize; i++) {
|
||||
curSum += weighted[i][1];
|
||||
}
|
||||
windowWeights.push(curSum);
|
||||
|
||||
for (var i = 0; i < weighted.length - windowSize; i++) {
|
||||
curSum -= weighted[i][1];
|
||||
curSum += weighted[i + windowSize][1];
|
||||
windowWeights.push(curSum);
|
||||
}
|
||||
|
||||
// If we didn't find the term, just pick the first window
|
||||
var maxSumIndex = 0;
|
||||
if (termFound) {
|
||||
var maxFound = 0;
|
||||
// backwards
|
||||
for (var i = windowWeights.length - 1; i >= 0; i--) {
|
||||
if (windowWeights[i] > maxFound) {
|
||||
maxFound = windowWeights[i];
|
||||
maxSumIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var teaser = [];
|
||||
var startIndex = weighted[maxSumIndex][2];
|
||||
for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) {
|
||||
var word = weighted[i];
|
||||
if (startIndex < word[2]) {
|
||||
// missing text from index to start of `word`
|
||||
teaser.push(body.substring(startIndex, word[2]));
|
||||
startIndex = word[2];
|
||||
}
|
||||
|
||||
// add <em/> around search terms
|
||||
if (word[1] === TERM_WEIGHT) {
|
||||
teaser.push("<b>");
|
||||
}
|
||||
startIndex = word[2] + word[0].length;
|
||||
teaser.push(body.substring(word[2], startIndex));
|
||||
|
||||
if (word[1] === TERM_WEIGHT) {
|
||||
teaser.push("</b>");
|
||||
}
|
||||
}
|
||||
teaser.push("…");
|
||||
return teaser.join("");
|
||||
}
|
||||
|
||||
function formatSearchResultItem(item, terms) {
|
||||
var li = document.createElement("li");
|
||||
li.classList.add("search-results__item");
|
||||
li.innerHTML = `<a href="${item.ref}">${item.doc.title}</a>`;
|
||||
li.innerHTML += `<div class="search-results__teaser">${makeTeaser(item.doc.body, terms)}</div>`;
|
||||
return li;
|
||||
}
|
||||
|
||||
// Go from the book view to the search view
|
||||
function toggleSearchMode() {
|
||||
var $bookContent = document.querySelector(".book-content");
|
||||
var $searchContainer = document.querySelector(".search-container");
|
||||
if ($searchContainer.classList.contains("search-container--is-visible")) {
|
||||
$searchContainer.classList.remove("search-container--is-visible");
|
||||
document.body.classList.remove("search-mode");
|
||||
$bookContent.style.display = "block";
|
||||
} else {
|
||||
$searchContainer.classList.add("search-container--is-visible");
|
||||
document.body.classList.add("search-mode");
|
||||
$bookContent.style.display = "none";
|
||||
document.getElementById("search").focus();
|
||||
}
|
||||
}
|
||||
|
||||
function initSearch() {
|
||||
var $searchInput = document.getElementById("search");
|
||||
if (!$searchInput) {
|
||||
return;
|
||||
}
|
||||
var $searchIcon = document.querySelector(".search-icon");
|
||||
$searchIcon.addEventListener("click", toggleSearchMode);
|
||||
|
||||
var $searchResults = document.querySelector(".search-results");
|
||||
var $searchResultsHeader = document.querySelector(".search-results__header");
|
||||
var $searchResultsItems = document.querySelector(".search-results__items");
|
||||
var MAX_ITEMS = 10;
|
||||
|
||||
var options = {
|
||||
bool: "AND",
|
||||
fields: {
|
||||
title: {boost: 2},
|
||||
body: {boost: 1},
|
||||
}
|
||||
};
|
||||
var currentTerm = "";
|
||||
var index = elasticlunr.Index.load(window.searchIndex);
|
||||
|
||||
$searchInput.addEventListener("keyup", debounce(function() {
|
||||
var term = $searchInput.value.trim();
|
||||
if (term === currentTerm || !index) {
|
||||
return;
|
||||
}
|
||||
$searchResults.style.display = term === "" ? "none" : "block";
|
||||
$searchResultsItems.innerHTML = "";
|
||||
if (term === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
var results = index.search(term, options).filter(function (r) {
|
||||
return r.doc.body !== "";
|
||||
});
|
||||
if (results.length === 0) {
|
||||
$searchResultsHeader.innerText = `No search results for '${term}'.`;
|
||||
return;
|
||||
}
|
||||
|
||||
currentTerm = term;
|
||||
$searchResultsHeader.innerText = `${results.length} search results for '${term}':`;
|
||||
for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) {
|
||||
if (!results[i].doc.body) {
|
||||
continue;
|
||||
}
|
||||
// var item = document.createElement("li");
|
||||
// item.innerHTML = formatSearchResultItem(results[i], term.split(" "));
|
||||
console.log(results[i]);
|
||||
$searchResultsItems.appendChild(formatSearchResultItem(results[i], term.split(" ")));
|
||||
}
|
||||
}, 150));
|
||||
}
|
||||
|
||||
if (document.readyState === "complete" ||
|
||||
(document.readyState !== "loading" && !document.documentElement.doScroll)
|
||||
) {
|
||||
initToggleMenu();
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
initToggleMenu();
|
||||
initSearch();
|
||||
});
|
||||
}
|
||||
function initToggleMenu() {
|
||||
var $menu = document.querySelector(".menu");
|
||||
var $menuIcon = document.querySelector(".menu-icon");
|
||||
var $page = document.querySelector(".page");
|
||||
$menuIcon.addEventListener("click", function () {
|
||||
$menu.classList.toggle("menu-hidden");
|
||||
$page.classList.toggle("page-without-menu");
|
||||
});
|
||||
}
|
||||
function debounce(func, wait) {
|
||||
var timeout;
|
||||
return function () {
|
||||
var context = this;
|
||||
var args = arguments;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(function () {
|
||||
timeout = null;
|
||||
func.apply(context, args);
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
// Taken from mdbook
|
||||
// The strategy is as follows:
|
||||
// First, assign a value to each word in the document:
|
||||
// Words that correspond to search terms (stemmer aware): 40
|
||||
// Normal words: 2
|
||||
// First word in a sentence: 8
|
||||
// Then use a sliding window with a constant number of words and count the
|
||||
// sum of the values of the words within the window. Then use the window that got the
|
||||
// maximum sum. If there are multiple maximas, then get the last one.
|
||||
// Enclose the terms in <b>.
|
||||
function makeTeaser(body, terms) {
|
||||
var TERM_WEIGHT = 40;
|
||||
var NORMAL_WORD_WEIGHT = 2;
|
||||
var FIRST_WORD_WEIGHT = 8;
|
||||
var TEASER_MAX_WORDS = 30;
|
||||
var stemmedTerms = terms.map(function (w) {
|
||||
return elasticlunr.stemmer(w.toLowerCase());
|
||||
});
|
||||
var termFound = false;
|
||||
var index = 0;
|
||||
var weighted = []; // contains elements of ["word", weight, index_in_document]
|
||||
// split in sentences, then words
|
||||
var sentences = body.toLowerCase().split(". ");
|
||||
for (var i in sentences) {
|
||||
var words = sentences[i].split(" ");
|
||||
var value = FIRST_WORD_WEIGHT;
|
||||
for (var j in words) {
|
||||
var word = words[j];
|
||||
if (word.length > 0) {
|
||||
for (var k in stemmedTerms) {
|
||||
if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) {
|
||||
value = TERM_WEIGHT;
|
||||
termFound = true;
|
||||
}
|
||||
}
|
||||
weighted.push([word, value, index]);
|
||||
value = NORMAL_WORD_WEIGHT;
|
||||
}
|
||||
index += word.length;
|
||||
index += 1; // ' ' or '.' if last word in sentence
|
||||
}
|
||||
index += 1; // because we split at a two-char boundary '. '
|
||||
}
|
||||
if (weighted.length === 0) {
|
||||
return body;
|
||||
}
|
||||
var windowWeights = [];
|
||||
var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS);
|
||||
// We add a window with all the weights first
|
||||
var curSum = 0;
|
||||
for (var i = 0; i < windowSize; i++) {
|
||||
curSum += weighted[i][1];
|
||||
}
|
||||
windowWeights.push(curSum);
|
||||
for (var i = 0; i < weighted.length - windowSize; i++) {
|
||||
curSum -= weighted[i][1];
|
||||
curSum += weighted[i + windowSize][1];
|
||||
windowWeights.push(curSum);
|
||||
}
|
||||
// If we didn't find the term, just pick the first window
|
||||
var maxSumIndex = 0;
|
||||
if (termFound) {
|
||||
var maxFound = 0;
|
||||
// backwards
|
||||
for (var i = windowWeights.length - 1; i >= 0; i--) {
|
||||
if (windowWeights[i] > maxFound) {
|
||||
maxFound = windowWeights[i];
|
||||
maxSumIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
var teaser = [];
|
||||
var startIndex = weighted[maxSumIndex][2];
|
||||
for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) {
|
||||
var word = weighted[i];
|
||||
if (startIndex < word[2]) {
|
||||
// missing text from index to start of `word`
|
||||
teaser.push(body.substring(startIndex, word[2]));
|
||||
startIndex = word[2];
|
||||
}
|
||||
// add <em/> around search terms
|
||||
if (word[1] === TERM_WEIGHT) {
|
||||
teaser.push("<b>");
|
||||
}
|
||||
startIndex = word[2] + word[0].length;
|
||||
teaser.push(body.substring(word[2], startIndex));
|
||||
if (word[1] === TERM_WEIGHT) {
|
||||
teaser.push("</b>");
|
||||
}
|
||||
}
|
||||
teaser.push("…");
|
||||
return teaser.join("");
|
||||
}
|
||||
function formatSearchResultItem(item, terms) {
|
||||
var li = document.createElement("li");
|
||||
li.classList.add("search-results__item");
|
||||
li.innerHTML = "<a href=\"" + item.ref + "\">" + item.doc.title + "</a>";
|
||||
li.innerHTML += "<div class=\"search-results__teaser\">" + makeTeaser(item.doc.body, terms) + "</div>";
|
||||
return li;
|
||||
}
|
||||
// Go from the book view to the search view
|
||||
function toggleSearchMode() {
|
||||
var $bookContent = document.querySelector(".book-content");
|
||||
var $searchContainer = document.querySelector(".search-container");
|
||||
if ($searchContainer.classList.contains("search-container--is-visible")) {
|
||||
$searchContainer.classList.remove("search-container--is-visible");
|
||||
document.body.classList.remove("search-mode");
|
||||
$bookContent.style.display = "block";
|
||||
}
|
||||
else {
|
||||
$searchContainer.classList.add("search-container--is-visible");
|
||||
document.body.classList.add("search-mode");
|
||||
$bookContent.style.display = "none";
|
||||
document.getElementById("search").focus();
|
||||
}
|
||||
}
|
||||
function initSearch() {
|
||||
var $searchInput = document.getElementById("search");
|
||||
if (!$searchInput) {
|
||||
return;
|
||||
}
|
||||
var $searchIcon = document.querySelector(".search-icon");
|
||||
$searchIcon.addEventListener("click", toggleSearchMode);
|
||||
var $searchResults = document.querySelector(".search-results");
|
||||
var $searchResultsHeader = document.querySelector(".search-results__header");
|
||||
var $searchResultsItems = document.querySelector(".search-results__items");
|
||||
var MAX_ITEMS = 10;
|
||||
var options = {
|
||||
bool: "AND",
|
||||
fields: {
|
||||
title: { boost: 2 },
|
||||
body: { boost: 1 }
|
||||
}
|
||||
};
|
||||
var currentTerm = "";
|
||||
var index = elasticlunr.Index.load(window.searchIndex);
|
||||
$searchInput.addEventListener("keyup", debounce(function () {
|
||||
var term = $searchInput.value.trim();
|
||||
if (term === currentTerm || !index) {
|
||||
return;
|
||||
}
|
||||
$searchResults.style.display = term === "" ? "none" : "block";
|
||||
$searchResultsItems.innerHTML = "";
|
||||
if (term === "") {
|
||||
return;
|
||||
}
|
||||
var results = index.search(term, options).filter(function (r) {
|
||||
return r.doc.body !== "";
|
||||
});
|
||||
if (results.length === 0) {
|
||||
$searchResultsHeader.innerText = "No search results for '" + term + "'.";
|
||||
return;
|
||||
}
|
||||
currentTerm = term;
|
||||
$searchResultsHeader.innerText = results.length + " search results for '" + term + "':";
|
||||
for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) {
|
||||
if (!results[i].doc.body) {
|
||||
continue;
|
||||
}
|
||||
// var item = document.createElement("li");
|
||||
// item.innerHTML = formatSearchResultItem(results[i], term.split(" "));
|
||||
console.log(results[i]);
|
||||
$searchResultsItems.appendChild(formatSearchResultItem(results[i], term.split(" ")));
|
||||
}
|
||||
}, 150));
|
||||
}
|
||||
if (document.readyState === "complete" ||
|
||||
(document.readyState !== "loading" && !document.documentElement.doScroll)) {
|
||||
initToggleMenu();
|
||||
}
|
||||
else {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
initToggleMenu();
|
||||
initSearch();
|
||||
});
|
||||
}
|
||||
@@ -8,5 +8,10 @@ $(document).ready(function() {
|
||||
|
||||
$(".back-button")
|
||||
.html("Go Back")
|
||||
.click(_ => window.history.back());
|
||||
.click(function(_) { window.history.back()});
|
||||
|
||||
if(navigator.userAgent.indexOf("Trident") > -1) {
|
||||
console.log(navigator.userAgent);
|
||||
$(".search-icon").css("display", "none");
|
||||
}
|
||||
});
|
||||
@@ -1,3 +1,6 @@
|
||||
1.5.7
|
||||
- Fixed Aggressor coloring and ladder high/low coloring in SIM
|
||||
|
||||
1.4.2
|
||||
- Be sure the system has a bts user before deployment. bts is the superuser, he has all permissions and can see all market data
|
||||
- After deployment re-permission all existing users for trading, riskadmin, and/or md admin permissions as appropriate
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
@@ -66,18 +68,77 @@ namespace mbts.ui.shell
|
||||
|
||||
protected override void OnExit(ExitEventArgs e)
|
||||
{
|
||||
Log.Gen.info("Application.OnExit");
|
||||
if (_runtime != null) _runtime.Dispose();
|
||||
utils.BTSNativeMethods.finit_spark_lib();
|
||||
base.OnExit(e);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
void start_stunnel(string path, uint job_error)
|
||||
{
|
||||
string config_error;
|
||||
if (!mbts.config.Configuration.try_initialize(out config_error))
|
||||
{
|
||||
_stunnel_process = Process.Start(path + "bin\\stunnel.exe");
|
||||
if (_stunnel_process != null)
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
if (mbts.config.Configuration.try_initialize(out config_error))
|
||||
break;
|
||||
System.Threading.Thread.Sleep(500);
|
||||
}
|
||||
}
|
||||
else
|
||||
stunnel_job_error = "Stunnel Job Error code: " + job_error +
|
||||
" could not start stunnel " + (config_error ?? string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
void kill_stunnel()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process[] ps = Process.GetProcessesByName("stunnel");
|
||||
ps.ForEach(p =>
|
||||
{
|
||||
try { p.Kill(); }
|
||||
catch {}
|
||||
});
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
Process _stunnel_process;
|
||||
static internal string stunnel_job_error { get; private set;}
|
||||
void init_stunnel(string[] argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
var path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\BTS\\stunnel\\";
|
||||
if (File.Exists(path + "config\\stunnel.conf"))
|
||||
{
|
||||
uint job_error = BTSUINativeMethods.setup_process_job_object();
|
||||
//if we can't put this process and stunnel into a job, try to stop stunnel then restart
|
||||
if(job_error != 0)
|
||||
kill_stunnel();
|
||||
start_stunnel(path, job_error);
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
stunnel_job_error = "Error starting stunnel: " + ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
public new int Run() {
|
||||
var argv = Environment.GetCommandLineArgs();
|
||||
try {
|
||||
if (argv.Contains("-multiple") ||
|
||||
_mutex == null ||
|
||||
_mutex.WaitOne(TimeSpan.Zero, true)) {
|
||||
try {
|
||||
if (argv.Contains("-multiple") || _mutex == null || _mutex.WaitOne(TimeSpan.Zero, true))
|
||||
{
|
||||
if(_mutex == null || _mutex.WaitOne(TimeSpan.Zero, true))
|
||||
init_stunnel(argv);
|
||||
return base.Run();
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -6,6 +6,7 @@ using bts.api.persistence;
|
||||
using bts.api.position;
|
||||
using bts.api.synthetic;
|
||||
using bts.co2;
|
||||
using bts.md;
|
||||
using bts.metrics;
|
||||
using bts.support;
|
||||
using System.ComponentModel.Composition;
|
||||
@@ -50,6 +51,9 @@ namespace Spark {
|
||||
AlgoServiceWrapper _algo_service_wrapper;
|
||||
[Export]
|
||||
public AlgoServiceWrapper algo_service_wrapper => _algo_service_wrapper ?? (_algo_service_wrapper = new AlgoServiceWrapper(context));
|
||||
|
||||
[Export]
|
||||
public MBOService mbo_service => context.locate<MBOService>();
|
||||
}
|
||||
|
||||
class AlgoServiceWrapper {
|
||||
|
||||
@@ -137,6 +137,12 @@ namespace mbts.spark {
|
||||
public ICommand ask_implied_size_click { get; }
|
||||
public ICommand working_asks_click { get; }
|
||||
public ICommand default_command { get; }
|
||||
public string market_description {get;}
|
||||
public string cleared_alias { get; }
|
||||
public string product_description { get; }
|
||||
public string hub_alias { get; }
|
||||
public string strip_name{ get; }
|
||||
public string mic_code { get; }
|
||||
|
||||
public ObservableCollection<RowAnimation> row_animations { get; } = new ObservableCollection<RowAnimation>();
|
||||
|
||||
|
||||
@@ -57,6 +57,13 @@ namespace mbts.spark {
|
||||
add_col(Columns.SessionLow, "Low", r => r.format_price(r.session_low), nameof(Row.session_low), DefaultColumnWidths.Price, session_low_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.SessionHigh, "High", r => r.format_price(r.session_high), nameof(Row.session_high), DefaultColumnWidths.Price, session_high_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.SecurityKey, "Security Key", r => r.security_key.ToString(), nameof(Row.security_key), DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
|
||||
add_col(Columns.MarketDescription, "Market Description", r => r.md.security.HasIce ? r.md.security.Ice.MarketDescription : string.Empty, nameof(Row.market_description), DefaultColumnWidths.SecurityKey, market_description_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_col(Columns.ClearedAlias, "Cleared Alias", r => r.md.security.HasIce ? r.md.security.Ice.ClearedAlias : string.Empty, nameof(Row.cleared_alias), DefaultColumnWidths.SecurityKey, cleared_alias_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_col(Columns.ProductDescription, "Product Description", r => r.md.security.HasIce ? r.md.security.Ice.ProductName : string.Empty, nameof(Row.product_description), DefaultColumnWidths.SecurityKey, product_description_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_col(Columns.HubAlias, "Hub Alias", r => r.md.security.HasIce ? r.md.security.Ice.HubAlias : string.Empty, nameof(Row.hub_alias), DefaultColumnWidths.SecurityKey, hub_alias_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_col(Columns.StripName, "Strip Name", r => r.md.security.HasIce ? r.md.security.Ice.StripName : string.Empty, nameof(Row.strip_name), DefaultColumnWidths.SecurityKey, strip_name_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_col(Columns.MicCode, "Mic Code", r => r.md.security.HasIce ? r.md.security.Ice.MicCode : string.Empty, nameof(Row.mic_code), DefaultColumnWidths.SecurityKey, mic_code_filter, DataGridFilterKnownTemplates.Regex);
|
||||
//
|
||||
void add_col(Columns col_key, string header, Func<Row, string> get_text, string prop, double width, IDataGridColumnFilter filter, string filter_template, bool is_animatable = false, Color? font_color = null, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
|
||||
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
|
||||
@@ -194,6 +201,14 @@ namespace mbts.spark {
|
||||
public IDataGridColumnPersistentFilter<Row, IntMinMaxState> working_ask_volume_filter { get; } = new IntMinMax<Row>(r => r.working_ask_volume, nameof(Row.working_ask_volume));
|
||||
public IDataGridColumnPersistentFilter<Row, ULongMinMaxState> volume_filter { get; } = new ULongMinMax<Row>(r => r.volume, nameof(Row.volume));
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
|
||||
|
||||
public IDataGridColumnPersistentFilter<Row, string> market_description_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.MarketDescription : "", nameof(Row.market_description));
|
||||
public IDataGridColumnPersistentFilter<Row, string> cleared_alias_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.ClearedAlias : "", nameof(Row.cleared_alias));
|
||||
public IDataGridColumnPersistentFilter<Row, string> product_description_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.ProductName : "", nameof(Row.product_description));
|
||||
public IDataGridColumnPersistentFilter<Row, string> hub_alias_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.HubAlias : "", nameof(Row.hub_alias));
|
||||
public IDataGridColumnPersistentFilter<Row, string> strip_name_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.StripName : "", nameof(Row.strip_name));
|
||||
public IDataGridColumnPersistentFilter<Row, string> mic_code_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.MicCode : "", nameof(Row.mic_code));
|
||||
|
||||
|
||||
public FilterStates get_filter_states() {
|
||||
return new FilterStates {
|
||||
@@ -216,7 +231,13 @@ namespace mbts.spark {
|
||||
working_bid_volume = working_bid_volume_filter.get_state(),
|
||||
working_ask_volume = working_ask_volume_filter.get_state(),
|
||||
volume = volume_filter.get_state(),
|
||||
security_key = security_key_filter.get_state()
|
||||
security_key = security_key_filter.get_state(),
|
||||
market_description = market_description_filter.get_state(),
|
||||
cleared_alias = cleared_alias_filter.get_state(),
|
||||
product_description = product_description_filter.get_state(),
|
||||
hub_alias = hub_alias_filter.get_state(),
|
||||
strip_name = strip_name_filter.get_state(),
|
||||
mic_code = mic_code_filter.get_state(),
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -8,7 +8,9 @@ namespace mbts.ui.spark {
|
||||
/// Interaction logic for MarketSheetsView.xaml
|
||||
/// </summary>
|
||||
partial class MarketViewView : UserControl {
|
||||
public enum Columns { Security, Volume, Last, LastSize, Position, WorkingBids, Bid, BidMergedSize, BidDirectSize, BidImpliedSize, Ask, AskMergedSize, AskDirectSize, AskImpliedSize, WorkingAsks, Settlement, DayChange, SessionLow, SessionHigh, SecurityKey }
|
||||
public enum Columns { Security, Volume, Last, LastSize, Position, WorkingBids, Bid, BidMergedSize, BidDirectSize, BidImpliedSize, Ask, AskMergedSize, AskDirectSize, AskImpliedSize, WorkingAsks, Settlement, DayChange, SessionLow, SessionHigh, SecurityKey,
|
||||
MarketDescription, ClearedAlias, ProductDescription, HubAlias, StripName, MicCode
|
||||
}
|
||||
|
||||
public MarketViewView() {
|
||||
InitializeComponent();
|
||||
|
||||
@@ -97,6 +97,12 @@ namespace bts.shell {
|
||||
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, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings market_description => new GridColumnSettings(col_id(Columns.MarketDescription), DefaultColumnHeaders.MarketDescription, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings cleared_alias => new GridColumnSettings(col_id(Columns.ClearedAlias), DefaultColumnHeaders.ClearedAlias, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings product_description => new GridColumnSettings(col_id(Columns.ProductDescription), DefaultColumnHeaders.ProductDescription, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings hub_alias => new GridColumnSettings(col_id(Columns.HubAlias), DefaultColumnHeaders.HubAlias, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings strip_name => new GridColumnSettings(col_id(Columns.StripName), DefaultColumnHeaders.StripName, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings mic_code => new GridColumnSettings(col_id(Columns.MicCode), DefaultColumnHeaders.MicCode, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
|
||||
|
||||
|
||||
@@ -128,6 +134,12 @@ namespace bts.shell {
|
||||
yield return session_low;
|
||||
yield return session_high;
|
||||
yield return security_key;
|
||||
yield return market_description;
|
||||
yield return cleared_alias;
|
||||
yield return product_description;
|
||||
yield return hub_alias;
|
||||
yield return strip_name;
|
||||
yield return mic_code;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +189,18 @@ namespace bts.shell {
|
||||
public UIntMinMaxState ask_direct_size;
|
||||
[ProtoMember(20)]
|
||||
public UIntMinMaxState ask_implied_size;
|
||||
[ProtoMember(21)]
|
||||
public string market_description;
|
||||
[ProtoMember(22)]
|
||||
public string cleared_alias;
|
||||
[ProtoMember(23)]
|
||||
public string product_description;
|
||||
[ProtoMember(24)]
|
||||
public string hub_alias;
|
||||
[ProtoMember(25)]
|
||||
public string strip_name;
|
||||
[ProtoMember(26)]
|
||||
public string mic_code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@
|
||||
<sys:Double x:Key="RowHeight">16</sys:Double>
|
||||
<sys:Double x:Key="NegativeRowHeight">-16</sys:Double>
|
||||
<sys:Double x:Key="ItemsHostCanBeDisabledCollapsedHeight">15</sys:Double>
|
||||
<sys:Double x:Key="HeaderPanelItemSeparation">6</sys:Double>
|
||||
<Thickness x:Key="Margin.LHeaderPanelItem" Left="{StaticResource HeaderPanelItemSeparation}"/>
|
||||
<Thickness
|
||||
x:Key="GroupLevelMargin"
|
||||
Left="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
|
||||
@@ -100,7 +102,7 @@
|
||||
</usp:DataGridParameterCellTemplateConverter.BooleanTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
|
||||
<DataTemplate>
|
||||
<c:TickUpDownDouble Value="{Binding value}" IsEnabled="{Binding enabled}" Increment="{Binding tick_increment, FallbackValue={u:Double 1}}"/>
|
||||
<c:TickUpDownDouble Value="{Binding value}" IsEnabled="{Binding enabled}" Increment="{Binding tick_increment, FallbackValue={u:Double 1}}" FlushTextImmediately="False"/>
|
||||
</DataTemplate>
|
||||
</usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.IntTemplate>
|
||||
@@ -369,16 +371,21 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="7*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="3*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="5*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" IsEnabled="{TemplateBinding IsNew}">Name</TextBlock>
|
||||
<TextBox
|
||||
x:Name="PART_Name_TextBox" Grid.Column="1" Margin="6,0,0,0" HorizontalAlignment="Stretch" IsEnabled="{TemplateBinding IsNew}"
|
||||
x:Name="PART_Name_TextBox"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin.LHeaderPanelItem}"
|
||||
MinWidth="80"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsEnabled="{TemplateBinding IsNew}"
|
||||
Text="{Binding SyntheticName, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}"
|
||||
>
|
||||
<u:Behavior.BindingUpdateGroups>
|
||||
@@ -390,18 +397,22 @@
|
||||
</u:Behavior.BindingUpdateGroups>
|
||||
</TextBox>
|
||||
<TextBlock
|
||||
Grid.Column="2" VerticalAlignment="Center" Margin="6,0,0,0" Text="Ticks" IsEnabled="{TemplateBinding IsNew}"
|
||||
Grid.Column="2" VerticalAlignment="Center" Margin="{StaticResource Margin.LHeaderPanelItem}" Text="Ticks" IsEnabled="{TemplateBinding IsNew}"
|
||||
Visibility="{Binding LegTuples.Count, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IntNotZeroVisibilityConverter}}"
|
||||
/>
|
||||
<c:TickFactorsPicker
|
||||
x:Name="PART_TickFactorsPicker" Grid.Column="3" Margin="6,0,0,0" IsEnabled="{TemplateBinding IsNew}" HorizontalAlignment="Stretch"
|
||||
x:Name="PART_TickFactorsPicker" Grid.Column="3" Margin="{StaticResource Margin.LHeaderPanelItem}" IsEnabled="{TemplateBinding IsNew}" HorizontalAlignment="Stretch"
|
||||
SelectedTickFactors="{Binding TickFactors, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
|
||||
Visibility="{Binding LegTuples.Count, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IntNotZeroVisibilityConverter}}"
|
||||
/>
|
||||
<TextBlock Grid.Column="4" VerticalAlignment="Center" Margin="6,0,0,0">Pricing</TextBlock>
|
||||
<TextBlock Grid.Column="4" VerticalAlignment="Center" Margin="{StaticResource Margin.LHeaderPanelItem}" IsEnabled="{TemplateBinding IsNew}" >Pricing</TextBlock>
|
||||
<ComboBox
|
||||
Grid.Column="5" Margin="6,0,0,0" IsEnabled="False"
|
||||
ItemsSource="{x:Static usp:ExecutionStrategyEditor.PriceCalcItems}" SelectedItem="{Binding PriceCalc, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Grid.Column="5"
|
||||
Margin="{StaticResource Margin.LHeaderPanelItem}"
|
||||
Width="90"
|
||||
IsEnabled="{TemplateBinding IsNew}"
|
||||
ItemsSource="{x:Static usp:ExecutionStrategyEditor.PriceCalcItems}"
|
||||
SelectedItem="{Binding price_calc}"
|
||||
>
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -409,7 +420,7 @@
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<c:HelpButton Grid.Column="6" Margin="3,0,0,0"/>
|
||||
<c:HelpButton Grid.Column="8" Margin="3,0,0,0" ManPage="bts_spark/spreader/#creating-a-synthetic" />
|
||||
</Grid>
|
||||
<c:DataGridEx
|
||||
x:Name="PART_DataGrid"
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace mbts.ui.spark {
|
||||
|
||||
#region PriceCalc
|
||||
public static readonly DependencyProperty PriceCalcProperty = DependencyProperty.Register("PriceCalc", typeof(SyntheticContract.Types.PriceCalc), typeof(ExecutionStrategyEditor),
|
||||
new FrameworkPropertyMetadata(SyntheticContract.Types.PriceCalc.Diff, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
|
||||
new FrameworkPropertyMetadata(SyntheticContract.Types.PriceCalc.Standard, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
|
||||
public SyntheticContract.Types.PriceCalc PriceCalc { get => (SyntheticContract.Types.PriceCalc)GetValue(PriceCalcProperty); set => SetValue(PriceCalcProperty, value); }
|
||||
|
||||
public static readonly IEnumerable<SyntheticContract.Types.PriceCalc> PriceCalcItems = EnumHelper.get_values<SyntheticContract.Types.PriceCalc>();
|
||||
@@ -315,7 +315,6 @@ namespace mbts.ui.spark {
|
||||
else if (t == typeof(uint)) return UIntTemplate;
|
||||
else if (t == typeof(QuantityMeasure)) return QuantityMeasureTemplate;
|
||||
else if (t == typeof(LegSpreaderParams.Types.MarketDataType)) return MarketDataTypeTemplate;
|
||||
else if (t == typeof(LegSpreaderParams.Types.LegMarketDataCalculation)) return MarketDataCalculationTemplate;
|
||||
else if (t == typeof(ConditionalQuoting.Types.PriceType)) return ConditionalPriceTypeTemplate;
|
||||
else return null;
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using mbts.ui.spark;
|
||||
using LegMarketDataCalculation = bts.spark.LegSpreaderParams.Types.LegMarketDataCalculation;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SyntheticEditorVM {
|
||||
class MarketDataCalculationModel : RowModelBase<LegMarketDataCalculation> {
|
||||
public override string display_name { get; } = "Market Data Calculation";
|
||||
public override string help_text { get; }
|
||||
public override string man_page { get; } = string.Empty;
|
||||
|
||||
public override LegMarketDataCalculation get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.LegMdCalc;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, LegMarketDataCalculation value) =>
|
||||
leg_tup.leg_params.LegMdCalc = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ namespace mbts.spark {
|
||||
readonly PriceMultiplierModel _price_mult_model;
|
||||
readonly List<IDisposable> _resources = new List<IDisposable>();
|
||||
readonly Property<bool> _has_working_orders, _can_add_leg;
|
||||
|
||||
Security _synthetic;
|
||||
bool _set_name_automatically, _set_ticks_automatically, _set_quantities_automatically, _set_price_mults_automatically, _is_setting_quantities, _is_setting_price_mults;
|
||||
TickFactors _tick_factors;
|
||||
@@ -207,7 +208,6 @@ namespace mbts.spark {
|
||||
//new MinLeanQtyModel(), //TAHA_SPREADER
|
||||
//new QuoteHiddenModel(),
|
||||
new MarketDataTypeModel(),
|
||||
new MarketDataCalculationModel(),
|
||||
new QuoteThrottleEnabledModel(quote_throttle_inside_ticks, quote_throttle_outside_ticks),
|
||||
quote_throttle_inside_ticks,
|
||||
quote_throttle_outside_ticks,
|
||||
@@ -291,7 +291,9 @@ namespace mbts.spark {
|
||||
public ExecutionStrategyImmutable get_strategy() {
|
||||
var mutable_strategy = _strategy.CloneToMutable();
|
||||
mutable_strategy.LegParamsMap.Clear();
|
||||
foreach (var lt in leg_tuples) mutable_strategy.LegParamsMap.Add(lt.leg.Security.Id, lt.leg_params);
|
||||
foreach (var lt in leg_tuples) {
|
||||
mutable_strategy.LegParamsMap.Add(lt.leg.Security.Id, lt.leg_params);
|
||||
}
|
||||
return new ExecutionStrategyImmutable(mutable_strategy);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace mbts.spark {
|
||||
public readonly AlgoKey key;
|
||||
|
||||
public AlgoData(CustomOrderDefinitionImmutable value)
|
||||
: this(value, value.CustomCase.display_name(), new AlgoKey(value.CustomCase)) { }
|
||||
: this(value, value.display_name(), OrderDataHelper.get_shallow_factory_key(value)) { }
|
||||
|
||||
public AlgoData(CustomOrderDefinitionImmutable value, string name, AlgoKey key) {
|
||||
this.value = value;
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace mbts.spark {
|
||||
_build_structure(definition.CloneToMutable(), ref structure, pos);
|
||||
|
||||
static void _build_structure(CustomOrderDefinition algo, ref int[] structure, int pos) {
|
||||
var cad = OrderDataHelper.get_child_algo_data(algo.CustomCase);
|
||||
var cad = OrderDataHelper.get_child_algo_data(algo);
|
||||
for (int di = 0; di < cad.Count; di++) {
|
||||
if (cad[di].get_child(algo) is CustomOrderDefinition child_algo) {
|
||||
ArrayHelper.ensure_capacity(ref structure, pos + 1);
|
||||
@@ -87,7 +87,7 @@ namespace mbts.spark {
|
||||
|
||||
static void _build_def(int[] structure, ref int pos, ref CustomOrderDefinition algo) {
|
||||
for (int i = pos; i < structure.Length; i++) {
|
||||
var cad = OrderDataHelper.get_child_algo_data(algo.CustomCase);
|
||||
var cad = OrderDataHelper.get_child_algo_data(algo);
|
||||
for (int di = 0; di < cad.Count; di++) {
|
||||
int mask = 1 << (31 - di);
|
||||
if ((structure[i] & mask) == mask) {
|
||||
@@ -95,10 +95,10 @@ namespace mbts.spark {
|
||||
var child_algo = OrderDataHelper.Defaults.CustomOrderDefinition(type);
|
||||
pos++;
|
||||
_build_def(structure, ref pos, ref child_algo);
|
||||
cad[di].set_child(algo, child_algo);
|
||||
cad[di].assign_child(algo, child_algo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,13 @@ using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Reactive;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class OrderBookRowVM : DisposableViewModelBase, IEquatable<OrderBookRowVM>, DIYGrid.IRowVM, DIYGrid.IActionableRowVM {
|
||||
@@ -44,6 +46,19 @@ namespace mbts.spark {
|
||||
modify_order_price?.TryRaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
//// This takes care of the case where the OB has all rows visible
|
||||
//// and an order is cancelled outside of the OB (say via the ladder).
|
||||
//// Since the OB window Commands only revaluate their CanExecuteChanged
|
||||
//// when the OrderBookVM.selected_order_changes, this emulates the selected
|
||||
//// order changing since the current_row setter invokes the selected_order_changed
|
||||
//// handler
|
||||
public IObservable<OrderSecurityAlgoMeta> subscribe_data() {
|
||||
return Observable.FromEvent<Action, OrderSecurityAlgoMeta>(
|
||||
cb => () => cb(order_tuple),
|
||||
cb => invalidated += cb,
|
||||
cb => invalidated -= cb);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
this.invalidated -= OrderBookRowVM_invalidated;
|
||||
base.Dispose(disposing);
|
||||
|
||||
@@ -19,12 +19,11 @@ using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using static bts.shell.OrderBookWindowFactory;
|
||||
using static mbts.spark.OrderBookView;
|
||||
using _GridSettingsManager =
|
||||
using _GridSettingsManager =
|
||||
mbts.ui.GridSettingsManager<
|
||||
mbts.spark.OrderBookRowVM,
|
||||
bts.api.orderman.OrderSecurity,
|
||||
bts.shell.INotificationServiceDataCache,
|
||||
mbts.OrderRecordTuplePredicate,
|
||||
mbts.spark.OrderBookRowVM,
|
||||
bts.api.orderman.OrderSecurity,
|
||||
mbts.OrderRecordTuplePredicate,
|
||||
bts.shell.OrderBookWindowFactory.OrderBookGridSettings>;
|
||||
|
||||
namespace mbts.spark {
|
||||
@@ -54,6 +53,7 @@ namespace mbts.spark {
|
||||
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, core_columns);
|
||||
|
||||
_resources = new List<IDisposable> {
|
||||
model.on_order_tuples.OnDispatcher().Subscribe(_row_collection.update),
|
||||
grid_settings_manager,
|
||||
@@ -62,7 +62,12 @@ namespace mbts.spark {
|
||||
_row_collection.CollectionChanged -= _row_collection_CollectionChanged;
|
||||
Disposable.dispose(_row_collection);
|
||||
}),
|
||||
_columns
|
||||
_columns,
|
||||
_current_row.SubscribeHookLatest(
|
||||
r => r.newval?.subscribe_data().Subscribe(
|
||||
data => selected_order_changed?.Invoke(data.order)
|
||||
)
|
||||
)
|
||||
};
|
||||
if (settings_manager != null) {
|
||||
_resources.Add(settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager));
|
||||
@@ -105,19 +110,23 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
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;
|
||||
try {
|
||||
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;
|
||||
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);
|
||||
} catch (Exception ex) {
|
||||
// [John]I observed a strange null-reference exception in this callback when the launcher was shutting down (see https://bluetradesys.atlassian.net/browse/SPARK-2089)
|
||||
Log.Ui.error($"[{nameof(OrderBookVM)}] Error handling selection change: {ex}");
|
||||
}
|
||||
|
||||
RowSelectionHelper.apply_changes(_selected_rows, e);
|
||||
selected_order_changed?.Invoke(current_order);
|
||||
}
|
||||
|
||||
public OrderBookStartupData create() {
|
||||
@@ -227,21 +236,25 @@ namespace mbts.spark {
|
||||
col_settings_flags[c.ToString()] = GridColumnSettingsFeatureFlags.ImmutableFontColor | GridColumnSettingsFeatureFlags.ImmutableFontWeight;
|
||||
}
|
||||
return new _GridSettingsManager(
|
||||
owner_title, bus, notification_service, model, rows, columns,
|
||||
owner_title, bus, model, rows, columns,
|
||||
() => new OrderRecordTuplePredicate(),
|
||||
() => {
|
||||
var pvm = PredicateBuilderVMWrapper.create<OrderSecurity, OrderRecordTuplePredicate, OrderRecordTuplePredicate.PredicateTypeOneofCase>(
|
||||
var pvm = PredicateVMWrapper.create<OrderSecurity, OrderRecordTuplePredicate, OrderRecordTuplePredicate.PredicateTypeOneofCase>(
|
||||
OrderRecordTuplePredicateVMFactory.get_oneofs(bus, model), rp => rp.predicate_type_case);
|
||||
pvm.selected_predicate_type = pvm.predicate_types.FirstOrDefault();
|
||||
return pvm;
|
||||
},
|
||||
col_id => {
|
||||
var pvm = PredicateBuilderVMWrapper.create<OrderSecurity, OrderRecordTuplePredicate, OrderRecordTuplePredicate.PredicateTypeOneofCase>(
|
||||
var pvm = PredicateVMWrapper.create<OrderSecurity, OrderRecordTuplePredicate, OrderRecordTuplePredicate.PredicateTypeOneofCase>(
|
||||
OrderRecordTuplePredicateVMFactory.get_oneofs(bus, model), rp => rp.predicate_type_case);
|
||||
pvm.selected_predicate_type = _corresponding_predicate_type(EnumHelper.parse_or_default<Columns>(col_id));
|
||||
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 == null) ? null : notification_service.data_cache,
|
||||
(gen, anims) => new OrderBookGridSettings(gen, anims.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_data, a.cell_animations, a.row_animations, a.sound_notifications))),
|
||||
p => p.create_predicate(notification_service.data_cache),
|
||||
p => p.create_notification_predicate(notification_service.data_cache),
|
||||
notification_service == null ? new Func<IPredicate<OrderSecurity>, SoundNotification, IDisposable>((p, sn) => Disposable.empty) : notification_service.link_to_order,
|
||||
notification_service == null ? new Func<INotificationPredicate<OrderSecurity>, IDisposable>(t => Disposable.empty) : notification_service.link_to_order,
|
||||
col_settings_flags);
|
||||
}
|
||||
|
||||
@@ -271,4 +284,4 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,17 +87,41 @@ namespace mbts.spark {
|
||||
|
||||
_cancel_buy_orders_command = SparkCommands.CancelBuyOrders.complete(
|
||||
_model.working_orders.Select(os => os.Values.Where(o => o.order.side() == Side.Buy).Select(o => o.order.Key)),
|
||||
wbids => wbids.Any()
|
||||
wbids => wbids.Any(),
|
||||
() => {
|
||||
var resp = BTSMessageBox.Show(
|
||||
"Are you sure you want to cancel all BUY orders?",
|
||||
"Cancel Buy Orders",
|
||||
BTSMessageBoxButtons.Yes | BTSMessageBoxButtons.No,
|
||||
MessageBoxImage.Question);
|
||||
return resp == BTSMessageBoxButtons.Yes;
|
||||
}
|
||||
);
|
||||
|
||||
_cancel_sell_orders_command = SparkCommands.CancelSellOrders.complete(
|
||||
_model.working_orders.Select(os => os.Values.Where(o => o.order.side() == Side.Sell).Select(o => o.order.Key)),
|
||||
wasks => wasks.Any()
|
||||
wasks => wasks.Any(),
|
||||
() => {
|
||||
var resp = BTSMessageBox.Show(
|
||||
"Are you sure you want to cancel all SELL orders?",
|
||||
"Cancel Sell Orders",
|
||||
BTSMessageBoxButtons.Yes | BTSMessageBoxButtons.No,
|
||||
MessageBoxImage.Question);
|
||||
return resp == BTSMessageBoxButtons.Yes;
|
||||
}
|
||||
);
|
||||
|
||||
_cancel_all_orders_command = SparkCommands.CancelOrders.complete(
|
||||
_model.working_orders.Select(os => os.Select(o => o.Key)),
|
||||
worders => worders.Any()
|
||||
worders => worders.Any(),
|
||||
() => {
|
||||
var resp = BTSMessageBox.Show(
|
||||
"Are you sure you want to cancel ALL orders?",
|
||||
"Cancel All Orders",
|
||||
BTSMessageBoxButtons.Yes | BTSMessageBoxButtons.No,
|
||||
MessageBoxImage.Question);
|
||||
return resp == BTSMessageBoxButtons.Yes;
|
||||
}
|
||||
);
|
||||
|
||||
_cancel_selected_orders_command = SparkCommands.CancelSelectedOrders.complete(
|
||||
|
||||
@@ -84,17 +84,17 @@ namespace bts.shell {
|
||||
static class DefaultColumns {
|
||||
|
||||
public static GridColumnSettings account => new GridColumnSettings(col_id(Columns.Account), DefaultColumnHeaders.Account, width: DefaultColumnWidths.Account);
|
||||
public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, width: DefaultColumnWidths.Exchange);
|
||||
public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, false, width: DefaultColumnWidths.Exchange);
|
||||
public static GridColumnSettings symbol => new GridColumnSettings(col_id(Columns.Symbol), DefaultColumnHeaders.Symbol, width: DefaultColumnWidths.Symbol);
|
||||
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
|
||||
public static GridColumnSettings security_type => new GridColumnSettings(col_id(Columns.SecurityType), DefaultColumnHeaders.SecurityType, width: DefaultColumnWidths.SecurityType);
|
||||
public static GridColumnSettings start_net_position => new GridColumnSettings(col_id(Columns.StartNetPos), DefaultColumnHeaders.StartNetPos, width: DefaultColumnWidths.Start_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings start_pnl => new GridColumnSettings(col_id(Columns.StartPNL), DefaultColumnHeaders.StartPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings start_pnl => new GridColumnSettings(col_id(Columns.StartPNL), DefaultColumnHeaders.StartPnL, false, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings day_long_pnl => new GridColumnSettings(col_id(Columns.DayLong), DefaultColumnHeaders.DayLong, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings day_short_pnl => new GridColumnSettings(col_id(Columns.DayShort), DefaultColumnHeaders.DayShort, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings day_pnl => new GridColumnSettings(col_id(Columns.DayPNL), DefaultColumnHeaders.DayPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings total_net_pos => new GridColumnSettings(col_id(Columns.TotalNetPos), DefaultColumnHeaders.TotalNetPos, width: DefaultColumnWidths.Total_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings total_pnl => new GridColumnSettings(col_id(Columns.TotalPNL), DefaultColumnHeaders.TotalPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings total_net_pos => new GridColumnSettings(col_id(Columns.TotalNetPos), DefaultColumnHeaders.TotalNetPos, false, width: DefaultColumnWidths.Total_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings total_pnl => new GridColumnSettings(col_id(Columns.TotalPNL), DefaultColumnHeaders.TotalPnL, false, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings pnl_price => new GridColumnSettings(col_id(Columns.PNLPrice), DefaultColumnHeaders.PnLPrice, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
|
||||
public static IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
@@ -158,6 +158,8 @@ namespace bts.shell {
|
||||
// ProtoMember 13 cannot be used further
|
||||
[ProtoMember(14)]
|
||||
public SecurityType[] security_types;
|
||||
[ProtoMember(15)]
|
||||
public bool[] has_day_trades;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using OrderTuple = System.ValueTuple<bts.api.SecurityImmutable?, bts.api.orderman.OrderRecordImmutable>;
|
||||
|
||||
namespace bts.shell {
|
||||
interface INotificationService {
|
||||
@@ -20,40 +19,14 @@ namespace bts.shell {
|
||||
INotificationServiceDataCache data_cache { get; }
|
||||
IDisposable link_to_activity(IPredicate<Activity> predicate, SoundNotification sound);
|
||||
IDisposable link_to_activity(INotificationPredicate<Activity> toast_notification_predicate);
|
||||
IDisposable link_to_order(IPredicate<OrderTuple> predicate, SoundNotification sound);
|
||||
IDisposable link_to_order(INotificationPredicate<OrderTuple> toast_notification_predicate);
|
||||
}
|
||||
|
||||
static class NotificationServiceExtensions {
|
||||
/// TODO: remove this extension and force callers to use the appropriate 'link' method in <see cref="INotificationService"/> (it is smelly, and is NOT ACTUALLY STRONGLY TYPED)
|
||||
public static IDisposable link<T>(this INotificationService notification_service, IPredicate<T> predicate, SoundNotification sound) {
|
||||
if (predicate is IPredicate<OrderTuple> orp)
|
||||
return notification_service.link_to_order(orp, sound);
|
||||
if (predicate is IPredicate<OrderSecurity> osp) {
|
||||
// Note: watch out for implicit types as generic type arguments (although OrderSecurity can be implicitly cast to OrderTuple, IPredicate<OrderSecurity> CANNOT be implicitly cast to IPredicate<OrderTuple>)
|
||||
return notification_service.link_to_order(PredicateBuilder.create<OrderTuple>(ot => osp.trigger(new OrderSecurity(ot.Item1, ot.Item2))), sound);
|
||||
}
|
||||
if (predicate is IPredicate<Activity> ap)
|
||||
return notification_service.link_to_activity(ap, sound);
|
||||
Log.Ui.error($"Unable to activate sound-trigger (unrecognized predicate type '{typeof(T)}').");
|
||||
return Disposable.empty;
|
||||
}
|
||||
|
||||
/// TODO: remove this extension and force callers to use the appropriate 'link' method in <see cref="INotificationService"/> (it is smelly, and is NOT ACTUALLY STRONGLY TYPED)
|
||||
public static IDisposable link<T>(this INotificationService notification_service, INotificationPredicate<T> toast_notification_predicate) {
|
||||
if (toast_notification_predicate is INotificationPredicate<OrderTuple> orp)
|
||||
return notification_service.link_to_order(orp);
|
||||
if (toast_notification_predicate is INotificationPredicate<Activity> ap)
|
||||
return notification_service.link_to_activity(ap);
|
||||
Log.Ui.error($"Unable to activate toast-notification-trigger (unrecognized predicate type '{typeof(T)}').");
|
||||
return Disposable.empty;
|
||||
}
|
||||
IDisposable link_to_order(IPredicate<OrderSecurity> predicate, SoundNotification sound);
|
||||
IDisposable link_to_order(INotificationPredicate<OrderSecurity> toast_notification_predicate);
|
||||
}
|
||||
|
||||
[Export(typeof(INotificationService))]
|
||||
partial class NotificationService : INotificationService, IDisposable {
|
||||
readonly NotificationProcessor<Activity> _activity_notifications;
|
||||
readonly NotificationProcessor<OrderTuple> _order_notifications;
|
||||
readonly NotificationProcessor<OrderSecurity> _order_notifications;
|
||||
readonly DataCache _data_cache = new DataCache();
|
||||
|
||||
IDisposable _dispose;
|
||||
@@ -62,7 +35,7 @@ namespace bts.shell {
|
||||
public NotificationService(IBus bus, INotifier notifier, OrdersModel orders_model) {
|
||||
this.notifier = notifier;
|
||||
_activity_notifications = new NotificationProcessor<Activity>(notifier);
|
||||
_order_notifications = new NotificationProcessor<OrderTuple>(notifier);
|
||||
_order_notifications = new NotificationProcessor<OrderSecurity>(notifier);
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.LogActivity).Subscribe(_on_activity),
|
||||
orders_model.on_orders.Subscribe(_on_orders)
|
||||
@@ -75,17 +48,17 @@ namespace bts.shell {
|
||||
|
||||
public IDisposable link_to_activity(IPredicate<Activity> predicate, SoundNotification sound) => _activity_notifications.link(predicate, sound);
|
||||
public IDisposable link_to_activity(INotificationPredicate<Activity> toast_notification_predicate) => _activity_notifications.link(toast_notification_predicate);
|
||||
public IDisposable link_to_order(IPredicate<OrderTuple> predicate, SoundNotification sound) => _order_notifications.link(predicate, sound);
|
||||
public IDisposable link_to_order(INotificationPredicate<OrderTuple> toast_notification_predicate) => _order_notifications.link(toast_notification_predicate);
|
||||
public IDisposable link_to_order(IPredicate<OrderSecurity> predicate, SoundNotification sound) => _order_notifications.link(predicate, sound);
|
||||
public IDisposable link_to_order(INotificationPredicate<OrderSecurity> toast_notification_predicate) => _order_notifications.link(toast_notification_predicate);
|
||||
public void Dispose() => Disposable.dispose(ref _dispose);
|
||||
|
||||
void _on_activity(Activity activity) =>
|
||||
_activity_notifications.process_item(activity);
|
||||
|
||||
void _on_orders(TableUpdate<(SecurityImmutable?,OrderRecordImmutable),uint> update) {
|
||||
void _on_orders(TableUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint> update) {
|
||||
// must update notification_service_data_cache first, some of the predicate/triggers (esp. ToastNotifications) will use the notification_service_data_cache to lookup order-history
|
||||
_data_cache.update(update.as_iincremental());
|
||||
foreach (var o in update.added)
|
||||
foreach (var o in update.added.Concat(update.changed))
|
||||
_order_notifications.process_item(o);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace bts.spark {
|
||||
static readonly IEqualityComparer<OrderRecordImmutable> _id_comparer =
|
||||
bts.utils.EqualityComparer.Create<OrderRecordImmutable, uint>(a => a.Key);
|
||||
|
||||
public OrderActivitySummarizer(IBus bus, IObservable<TableUpdate<(SecurityImmutable? sec,OrderRecordImmutable order), uint>> orders) {
|
||||
public OrderActivitySummarizer(IBus bus, IObservable<TableUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint>> orders) {
|
||||
_bus = bus;
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(sec => _security = sec),
|
||||
|
||||
@@ -346,7 +346,9 @@ namespace mbts.spark {
|
||||
_time_in_force.observe_current(start_immediately);
|
||||
|
||||
public IObservable<IAlgoVM> observe_order_type(bool start_immediately = false) =>
|
||||
_selected_algo_template.observe_current(start_immediately).Select(t => t.algo_root);
|
||||
_model.algo_model.templates_enabled() ?
|
||||
_selected_algo_template.observe_current(start_immediately).Select(t => t?.algo_root) :
|
||||
_selected_algo_definition.observe_selected_item(start_immediately).Select(d => d?.algo_root);
|
||||
|
||||
public OrderDefinition create(Side side) {
|
||||
if (HasErrors)
|
||||
|
||||
@@ -7,18 +7,31 @@ namespace bts.api.orderman {
|
||||
public OrderSecurity(SecurityImmutable? sec, OrderRecordImmutable order) {
|
||||
this.security = sec;
|
||||
this.order = order;
|
||||
piq = null;
|
||||
}
|
||||
public OrderSecurity((SecurityImmutable? sec, OrderRecordImmutable order) t) {
|
||||
security = t.sec;
|
||||
order = t.order;
|
||||
piq = null;
|
||||
}
|
||||
public OrderSecurity(SecurityImmutable? sec, OrderRecordImmutable order, uint? piq) {
|
||||
this.security = sec;
|
||||
this.order = order;
|
||||
this.piq = piq;
|
||||
}
|
||||
//public OrderSecurity(OrderSecurity t) {
|
||||
// security = t.security;
|
||||
// order = t.order;
|
||||
// piq = t.piq;
|
||||
//}
|
||||
public static implicit operator OrderSecurity(ValueTuple<SecurityImmutable?, OrderRecordImmutable> x) => new OrderSecurity(x);
|
||||
public SecurityImmutable? security { get; }
|
||||
public OrderRecordImmutable order { get; }
|
||||
public uint? piq { get; }
|
||||
|
||||
public override bool Equals(object obj) => (obj is OrderSecurity os) && Equals(os);
|
||||
public bool Equals(OrderSecurity other) => order.Equals(other.order) && security == other.security;
|
||||
public override int GetHashCode() => (security, order).GetHashCode();
|
||||
public bool Equals(OrderSecurity other) => order.Equals(other.order) && security == other.security && piq == other.piq;
|
||||
public override int GetHashCode() => (security, order, piq).GetHashCode();
|
||||
|
||||
public static bool operator == (OrderSecurity left, OrderSecurity right) => left.Equals(right);
|
||||
public static bool operator != (OrderSecurity left, OrderSecurity right) => !left.Equals(right);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
using Google.Protobuf.Reflection;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
@@ -10,6 +13,9 @@ namespace mbts.spark {
|
||||
[Export]
|
||||
public class DynamicOrderHelper
|
||||
{
|
||||
static readonly IReadOnlyDictionary<DynamicOrderParam.ValueOneofCase, (FieldDescriptor field_descriptor, Type field_type)> _descriptor_case_index =
|
||||
ProtoHelper.get_oneof_descriptor_index<DynamicOrderParam, DynamicOrderParam.ValueOneofCase>(nameof(DynamicOrderParam.ValueCase));
|
||||
|
||||
readonly RegisteredDynamicOrderTypes _dynamic_types;
|
||||
[ImportingConstructor]
|
||||
private DynamicOrderHelper(OrderService order_service)
|
||||
@@ -28,11 +34,47 @@ namespace mbts.spark {
|
||||
foreach(var p in d.ParamDesc)
|
||||
dorder.Dynamic.OrderParams.Add( p.ParamName, new DynamicOrderParam() );
|
||||
defs.Add(new CustomOrderDefinitionImmutable(dorder));
|
||||
var de = get_descriptor("");
|
||||
}
|
||||
}
|
||||
|
||||
internal DynamicOrderDescriptor get_descriptor(string dynamic_type) =>
|
||||
_dynamic_types.DynamicOrderDesc.FirstOrDefault(d => d.DynamicOrderType == dynamic_type);
|
||||
_dynamic_types.DynamicOrderDesc.FirstOrDefault(d => d.DynamicOrderType == dynamic_type) ?? new DynamicOrderDescriptor();
|
||||
|
||||
internal DynamicOrderDefinition create_def(string dynamic_type) {
|
||||
var desc = get_descriptor(dynamic_type);
|
||||
var def = new DynamicOrderDefinition();
|
||||
foreach (var p in desc.ParamDesc) {
|
||||
if (_is_child_algo_desc(p)) {
|
||||
def.OrderParams[p.ParamName] = new DynamicOrderParam { AsOrderDef = new CustomOrderDefinition() };
|
||||
} else if (_is_value_param_desc(p)) {
|
||||
var value_case = _get_value_case(p.ParamType.Primitive);
|
||||
if (_descriptor_case_index.TryGetValue(value_case, out (FieldDescriptor fd, Type type) tup)) {
|
||||
var dop = new DynamicOrderParam();
|
||||
tup.fd.Accessor.SetValue(dop, ConvertUtils.get_default(tup.type));
|
||||
def.OrderParams[p.ParamName] = dop;
|
||||
}
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
static DynamicOrderParam.ValueOneofCase _get_value_case(DynamicOrderPrimitiveDescriptor primitive_descriptor_case) {
|
||||
switch (primitive_descriptor_case) {
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveBool: return DynamicOrderParam.ValueOneofCase.AsBool;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveDouble: return DynamicOrderParam.ValueOneofCase.AsBool;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveInt: return DynamicOrderParam.ValueOneofCase.AsInt32;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveUint: return DynamicOrderParam.ValueOneofCase.AsUint32;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveString: return DynamicOrderParam.ValueOneofCase.AsString;
|
||||
default: throw ExceptionBuilder.Argument.unexpected_case(primitive_descriptor_case, nameof(primitive_descriptor_case));
|
||||
}
|
||||
}
|
||||
|
||||
static bool _is_child_algo_desc(DynamicOrderParamDescriptor desc) =>
|
||||
desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive &&
|
||||
desc.ParamType.Primitive == DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef;
|
||||
|
||||
static bool _is_value_param_desc(DynamicOrderParamDescriptor desc) =>
|
||||
desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Enumerated ||
|
||||
(desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive && desc.ParamType.Primitive != DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,36 @@
|
||||
using bts.api.orderman;
|
||||
using mbts.collections;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
class DynamicOrderVM : OrderTypeVMBase<DynamicOrderDefinition, DynamicOrderDefinitionImmutable> {
|
||||
readonly ObservableKeyedCollection<DynamicOrderParamVM, string> _parameters = new ObservableKeyedCollection<DynamicOrderParamVM, string>(vm => vm.descriptor.ParamName);
|
||||
readonly Property<string> _dyn_type;
|
||||
OrdersModel _model;
|
||||
bool _active;
|
||||
|
||||
public DynamicOrderVM(AlgoKey key, string dyn_type, string display_name, AlgoEditingPermissions editing_permissions)
|
||||
: base(key, display_name, editing_permissions) {
|
||||
_dyn_type = add_property(nameof(this.dyn_type), DynamicOrderDefinition.DynamicOrderTypeFieldNumber, dyn_type);
|
||||
_parameters = new ObservableKeyedCollection<DynamicOrderParamVM, string>(vm => vm.descriptor.ParamName);
|
||||
_resources.AddRange(
|
||||
_dyn_type.Subscribe(_ => _reset_dynamic_types()),
|
||||
_dyn_type.Subscribe(_ => {
|
||||
_sync_dynamic_params();
|
||||
sync_selectors();
|
||||
}),
|
||||
ObservableAutoWrapper.connect_item_hooks(_parameters.AsCollection(), pvm => {
|
||||
add_property(pvm.descriptor.ParamName, mdef => mdef.OrderParams[pvm.descriptor.ParamName] = pvm.param, mdef => {
|
||||
if (mdef.OrderParams.TryGetValue(pvm.descriptor.ParamName, out DynamicOrderParam p))
|
||||
pvm.param = p;
|
||||
});
|
||||
return Disposable.Create(() => remove_property(pvm.descriptor.ParamName));
|
||||
}),
|
||||
ObservableAutoWrapper.connect_item_hooks(_parameters, p => {
|
||||
p.PropertyChanged += on_param_prop_changed;
|
||||
return Disposable.Create(() => p.PropertyChanged -= on_param_prop_changed);
|
||||
@@ -31,103 +39,26 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public string dyn_type => _dyn_type.Value;
|
||||
public ObservableCollection<DynamicOrderParamVM> parameters {
|
||||
get {
|
||||
_ensure_init();
|
||||
return _parameters;
|
||||
public ObservableCollection<DynamicOrderParamVM> parameters => _parameters;
|
||||
|
||||
protected override void on_model_internal() =>
|
||||
_sync_dynamic_params();
|
||||
|
||||
protected override IEnumerable<ChildAlgoData> get_selector_datum() =>
|
||||
OrderDataHelper.get_child_algo_data(new CustomOrderDefinition {
|
||||
Dynamic = model?.dynamic_helper?.create_def(dyn_type) ?? new DynamicOrderDefinition()
|
||||
});
|
||||
|
||||
void _sync_dynamic_params() {
|
||||
if (model != null) {
|
||||
var desc = model.dynamic_helper.get_descriptor(dyn_type);
|
||||
_parameters.SyncWithDictionary(desc.ParamDesc.Where(_is_value_param_desc).ToDictionary(p => p.ParamName), DynamicOrderParamVM.create);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void assign_to_internal(DynamicOrderDefinition dynamic_def) {
|
||||
foreach (var vm in _parameters.Values)
|
||||
dynamic_def.OrderParams[vm.descriptor.ParamName] = vm.param;
|
||||
}
|
||||
|
||||
protected override void load_from_internal(DynamicOrderDefinitionImmutable dyn_defi) {
|
||||
foreach (var p in _parameters) {
|
||||
if (dyn_defi.OrderParams.TryGetValue(p.descriptor.ParamName, out DynamicOrderParam dop)) {
|
||||
p.param = dop;
|
||||
} else {
|
||||
p.param.ClearValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task on_load_from_async(CustomOrderDefinitionImmutable defi) {
|
||||
await Wait.until_true(() => _model != null, TimeSpan.FromSeconds(60));
|
||||
_ensure_init();
|
||||
}
|
||||
|
||||
void _ensure_init() {
|
||||
if (!_active) {
|
||||
_active = true;
|
||||
// lazy initialization is required to avoid a stack overflow (otherwise, the algo-hierarchy would grow recursively without stopping).
|
||||
_reset_dynamic_types();
|
||||
}
|
||||
}
|
||||
protected override void on_model_internal(OrdersModel model) {
|
||||
_model = model;
|
||||
_reset_dynamic_types();
|
||||
}
|
||||
|
||||
void _reset_dynamic_types() {
|
||||
if (!_active)
|
||||
return;
|
||||
var value_descs = new Dictionary<string, DynamicOrderParamDescriptor>();
|
||||
var child_descs = new Dictionary<string, DynamicOrderParamDescriptor>();
|
||||
if (_model != null) {
|
||||
var desc = _model.dynamic_helper.get_descriptor(dyn_type);
|
||||
if (desc != null) {
|
||||
foreach (var p in desc.ParamDesc) {
|
||||
if (_is_value_param_desc(p)) {
|
||||
value_descs[p.ParamName] = p;
|
||||
} else if (_is_child_algo_desc(p)) {
|
||||
child_descs[p.ParamName] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_parameters.SyncWithDictionary(value_descs, DynamicOrderParamVM.create);
|
||||
using (reset_selectors()) {
|
||||
foreach (var kv in child_descs) {
|
||||
add_selector(kv.Key, get_child, set_child);
|
||||
// local methods
|
||||
CustomOrderDefinitionImmutable? get_child(DynamicOrderDefinitionImmutable dyn_defi) {
|
||||
if (dyn_defi.OrderParams.TryGetValue(kv.Key, out DynamicOrderParam p) && p.ValueCase == DynamicOrderParam.ValueOneofCase.AsOrderDef)
|
||||
return new CustomOrderDefinitionImmutable(p.AsOrderDef);
|
||||
return null;
|
||||
}
|
||||
void set_child(DynamicOrderDefinition dyn_def, CustomOrderDefinition child) =>
|
||||
dyn_def.OrderParams[kv.Key] = new DynamicOrderParam { AsOrderDef = child };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool _is_child_algo_desc(DynamicOrderParamDescriptor desc) =>
|
||||
desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive &&
|
||||
desc.ParamType.Primitive == DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef;
|
||||
|
||||
static bool _is_value_param_desc(DynamicOrderParamDescriptor desc) =>
|
||||
desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Enumerated ||
|
||||
(desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive && desc.ParamType.Primitive != DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef);
|
||||
|
||||
public class SelectorVM : ViewModelBase {
|
||||
readonly IAlgoSelector _selector;
|
||||
|
||||
public SelectorVM(IAlgoSelector selector) {
|
||||
_selector = selector;
|
||||
_selector.selected_algo_changed += _on_selected_algo_changed;
|
||||
}
|
||||
|
||||
public IAlgoVM selected_algo { get => _selector.selected_algo; set => _selector.selected_algo = value; }
|
||||
public ObservableCollection<IAlgoVM> algos => _selector.algos;
|
||||
|
||||
public void Dispose() =>
|
||||
_selector.selected_algo_changed -= _on_selected_algo_changed;
|
||||
|
||||
void _on_selected_algo_changed(IAlgoVM oldval, IAlgoVM newval) =>
|
||||
RaisePropertyChanged(nameof(selected_algo));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,11 +113,12 @@ namespace mbts.spark {
|
||||
readonly IDictionary<string, object> _children_vm = new ExpandoObject();
|
||||
protected CompositeDisposable _resources = new CompositeDisposable();
|
||||
|
||||
readonly List<(Action<TMutable> assign, Action<TMutable> load)> _auto_props = new List<(Action<TMutable> assign, Action<TMutable> load)>();
|
||||
readonly ObservableKeyedCollection<(string name, Action<TMutable> assign, Action<TMutable> load), string> _auto_props = new ObservableKeyedCollection<(string name, Action<TMutable> assign, Action<TMutable> load), string>(t => t.name);
|
||||
readonly ObservableKeyedCollection<AlgoSelectorData, string> _child_algo_selector_data = new ObservableKeyedCollection<AlgoSelectorData, string>(sd => sd.prop_name);
|
||||
readonly ObservableAutoWrapper<IAlgoSelector, AlgoSelectorData> _child_algo_selectors;
|
||||
readonly Property<bool> _is_expanded, _can_save_values, _can_save_structure;
|
||||
readonly DeferrableActionWrapper _def_change_notifier;
|
||||
IDisposable _pending_load;
|
||||
|
||||
protected OrderTypeVMBase(AlgoKey id, string display_name, AlgoEditingPermissions editing_permissions) {
|
||||
this.id = id;
|
||||
@@ -127,9 +128,7 @@ namespace mbts.spark {
|
||||
_can_save_values = backing(nameof(can_save_values), true);
|
||||
_can_save_structure = backing(nameof(can_save_structure), true);
|
||||
_is_expanded = backing<bool>(nameof(is_expanded));
|
||||
foreach (var c in OrderDataHelper.get_child_algo_data(order_type)) {
|
||||
_children_vm[c.name] = add_selector(c.name, c.descriptor);
|
||||
}
|
||||
meta = OrderDataHelper.get_metadata(order_type);
|
||||
_def_change_notifier = new DeferrableActionWrapper(() => definition_changed?.Invoke());
|
||||
PropertyChanged += (s, e) => {
|
||||
switch (e.PropertyName) {
|
||||
@@ -170,39 +169,41 @@ namespace mbts.spark {
|
||||
}, true));
|
||||
}
|
||||
|
||||
public AlgoEditingPermissions editing_permissions { get; private set; }
|
||||
public event Action definition_changed;
|
||||
// core
|
||||
public AlgoDefi definition => new AlgoDefi(this.created_assigned());
|
||||
public AlgoType order_type => OrderDataHelper.get_order_type(new TMutable());
|
||||
public bool is_bts_type => display_name.StartsWith("BTS");
|
||||
public AlgoKey id { get; }
|
||||
// display
|
||||
public bool is_bts_type => display_name.StartsWith("BTS");
|
||||
public string display_name { get; private set; }
|
||||
public OrderMetadata meta => OrderDataHelper.get_metadata(order_type);
|
||||
public virtual bool can_expand => !meta.NoProps;
|
||||
public virtual bool is_expanded {
|
||||
get => _is_expanded.Value;
|
||||
set => _is_expanded.Value = value;
|
||||
}
|
||||
public OrderMetadata meta { get; }
|
||||
// child algos
|
||||
public dynamic children_vm => _children_vm;
|
||||
public ObservableCollection<IAlgoSelector> child_algo_selectors => _child_algo_selectors;
|
||||
// editing permissions
|
||||
public AlgoEditingPermissions editing_permissions { get; private set; }
|
||||
public bool can_edit_values => EnumHelper.includes_flag(editing_permissions, AlgoEditingPermissions.AllowEditValues);
|
||||
public bool can_edit_structure => EnumHelper.includes_flag(editing_permissions, AlgoEditingPermissions.AllowEditStructure);
|
||||
public bool can_save_values => _can_save_values.Value;
|
||||
public bool can_save_structure => _can_save_structure.Value;
|
||||
// misc
|
||||
public virtual bool can_expand => !meta.NoProps;
|
||||
public virtual bool is_expanded { get => _is_expanded.Value; set => _is_expanded.Value = value; }
|
||||
// protected (observed props)
|
||||
protected OrdersModel model { get; private set; }
|
||||
protected SecurityMD? security_md { get; private set; }
|
||||
protected SecurityImmutable? security => security_md?.security;
|
||||
|
||||
public void assign_to(AlgoDef value) {
|
||||
var algo = new TMutable();
|
||||
foreach (var (assign, load) in _auto_props)
|
||||
foreach (var (name, assign, load) in _auto_props)
|
||||
assign(algo);
|
||||
_descriptor.Accessor.SetValue(value, algo);
|
||||
foreach (var sd in _child_algo_selector_data) {
|
||||
if (sd.selector.selected_algo != null)
|
||||
sd.set_order(value, sd.selector.selected_algo.created_assigned());
|
||||
}
|
||||
assign_to_internal(algo);
|
||||
}
|
||||
|
||||
public void set_permissions(AlgoEditingPermissions editing_permissions) {
|
||||
@@ -217,26 +218,23 @@ namespace mbts.spark {
|
||||
public void load_from(AlgoDefi value) {
|
||||
if (value.CustomCase != order_type)
|
||||
return;
|
||||
using (_def_change_notifier.defer_execution()) {
|
||||
foreach (var sd in _child_algo_selector_data) {
|
||||
var inner_def = sd.get_order(value);
|
||||
if (inner_def.HasValue) {
|
||||
if (security.HasValue) {
|
||||
inner_def = OrderDataHelper.fit_to_type(inner_def.Value, security.Value.ContractCase);
|
||||
if (!inner_def.HasValue)
|
||||
continue;
|
||||
var algom = _descriptor.Accessor.GetValue(value.CloneToMutable()) as TMutable ?? new TMutable();
|
||||
Disposable.replace(ref _pending_load,
|
||||
new CompositeDisposable(
|
||||
ObservableAutoWrapper.connect_item_actions(_child_algo_selector_data.AsCollection(), sd => {
|
||||
var inner_def = sd.get_order(value);
|
||||
if (inner_def.HasValue) {
|
||||
if (security.HasValue) {
|
||||
inner_def = OrderDataHelper.fit_to_type(inner_def.Value, security.Value.ContractCase);
|
||||
if (!inner_def.HasValue)
|
||||
return;
|
||||
}
|
||||
var vm = sd.selector.algos.GetOrAdd(OrderDataHelper.get_shallow_factory_key(inner_def.Value), k => create(k, inner_def.Value.display_name(), inner_def.Value.CustomCase));
|
||||
vm.load_from(inner_def.Value);
|
||||
sd.selector.selected_algo = vm;
|
||||
}
|
||||
var vm = sd.selector.algos.GetOrAdd(OrderDataHelper.get_shallow_factory_key(inner_def.Value), k => create(k, inner_def.Value.display_name(), inner_def.Value.CustomCase));
|
||||
vm.load_from(inner_def.Value);
|
||||
sd.selector.selected_algo = vm;
|
||||
}
|
||||
}
|
||||
var algom = _descriptor.Accessor.GetValue(value.CloneToMutable()) as TMutable ?? new TMutable();
|
||||
var algoi = _create_immutable(algom);
|
||||
foreach (var (assign, load) in _auto_props)
|
||||
load(algom);
|
||||
load_from_internal(algoi);
|
||||
}
|
||||
}),
|
||||
ObservableAutoWrapper.connect_item_actions(_auto_props.AsCollection(), p => p.load(algom))));
|
||||
}
|
||||
|
||||
public virtual async Task<AlgoDefi?> load_from_async(AlgoDefi defi) {
|
||||
@@ -254,92 +252,72 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
var algom = _descriptor.Accessor.GetValue(defi.CloneToMutable()) as TMutable ?? new TMutable();
|
||||
var algoi = _create_immutable(algom);
|
||||
foreach (var (assign, load) in _auto_props)
|
||||
foreach (var (name, assign, load) in _auto_props)
|
||||
load(algom);
|
||||
load_from_internal(algoi);
|
||||
return defi;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Task on_load_from_async(AlgoDefi defi) => Task.CompletedTask;
|
||||
protected virtual void assign_to_internal(TMutable value) { }
|
||||
protected virtual void load_from_internal(TImmutable value) { }
|
||||
|
||||
protected void raise_definition_changed() =>
|
||||
_def_change_notifier.execute();
|
||||
|
||||
protected virtual void validate() { }
|
||||
|
||||
protected virtual void on_permissions_changed() {
|
||||
foreach (var vm in child_algo_selectors.SelectMany(s => s.algos.AsCollection()))
|
||||
vm.set_permissions(editing_permissions);
|
||||
}
|
||||
|
||||
protected void raise_definition_changed() =>
|
||||
_def_change_notifier.execute();
|
||||
|
||||
protected virtual void validate() { }
|
||||
|
||||
protected override void Dispose(bool disposing) =>
|
||||
Disposable.dispose(ref _resources);
|
||||
|
||||
#region Chidl Algo Selectors (add/remove/reset)
|
||||
protected IAlgoSelector add_selector(string vm_prop_name, FieldDescriptor inner_desc) {
|
||||
return add_selector(vm_prop_name, def => {
|
||||
if (_descriptor.Accessor.GetValue(def.CloneToMutable()) is TMutable mval) {
|
||||
if (inner_desc.Accessor.GetValue(mval) is AlgoDef inner_def)
|
||||
return new AlgoDefi(inner_def);
|
||||
}
|
||||
return null;
|
||||
}, (mdef, inner_def) => {
|
||||
if (_descriptor.Accessor.GetValue(mdef) is TMutable mval)
|
||||
inner_desc.Accessor.SetValue(mval, inner_def);
|
||||
});
|
||||
}
|
||||
|
||||
protected IAlgoSelector add_selector(string prop_name, Func<AlgoDefi, AlgoDefi?> get_order, Action<AlgoDef, AlgoDef> set_order) =>
|
||||
add_selector(prop_name, get_order, set_order, (s, e) => validate());
|
||||
|
||||
protected IAlgoSelector add_selector(string prop_name, Func<AlgoDefi, AlgoDefi?> get_order, Action<AlgoDef, AlgoDef> set_order, EventHandler<DataErrorsChangedEventArgs> on_errors_changed) {
|
||||
var rv = new AlgoSelector();
|
||||
_child_algo_selector_data[prop_name] = new AlgoSelectorData(prop_name, rv, get_order, set_order);
|
||||
return rv;
|
||||
}
|
||||
|
||||
protected IAlgoSelector add_selector(string prop_name, Func<TImmutable, AlgoDefi?> get_algo, Action<TMutable, AlgoDef> set_algo) {
|
||||
var rv = new AlgoSelector();
|
||||
_child_algo_selector_data[prop_name] = new AlgoSelectorData(prop_name, rv, def => _descriptor.Accessor.GetValue(def.CloneToMutable()) is TMutable mval ? get_algo(_create_immutable(mval)) : null,
|
||||
(def, child_def) => {
|
||||
if (_descriptor.Accessor.GetValue(def) is TMutable mval)
|
||||
set_algo(mval, child_def);
|
||||
});
|
||||
return rv;
|
||||
}
|
||||
|
||||
protected bool remove_selector(string prop_name) =>
|
||||
_child_algo_selector_data.Remove(prop_name);
|
||||
|
||||
/// TODO: maybe <see cref="_child_algo_selector_data"/> should just be a protected field?
|
||||
/// Conversely, it is nice to hide these sort of implementation-specific details from inheriting classes.
|
||||
/// Fwiw, as of 2/12/2019, the only class that needs the ability to add/remove selectors dynamically is <see cref="DynamicOrderVM"/>.
|
||||
/// The other implementations simply add a whatever selectors they need in their constructor.
|
||||
protected IDisposable reset_selectors() {
|
||||
var to_remove = _child_algo_selector_data.Keys.ToList();
|
||||
_child_algo_selector_data.CollectionChanged += on_change;
|
||||
void on_change(object sender, NotifyCollectionChangedEventArgs e) {
|
||||
switch (e.Action) {
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
throw new InvalidOperationException($"{nameof(_child_algo_selector_data)} was reset; unable to continue processing incremental changes.");
|
||||
default:
|
||||
// catches Replace/Remove/Add ops (Add ops account for cases where the item was effectively replaced by a remove/re-add)
|
||||
if (e.NewItems != null) {
|
||||
foreach (AlgoSelectorData sd in e.NewItems)
|
||||
to_remove.Remove(sd.prop_name);
|
||||
}
|
||||
break;
|
||||
#region Child Algo Selectors
|
||||
protected virtual IEnumerable<ChildAlgoData> get_selector_datum() =>
|
||||
OrderDataHelper.get_child_algo_data(order_type);
|
||||
|
||||
protected void sync_selectors() {
|
||||
using (reset()) {
|
||||
foreach (var c in get_selector_datum()) {
|
||||
_children_vm[c.name] = mk_selector(c.name, new Func<AlgoDefi, AlgoDefi?>(defi => {
|
||||
var mchild = c.get_child(defi.CloneToMutable());
|
||||
if (mchild != null)
|
||||
return new AlgoDefi(mchild);
|
||||
return null;
|
||||
}),
|
||||
c.assign_child);
|
||||
}
|
||||
_populate_selectors();
|
||||
}
|
||||
// local methods
|
||||
IDisposable reset() {
|
||||
var to_remove = _child_algo_selector_data.Keys.ToList();
|
||||
_child_algo_selector_data.CollectionChanged += on_change;
|
||||
void on_change(object sender, NotifyCollectionChangedEventArgs e) {
|
||||
switch (e.Action) {
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
throw new InvalidOperationException($"{nameof(_child_algo_selector_data)} was reset; unable to continue processing incremental changes.");
|
||||
default:
|
||||
// catches Replace/Remove/Add ops (Add ops account for cases where the item was effectively replaced by a remove/re-add)
|
||||
if (e.NewItems != null) {
|
||||
foreach (AlgoSelectorData sd in e.NewItems)
|
||||
to_remove.Remove(sd.prop_name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Disposable.Create(() => {
|
||||
_child_algo_selector_data.CollectionChanged -= on_change;
|
||||
foreach (var id in to_remove)
|
||||
_child_algo_selector_data.Remove(id);
|
||||
});
|
||||
}
|
||||
IAlgoSelector mk_selector(string prop_name, Func<AlgoDefi, AlgoDefi?> get_order, Action<AlgoDef, AlgoDef> set_order) {
|
||||
var rv = new AlgoSelector();
|
||||
_child_algo_selector_data[prop_name] = new AlgoSelectorData(prop_name, rv, get_order, set_order);
|
||||
return rv;
|
||||
}
|
||||
return Disposable.Create(() => {
|
||||
_child_algo_selector_data.CollectionChanged -= on_change;
|
||||
foreach (var id in to_remove)
|
||||
_child_algo_selector_data.Remove(id);
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -347,7 +325,7 @@ namespace mbts.spark {
|
||||
protected Property<T> add_property<T>(string prop_name, int target_field_number, T init_value = default) {
|
||||
var prop = backing(prop_name, init_value);
|
||||
var field_desc = new TMutable().Descriptor.FindFieldByNumber(target_field_number);
|
||||
_auto_props.Add((mval => {
|
||||
_auto_props.Add((prop_name, mval => {
|
||||
if (mval != null)
|
||||
field_desc.Accessor.SetValue(mval, prop.Value);
|
||||
}, mval => {
|
||||
@@ -356,22 +334,12 @@ namespace mbts.spark {
|
||||
return prop;
|
||||
}
|
||||
|
||||
protected UserProperty<T> add_user_property<T>(string prop_name, int target_field_number, T init_value = default) {
|
||||
var prop = user_backing(prop_name, init_value);
|
||||
var field_desc = new TMutable().Descriptor.FindFieldByNumber(target_field_number);
|
||||
add_property(
|
||||
mdef => ConvertUtils.try_convert<T>(field_desc.Accessor.GetValue(mdef)),
|
||||
(mdef, val) => field_desc.Accessor.SetValue(mdef, val),
|
||||
val => prop.bypass_setter(val),
|
||||
() => prop.Value);
|
||||
return prop;
|
||||
}
|
||||
|
||||
protected Property<TImmutableValue> add_property<TMutableValue, TImmutableValue>(string prop_name, int target_field_number, TImmutableValue init_value = default) where TMutableValue : class, IMessage, new() where TImmutableValue : IImmutableMessage {
|
||||
var prop = backing(prop_name, init_value);
|
||||
var field_desc = new TMutable().Descriptor.FindFieldByNumber(target_field_number);
|
||||
var immutable_factory = OrderTypeVMUtils.immutable_factory<TMutableValue, TImmutableValue>();
|
||||
add_property<TMutableValue>(
|
||||
prop_name,
|
||||
mdef => field_desc.Accessor.GetValue(mdef) as TMutableValue,
|
||||
(mdef, mval) => field_desc.Accessor.SetValue(mdef, mval),
|
||||
mval => prop.set(immutable_factory(mval ?? new TMutableValue())),
|
||||
@@ -383,15 +351,36 @@ namespace mbts.spark {
|
||||
return prop;
|
||||
}
|
||||
|
||||
protected void add_property<TValue>(Func<TMutable, TValue> get_from_def, Action<TMutable, TValue> assign_to_def, Action<TValue> set_local, Func<TValue> get_local) =>
|
||||
_auto_props.Add((def => {
|
||||
protected void add_property<TValue>(string prop_name, Func<TMutable, TValue> get_from_def, Action<TMutable, TValue> assign_to_def, Action<TValue> set_local, Func<TValue> get_local) =>
|
||||
_auto_props.Add((
|
||||
prop_name,
|
||||
def => {
|
||||
assign_to_def(def, get_local());
|
||||
}, def => {
|
||||
set_local(get_from_def(def));
|
||||
}));
|
||||
|
||||
protected virtual void on_model_internal(OrdersModel model) { }
|
||||
protected virtual void on_security_md_internal(SecurityMD? security_md) { }
|
||||
protected void add_property(string prop_name, Action<TMutable> assign, Action<TMutable> load) =>
|
||||
_auto_props[prop_name] = (prop_name, assign, load);
|
||||
|
||||
protected UserProperty<T> add_user_property<T>(string prop_name, int target_field_number, T init_value = default) {
|
||||
var prop = user_backing(prop_name, init_value);
|
||||
var field_desc = new TMutable().Descriptor.FindFieldByNumber(target_field_number);
|
||||
add_property(
|
||||
prop_name,
|
||||
mdef => ConvertUtils.try_convert<T>(field_desc.Accessor.GetValue(mdef)),
|
||||
(mdef, val) => field_desc.Accessor.SetValue(mdef, val),
|
||||
val => prop.bypass_setter(val),
|
||||
() => prop.Value);
|
||||
return prop;
|
||||
}
|
||||
|
||||
protected void remove_property(string prop_name) =>
|
||||
_auto_props.Remove(prop_name);
|
||||
#endregion
|
||||
|
||||
protected virtual void on_model_internal() { }
|
||||
protected virtual void on_security_md_internal() { }
|
||||
|
||||
// model
|
||||
void IObserver<OrdersModel>.OnNext(OrdersModel model) {
|
||||
@@ -402,36 +391,39 @@ namespace mbts.spark {
|
||||
return;
|
||||
}
|
||||
this.model = model;
|
||||
on_model_internal(model);
|
||||
_reset();
|
||||
using (_def_change_notifier.defer_execution()) {
|
||||
on_model_internal();
|
||||
sync_selectors();
|
||||
}
|
||||
}
|
||||
void IObserver<OrdersModel>.OnError(Exception error) { }
|
||||
void IObserver<OrdersModel>.OnCompleted() { }
|
||||
// security md
|
||||
void IObserver<SecurityMD?>.OnNext(SecurityMD? security_md) {
|
||||
this.security_md = security_md;
|
||||
on_security_md_internal(security_md);
|
||||
_reset();
|
||||
using (_def_change_notifier.defer_execution()) {
|
||||
on_security_md_internal();
|
||||
_populate_selectors();
|
||||
}
|
||||
}
|
||||
void IObserver<SecurityMD?>.OnError(Exception error) { }
|
||||
void IObserver<SecurityMD?>.OnCompleted() { }
|
||||
|
||||
IDisposable _hook;
|
||||
void _reset() {
|
||||
IDisposable _selector_data_hooks;
|
||||
void _populate_selectors() {
|
||||
if (model != null && security.HasValue) {
|
||||
var algo_data = model.factory.default_algos(security.Value);
|
||||
var default_algo = new AlgoKey(OrderDataHelper.get_default_algo(OrderDataHelper.get_algo_format(security.Value.ContractCase)));
|
||||
foreach (var sd in _child_algo_selectors)
|
||||
sd.load_algo_data_recursive(algo_data, default_algo);
|
||||
Disposable.replace(ref _hook, ObservableAutoWrapper.connect_item_hooks(child_algo_selectors, s => {
|
||||
s.load_algo_data_recursive(algo_data, default_algo);
|
||||
return Disposable.empty;
|
||||
}));
|
||||
Disposable.replace(
|
||||
ref _selector_data_hooks,
|
||||
ObservableAutoWrapper.connect_item_hooks(child_algo_selectors, s => {
|
||||
s.load_algo_data_recursive(algo_data, default_algo);
|
||||
return Disposable.empty;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<OrdersModel> observer) => throw new NotImplementedException();
|
||||
#endregion
|
||||
}
|
||||
|
||||
struct AlgoSelectorData {
|
||||
@@ -521,4 +513,4 @@ namespace mbts.spark {
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,6 @@ namespace mbts.spark {
|
||||
class SpreaderOrderVM : OrderTypeVMBase<SpreaderDef, SpreaderDefi>, IAlgoVM, ILazyAlgo, IObserver<SecurityMD?>, IObserver<OrdersModel> {
|
||||
readonly Property<SpreaderDefi?> _template;
|
||||
readonly StrategyEditorWrapper _strategy_editor_wrapper;
|
||||
OrdersModel _model;
|
||||
SecurityMD? _security_md;
|
||||
IDisposable _strategy_sub;
|
||||
|
||||
public SpreaderOrderVM(AlgoKey key, string display_name, AlgoEditingPermissions editing_permissions)
|
||||
@@ -23,7 +21,38 @@ namespace mbts.spark {
|
||||
_template = backing<SpreaderDefi?>(nameof(template));
|
||||
/// <see cref="_strategy_editor_wrapper"/> depends on <see cref="_template"/> (i.e. initialize the latter first).
|
||||
_strategy_editor_wrapper = new StrategyEditorWrapper(this);
|
||||
concat_disposable(_strategy_editor_wrapper, _template.Subscribe(vals => validate()));
|
||||
_resources.AddRange(_strategy_editor_wrapper, _template.Subscribe(vals => validate()));
|
||||
// strategy
|
||||
add_property(
|
||||
nameof(SpreaderDef.ExecutionStrategy),
|
||||
mdef => mdef.ExecutionStrategy,
|
||||
(mdef, val) => mdef.ExecutionStrategy = val,
|
||||
val => {
|
||||
if (template.HasValue) {
|
||||
template = template.Value.with(execution_strategy: val);
|
||||
} else {
|
||||
var mdef = new SpreaderDef {
|
||||
Security = security?.CloneToMutable(),
|
||||
ExecutionStrategy = val
|
||||
};
|
||||
template = new SpreaderDefi(mdef);
|
||||
}
|
||||
},
|
||||
() => strategy_editor?.get_strategy().CloneToMutable());
|
||||
// security
|
||||
add_property(
|
||||
nameof(SpreaderDef.Security),
|
||||
mdef => mdef.Security,
|
||||
(mdef, val) => mdef.Security = val,
|
||||
val => {
|
||||
if (template.HasValue) {
|
||||
template = template.Value.with(security: val);
|
||||
} else {
|
||||
var mdef = new SpreaderDef { Security = val };
|
||||
template = new SpreaderDefi(mdef);
|
||||
}
|
||||
},
|
||||
() => security?.CloneToMutable());
|
||||
validate();
|
||||
}
|
||||
|
||||
@@ -43,12 +72,6 @@ namespace mbts.spark {
|
||||
}
|
||||
public bool is_loaded { get; private set; }
|
||||
|
||||
protected override void assign_to_internal(SpreaderDef value) =>
|
||||
value.MergeFrom(template.HasValue ? template.Value.CloneToMutable() : new SpreaderDef());
|
||||
|
||||
protected override void load_from_internal(SpreaderDefi value) =>
|
||||
_template.set(value);
|
||||
|
||||
protected override void validate() {
|
||||
base.validate();
|
||||
if (!template.HasValue)
|
||||
@@ -56,26 +79,15 @@ namespace mbts.spark {
|
||||
else clear_error(nameof(template));
|
||||
}
|
||||
|
||||
void IObserver<SecurityMD?>.OnCompleted() { }
|
||||
void IObserver<SecurityMD?>.OnError(Exception error) { }
|
||||
void IObserver<SecurityMD?>.OnNext(SecurityMD? security_md) {
|
||||
_security_md = security_md;
|
||||
_reset_strategy_sub();
|
||||
}
|
||||
|
||||
void IObserver<OrdersModel>.OnCompleted() { }
|
||||
void IObserver<OrdersModel>.OnError(Exception error) { }
|
||||
void IObserver<OrdersModel>.OnNext(OrdersModel model) {
|
||||
_model = model;
|
||||
_reset_strategy_sub();
|
||||
}
|
||||
protected override void on_security_md_internal() => _reset_strategy_sub();
|
||||
protected override void on_model_internal() => _reset_strategy_sub();
|
||||
|
||||
void _reset_strategy_sub() {
|
||||
Disposable.dispose(ref _strategy_sub);
|
||||
if (_model != null) {
|
||||
if (_security_md.HasValue) {
|
||||
_strategy_sub = _model.spreader.security_strategy(_security_md.Value.security.Id)
|
||||
.Select(strategy => new SpreaderDefi?(_model.factory.spreader_orderi(_security_md.Value.security, strategy)))
|
||||
if (model != null) {
|
||||
if (security_md.HasValue) {
|
||||
_strategy_sub = model.spreader.security_strategy(security_md.Value.security.Id)
|
||||
.Select(strategy => new SpreaderDefi?(model.factory.spreader_orderi(security_md.Value.security, strategy)))
|
||||
.OnDispatcher()
|
||||
.Subscribe(s => {
|
||||
is_loaded = true;
|
||||
@@ -115,7 +127,7 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
OrdersModel _model => _owner._model;
|
||||
OrdersModel _model => _owner.model;
|
||||
SpreaderDefi? _template { get => _owner.template; set => _owner.template = value; }
|
||||
|
||||
public void Dispose() {
|
||||
|
||||
@@ -31,11 +31,6 @@ namespace mbts.spark {
|
||||
|
||||
public ContractSpecificationImmutable? spec => _security_md.spec_or_default();
|
||||
|
||||
protected override void load_from_internal(StopLimitDefi value) {
|
||||
// TODO: the StopLimitOrderDefinition, StopLimitOrderDefinitionImmutable needs relative vs absolute prices to function as a useful template (talk to Pedro)
|
||||
_stop_price.bypass_setter(value.StopPrice);
|
||||
}
|
||||
|
||||
// no need to constantly poll the md, just check the inline props when the popup opens, and right before sending an order
|
||||
// (if the user never looks at the popup, assume that the bid_price will be used)
|
||||
// TODO: add settings so the trigger-price can default to something relative to the md, e.g "bid-price" or "ask-price - nticks" or "best-bid-price with a minimum size of 10"
|
||||
|
||||
@@ -16,14 +16,26 @@ namespace mbts.spark {
|
||||
|
||||
public TimeBoxOrderVM(AlgoKey key, string display_name, AlgoEditingPermissions editing_permissions)
|
||||
: base(key, display_name, editing_permissions) {
|
||||
add_property(def => def.Start, (def, val) => def.Start = val, val => absolute_start = TimeTextTuple.FromDateTime(val.ToDateTime()), () => {
|
||||
_validate(out DateTime? start_time, out DateTime? _);
|
||||
return start_time.GetValueOrDefault().ToUniversalTime().ToGoogleTime();
|
||||
});
|
||||
add_property(def => def.Stop, (def, val) => def.Stop = val, val => absolute_stop = TimeTextTuple.FromDateTime(val.ToDateTime()), () => {
|
||||
_validate(out DateTime? _, out DateTime? stop_time);
|
||||
return stop_time.GetValueOrDefault().ToUniversalTime().ToGoogleTime();
|
||||
});
|
||||
// start
|
||||
add_property(
|
||||
nameof(TimeBoxOrderDefinition.Start),
|
||||
def => def.Start,
|
||||
(def, val) => def.Start = val,
|
||||
val => absolute_start = TimeTextTuple.FromDateTime(val.ToDateTime()),
|
||||
() => {
|
||||
_validate(out DateTime? start_time, out DateTime? _);
|
||||
return start_time.GetValueOrDefault().ToUniversalTime().ToGoogleTime();
|
||||
});
|
||||
// stop
|
||||
add_property(
|
||||
nameof(TimeBoxOrderDefinition.Stop),
|
||||
def => def.Stop,
|
||||
(def, val) => def.Stop = val,
|
||||
val => absolute_stop = TimeTextTuple.FromDateTime(val.ToDateTime()),
|
||||
() => {
|
||||
_validate(out DateTime? _, out DateTime? stop_time);
|
||||
return stop_time.GetValueOrDefault().ToUniversalTime().ToGoogleTime();
|
||||
});
|
||||
validate();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.api.orderman;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
@@ -31,13 +30,59 @@ namespace mbts.spark {
|
||||
_is_priceout_checked = create_user_prop(nameof(is_priceout_checked), false);
|
||||
_is_timeout_checked = create_user_prop(nameof(is_timeout_checked), false);
|
||||
_timeout_secs = create_user_prop(nameof(timeout_secs), 10);
|
||||
add_property(nameof(TriggerDef), assign, load);
|
||||
validate();
|
||||
//
|
||||
// local methods
|
||||
UserProperty<T> create_user_prop<T>(string name, T default_value = default) {
|
||||
var rv = user_backing(name, default_value);
|
||||
rv.Changed += validation_callback;
|
||||
return rv;
|
||||
}
|
||||
void assign(TriggerDef trigger_def) {
|
||||
trigger_def.TriggerAction = new TriggerAction();
|
||||
if (security.HasValue)
|
||||
trigger_def.TriggerAction.SecurityId = security.Value.Id;
|
||||
trigger_def.TriggerCondition = new TriggerCondition() {
|
||||
PriceType = trigger_price_type,
|
||||
AbsolutePriceTrigger = new AbsolutePriceTrigger() { TriggerQuantity = new TriggerQuantity() },
|
||||
};
|
||||
trigger_def.ExitCondition = new TriggerExitCondition {
|
||||
ConditionalCancel = is_priceout_checked,
|
||||
TimeoutEnabled = is_timeout_checked,
|
||||
TimeoutSeconds = (uint)(timeout_secs)
|
||||
};
|
||||
switch (order_price_case) {
|
||||
case OrderpriceOneofCase.Price:
|
||||
// 'fixed price' is no longer supported (ca. 03/04/2019)
|
||||
goto case OrderpriceOneofCase.Payup;
|
||||
case OrderpriceOneofCase.Payup:
|
||||
trigger_def.TriggerAction.Price = double.NaN;
|
||||
trigger_def.TriggerAction.Payup = new TriggerPayupPrice {
|
||||
PayupTicks = payup_ticks,
|
||||
PayupPriceType = payup_price_type
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
void load(TriggerDef trigger_defi) {
|
||||
if (trigger_defi.TriggerCondition is TriggerCondition c) {
|
||||
trigger_price_type = c.PriceType;
|
||||
}
|
||||
if (trigger_defi.ExitCondition is TriggerExitCondition ec) {
|
||||
is_priceout_checked = ec.ConditionalCancel;
|
||||
is_timeout_checked = ec.TimeoutEnabled;
|
||||
if (ec.TimeoutSeconds < int.MaxValue)
|
||||
timeout_secs = (int)ec.TimeoutSeconds;
|
||||
}
|
||||
if (trigger_defi.TriggerAction is TriggerAction a) {
|
||||
if (a.Payup is TriggerPayupPrice pp) {
|
||||
order_price_case = OrderpriceOneofCase.Payup;
|
||||
fixed_price = double.NaN;
|
||||
payup_ticks = pp.PayupTicks;
|
||||
payup_price_type = pp.PayupPriceType;
|
||||
}
|
||||
}
|
||||
}
|
||||
void validation_callback<T>(T oldval, T newval) => validate();
|
||||
}
|
||||
|
||||
@@ -56,68 +101,20 @@ namespace mbts.spark {
|
||||
public bool is_timeout_checked { get => _is_timeout_checked.Value; set => _is_timeout_checked.Value = value; }
|
||||
public int timeout_secs { get => _timeout_secs.Value; set => _timeout_secs.Value = value; }
|
||||
|
||||
protected override void assign_to_internal(TriggerDef trigger_def) {
|
||||
trigger_def.TriggerAction = new TriggerAction();
|
||||
if (security.HasValue)
|
||||
trigger_def.TriggerAction.SecurityId = security.Value.Id;
|
||||
trigger_def.TriggerCondition = new TriggerCondition() {
|
||||
PriceType = trigger_price_type,
|
||||
AbsolutePriceTrigger = new AbsolutePriceTrigger() { TriggerQuantity = new TriggerQuantity() },
|
||||
};
|
||||
trigger_def.ExitCondition = new TriggerExitCondition {
|
||||
ConditionalCancel = is_priceout_checked,
|
||||
TimeoutEnabled = is_timeout_checked,
|
||||
TimeoutSeconds = (uint)(timeout_secs)
|
||||
};
|
||||
switch (order_price_case) {
|
||||
case OrderpriceOneofCase.Price:
|
||||
// should already be null, setting it here for clarity
|
||||
trigger_def.TriggerAction.Payup = null;
|
||||
trigger_def.TriggerAction.Price = fixed_price;
|
||||
break;
|
||||
case OrderpriceOneofCase.Payup:
|
||||
trigger_def.TriggerAction.Price = double.NaN;
|
||||
trigger_def.TriggerAction.Payup = new TriggerPayupPrice {
|
||||
PayupTicks = payup_ticks,
|
||||
PayupPriceType = payup_price_type
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void load_from_internal(TriggerDefi trigger_defi) {
|
||||
if (trigger_defi.HasTriggerCondition) {
|
||||
trigger_price_type = trigger_defi.TriggerCondition.PriceType;
|
||||
}
|
||||
if (trigger_defi.HasExitCondition) {
|
||||
var exit_condition = trigger_defi.ExitCondition;
|
||||
is_priceout_checked = exit_condition.ConditionalCancel;
|
||||
is_timeout_checked = exit_condition.TimeoutEnabled;
|
||||
if (exit_condition.TimeoutSeconds < int.MaxValue)
|
||||
timeout_secs = (int)exit_condition.TimeoutSeconds;
|
||||
}
|
||||
if (trigger_defi.HasTriggerAction) {
|
||||
var trigger_action = trigger_defi.TriggerAction;
|
||||
if (trigger_action.HasPayup) {
|
||||
order_price_case = OrderpriceOneofCase.Payup;
|
||||
fixed_price = double.NaN;
|
||||
var payup = trigger_action.Payup;
|
||||
payup_ticks = payup.PayupTicks;
|
||||
payup_price_type = payup.PayupPriceType;
|
||||
} else {
|
||||
order_price_case = OrderpriceOneofCase.Price;
|
||||
fixed_price = trigger_action.Price;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _refresh_md() {
|
||||
protected override void on_security_md_internal() {
|
||||
if (security.HasValue) {
|
||||
security_md.Value.md.get_bbo(ref _bbo_ref, out co2.NativePriceSize bid, out co2.NativePriceSize ask);
|
||||
_best_bid = bid.price;
|
||||
_best_ask = ask.price;
|
||||
security_md.Value.md.get_last_trade(ref _last_trade_ref, out co2.NativePriceSize ps, out DateTime ts, out ulong v);
|
||||
}
|
||||
if (double.IsNaN(fixed_price) || !_fixed_price.setter_called) {
|
||||
switch (trigger_price_type) {
|
||||
case TriggerPriceType.Bid: _fixed_price.bypass_setter(_best_bid); break;
|
||||
case TriggerPriceType.Ask: _fixed_price.bypass_setter(_best_ask); break;
|
||||
case TriggerPriceType.LastTradePrice: _fixed_price.bypass_setter(_last_trade_price); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void validate() {
|
||||
@@ -131,17 +128,6 @@ namespace mbts.spark {
|
||||
}
|
||||
assert(!is_timeout_checked || timeout_secs > 0, nameof(timeout_secs), "Please specify valid Timeout(secs).");
|
||||
}
|
||||
|
||||
protected override void on_security_md_internal(SecurityMD? security_md) {
|
||||
_refresh_md();
|
||||
if (double.IsNaN(fixed_price) || !_fixed_price.setter_called) {
|
||||
switch (trigger_price_type) {
|
||||
case TriggerPriceType.Bid: _fixed_price.bypass_setter(_best_bid); break;
|
||||
case TriggerPriceType.Ask: _fixed_price.bypass_setter(_best_ask); break;
|
||||
case TriggerPriceType.LastTradePrice: _fixed_price.bypass_setter(_last_trade_price); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,11 @@ using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.api.position;
|
||||
using bts.md;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
using mbts.collections;
|
||||
using mbts.config;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
@@ -16,8 +18,10 @@ using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Threading.Tasks;
|
||||
using OrderUpdate = bts.api.core.TableUpdate<(bts.api.SecurityImmutable? sec, bts.api.orderman.OrderRecordImmutable order), uint>;
|
||||
using OrderUpdatePiq = bts.api.core.TableUpdate<bts.api.orderman.OrderSecurity, uint>;
|
||||
|
||||
namespace mbts.spark {
|
||||
[Export]
|
||||
@@ -26,21 +30,26 @@ namespace mbts.spark {
|
||||
readonly OrderFactory _factory;
|
||||
readonly OrderService _service;
|
||||
readonly PositionService _position_service;
|
||||
readonly MBOService _mbo_service;
|
||||
readonly SpreaderModel _spreader;
|
||||
readonly ErrorReporter _error;
|
||||
readonly DynamicOrderHelper _dynamic_helper;
|
||||
readonly OrderActivitySummarizer _activity;
|
||||
readonly TableUpdateDemuxer<OrderRecordImmutable, uint, uint> _security_orders_demuxer;
|
||||
// we need a new one of these for the time being since the ladder is subscribing to orders with piq
|
||||
readonly TableUpdateDemuxer<OrderRecordImmutable, uint, uint> _security_orders_piq_demuxer;
|
||||
readonly TableUpdateDemuxer<PositionUnitImmutable, int, uint> _security_positions_demuxer;
|
||||
IDisposable _dispose;
|
||||
static readonly bool _piq_enabled = Configuration.get<bts.config.toggles.PlacementInQueue>().enabled;
|
||||
|
||||
[ImportingConstructor]
|
||||
OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service,
|
||||
OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service, MBOService mbo_service,
|
||||
SpreaderModel spreader, DynamicOrderHelper dynamic_helper, AdminModel admin_model, IAlgoStudioModel algo_model, SecurityStore sstore, ErrorReporter error) {
|
||||
_bus = bus;
|
||||
_factory = factory;
|
||||
_service = service;
|
||||
_position_service = position_service;
|
||||
_mbo_service = mbo_service;
|
||||
_spreader = spreader;
|
||||
_error = error;
|
||||
_dynamic_helper = dynamic_helper;
|
||||
@@ -48,13 +57,34 @@ namespace mbts.spark {
|
||||
this.algo_model = algo_model;
|
||||
var scheduler = ThreadPoolScheduler.Instance;
|
||||
var interval = TimeSpan.FromMilliseconds(100);
|
||||
on_orders =
|
||||
on_orders =
|
||||
_service
|
||||
.subscribei_orders()
|
||||
.Naggle(interval, scheduler)
|
||||
.PermissionFilter(admin_model.on_admin_data)
|
||||
.join(sstore, order => order.security_key())
|
||||
.Table().Cache();
|
||||
.Table()
|
||||
.Cache();
|
||||
|
||||
if (_piq_enabled) {
|
||||
on_orders_with_piq = on_orders
|
||||
.LeftJoin<
|
||||
(SecurityImmutable?, OrderRecordImmutable),
|
||||
OrderPIQImmutable,
|
||||
OrderSecurity,
|
||||
uint
|
||||
>(mbo_service.subscribei_piq().Table(),
|
||||
si_ort => si_ort.Item2.Key,
|
||||
p => p.OrderId,
|
||||
(si_ort, r, p) => new OrderSecurity(si_ort.Item1, si_ort.Item2, p.Position),
|
||||
(si_ort, r) => new OrderSecurity(si_ort.Item1, si_ort.Item2, null),
|
||||
osp => osp.order.Key);
|
||||
} else {
|
||||
on_orders_with_piq = on_orders
|
||||
.Select((ft) => new OrderSecurity(ft.Item1, ft.Item2, null), os => os.order.Key)
|
||||
.Replay(1);
|
||||
}
|
||||
|
||||
on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).PermissionFilter(admin_model.on_admin_data).Table().Cache();
|
||||
working_orders =
|
||||
on_orders.Scan(
|
||||
@@ -62,6 +92,13 @@ namespace mbts.spark {
|
||||
(workers, table) => _update_working_orders(workers, table))
|
||||
.Replay(1).RefCount();
|
||||
|
||||
working_orders_with_piq =
|
||||
on_orders_with_piq.Scan(
|
||||
ImmutableDictionary<uint, OrderSecurity>.Empty,
|
||||
(workers, table) => _update_working_orders(workers, table))
|
||||
.Replay(1).RefCount();
|
||||
|
||||
|
||||
on_order_securities =
|
||||
on_orders
|
||||
.as_iincremental()
|
||||
@@ -70,6 +107,7 @@ namespace mbts.spark {
|
||||
|
||||
_activity = new OrderActivitySummarizer(bus, on_orders);
|
||||
_security_orders_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
|
||||
_security_orders_piq_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
|
||||
_security_positions_demuxer = new TableUpdateDemuxer<PositionUnitImmutable, int, uint>(o => o.RecordId, p => p.SecurityId);
|
||||
var algo_meta_join = algo_model.definitions.LeftJoin<AlgoData, AlgoDefinitionMetadata, (AlgoData algo, AlgoDefinitionMetadata? meta), AlgoKey>(
|
||||
algo_model.metadata_service.subscribe_metadata().Table(m => m.id),
|
||||
@@ -104,6 +142,7 @@ namespace mbts.spark {
|
||||
on_fill_tuples = fill_order_tuples_join;
|
||||
_dispose = Disposable.Create(
|
||||
on_orders.Select(t => t.order, o => o.Key).Subscribe(_security_orders_demuxer),
|
||||
on_orders_with_piq.RefCount().Select(t => t.order, o => o.Key).Subscribe(_security_orders_piq_demuxer),
|
||||
on_positions.Subscribe(_security_positions_demuxer),
|
||||
order_secs_algo_meta_join.Connect(),
|
||||
algo_meta_join.Connect(),
|
||||
@@ -121,11 +160,14 @@ namespace mbts.spark {
|
||||
public SpreaderModel spreader => _spreader;
|
||||
public DynamicOrderHelper dynamic_helper => _dynamic_helper;
|
||||
public IObservable<OrderUpdate> on_orders { get; }
|
||||
public IConnectableObservable<OrderUpdatePiq> on_orders_with_piq { get; }
|
||||
public IObservable<TableUpdate<OrderSecurityAlgoMeta, uint>> on_order_tuples { get; }
|
||||
public IObservable<IIncrementalUpdate<SecurityImmutable, uint>> on_order_securities { get; }
|
||||
public IObservable<TableUpdate<FillTuple, string>> on_fill_tuples { get; }
|
||||
public IObservable<IReadOnlyDictionary<uint,OrderSecurity>> working_orders { get; }
|
||||
public IObservable<IReadOnlyDictionary<uint, OrderSecurity>> working_orders_with_piq { get; }
|
||||
public IObservable<TableUpdate<PositionUnitImmutable, int>> on_positions { get; }
|
||||
|
||||
public AdminModel admin_model { get; }
|
||||
public IAlgoStudioModel algo_model { get; }
|
||||
|
||||
@@ -197,8 +239,30 @@ namespace mbts.spark {
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
|
||||
static ImmutableDictionary<uint, OrderSecurity>
|
||||
_update_working_orders(ImmutableDictionary<uint, OrderSecurity> target, OrderUpdate table) {
|
||||
static ImmutableDictionary<uint, OrderSecurity> _update_working_orders(ImmutableDictionary<uint, OrderSecurity> target, OrderUpdate table) {
|
||||
var clone = target.ToBuilder();
|
||||
foreach (var o in table.removed) {
|
||||
if (o.order.is_active()) {
|
||||
clone.Remove(o.order.Key);
|
||||
}
|
||||
}
|
||||
foreach (var o in table.changes) {
|
||||
if (o.Old.order.is_active()) {
|
||||
clone.Remove(o.Old.order.Key);
|
||||
}
|
||||
if (o.New.order.is_active()) {
|
||||
clone.Add(o.New.order.Key, o.New);
|
||||
}
|
||||
}
|
||||
foreach (var o in table.added) {
|
||||
if (o.order.is_active()) {
|
||||
clone.Add(o.order.Key, o);
|
||||
}
|
||||
}
|
||||
return clone.ToImmutableDictionary();
|
||||
}
|
||||
|
||||
static ImmutableDictionary<uint, OrderSecurity> _update_working_orders(ImmutableDictionary<uint, OrderSecurity> target, OrderUpdatePiq table) {
|
||||
var clone = target.ToBuilder();
|
||||
foreach (var o in table.removed) {
|
||||
if (o.order.is_active()) {
|
||||
@@ -222,6 +286,7 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// usinig new OrdersModel extensions to reduce the amount of code change needed to switch uis over to the new OrdersModel,
|
||||
// without changing any code in the actual new OrdersModel class.
|
||||
static class OrdersModelExtensions {
|
||||
@@ -285,7 +350,7 @@ namespace mbts.spark {
|
||||
// DO NOT REMOVE (END)
|
||||
|
||||
public static IObservable<ITableUpdateTransform<OrderRecordImmutable, uint>> on_security_root_orders(this OrdersModel orders_model, uint security_key) =>
|
||||
orders_model.on_security_orders(security_key).Scan(new RootOrderData(), (rod, table) => {
|
||||
orders_model.on_security_orders(security_key).AddRemoveOnly().Scan(new RootOrderData(), (rod, table) => {
|
||||
// ** using HashSet/List pairs, rather than a Dictionary, because TableUpdateTransform calls for IReadOnlyLists, while Dictionary.Values only implements IReadOnlyCollection.
|
||||
// (this way, we avoid copying both added/removed Dictionary.Values to new Lists at the end, see comment at bottom of method if this is unclear) **
|
||||
var added_root_keys = new HashSet<uint>();
|
||||
|
||||
@@ -4,7 +4,7 @@ using System;
|
||||
|
||||
namespace mbts {
|
||||
[ProtoContract]
|
||||
class DiscreteStringPredicate : IDiscreteValuesPredicateBuilder<string>, IEquatable<DiscreteStringPredicate> {
|
||||
class DiscreteStringPredicate : IDiscreteValuesPredicateData<string>, IEquatable<DiscreteStringPredicate> {
|
||||
[ProtoMember(1)]
|
||||
string[] _values;
|
||||
[ProtoMember(2)]
|
||||
|
||||
@@ -4,7 +4,7 @@ using System;
|
||||
|
||||
namespace mbts {
|
||||
[ProtoContract]
|
||||
class DiscreteUIntPredicate : IDiscreteValuesPredicateBuilder<uint, uint?, uint[]>, IEquatable<DiscreteUIntPredicate> {
|
||||
class DiscreteUIntPredicate : IDiscreteValuesPredicateData<uint, uint?, uint[]>, IEquatable<DiscreteUIntPredicate> {
|
||||
[ProtoMember(1)]
|
||||
uint[] _values;
|
||||
[ProtoMember(2)]
|
||||
@@ -16,7 +16,7 @@ namespace mbts {
|
||||
public uint[] not_in_set { get => _get_set(true); set => _set(true, value); }
|
||||
|
||||
public DiscreteValuesOneofCase predicate_type_case => DiscreteValuesPredicateUtils.get_predicate_type_case(_values, _negated);
|
||||
public IPredicate<uint> create(object obj) => DiscreteValuesPredicateUtils.create(this);
|
||||
public IPredicate<uint> create() => DiscreteValuesPredicateUtils.create(this);
|
||||
|
||||
public bool Equals(DiscreteUIntPredicate other) => DiscreteValuesPredicateUtils.equals(this, other);
|
||||
public override bool Equals(object obj) => Equals(obj as DiscreteUIntPredicate);
|
||||
|
||||
@@ -8,14 +8,14 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts {
|
||||
interface IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> : IPredicateBuilder<T> {
|
||||
interface IDiscreteValuesPredicateData<T, TSingle, TMulti> {
|
||||
TSingle is_value { get; set; }
|
||||
TSingle not_value { get; set; }
|
||||
TMulti in_set { get; set; }
|
||||
TMulti not_in_set { get; set; }
|
||||
DiscreteValuesOneofCase predicate_type_case { get; }
|
||||
}
|
||||
interface IDiscreteValuesPredicateBuilder<T> : IDiscreteValuesPredicateBuilder<T, T, T[]> { }
|
||||
interface IDiscreteValuesPredicateData<T> : IDiscreteValuesPredicateData<T, T, T[]> { }
|
||||
|
||||
public enum DiscreteValuesOneofCase {
|
||||
None,
|
||||
@@ -32,8 +32,8 @@ namespace mbts {
|
||||
|
||||
namespace mbts.ui.settings {
|
||||
static class DiscreteValuesPredicateUtils {
|
||||
public static bool equals<T>(IDiscreteValuesPredicateBuilder<T> a, IDiscreteValuesPredicateBuilder<T> b) => a.safe_equals(b, _equals);
|
||||
static bool _equals<T>(IDiscreteValuesPredicateBuilder<T> a, IDiscreteValuesPredicateBuilder<T> b) {
|
||||
public static bool equals<T>(IDiscreteValuesPredicateData<T> a, IDiscreteValuesPredicateData<T> b) => a.safe_equals(b, _equals);
|
||||
static bool _equals<T>(IDiscreteValuesPredicateData<T> a, IDiscreteValuesPredicateData<T> b) {
|
||||
var dvoc = a.predicate_type_case;
|
||||
if (dvoc != b.predicate_type_case)
|
||||
return false;
|
||||
@@ -47,8 +47,8 @@ namespace mbts.ui.settings {
|
||||
}
|
||||
}
|
||||
|
||||
public static bool equals<T, TSingle, TMulti>(IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> a, IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> b) => a.safe_equals(b, _equals);
|
||||
public static bool _equals<T, TSingle, TMulti>(IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> a, IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> b) {
|
||||
public static bool equals<T, TSingle, TMulti>(IDiscreteValuesPredicateData<T, TSingle, TMulti> a, IDiscreteValuesPredicateData<T, TSingle, TMulti> b) => a.safe_equals(b, _equals);
|
||||
public static bool _equals<T, TSingle, TMulti>(IDiscreteValuesPredicateData<T, TSingle, TMulti> a, IDiscreteValuesPredicateData<T, TSingle, TMulti> b) {
|
||||
var dvoc = a.predicate_type_case;
|
||||
if (dvoc != b.predicate_type_case)
|
||||
return false;
|
||||
@@ -62,7 +62,7 @@ namespace mbts.ui.settings {
|
||||
}
|
||||
}
|
||||
|
||||
public static int hash<T>(IDiscreteValuesPredicateBuilder<T> a) {
|
||||
public static int hash<T>(IDiscreteValuesPredicateData<T> a) {
|
||||
switch (a.predicate_type_case) {
|
||||
case DiscreteValuesOneofCase.IsValue: return a.is_value.GetHashCode() % MathUtils.Primes.P_17191;
|
||||
case DiscreteValuesOneofCase.NotValue: return a.not_value.GetHashCode() % MathUtils.Primes.P_83987;
|
||||
@@ -72,7 +72,7 @@ namespace mbts.ui.settings {
|
||||
}
|
||||
}
|
||||
|
||||
public static int hash<T, TSingle, TMulti>(IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> a) {
|
||||
public static int hash<T, TSingle, TMulti>(IDiscreteValuesPredicateData<T, TSingle, TMulti> a) {
|
||||
switch (a.predicate_type_case) {
|
||||
case DiscreteValuesOneofCase.IsValue: return a.is_value.GetHashCode() % MathUtils.Primes.P_17191;
|
||||
case DiscreteValuesOneofCase.NotValue: return a.not_value.GetHashCode() % MathUtils.Primes.P_83987;
|
||||
@@ -88,7 +88,7 @@ namespace mbts.ui.settings {
|
||||
(negated ? DiscreteValuesOneofCase.NotValue : DiscreteValuesOneofCase.IsValue) :
|
||||
(negated ? DiscreteValuesOneofCase.NotInSet : DiscreteValuesOneofCase.IsInSet);
|
||||
|
||||
public static DiscreteValuesOneofCase get_predicate_type_case<T, TSingle, TMulti>(IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> predicate_builder) {
|
||||
public static DiscreteValuesOneofCase get_predicate_type_case<T, TSingle, TMulti>(IDiscreteValuesPredicateData<T, TSingle, TMulti> predicate_builder) {
|
||||
if (predicate_builder.is_value != null) return DiscreteValuesOneofCase.IsValue;
|
||||
if (predicate_builder.not_value != null) return DiscreteValuesOneofCase.NotValue;
|
||||
if (predicate_builder.in_set != null) return DiscreteValuesOneofCase.IsInSet;
|
||||
@@ -96,7 +96,7 @@ namespace mbts.ui.settings {
|
||||
return DiscreteValuesOneofCase.None;
|
||||
}
|
||||
|
||||
public static IPredicate<T> create<T>(IDiscreteValuesPredicateBuilder<T> predicate_builder) {
|
||||
public static IPredicate<T> create<T>(IDiscreteValuesPredicateData<T> predicate_builder) {
|
||||
switch (predicate_builder.predicate_type_case) {
|
||||
case DiscreteValuesOneofCase.IsValue: return PredicateBuilder.equals(predicate_builder.is_value);
|
||||
case DiscreteValuesOneofCase.NotValue: return PredicateBuilder.not_equals(predicate_builder.not_value);
|
||||
@@ -106,7 +106,7 @@ namespace mbts.ui.settings {
|
||||
}
|
||||
}
|
||||
|
||||
public static IPredicate<T> create<T>(IDiscreteValuesPredicateBuilder<T, T?, T[]> predicate_builder) where T : struct {
|
||||
public static IPredicate<T> create<T>(IDiscreteValuesPredicateData<T, T?, T[]> predicate_builder) where T : struct {
|
||||
switch (predicate_builder.predicate_type_case) {
|
||||
case DiscreteValuesOneofCase.IsValue: return PredicateBuilder.equals(predicate_builder.is_value.Value);
|
||||
case DiscreteValuesOneofCase.NotValue: return PredicateBuilder.not_equals(predicate_builder.not_value.Value);
|
||||
@@ -118,13 +118,13 @@ namespace mbts.ui.settings {
|
||||
}
|
||||
static class DiscreteValuesPredicateVMFactory {
|
||||
public static Dictionary<DiscreteValuesOneofCase, ITemplatedPropertyWrapperVM<TPredicate>> get_oneofs_value_type<T, TPredicate>(IEnumerable<T> items)
|
||||
where T : struct where TPredicate : IDiscreteValuesPredicateBuilder<T, T?, T[]> =>
|
||||
where T : struct where TPredicate : IDiscreteValuesPredicateData<T, T?, T[]> =>
|
||||
//
|
||||
get_oneofs<T, T?, T[], TPredicate>(identity_view_value_collection(items), val => val.Value, val => val, vals => vals ?? Enumerable.Empty<T>(), vals => vals?.ToArray() ?? new T[0], get_identity_view);
|
||||
|
||||
public static Dictionary<DiscreteValuesOneofCase, ITemplatedPropertyWrapperVM<TPredicate>> get_oneofs<T, TSingle, TMulti, TPredicate>(
|
||||
IObservableCollection<IViewValue<T>> items, Func<TSingle, T> from_single, Func<T, TSingle> to_single, Func<TMulti,IEnumerable<T>> from_multi, Func<IEnumerable<T>, TMulti> to_multi, Func<T, object> get_view)
|
||||
where TPredicate : IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> {
|
||||
where TPredicate : IDiscreteValuesPredicateData<T, TSingle, TMulti> {
|
||||
//
|
||||
return new Dictionary<DiscreteValuesOneofCase, ITemplatedPropertyWrapperVM<TPredicate>> {
|
||||
{ DiscreteValuesOneofCase.IsValue, PropertyWrapperVMUtils.create_selector_vm(
|
||||
@@ -153,14 +153,14 @@ namespace mbts.ui.settings {
|
||||
public static Dictionary<DiscreteValuesOneofCase, ITemplatedPropertyWrapperVM<TPredicate>> get_oneofs<T, TKey, TSingle, TMulti, TPredicate>(
|
||||
IEnumerable<T> items, Func<T, TKey> key, Func<TSingle, TKey> from_single, Func<TKey, TSingle> to_single, Func<TMulti, IEnumerable<TKey>> from_multi, Func<IEnumerable<TKey>, TMulti> to_multi)
|
||||
where TKey : struct, IEquatable<TKey>
|
||||
where TPredicate : IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> =>
|
||||
where TPredicate : IDiscreteValuesPredicateData<T, TSingle, TMulti> =>
|
||||
//
|
||||
get_oneofs<T, TKey, TSingle, TMulti, TPredicate>(RXCollection.from_enumerable(items.Select<T, IViewValue<T>>(v => new ViewValueTuple<T>(v, v))), key, from_single, to_single, from_multi, to_multi);
|
||||
|
||||
public static Dictionary<DiscreteValuesOneofCase, ITemplatedPropertyWrapperVM<TPredicate>> get_oneofs<T, TKey, TSingle, TMulti, TPredicate>(
|
||||
IObservableCollection<IViewValue<T>> items, Func<T, TKey> key, Func<TSingle, TKey> from_single, Func<TKey, TSingle> to_single, Func<TMulti, IEnumerable<TKey>> from_multi, Func<IEnumerable<TKey>, TMulti> to_multi)
|
||||
where TKey : struct, IEquatable<TKey>
|
||||
where TPredicate : IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> {
|
||||
where TPredicate : IDiscreteValuesPredicateData<T, TSingle, TMulti> {
|
||||
//
|
||||
return new Dictionary<DiscreteValuesOneofCase, ITemplatedPropertyWrapperVM<TPredicate>> {
|
||||
{ DiscreteValuesOneofCase.IsValue, PropertyWrapperVMUtils.create_keyed_selector_vm<TPredicate, IViewValue<T>, TKey>(
|
||||
|
||||
@@ -5,7 +5,7 @@ using System;
|
||||
|
||||
namespace mbts {
|
||||
[ProtoContract]
|
||||
class ExchangePredicate : IDiscreteValuesPredicateBuilder<Venue, Venue?, Venue[]>, IEquatable<ExchangePredicate> {
|
||||
class ExchangePredicate : IDiscreteValuesPredicateData<Venue, Venue?, Venue[]>, IEquatable<ExchangePredicate> {
|
||||
[ProtoMember(1)]
|
||||
Venue[] _values;
|
||||
[ProtoMember(2)]
|
||||
@@ -17,7 +17,7 @@ namespace mbts {
|
||||
public Venue[] not_in_set { get => _get_set(true); set => _set(true, value); }
|
||||
|
||||
public DiscreteValuesOneofCase predicate_type_case => DiscreteValuesPredicateUtils.get_predicate_type_case(_values, _negated);
|
||||
public IPredicate<Venue> create(object obj) => DiscreteValuesPredicateUtils.create(this);
|
||||
public IPredicate<Venue> create() => DiscreteValuesPredicateUtils.create(this);
|
||||
|
||||
public bool Equals(ExchangePredicate other) => DiscreteValuesPredicateUtils.equals(this, other);
|
||||
public override bool Equals(object obj) => Equals(obj as ExchangePredicate);
|
||||
|
||||
@@ -5,7 +5,7 @@ using System;
|
||||
|
||||
namespace mbts {
|
||||
[ProtoContract]
|
||||
class OrderStatusPredicate : IDiscreteValuesPredicateBuilder<OrderStatus, OrderStatus?, OrderStatus[]>, IEquatable<OrderStatusPredicate> {
|
||||
class OrderStatusPredicate : IDiscreteValuesPredicateData<OrderStatus, OrderStatus?, OrderStatus[]>, IEquatable<OrderStatusPredicate> {
|
||||
[ProtoMember(1)]
|
||||
OrderStatus[] _values;
|
||||
[ProtoMember(2)]
|
||||
@@ -17,7 +17,7 @@ namespace mbts {
|
||||
public OrderStatus[] not_in_set { get => _get_set(true); set => _set(true, value); }
|
||||
|
||||
public DiscreteValuesOneofCase predicate_type_case => DiscreteValuesPredicateUtils.get_predicate_type_case(_values, _negated);
|
||||
public IPredicate<OrderStatus> create(object obj) => DiscreteValuesPredicateUtils.create(this);
|
||||
public IPredicate<OrderStatus> create() => DiscreteValuesPredicateUtils.create(this);
|
||||
|
||||
public bool Equals(OrderStatusPredicate other) => DiscreteValuesPredicateUtils.equals(this, other);
|
||||
public override bool Equals(object obj) => Equals(obj as OrderStatusPredicate);
|
||||
|
||||
@@ -5,7 +5,7 @@ using System;
|
||||
|
||||
namespace mbts {
|
||||
[ProtoContract]
|
||||
class OrderSubTypePredicate : IDiscreteValuesPredicateBuilder<OrderSubType, OrderSubType?, OrderSubType[]>, IEquatable<OrderSubTypePredicate> {
|
||||
class OrderSubTypePredicate : IDiscreteValuesPredicateData<OrderSubType, OrderSubType?, OrderSubType[]>, IEquatable<OrderSubTypePredicate> {
|
||||
[ProtoMember(1)]
|
||||
OrderSubType[] _values;
|
||||
[ProtoMember(2)]
|
||||
@@ -17,7 +17,7 @@ namespace mbts {
|
||||
public OrderSubType[] not_in_set { get => _get_set(true); set => _set(true, value); }
|
||||
|
||||
public DiscreteValuesOneofCase predicate_type_case => DiscreteValuesPredicateUtils.get_predicate_type_case(_values, _negated);
|
||||
public IPredicate<OrderSubType> create(object obj) => DiscreteValuesPredicateUtils.create(this);
|
||||
public IPredicate<OrderSubType> create() => DiscreteValuesPredicateUtils.create(this);
|
||||
|
||||
public bool Equals(OrderSubTypePredicate other) => DiscreteValuesPredicateUtils.equals(this, other);
|
||||
public override bool Equals(object obj) => Equals(obj as OrderSubTypePredicate);
|
||||
|
||||
@@ -5,7 +5,7 @@ using OrderType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
|
||||
|
||||
namespace mbts {
|
||||
[ProtoContract]
|
||||
class OrderTypePredicate : IDiscreteValuesPredicateBuilder<OrderType, OrderType?, OrderType[]>, IEquatable<OrderTypePredicate> {
|
||||
class OrderTypePredicate : IDiscreteValuesPredicateData<OrderType, OrderType?, OrderType[]>, IEquatable<OrderTypePredicate> {
|
||||
[ProtoMember(1)]
|
||||
OrderType[] _values;
|
||||
[ProtoMember(2)]
|
||||
@@ -17,7 +17,7 @@ namespace mbts {
|
||||
public OrderType[] not_in_set { get => _get_set(true); set => _set(true, value); }
|
||||
|
||||
public DiscreteValuesOneofCase predicate_type_case => DiscreteValuesPredicateUtils.get_predicate_type_case(_values, _negated);
|
||||
public IPredicate<OrderType> create(object obj) => DiscreteValuesPredicateUtils.create(this);
|
||||
public IPredicate<OrderType> create() => DiscreteValuesPredicateUtils.create(this);
|
||||
|
||||
public bool Equals(OrderTypePredicate other) => DiscreteValuesPredicateUtils.equals(this, other);
|
||||
public override bool Equals(object obj) => Equals(obj as OrderTypePredicate);
|
||||
|
||||
@@ -5,7 +5,7 @@ using System;
|
||||
|
||||
namespace mbts {
|
||||
[ProtoContract]
|
||||
class TimeInForcePredicate : IDiscreteValuesPredicateBuilder<TimeInForce, TimeInForce?, TimeInForce[]>, IEquatable<TimeInForcePredicate> {
|
||||
class TimeInForcePredicate : IDiscreteValuesPredicateData<TimeInForce, TimeInForce?, TimeInForce[]>, IEquatable<TimeInForcePredicate> {
|
||||
[ProtoMember(1)]
|
||||
TimeInForce[] _values;
|
||||
[ProtoMember(2)]
|
||||
@@ -17,7 +17,7 @@ namespace mbts {
|
||||
public TimeInForce[] not_in_set { get => _get_set(true); set => _set(true, value); }
|
||||
|
||||
public DiscreteValuesOneofCase predicate_type_case => DiscreteValuesPredicateUtils.get_predicate_type_case(_values, _negate);
|
||||
public IPredicate<TimeInForce> create(object obj) => DiscreteValuesPredicateUtils.create(this);
|
||||
public IPredicate<TimeInForce> create() => DiscreteValuesPredicateUtils.create(this);
|
||||
|
||||
public bool Equals(TimeInForcePredicate other) => DiscreteValuesPredicateUtils.equals(this, other);
|
||||
public override bool Equals(object obj) => Equals(obj as TimeInForcePredicate);
|
||||
|
||||
@@ -4,7 +4,6 @@ using bts.shell;
|
||||
using bts.utils;
|
||||
using mbts.spark;
|
||||
using mbts.ui.settings;
|
||||
using mbts.utils;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -14,7 +13,7 @@ namespace mbts {
|
||||
static class OrderCanceledEventPredicateVMFactory {
|
||||
public static Dictionary<OrderCanceledEventPredicate.PredicateTypeOneofCase, ITemplatedPropertyWrapperVM<OrderCanceledEventPredicate>> get_oneofs(IBus bus) =>
|
||||
new Dictionary<OrderCanceledEventPredicate.PredicateTypeOneofCase, ITemplatedPropertyWrapperVM<OrderCanceledEventPredicate>> {
|
||||
{ OrderCanceledEventPredicate.PredicateTypeOneofCase.AnyOrder, PredicateBuilderVMWrapper.create<OrderCanceledEventPredicate>(p => p.security = null) },
|
||||
{ OrderCanceledEventPredicate.PredicateTypeOneofCase.AnyOrder, PredicateVMWrapper.create<OrderCanceledEventPredicate>(p => p.security = null) },
|
||||
{ OrderCanceledEventPredicate.PredicateTypeOneofCase.SecurityOrder, SecurityCriteriaVMFactory.create<OrderCanceledEventPredicate>(bus, o => o.security?.is_value ?? new SecurityCriteria(),
|
||||
(p, s) => {
|
||||
if (p.security == null)
|
||||
@@ -26,7 +25,7 @@ namespace mbts {
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class OrderCanceledEventPredicate : IPredicateBuilder<OrderSecurity, INotificationServiceDataCache>, IEquatable<OrderCanceledEventPredicate> {
|
||||
class OrderCanceledEventPredicate : IEquatable<OrderCanceledEventPredicate> {
|
||||
public enum PredicateTypeOneofCase {
|
||||
[Description("(any order)")]
|
||||
AnyOrder,
|
||||
@@ -52,7 +51,7 @@ namespace mbts {
|
||||
OrderEventPredicateHelper.create_status_transition_predicate(OrderStatus.Canceled, security, data_cache);
|
||||
|
||||
public INotificationPredicate<OrderSecurity> create_toast(INotificationServiceDataCache data_cache) =>
|
||||
new MyToaster(security?.create(data_cache), data_cache);
|
||||
new MyToaster(security?.create(), data_cache);
|
||||
|
||||
public bool Equals(OrderCanceledEventPredicate other) => other != null && other.security.safe_equals(other.security);
|
||||
public override bool Equals(object obj) => Equals(obj as OrderCanceledEventPredicate);
|
||||
|
||||
@@ -4,7 +4,6 @@ using bts.shell;
|
||||
using bts.utils;
|
||||
using mbts.spark;
|
||||
using mbts.ui.settings;
|
||||
using mbts.utils;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -14,7 +13,7 @@ namespace mbts {
|
||||
static class OrderCompletedEventPredicateVMFactory {
|
||||
public static Dictionary<OrderCompletedEventPredicate.PredicateTypeOneofCase, ITemplatedPropertyWrapperVM<OrderCompletedEventPredicate>> get_oneofs(IBus bus) =>
|
||||
new Dictionary<OrderCompletedEventPredicate.PredicateTypeOneofCase, ITemplatedPropertyWrapperVM<OrderCompletedEventPredicate>> {
|
||||
{ OrderCompletedEventPredicate.PredicateTypeOneofCase.AnyOrder, PredicateBuilderVMWrapper.create<OrderCompletedEventPredicate>(p => p.security = null) },
|
||||
{ OrderCompletedEventPredicate.PredicateTypeOneofCase.AnyOrder, PredicateVMWrapper.create<OrderCompletedEventPredicate>(p => p.security = null) },
|
||||
{ OrderCompletedEventPredicate.PredicateTypeOneofCase.SecurityOrder, SecurityCriteriaVMFactory.create<OrderCompletedEventPredicate>(bus, o => o.security?.is_value ?? new SecurityCriteria(),
|
||||
(p, s) => {
|
||||
if (p.security == null)
|
||||
@@ -26,7 +25,7 @@ namespace mbts {
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class OrderCompletedEventPredicate : IPredicateBuilder<OrderSecurity, INotificationServiceDataCache>, IEquatable<OrderCompletedEventPredicate> {
|
||||
class OrderCompletedEventPredicate : IEquatable<OrderCompletedEventPredicate> {
|
||||
public enum PredicateTypeOneofCase {
|
||||
[Description("(any order)")]
|
||||
AnyOrder,
|
||||
@@ -52,7 +51,7 @@ namespace mbts {
|
||||
OrderEventPredicateHelper.create_status_transition_predicate(OrderStatus.Completed, security, data_cache);
|
||||
|
||||
public INotificationPredicate<OrderSecurity> create_toast(INotificationServiceDataCache data_cache) =>
|
||||
new MyToaster(security?.create(data_cache), data_cache);
|
||||
new MyToaster(security?.create(), data_cache);
|
||||
|
||||
public bool Equals(OrderCompletedEventPredicate other) => other != null && other.security.safe_equals(other.security);
|
||||
public override bool Equals(object obj) => Equals(obj as OrderCompletedEventPredicate);
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
using bts.api.orderman;
|
||||
using bts.shell;
|
||||
using bts.utils;
|
||||
using mbts.spark;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
|
||||
namespace mbts {
|
||||
@@ -39,7 +37,7 @@ namespace mbts {
|
||||
}
|
||||
|
||||
public static IPredicate<OrderSecurity> create_status_transition_predicate(OrderStatus status, SecurityPredicate security_predicate, INotificationServiceDataCache data_cache) =>
|
||||
new OrderStatusTransitionPredicate(status, security_predicate?.create(data_cache), data_cache);
|
||||
new OrderStatusTransitionPredicate(status, security_predicate?.create(), data_cache);
|
||||
|
||||
class OrderStatusTransitionPredicate : IPredicate<OrderSecurity>, IEquatable<OrderStatusTransitionPredicate> {
|
||||
readonly IPredicate<SecurityImmutable> _security_predicate;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace mbts {
|
||||
static class OrderFilledEventPredicateVMFactory {
|
||||
public static Dictionary<OrderFilledEventPredicate.PredicateTypeOneofCase, ITemplatedPropertyWrapperVM<OrderFilledEventPredicate>> get_oneofs(IBus bus) =>
|
||||
new Dictionary<OrderFilledEventPredicate.PredicateTypeOneofCase, ITemplatedPropertyWrapperVM<OrderFilledEventPredicate>> {
|
||||
{ OrderFilledEventPredicate.PredicateTypeOneofCase.AnyOrder, PredicateBuilderVMWrapper.create<OrderFilledEventPredicate>(p => p.security = null) },
|
||||
{ OrderFilledEventPredicate.PredicateTypeOneofCase.AnyOrder, PredicateVMWrapper.create<OrderFilledEventPredicate>(p => p.security = null) },
|
||||
{ OrderFilledEventPredicate.PredicateTypeOneofCase.SecurityOrder, SecurityCriteriaVMFactory.create<OrderFilledEventPredicate>(bus, o => o.security?.is_value ?? new SecurityCriteria(),
|
||||
(p, s) => {
|
||||
if (p.security == null) {
|
||||
@@ -24,7 +24,7 @@ namespace mbts {
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class OrderFilledEventPredicate : IPredicateBuilder<OrderSecurity, INotificationServiceDataCache>, IEquatable<OrderFilledEventPredicate> {
|
||||
class OrderFilledEventPredicate : IEquatable<OrderFilledEventPredicate> {
|
||||
public enum PredicateTypeOneofCase {
|
||||
[PredicateCase("(any order)")]
|
||||
AnyOrder,
|
||||
@@ -59,7 +59,7 @@ namespace mbts {
|
||||
public override int GetHashCode() => security == null ? 0 : security.GetHashCode();
|
||||
|
||||
FillTriggerModel _create_model(INotificationServiceDataCache nsdc) =>
|
||||
new FillTriggerModel(nsdc, security?.create(nsdc));
|
||||
new FillTriggerModel(nsdc, security?.create());
|
||||
|
||||
class FillTriggerModel {
|
||||
readonly IPredicate<SecurityImmutable> _security_predicate;
|
||||
@@ -82,7 +82,7 @@ namespace mbts {
|
||||
// old order will be a copy of new order, except with filledQuantity set to 0
|
||||
var old_state = ort.order.State.CloneToMutable();
|
||||
old_state.FilledQuantity = 0;
|
||||
return (new OrderSecurity(ort.security, ort.order.with(state: old_state)), ort);
|
||||
return (new OrderSecurity(ort.security, ort.order.with(state: old_state), ort.piq), ort);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -56,12 +56,12 @@ namespace mbts {
|
||||
Func<TChildPredicate, TChildPredicateOneof> key,
|
||||
Dictionary<TChildPredicateOneof, ITemplatedPropertyWrapperVM<TChildPredicate>> oneofs) where TChildPredicateOneof : struct where TChildPredicate : new() =>
|
||||
//
|
||||
PredicateBuilderVMWrapper.wrap_child_oneof<OrderRecordTuplePredicate, TChildPredicate, TChildPredicateOneof>(get, set, key, oneofs);
|
||||
PredicateVMWrapper.wrap_child_oneof<OrderRecordTuplePredicate, TChildPredicate, TChildPredicateOneof>(get, set, key, oneofs);
|
||||
}
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class OrderRecordTuplePredicate : INotificationPredicateBuilder<OrderSecurity, INotificationServiceDataCache>, IEquatable<OrderRecordTuplePredicate> {
|
||||
class OrderRecordTuplePredicate : IEquatable<OrderRecordTuplePredicate> {
|
||||
SecurityPredicate _security;
|
||||
SidePredicate _side;
|
||||
DoublePredicate _price, _last_price, _avg_price;
|
||||
@@ -208,39 +208,36 @@ namespace mbts {
|
||||
|
||||
public IPredicate<OrderSecurity> create_predicate(INotificationServiceDataCache data_cache) {
|
||||
switch (predicate_type_case) {
|
||||
case PTOC.Security: return wrap(_security, r => r.security ?? SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
case PTOC.Time: return wrap(_time_created, r => r.order.created());
|
||||
case PTOC.LastChange: return wrap(_last_change, r => r.order.last_change());
|
||||
case PTOC.User: return wrap(_user, r => r.order.user_id());
|
||||
case PTOC.Account: return wrap(_account, r => r.order.account_id());
|
||||
case PTOC.Exchange: return wrap(_exchange, r => r.security.exchange_or_default());
|
||||
case PTOC.TimeInForce: return wrap(_time_in_force, r => r.order.time_in_force());
|
||||
case PTOC.OrderType: return wrap(_order_type, r => r.order.order_type());
|
||||
case PTOC.SubType: return wrap(_order_sub_type, r => r.order.sub_type());
|
||||
case PTOC.Side: return wrap(_side, r => r.order.side());
|
||||
case PTOC.Size: return wrap(_quantity, r => r.order.quantity());
|
||||
case PTOC.Price: return wrap(_price, r => r.order.price());
|
||||
case PTOC.Filled: return wrap(_filled_quantity, r => r.order.filled());
|
||||
case PTOC.AvgFilled: return wrap(_avg_price, r => r.order.avg_price());
|
||||
case PTOC.LastPrice: return wrap(_last_price, r => r.order.last_price());
|
||||
case PTOC.Status: return wrap(_order_status, r => r.order.status());
|
||||
case PTOC.LastMessage: return wrap(_last_message, r => r.order.last_msg());
|
||||
case PTOC.SecurityKey: return wrap(_security_key, r => r.order.security_key());
|
||||
case PTOC.OrderKey: return wrap(_order_key, r => r.order.Key);
|
||||
case PTOC.ParentKey: return wrap(_parent_key, r => r.order.ParentKey);
|
||||
case PTOC.Security: return wrap(_security.create(), r => r.security ?? SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
case PTOC.Time: return wrap(_time_created.create(), r => r.order.created());
|
||||
case PTOC.LastChange: return wrap(_last_change.create(), r => r.order.last_change());
|
||||
case PTOC.User: return wrap(_user.create(), r => r.order.user_id());
|
||||
case PTOC.Account: return wrap(_account.create(), r => r.order.account_id());
|
||||
case PTOC.Exchange: return wrap(_exchange.create(), r => r.security.exchange_or_default());
|
||||
case PTOC.TimeInForce: return wrap(_time_in_force.create(), r => r.order.time_in_force());
|
||||
case PTOC.OrderType: return wrap(_order_type.create(), r => r.order.order_type());
|
||||
case PTOC.SubType: return wrap(_order_sub_type.create(), r => r.order.sub_type());
|
||||
case PTOC.Side: return wrap(_side.create(), r => r.order.side());
|
||||
case PTOC.Size: return wrap(_quantity.create(), r => r.order.quantity());
|
||||
case PTOC.Price: return wrap(_price.create(), r => r.order.price());
|
||||
case PTOC.Filled: return wrap(_filled_quantity.create(), r => r.order.filled());
|
||||
case PTOC.AvgFilled: return wrap(_avg_price.create(), r => r.order.avg_price());
|
||||
case PTOC.LastPrice: return wrap(_last_price.create(), r => r.order.last_price());
|
||||
case PTOC.Status: return wrap(_order_status.create(), r => r.order.status());
|
||||
case PTOC.LastMessage: return wrap(_last_message.create(), r => r.order.last_msg());
|
||||
case PTOC.SecurityKey: return wrap(_security_key.create(), r => r.order.security_key());
|
||||
case PTOC.OrderKey: return wrap(_order_key.create(), r => r.order.Key);
|
||||
case PTOC.ParentKey: return wrap(_parent_key.create(), r => r.order.ParentKey);
|
||||
case PTOC.FilledEvent: return _filled_event.create_predicate(data_cache);
|
||||
case PTOC.CompletedEvent: return _completed_event.create_predicate(data_cache);
|
||||
case PTOC.CanceledEvent: return _canceled_event.create(data_cache);
|
||||
default: return null;
|
||||
}
|
||||
//
|
||||
IPredicate<OrderSecurity> wrap<T>(IPredicateBuilder<T> trigger, Func<OrderSecurity, T> select) =>
|
||||
trigger.create(data_cache).wrap<OrderSecurity,T>(select);
|
||||
IPredicate<OrderSecurity> wrap<T>(IPredicate<T> trigger, Func<OrderSecurity, T> select) =>
|
||||
trigger.wrap<OrderSecurity, T>(select);
|
||||
}
|
||||
|
||||
IPredicate<OrderSecurity> IPredicateBuilder<OrderSecurity>.create(object obj) =>
|
||||
create_predicate((INotificationServiceDataCache)obj);
|
||||
|
||||
public INotificationPredicate<OrderSecurity> create_notification_predicate(INotificationServiceDataCache notification_service_data_cache) {
|
||||
if (!is_toast_enabled)
|
||||
return null;
|
||||
@@ -252,9 +249,6 @@ namespace mbts {
|
||||
}
|
||||
}
|
||||
|
||||
INotificationPredicate<OrderSecurity> INotificationPredicateBuilder<OrderSecurity>.create_notification_predicate(object obj) =>
|
||||
create_notification_predicate((INotificationServiceDataCache)obj);
|
||||
|
||||
public bool Equals(OrderRecordTuplePredicate other) {
|
||||
if (other == null)
|
||||
return false;
|
||||
@@ -378,9 +372,9 @@ namespace mbts {
|
||||
_sound_animations = new List<SoundNotification> ( sound_animations );
|
||||
}
|
||||
|
||||
public OrderRecordTuplePredicate predicate_builder => _predicate;
|
||||
public OrderRecordTuplePredicate predicate_data => _predicate;
|
||||
public IReadOnlyList<CellAnimation> cell_animations => _cell_animations ?? (_cell_animations = new List<CellAnimation>());
|
||||
public IReadOnlyList<RowAnimation> row_animations => _row_animations ?? (_row_animations = new List<RowAnimation>());
|
||||
public IReadOnlyList<SoundNotification> sound_notifications => _sound_animations ?? (_sound_animations = new List<SoundNotification>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using bts.proto;
|
||||
using bts.utils;
|
||||
using mbts.spark;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.spark;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
@@ -24,7 +23,7 @@ namespace mbts {
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class DoublePredicate : IPredicateBuilder<double>, IEquatable<DoublePredicate> {
|
||||
class DoublePredicate : IEquatable<DoublePredicate> {
|
||||
// not using a 'negate' flag here bc its meaning is not consistent for all value-types
|
||||
// greater than vs less than for specific values, or inside vs outside for value-ranges
|
||||
// trading a little more code for more clarity
|
||||
@@ -62,7 +61,7 @@ namespace mbts {
|
||||
}
|
||||
}
|
||||
|
||||
public IPredicate<double> create(object obj) {
|
||||
public IPredicate<double> create() {
|
||||
switch (predicate_type_case) {
|
||||
case PredicateTypeOneofCase.GreaterThan: return PredicateBuilder.greater_than(_greater_than.Value);
|
||||
case PredicateTypeOneofCase.LessThan: return PredicateBuilder.less_than(_less_than.Value);
|
||||
|
||||