Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f871dfe142 | ||
|
|
d44de204ee | ||
|
|
04beea61eb | ||
|
|
2181b92970 | ||
|
|
693c730f51 | ||
|
|
0ccba5bb0b | ||
|
|
3458be59d7 | ||
|
|
7dc9aa0710 | ||
|
|
88d6dd0f00 | ||
|
|
ce4c46da04 | ||
|
|
59c1f6a511 | ||
|
|
938caf9a4f | ||
|
|
df4f061b6c | ||
|
|
8afee21df7 | ||
|
|
f34014eb25 | ||
|
|
b07eec08b4 | ||
|
|
964274f5c4 | ||
|
|
c16941977c | ||
|
|
6b19119ab2 | ||
|
|
ea42086e96 | ||
|
|
292873b189 | ||
|
|
8545a5ef38 | ||
|
|
6a52fff3d5 | ||
|
|
4ec6fafbc2 | ||
|
|
b08e2c2ee6 | ||
|
|
8bdd033501 | ||
|
|
76466e83f0 | ||
|
|
2540dc0510 | ||
|
|
f0132c84b6 | ||
|
|
57ca9596c8 | ||
|
|
bf4953f6dc | ||
|
|
c277fcd503 | ||
|
|
dff57a43dd | ||
|
|
39c71d0c16 | ||
|
|
009e72953c | ||
|
|
635163c895 | ||
|
|
95e96a6afc | ||
|
|
c7f0e2dd58 | ||
|
|
f5e79a65a3 | ||
|
|
c958f4bf1a | ||
|
|
95939a5534 | ||
|
|
9a7164b42b | ||
|
|
f347fda47f | ||
|
|
d75ab3a63f | ||
|
|
b30f59227e | ||
|
|
ef60d4200a | ||
|
|
ed6f0b8bdb | ||
|
|
766116eac4 | ||
|
|
8d49de3f61 | ||
|
|
16cf4554c5 | ||
|
|
6b17efee60 | ||
|
|
5d64213455 | ||
|
|
0ef44e92aa | ||
|
|
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",
|
||||
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
|
||||
"args": ["test", "[md]"],
|
||||
"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
|
||||
|
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 |
@@ -4,6 +4,40 @@ weight = 3
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
## 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
|
||||
|
||||
@@ -66,6 +66,7 @@ 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);
|
||||
|
||||
@@ -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.link_to_order,
|
||||
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 working 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 working 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 working 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,8 +48,8 @@ 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) =>
|
||||
@@ -85,7 +58,7 @@ namespace bts.shell {
|
||||
void _on_orders(TableUpdate<(SecurityImmutable?,OrderRecordImmutable),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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,7 +285,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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using bts.api;
|
||||
using bts.utils;
|
||||
using mbts.spark;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using ProtoBuf;
|
||||
@@ -23,7 +22,7 @@ namespace mbts {
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class SidePredicate : IPredicateBuilder<Side>, IEquatable<SidePredicate> {
|
||||
class SidePredicate : IEquatable<SidePredicate> {
|
||||
Side? _is_value;
|
||||
Side? _not_value;
|
||||
|
||||
@@ -48,7 +47,7 @@ namespace mbts {
|
||||
}
|
||||
}
|
||||
|
||||
public IPredicate<Side> create(object obj) {
|
||||
public IPredicate<Side> create() {
|
||||
switch (predicate_type_case) {
|
||||
case PredicateTypeOneofCase.IsValue: return PredicateBuilder.equals(_is_value.Value);
|
||||
case PredicateTypeOneofCase.NotValue: return PredicateBuilder.not_equals(_not_value.Value);
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using bts.proto;
|
||||
using bts.utils;
|
||||
using mbts.spark;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.spark;
|
||||
using mbts.utils;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -27,7 +25,7 @@ namespace mbts {
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class StringPredicate : IPredicateBuilder<string>, IEquatable<StringPredicate> {
|
||||
class StringPredicate : IEquatable<StringPredicate> {
|
||||
StringIgnoreCase? _is_string, _not_string, _contains_string, _not_contains_string;
|
||||
|
||||
public enum PredicateTypeOneofCase {
|
||||
@@ -61,7 +59,7 @@ namespace mbts {
|
||||
}
|
||||
}
|
||||
|
||||
public IPredicate<string> create(object obj) {
|
||||
public IPredicate<string> create() {
|
||||
switch (predicate_type_case) {
|
||||
case PTOC.IsMatch: return PredicateBuilder.equals(_is_string.Value.text, _get_eq_comparer(_is_string));
|
||||
case PTOC.NotMatch: return PredicateBuilder.not_equals(_not_string.Value.text, _get_eq_comparer(_not_string));
|
||||
|
||||
@@ -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;
|
||||
@@ -21,7 +20,7 @@ namespace mbts {
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class TimePredicate : IPredicateBuilder<DateTime>, IEquatable<TimePredicate> {
|
||||
class TimePredicate : IEquatable<TimePredicate> {
|
||||
// not using a 'negate' flag here bc its meaning is not consistent for all value-types
|
||||
// before vs after for specific times, or inside vs outside for time-ranges
|
||||
// trading a little more code for clarity
|
||||
@@ -59,7 +58,7 @@ namespace mbts {
|
||||
}
|
||||
}
|
||||
|
||||
public IPredicate<DateTime> create(object obj) {
|
||||
public IPredicate<DateTime> create() {
|
||||
switch (predicate_type_case) {
|
||||
case PTOC.Before: return PredicateBuilder.less_than(_before.Value.get_date_time());
|
||||
case PTOC.After: return PredicateBuilder.greater_than(_after.Value.get_date_time());
|
||||
|
||||
@@ -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 UIntPredicate : IPredicateBuilder<uint>, IEquatable<UIntPredicate> {
|
||||
class UIntPredicate : IEquatable<UIntPredicate> {
|
||||
// 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<uint> create(object obj) {
|
||||
public IPredicate<uint> 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);
|
||||
|
||||
@@ -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 UShortPredicate : IPredicateBuilder<ushort>, IEquatable<UShortPredicate> {
|
||||
class UShortPredicate : IEquatable<UShortPredicate> {
|
||||
// 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
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace mbts {
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class SecurityPredicate : IPredicateBuilder<SecurityImmutable>, IEquatable<SecurityPredicate> {
|
||||
class SecurityPredicate : IEquatable<SecurityPredicate> {
|
||||
[ProtoMember(1)]
|
||||
SecurityCriteria _criteria;
|
||||
[ProtoMember(2)]
|
||||
@@ -43,7 +43,8 @@ namespace mbts {
|
||||
_criteria == null ? PTOC.None :
|
||||
_negate ? PTOC.NotInCriteria : PTOC.InCriteria;
|
||||
|
||||
public IPredicate<SecurityImmutable> create(object obj) => _criteria.is_defined() ? new MyPredicate(_criteria, _negate) : null;
|
||||
public IPredicate<SecurityImmutable> create() => _criteria.is_defined() ? new MyPredicate(_criteria, _negate) : null;
|
||||
|
||||
public bool Equals(SecurityPredicate other) =>
|
||||
other != null &&
|
||||
_negate == other._negate &&
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using bts.shell;
|
||||
using bts.utils;
|
||||
using mbts.utils;
|
||||
using bts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
@@ -14,23 +12,6 @@ namespace mbts {
|
||||
bool trigger(T value);
|
||||
}
|
||||
|
||||
interface IPredicateBuilder<T> {
|
||||
IPredicate<T> create(object obj);
|
||||
}
|
||||
|
||||
interface INotificationPredicateBuilder<T> : IPredicateBuilder<T> {
|
||||
bool is_toast_enabled { get; set; }
|
||||
INotificationPredicate<T> create_notification_predicate(object obj);
|
||||
}
|
||||
|
||||
interface IPredicateBuilder<T, TArg> : IPredicateBuilder<T> {
|
||||
IPredicate<T> create_predicate(TArg arg);
|
||||
}
|
||||
|
||||
interface INotificationPredicateBuilder<T, TArg> : IPredicateBuilder<T, TArg>, INotificationPredicateBuilder<T> {
|
||||
INotificationPredicate<T> create_notification_predicate(TArg arg);
|
||||
}
|
||||
|
||||
public enum PredicateCategory {
|
||||
Normal,
|
||||
Event
|
||||
|
||||