Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd5c680155 |
No files matched your search
Vendored
+91
-177
@@ -1,14 +1,83 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "(gdb) Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/ninja_debug/server/sparkd/sparkd",
|
||||
"args": ["run"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceRoot}/build/ninja_debug/src/server/sparkd",
|
||||
"environment": [],
|
||||
"externalConsole": true,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Client Launch",
|
||||
"type": "clr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/Debug/bin/net471/spark.exe",
|
||||
"args": [],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "SDK Launch",
|
||||
"type": "clr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
|
||||
"args": ["md","GLBX","Future","CL", "CLZ9", "--user=bts", "--pass="],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Excel Launch",
|
||||
"type": "clr",
|
||||
"request": "launch",
|
||||
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
|
||||
"args": ["${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Client Tests",
|
||||
"type": "clr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
|
||||
"args": ["test","test_filter"],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Client Attach",
|
||||
"type": "clr",
|
||||
"request": "attach",
|
||||
"processId":"${command:pickProcess}"
|
||||
},
|
||||
{
|
||||
"name": "SparkVM Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
|
||||
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
|
||||
"args": [
|
||||
"run"
|
||||
],
|
||||
"args": ["run"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/server/",
|
||||
"environment": [],
|
||||
@@ -33,113 +102,24 @@
|
||||
"description": "LD LIBRARY",
|
||||
"text": "set env LD_LIBRARY_PATH=.",
|
||||
"ignoreFailures": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": [
|
||||
"bts@sparkvm"
|
||||
],
|
||||
"pipeArgs": ["bts@sparkvm"],
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/ninja_debug/server/sparkd/sparkd",
|
||||
"args": [
|
||||
"run"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceRoot}/build/ninja_debug/src/server/sparkd",
|
||||
"environment": [],
|
||||
"externalConsole": true,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Client Launch",
|
||||
"type": "clr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/Debug/bin/net471/spark.exe",
|
||||
"args": [],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "SDK Launch",
|
||||
"type": "clr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
|
||||
"args": [
|
||||
"md",
|
||||
"GLBX",
|
||||
"Future",
|
||||
"CL",
|
||||
"CLZ9",
|
||||
"--user=bts",
|
||||
"--pass="
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Excel Launch",
|
||||
"type": "clr",
|
||||
"request": "launch",
|
||||
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
|
||||
"args": [
|
||||
"${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Client Tests",
|
||||
"type": "clr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
|
||||
"args": [
|
||||
"test",
|
||||
"test_filter"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Client Attach",
|
||||
"type": "clr",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
},
|
||||
{
|
||||
"name": "Spreader Tests",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"args": [
|
||||
"test",
|
||||
"[spreader]"
|
||||
],
|
||||
"args": ["test", "[spreader]"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
|
||||
"environment": [],
|
||||
@@ -159,9 +139,7 @@
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": [
|
||||
"bts@sparkvm"
|
||||
],
|
||||
"pipeArgs": ["bts@sparkvm"],
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
@@ -173,10 +151,7 @@
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"args": [
|
||||
"test",
|
||||
"[admin_service]"
|
||||
],
|
||||
"args": ["test", "[admin_service]"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
|
||||
"environment": [],
|
||||
@@ -196,9 +171,7 @@
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": [
|
||||
"bts@sparkvm"
|
||||
],
|
||||
"pipeArgs": ["bts@sparkvm"],
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
@@ -210,10 +183,7 @@
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"args": [
|
||||
"test",
|
||||
"[limiter]"
|
||||
],
|
||||
"args": ["test", "[limiter]"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
|
||||
"environment": [],
|
||||
@@ -233,9 +203,7 @@
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": [
|
||||
"bts@sparkvm"
|
||||
],
|
||||
"pipeArgs": ["bts@sparkvm"],
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
@@ -247,10 +215,7 @@
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
|
||||
"args": [
|
||||
"test",
|
||||
"[mbo]"
|
||||
],
|
||||
"args": ["test", "[mbo]"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/server/",
|
||||
"environment": [],
|
||||
@@ -275,16 +240,14 @@
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": [
|
||||
"bts@sparkvm"
|
||||
],
|
||||
"pipeArgs": ["bts@sparkvm"],
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
"name": "SparkVM attach",
|
||||
"type": "cppdbg",
|
||||
"request": "attach",
|
||||
@@ -292,18 +255,16 @@
|
||||
"processId": "${command:pickRemoteProcess}",
|
||||
"MIMode": "gdb",
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": [
|
||||
"bts@sparkvm"
|
||||
],
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": ["bts@sparkvm"],
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
{
|
||||
},
|
||||
{
|
||||
"name": "Python",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
@@ -313,59 +274,12 @@
|
||||
"cwd": "${workspaceRoot}",
|
||||
"env": {},
|
||||
"envFile": "${workspaceRoot}/.env",
|
||||
"args": [
|
||||
"DEBUG"
|
||||
],
|
||||
"args": [ "DEBUG" ],
|
||||
"debugOptions": [
|
||||
"WaitOnAbnormalExit",
|
||||
"WaitOnNormalExit",
|
||||
"RedirectOutput"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Remote Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickRemoteProcess}",
|
||||
"pipeTransport": {
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": [
|
||||
"bts@sparkvm"
|
||||
],
|
||||
"debuggerPath": "/home/bts/vsdbg/vsdbg",
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"quoteArgs": true
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
},
|
||||
"justMyCode": true
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Remote Launch",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "/usr/bin/dotnet",
|
||||
"args": [
|
||||
"sparkcmd.dll",
|
||||
"test",
|
||||
"test_join_create_and_takeout_market"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/server",
|
||||
"pipeTransport": {
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": [
|
||||
"bts@sparkvm"
|
||||
],
|
||||
"debuggerPath": "/home/bts/vsdbg/vsdbg",
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"quoteArgs": true
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
},
|
||||
"justMyCode": false
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Algos/_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/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)
|
||||
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)
|
||||
@@ -7,6 +7,7 @@ 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
|
||||
|
||||
@@ -4,48 +4,6 @@ weight = 3
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
## 2.0.3
|
||||
### Internal changes
|
||||
- Improved backwards compatibility in UI settings
|
||||
|
||||
|
||||
## 2.0.2
|
||||
### User Visible Changes
|
||||
- Middle click the ladder price column to show the orders for that price level
|
||||
- Middle click the bid or ask column in the ladder no longer sends an order
|
||||
|
||||
### Internal changes
|
||||
- Improved market by order performance under heavy load
|
||||
- Fixed a concurrency bug in SIM
|
||||
|
||||
## 2.0.0
|
||||
### User Visible Changes
|
||||
- Support for attaching and detaching algos to existing orders
|
||||
- Support for display and update of all parameters in running algos
|
||||
- Streamlining of order badge decorations in ladder
|
||||
- New order search/filtering algorithm for order book
|
||||
- Simpler representation of complex algo chains in order book and mini order book
|
||||
- Visual and usability improvements to mini order book
|
||||
- Streamlining of algo parameter dialog in ladder
|
||||
- New spreader queue holder behavior
|
||||
- Visual improvements to the order book
|
||||
- New server side API
|
||||
- Improved admin UI performance
|
||||
- Optimized PIQ rendering
|
||||
### Internal changes
|
||||
- Exchange simulator no longer requires daily sequence number reset
|
||||
- Improved MD resilience to corrupted shared memory
|
||||
- Native orders can now buffer updates/cancels when disconnected or pending first acknowledgment
|
||||
- Improved settings manager tool output
|
||||
- Added diagnostics in the event MD memory corruption is detected
|
||||
- Added systems tests for join,lean, iceberg and spreader orders
|
||||
- Made exchange order recovery more resilient in the presence of database loss
|
||||
|
||||
## 1.6.2
|
||||
### User Visible Changes
|
||||
### Exchange simulator
|
||||
- Exchange simulator now now produce position in queue data
|
||||
|
||||
## 1.6.0
|
||||
### User Visible Changes
|
||||
#### New Features
|
||||
|
||||
+1
-1
Submodule docker/linux updated: e13bb5d8df...ce08a5129f.
@@ -22,9 +22,7 @@ namespace mbts.ui {
|
||||
AddBoxesGeometryKey = nameof(AddBoxesGeometryKey),
|
||||
AddFileGeometryKey = nameof(AddFileGeometryKey),
|
||||
AddFileThinGeometryKey = nameof(AddFileThinGeometryKey),
|
||||
AttachAlgosKey = nameof(AttachAlgosKey),
|
||||
CheckMarkGeometryKey = nameof(CheckMarkGeometryKey),
|
||||
DetachAlgosKey = nameof(DetachAlgosKey),
|
||||
DiskGeometryKey = nameof(DiskGeometryKey),
|
||||
DiskFileGeometryKey = nameof(DiskFileGeometryKey),
|
||||
DocumentGeometryKey = nameof(DocumentGeometryKey),
|
||||
@@ -37,14 +35,10 @@ namespace mbts.ui {
|
||||
EditDocumentAltGeometryKey = nameof(EditDocumentAltGeometryKey),
|
||||
EditDocumentGeometryKey = nameof(EditDocumentGeometryKey),
|
||||
GearGeometryKey = nameof(GearGeometryKey),
|
||||
LetterAKey = nameof(LetterAKey),
|
||||
LetterDKey = nameof(LetterDKey),
|
||||
OrdersGeometryKey = nameof(OrdersGeometryKey),
|
||||
PaymentGeometryKey = nameof(PaymentGeometryKey),
|
||||
PencilGeometryKey = nameof(PencilGeometryKey),
|
||||
PlayGeometryKey = nameof(PlayGeometryKey),
|
||||
ProhibitedGeometryKey = nameof(ProhibitedGeometryKey),
|
||||
RubberStampGeometryKey = nameof(RubberStampGeometryKey),
|
||||
StarGeometryKey = nameof(StarGeometryKey),
|
||||
ToolsGeometryKey = nameof(ToolsGeometryKey),
|
||||
TrashGeometryKey = nameof(TrashGeometryKey),
|
||||
@@ -121,7 +115,6 @@ namespace mbts.ui {
|
||||
public static ImageSource AddBoxes => create_icon(AddBoxesGeometryKey);
|
||||
public static ImageSource AddEncircled { get { return load_resource_and_freeze("AddEncircled"); } }
|
||||
public static ImageSource AddFile => create_icon(Smashicons.AddFileGeometryKey);
|
||||
public static ImageSource AttachAlgos => create_icon(AttachAlgosKey);
|
||||
public static ImageSource Beaker { get { return load_resource_and_freeze("Beaker"); } }
|
||||
public static ImageSource Book { get { return load_resource_and_freeze("Book"); } }
|
||||
|
||||
@@ -146,7 +139,6 @@ namespace mbts.ui {
|
||||
public static ImageSource CheckMark => create_icon(CheckMarkGeometryKey);
|
||||
public static ImageSource Clock { get { return load_resource_and_freeze("Clock"); } }
|
||||
public static ImageSource Defrag { get { return load_resource_and_freeze("Defrag"); } }
|
||||
public static ImageSource DetachAlgos => create_icon_and_rotate(DetachAlgosKey, Brushes.Red);
|
||||
public static ImageSource Disk => create_icon(DiskGeometryKey);
|
||||
public static ImageSource DiskFile => create_icon(DiskFileGeometryKey);
|
||||
public static ImageSource Display { get { return load_resource_and_freeze("Display"); } }
|
||||
@@ -166,8 +158,6 @@ namespace mbts.ui {
|
||||
public static ImageSource Knight { get { return load_resource_and_freeze("Knight"); } }
|
||||
public static ImageSource Payment => create_icon(PaymentGeometryKey);
|
||||
public static ImageSource Pencil => create_icon(PencilGeometryKey);
|
||||
public static ImageSource Prohibited => create_icon(ProhibitedGeometryKey);
|
||||
public static ImageSource RubberStamp => create_icon(RubberStampGeometryKey);
|
||||
public static ImageSource RedoCircular => create_icon(UndoCircularGeometryKey, Brushes.Black, null, new ScaleTransform(-1, 1));
|
||||
public static ImageSource Spawn { get { return load_resource_and_freeze("Spawn"); } }
|
||||
public static ImageSource Star => create_icon(StarGeometryKey);
|
||||
@@ -196,19 +186,6 @@ namespace mbts.ui {
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static ImageSource create_icon_and_rotate(string geometric_path_key, Brush brush = null, Pen pen = null, Transform transform = null, double degrees = 90) {
|
||||
if (brush == null) {
|
||||
brush = Brushes.Black;
|
||||
}
|
||||
var drawing = create_drawing(geometric_path_key, brush, pen, transform);
|
||||
var dg = new DrawingGroup();
|
||||
dg.Children.Add(drawing);
|
||||
dg.Transform = new RotateTransform(degrees);
|
||||
var rv = new DrawingImage(dg);
|
||||
rv.Freeze();
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static Drawing create_drawing(string geometric_path_key) =>
|
||||
create_drawing(geometric_path_key, Brushes.Black);
|
||||
|
||||
|
||||
@@ -5,14 +5,6 @@
|
||||
xmlns:mbts="clr-namespace:mbts.ui"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
|
||||
<sys:String x:Key="{x:Static mbts:Icons.AttachAlgosKey}">
|
||||
M10.519881,14.945064C11.300896,14.955074,12.079896,15.102078,12.819896,15.386075L10.878893,17.326085C9.6458764,17.201085,8.3688531,17.591071,7.4258485,18.533091L3.5977991,22.360102C1.9327704,24.026093 1.9317786,26.736124 3.5977991,28.402117 5.2638197,30.068108 7.9748359,30.068108 9.6398644,28.402117L13.467914,24.575106C14.410919,23.632109,14.800938,22.355097,14.675937,21.121083L16.615963,19.180096C17.523962,21.546103,17.034943,24.325104,15.126931,26.234108L11.298882,30.061119C8.7138591,32.64612 4.5238051,32.64612 1.9387672,30.062127 -0.64625573,27.477126 -0.64625573,23.287106 1.9387672,20.701098L5.7668166,16.875065C7.077837,15.564055,8.8018727,14.923061,10.519881,14.945064z M19.825993,10.418039L21.580012,12.17305 12.089907,21.665091 10.333873,19.911087z M25.383069,0C27.077088,3.0538786E-08 28.771109,0.64599859 30.063117,1.9390028 32.647133,4.5230274 32.647133,8.7120385 30.062109,11.298047L26.235068,15.125058C24.327055,17.033085,21.547022,17.52207,19.181973,16.614077L21.121998,14.674067C22.356022,14.799068,23.633046,14.409051,24.57605,13.466054L28.403094,9.6400513C30.068121,7.9740295 30.069129,5.2630205 28.403094,3.5980362 26.738064,1.9320143 24.027033,1.9320143 22.362034,3.5980362L18.533986,7.4240398C17.591958,8.3670363,17.201968,9.643042,17.32697,10.877056L15.385937,12.817066C14.478913,10.452035,14.968939,7.6730337,16.875945,5.7660436L20.703019,1.9390028C21.995026,0.64599859,23.689047,3.0538786E-08,25.383069,0z
|
||||
</sys:String>
|
||||
<sys:String x:Key="{x:Static mbts:Icons.DetachAlgosKey}">
|
||||
M19.522852,25.255011L20.920847,25.256994 20.920847,29.729012 19.522852,29.729012z M22.826874,21.952002L25.989905,25.113989 25.001887,26.101996 21.838856,22.941015z M25.269894,19.506992L29.742937,19.509007 29.742937,20.906012 25.269894,20.907995z M10.519729,14.945011C11.300744,14.954991,12.079745,15.103001,12.818766,15.386997L10.878742,17.327C9.6457264,17.200016,8.368704,17.592014,7.4257004,18.532993L3.597655,22.359015C1.9326275,24.025 1.9316508,26.734991 3.597655,28.40299 5.2636887,30.068001 7.9747178,30.066017 9.6397144,28.40299L13.468767,24.575018C14.410794,23.633001,14.800783,22.355993,14.674774,21.122015L16.615807,19.181004C17.523806,21.547003,17.034817,24.325995,15.126776,26.233008L11.298761,30.061012C8.713709,32.644997 4.5236599,32.644997 1.9396465,30.061012 -0.64638097,27.475012 -0.64638097,23.286017 1.9386395,20.702002L5.7666852,16.875004C7.0777046,15.562992,8.8017227,14.922001,10.519729,14.945011z M2.1536381,10.959994L6.6257035,10.959994 6.6266801,12.359988 2.1536381,12.359988z M6.7686803,5.894014L9.9307338,9.0549947 8.9427158,10.044008 5.7806932,6.8819901z M10.976735,2.1390082L12.374761,2.1390082 12.375768,6.6130081 10.975728,6.6130081z M25.382902,0C27.076922,-1.8170738E-08 28.770941,0.6470033 30.063955,1.9389956 32.646992,4.5220043 32.646992,8.7130147 30.061941,11.297L26.234902,15.126011C24.326891,17.032993,21.546862,17.521,19.181845,16.614994L21.121868,14.674015C22.35586,14.798008,23.63389,14.407992,24.575886,13.467013L28.403933,9.6390096C30.067953,7.972994 30.069967,5.2619945 28.403933,3.5960094 26.738905,1.9310002 24.0269,1.9320073 22.361872,3.5960094L18.533827,7.4249893C17.591829,8.3659993,17.201811,9.6440145,17.32782,10.87601L15.385781,12.817996C14.477781,10.453005,14.968784,7.6730062,16.875818,5.7659928L20.702858,1.9379885C21.994864,0.64599622,23.688883,-1.8170738E-08,25.382902,0z
|
||||
</sys:String>
|
||||
|
||||
<!--AddBoxes (the icon is free to use if we include this information in our credit section)
|
||||
Source: https://www.flaticon.com/authors/gregor-cresnar
|
||||
Ttile: Add free icon
|
||||
@@ -48,17 +40,6 @@
|
||||
</sys:String>
|
||||
</x:Array>
|
||||
|
||||
<!--File (the icon is free to use if we include this information in our credit section)
|
||||
Source: https://www.flaticon.com/authors/chanut
|
||||
Ttile: Cancel free icon
|
||||
Credit: Icon made by Chanut from www.flaticon.com-->
|
||||
<sys:String x:Key="{x:Static mbts:Icons.ProhibitedGeometryKey}">
|
||||
M32.759,0C14.696,0,0,14.695,0,32.759s14.695,32.759,32.759,32.759s32.759-14.695,32.759-32.759S50.822,0,32.759,0z
|
||||
M6,32.759C6,18.004,18.004,6,32.759,6c6.648,0,12.734,2.443,17.419,6.472L12.472,50.178C8.443,45.493,6,39.407,6,32.759z
|
||||
M32.759,59.518c-5.948,0-11.447-1.953-15.895-5.248l37.405-37.405c3.295,4.448,5.248,9.947,5.248,15.895
|
||||
C59.518,47.514,47.514,59.518,32.759,59.518z
|
||||
</sys:String>
|
||||
|
||||
<!--File (the icon is free to use if we include this information in our credit section)
|
||||
Source: https://www.flaticon.com/authors/smashicons
|
||||
Ttile: File free icon
|
||||
@@ -290,10 +271,6 @@
|
||||
M0,0L25.194,16 0,32z
|
||||
</sys:String>
|
||||
|
||||
<sys:String x:Key="{x:Static mbts:Icons.RubberStampGeometryKey}">
|
||||
M1.8950181,29.607025L24.423994,29.607025 24.423994,31.103027C24.423994,31.601013,24.025009,32,23.526993,32L2.7920208,32C2.2929976,32,1.8950181,31.601013,1.8950181,31.103027z M0,19.838013L26.218001,19.838013 26.218001,27.813019 0,27.813019z M12.960014,0C16.549,0 19.538988,2.9909973 19.538988,6.57901 19.538988,8.0750122 19.041002,9.3710022 18.243,10.46701L18.043995,10.766022C17.345997,11.863007 16.647999,12.76001 16.647999,12.76001 15.052999,15.651001 15.253011,17.745026 15.253011,17.745026L10.667015,17.745026C10.667015,17.745026 10.866997,15.651001 9.2719967,12.76001 9.2719967,12.76001 8.5739996,11.863007 7.8760016,10.766022L7.6759898,10.46701 7.5769908,10.368011C6.7790176,9.3710022 6.3810074,7.9750061 6.3810074,6.57901 6.3810074,2.9909973 9.2719967,0 12.960014,0z
|
||||
</sys:String>
|
||||
|
||||
<!--File (the icon is free to use if we include this information in our credit section)
|
||||
Source: https://www.flaticon.com/authors/smashicons
|
||||
Ttile: File free icon
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using mbts.co2;
|
||||
using mbts.collections;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
@@ -204,15 +204,13 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
void _show_order_book(Side side) {
|
||||
var ob_settings = bts.shell.orderbook.OrderBookStartupData.Default;
|
||||
ob_settings.filters = new bts.shell.orderbook.FilterStates {
|
||||
statuses = new[] { OrderStatus.Working },
|
||||
sides = new[] { side },
|
||||
security_criteria = new SecurityCriteria(security)
|
||||
};
|
||||
_bus.post(SparkTags.ShowOrderBookTag, ob_settings);
|
||||
}
|
||||
void _show_order_book(Side side) =>
|
||||
_bus.post(SparkTags.ShowOrderBookTag, new bts.shell.OrderBookWindowFactory.OrderBookStartupData(
|
||||
new bts.shell.OrderBookWindowFactory.FilterStates {
|
||||
statuses = new[] { OrderStatus.Working },
|
||||
sides = new[] { side },
|
||||
security_criteria = new SecurityCriteria(security)
|
||||
}));
|
||||
|
||||
void _show_order_ticket(MarketViewView.Columns col) {
|
||||
var order = _orders_model.factory.order(security);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using bts.api;
|
||||
using bts.collections;
|
||||
using bts.utils;
|
||||
using mbts.persist;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
@@ -9,6 +7,7 @@ using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Windows.Data;
|
||||
@@ -23,6 +22,7 @@ namespace mbts.spark {
|
||||
readonly OrdersModel _orders_model;
|
||||
IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
|
||||
|
||||
public MarketViewVM(IBus bus, OrdersModel orders_model, IObservable<StartupData> state, IWindowSettingsManager<GridSettings> settings_manager )
|
||||
: this(bus, orders_model, state, state.SelectManyInSeries(s => s.security_criteria.subscribe_md(bus)), settings_manager) { }
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace mbts.spark {
|
||||
add_col(Columns.Security, "Security", r => r.security.ToString(), nameof(Row.security), DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_col(Columns.Volume, "Volume", r => r.volume.ToString(), nameof(Row.volume), DefaultColumnWidths.Volume, volume_filter, DataGridFilterKnownTemplates.ULongMinMax);
|
||||
add_col(Columns.Last, "Last", r => r.format_price(r.last_price), nameof(Row.last_price), DefaultColumnWidths.Price, last_price_filter, DataGridFilterKnownTemplates.DoubleMinMax);
|
||||
add_col(Columns.LastSize, merged_size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UIntMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.LastSize, merged_size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UShortMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.Position, "Pos", r => r.position.ToString(), nameof(Row.position), DefaultColumnWidths.Size, position_filter, DataGridFilterKnownTemplates.IntMinMax, true, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_working_orders_col(Columns.WorkingBids, working_col_header, nameof(Row.working_bids_text), nameof(Row.working_best_bid), nameof(Row.working_bids_click), working_bid_volume_filter, BTSColors.MarketData.bid, System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.BidImpliedSize, implied_size_col_header, r => r.bid_implied_size.ToString(), nameof(Row.bid_implied_size), DefaultColumnWidths.ImpliedDirectMergedSize, bid_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
@@ -58,12 +58,12 @@ namespace mbts.spark {
|
||||
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.security.market_description(), nameof(Row.market_description), DefaultColumnWidths.SecurityKey, market_description_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_col(Columns.ClearedAlias, "Cleared Alias", r => r.security.cleared_alias(), nameof(Row.cleared_alias), DefaultColumnWidths.SecurityKey, cleared_alias_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_col(Columns.ProductDescription, "Product Description", r => r.security.product_name(), nameof(Row.product_description), DefaultColumnWidths.SecurityKey, product_description_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_col(Columns.HubAlias, "Hub Alias", r => r.security.hub_alias(), nameof(Row.hub_alias), DefaultColumnWidths.SecurityKey, hub_alias_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_col(Columns.StripName, "Strip Name", r => r.security.strip_name(), nameof(Row.strip_name), DefaultColumnWidths.SecurityKey, strip_name_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_col(Columns.MicCode, "Mic Code", r => r.security.mic_code(), nameof(Row.mic_code), DefaultColumnWidths.SecurityKey, mic_code_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) {
|
||||
@@ -81,7 +81,7 @@ namespace mbts.spark {
|
||||
add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.MViewPrice, foreground, background, DefaultSelectionHighlight, h_alignment);
|
||||
|
||||
void add_hl_merged_size_col(Columns col_key, string header, string sort_prop, string text_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) =>
|
||||
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.UIntMinMax, DefaultColumnWidths.ImpliedDirectMergedSize, foreground, null, null, h_alignment);
|
||||
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.ImpliedDirectMergedSize, foreground, null, null, h_alignment);
|
||||
|
||||
void add_hl_working_orders_col(Columns col_key, string header, string text_prop, string sort_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) =>
|
||||
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.IntMinMax, working_orders_col_width, foreground, null, null, h_alignment);
|
||||
@@ -180,40 +180,6 @@ namespace mbts.spark {
|
||||
return grid_settings_manager.create();
|
||||
}
|
||||
|
||||
public static IComparer<Row> get_column_comparer(Columns column) {
|
||||
switch (column) {
|
||||
case Columns.Security: return wrap(r => r.security);
|
||||
case Columns.SecurityKey: return wrap(r => r.security.Id);
|
||||
case Columns.Volume: return wrap(r => r.volume);
|
||||
case Columns.Last: return wrap(r => r.last_price);
|
||||
case Columns.LastSize: return wrap(r => r.last_size);
|
||||
case Columns.Position: return wrap(r => r.position);
|
||||
case Columns.WorkingBids: return wrap(r => r.working_bid_volume);
|
||||
case Columns.BidImpliedSize: return wrap(r => r.bid_implied_size);
|
||||
case Columns.BidDirectSize: return wrap(r => r.bid_direct_size);
|
||||
case Columns.BidMergedSize: return wrap(r => r.bid_merged_size);
|
||||
case Columns.Bid: return wrap(r => r.bid_price);
|
||||
case Columns.Ask: return wrap(r => r.ask_price);
|
||||
case Columns.AskMergedSize: return wrap(r => r.ask_merged_size);
|
||||
case Columns.AskDirectSize: return wrap(r => r.ask_direct_size);
|
||||
case Columns.AskImpliedSize: return wrap(r => r.ask_implied_size);
|
||||
case Columns.WorkingAsks: return wrap(r => r.working_ask_volume);
|
||||
case Columns.Settlement: return wrap(r => r.settlement);
|
||||
case Columns.DayChange: return wrap(r => r.day_change);
|
||||
case Columns.SessionLow: return wrap(r => r.session_low);
|
||||
case Columns.SessionHigh: return wrap(r => r.session_high);
|
||||
case Columns.MarketDescription: return wrap(r => r.market_description);
|
||||
case Columns.ClearedAlias: return wrap(r => r.security.cleared_alias());
|
||||
case Columns.ProductDescription: return wrap(r => r.security.product_name());
|
||||
case Columns.HubAlias: return wrap(r => r.security.hub_alias());
|
||||
case Columns.StripName: return wrap(r => r.security.strip_name());
|
||||
case Columns.MicCode: return wrap(r => r.security.mic_code());
|
||||
default: return null;
|
||||
}
|
||||
IComparer<Row> wrap<T>(Func<Row, T> selector) =>
|
||||
Comparer<T>.Default.wrap(selector);
|
||||
}
|
||||
|
||||
#region Filters
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_filter { get; }
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> bid_price_filter { get; } = new DoubleMinMax<Row>(r => r.bid_price, nameof(Row.bid_price));
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using bts.api;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
using mbts.persist;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
@@ -12,13 +11,12 @@ using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
|
||||
namespace mbts.spark {
|
||||
class MarketViewWindow : SparkWindow<MarketViewWindowFactory.StartupData>, ISharedWindow, ISlowMeasureContentSize {
|
||||
const string _window_type = "MarketView", _window_type_display_name = "Market View";
|
||||
class MarketViewWindow : SparkWindow<MarketViewWindowFactory.StartupData>, ISharedWindow {
|
||||
const string _window_type = "MarketView", _thread_id = "MarketView", _window_type_display_name = "Market View";
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly AllowOnce _init_guard = new AllowOnce("MarketView");
|
||||
readonly IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
@@ -27,9 +25,6 @@ namespace mbts.spark {
|
||||
MarketViewWindowFactory.StartupData _state;
|
||||
IReadOnlyList<SparkCommand> _commands;
|
||||
IDisposable _subscriptions;
|
||||
SecurityCriteria
|
||||
_selected_security_criteria_backing,
|
||||
_sorted_selected_security_criteria_backing;
|
||||
|
||||
public MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state, WindowSettingsManager<GridSettings> settings_manager)
|
||||
: base("Market View", bus, factory, SparkCommands.ShowMarketView.icon) {
|
||||
@@ -37,7 +32,6 @@ namespace mbts.spark {
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
Icon.Freeze();
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().market_view ? "MarketView" : null;
|
||||
}
|
||||
|
||||
event Action<MarketViewWindowFactory.StartupData> _state_changed;
|
||||
@@ -48,53 +42,12 @@ namespace mbts.spark {
|
||||
public override MarketViewWindowFactory.StartupData SaveState =>
|
||||
_state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.get_filter_states(), _vm.get_grid_save_state(), _vm.rows_view.get_sorting_criteria(_vm.columns));
|
||||
|
||||
public override double MinWidth => MarketViewWindowFactory.StartupData.DefaultGridSettings.columns.Where(c => c.is_visible).Sum(gcs => gcs.width)
|
||||
public override double MinWidth => MarketViewWindowFactory.StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
|
||||
public string thread_id { get; }
|
||||
|
||||
SecurityCriteria _selected_security_criteria {
|
||||
get {
|
||||
if (_selected_security_criteria_backing == null) {
|
||||
if (_sorted_selected_security_criteria_backing != null) {
|
||||
_selected_security_criteria_backing = _sorted_selected_security_criteria_backing;
|
||||
} else if (_vm != null)
|
||||
_selected_security_criteria_backing = new SecurityCriteria(_vm.selected_rows.Select(r => r.security));
|
||||
}
|
||||
return _selected_security_criteria_backing;
|
||||
}
|
||||
}
|
||||
SecurityCriteria _sorted_selected_security_criteria {
|
||||
get {
|
||||
if (_sorted_selected_security_criteria_backing == null) {
|
||||
if (_vm != null) {
|
||||
var sorting_criteria = _vm.rows_view.get_sorting_criteria(_vm.columns);
|
||||
var comp_builder = new ComparerBuilder<MarketViewVM.Row>();
|
||||
foreach (var sc in sorting_criteria) {
|
||||
if (_vm.columns.TryGetFirst(col => string.Equals(col.id, sc.col_name, StringComparison.CurrentCultureIgnoreCase), out var match)) {
|
||||
if (match is ui.controls.DIYGrid.IKeyedColumn<MarketViewView.Columns> kcol) {
|
||||
var col_comp = MarketViewVM.get_column_comparer(kcol.key);
|
||||
if (col_comp != null) {
|
||||
comp_builder.add(sc.is_ascending ? col_comp : col_comp.reverse());
|
||||
} else {
|
||||
Log.Ui.warn($"{_window_type} is unable to find the row comparer for the '{sc.col_name}' column.");
|
||||
}
|
||||
} else {
|
||||
Log.Ui.warn($"{_window_type} is unable find a column with id '{sc.col_name}.'");
|
||||
}
|
||||
}
|
||||
}
|
||||
var comp = comp_builder.to_comparer();
|
||||
_sorted_selected_security_criteria_backing = new SecurityCriteria(_vm.selected_rows.Ordered(comp).Select(r => r.security));
|
||||
}
|
||||
}
|
||||
return _sorted_selected_security_criteria_backing;
|
||||
}
|
||||
}
|
||||
|
||||
bool _has_selected_rows() => _vm != null && _vm.selected_rows.Count > 0;
|
||||
public string thread_id => _thread_id;
|
||||
|
||||
public void thread_init() {
|
||||
_init_guard.check_access();
|
||||
@@ -102,44 +55,17 @@ namespace mbts.spark {
|
||||
var state_src = Observable.FromEvent<Action<MarketViewWindowFactory.StartupData>, MarketViewWindowFactory.StartupData>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state);
|
||||
_vm = new MarketViewVM(_bus, _orders_model, state_src, _settings_manager);
|
||||
_view = new MarketViewView { DataContext = _vm };
|
||||
_view._grid.Sorting += on_grid_sorting;
|
||||
/// The 'CanExecute' plumbing for the cmds below is designed to avoid potentially costly .ToList() operations on the selected_rows/securities.
|
||||
/// Some of the cmds post ITags with data that is, or has, a SecurityCriteria member; the SecurityCriteria constructor creates a new List of the specified securities.
|
||||
/// For these commands, the objective is to evaluate 'CanExecute' without instantiating the SecurityCriteria (or more importantly, without creating a new List).
|
||||
// show time and sales
|
||||
var show_tas_cmd = new DelegateCommand(() => {
|
||||
var sc = _selected_security_criteria;
|
||||
if (sc.is_defined())
|
||||
_bus.post(SparkTags.ShowTimeAndSalesTag, sc);
|
||||
}, _has_selected_rows);
|
||||
var show_tas_actual_cmd = SparkCommands.ShowTimeAndSales;
|
||||
var show_tas_cmd_wrapper = new SparkCommandCommand(show_tas_actual_cmd.title, show_tas_actual_cmd.icon, show_tas_cmd);
|
||||
// show fills
|
||||
var show_fills_cmd = new DelegateCommand(() => {
|
||||
var sc = _selected_security_criteria;
|
||||
_bus.post(SparkTags.ShowFillsTag, new FillsWindowFactory.StartupData(
|
||||
new FillsWindowFactory.FilterStates {
|
||||
security = sc.is_defined() ? sc : _state.security_criteria
|
||||
}));
|
||||
});
|
||||
var show_fills_actual_cmd = SparkCommands.ShowFills();
|
||||
var show_fills_cmd_wrapper = new SparkCommandCommand(show_fills_actual_cmd.title, show_fills_actual_cmd.icon, show_fills_cmd, SparkCommands.KeyGestures.ShowFills);
|
||||
// show synthetic create
|
||||
var show_synth_create_cmd = new DelegateCommand(() => {
|
||||
var sc = _sorted_selected_security_criteria;
|
||||
if (sc.is_defined())
|
||||
_bus.post(SparkTags.ShowSyntheticCreateTag, sc);
|
||||
}, () => _vm.selected_rows.Count > 0 && _vm.selected_rows.Count <= 24);
|
||||
var show_synth_create_actual_cmd = SparkCommands.ShowSyntheticCreate();
|
||||
var show_synth_create_cmd_wrapper = new SparkCommandCommand(show_synth_create_actual_cmd.title, show_synth_create_actual_cmd.icon, show_synth_create_cmd, SparkCommands.KeyGestures.ShowSyntheticCreate);
|
||||
|
||||
var update_securities_cmd = SparkCommands.change_securities_callback(_window_type_display_name);
|
||||
_commands = new List<SparkCommand> {
|
||||
SparkCommands.ShowLadders(SparkCommands.KeyGestures.ShowLadder).complete(_vm.selected_security_mds_observable(), smds => smds.Count > 0),
|
||||
SparkCommands.ShowOrderTicketFromSecurity(SparkCommands.KeyGestures.ShowOrderTicket).complete(_vm.current_security_md, md => md.HasValue, ConvertUtils.ValueOrDefault),
|
||||
show_fills_cmd_wrapper,
|
||||
show_tas_cmd_wrapper,
|
||||
show_synth_create_cmd_wrapper,
|
||||
SparkCommands.ShowLadders.complete(_vm.selected_security_mds_observable(), smds => smds.Count > 0),
|
||||
SparkCommands.ShowOrderTicketFromSecurity.complete(_vm.current_security_md, md => md.HasValue, ConvertUtils.ValueOrDefault),
|
||||
SparkCommands.ShowFills.complete(_vm.selected_securities_observable(), _ => true,
|
||||
secs => new FillsWindowFactory.StartupData(new FillsWindowFactory.FilterStates {
|
||||
security = secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria
|
||||
})
|
||||
),
|
||||
SparkCommands.ShowTimeAndSales.complete(_vm.selected_securities_observable(), _ => true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
|
||||
SparkCommands.ShowSyntheticCreate.complete(_vm.sorted_selected_securities_observable(), secs => secs.Count > 0 && secs.Count <= 24, secs => new SecurityCriteria(secs)),
|
||||
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)),
|
||||
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
|
||||
SparkCommands.window_settings_command(new DialogCommand(() => {
|
||||
@@ -160,27 +86,8 @@ namespace mbts.spark {
|
||||
return dialog;
|
||||
}))
|
||||
};
|
||||
//
|
||||
_vm.selection_changed += on_selection_changed;
|
||||
_subscriptions = new CompositeDisposable(_bus.subscribe(update_securities_cmd.tag).OnDispatcher().Subscribe(c =>
|
||||
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.get_filter_states(), _vm.grid_settings_manager.create())))) {
|
||||
() => {
|
||||
_vm.selection_changed -= on_selection_changed;
|
||||
_view._grid.Sorting -= on_grid_sorting;
|
||||
}
|
||||
};
|
||||
|
||||
void on_selection_changed((IEnumerable<MarketViewVM.Row> removed, IEnumerable<MarketViewVM.Row> added) change) {
|
||||
// invalidate selected security criteria
|
||||
_selected_security_criteria_backing = null;
|
||||
_sorted_selected_security_criteria_backing = null;
|
||||
show_tas_cmd.RaiseCanExecuteChanged();
|
||||
show_fills_cmd.RaiseCanExecuteChanged();
|
||||
show_synth_create_cmd.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
void on_grid_sorting(object sender, ui.controls.DIYGridSortingEventArgs e) =>
|
||||
_sorted_selected_security_criteria_backing = null;
|
||||
_subscriptions = _bus.subscribe(update_securities_cmd.tag).OnDispatcher().Subscribe(c =>
|
||||
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.get_filter_states(), _vm.grid_settings_manager.create())));
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using bts.api;
|
||||
using bts.co2;
|
||||
using mbts.co2;
|
||||
using mbts.collections.extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
using bts.api;
|
||||
using bts.co2;
|
||||
using bts.collections;
|
||||
using mbts.persist;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using static bts.shell.TimeAndSalesWindowFactory;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
using mbts.persist;
|
||||
using mbts.ui.settings;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
class TimeAndSalesVM : SecurityGrid<TimeAndSalesVM.Row, TradeInfo> {
|
||||
|
||||
@@ -19,7 +19,7 @@ using static bts.shell.TimeAndSalesWindowFactory;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
|
||||
namespace mbts.spark {
|
||||
class TimeAndSalesWindow : SparkWindow<StartupData>, ISharedWindow {
|
||||
class TimeAndSalesWindow : SparkWindow<StartupData>, ISlowWindow {
|
||||
const string _window_type = "TimeAndSales", _window_type_display_name = "Time And Sales";
|
||||
readonly MDConsumer _consumer;
|
||||
readonly TimeAndSalesWindowFactory _factory;
|
||||
@@ -48,7 +48,7 @@ namespace mbts.spark {
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
public string thread_id { get; }
|
||||
|
||||
public override StartupData SaveState => _state = _vm.value.create();
|
||||
|
||||
public override void Dispose() =>
|
||||
@@ -62,9 +62,9 @@ namespace mbts.spark {
|
||||
var current_smd_source = connectable_builder.create(vm => vm.current_security_md_observable(_bus)).OnDispatcher();
|
||||
var selected_secs_source = connectable_builder.create(vm => vm.selected_securities_observable()).OnDispatcher();
|
||||
_commands = new SparkCommand[] {
|
||||
SparkCommands.ShowLadder(SparkCommands.KeyGestures.ShowLadder).complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
|
||||
SparkCommands.ShowOrderTicketFromSecurity(SparkCommands.KeyGestures.ShowOrderTicket).complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
|
||||
SparkCommands.ShowFills(SparkCommands.KeyGestures.ShowFills).complete(selected_secs_source, always_true, get_fills_startup_data),
|
||||
SparkCommands.ShowLadder.complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
|
||||
SparkCommands.ShowOrderTicketFromSecurity.complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
|
||||
SparkCommands.ShowFills.complete(selected_secs_source, always_true, get_fills_startup_data),
|
||||
SparkCommands.ShowMarketView.complete(selected_secs_source, always_true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
|
||||
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)),
|
||||
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
|
||||
|
||||
@@ -420,6 +420,7 @@
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<c:HelpButton Grid.Column="8" Margin="3,0,0,0" ManPage="bts_spark/spreader/#creating-a-synthetic" />
|
||||
</Grid>
|
||||
<c:DataGridEx
|
||||
x:Name="PART_DataGrid"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using bts.api;
|
||||
using bts.collections;
|
||||
using bts.spark;
|
||||
using mbts.spark;
|
||||
using mbts.ui.adorners;
|
||||
@@ -8,6 +7,7 @@ using mbts.ui.utility;
|
||||
using mbts.user.settings;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/Modules/Spreader/ExecutionStrategyEditor.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<Style TargetType="ScrollViewer">
|
||||
<Setter Property="u:ScrollViewerCorrector.FixNestedScrolling" Value="False"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
|
||||
@@ -16,7 +16,7 @@ using System.Windows;
|
||||
using static bts.shell.SyntheticManagementWindowFactory;
|
||||
|
||||
namespace mbts.spark {
|
||||
class SyntheticManagementWindow : SparkWindow<StartupData>, ISharedWindow {
|
||||
class SyntheticManagementWindow : SparkWindow<StartupData>, ISlowWindow {
|
||||
const string _window_type = "SyntheticManagement";
|
||||
SingleValuePump<SyntheticManagementDialogVM> _vm = new SingleValuePump<SyntheticManagementDialogVM>();
|
||||
StartupData _state;
|
||||
@@ -33,20 +33,8 @@ namespace mbts.spark {
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
Icon.Freeze();
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().synthetic_management ? "SyntheticManagement" : null;
|
||||
}
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override UIElement Content => _view;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public override StartupData SaveState => _state = _vm.value.create();
|
||||
public override double MinWidth =>
|
||||
StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
public string thread_id { get; }
|
||||
|
||||
public void thread_init() {
|
||||
Log.notnull(_state);
|
||||
|
||||
@@ -66,15 +54,14 @@ namespace mbts.spark {
|
||||
), SparkCommand.create("Clone", Icons.CloneSynthetic, SparkTags.ShowSyntheticCloneTag).complete(
|
||||
current_row_connectable_builder.create(observe_row_strategy),
|
||||
rs => rs.HasValue, select_row_strategy_tuple
|
||||
), SparkCommands.ShowLadder(SparkCommands.KeyGestures.ShowLadder).complete(
|
||||
), SparkCommands.ShowLadder.complete(
|
||||
vm_connectable_builder.create(vm => vm.current_security_md), md => md.HasValue, ConvertUtils.ValueOrDefault
|
||||
), SparkCommands.ShowOrders.complete(() => {
|
||||
var ob_settings = bts.shell.orderbook.OrderBookStartupData.Default;
|
||||
ob_settings.filters = new bts.shell.orderbook.FilterStates {
|
||||
security_criteria = new SecurityCriteria(_vm.value.row_vms.Select(r => r.security))
|
||||
};
|
||||
return ob_settings;
|
||||
}), SparkCommands.window_settings_command(new DialogCommand(() => {
|
||||
), SparkCommands.ShowOrders.complete(() => new OrderBookWindowFactory.OrderBookStartupData {
|
||||
filters = new OrderBookWindowFactory.FilterStates {
|
||||
security_criteria = new SecurityCriteria(_vm.value.row_vms.Select(r => r.security))
|
||||
}
|
||||
}
|
||||
), SparkCommands.window_settings_command(new DialogCommand(() => {
|
||||
var vm = new WindowSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
|
||||
_settings_manager,
|
||||
us => us.synthetic_management_grid,
|
||||
@@ -114,6 +101,16 @@ namespace mbts.spark {
|
||||
else _view.DataContext = _vm.value;
|
||||
}
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override UIElement Content => _view;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public override StartupData SaveState => _state = _vm.value.create();
|
||||
public override double MinWidth =>
|
||||
StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
|
||||
public override void Dispose() {
|
||||
Disposable.dispose(ref _vm);
|
||||
Commands.OfType<IDisposable>().DisposeAll();
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
+6
-4
@@ -1,10 +1,12 @@
|
||||
using bts.shell;
|
||||
using mbts.ui.spark;
|
||||
using bts.spark;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
using PriceType = bts.spark.ConditionalQuoting.Types.PriceType;
|
||||
using mbts.collections.extensions;
|
||||
using System.Collections.Generic;
|
||||
using bts.shell;
|
||||
|
||||
namespace mbts.spark {
|
||||
namespace mbts.spark
|
||||
{
|
||||
partial class SyntheticEditorVM
|
||||
{
|
||||
class ConditionalQuotingPriceTypeModel : RowModelBase<PriceType>
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
using bts.spark;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
partial class SyntheticEditorVM
|
||||
{
|
||||
class QuoteQueueHolderModel : RowModelBase<uint>, IHaveFontParams
|
||||
{
|
||||
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
|
||||
public QuoteQueueHolderModel()
|
||||
{
|
||||
}
|
||||
public override string display_name { get; } = "Queue Holder Length";
|
||||
public override string help_text { get; } = "How many levels of queue to hold";
|
||||
public override string man_page { get; } = string.Empty;
|
||||
public override ParameterSection[] section_path { get; } = new[] { Sections.Quote };
|
||||
public FontWeight font_weight => FontWeights.SemiBold;
|
||||
|
||||
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.QuoteParams.NQueueHolderOrders = _value_from_store(leg_tup);
|
||||
|
||||
public override uint get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_value(leg_tup).NQueueHolderOrders;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value)
|
||||
{
|
||||
_value_store[leg_tup.SecurityKey] = value;
|
||||
leg_tup.leg_params.QuoteParams.NQueueHolderOrders = value ;
|
||||
}
|
||||
|
||||
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_value(leg_tup) != null;
|
||||
|
||||
QuoteParams _get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.QuoteParams;
|
||||
|
||||
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
|
||||
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => 0u);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using bts.api;
|
||||
using bts.collections;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
@@ -73,7 +72,7 @@ namespace mbts.spark {
|
||||
_has_working_orders = backing(nameof(has_working_orders), false);
|
||||
contract_picker_model = new ContractPickerModel(bus, false, null, new SecurityCriteria(new[] {
|
||||
new SecurityCriteriaClause() {
|
||||
exchanges = EnumHelper.get_values_except(Venue.Bts).ToArray()
|
||||
exchanges = EnumHelper.get_values_except(Venue.Bts).ToLArray()
|
||||
}
|
||||
}));
|
||||
contract_picker_model.cancel_action = _close_new_leg_popup;
|
||||
@@ -200,16 +199,14 @@ namespace mbts.spark {
|
||||
|
||||
var post_hedge_opposite_market_based_cross_threshold = new PostHedgeOppositeMarketVolumeBasedCrossThresholdModel();
|
||||
var post_hedge_opposite_market_based_cross_cross_ticks = new PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel();
|
||||
var is_quote_model = new IsQuoteModel();
|
||||
rows = new IRowModel[] {
|
||||
_quantity_model,
|
||||
_price_mult_model,
|
||||
is_quote_model,
|
||||
new IsQuoteModel(),
|
||||
//new EnableHedgeModel(), //TAHA_SPREADER
|
||||
new HedgeRatioModel(),
|
||||
//new MinLeanQtyModel(), //TAHA_SPREADER
|
||||
//new QuoteHiddenModel(),
|
||||
new QuoteQueueHolderModel(),
|
||||
new MarketDataTypeModel(),
|
||||
new QuoteThrottleEnabledModel(quote_throttle_inside_ticks, quote_throttle_outside_ticks),
|
||||
quote_throttle_inside_ticks,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using bts.api;
|
||||
using bts.collections;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.persist;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -168,13 +169,12 @@ namespace mbts.spark {
|
||||
void _show_ladder() =>
|
||||
_bus.post(SparkTags.ShowLadderTag, md);
|
||||
|
||||
void _show_order_book() {
|
||||
var ob_settings = bts.shell.orderbook.OrderBookStartupData.Default;
|
||||
ob_settings.filters = new bts.shell.orderbook.FilterStates {
|
||||
security_criteria = new SecurityCriteria(security)
|
||||
};
|
||||
_bus.post(SparkTags.ShowOrderBookTag, ob_settings);
|
||||
}
|
||||
void _show_order_book() =>
|
||||
_bus.post(SparkTags.ShowOrderBookTag, new OrderBookWindowFactory.OrderBookStartupData {
|
||||
filters = new OrderBookWindowFactory.FilterStates {
|
||||
security_criteria = new SecurityCriteria(security)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/Order/AlgoTemplateTreeView.xaml"/>
|
||||
<ResourceDictionary Source="/view/Controls/Order/AlgoPickerNodeView.xaml"/>
|
||||
<ResourceDictionary Source="/view/Controls/Order/AlgoContentTemplateSelector.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!--Primitive Constants-->
|
||||
<sys:Double x:Key="HorizontalPush1">4</sys:Double>
|
||||
@@ -557,27 +559,85 @@
|
||||
</Grid>
|
||||
</RadioButton>
|
||||
</Grid>
|
||||
<usp:AlgoEditorView
|
||||
<GroupBox
|
||||
x:Name="content_host"
|
||||
Grid.Row="2"
|
||||
DataContext="{Binding content_source}"
|
||||
Background="Transparent"
|
||||
BorderBrush="{Binding algo_root, Converter={StaticResource OrderTypeBrushConverter}}"
|
||||
BorderBrush="{Binding content_source.algo_root, Converter={StaticResource OrderTypeBrushConverter}}"
|
||||
Margin="0,6,0,0"
|
||||
Padding="12,8"
|
||||
/>
|
||||
<StackPanel Grid.Row="3" HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<StackPanel.Margin>
|
||||
<Thickness Left="{StaticResource DialogSkirtPadding.Left}" Top="{StaticResource VerticalPush2}" Right="{StaticResource DialogSkirtButtonPadding.Right}" Bottom="{StaticResource DialogSkirtPadding.Bottom}"/>
|
||||
</StackPanel.Margin>
|
||||
<Button ToolTipService.ShowOnDisabled="True" Margin="{StaticResource DialogSkirtButtonMargin}" Command="{Binding save_cmd}" CommandParameter="{Binding content_source}" Width="100" ToolTip="{Binding save_cmd_tooltip}" IsDefault="True">
|
||||
<TextBlock Text="{Binding selected_definition.name, StringFormat='{}Save {0}'}"/>
|
||||
</Button>
|
||||
<Button ToolTipService.ShowOnDisabled="True" Margin="{StaticResource DialogSkirtButtonMargin}" Command="{Binding revert_cmd}" CommandParameter="{Binding content_source}" Width="100" ToolTip="{Binding revert_cmd_tooltip}">
|
||||
<TextBlock Text="{Binding selected_definition.name, StringFormat='{}Revert {0}'}"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
>
|
||||
<GroupBox.Style>
|
||||
<Style TargetType="GroupBox" BasedOn="{StaticResource ContentHostGroupBoxStyle}">
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="BorderBrush" Value="Transparent">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
</Trigger>
|
||||
<Trigger Property="usp:AlgoStudioView.IsMouseOverNodeContent" Value="True">
|
||||
<Setter Property="BorderThickness" Value="2"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</GroupBox.Style>
|
||||
<GroupBox.Header>
|
||||
<ComboBox
|
||||
Grid.Column="3"
|
||||
ToolTipService.ShowOnDisabled="True"
|
||||
SelectedItem="{Binding selected_definition.algo_root}"
|
||||
ItemsSource="{Binding selected_definition.algos_view}"
|
||||
Margin="{StaticResource Margin.LP1}"
|
||||
Width="140"
|
||||
>
|
||||
<ComboBox.Style>
|
||||
<Style TargetType="ComboBox">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding content_source}" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding selected_definition.templates.Count, Converter={StaticResource IntIsZeroConverter}, FallbackValue={u:False}}" Value="{u:True}">
|
||||
<Setter Property="IsEnabled" Value="True"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding selected_definition.templates.Count, Converter={StaticResource IntIsZeroConverter}, FallbackValue={u:False}}" Value="{u:False}">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
<Setter Property="ToolTip" Value="Cannot change structure while templates are present."/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.Style>
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<usp:OrderTypeItem DisplayName="{Binding display_name}" BadgeFill="{Binding meta.Color, Converter={StaticResource ColorToBrushConverter}}"/>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</GroupBox.Header>
|
||||
<ContentControl
|
||||
Grid.IsSharedSizeScope="True"
|
||||
Content="{Binding selected_definition.algo_root}"
|
||||
ContentTemplateSelector="{StaticResource AlgoTemplateSelector}"
|
||||
Margin="{StaticResource Margin.TP1}"
|
||||
>
|
||||
<ContentControl.Resources>
|
||||
<Style TargetType="GroupBox" BasedOn="{StaticResource ContentHostGroupBoxStyle}"/>
|
||||
</ContentControl.Resources>
|
||||
</ContentControl>
|
||||
</GroupBox>
|
||||
<c:DialogSkirt Grid.Row="3" Margin="{StaticResource Margin.TP2}" Background="Transparent">
|
||||
<c:DialogSkirt.RightItems>
|
||||
<x:ArrayExtension Type="{x:Type Button}">
|
||||
<Button ToolTipService.ShowOnDisabled="True" Command="{Binding save_cmd}" CommandParameter="{Binding content_source}" Width="100" Style="{StaticResource {x:Static c:DialogSkirt.DialogSkirtButtonStyleKey}}" ToolTip="{Binding save_cmd_tooltip}" IsDefault="True">
|
||||
<TextBlock Text="{Binding selected_definition.name, StringFormat='{}Save {0}'}"/>
|
||||
</Button>
|
||||
<Button ToolTipService.ShowOnDisabled="True" Command="{Binding revert_cmd}" CommandParameter="{Binding content_source}" Width="100" Style="{StaticResource {x:Static c:DialogSkirt.DialogSkirtButtonStyleKey}}" ToolTip="{Binding revert_cmd_tooltip}">
|
||||
<TextBlock Text="{Binding selected_definition.name, StringFormat='{}Revert {0}'}"/>
|
||||
</Button>
|
||||
</x:ArrayExtension>
|
||||
</c:DialogSkirt.RightItems>
|
||||
</c:DialogSkirt>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -1,6 +1,8 @@
|
||||
using mbts.spark;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.extensions;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
@@ -16,11 +18,26 @@ namespace mbts.ui.spark {
|
||||
public AlgoStudioView() {
|
||||
InitializeComponent();
|
||||
DataContextChanged += (s, e) => set_controller();
|
||||
content_host.PreviewMouseMove += content_host_PreviewMouseMove;
|
||||
content_host.MouseEnter += content_host_MouseEnter;
|
||||
content_host.MouseLeave += content_host_MouseLeave;
|
||||
set_controller();
|
||||
// local methods
|
||||
void set_controller() => Disposable.replace(ref _controller, AlgoStudioViewControllerFactory.create(DataContext, _tree_view, name_text_box, custom_badge_color_picker_button, custom_badge_color_picker_popup));
|
||||
void content_host_PreviewMouseMove(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
|
||||
void content_host_MouseEnter(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
|
||||
void content_host_MouseLeave(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
|
||||
void refresh_is_mouse_over_content_host(MouseEventArgs e) => content_host.set_current_if_changed(IsMouseOverNodeContentProperty, this.ChildAtPoint<GroupBox>(e.GetPosition(this)) == content_host);
|
||||
}
|
||||
|
||||
#region IsMouseOverNodeContent
|
||||
public static readonly DependencyProperty IsMouseOverNodeContentProperty =
|
||||
DependencyProperty.RegisterAttached("IsMouseOverNodeContent", typeof(bool), typeof(AlgoStudioView), new PropertyMetadata(false));
|
||||
|
||||
public static bool GetIsMouseOverNodeContent(DependencyObject d) => (bool)d.GetValue(IsMouseOverNodeContentProperty);
|
||||
public static void SetIsMouseOverNodeContent(DependencyObject d, bool value) => d.SetValue(IsMouseOverNodeContentProperty, value);
|
||||
#endregion
|
||||
|
||||
public new AlgoStudioVM DataContext {
|
||||
get => base.DataContext as AlgoStudioVM;
|
||||
set => base.DataContext = value;
|
||||
|
||||
@@ -13,7 +13,7 @@ using System.Windows;
|
||||
using StartupData = bts.shell.AlgoStudioWindowFactory.StartupData;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal class AlgoStudioWindow : SparkWindow<StartupData>, ISharedWindow {
|
||||
internal class AlgoStudioWindow : SparkWindow<StartupData>, IShellWindow {
|
||||
public const string
|
||||
NAME = "Algo Studio",
|
||||
TemplatesEnabledKey = nameof(TemplatesEnabledKey),
|
||||
@@ -37,7 +37,7 @@ namespace mbts.spark {
|
||||
: base(NAME, bus, factory, SparkCommands.ShowAlgoStudio.icon) {
|
||||
_model = model;
|
||||
_state = state;
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().algo_studio ? "AlgoStudio" : null;
|
||||
thread_init();
|
||||
}
|
||||
|
||||
public override StartupData SaveState => _state = _vm.create();
|
||||
@@ -45,12 +45,12 @@ namespace mbts.spark {
|
||||
public override string window_type => NAME;
|
||||
public override UIElement Content => _view;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public string thread_id => NAME;
|
||||
public override double MinWidth =>
|
||||
SPARK_MENU_COLLAPSED_WIDTH +
|
||||
LAUNCHER_BUTTON_BORDER_WIDTH +
|
||||
DEFAULT_CONTENT_WIDTH;
|
||||
public override double MinHeight => DEFAULT_CONTENT_HEIGHT;
|
||||
public string thread_id { get; }
|
||||
|
||||
bts.config.AlgoStudioOptions _ui_configuration => _model.algo_model.ui_configuration;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using bts.utils;
|
||||
using mbts.spark.utilities;
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.spark {
|
||||
struct AlgoData : IEquatable<AlgoData> {
|
||||
@@ -37,31 +36,4 @@ namespace mbts.spark {
|
||||
public static bool operator ==(AlgoData a, AlgoData b) => a.Equals(b);
|
||||
public static bool operator !=(AlgoData a, AlgoData b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
struct AlgoDataVM : IEquatable<AlgoDataVM> {
|
||||
public readonly AlgoData data;
|
||||
public readonly AlgoDefinitionMetadata? meta;
|
||||
|
||||
public AlgoDataVM(AlgoData data, AlgoDefinitionMetadata? meta = null) {
|
||||
this.data = data;
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
public string name => data.name;
|
||||
public Brush badge_fill => OrderUtils.badge_fill(meta, OrderDataHelper.get_metadata(data.value.CustomCase));
|
||||
public AlgoDisplayGroup group => data.key.display_group();
|
||||
|
||||
public override string ToString() =>
|
||||
data.name;
|
||||
|
||||
public static AlgoDataVM from_data(AlgoData data, AlgoDefinitionMetadata? meta) =>
|
||||
new AlgoDataVM(data, meta);
|
||||
|
||||
public static AlgoDataVM from_data((AlgoData algo, AlgoDefinitionMetadata? meta) tup) =>
|
||||
new AlgoDataVM(tup.algo, tup.meta);
|
||||
|
||||
public bool Equals(AlgoDataVM other) => data == other.data && meta == other.meta;
|
||||
public override bool Equals(object obj) => obj is AlgoDataVM other && Equals(other);
|
||||
public override int GetHashCode() => EqualityComparer.aggregate_hash(data.GetHashCode(), meta.GetHashCode());
|
||||
}
|
||||
}
|
||||
@@ -1,76 +1,9 @@
|
||||
using bts.api.orderman;
|
||||
using bts.persist.algo;
|
||||
using bts.utils;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
|
||||
namespace mbts.spark {
|
||||
[ProtoContract]
|
||||
struct AlgoID : IEquatable<AlgoID> {
|
||||
[ProtoMember(1)]
|
||||
readonly CustomOrderDefinition.CustomOneofCase? _factory_key;
|
||||
[ProtoMember(2)]
|
||||
readonly string _dynamic_key;
|
||||
[ProtoMember(3)]
|
||||
readonly uint? _persisted_key;
|
||||
|
||||
public AlgoID(CustomOrderDefinition.CustomOneofCase factory_key) {
|
||||
_factory_key = factory_key;
|
||||
_dynamic_key = null;
|
||||
_persisted_key = null;
|
||||
}
|
||||
|
||||
public AlgoID(string dynamic_key) {
|
||||
_factory_key = null;
|
||||
_dynamic_key = dynamic_key;
|
||||
_persisted_key = null;
|
||||
}
|
||||
|
||||
public AlgoID(uint persisted_key) {
|
||||
_factory_key = null;
|
||||
_dynamic_key = null;
|
||||
_persisted_key = persisted_key;
|
||||
}
|
||||
|
||||
public static AlgoID from_order_def(OrderDefinitionImmutable order_def) {
|
||||
switch (order_def.CustomDef.CustomCase) {
|
||||
case CustomOrderDefinition.CustomOneofCase.Composite: return new AlgoID(order_def.CustomDef.Composite.AlgoTemplateKey);
|
||||
case CustomOrderDefinition.CustomOneofCase.Dynamic: return new AlgoID(order_def.CustomDef.Dynamic.DynamicOrderType);
|
||||
default: return new AlgoID(order_def.CustomDef.CustomCase);
|
||||
}
|
||||
}
|
||||
|
||||
public static AlgoID from_algo_key(AlgoKey key) {
|
||||
switch (key.mode) {
|
||||
case AlgoKey.Mode.Factory:
|
||||
if (key.factory.structure.structure.Count > 0)
|
||||
return new AlgoID((CustomOrderDefinition.CustomOneofCase)key.factory.structure.structure[0]);
|
||||
break;
|
||||
case AlgoKey.Mode.Persisted:
|
||||
return new AlgoID(key.persisted.id_uint);
|
||||
}
|
||||
return new AlgoID(CustomOrderDefinition.CustomOneofCase.None);
|
||||
}
|
||||
|
||||
public bool Equals(AlgoID other) =>
|
||||
_factory_key == other._factory_key &&
|
||||
_dynamic_key == other._dynamic_key &&
|
||||
_persisted_key == other._persisted_key;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is AlgoID && Equals(obj);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
_factory_key?.GetHashCode() ?? 0,
|
||||
_dynamic_key?.GetHashCode() ?? 0,
|
||||
_persisted_key?.GetHashCode() ?? 0);
|
||||
|
||||
public static bool operator ==(AlgoID a, AlgoID b) => a.Equals(b);
|
||||
public static bool operator !=(AlgoID a, AlgoID b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
/// TODO: Remove <see cref="AlgoKey"/>, and redirect references to <see cref="AlgoID"/>.
|
||||
struct AlgoKey : IEquatable<AlgoKey> {
|
||||
readonly AlgoPersistedKey? _persisted_key;
|
||||
readonly AlgoNameStructureKey? _factory_key;
|
||||
@@ -150,4 +83,4 @@ namespace mbts.spark {
|
||||
|
||||
InvalidOperationException _unexpected_mode_ex() => new InvalidOperationException($"Unexpected Mode '{mode}.'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.utility;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
@@ -11,6 +10,12 @@ namespace mbts.spark {
|
||||
public readonly string name;
|
||||
public readonly AlgoStructureKey structure;
|
||||
|
||||
public AlgoNameStructureKey(CustomOrderDefinitionImmutable definition)
|
||||
: this(definition.CustomCase.type_id(), definition) { }
|
||||
|
||||
public AlgoNameStructureKey(string name, CustomOrderDefinitionImmutable definition)
|
||||
: this(name, new AlgoStructureKey(definition)) { }
|
||||
|
||||
public AlgoNameStructureKey(CustomOrderDefinition.CustomOneofCase base_type)
|
||||
: this(base_type.type_id(), new AlgoStructureKey(base_type)) { }
|
||||
|
||||
@@ -26,139 +31,71 @@ namespace mbts.spark {
|
||||
public static bool operator !=(AlgoNameStructureKey a, AlgoNameStructureKey b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
struct AlgoStructureKey : IEquatable<AlgoStructureKey> {
|
||||
[ProtoMember(1)]
|
||||
readonly int[] _structure;
|
||||
|
||||
public AlgoStructureKey(CustomOrderDefinition.CustomOneofCase base_type)
|
||||
: this(new[] { (int)base_type }) { }
|
||||
|
||||
public AlgoStructureKey(CustomOrderDefinitionImmutable definition, IMaskIndexInt32<string> dynamic_type_index) =>
|
||||
_structure = AlgoStructureSerializer.to_array(definition, dynamic_type_index);
|
||||
public AlgoStructureKey(CustomOrderDefinitionImmutable definition) {
|
||||
int[] s = new[] { (int)definition.CustomCase };
|
||||
_build_structure(definition, ref s, 1);
|
||||
_structure = s;
|
||||
}
|
||||
|
||||
public AlgoStructureKey(int[] structure) =>
|
||||
_structure = structure;
|
||||
|
||||
public IReadOnlyList<int> structure => _structure ?? Array.Empty<int>();
|
||||
|
||||
public bool Equals(AlgoStructureKey other) {
|
||||
if (_structure == null) {
|
||||
return other._structure == null;
|
||||
} else {
|
||||
/// <see cref="Enumerable.SequenceEqual{TSource}(IEnumerable{TSource}, IEnumerable{TSource})"/> is not optimized to check Count/Length, since the args are <see cref="IEnumerable{T}"/>.
|
||||
return other._structure != null && _structure.Length == other._structure.Length && _structure.SequenceEqual(other._structure);
|
||||
}
|
||||
public CustomOrderDefinitionImmutable build_def() {
|
||||
int pos = 0;
|
||||
var mdef = new CustomOrderDefinition();
|
||||
_build_def(_structure, ref pos, ref mdef);
|
||||
return new CustomOrderDefinitionImmutable(mdef);
|
||||
}
|
||||
|
||||
public bool Equals(AlgoStructureKey other) =>
|
||||
_structure.Length == other._structure.Length &&
|
||||
_structure.SequenceEqual(other._structure);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is AlgoStructureKey other && Equals(other);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
_structure == null ? 0 : EqualityComparer.aggregate_hash(_structure);
|
||||
EqualityComparer.aggregate_hash(_structure);
|
||||
|
||||
public static bool operator ==(AlgoStructureKey a, AlgoStructureKey b) => a.Equals(b);
|
||||
public static bool operator !=(AlgoStructureKey a, AlgoStructureKey b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
static class AlgoStructureSerializer {
|
||||
const int
|
||||
_dynamic_mask = 1 << 31,
|
||||
_child_mask_offset = 30;
|
||||
static void _build_structure(CustomOrderDefinitionImmutable definition, ref int[] structure, int pos) =>
|
||||
_build_structure(definition.CloneToMutable(), ref structure, pos);
|
||||
|
||||
public static CustomOrderDefinitionImmutable build_algoi(this AlgoStructureKey structure_key, DynamicOrderHelper dynamic_helper) =>
|
||||
parsei(structure_key.structure, dynamic_helper);
|
||||
|
||||
public static CustomOrderDefinitionImmutable build_algo(this AlgoStructureKey structure_key, DynamicOrderHelper dynamic_helper) =>
|
||||
parsei(structure_key.structure, dynamic_helper);
|
||||
|
||||
public static CustomOrderDefinition.CustomOneofCase order_type(this AlgoStructureKey structure_key) =>
|
||||
structure_key.structure.Count > 0 ? (CustomOrderDefinition.CustomOneofCase)structure_key.structure[0] : CustomOrderDefinition.CustomOneofCase.None;
|
||||
|
||||
public static AlgoStructureKey structure_key(CustomOrderDefinitionImmutable algo, IMaskIndexInt32<string> dynamic_type_index) =>
|
||||
new AlgoStructureKey(to_array(algo, dynamic_type_index));
|
||||
|
||||
public static AlgoStructureKey structure_key(CustomOrderDefinition algo, IMaskIndexInt32<string> dynamic_type_index) =>
|
||||
new AlgoStructureKey(to_array(algo, dynamic_type_index));
|
||||
|
||||
public static int[] to_array(CustomOrderDefinitionImmutable algo, IMaskIndexInt32<string> dynamic_type_index) =>
|
||||
to_array(algo.CloneToMutable(), dynamic_type_index);
|
||||
|
||||
public static int[] to_array(CustomOrderDefinition algo, IMaskIndexInt32<string> dynamic_type_index) {
|
||||
int[] s = new[] { (int)algo.CustomCase };
|
||||
_build_structure(algo, ref s, dynamic_type_index);
|
||||
return s;
|
||||
static void _build_structure(CustomOrderDefinition algo, ref int[] structure, int pos) {
|
||||
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);
|
||||
int del_mask = 1 << (31 - di);
|
||||
structure[pos++] = (int)child_algo.CustomCase | del_mask;
|
||||
_build_structure(child_algo, ref structure, pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ArrayHelper.trim(ref structure, pos);
|
||||
}
|
||||
|
||||
public static CustomOrderDefinitionImmutable from_array(int[] structure, DynamicOrderHelper dynamic_helper) =>
|
||||
parsei(structure, dynamic_helper);
|
||||
|
||||
public static CustomOrderDefinitionImmutable parsei(IReadOnlyList<int> structure, DynamicOrderHelper dynamic_helper) =>
|
||||
new CustomOrderDefinitionImmutable(parse(structure, dynamic_helper));
|
||||
|
||||
public static CustomOrderDefinition parse(IReadOnlyList<int> structure, DynamicOrderHelper dynamic_helper) {
|
||||
if (structure == null || structure.Count == 0)
|
||||
return new CustomOrderDefinition();
|
||||
int pos = 1;
|
||||
var mdef = OrderDataHelper.create_algo((CustomOrderDefinition.CustomOneofCase)structure[0]);
|
||||
_build_def(structure, ref pos, ref mdef, dynamic_helper);
|
||||
return mdef;
|
||||
}
|
||||
|
||||
static void _build_structure(CustomOrderDefinition algo, ref int[] structure, IMaskIndexInt32<string> dynamic_type_index) {
|
||||
if (structure == null || structure.Length == 0)
|
||||
structure = new int[1];
|
||||
structure[0] = (int)algo.CustomCase;
|
||||
int p = 1;
|
||||
_build_structure(algo, ref structure, ref p, dynamic_type_index);
|
||||
ArrayHelper.trim(ref structure, p);
|
||||
}
|
||||
|
||||
static void _build_structure(CustomOrderDefinition algo, ref int[] structure, ref int pos, IMaskIndexInt32<string> dynamic_type_index) {
|
||||
IEnumerable<(CustomOrderDefinition child_algo, int field_pos)> children;
|
||||
if (algo.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic) {
|
||||
ArrayHelper.ensure_capacity(ref structure, pos + 1);
|
||||
structure[pos++] = dynamic_type_index.to_int(algo.Dynamic.DynamicOrderType) | _dynamic_mask;
|
||||
children = algo.Dynamic.OrderParams.Where(kv => kv.Value.ValueCase == DynamicOrderParam.ValueOneofCase.AsOrderDef).Select((kv, i) => (kv.Value.AsOrderDef, i));
|
||||
} else {
|
||||
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);
|
||||
children = cad.Select((c, i) => (c.get_child(algo), i));
|
||||
children = children.Where(c => c.child_algo != null);
|
||||
}
|
||||
foreach ((var child, int field_pos) in children) {
|
||||
ArrayHelper.ensure_capacity(ref structure, pos + 1);
|
||||
structure[pos++] = EnumMaskIndexInt32<CustomOrderDefinition.CustomOneofCase>.to_int(child.CustomCase) | _child_algo_mask(field_pos);
|
||||
_build_structure(child, ref structure, ref pos, dynamic_type_index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a mask for a particular child-algo field of a particular algo definition.
|
||||
/// </summary>
|
||||
/// <param name="field_pos">The index wrt the deterministic enumeration of child algos for a particular definition (e.g., Hedge = 0, Child = 1).</param>
|
||||
static int _child_algo_mask(int field_pos) => 1 << (_child_mask_offset - field_pos);
|
||||
|
||||
static void _build_def(IReadOnlyList<int> structure, ref int pos, ref CustomOrderDefinition algo, DynamicOrderHelper dynamic_helper) {
|
||||
var local_algo = algo;
|
||||
IEnumerable<(Action<CustomOrderDefinition> assign_child, int field_pos)> children;
|
||||
if (algo.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic) {
|
||||
var dyn_def = algo.Dynamic;
|
||||
dyn_def.DynamicOrderType = dynamic_helper.dynamic_type_index.from_int(structure[pos++] ^ _dynamic_mask);
|
||||
dynamic_helper.add_dynamic_params(ref dyn_def);
|
||||
children = dyn_def.OrderParams
|
||||
.Where(kv => kv.Value.ValueCase == DynamicOrderParam.ValueOneofCase.AsOrderDef)
|
||||
.Select((kv, i) => (new Action<CustomOrderDefinition>(child_algo => kv.Value.AsOrderDef = child_algo), i));
|
||||
} else {
|
||||
children = OrderDataHelper.get_child_algo_data(algo)
|
||||
.Select((c, i) => (new Action<CustomOrderDefinition>(child_algo => c.assign_child(local_algo, child_algo)), i));
|
||||
}
|
||||
using (var children_en = children.GetEnumerator()) {
|
||||
while (pos < structure.Count && children_en.MoveNext()) {
|
||||
(var assign_child, int field_pos) = children_en.Current;
|
||||
int mask = _child_algo_mask(field_pos);
|
||||
if ((structure[pos] & mask) == mask) {
|
||||
var type = EnumMaskIndexInt32<CustomOrderDefinition.CustomOneofCase>.from_int(structure[pos++] ^ mask);
|
||||
var child_algo = OrderDataHelper.create_algo(type);
|
||||
_build_def(structure, ref pos, ref child_algo, dynamic_helper);
|
||||
assign_child(child_algo);
|
||||
for (int di = 0; di < cad.Count; di++) {
|
||||
int mask = 1 << (31 - di);
|
||||
if ((structure[i] & mask) == mask) {
|
||||
var type = (CustomOrderDefinition.CustomOneofCase)(structure[i] ^ mask);
|
||||
var child_algo = OrderDataHelper.Defaults.CustomOrderDefinition(type);
|
||||
pos++;
|
||||
_build_def(structure, ref pos, ref child_algo);
|
||||
cad[di].assign_child(algo, child_algo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ namespace mbts.spark {
|
||||
is_hidden.GetHashCode(),
|
||||
abbreviation == null ? 0 : abbreviation.GetHashCode(),
|
||||
badge_color?.GetHashCode() ?? 0,
|
||||
_favorite_templates == null ? 0 : EqualityComparer.aggregate_hash_limited(_favorite_templates));
|
||||
EqualityComparer.aggregate_hash_limited(_favorite_templates));
|
||||
|
||||
public static bool operator ==(AlgoDefinitionMetadata a, AlgoDefinitionMetadata b) => a.Equals(b);
|
||||
public static bool operator !=(AlgoDefinitionMetadata a, AlgoDefinitionMetadata b) => !a.Equals(b);
|
||||
|
||||
@@ -1,37 +1,24 @@
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
using mbts.collections;
|
||||
using mbts.ui.utilities;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
static class AlgoCollectionHelper {
|
||||
public static readonly IComparer<AlgoDefinitionVM> algo_definition_vm_comparer =
|
||||
new ComparerBuilder<AlgoDefinitionVM>(
|
||||
new[] {
|
||||
Comparer<AlgoDefinitionVM>.Create(_CompareUtils.compare_groups),
|
||||
new [] {
|
||||
Comparer<AlgoDefinitionVM>.Create(_CompareUtils.compare_modes),
|
||||
Comparer<AlgoDefinitionVM>.Create(_CompareUtils.compare_names)
|
||||
}).to_comparer().to_safe();
|
||||
|
||||
public static readonly IComparer<AlgoData> algo_data_comparer =
|
||||
new ComparerBuilder<AlgoData>(
|
||||
new[] {
|
||||
Comparer<AlgoData>.Create(_CompareUtils.compare_groups),
|
||||
Comparer<AlgoData>.Create(_CompareUtils.compare_names)
|
||||
}
|
||||
).to_comparer();
|
||||
|
||||
public static readonly IComparer<AlgoDataVM> algo_data_vm_comparer =
|
||||
algo_data_comparer.wrap<AlgoData, AlgoDataVM>(vm => vm.data);
|
||||
|
||||
public static readonly IComparer<IAlgoVM> algo_vm_comparer =
|
||||
new ComparerBuilder<IAlgoVM>(
|
||||
new[] {
|
||||
Comparer<IAlgoVM>.Create(_CompareUtils.compare_groups),
|
||||
new [] {
|
||||
Comparer<IAlgoVM>.Create(_CompareUtils.compare_modes),
|
||||
Comparer<IAlgoVM>.Create(_CompareUtils.compare_names)
|
||||
}).to_comparer().to_safe();
|
||||
|
||||
@@ -47,75 +34,29 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public static void update_persisted_definition_wrappers(IDictionary<AlgoKey, IAlgoVM> target, TableUpdate<AlgoData, AlgoKey> update, Func<AlgoData, AlgoDefinitionVM> factory) {
|
||||
foreach (var d in update.removed)
|
||||
target.Remove(d.key);
|
||||
foreach (var d in update.changed) {
|
||||
var vm = target[d.key];
|
||||
if (vm is AlgoVMWrapper wrapper) {
|
||||
wrapper.value.load(d);
|
||||
} else vm.load_from(d.value);
|
||||
}
|
||||
foreach (var d in update.added) {
|
||||
if (target.TryGetValue(d.key, out IAlgoVM def)) {
|
||||
def.load_from(d.value);
|
||||
} else target.Add(d.key, new AlgoVMWrapper(factory(d)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void commit_all(this IEnumerable<AlgoParamRowVM> algo_param_rows) =>
|
||||
algo_param_rows.Select(r => r.param).commit_all();
|
||||
|
||||
public static void commit_all(this IEnumerable<IAlgoParamVM> algo_params) {
|
||||
foreach (var p in algo_params)
|
||||
p.mark_committed();
|
||||
}
|
||||
|
||||
public static void revert_all(this IEnumerable<AlgoParamRowVM> algo_param_rows) =>
|
||||
algo_param_rows.Select(r => r.param).revert_all();
|
||||
|
||||
public static void revert_all(this IEnumerable<IAlgoParamVM> algo_params) {
|
||||
foreach (var p in algo_params)
|
||||
p.revert();
|
||||
}
|
||||
|
||||
public static AlgoDisplayGroup display_group(CustomOrderDefinition.CustomOneofCase order_type) => order_type.is_bts_algo_type() ? AlgoDisplayGroup.BTS : AlgoDisplayGroup.Exchange;
|
||||
|
||||
public static AlgoDisplayGroup display_group(this AlgoKey key) {
|
||||
switch (key.mode) {
|
||||
case AlgoKey.Mode.Factory: return display_group(key.factory.structure.order_type());
|
||||
case AlgoKey.Mode.Unpersisted:
|
||||
case AlgoKey.Mode.Persisted:
|
||||
return AlgoDisplayGroup.Studio;
|
||||
}
|
||||
return AlgoDisplayGroup.BTS;
|
||||
}
|
||||
|
||||
static class _CompareUtils {
|
||||
static AlgoDisplayGroup[] _sorted_groups = new[] {
|
||||
AlgoDisplayGroup.Exchange,
|
||||
AlgoDisplayGroup.BTS,
|
||||
AlgoDisplayGroup.Studio,
|
||||
AlgoDisplayGroup.API
|
||||
};
|
||||
public static int compare_modes(AlgoDefinitionVM a, AlgoDefinitionVM b) =>
|
||||
_compare_modes(a.key.mode, b.key.mode);
|
||||
|
||||
public static int compare_groups(AlgoDefinitionVM a, AlgoDefinitionVM b) =>
|
||||
Array.IndexOf(_sorted_groups, a.group).CompareTo(Array.IndexOf(_sorted_groups, b.group));
|
||||
|
||||
public static int compare_groups(AlgoData a, AlgoData b) =>
|
||||
Array.IndexOf(_sorted_groups, a.key.display_group()).CompareTo(Array.IndexOf(_sorted_groups, b.key.display_group()));
|
||||
|
||||
public static int compare_groups(IAlgoVM a, IAlgoVM b) =>
|
||||
Array.IndexOf(_sorted_groups, a.group).CompareTo(Array.IndexOf(_sorted_groups, b.group));
|
||||
public static int compare_modes(IAlgoVM a, IAlgoVM b) =>
|
||||
_compare_modes(a.id.mode, b.id.mode);
|
||||
|
||||
public static int compare_names(AlgoDefinitionVM a, AlgoDefinitionVM b) =>
|
||||
DisplayNameHelper.compare_alpha_numeric(a.name, b.name);
|
||||
|
||||
public static int compare_names(AlgoData a, AlgoData b) =>
|
||||
DisplayNameHelper.compare_alpha_numeric(a.name, b.name);
|
||||
|
||||
public static int compare_names(IAlgoVM a, IAlgoVM b) =>
|
||||
DisplayNameHelper.compare_alpha_numeric(a.display_name, b.display_name);
|
||||
|
||||
static int _compare_modes(AlgoKey.Mode a, AlgoKey.Mode b) {
|
||||
// Factory mode is 'lowest', consider all other modes equivalent.
|
||||
if (a == AlgoKey.Mode.Factory) {
|
||||
if (b != AlgoKey.Mode.Factory)
|
||||
return -1;
|
||||
} else if (b == AlgoKey.Mode.Factory) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using bts.proto;
|
||||
using bts.utils;
|
||||
using mbts.collections;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.controls.color_picker;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
@@ -27,8 +25,7 @@ namespace mbts.spark {
|
||||
None = 0,
|
||||
Exchange = 1,
|
||||
Spreader = 1 << 1,
|
||||
Attachable = 1 << 2,
|
||||
DefaultPlaceholder = 1 << 3
|
||||
DefaultPlaceholder = 1 << 2
|
||||
}
|
||||
|
||||
class AlgoDefinitionVM : DisposableViewModelBase, IAlgoStudioContentVM, IAlgoSelector, IEquatable<AlgoDefinitionVM> {
|
||||
@@ -49,8 +46,7 @@ namespace mbts.spark {
|
||||
readonly DeferrableActionWrapper _refresh_on_local_changes;
|
||||
readonly ReactiveThresholdMonitor<AlgoDefinitionTemplateVM> _any_templates_dirty_monitor = new ReactiveThresholdMonitor<AlgoDefinitionTemplateVM>(t => t.observe_is_dirty());
|
||||
readonly Property<AlgoFormats> _selected_format;
|
||||
readonly EnumMaskInt32<AlgoEditingPermissions> _permissions_mask = new EnumMaskInt32<AlgoEditingPermissions>();
|
||||
|
||||
AlgoEditingPermissions _editing_permissions;
|
||||
int? _favorite_index;
|
||||
Brush _badge_fill;
|
||||
Snapshot? _committed;
|
||||
@@ -58,43 +54,29 @@ namespace mbts.spark {
|
||||
TableUpdateBuffer<AlgoTemplateData, AlgoKey> _template_update_buffer;
|
||||
bool _connected;
|
||||
|
||||
public static AlgoDefinitionVM create(OrdersModel model, AlgoData data, bool connect = false, AlgoFormats init_format = AlgoFormats.Exchange, AlgoEditingPermissions editing_permissions = (AlgoEditingPermissions)int.MaxValue) {
|
||||
var rv = new AlgoDefinitionVM(model, data, init_format, editing_permissions);
|
||||
public static AlgoDefinitionVM create(OrdersModel model, AlgoData data, bool connect = false, AlgoFormats init_format = AlgoFormats.Exchange) {
|
||||
var rv = new AlgoDefinitionVM(model, data, init_format);
|
||||
if (connect)
|
||||
rv.connect();
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static AlgoDefinitionVM create_placeholder(OrdersModel model, CustomOrderDefinitionImmutable def, string name, AlgoFormats init_format) =>
|
||||
new AlgoDefinitionVM(model, new AlgoData(def, name, new AlgoKey(Guid.NewGuid())), init_format, (AlgoEditingPermissions)int.MaxValue);
|
||||
new AlgoDefinitionVM(model, new AlgoData(def, name, new AlgoKey(Guid.NewGuid())), init_format);
|
||||
|
||||
AlgoDefinitionVM(OrdersModel model, AlgoData def_data, AlgoFormats init_format, AlgoEditingPermissions init_permissions) {
|
||||
AlgoDefinitionVM(OrdersModel model, AlgoData def_data, AlgoFormats init_format = AlgoFormats.Exchange) {
|
||||
// init core data
|
||||
_model = model;
|
||||
_permissions_mask.mask = init_permissions;
|
||||
_permissions_mask.raw = init_permissions;
|
||||
var resources = new CompositeDisposable();
|
||||
if (!model.algo_model.ui_configuration.can_modify_definition_values)
|
||||
editing_permissions_mask ^= (AlgoEditingPermissions.AllowEditValues | AlgoEditingPermissions.AllowSaveValues);
|
||||
if (!model.algo_model.ui_configuration.can_modify_definition_structure)
|
||||
editing_permissions_mask ^= (AlgoEditingPermissions.AllowEditStructure | AlgoEditingPermissions.AllowSaveStructure);
|
||||
else if (model.algo_model.templates_enabled()) {
|
||||
resources.Add(_templates.Subscribe((s, e) => {
|
||||
if (_templates.Count == 0) {
|
||||
editing_permissions_mask |= AlgoEditingPermissions.AllowEditStructure | AlgoEditingPermissions.AllowSaveStructure;
|
||||
} else {
|
||||
editing_permissions_mask ^= (AlgoEditingPermissions.AllowEditStructure | AlgoEditingPermissions.AllowSaveStructure);
|
||||
}
|
||||
}));
|
||||
}
|
||||
key = def_data.key;
|
||||
algos_view = ObservableAutoWrapper.create_sorted_wrapper(_algo_roots, AlgoCollectionHelper.algo_vm_comparer);
|
||||
_template_update_buffer = new TableUpdateBuffer<AlgoTemplateData, AlgoKey>(t => t.key);
|
||||
_templates_with_custom = new ObservableAppendCollection<AlgoDefinitionTemplateVM>(templates);
|
||||
templates_view = (ListCollectionView)CollectionViewSource.GetDefaultView(_templates_with_custom);
|
||||
custom_badge_color_palettes.AddRange(ColorPalette.all_presets(model.factory).Select(p => new ColorPaletteVM(p)));
|
||||
_editing_permissions = _get_permissions();
|
||||
_version = new VersionContainer<Snapshot>(_snapshot);
|
||||
var conf = _model.algo_model.ui_configuration;
|
||||
var resources = new CompositeDisposable();
|
||||
// init backing props
|
||||
_name = backing(nameof(name), def_data.name);
|
||||
_abbreviation = backing(nameof(abbreviation), string.Empty);
|
||||
@@ -105,7 +87,7 @@ namespace mbts.spark {
|
||||
_is_selected = backing(nameof(is_selected), true);
|
||||
_is_dirty = backing(nameof(is_dirty), () => key.mode == AlgoKey.Mode.Unpersisted || !_committed.HasValue || !_committed.Value.Equals(_version.value));
|
||||
var factory_def_data = def_data.key.mode == AlgoKey.Mode.Factory ? def_data : new AlgoData(def_data.value);
|
||||
_algo_root = backing(nameof(algo_root), _algo_roots.GetOrAdd(factory_def_data.key, _ => OrderTypeVMFactory.create(factory_def_data)));
|
||||
_algo_root = backing(nameof(algo_root), _algo_roots.GetOrAdd(factory_def_data.key, _ => OrderTypeVMFactory.create(factory_def_data, _editing_permissions)));
|
||||
_visible_algo_root = backing<IAlgoVM>(nameof(visible_algo_root));
|
||||
_can_redo = backing(nameof(can_redo), () => _edit_stack.get_can_redo(_version.value));
|
||||
_can_undo = backing(nameof(can_undo), () => _edit_stack.get_can_undo(_version.value));
|
||||
@@ -131,16 +113,28 @@ namespace mbts.spark {
|
||||
as_demuxer().subscribe_many(_refresh_on_local_changes.execute, nameof(this.name), nameof(abbreviation), nameof(badge_fill)),
|
||||
_edit_pane_vs_model.add_source(VisualStateNames.BadgeSelector.GroupName, custom_badge_color.observe_nullable(true).Select(t => t.HasValue ? VisualStateNames.BadgeSelector.Active : VisualStateNames.BadgeSelector.Inactive)),
|
||||
_edit_pane_vs_model.add_source(VisualStateNames.Abbreviation.GroupName, _abbreviation.observe_current(true).Select(a => string.IsNullOrEmpty(a) ? VisualStateNames.Abbreviation.Unpopulated : VisualStateNames.Abbreviation.Populated)),
|
||||
_algo_root.observe_current(true).SubscribeEach(vm =>
|
||||
vm == null ? null :
|
||||
Disposable.Create(
|
||||
vm.subscribe_is_dirty().Subscribe(v => {
|
||||
if (!v) {
|
||||
templates.ObserveSelf().Select(_ => _get_permissions()).DistinctUntilChanged().OnDispatcher().Subscribe(on_editing_permissions),
|
||||
_algo_root.observe_current(true).SubscribeHookLatest(vm => {
|
||||
if (vm == null)
|
||||
return Disposable.empty;
|
||||
if (vm is ILazyAlgo la) {
|
||||
bool synced_with_loaded = false;
|
||||
return vm.subscribe_definition().Subscribe(_ => {
|
||||
if (la.is_loaded) {
|
||||
if (!synced_with_loaded) {
|
||||
synced_with_loaded = true;
|
||||
_version.increment_version();
|
||||
_on_committed_changed();
|
||||
} else {
|
||||
_refresh_on_local_changes.execute();
|
||||
}
|
||||
}),
|
||||
vm.subscribe_definition().Subscribe(_ => _refresh_on_local_changes.execute()))),
|
||||
} else {
|
||||
synced_with_loaded = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
return vm.subscribe_definition().Subscribe(_ => _refresh_on_local_changes.execute());
|
||||
}),
|
||||
ObservableAutoWrapper.connect_item_hooks(algo_roots, a => a as IDisposable ?? Disposable.empty, true),
|
||||
ObservableAutoWrapper.connect_item_hooks(templates, t => new CompositeDisposable(_any_templates_dirty_monitor.join(t), t), true),
|
||||
Disposable.Create(() => {
|
||||
@@ -158,23 +152,16 @@ namespace mbts.spark {
|
||||
_edit_pane_vs_model.set_state(VisualStateNames.Name.GroupName, VisualStateNames.Name.Inactive);
|
||||
break;
|
||||
}
|
||||
var permission_src = _permissions_mask.observe_result();
|
||||
resources.Add(_selected_format.observe_current(true).CombineLatest(permission_src, (fmt, perms) => (fmt, perms))
|
||||
.SubscribeEach(t => {
|
||||
if ((t.perms & AlgoEditingPermissions.AllowEditStructure) != 0) {
|
||||
using (_refresh_on_local_changes.defer_execution()) {
|
||||
var algo_data = model.factory.default_algos(t.fmt);
|
||||
var default_algo = new AlgoKey(OrderDataHelper.get_default_algo(t.fmt));
|
||||
return this.load_algo_data_recursive(algo_data, default_algo);
|
||||
}
|
||||
}
|
||||
return Disposable.empty;
|
||||
}));
|
||||
resources.Add(permission_src.Subscribe(_ => RaisePropertyChanged(nameof(editing_permissions))));
|
||||
resources.Add(this.connect_data(permission_src));
|
||||
resources.Add(_selected_format.observe_current(true).SubscribeHookLatest(f => {
|
||||
using (_refresh_on_local_changes.defer_execution()) {
|
||||
var algo_data = model.factory.default_algos(f);
|
||||
var default_algo = new AlgoKey(OrderDataHelper.get_default_algo(f));
|
||||
return this.load_algo_data_recursive(algo_data, default_algo);
|
||||
}
|
||||
}));
|
||||
resources.Add(this.connect_data(Observable.Return(model)));
|
||||
resources.Add(_algo_root.Subscribe(_ => RaisePropertyChanged(nameof(IAlgoSelector.selected_algo), nameof(IAlgoParamVM.value), nameof(group))));
|
||||
_dispose = resources;
|
||||
on_editing_permissions(_get_permissions());
|
||||
await algo_root.load_from_async(def_data.value);
|
||||
_version.increment_version();
|
||||
_edit_stack.reset(_version.value);
|
||||
@@ -182,6 +169,11 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
// local methods
|
||||
void on_editing_permissions(AlgoEditingPermissions eps) {
|
||||
_editing_permissions = eps;
|
||||
foreach (var vm in algo_roots)
|
||||
vm.set_permissions(eps);
|
||||
}
|
||||
void on_can_redo_changed(bool oldval, bool newval) => can_redo_changed?.Invoke();
|
||||
void on_can_undo_changed(bool oldval, bool newval) => can_undo_changed?.Invoke();
|
||||
void on_local_changed() {
|
||||
@@ -202,7 +194,6 @@ namespace mbts.spark {
|
||||
public string name { get => _name.Value; set => _name.set(value); }
|
||||
public string abbreviation { get => _abbreviation.Value; set => _abbreviation.set(value); }
|
||||
public ObservableCollection<IAlgoVM> algo_roots => _algo_roots;
|
||||
public IDictionary<AlgoKey, IAlgoVM> algo_roots_index => _algo_roots;
|
||||
public IReadOnlyObservableCollection<IAlgoVM> algos_view { get; }
|
||||
public IAlgoVM algo_root { get => _algo_root.Value; set => _algo_root.set(value); }
|
||||
public IAlgoVM visible_algo_root => _visible_algo_root.Value;
|
||||
@@ -219,20 +210,6 @@ namespace mbts.spark {
|
||||
public bool is_selected { get => _is_selected.Value; set => _is_selected.set(value); }
|
||||
public bool is_dirty => _is_dirty.Value;
|
||||
public bool is_factory_definition => key.mode == AlgoKey.Mode.Factory;
|
||||
public AlgoDisplayGroup group {
|
||||
get {
|
||||
switch (key.mode) {
|
||||
case AlgoKey.Mode.Factory:
|
||||
if (algo_root != null)
|
||||
return algo_root.group;
|
||||
break;
|
||||
case AlgoKey.Mode.Unpersisted:
|
||||
case AlgoKey.Mode.Persisted:
|
||||
return AlgoDisplayGroup.Studio;
|
||||
}
|
||||
return AlgoDisplayGroup.BTS;
|
||||
}
|
||||
}
|
||||
public bool can_redo => _can_redo.Value;
|
||||
public bool can_undo => _can_undo.Value;
|
||||
public EnabledValueVM<Color> custom_badge_color { get; } = new EnabledValueVM<Color>();
|
||||
@@ -253,51 +230,13 @@ namespace mbts.spark {
|
||||
// visual states
|
||||
public ObservableCollection<string> edit_pane_visual_states => _edit_pane_vs_model.states;
|
||||
public ObservableCollection<string> tree_view_visual_states => _tree_view_vs_model.states;
|
||||
// editing permissions
|
||||
public AlgoEditingPermissions editing_permissions_mask { get => _permissions_mask.mask; set => _permissions_mask.mask = value; }
|
||||
public AlgoEditingPermissions editing_permissions => _permissions_mask.result;
|
||||
|
||||
#region IAlgoSelector
|
||||
event PropertyChange<IAlgoVM> IAlgoSelector.selected_algo_changed {
|
||||
add => algo_root_changed += value;
|
||||
remove => algo_root_changed -= value;
|
||||
}
|
||||
event PropertyChange<IAlgoVM> IAlgoParamVM<IAlgoVM>.value_changed {
|
||||
add => algo_root_changed += value;
|
||||
remove => algo_root_changed -= value;
|
||||
}
|
||||
event PropertyChange<AlgoParamPermissions> IAlgoParamVM.permissions_changed { add { } remove { } }
|
||||
event Action<bool> IAlgoParamVM.is_dirty_changed { add { } remove { } }
|
||||
|
||||
string IAlgoParamVM.id => key.ToString();
|
||||
string IAlgoParamVM.display_name => "Algo";
|
||||
|
||||
event PropertyChange<IAlgoVM> IAlgoSelector.selected_algo_changed { add => algo_root_changed += value; remove => algo_root_changed -= value; }
|
||||
ObservableKeyedCollection<IAlgoVM, AlgoKey> IAlgoSelector.algos => _algo_roots;
|
||||
IAlgoVM IAlgoSelector.selected_algo {
|
||||
get => algo_root;
|
||||
set => algo_root = value;
|
||||
}
|
||||
IAlgoVM IAlgoParamVM<IAlgoVM>.value {
|
||||
get => algo_root;
|
||||
set => algo_root = value;
|
||||
}
|
||||
bool IAlgoParamVM.has_value => _algo_root != null;
|
||||
bool IAlgoParamVM.is_enabled { get; set; } = true;
|
||||
object IAlgoParamVM.value {
|
||||
get => algo_root;
|
||||
set => algo_root = (IAlgoVM)value;
|
||||
}
|
||||
AlgoParamPermissions _permissions = AlgoParamPermissions.ReadWrite;
|
||||
AlgoParamPermissions IAlgoParamVM.permissions { get => _permissions; set => _permissions = value; }
|
||||
|
||||
IAlgoVM IAlgoSelector.selected_algo { get => algo_root; set => algo_root = value; }
|
||||
void IAlgoSelector.load_algo_data(IReadOnlyDictionary<AlgoKey, AlgoData> algo_data, AlgoKey? default_algo) =>
|
||||
_load_algo_data(algo_data, default_algo);
|
||||
|
||||
void IAlgoParamVM.revert() { }
|
||||
void IAlgoParamVM.mark_committed() { }
|
||||
|
||||
IObservable<IAlgoVM> IAlgoSelector.subscribe_selected_algo() =>
|
||||
this.observe_algo_root();
|
||||
_load_algo_data(algo_data, default_algo);
|
||||
#endregion
|
||||
|
||||
#region IObserver (algo data...)
|
||||
@@ -309,7 +248,7 @@ namespace mbts.spark {
|
||||
|
||||
void _load_algo_data(IReadOnlyDictionary<AlgoKey, AlgoData> algo_data, AlgoKey? default_algo) {
|
||||
var orig_algo = algo_root?.id;
|
||||
_algo_roots.SyncWithDictionary(algo_data, d => OrderTypeVMFactory.create(d, is_expanded: true));
|
||||
_algo_roots.SyncWithDictionary(algo_data, d => OrderTypeVMFactory.create(d, _editing_permissions, true));
|
||||
if (!try_select(orig_algo) && !try_select(default_algo))
|
||||
algo_root = algo_roots.FirstOrDefault();
|
||||
//
|
||||
@@ -359,47 +298,6 @@ namespace mbts.spark {
|
||||
|
||||
public IDisposable buffer_updates() => _template_update_buffer.buffer_updates();
|
||||
|
||||
public void load_def(CustomOrderDefinitionImmutable def) {
|
||||
var key = OrderDataHelper.get_shallow_factory_key(def);
|
||||
if (algo_root?.id == key) {
|
||||
algo_root.load_from(def);
|
||||
} else if (_algo_roots.TryGetValue(key, out IAlgoVM vm)) {
|
||||
vm.load_from(def);
|
||||
algo_root = vm;
|
||||
} else {
|
||||
algo_roots.Add(vm = _mk_algo_root(def));
|
||||
algo_root = vm;
|
||||
}
|
||||
}
|
||||
|
||||
public void load_many(IEnumerable<CustomOrderDefinitionImmutable> defs) {
|
||||
/// don't load composite algos; we don't support recursive composites (<see cref="OrderUtils.actual(CustomOrderDefinitionImmutable)"/>)
|
||||
if (defs is IList<CustomOrderDefinitionImmutable> def_list) {
|
||||
def_list = def_list.WrapSelector(d => d.actual(), null);
|
||||
} else {
|
||||
def_list = defs.Select(d => d.actual()).ToList();
|
||||
}
|
||||
switch (def_list.Count) {
|
||||
case 0: algo_root = null; break;
|
||||
case 1: load_def(def_list[0]); break;
|
||||
default:
|
||||
var def = def_list[0];
|
||||
var key = OrderDataHelper.get_shallow_factory_key(def);
|
||||
if (algo_root?.id == key) {
|
||||
algo_root.load_many(def_list);
|
||||
} else if (_algo_roots.TryGetValue(key, out IAlgoVM vm)) {
|
||||
vm.load_many(def_list);
|
||||
algo_root = vm;
|
||||
} else {
|
||||
OrderTypeVMFactory.create(key, def.display_name(), def.CustomCase, is_expanded: true);
|
||||
algo_roots.Add(vm = _mk_algo_root(def));
|
||||
vm.load_many(def_list);
|
||||
algo_root = vm;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void load(AlgoData data) {
|
||||
_assert_can_load(data.key);
|
||||
if (algo_root != null)
|
||||
@@ -411,7 +309,7 @@ namespace mbts.spark {
|
||||
|
||||
// pass a copy of the persisted state - rather than use local values - because if something goes wrong, it is better to be in sync with data fresh from the server/api.
|
||||
public AlgoDefinitionVM to_persisted(AlgoData def) =>
|
||||
new AlgoDefinitionVM(_model, def, selected_format, editing_permissions_mask);
|
||||
new AlgoDefinitionVM(_model, def);
|
||||
|
||||
public AlgoData create_algo_def() =>
|
||||
new AlgoData(algo_root.definition, name, key);
|
||||
@@ -424,6 +322,16 @@ namespace mbts.spark {
|
||||
public override int GetHashCode() => key.GetHashCode();
|
||||
public override string ToString() => algo_root == null ? $"!:{name}" : $"{algo_root.order_type}:{name}";
|
||||
|
||||
AlgoEditingPermissions _get_permissions() {
|
||||
var permissions = AlgoEditingPermissions.None;
|
||||
if (_model.algo_model.ui_configuration.can_modify_definition_values) {
|
||||
permissions |= AlgoEditingPermissions.AllowEditValues | AlgoEditingPermissions.AllowSaveValues;
|
||||
}
|
||||
if (_model.algo_model.ui_configuration.can_modify_definition_structure || (_model.algo_model.templates_enabled() && _templates.Count == 0))
|
||||
permissions |= AlgoEditingPermissions.AllowEditStructure | AlgoEditingPermissions.AllowSaveStructure;
|
||||
return permissions;
|
||||
}
|
||||
|
||||
void _assert_can_load(AlgoKey data_key) {
|
||||
if (key.mode != data_key.mode)
|
||||
throw new InvalidOperationException($"{typeof(AlgoDefinitionVM)} '{nameof(key)}.{nameof(AlgoKey.mode)}' must be {data_key.mode} (actually {key.mode}), to load specified data.");
|
||||
@@ -451,14 +359,10 @@ namespace mbts.spark {
|
||||
_committed = _version.value;
|
||||
_is_dirty.refresh();
|
||||
_refresh_undo_redo();
|
||||
RaisePropertyChanged(nameof(committed_value), nameof(current_value));
|
||||
}
|
||||
|
||||
public Snapshot? committed_value => _committed;
|
||||
public Snapshot current_value => _version.value;
|
||||
|
||||
IAlgoVM _mk_algo_root(CustomOrderDefinitionImmutable definition) =>
|
||||
OrderTypeVMFactory.create(new AlgoData(definition), is_expanded: true);
|
||||
OrderTypeVMFactory.create(new AlgoData(definition), editing_permissions: _editing_permissions, true);
|
||||
|
||||
void _on_metadata(AlgoDefinitionMetadata metadata) {
|
||||
using (_refresh_on_local_changes.supress_execution()) {
|
||||
@@ -559,17 +463,5 @@ namespace mbts.spark {
|
||||
public static bool operator ==(Snapshot a, Snapshot b) => a.Equals(b);
|
||||
public static bool operator !=(Snapshot a, Snapshot b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
struct SnapshotTemplateEqualityComparer : IEqualityComparer<Snapshot> {
|
||||
public bool Equals(Snapshot x, Snapshot y) =>
|
||||
x.abbreviation == y.abbreviation &&
|
||||
x.custom_badge_color == y.custom_badge_color &&
|
||||
x.format == y.format &&
|
||||
x.data.key == y.data.key &&
|
||||
x.data.name == y.data.name &&
|
||||
x.data.value.Equals(y.data.value);
|
||||
|
||||
public int GetHashCode(Snapshot obj) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
using bts.utils;
|
||||
using mbts.collections;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
class AlgoParamRowVM : DisposableViewModelBase, DIYGrid.IRowVM, IEditableObject, ITemplateSelectorKey, IEquatable<AlgoParamRowVM> {
|
||||
bool _is_enabled;
|
||||
string _template_key;
|
||||
ICommand _set_value_cmd;
|
||||
|
||||
public AlgoParamRowVM(IAlgoVM algo, IAlgoParamVM param, AlgoParamRowVM parent, string template_key = null) {
|
||||
this.param = param;
|
||||
this.algo = algo;
|
||||
this.parent = parent;
|
||||
_template_key = template_key;
|
||||
param.permissions_changed += on_permissions_changed;
|
||||
_dispose = Disposable.Create(() => param.permissions_changed -= on_permissions_changed);
|
||||
void on_permissions_changed(AlgoParamPermissions oldval, AlgoParamPermissions newval) => RaisePropertyChanged(nameof(permissions));
|
||||
}
|
||||
|
||||
public event Action invalidated;
|
||||
|
||||
public IAlgoVM algo { get; set; }
|
||||
public IAlgoParamVM param { get; }
|
||||
public AlgoParamPermissions permissions => param.permissions;
|
||||
public AlgoParamRowVM parent { get; }
|
||||
public int level => parent == null ? 0 : parent.level + 1;
|
||||
public string path_disambiguation {
|
||||
get {
|
||||
if (parent == null)
|
||||
return string.Empty;
|
||||
string pd = parent.path_disambiguation;
|
||||
if (parent.algo.order_type == bts.api.orderman.CustomOrderDefinition.CustomOneofCase.Cover) {
|
||||
if (pd.Length > 0)
|
||||
pd += ".";
|
||||
pd += parent.param.display_name;
|
||||
}
|
||||
return pd;
|
||||
}
|
||||
}
|
||||
public bool is_enabled { get => _is_enabled; set => Set(ref _is_enabled, value); }
|
||||
public ICommand set_value_cmd {
|
||||
get {
|
||||
if (_set_value_cmd == null) {
|
||||
var cmd = new DelegateCommand(v => {
|
||||
var orig_val = param.value;
|
||||
try {
|
||||
param.value = v;
|
||||
} catch (InvalidCastException) {
|
||||
param.value = orig_val;
|
||||
}
|
||||
}, _ => param.permissions == AlgoParamPermissions.ReadWrite);
|
||||
concated_disposable(param.subscribe_permissions().Subscribe(_ => cmd.RaiseCanExecuteChanged()));
|
||||
_set_value_cmd = cmd;
|
||||
}
|
||||
return _set_value_cmd;
|
||||
}
|
||||
}
|
||||
string ITemplateSelectorKey.key => _template_key;
|
||||
|
||||
public void raise_invalidated() => invalidated?.Invoke();
|
||||
|
||||
public bool Equals(AlgoParamRowVM other) =>
|
||||
other != null &&
|
||||
param.id == other.param.id &&
|
||||
parent.safe_equals(other.parent);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as AlgoParamRowVM);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(param.id.GetHashCode(), parent?.GetHashCode() ?? 0);
|
||||
|
||||
#region IEditableObject
|
||||
object _committed_value;
|
||||
void IEditableObject.BeginEdit() => _committed_value = param.value;
|
||||
void IEditableObject.EndEdit() => _committed_value = null;
|
||||
void IEditableObject.CancelEdit() {
|
||||
if (_committed_value != null)
|
||||
param.value = _committed_value;
|
||||
_committed_value = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
class AlgoSelectorParamRowVM : AlgoParamRowVM {
|
||||
readonly IAlgoSelector _selector;
|
||||
|
||||
public AlgoSelectorParamRowVM(IAlgoVM algo_vm, IAlgoSelector selector, AlgoParamRowVM parent) : base(algo_vm, selector, parent) =>
|
||||
_selector = selector;
|
||||
|
||||
public ObservableKeyedCollection<IAlgoVM, AlgoKey> algos => _selector.algos;
|
||||
}
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using bts.spark;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
class AlgoVMWrapper : DisposableViewModelBase, IAlgoVM,
|
||||
IObserver<SecurityMD?>,
|
||||
IObserver<SecurityImmutable?>,
|
||||
IObserver<OrdersModel>,
|
||||
IObserver<AlgoEditingPermissions> {
|
||||
static readonly HashSet<string>
|
||||
_algo_root_echo_props = new HashSet<string> {
|
||||
nameof(is_expanded),
|
||||
nameof(editing_permissions),
|
||||
nameof(definition),
|
||||
nameof(display_name)
|
||||
};
|
||||
static readonly IReadOnlyDictionary<string, string>
|
||||
_value_echo_props = new Dictionary<string, string> {
|
||||
{ nameof(AlgoDefinitionVM.name), nameof(display_name) },
|
||||
{ nameof(AlgoDefinitionVM.abbreviation), nameof(abbreviation) },
|
||||
{ nameof(AlgoDefinitionVM.badge_fill), nameof(meta) }
|
||||
};
|
||||
readonly ObservableAggregateCollection<IAlgoSelector> _child_algo_selectors = new ObservableAggregateCollection<IAlgoSelector>();
|
||||
readonly ObservableAggregateCollection<IAlgoParamVM> _algo_params = new ObservableAggregateCollection<IAlgoParamVM>();
|
||||
readonly DisposableContainer _value_hooks = new DisposableContainer();
|
||||
Action _definition_changed = () => { };
|
||||
_ObservedData? _data_cache;
|
||||
AlgoDefinitionVM _value;
|
||||
|
||||
public AlgoVMWrapper(AlgoDefinitionVM algo_definition_vm) {
|
||||
value = algo_definition_vm;
|
||||
_dispose = new CompositeDisposable(_value_hooks) {
|
||||
_child_algo_selectors.clear_collections,
|
||||
_algo_params.clear_collections,
|
||||
() => _value?.Dispose()
|
||||
};
|
||||
}
|
||||
|
||||
public event Action definition_changed { add => _definition_changed += value; remove => _definition_changed -= value; }
|
||||
|
||||
public AlgoDefinitionVM value {
|
||||
get => _value;
|
||||
set {
|
||||
if (_value != value) {
|
||||
_value?.Dispose();
|
||||
_value = value;
|
||||
_value_hooks.replace(_set_hooks(value));
|
||||
RaisePropertyChanged(nameof(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
public AlgoKey id => _value.key;
|
||||
public CustomOrderDefinition.CustomOneofCase order_type => _algo_root?.order_type ?? CustomOrderDefinition.CustomOneofCase.None;
|
||||
public OrderMetadata meta {
|
||||
get {
|
||||
if (_value != null) {
|
||||
if (_value.custom_badge_color.enabled)
|
||||
return new OrderMetadata { Color = value.custom_badge_color.value.rgb_color(), KeyBinding = value.abbreviation.if_null_or_empty(value.name) };
|
||||
else if (_value.algo_root != null)
|
||||
return _value.algo_root.meta;
|
||||
}
|
||||
return OrderDataHelper.get_metadata(order_type);
|
||||
}
|
||||
}
|
||||
public AlgoEditingPermissions editing_permissions => _value?.editing_permissions ?? AlgoEditingPermissions.None;
|
||||
public bool can_expand => _algo_root?.can_expand ?? false;
|
||||
public bool is_expanded {
|
||||
get => _value.algo_root?.is_expanded ?? false;
|
||||
set {
|
||||
if (_value != null)
|
||||
_value.is_expanded = value;
|
||||
}
|
||||
}
|
||||
public string display_name => _value?.name ?? string.Empty;
|
||||
public string abbreviation => _value?.abbreviation;
|
||||
public AlgoDisplayGroup group => _value?.group ?? AlgoDisplayGroup.BTS;
|
||||
public CustomOrderDefinitionImmutable definition => _algo_root?.definition ?? new CustomOrderDefinitionImmutable(new CustomOrderDefinition());
|
||||
public ObservableCollection<IAlgoSelector> child_algo_selectors => _child_algo_selectors;
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public ObservableCollection<IAlgoSelector> child_algo_selectors_view {
|
||||
get {
|
||||
_value?.algo_root?.ensure_init();
|
||||
return _child_algo_selectors;
|
||||
}
|
||||
}
|
||||
public IReadOnlyObservableCollection<IAlgoParamVM> algo_params => _algo_params;
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public dynamic children_vm => _algo_root?.children_vm;
|
||||
|
||||
IAlgoVM _algo_root => _value?.algo_root;
|
||||
|
||||
public void assign_to(CustomOrderDefinition value) => _algo_root?.assign_to(value);
|
||||
public bool key_match(KeyEventArgs e) => _algo_root?.key_match(e) ?? false;
|
||||
public void load_from(CustomOrderDefinitionImmutable value) => _algo_root?.load_from(value);
|
||||
public void load_many(IEnumerable<CustomOrderDefinitionImmutable> values) => _algo_root?.load_many(values);
|
||||
public Task<CustomOrderDefinitionImmutable?> load_from_async(CustomOrderDefinitionImmutable definition) => _algo_root is IAlgoVM vm ? vm.load_from_async(definition) : Task.FromResult<CustomOrderDefinitionImmutable?>(null);
|
||||
void IAlgoVM.ensure_init() => _value?.algo_root?.ensure_init();
|
||||
|
||||
IDisposable _set_hooks(AlgoDefinitionVM vm) {
|
||||
var algo_root_subs = new DisposableContainer();
|
||||
vm.PropertyChanged += on_value_prop_changed;
|
||||
return new CompositeDisposable(
|
||||
vm.observe_algo_root().Subscribe(r => {
|
||||
algo_root_subs.replace(null);
|
||||
_child_algo_selectors.clear_collections();
|
||||
_algo_params.clear_collections();
|
||||
if (r != null) {
|
||||
if (_data_cache.HasValue) {
|
||||
_data_cache.Value.forward_to(r);
|
||||
}
|
||||
_child_algo_selectors.add_collection(r.child_algo_selectors);
|
||||
_algo_params.add_collection(r.algo_params);
|
||||
((INotifyPropertyChanged)r).PropertyChanged += on_algo_root_prop_changed;
|
||||
algo_root_subs.replace(new CompositeDisposable(
|
||||
r.subscribe_definition().Subscribe(_ => _definition_changed?.Invoke())) {
|
||||
() => ((INotifyPropertyChanged)r).PropertyChanged -= on_algo_root_prop_changed
|
||||
});
|
||||
}
|
||||
RaisePropertyChanged(nameof(group));
|
||||
}),
|
||||
algo_root_subs) {
|
||||
() => vm.PropertyChanged -= on_value_prop_changed
|
||||
};
|
||||
void on_algo_root_prop_changed(object sender, PropertyChangedEventArgs e) {
|
||||
if (_algo_root_echo_props.Contains(e.PropertyName))
|
||||
RaisePropertyChanged(e.PropertyName);
|
||||
}
|
||||
void on_value_prop_changed(object sender, PropertyChangedEventArgs e) {
|
||||
if (_value_echo_props.TryGetValue(e.PropertyName, out string wrapper_prop))
|
||||
RaisePropertyChanged(wrapper_prop);
|
||||
}
|
||||
}
|
||||
|
||||
// permissions
|
||||
void IObserver<AlgoEditingPermissions>.OnNext(AlgoEditingPermissions value) {
|
||||
_data_cache = _data_cache?.with(value) ?? new _ObservedData(value, default, default, default);
|
||||
(_algo_root as IObserver<AlgoEditingPermissions>)?.OnNext(value);
|
||||
}
|
||||
void IObserver<AlgoEditingPermissions>.OnError(Exception error) { }
|
||||
void IObserver<AlgoEditingPermissions>.OnCompleted() { }
|
||||
// model
|
||||
void IObserver<OrdersModel>.OnNext(OrdersModel value) {
|
||||
_data_cache = _data_cache?.with(value) ?? new _ObservedData(default, value, default, default);
|
||||
(_algo_root as IObserver<OrdersModel>)?.OnNext(value);
|
||||
}
|
||||
void IObserver<OrdersModel>.OnError(Exception error) { }
|
||||
void IObserver<OrdersModel>.OnCompleted() { }
|
||||
// security
|
||||
void IObserver<SecurityImmutable?>.OnNext(SecurityImmutable? value) {
|
||||
_data_cache = _data_cache?.with(value) ?? new _ObservedData(default, default, value, default);
|
||||
(_algo_root as IObserver<SecurityImmutable?>)?.OnNext(value);
|
||||
}
|
||||
void IObserver<SecurityImmutable?>.OnError(Exception error) { }
|
||||
void IObserver<SecurityImmutable?>.OnCompleted() { }
|
||||
// security md
|
||||
void IObserver<SecurityMD?>.OnNext(SecurityMD? value) {
|
||||
_data_cache = _data_cache?.with(value) ?? new _ObservedData(default, default, default, value);
|
||||
(_algo_root as IObserver<SecurityMD?>)?.OnNext(value);
|
||||
}
|
||||
void IObserver<SecurityMD?>.OnError(Exception error) { }
|
||||
void IObserver<SecurityMD?>.OnCompleted() { }
|
||||
|
||||
struct _ObservedData {
|
||||
public readonly AlgoEditingPermissions permissions;
|
||||
public readonly OrdersModel model;
|
||||
public readonly SecurityImmutable? security;
|
||||
public readonly SecurityMD? security_md;
|
||||
|
||||
public _ObservedData(AlgoEditingPermissions permissions, OrdersModel model, SecurityImmutable? security, SecurityMD? security_md) {
|
||||
this.permissions = permissions;
|
||||
this.model = model;
|
||||
this.security = security;
|
||||
this.security_md = security_md;
|
||||
}
|
||||
|
||||
public void forward_to(IAlgoVM recipient) {
|
||||
(recipient as IObserver<AlgoEditingPermissions>)?.OnNext(permissions);
|
||||
if (model != null)
|
||||
(recipient as IObserver<OrdersModel>)?.OnNext(model);
|
||||
if (security_md.HasValue)
|
||||
(recipient as IObserver<SecurityMD?>)?.OnNext(security_md);
|
||||
if (security.HasValue)
|
||||
(recipient as IObserver<SecurityImmutable?>)?.OnNext(security);
|
||||
}
|
||||
|
||||
public _ObservedData with(AlgoEditingPermissions? permissions) =>
|
||||
new _ObservedData(permissions ?? this.permissions, model, security, security_md);
|
||||
|
||||
public _ObservedData with(OrdersModel model) =>
|
||||
new _ObservedData(permissions, model, security, security_md);
|
||||
|
||||
public _ObservedData with(SecurityImmutable? security) =>
|
||||
new _ObservedData(permissions, model, security, security_md);
|
||||
|
||||
public _ObservedData with(SecurityMD? security_md) =>
|
||||
new _ObservedData(permissions, model, security, security_md);
|
||||
}
|
||||
}
|
||||
}
|
||||
-622
@@ -1,622 +0,0 @@
|
||||
<UserControl
|
||||
x:Name="me"
|
||||
x:Class="mbts.ui.spark.AlgoEditorGridView"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uag="clr-namespace:mbts.ui.spark.algo"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Resources/DataGrid.xaml"/>
|
||||
<ResourceDictionary Source="/Modules/Spreader/ExecutionStrategyEditor.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!--Primitive Constants-->
|
||||
<sys:Double x:Key="HorizontalPush1">4</sys:Double>
|
||||
<sys:Double x:Key="HorizontalPush2">8</sys:Double>
|
||||
<sys:Double x:Key="HorizontalPush3">12</sys:Double>
|
||||
<sys:Double x:Key="VerticalPush1">4</sys:Double>
|
||||
<sys:Double x:Key="VerticalPush2">8</sys:Double>
|
||||
<sys:Double x:Key="VerticalPush3">12</sys:Double>
|
||||
<sys:String x:Key="NonUniformValueText">-</sys:String>
|
||||
<Thickness x:Key="Margin.LP1" Left="{StaticResource HorizontalPush1}"/>
|
||||
<Thickness x:Key="Margin.LP2" Left="{StaticResource HorizontalPush2}"/>
|
||||
<Thickness x:Key="Margin.LP3" Left="{StaticResource HorizontalPush3}"/>
|
||||
<Thickness x:Key="Margin.TP1" Top="{StaticResource VerticalPush1}"/>
|
||||
<Thickness x:Key="Margin.TP2" Top="{StaticResource VerticalPush2}"/>
|
||||
<Thickness x:Key="ComboBox.CellPadding" Top="2" Bottom="2"/>
|
||||
<!--Converters-->
|
||||
<uspcv:OrderTypeBrushConverter x:Key="OrderTypeBrushConverter" DefaultBrush="Transparent"/>
|
||||
<cv:TimeConverter x:Key="TimeConverter"/>
|
||||
<cv:GridLengthConverter x:Key="GridLengthPixelConverter" Multiplier="18" UnitType="Pixel"/>
|
||||
<cv:EnumItemsSourceConverter x:Key="EnumItemsSourceConverter"/>
|
||||
<cv:NullSubstitutionConverterWrapper x:Key="SimpleValueTextConverter" ValueForNull="{StaticResource NonUniformValueText}"/>
|
||||
<cv:NullSubstitutionConverterWrapper x:Key="NullableEnumDescriptionConverter" Converter="{StaticResource EnumDescriptionConverter}" ValueForNull="{StaticResource NonUniformValueText}"/>
|
||||
<cv:NullSubstitutionConverterWrapper x:Key="QuantityMeasureTextConverter" Converter="{x:Static cv:ValueConverters.QuantityMeasureTextConverter}" ValueForNull="{StaticResource NonUniformValueText}"/>
|
||||
<cv:DoubleTextConverter x:Key="DoubleTextConverter" NaNText="{StaticResource NonUniformValueText}"/>
|
||||
<cv:ValueSubstitutionConverterWrapper x:Key="StringTextConverter">
|
||||
<cv:KeyValue Key="{x:Null}" Value="{StaticResource NonUniformValueText}"/>
|
||||
<cv:KeyValue Key="{x:Static sys:String.Empty}" Value="{StaticResource NonUniformValueText}"/>
|
||||
</cv:ValueSubstitutionConverterWrapper>
|
||||
<cv:EnumHasFlagsConverter x:Key="ParamPermissionsToIsReadOnlyConverter">
|
||||
<sp:AlgoParamPermissions>None</sp:AlgoParamPermissions>
|
||||
<sp:AlgoParamPermissions>ReadOnly</sp:AlgoParamPermissions>
|
||||
</cv:EnumHasFlagsConverter>
|
||||
<!--Default Styles-->
|
||||
<Style TargetType="uag:AlgoPickerNodeView" BasedOn="{StaticResource {x:Type uag:AlgoPickerNodeView}}">
|
||||
<Setter Property="DropDownStyleKey" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorView}, Converter={x:Static usp:AlgoContentTemplateSelector.AlgoDropDownStyleKeyConverter}}"/>
|
||||
</Style>
|
||||
<!--Common Templates-->
|
||||
<DataTemplate x:Key="AlgoVMItemTemplate">
|
||||
<usp:OrderTypeItem DisplayName="{Binding display_name}" BadgeFill="{Binding meta.Color, Converter={StaticResource ColorToBrushConverter}}"/>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="ExecutionStrategyTemplate">
|
||||
<TextBlock>
|
||||
<Hyperlink Click="_on_execution_strategy_toggle_click">
|
||||
Show Strategy Params
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="IndentedParamNameAndBadgeCellTemplate">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{Binding level, Converter={StaticResource GridLengthPixelConverter}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<usp:OrderTypeItem Grid.Column="1" Focusable="False" IsTabStop="False" BadgeFill="{Binding Path='(sp:AlgoParamRowVM.algo).(sp:IAlgoVM.meta).Color', Converter={StaticResource ColorToBrushConverter}}" DisplayName="{Binding Path='param.(sp:IAlgoParamVM.display_name)'}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="NullableEnumDescriptionTemplate">
|
||||
<TextBlock Text="{Binding Converter={StaticResource NullableEnumDescriptionConverter}}"/>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="ParamNameAndDisambiguationCellTemplate">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Name="disambig_text" Margin="0,0,2,0" FontWeight="Light" Text="{Binding path_disambiguation, StringFormat='[{0}]', Mode=OneWay}"/>
|
||||
<TextBlock Text="{Binding Path='param.(sp:IAlgoParamVM.display_name)', Mode=OneWay}"/>
|
||||
<TextBlock Text="*" Visibility="{Binding param.is_dirty, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
</StackPanel>
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding path_disambiguation}" Value="{x:Static sys:String.Empty}">
|
||||
<Setter TargetName="disambig_text" Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="SimpleValueTextTemplate">
|
||||
<TextBlock x:Name="value_text" Text="{Binding param.value, Converter={StaticResource SimpleValueTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
|
||||
<!--Param Name Templates-->
|
||||
<u:KeyedTemplateSelector x:Key="ParamNameTemplateSelector" Default="{StaticResource ParamNameAndDisambiguationCellTemplate}">
|
||||
<u:TemplateSelectorKeyValue Key="{x:Static usp:AlgoParamTemplateKeys.Price}">
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="price_size_col0"/>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="price_size_col1"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Name="disambig_text" Margin="0,0,2,0" FontWeight="Light" Text="{Binding path_disambiguation, StringFormat='[{0}]', Mode=OneWay}"/>
|
||||
<TextBlock Text="{Binding Path='param.(sp:IAlgoParamVM.display_name)', Mode=OneWay}"/>
|
||||
<TextBlock Text="*" Visibility="{Binding param.is_dirty, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
</StackPanel>
|
||||
<TextBlock x:Name="bid_text" Grid.Column="1" HorizontalAlignment="Center">
|
||||
<TextBlock.ToolTip>
|
||||
<TextBlock>
|
||||
Best Bid Price (click to assign)
|
||||
<LineBreak/>
|
||||
<Italic>
|
||||
Hold a number key to cross the market by 'n' ticks.
|
||||
</Italic>
|
||||
</TextBlock>
|
||||
</TextBlock.ToolTip>
|
||||
<Hyperlink
|
||||
Focusable="False"
|
||||
Foreground="{u:BTSColor MarketData.bid}"
|
||||
Command="{Binding DataContext.payup_bid_cmd, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
|
||||
PreviewMouseLeftButtonDown="{x:Static usp:AlgoEditorGridView.PayupTicksMouseButtonHandler}"
|
||||
>
|
||||
<Run>
|
||||
<Run.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="DataContext.bbo.bid" RelativeSource="{RelativeSource AncestorType=usp:AlgoEditorGridView}"/>
|
||||
<Binding Path="DataContext.spec" RelativeSource="{RelativeSource AncestorType=usp:AlgoEditorGridView}"/>
|
||||
</MultiBinding>
|
||||
</Run.Text>
|
||||
</Run>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock x:Name="ask_text" Grid.Column="2" HorizontalAlignment="Center" Margin="4,0">
|
||||
<TextBlock.ToolTip>
|
||||
<TextBlock>
|
||||
Best Ask Price (click to assign)
|
||||
<LineBreak/>
|
||||
<Italic>
|
||||
Hold a number key to cross the market by 'n' ticks.
|
||||
</Italic>
|
||||
</TextBlock>
|
||||
</TextBlock.ToolTip>
|
||||
<Hyperlink
|
||||
Focusable="False"
|
||||
Foreground="{u:BTSColor MarketData.ask}"
|
||||
Command="{Binding DataContext.payup_ask_cmd, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
|
||||
PreviewMouseLeftButtonDown="{x:Static usp:AlgoEditorGridView.PayupTicksMouseButtonHandler}"
|
||||
>
|
||||
<Run>
|
||||
<Run.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="DataContext.bbo.ask" RelativeSource="{RelativeSource AncestorType=usp:AlgoEditorGridView}"/>
|
||||
<Binding Path="DataContext.spec" RelativeSource="{RelativeSource AncestorType=usp:AlgoEditorGridView}"/>
|
||||
</MultiBinding>
|
||||
</Run.Text>
|
||||
</Run>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding path_disambiguation}" Value="{x:Static sys:String.Empty}">
|
||||
<Setter TargetName="disambig_text" Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding DataContext.security, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}" Value="{x:Null}">
|
||||
<Setter TargetName="bid_text" Property="Visibility" Value="Collapsed"/>
|
||||
<Setter TargetName="ask_text" Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding param.permissions, Converter={StaticResource ParamPermissionsToIsReadOnlyConverter}}" Value="{u:True}">
|
||||
<Setter TargetName="bid_text" Property="ToolTip" Value="Best Bid Price"/>
|
||||
<Setter TargetName="ask_text" Property="ToolTip" Value="Best Ask Price"/>
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
</u:TemplateSelectorKeyValue>
|
||||
<u:TemplateSelectorKeyValue Key="{x:Static usp:AlgoParamTemplateKeys.Size}">
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="price_size_col0"/>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="price_size_col1"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Name="disambig_text" Margin="0,0,2,0" FontWeight="Light" Text="{Binding path_disambiguation, StringFormat='[{0}]', Mode=OneWay}"/>
|
||||
<TextBlock Text="{Binding Path='param.(sp:IAlgoParamVM.display_name)', Mode=OneWay}"/>
|
||||
<TextBlock Text="*" Visibility="{Binding param.is_dirty, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
</StackPanel>
|
||||
<TextBlock
|
||||
x:Name="bid_text"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{u:BTSColor MarketData.bid}"
|
||||
Text="{Binding DataContext.bbo.bid_size, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
|
||||
ToolTip="Best Bid Size"
|
||||
/>
|
||||
<TextBlock
|
||||
x:Name="ask_text"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="4,0"
|
||||
Foreground="{u:BTSColor MarketData.ask}"
|
||||
Text="{Binding DataContext.bbo.ask_size, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
|
||||
ToolTip="Best Ask Size"
|
||||
/>
|
||||
</Grid>
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding path_disambiguation}" Value="{x:Static sys:String.Empty}">
|
||||
<Setter TargetName="disambig_text" Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding DataContext.security, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}" Value="{x:Null}">
|
||||
<Setter TargetName="bid_text" Property="Visibility" Value="Collapsed"/>
|
||||
<Setter TargetName="ask_text" Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
</u:TemplateSelectorKeyValue>
|
||||
</u:KeyedTemplateSelector>
|
||||
|
||||
<!--Read Templates-->
|
||||
<usp:AlgoParamTemplateSelector
|
||||
x:Key="ParamTemplateSelector"
|
||||
IntTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
NullableIntTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
UIntTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
NullableUIntTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
ExecutionStrategyTemplate="{StaticResource ExecutionStrategyTemplate}"
|
||||
>
|
||||
<usp:AlgoParamTemplateSelector.KeyedTemplates>
|
||||
<u:TemplateSelectorKeyValue Key="{x:Static usp:AlgoParamTemplateKeys.Price}">
|
||||
<DataTemplate>
|
||||
<TextBlock>
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="param.value"/>
|
||||
<Binding Path="DataContext.spec" RelativeSource="{RelativeSource AncestorType=usp:AlgoEditorGridView}"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</u:TemplateSelectorKeyValue>
|
||||
</usp:AlgoParamTemplateSelector.KeyedTemplates>
|
||||
<!--Bool-->
|
||||
<usp:AlgoParamTemplateSelector.BoolTemplate>
|
||||
<DataTemplate>
|
||||
<CheckBox Focusable="False" VerticalAlignment="Center" IsChecked="{Binding param.value, Mode=TwoWay}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.BoolTemplate>
|
||||
<!--Double-->
|
||||
<usp:AlgoParamTemplateSelector.DoubleTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding param.value, Converter={StaticResource DoubleTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.DoubleTemplate>
|
||||
<!--Enum-->
|
||||
<usp:AlgoParamTemplateSelector.EnumTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding param.value, Converter={StaticResource EnumDescriptionConverter}}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.EnumTemplate>
|
||||
<!--Nullable Enum-->
|
||||
<usp:AlgoParamTemplateSelector.NullableEnumTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding param.value, Converter={StaticResource NullableEnumDescriptionConverter}}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.NullableEnumTemplate>
|
||||
<!--Order Def-->
|
||||
<usp:AlgoParamTemplateSelector.OrderDefTemplate>
|
||||
<DataTemplate>
|
||||
<usp:OrderTypeItem BadgeFill="{Binding Path='(sp:AlgoParamRowVM.param).(sp:IAlgoSelector.selected_algo).meta.Color', Converter={StaticResource ColorToBrushConverter}}" DisplayName="{Binding Path='(sp:AlgoParamRowVM.param).(sp:IAlgoSelector.selected_algo).(sp:IAlgoVM.display_name)'}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.OrderDefTemplate>
|
||||
<!--Quantity Measure-->
|
||||
<usp:AlgoParamTemplateSelector.QuantityMeasureTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding param.value, Converter={StaticResource QuantityMeasureTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.QuantityMeasureTemplate>
|
||||
<!--String-->
|
||||
<usp:AlgoParamTemplateSelector.StringTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding param.value, Converter={StaticResource StringTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.StringTemplate>
|
||||
<!--Timestamp-->
|
||||
<usp:AlgoParamTemplateSelector.TimestampTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding param.value, Converter={StaticResource TimeConverter}}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.TimestampTemplate>
|
||||
</usp:AlgoParamTemplateSelector>
|
||||
|
||||
<!--Write Templates-->
|
||||
<usp:AlgoParamTemplateSelector x:Key="ParamEditingTemplateSelector" ExecutionStrategyTemplate="{StaticResource ExecutionStrategyTemplate}">
|
||||
<usp:AlgoParamTemplateSelector.KeyedTemplates>
|
||||
<u:TemplateSelectorKeyValue Key="{x:Static usp:AlgoParamTemplateKeys.Price}">
|
||||
<DataTemplate>
|
||||
<usp:PriceEntryControl
|
||||
Value="{Binding param.value, UpdateSourceTrigger=PropertyChanged}"
|
||||
ContractSpec="{Binding DataContext.spec, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
|
||||
Focusable="False"
|
||||
IsTabStop="False"
|
||||
ArrowKeyTickModifiers="Ctrl"
|
||||
>
|
||||
<usp:PriceEntryControl.CommandBindings>
|
||||
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
|
||||
</usp:PriceEntryControl.CommandBindings>
|
||||
<usp:PriceEntryControl.InputBindings>
|
||||
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
|
||||
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
|
||||
</usp:PriceEntryControl.InputBindings>
|
||||
</usp:PriceEntryControl>
|
||||
</DataTemplate>
|
||||
</u:TemplateSelectorKeyValue>
|
||||
</usp:AlgoParamTemplateSelector.KeyedTemplates>
|
||||
<!--Bool-->
|
||||
<usp:AlgoParamTemplateSelector.BoolTemplate>
|
||||
<DataTemplate>
|
||||
<CheckBox Focusable="False" VerticalAlignment="Center" IsChecked="{Binding param.value, Mode=TwoWay}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.BoolTemplate>
|
||||
<!--Double-->
|
||||
<usp:AlgoParamTemplateSelector.DoubleTemplate>
|
||||
<DataTemplate>
|
||||
<c:TickUpDownDouble x:Name="tick_up_down" Focusable="False" HorizontalAlignment="Stretch" Value="{Binding param.value, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
|
||||
<c:TickUpDownDouble.CommandBindings>
|
||||
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
|
||||
</c:TickUpDownDouble.CommandBindings>
|
||||
<c:TickUpDownDouble.InputBindings>
|
||||
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
|
||||
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
|
||||
</c:TickUpDownDouble.InputBindings>
|
||||
</c:TickUpDownDouble>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.DoubleTemplate>
|
||||
<!--Enum-->
|
||||
<usp:AlgoParamTemplateSelector.EnumTemplate>
|
||||
<DataTemplate>
|
||||
<ComboBox
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding param.value, Converter={StaticResource EnumItemsSourceConverter}, Mode=OneTime}"
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
SelectedItem="{Binding param.value, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.EnumTemplate>
|
||||
<!--Int-->
|
||||
<usp:AlgoParamTemplateSelector.IntTemplate>
|
||||
<DataTemplate>
|
||||
<c:TickUpDownInt Focusable="False" HorizontalAlignment="Stretch" Value="{Binding param.value, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
|
||||
<c:TickUpDownInt.CommandBindings>
|
||||
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
|
||||
</c:TickUpDownInt.CommandBindings>
|
||||
<c:TickUpDownInt.InputBindings>
|
||||
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
|
||||
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
|
||||
</c:TickUpDownInt.InputBindings>
|
||||
</c:TickUpDownInt>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.IntTemplate>
|
||||
<!--Nullable Bool-->
|
||||
<usp:AlgoParamTemplateSelector.NullableBoolTemplate>
|
||||
<DataTemplate>
|
||||
<CheckBox Focusable="False" VerticalAlignment="Center" IsThreeState="True" IsChecked="{Binding param.value, Mode=TwoWay}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.NullableBoolTemplate>
|
||||
<!--Nullable Enum-->
|
||||
<usp:AlgoParamTemplateSelector.NullableEnumTemplate>
|
||||
<DataTemplate>
|
||||
<ComboBox
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding Converter={x:Static usp:AlgoEditorGridView.NullableEnumParamRowItemsSourceConverter}}"
|
||||
ItemTemplate="{StaticResource NullableEnumDescriptionTemplate}"
|
||||
SelectedItem="{Binding param.value, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.NullableEnumTemplate>
|
||||
<!--Nullable Int-->
|
||||
<usp:AlgoParamTemplateSelector.NullableIntTemplate>
|
||||
<DataTemplate>
|
||||
<c:TickUpDownNullableInt Focusable="False" HorizontalAlignment="Stretch" Value="{Binding param.value, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
|
||||
<c:TickUpDownNullableInt.CommandBindings>
|
||||
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
|
||||
</c:TickUpDownNullableInt.CommandBindings>
|
||||
<c:TickUpDownNullableInt.InputBindings>
|
||||
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
|
||||
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
|
||||
</c:TickUpDownNullableInt.InputBindings>
|
||||
</c:TickUpDownNullableInt>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.NullableIntTemplate>
|
||||
<!--Nullable UInt-->
|
||||
<usp:AlgoParamTemplateSelector.NullableUIntTemplate>
|
||||
<DataTemplate>
|
||||
<c:TickUpDownNullableUInt Focusable="False" HorizontalAlignment="Stretch" Value="{Binding param.value, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
|
||||
<c:TickUpDownNullableUInt.CommandBindings>
|
||||
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
|
||||
</c:TickUpDownNullableUInt.CommandBindings>
|
||||
<c:TickUpDownNullableUInt.InputBindings>
|
||||
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
|
||||
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
|
||||
</c:TickUpDownNullableUInt.InputBindings>
|
||||
</c:TickUpDownNullableUInt>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.NullableUIntTemplate>
|
||||
<!--Order Def-->
|
||||
<usp:AlgoParamTemplateSelector.OrderDefTemplate>
|
||||
<DataTemplate>
|
||||
<ComboBox HorizontalAlignment="Stretch" DataContext="{Binding param}" ItemsSource="{Binding Path='(sp:IAlgoSelector.algos)'}" ItemTemplate="{StaticResource AlgoVMItemTemplate}" SelectedItem="{Binding Path='(sp:IAlgoSelector.selected_algo)', UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.OrderDefTemplate>
|
||||
<!--QuantityMeasure-->
|
||||
<usp:AlgoParamTemplateSelector.QuantityMeasureTemplate>
|
||||
<DataTemplate>
|
||||
<uag:QuantityMeasureControl Focusable="False" HorizontalAlignment="Stretch" QuantityMeasure="{Binding param.value, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
|
||||
<uag:QuantityMeasureControl.CommandBindings>
|
||||
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
|
||||
</uag:QuantityMeasureControl.CommandBindings>
|
||||
<uag:QuantityMeasureControl.InputBindings>
|
||||
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
|
||||
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
|
||||
</uag:QuantityMeasureControl.InputBindings>
|
||||
</uag:QuantityMeasureControl>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.QuantityMeasureTemplate>
|
||||
<!--Timestamp-->
|
||||
<usp:AlgoParamTemplateSelector.TimestampTemplate>
|
||||
<DataTemplate>
|
||||
<Grid DataContext="{Binding algo}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox
|
||||
x:Name="PART_StartMode_ComboBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding start_modes}"
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
SelectedItem="{Binding start_mode}"
|
||||
/>
|
||||
<c:DateTimePicker
|
||||
x:Name="PART_AbsoluteStart_DateTimePicker"
|
||||
Grid.Column="2"
|
||||
TimeTuple="{Binding absolute_start}"
|
||||
Visibility="{Binding show_absolute_start, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
/>
|
||||
<c:TimeSpanControl
|
||||
x:Name="PART_RelativeStart_TimeSpanControl"
|
||||
Grid.Column="2"
|
||||
TimeSpan="{Binding relative_start, Mode=TwoWay}"
|
||||
Visibility="{Binding show_relative_start, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.TimestampTemplate>
|
||||
<!--UInt-->
|
||||
<usp:AlgoParamTemplateSelector.UIntTemplate>
|
||||
<DataTemplate>
|
||||
<c:TickUpDownUInt Focusable="False" HorizontalAlignment="Stretch" Value="{Binding param.value, UpdateSourceTrigger=PropertyChanged}">
|
||||
<c:TickUpDownUInt.CommandBindings>
|
||||
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
|
||||
</c:TickUpDownUInt.CommandBindings>
|
||||
<c:TickUpDownUInt.InputBindings>
|
||||
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
|
||||
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
|
||||
</c:TickUpDownUInt.InputBindings>
|
||||
</c:TickUpDownUInt>
|
||||
</DataTemplate>
|
||||
</usp:AlgoParamTemplateSelector.UIntTemplate>
|
||||
</usp:AlgoParamTemplateSelector>
|
||||
<usp:AlgoEditorGridRowStyleSelector x:Key="RowStyleSelector">
|
||||
<usp:AlgoEditorGridRowStyleSelector.DefaultStyle>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Style.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding param.is_dirty}" Value="{u:True}"/>
|
||||
<Condition Binding="{Binding AnimateDirty, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}" Value="{u:True}"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="Background" Value="#8098FB98"/>
|
||||
<Setter Property="ToolTip" Value="Value has been modified."/>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</usp:AlgoEditorGridRowStyleSelector.DefaultStyle>
|
||||
<usp:AlgoEditorGridRowStyleSelector.SpreaderStyle>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Setter Property="DetailsVisibility" Value="Collapsed"/>
|
||||
<Setter Property="DetailsTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<AdornerDecorator>
|
||||
<usp:ExecutionStrategyEditor
|
||||
DataContext="{Binding algo.strategy_editor}"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="6"
|
||||
SyntheticName="{Binding name}"
|
||||
TickFactors="{Binding tick_factors}"
|
||||
IsNew="{Binding is_new}"
|
||||
CanUserAddNewLeg="{Binding can_add_leg}"
|
||||
IsNewLegPopupOpen="{Binding is_new_leg_popup_open}"
|
||||
PriceCalc="{Binding price_calc}"
|
||||
ParameterRows="{Binding rows}"
|
||||
LegTuples="{Binding leg_tuples}"
|
||||
RemoveLegCommand="{Binding remove_leg}"
|
||||
ContractPickerModel="{Binding contract_picker_model}"
|
||||
/>
|
||||
</AdornerDecorator>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</usp:AlgoEditorGridRowStyleSelector.SpreaderStyle>
|
||||
</usp:AlgoEditorGridRowStyleSelector>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager>
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding IsReadOnly, Source={u:DirectReference me}, Converter={x:Static usp:AlgoEditorGridView.GeneralVisualStateConverter}}"/>
|
||||
<u:StateDefinition State="{Binding CanUserChangeAlgoRoot, Source={u:DirectReference me}, Converter={x:Static usp:AlgoEditorGridView.AlgoRootVisualStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested usp:AlgoEditorGridView.VisualStateNames.General}}">
|
||||
<VisualState Name="{x:Static Normal, MemberType={u:Nested usp:AlgoEditorGridView.VisualStateNames.General}}"/>
|
||||
<VisualState Name="{x:Static ReadOnly, MemberType={u:Nested usp:AlgoEditorGridView.VisualStateNames.General}}">
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_param_grid" Storyboard.TargetProperty="(DataGrid.IsReadOnly)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="True"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<ColorAnimation Storyboard.TargetName="_param_grid" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="Gray"/>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="root_algo_combo_box" Storyboard.TargetProperty="(Control.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested usp:AlgoEditorGridView.VisualStateNames.AlgoRoot}}">
|
||||
<VisualState Name="{x:Static Inactive, MemberType={u:Nested usp:AlgoEditorGridView.VisualStateNames.AlgoRoot}}"/>
|
||||
<VisualState Name="{x:Static Active, MemberType={u:Nested usp:AlgoEditorGridView.VisualStateNames.AlgoRoot}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="root_algo_label" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="root_algo_combo_box" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ThicknessAnimation Storyboard.TargetName="_param_grid" Storyboard.TargetProperty="(Control.Margin)" BeginTime="0" Duration="0" To="0,4,0,0"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock x:Name="root_algo_label" VerticalAlignment="Center" Margin="{StaticResource Margin.LP2}" Visibility="Collapsed">Algo</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="root_algo_combo_box"
|
||||
Margin="40,0,0,0"
|
||||
ItemsSource="{Binding AlgoRootsViewSource.View, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
|
||||
SelectedItem="{Binding SelectedAlgoRoot, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
|
||||
Visibility="Collapsed"
|
||||
>
|
||||
<ComboBox.GroupStyle>
|
||||
<GroupStyle ContainerStyle="{StaticResource ComboBox.DelineatedGroupContainerStyle}"/>
|
||||
</ComboBox.GroupStyle>
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<usp:OrderTypeItem BadgeFill="{Binding meta.Color, Converter={StaticResource ColorToBrushConverter}}" DisplayName="{Binding display_name}"/>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<DataGrid
|
||||
x:Name="_param_grid"
|
||||
Grid.Row="1"
|
||||
Grid.IsSharedSizeScope="True"
|
||||
VirtualizingPanel.ScrollUnit="Pixel"
|
||||
AlternatingRowBackground="{x:Null}"
|
||||
IsReadOnly="False"
|
||||
Margin="0,2,0,0"
|
||||
MinRowHeight="18"
|
||||
ColumnHeaderHeight="0"
|
||||
ItemsSource="{Binding AlgoParamRowsViewSource.View, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
|
||||
SelectionMode="Single"
|
||||
RowStyleSelector="{StaticResource RowStyleSelector}"
|
||||
>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn IsReadOnly="True" Header="Param" CellTemplateSelector="{StaticResource ParamNameTemplateSelector}" MinWidth="120" Width="*">
|
||||
<DataGridTemplateColumn.CellStyle>
|
||||
<Style TargetType="DataGridCell" BasedOn="{StaticResource DataGridCellBaseStyle}">
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
</Style>
|
||||
</DataGridTemplateColumn.CellStyle>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Header="Value" CellTemplateSelector="{StaticResource ParamTemplateSelector}" CellEditingTemplateSelector="{StaticResource ParamEditingTemplateSelector}" MinWidth="120" Width="*">
|
||||
<DataGridTemplateColumn.CellStyle>
|
||||
<Style TargetType="DataGridCell" BasedOn="{StaticResource DataGridCellBaseStyle}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsReadOnly, RelativeSource={RelativeSource AncestorType=DataGrid}}" Value="{u:False}">
|
||||
<Setter Property="ToolTip" Value="Click to edit"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</DataGridTemplateColumn.CellStyle>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
-207
@@ -1,207 +0,0 @@
|
||||
using bts.collections;
|
||||
using bts.utils;
|
||||
using mbts.spark;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
partial class AlgoEditorGridView : UserControl {
|
||||
public static readonly IValueConverter
|
||||
GeneralVisualStateConverter = ValueConverter.create<bool, string>(is_read_only => is_read_only ? VisualStateNames.General.ReadOnly : VisualStateNames.General.Normal),
|
||||
AlgoRootVisualStateConverter = ValueConverter.create<bool, string>(show_bbo => show_bbo ? VisualStateNames.AlgoRoot.Active : VisualStateNames.AlgoRoot.Inactive),
|
||||
AlgoRootComboBoxMarginConverter = ValueConverter.create<double, Thickness>(name_col_width => new Thickness(name_col_width + 8, 0, 0, 0)),
|
||||
NullableEnumParamRowItemsSourceConverter = ValueConverter.create<AlgoParamRowVM, IEnumerable>(param_row => {
|
||||
if (param_row?.param != null) {
|
||||
var tenum = param_row.param.GetType().GetInterfaceGenericArgument(AlgoParamTemplateSelector.generic_algo_param_itype);
|
||||
if (tenum.IsNullable()) {
|
||||
// .First() should be safe, since we know this is a Nullable<T>.
|
||||
tenum = tenum.GetGenericArguments().First();
|
||||
}
|
||||
if (tenum.IsEnum)
|
||||
return Enum.GetValues(tenum);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
public static readonly MouseButtonEventHandler PayupTicksMouseButtonHandler = (s, e) => {
|
||||
if ((s as Hyperlink)?.Command is ICommand cmd) {
|
||||
cmd.ExecuteIfCan(KeyboardExtensions.GetPressedNumber() ?? 0);
|
||||
e.Handled = true;
|
||||
}
|
||||
};
|
||||
|
||||
readonly ObservableAggregateCollection<AlgoParamRowVM> _param_rows = new ObservableAggregateCollection<AlgoParamRowVM>();
|
||||
|
||||
public AlgoEditorGridView() {
|
||||
InitializeComponent();
|
||||
_set_value_column_width();
|
||||
// AlgoRoots
|
||||
AlgoRootsViewSource = new CollectionViewSource<_AlgoRootsCollectionView>();
|
||||
BindingOperations.SetBinding(AlgoRootsViewSource, CollectionViewSource.SourceProperty, new Binding(nameof(AlgoRoots)) { Source = this });
|
||||
AlgoRootsViewSource.GroupDescriptions.Add(new PropertyGroupDescription(nameof(IAlgoVM.group)));
|
||||
// AlgoParamRows
|
||||
AlgoParamRowsViewSource = new CollectionViewSource<ListCollectionView<AlgoParamRowVM>>();
|
||||
BindingOperations.SetBinding(AlgoParamRowsViewSource, CollectionViewSource.SourceProperty, new Binding(nameof(AlgoParamRows)) { Source = this });
|
||||
AlgoParamRowsViewSource.IsLiveFilteringRequested = true;
|
||||
AlgoParamRowsViewSource.LiveFilteringProperties.Add(nameof(AlgoParamRowVM.permissions));
|
||||
AlgoParamRowsViewSource.Filter += param_rows_filter;
|
||||
DataGridEditHelper.set_hooks(this);
|
||||
void param_rows_filter(object sender, FilterEventArgs e) =>
|
||||
e.Accepted = e.Item is AlgoParamRowVM row && row.param.permissions != AlgoParamPermissions.None;
|
||||
}
|
||||
|
||||
public enum Columns { ParamName, Value }
|
||||
|
||||
event Action CommitEditRequested, CancelEditRequested;
|
||||
|
||||
public CollectionViewSource AlgoRootsViewSource { get; }
|
||||
public CollectionViewSource<ListCollectionView<AlgoParamRowVM>> AlgoParamRowsViewSource { get; }
|
||||
|
||||
bool _stretch_value_column = true;
|
||||
public bool StretchValueColumn {
|
||||
get => _stretch_value_column;
|
||||
set {
|
||||
if (_stretch_value_column != value) {
|
||||
_stretch_value_column = value;
|
||||
_set_value_column_width();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region AnimateDirtyParams
|
||||
public static readonly DependencyProperty AnimateDirtyProperty =
|
||||
WPFHelper.create_dp<bool, AlgoEditorGridView>(nameof(AnimateDirty), true);
|
||||
|
||||
public bool AnimateDirty {
|
||||
get => (bool)GetValue(AnimateDirtyProperty);
|
||||
set => SetValue(AnimateDirtyProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IsReadOnly
|
||||
public static readonly DependencyProperty IsReadOnlyProperty =
|
||||
WPFHelper.create_dp<bool, AlgoEditorGridView>(nameof(IsReadOnly));
|
||||
|
||||
public bool IsReadOnly {
|
||||
get => (bool)GetValue(IsReadOnlyProperty);
|
||||
set => SetValue(IsReadOnlyProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CanUserChangeAlgoRoot
|
||||
public static readonly DependencyProperty CanUserChangeAlgoRootProperty =
|
||||
WPFHelper.create_dp<bool, AlgoEditorGridView>(nameof(CanUserChangeAlgoRoot));
|
||||
|
||||
public bool CanUserChangeAlgoRoot {
|
||||
get => (bool)GetValue(CanUserChangeAlgoRootProperty);
|
||||
set => SetValue(CanUserChangeAlgoRootProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AlgoRoots
|
||||
public static readonly DependencyProperty AlgoRootsProperty =
|
||||
WPFHelper.create_dp<IReadOnlyObservableCollection<IAlgoVM>, AlgoEditorGridView>(nameof(AlgoRoots));
|
||||
|
||||
public IReadOnlyObservableCollection<IAlgoVM> AlgoRoots {
|
||||
get => GetValue(AlgoRootsProperty) as IReadOnlyObservableCollection<IAlgoVM>;
|
||||
set => SetValue(AlgoRootsProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SelectedAlgoRoot
|
||||
public static readonly DependencyProperty SelectedAlgoRootProperty =
|
||||
WPFHelper.create_dp<IAlgoVM, AlgoEditorGridView>(nameof(SelectedAlgoRoot), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
|
||||
|
||||
public IAlgoVM SelectedAlgoRoot {
|
||||
get => GetValue(SelectedAlgoRootProperty) as IAlgoVM;
|
||||
set => SetValue(SelectedAlgoRootProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AlgoParamRows
|
||||
public static readonly DependencyProperty AlgoParamRowsProperty =
|
||||
WPFHelper.create_dp<IReadOnlyObservableCollection<AlgoParamRowVM>, AlgoEditorGridView>(nameof(AlgoParamRows));
|
||||
|
||||
public IReadOnlyObservableCollection<AlgoParamRowVM> AlgoParamRows {
|
||||
get => GetValue(AlgoParamRowsProperty) as IReadOnlyObservableCollection<AlgoParamRowVM>;
|
||||
set => SetValue(AlgoParamRowsProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static AlgoEditorGridView create_attach_view() =>
|
||||
new AlgoEditorGridView {
|
||||
Margin = new Thickness(6),
|
||||
CanUserChangeAlgoRoot = true,
|
||||
};
|
||||
|
||||
public void commit_edit() => CommitEditRequested?.Invoke();
|
||||
public void cancel_edit() => CancelEditRequested?.Invoke();
|
||||
|
||||
void _set_value_column_width() {
|
||||
foreach (var c in _param_grid?.Columns.empty_if_null())
|
||||
c.Width = _stretch_value_column ? new DataGridLength(1, DataGridLengthUnitType.Star) : DataGridLength.Auto;
|
||||
}
|
||||
|
||||
void _on_execution_strategy_toggle_click(object sender, RoutedEventArgs e) {
|
||||
if (sender is Hyperlink hl && hl.try_find_ancestor_of_type(out DataGridRow row_view)) {
|
||||
Visibility vis; string text;
|
||||
if (row_view.DetailsVisibility == Visibility.Visible) {
|
||||
vis = Visibility.Collapsed;
|
||||
text = "Show Strategy Params";
|
||||
} else {
|
||||
vis = Visibility.Visible;
|
||||
text = "Hide Strategy Params";
|
||||
}
|
||||
hl.Inlines.Clear();
|
||||
hl.Inlines.Add(text);
|
||||
row_view.SetCurrentValue(DataGridRow.DetailsVisibilityProperty, vis);
|
||||
}
|
||||
}
|
||||
|
||||
public static class VisualStateNames {
|
||||
public static class General {
|
||||
public const string GroupName = nameof(General);
|
||||
|
||||
public static readonly string
|
||||
Normal = $"{GroupName}{nameof(Normal)}",
|
||||
ReadOnly = $"{GroupName}{nameof(ReadOnly)}";
|
||||
}
|
||||
|
||||
public static class AlgoRoot {
|
||||
public const string GroupName = nameof(AlgoRoot);
|
||||
|
||||
public static readonly string
|
||||
Active = $"{GroupName}{VisualStates.Active}",
|
||||
Inactive = $"{GroupName}{VisualStates.Inactive}";
|
||||
}
|
||||
}
|
||||
|
||||
class _AlgoRootsCollectionView : ListCollectionView<IAlgoVM> {
|
||||
public _AlgoRootsCollectionView(IList<IAlgoVM> list) : base(list) =>
|
||||
CustomSort = AlgoCollectionHelper.algo_vm_comparer.to_non_generic();
|
||||
}
|
||||
}
|
||||
|
||||
class AlgoEditorGridRowStyleSelector : StyleSelector {
|
||||
public Style DefaultStyle { get; set; }
|
||||
public Style SpreaderStyle { get; set; }
|
||||
|
||||
public override Style SelectStyle(object item, DependencyObject container) {
|
||||
if (item is AlgoParamRowVM row) {
|
||||
if (row.param.id == nameof(bts.api.orderman.SpreaderOrderDefinition.ExecutionStrategy))
|
||||
return SpreaderStyle;
|
||||
}
|
||||
return DefaultStyle;
|
||||
}
|
||||
}
|
||||
}
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
using bts.collections;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
|
||||
namespace mbts.spark {
|
||||
static class AlgoParamRowsHelper {
|
||||
public static IDisposable connect(AlgoDefinitionVM algo, ObservableAggregateCollection<AlgoParamRowVM> rows, bool include_algo_type_rows) =>
|
||||
algo == null ? null : new CompositeDisposable(algo.observe_algo_root().SubscribeEach(a => {
|
||||
rows.clear_collections();
|
||||
if (a == null)
|
||||
return Disposable.empty;
|
||||
var child_rows = new ObservableAggregateCollection<AlgoParamRowVM>();
|
||||
/// Note: the order of operations here matters
|
||||
/// <see cref="_wrap_rows(IAlgoVM, AlgoParamRowVM, ObservableAggregateCollection{AlgoParamRowVM}, bool)" /> has the potential to trigger a lot of CollectionChanged events (depending on how deep the algo-tree is).
|
||||
/// There is no need to burden the aggregate 'rows' with echoing all of these events, if the child-row collection is finished changing when it is added to the aggregate.
|
||||
var hook = _wrap_rows(a, null, child_rows, include_algo_type_rows);
|
||||
rows.add_collection(child_rows.AsReadOnly());
|
||||
return hook;
|
||||
})) {
|
||||
rows.clear_collections
|
||||
};
|
||||
|
||||
public static IDisposable negate_is_dirty(IReadOnlyObservableCollection<AlgoParamRowVM> rows) {
|
||||
return ObservableAutoWrapper.connect_item_hooks(rows, hook);
|
||||
IDisposable hook(AlgoParamRowVM r) {
|
||||
r.param.is_dirty_changed += negate;
|
||||
return Disposable.Create(unhook);
|
||||
void negate(bool is_dirty) {
|
||||
if (is_dirty)
|
||||
r.param.mark_committed();
|
||||
}
|
||||
void unhook() =>
|
||||
r.param.is_dirty_changed -= negate;
|
||||
}
|
||||
}
|
||||
|
||||
static IDisposable _wrap_rows(AlgoDefinitionVM algo_def, ObservableAggregateCollection<AlgoParamRowVM> rows, bool include_algo_type_rows) {
|
||||
AlgoParamRowVM root = null;
|
||||
ObservableAggregateCollection<AlgoParamRowVM> algo_params;
|
||||
if (include_algo_type_rows) {
|
||||
// while normal params live and die with the selected algo-type (when the algo-type changes, they are removed, and replaced with params for the new algo type),
|
||||
// the algo-type row is constant (it is the row with the algo-type dropdown, which the user may use to select different algos).
|
||||
algo_params = new ObservableAggregateCollection<AlgoParamRowVM>();
|
||||
rows.add_collection(new ObservableAppendCollection<AlgoParamRowVM>(algo_params, root = new AlgoSelectorParamRowVM(new AlgoVMWrapper(algo_def), algo_def, null)).AsReadOnly());
|
||||
} else {
|
||||
algo_params = rows;
|
||||
}
|
||||
return algo_def.observe_algo_root().SubscribeEach(a => {
|
||||
algo_params.clear_collections();
|
||||
if (a == null)
|
||||
return Disposable.empty;
|
||||
var child_rows = new ObservableAggregateCollection<AlgoParamRowVM>();
|
||||
/// Note: the order of operations here matters
|
||||
/// <see cref="_wrap_rows(IAlgoVM, AlgoParamRowVM, ObservableAggregateCollection{AlgoParamRowVM}, bool)" /> has the potential to trigger a lot of CollectionChanged events (depending on how deep the algo-tree is).
|
||||
/// There is no need to burden the aggregate 'rows' with echoing all of these events, if the child-row collection is finished changing when it is added to the aggregate.
|
||||
var hook = _wrap_rows(a, root, child_rows, include_algo_type_rows);
|
||||
algo_params.add_collection(child_rows.AsReadOnly());
|
||||
return hook;
|
||||
});
|
||||
}
|
||||
|
||||
static IDisposable _wrap_rows(IAlgoVM algo_root, AlgoParamRowVM parent, ObservableAggregateCollection<AlgoParamRowVM> all_rows, bool include_algo_type_rows) {
|
||||
var wrapped_params = new ObservableAutoWrapper<AlgoParamRowVM, IAlgoParamVM>(algo_root.algo_params, pvm => new AlgoParamRowVM(algo_root, pvm, parent));
|
||||
var selector_params = new ObservableCollection<AlgoParamRowVM>();
|
||||
all_rows.add_collection(wrapped_params);
|
||||
all_rows.add_collection(selector_params);
|
||||
return ObservableAggregateCollection.connect(selector_params, algo_root.child_algo_selectors, new Func<IAlgoSelector, (ObservableCollection<AlgoParamRowVM>, IDisposable)>(hook_children));
|
||||
(ObservableCollection<AlgoParamRowVM>, IDisposable) hook_children(IAlgoSelector s) {
|
||||
var child_rows = new ObservableAggregateCollection<AlgoParamRowVM>();
|
||||
var selector_wrapper = new AlgoSelectorParamRowVM(algo_root, s, parent);
|
||||
return (
|
||||
include_algo_type_rows ?
|
||||
(ObservableCollection<AlgoParamRowVM>)new ObservableAppendCollection<AlgoParamRowVM>(child_rows, selector_wrapper) :
|
||||
child_rows,
|
||||
s.subscribe_selected_algo().SubscribeEach(a => {
|
||||
s.value = a;
|
||||
child_rows.clear_collections();
|
||||
return _wrap_rows(a, selector_wrapper, child_rows, include_algo_type_rows);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
using bts.collections;
|
||||
using bts.spark;
|
||||
using mbts.spark;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
static class AlgoParamTemplateKeys {
|
||||
public const string
|
||||
Price = nameof(Price),
|
||||
Size = nameof(Size);
|
||||
}
|
||||
|
||||
/// TODO: remove this class and just use a <see cref="KeyedTemplateSelector"/>.
|
||||
class AlgoParamTemplateSelector : DataTemplateSelector {
|
||||
public static readonly Type generic_algo_param_itype = typeof(IAlgoParamVM<>);
|
||||
|
||||
KeyedCollection<TemplateSelectorKeyValue, string> _keyed_templates = new KeyedCollection<TemplateSelectorKeyValue, string>(tsk => tsk.Key);
|
||||
|
||||
public DataTemplate BoolTemplate { get; set; }
|
||||
public DataTemplate NullableBoolTemplate { get; set; }
|
||||
public DataTemplate IntTemplate { get; set; }
|
||||
public DataTemplate NullableIntTemplate { get; set; }
|
||||
public DataTemplate UIntTemplate { get; set; }
|
||||
public DataTemplate NullableUIntTemplate { get; set; }
|
||||
public DataTemplate DoubleTemplate { get; set; }
|
||||
public DataTemplate EnumTemplate { get; set; }
|
||||
public DataTemplate NullableEnumTemplate { get; set; }
|
||||
public DataTemplate StringTemplate { get; set; }
|
||||
public DataTemplate QuantityMeasureTemplate { get; set; }
|
||||
public DataTemplate OrderDefTemplate { get; set; }
|
||||
public DataTemplate ExecutionStrategyTemplate { get; set; }
|
||||
public DataTemplate TimestampTemplate { get; set; }
|
||||
|
||||
/// <see cref="_keyed_templates"/> is needed for the 'price' param. Price was formerly edited in a separate control, however now it is just another param row.
|
||||
/// Before this, the generic type argument in the <see cref="IAlgoParamVM{T}"/> implementation was enough to determine the template.
|
||||
/// Since 'price' is just a double, further specification was needed to associate it with a 'PriceEntryControl' template.
|
||||
public IList KeyedTemplates => _keyed_templates;
|
||||
|
||||
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
|
||||
if (item is ITemplateSelectorKey tsk && tsk.key is string template_key && _keyed_templates.TryGetValue(template_key, out var tkv))
|
||||
return tkv.Value;
|
||||
IAlgoParamVM pvm = item as IAlgoParamVM ?? (item as AlgoParamRowVM)?.param;
|
||||
if (pvm != null) {
|
||||
if (pvm is IAlgoParamVM<double>) return DoubleTemplate;
|
||||
if (pvm is IAlgoParamVM<int>) return IntTemplate;
|
||||
if (pvm is IAlgoParamVM<int?>) return NullableIntTemplate;
|
||||
if (pvm is IAlgoParamVM<uint>) return UIntTemplate;
|
||||
if (pvm is IAlgoParamVM<uint?>) return NullableUIntTemplate;
|
||||
if (pvm is IAlgoParamVM<bool>) return NullableBoolTemplate;
|
||||
if (pvm is IAlgoParamVM<bool?>) return BoolTemplate;
|
||||
if (pvm is IAlgoParamVM<string>) return StringTemplate;
|
||||
if (pvm is IAlgoParamVM<IAlgoVM>) return OrderDefTemplate;
|
||||
if (pvm is IAlgoParamVM<QuantityMeasureImmutable?>) return QuantityMeasureTemplate;
|
||||
if (pvm is IAlgoParamVM<ExecutionStrategy>) return ExecutionStrategyTemplate;
|
||||
if (pvm is IAlgoParamVM<Google.Protobuf.WellKnownTypes.Timestamp>) return TimestampTemplate;
|
||||
var targ = ReflectionHelper.GetGenericTypeArgs(pvm.GetType(), generic_algo_param_itype).FirstOrDefault();
|
||||
if (targ != null) {
|
||||
if (targ.IsEnum)
|
||||
return EnumTemplate;
|
||||
else if (targ.IsNullable()) {
|
||||
targ = targ.GetGenericArguments()[0];
|
||||
if (targ.IsEnum)
|
||||
return NullableEnumTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
-181
@@ -1,181 +0,0 @@
|
||||
#define ENTER_BEHAVES_LIKE_TAB
|
||||
using bts;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.extensions;
|
||||
using mbts.ui.spark.algo;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
partial class AlgoEditorGridView {
|
||||
static class DataGridEditHelper {
|
||||
public static IDisposable set_hooks(AlgoEditorGridView algo_editor) {
|
||||
var grid = algo_editor._param_grid;
|
||||
grid.PreviewKeyDown += on_preview_key_down;
|
||||
grid.KeyDown += on_key_down;
|
||||
grid.SelectionChanged += on_selection_changed;
|
||||
grid.PreparingCellForEdit += on_preparing_cell_for_edit;
|
||||
grid.CellEditEnding += on_cell_edit_ending;
|
||||
grid.GotFocus += on_got_focus;
|
||||
algo_editor.CommitEditRequested += on_commit_edit_requested;
|
||||
algo_editor.CancelEditRequested += on_cancel_edit_requested;
|
||||
return Disposable.Create(() => {
|
||||
grid.PreviewKeyDown += on_preview_key_down;
|
||||
grid.KeyDown -= on_key_down;
|
||||
grid.SelectionChanged -= on_selection_changed;
|
||||
grid.PreparingCellForEdit -= on_preparing_cell_for_edit;
|
||||
grid.CellEditEnding -= on_cell_edit_ending;
|
||||
grid.GotFocus -= on_got_focus;
|
||||
algo_editor.CommitEditRequested -= on_commit_edit_requested;
|
||||
algo_editor.CancelEditRequested -= on_cancel_edit_requested;
|
||||
});
|
||||
/// Handlers
|
||||
// hooked up to PreviewKeyDown, instead of KeyDown, so the cells can't override this behavior
|
||||
void on_preview_key_down(object sender, KeyEventArgs e) {
|
||||
switch (e.Key) {
|
||||
case Key.Escape: e.Handled = cancel_edit(); break;
|
||||
case Key.Enter:
|
||||
if (grid.TryGetCurrentRowView(out var row_view)) {
|
||||
if (row_view.IsEditing) {
|
||||
commit_edit();
|
||||
#if ENTER_BEHAVES_LIKE_TAB
|
||||
NavigationHelper.keyboard_move_focus(FocusNavigationDirection.Down);
|
||||
#endif
|
||||
} else grid.BeginEdit();
|
||||
}
|
||||
e.Handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// hooked up to KeyDown, instead of PreviewKeyDown, so cells can implement custom Key/CommandBindings, or other key-based behavior without the DataGrid handling it in the PreviewKeyDown event.
|
||||
// for ex: TickUpDown cells can do things like TickUp/Down when Ctrl+Up/Down arrow-key is pressed.
|
||||
void on_key_down(object sender, KeyEventArgs e) {
|
||||
switch (e.Key) {
|
||||
case Key.Up: NavigationHelper.keyboard_move_focus(FocusNavigationDirection.Up); break;
|
||||
case Key.Down: NavigationHelper.keyboard_move_focus(FocusNavigationDirection.Down); break;
|
||||
default: return;
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
void on_selection_changed(object sender, SelectionChangedEventArgs e) {
|
||||
_commit(e.RemovedItems?.Cast<object>()?.SafeSingleOrDefault());
|
||||
_commit(e.AddedItems?.Cast<object>()?.SafeSingleOrDefault());
|
||||
void _commit(object row_item) {
|
||||
if (grid.TryGetRowView(row_item, out var row_view) && row_view.IsEditing)
|
||||
grid.CommitEdit();
|
||||
}
|
||||
}
|
||||
|
||||
void on_preparing_cell_for_edit(object sender, DataGridPreparingCellForEditEventArgs e) {
|
||||
if (e.EditingElement is ContentPresenter presenter) {
|
||||
OneOf.Match(get<TextBox>, get<ComboBox>, get<QuantityMeasureControl>, get<ToggleButton>, text_box, combo_box, quantity_measure_control, toggle_button);
|
||||
T get<T>() => presenter.descendants_of_type<T>().FirstOrDefault();
|
||||
// TextBox
|
||||
void text_box(TextBox tb) => Keyboard.Focus(tb);
|
||||
// ComboBox
|
||||
void combo_box(ComboBox cb) { }
|
||||
// QuantityMeasureControl
|
||||
async void quantity_measure_control(QuantityMeasureControl qmc) {
|
||||
if (!focus_text()) {
|
||||
await qmc.LoadedAsync();
|
||||
focus_text();
|
||||
}
|
||||
bool focus_text() =>
|
||||
qmc.try_find_descendent_of_type(out TextBox tb) && Keyboard.Focus(tb) == tb;
|
||||
}
|
||||
// ToggleButton
|
||||
void toggle_button(ToggleButton tb) {
|
||||
if (Mouse.LeftButton == MouseButtonState.Pressed && !tb.IsLoaded) {
|
||||
// this is a hack to toggle IsChecked when the user clicks a checkbox cell that is not already in edit mode.
|
||||
IDisposable dispose = null;
|
||||
dispose = Disposable.Create(
|
||||
tb.SubscribePreviewMouseUp(on_mouse_up),
|
||||
tb.SubscribeMouseLeave(on_mouse_leave));
|
||||
void on_mouse_up(object s, MouseButtonEventArgs mbe) {
|
||||
if (tb.IsMouseOver)
|
||||
tb.SetCurrentValue(ToggleButton.IsCheckedProperty, !tb.IsChecked);
|
||||
Disposable.dispose(ref dispose);
|
||||
}
|
||||
void on_mouse_leave(object s, MouseEventArgs me) =>
|
||||
Disposable.dispose(ref dispose);
|
||||
}
|
||||
}
|
||||
}
|
||||
e.EditingElement.Focus();
|
||||
}
|
||||
|
||||
void on_cell_edit_ending(object sender, DataGridCellEditEndingEventArgs e) {
|
||||
if (e.EditingElement is ContentPresenter presenter) {
|
||||
switch (e.EditAction) {
|
||||
case DataGridEditAction.Cancel: OneOf.Match(get<TextBox>, get<ComboBox>, get<ToggleButton>, cancel_text_box, cancel_combo_box, cancel_toggle_button); break;
|
||||
case DataGridEditAction.Commit: OneOf.Match(get<TextBox>, get<ComboBox>, get<ToggleButton>, commit_text_box, commit_combo_box, commit_toggle_button); break;
|
||||
}
|
||||
T get<T>() => presenter.descendants_of_type<T>().FirstOrDefault();
|
||||
// TextBox
|
||||
void commit_text_box(TextBox tb) => tb.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
|
||||
void cancel_text_box(TextBox tb) => tb.GetBindingExpression(TextBox.TextProperty)?.UpdateTarget();
|
||||
// ComboBox
|
||||
void commit_combo_box(ComboBox cb) { }
|
||||
void cancel_combo_box(ComboBox cb) { }
|
||||
// ToggleButton
|
||||
void commit_toggle_button(ToggleButton tb) { }
|
||||
void cancel_toggle_button(ToggleButton tb) { }
|
||||
}
|
||||
}
|
||||
|
||||
void on_got_focus(object sender, RoutedEventArgs e) {
|
||||
if (e.OriginalSource is DataGridCell cell && cell.try_find_ancestor_of_type(out DataGridRow row)) {
|
||||
if (row.IsEditing) {
|
||||
if (!cell.IsEditing) {
|
||||
grid.CancelEdit(DataGridEditingUnit.Row);
|
||||
if (try_set_current())
|
||||
grid.BeginEdit(e);
|
||||
}
|
||||
} else {
|
||||
try_set_current();
|
||||
if (!row.IsEditing)
|
||||
grid.BeginEdit(e);
|
||||
}
|
||||
bool try_set_current() {
|
||||
grid.SetCurrentValue(DataGrid.CurrentItemProperty, row.Item);
|
||||
if (grid.TryGetCurrentRowView(out var row_view)) {
|
||||
row_view.SetCurrentValue(DataGridRow.IsSelectedProperty, true);
|
||||
grid.SetCurrentValue(DataGrid.CurrentCellProperty, new DataGridCellInfo(row.Item, grid.Columns[1]));
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on_commit_edit_requested() {
|
||||
if (grid.TryGetCurrentRowView(out var row_view) && row_view.IsEditing)
|
||||
commit_edit();
|
||||
}
|
||||
|
||||
void commit_edit() {
|
||||
using (grid.SuspendGotFocus(on_got_focus))
|
||||
grid.CommitEdit(DataGridEditingUnit.Row, true);
|
||||
}
|
||||
|
||||
void on_cancel_edit_requested() =>
|
||||
cancel_edit();
|
||||
|
||||
bool cancel_edit() {
|
||||
if (grid.CurrentCell != null && grid.TryGetCurrentRowView(out var row_view) && row_view.IsEditing) {
|
||||
using (grid.SuspendGotFocus(on_got_focus))
|
||||
grid.CancelEdit(DataGridEditingUnit.Row);
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="mbts.ui.spark.AlgoEditorView"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uag="clr-namespace:mbts.ui.spark.algo"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/Order/AlgoPickerNodeView.xaml"/>
|
||||
<ResourceDictionary Source="/view/Controls/Order/AlgoContentTemplateSelector.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!--Primitive Constants-->
|
||||
<sys:Double x:Key="HorizontalPush1">4</sys:Double>
|
||||
<sys:Double x:Key="HorizontalPush2">8</sys:Double>
|
||||
<sys:Double x:Key="HorizontalPush3">12</sys:Double>
|
||||
<sys:Double x:Key="VerticalPush1">4</sys:Double>
|
||||
<sys:Double x:Key="VerticalPush2">8</sys:Double>
|
||||
<sys:Double x:Key="VerticalPush3">12</sys:Double>
|
||||
<Thickness x:Key="Margin.LP1" Left="{StaticResource HorizontalPush1}"/>
|
||||
<Thickness x:Key="Margin.LP2" Left="{StaticResource HorizontalPush2}"/>
|
||||
<Thickness x:Key="Margin.LP3" Left="{StaticResource HorizontalPush3}"/>
|
||||
<Thickness x:Key="Margin.TP1" Top="{StaticResource VerticalPush1}"/>
|
||||
<Thickness x:Key="Margin.TP2" Top="{StaticResource VerticalPush2}"/>
|
||||
<!--Converters-->
|
||||
<uspcv:OrderTypeBrushConverter x:Key="OrderTypeBrushConverter" DefaultBrush="Transparent"/>
|
||||
<!--Default Styles-->
|
||||
<Style x:Key="ContentHostGroupBoxStyle" TargetType="GroupBox">
|
||||
<Setter Property="Padding" Value="8"/>
|
||||
</Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="u:TextBoxBehavior.EnterKeyBehavior" Value="CommitAndRelease"/>
|
||||
<Setter Property="u:TextBoxBehavior.ReleaseOnEscape" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="uag:AlgoPickerNodeView" BasedOn="{StaticResource {x:Type uag:AlgoPickerNodeView}}">
|
||||
<Setter Property="DropDownStyleKey" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorView}, Converter={x:Static usp:AlgoContentTemplateSelector.AlgoDropDownStyleKeyConverter}}"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<GroupBox
|
||||
x:Name="content_host"
|
||||
Grid.Row="2"
|
||||
Background="Transparent"
|
||||
BorderBrush="{Binding algo_root, Converter={StaticResource OrderTypeBrushConverter}}"
|
||||
Margin="0,6,0,0"
|
||||
Padding="12,8"
|
||||
>
|
||||
<GroupBox.Style>
|
||||
<Style TargetType="GroupBox" BasedOn="{StaticResource ContentHostGroupBoxStyle}">
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="BorderBrush" Value="Transparent">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
</Trigger>
|
||||
<Trigger Property="usp:AlgoEditorView.IsMouseOverNodeContent" Value="True">
|
||||
<Setter Property="BorderThickness" Value="2"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</GroupBox.Style>
|
||||
<GroupBox.Header>
|
||||
<ComboBox
|
||||
ToolTipService.ShowOnDisabled="True"
|
||||
Width="140"
|
||||
Margin="{StaticResource Margin.LP1}"
|
||||
ItemsSource="{Binding algos_view}"
|
||||
SelectedItem="{Binding algo_root}"
|
||||
>
|
||||
<ComboBox.Style>
|
||||
<Style TargetType="ComboBox">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding templates.Count, Converter={StaticResource IntIsZeroConverter}, FallbackValue={u:False}}" Value="{u:True}">
|
||||
<Setter Property="IsEnabled" Value="True"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding templates.Count, Converter={StaticResource IntIsZeroConverter}, FallbackValue={u:False}}" Value="{u:False}">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
<Setter Property="ToolTip" Value="Cannot change structure while templates are present."/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.Style>
|
||||
<ComboBox.IsEnabled>
|
||||
<Binding Path="editing_permissions">
|
||||
<Binding.Converter>
|
||||
<cv:EnumHasFlagsConverter>
|
||||
<sp:AlgoEditingPermissions>AllowEditStructure</sp:AlgoEditingPermissions>
|
||||
</cv:EnumHasFlagsConverter>
|
||||
</Binding.Converter>
|
||||
</Binding>
|
||||
</ComboBox.IsEnabled>
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<usp:OrderTypeItem DisplayName="{Binding display_name}" BadgeFill="{Binding meta.Color, Converter={StaticResource ColorToBrushConverter}}"/>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</GroupBox.Header>
|
||||
<ContentControl
|
||||
Grid.IsSharedSizeScope="True"
|
||||
Content="{Binding algo_root, Converter={x:Static usp:AlgoContentTemplateSelector.DataContextConverter}}"
|
||||
ContentTemplateSelector="{StaticResource AlgoTemplateSelector}"
|
||||
Margin="{StaticResource Margin.TP1}"
|
||||
Visibility="{Binding algo_root.can_expand, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
IsTabStop="False"
|
||||
>
|
||||
<ContentControl.Resources>
|
||||
<Style TargetType="GroupBox" BasedOn="{StaticResource ContentHostGroupBoxStyle}"/>
|
||||
</ContentControl.Resources>
|
||||
</ContentControl>
|
||||
</GroupBox>
|
||||
</UserControl>
|
||||
@@ -1,28 +0,0 @@
|
||||
using mbts.ui.extensions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
partial class AlgoEditorView : UserControl {
|
||||
public AlgoEditorView() {
|
||||
InitializeComponent();
|
||||
content_host.PreviewMouseMove += content_host_PreviewMouseMove;
|
||||
content_host.MouseEnter += content_host_MouseEnter;
|
||||
content_host.MouseLeave += content_host_MouseLeave;
|
||||
// local methods
|
||||
void content_host_PreviewMouseMove(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
|
||||
void content_host_MouseEnter(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
|
||||
void content_host_MouseLeave(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
|
||||
void refresh_is_mouse_over_content_host(MouseEventArgs e) => content_host.set_current_if_changed(IsMouseOverNodeContentProperty, this.ChildAtPoint<GroupBox>(e.GetPosition(this)) == content_host);
|
||||
}
|
||||
|
||||
#region IsMouseOverNodeContent
|
||||
public static readonly DependencyProperty IsMouseOverNodeContentProperty =
|
||||
DependencyProperty.RegisterAttached("IsMouseOverNodeContent", typeof(bool), typeof(AlgoEditorView), new PropertyMetadata(false));
|
||||
|
||||
public static bool GetIsMouseOverNodeContent(DependencyObject d) => (bool)d.GetValue(IsMouseOverNodeContentProperty);
|
||||
public static void SetIsMouseOverNodeContent(DependencyObject d, bool value) => d.SetValue(IsMouseOverNodeContentProperty, value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
using mbts.collections;
|
||||
@@ -17,10 +16,8 @@ using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Windows.Data;
|
||||
@@ -34,7 +31,7 @@ using OrderType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
|
||||
namespace mbts.spark {
|
||||
class FillsVM : DisposableViewModelBase, IHandleGridSorting {
|
||||
readonly OrdersModel _model;
|
||||
readonly OperableCollection<Row, FillTuple, string> _rows;
|
||||
readonly RXOperableCollection<Row, FillTuple, string> _rows;
|
||||
readonly DIYGridSortManager<Columns> _sort_manager;
|
||||
readonly IDIYGridFiltersManager<FillTuple, Columns, FilterStates> _filters_manager;
|
||||
readonly ObservableKeyedCollection<ColumnBase, Columns> _columns = new ObservableKeyedCollection<ColumnBase, Columns>(_col_key);
|
||||
@@ -45,7 +42,7 @@ namespace mbts.spark {
|
||||
public FillsVM(IBus bus, OrdersModel model, IObservable<StartupData> state_obs, IWindowSettingsManager<GridSettings> settings_manager)
|
||||
: this(bus, model, state_obs, _get_observable(bus, model, state_obs), settings_manager) { }
|
||||
|
||||
public FillsVM(IBus bus, OrdersModel model, IObservable<StartupData> state_obs, IObservable<TableUpdate<FillTuple, string>> fills_source, IWindowSettingsManager<GridSettings> settings_manager) {
|
||||
FillsVM(IBus bus, OrdersModel model, IObservable<StartupData> state_obs, IConnectableObservable<TableUpdate<FillTuple, string>> fills_source, IWindowSettingsManager<GridSettings> settings_manager) {
|
||||
_model = model;
|
||||
_settings_manager = settings_manager;
|
||||
/// Columns
|
||||
@@ -87,7 +84,7 @@ namespace mbts.spark {
|
||||
add_col(Columns.OrderKey, DefaultColumnHeaders.OrderKey, r => r.order_key.ToString(), new RegexFilterVM<FillTuple>(r => r.fill.OrderKey.ToString()), DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.OrderKey);
|
||||
add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), new RegexFilterVM<FillTuple>(r => r.fill.SecurityKey.ToString()), DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey);
|
||||
/// Rows
|
||||
_rows = new OperableCollection<Row, FillTuple, string>(
|
||||
_rows = new RXOperableCollection<Row, FillTuple, string>(
|
||||
(ft, fk) => new Row(ft, model),
|
||||
ft => ft.fill.FillKey,
|
||||
new ComparerBuilder<FillTuple>(new[] {
|
||||
@@ -101,17 +98,16 @@ namespace mbts.spark {
|
||||
// grid settings
|
||||
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, new Dictionary<string, GridColumnSettingsFeatureFlags> { }, rows);
|
||||
// this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.)
|
||||
if (_settings_manager != null)
|
||||
grid_settings_manager.load_from(_settings_manager.current_settings());
|
||||
grid_settings_manager.load_from(settings_manager.current_settings());
|
||||
//
|
||||
_dispose = new CompositeDisposable(
|
||||
_dispose = Disposable.Create(
|
||||
ObservableAutoWrapper.connect(col_tups, columns, ct => ct.col),
|
||||
fills_source.OnDispatcher().Subscribe(t => {
|
||||
_rows.update(t);
|
||||
Set(ref _total_row_count, t.state.Count, nameof(total_row_count));
|
||||
}),
|
||||
state_obs.OnDispatcher().Subscribe(s => {
|
||||
if (s.grid != null && _settings_manager != null) {
|
||||
if (s.grid != null) {
|
||||
// this means we are not using defaults,
|
||||
// Note: settings_manager in turn notifies grid_settings_manager of this update
|
||||
_settings_manager.instance_settings = s.grid;
|
||||
@@ -124,8 +120,8 @@ namespace mbts.spark {
|
||||
};
|
||||
rows_manager.on_settings(fs, s.sorting_criterion);
|
||||
}),
|
||||
settings_manager == null ? Disposable.empty : settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
|
||||
(fills_source as IConnectableObservable<TableUpdate<FillTuple, string>>)?.Connect() ?? Disposable.empty
|
||||
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
|
||||
fills_source.Connect()
|
||||
);
|
||||
// local methods
|
||||
void add_col<TFilter>(Columns col_key, string header, Func<Row, string> get_text, IDataGridColumnPersistentFilter<FillTuple, TFilter> filter, string filter_template, double width) =>
|
||||
@@ -157,12 +153,12 @@ namespace mbts.spark {
|
||||
public IEnumerable<SortingCriteria> get_sorting_criterion() =>
|
||||
_sort_manager.create();
|
||||
|
||||
public void on_sorting(object sender, DIYGridSortingEventArgs e) =>
|
||||
_sort_manager.on_sorting(e);
|
||||
|
||||
static Columns _col_key(ColumnBase column) =>
|
||||
((IKeyedColumn<Columns>)column).key;
|
||||
|
||||
void IHandleGridSorting.on_sorting(object sender, DIYGridSortingEventArgs e) =>
|
||||
_sort_manager.on_sorting(e);
|
||||
|
||||
public FilterStates get_filters() =>
|
||||
_filters_manager.create();
|
||||
|
||||
@@ -233,7 +229,7 @@ namespace mbts.spark {
|
||||
public OrderType order_type => _order.order_type();
|
||||
public string display_name => OrderUtils.display_name(_order, _fill_tuple.algo);
|
||||
public OrderMetadata order_metadata => _order_metadata ?? (_order_metadata = _model.factory.get_metadata(_order.Def.CustomDef.CustomCase));
|
||||
public Brush badge_fill => _badge_fill ?? (_badge_fill = OrderUtils.badge_fill(_fill_tuple.algo_meta, order_metadata));
|
||||
public Brush badge_fill => _badge_fill ?? (_badge_fill = OrderUtils.badge_fill(_order, _fill_tuple.algo_meta, order_metadata));
|
||||
public string account { get; private set; }
|
||||
public string user => _order.user();
|
||||
public Venue exchange => security.Exchange;
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace mbts.spark {
|
||||
/// <summary>
|
||||
/// This class provides a stable DataContext for a fills view, with an internal <see cref="FillsVM"/> that may be disposed and recreated as needed.
|
||||
/// </summary>
|
||||
class FillsVMContainer : IHandleGridSorting, IDisposable, IWillLetYouKnowWhenIDispose {
|
||||
bool _disposed;
|
||||
FillsVM _value;
|
||||
IDisposable _columns_duplex;
|
||||
|
||||
public event Action<bool> OnDisposed;
|
||||
|
||||
public FillsVM value {
|
||||
get => _value;
|
||||
set {
|
||||
if (value != _value) {
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(FillsVMContainer));
|
||||
Disposable.dispose(ref _columns_duplex);
|
||||
if (_value != null) {
|
||||
columns.Clear();
|
||||
rows.clear_collections();
|
||||
}
|
||||
Disposable.dispose(ref _value);
|
||||
_value = value;
|
||||
if (_value != null) {
|
||||
_columns_duplex = ObservableAutoWrapper.connect_two_way(_value.columns, columns);
|
||||
rows.add_collection(_value.rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableAggregateCollection<FillsVM.Row> rows { get; } = new ObservableAggregateCollection<FillsVM.Row>();
|
||||
public ObservableCollection<DIYGrid.ColumnBase> columns { get; } = new ObservableCollection<DIYGrid.ColumnBase>();
|
||||
|
||||
public void Dispose() {
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(FillsVMContainer));
|
||||
value = null;
|
||||
OnDisposed?.Invoke(true);
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void on_sorting(object sender, DIYGridSortingEventArgs e) {
|
||||
if (!_disposed)
|
||||
_value?.on_sorting(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ using StartupData = bts.shell.FillsWindowFactory.StartupData;
|
||||
namespace mbts.spark {
|
||||
class FillsWindow : SparkWindow<StartupData>, ISharedWindow {
|
||||
public const string window_type_name = "Fills";
|
||||
const string _thread_id = "Fills";
|
||||
readonly AllowOnce _init_guard = new AllowOnce("Fills");
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
@@ -28,11 +29,10 @@ namespace mbts.spark {
|
||||
IDisposable _subscriptions;
|
||||
|
||||
public FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state, WindowSettingsManager<GridSettings> settings_manager)
|
||||
: base("Fills", bus, factory, SparkCommands.ShowFills().icon) {
|
||||
: base("Fills", bus, factory, SparkCommands.ShowFills.icon) {
|
||||
_orders_model = orders_model;
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().fills ? "Fills" : null;
|
||||
}
|
||||
|
||||
event Action<FillsWindowFactory.StartupData> _state_changed;
|
||||
@@ -48,7 +48,7 @@ namespace mbts.spark {
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
|
||||
public string thread_id { get; }
|
||||
public string thread_id => _thread_id;
|
||||
|
||||
public void thread_init() {
|
||||
_init_guard.check_access();
|
||||
|
||||
@@ -20,27 +20,6 @@ namespace bts.shell {
|
||||
readonly IBus _bus;
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
/// Fills 'sorting-criterion' settings were compromised a while back (mid-late 2018).
|
||||
/// The issue was that, when a column was sorted, its 'sort member path' was assigned to the SortingCriteria.col_name,
|
||||
/// rather than the <see cref="Columns"/> enum/id string.
|
||||
/// This dictionary serves as a reverse-lookup so the sort member path can be converted to the enum/id string.
|
||||
static readonly IReadOnlyDictionary<string, Columns> _sort_member_to_id =
|
||||
new Dictionary<string, Columns> {
|
||||
{ nameof(FillsVM.Row.account), Columns.Account },
|
||||
{ nameof(FillsVM.Row.exchange), Columns.Exchange },
|
||||
{ nameof(FillsVM.Row.fill_key), Columns.FillKey },
|
||||
{ nameof(FillsVM.Row.fill_kind), Columns.FillKind },
|
||||
{ nameof(FillsVM.Row.fill_time), Columns.FillTime },
|
||||
{ nameof(FillsVM.Row.order_key), Columns.OrderKey },
|
||||
{ nameof(FillsVM.Row.order_type), Columns.OrderType },
|
||||
{ nameof(FillsVM.Row.price), Columns.Price },
|
||||
{ nameof(FillsVM.Row.security), Columns.Security },
|
||||
{ nameof(FillsVM.Row.security_key), Columns.SecurityKey },
|
||||
{ nameof(FillsVM.Row.side), Columns.Side },
|
||||
{ nameof(FillsVM.Row.size), Columns.Size },
|
||||
{ nameof(FillsVM.Row.time_in_force), Columns.TimeInForce },
|
||||
{ nameof(FillsVM.Row.user), Columns.User }
|
||||
};
|
||||
|
||||
public FillsWindowFactory(TradingShellModule module, IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model)
|
||||
: base(module, "Fills") {
|
||||
@@ -59,7 +38,6 @@ namespace bts.shell {
|
||||
_bus.post(SparkTags.ShowWindow, await load(state));
|
||||
|
||||
IShellWindow _create(StartupData state) {
|
||||
GridSettingsHelper.normalize_sorting_criterion(state.sorting_criterion, _sort_member_to_id);
|
||||
var settings_manager = new WindowSettingsManager<GridSettings>(_settings_hub,
|
||||
state.grid,
|
||||
get: us => us.fills_grid,
|
||||
@@ -91,7 +69,6 @@ namespace bts.shell {
|
||||
}
|
||||
|
||||
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
|
||||
public static GridSettings MiniGridSettings => GridSettings.Default.with(DefaultColumns.Mini.all, DefaultColumns.Mini.sorting_criterion);
|
||||
public IReadOnlyList<SortingCriteria> sorting_criterion => _sorting_criteria ?? (_sorting_criteria = DefaultColumns.default_sorting_criterion.ToList());
|
||||
public static StartupData Default() =>
|
||||
new StartupData(null, null, new FilterStates(), null);
|
||||
@@ -105,7 +82,7 @@ namespace bts.shell {
|
||||
public static GridColumnSettings time_in_force => new GridColumnSettings(col_id(Columns.TimeInForce), DefaultColumnHeaders.TimeInForce, width: DefaultColumnWidths.TimeInForce);
|
||||
public static GridColumnSettings order_type => new GridColumnSettings(col_id(Columns.OrderType), DefaultColumnHeaders.OrderType, width: DefaultColumnWidths.OrderType);
|
||||
public static GridColumnSettings side => new GridColumnSettings(col_id(Columns.Side), DefaultColumnHeaders.Side, width: DefaultColumnWidths.Side);
|
||||
public static GridColumnSettings quantity => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings quantity => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Side, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings fill_kind => new GridColumnSettings(col_id(Columns.FillKind), DefaultColumnHeaders.FillKind, width: DefaultColumnWidths.FillKind);
|
||||
public static GridColumnSettings fill_key => new GridColumnSettings(col_id(Columns.FillKey), DefaultColumnHeaders.FillKey, width: DefaultColumnWidths.FillKey);
|
||||
@@ -116,7 +93,7 @@ namespace bts.shell {
|
||||
yield return new SortingCriteria(fill_time.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<GridColumnSettings> all {
|
||||
get {
|
||||
yield return security;
|
||||
@@ -135,34 +112,8 @@ namespace bts.shell {
|
||||
yield return security_key;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Mini {
|
||||
static readonly IReadOnlyDictionary<Columns, (bool is_visible, double? width_override)> _mini_cols_data =
|
||||
new Dictionary<Columns, (bool is_visible, double? width_override)> {
|
||||
{ Columns.Security, (true, null) },
|
||||
{ Columns.FillTime, (true, ShortColumnWidths.Time) },
|
||||
{ Columns.OrderType, (true, ShortColumnWidths.OrderType) },
|
||||
{ Columns.Side, (true, ShortColumnWidths.Side) },
|
||||
{ Columns.Size, (true, ShortColumnWidths.Size) },
|
||||
{ Columns.Price, (true, ShortColumnWidths.Price) },
|
||||
};
|
||||
|
||||
public static IEnumerable<GridColumnSettings> all => DefaultColumns.all.Select(_convert);
|
||||
public static IEnumerable<SortingCriteria> sorting_criterion => default_sorting_criterion.Where(c => _try_get_data(c.col_name, out (bool is_visible, double? _) d) && d.is_visible);
|
||||
|
||||
static bool _try_get_data(string id_str, out (bool is_visible, double? width_override) data) {
|
||||
if (Enum.TryParse(id_str, out Columns k) && _mini_cols_data.TryGetValue(k, out data))
|
||||
return true;
|
||||
data = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
static GridColumnSettings _convert(GridColumnSettings default_col) =>
|
||||
_try_get_data(default_col.id, out (bool is_visible, double? width_override) d) ?
|
||||
default_col.with(is_visible: d.is_visible, width: d.width_override) :
|
||||
default_col.with(is_visible: false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
using mbts.ui;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
static class LadderViewHelper {
|
||||
public static readonly DragDeltaEventHandler ColumnHeaderThumb_DragDeltaHandler =
|
||||
(s, e) =>
|
||||
e.Handled =
|
||||
s is Thumb thumb &&
|
||||
thumb.try_find_ancestor_of_type(out Canvas canvas) &&
|
||||
canvas.ActualWidth + e.HorizontalChange < canvas.MinWidth;
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="mbts.ui.spark.MiniLadderView"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
Background="{x:Static u:BTSColors.common_background_brush}"
|
||||
TextElement.FontSize="{x:Static u:FontSizes.Small}"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<Style TargetType="GridViewColumnHeader">
|
||||
<Setter Property="Background" Value="{u:BTSColor header}"/>
|
||||
<Setter Property="Foreground" Value="{u:BTSColor header_text}"/>
|
||||
<Setter Property="BorderBrush" Value="{u:BTSColor header_border}"/>
|
||||
<Setter Property="BorderThickness" Value="0,0,1,0"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="Height" Value="23" />
|
||||
<Setter Property="MinWidth" Value="30" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
|
||||
<Grid SnapsToDevicePixels="True">
|
||||
<Border x:Name="HeaderBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition MaxHeight="7"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Padding="{TemplateBinding Padding}" Grid.RowSpan="2">
|
||||
<ContentPresenter x:Name="HeaderContent"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentStringFormat="{TemplateBinding ContentStringFormat}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
Margin="0,0,0,1"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Canvas MinWidth="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=MinWidth, Mode=OneTime}">
|
||||
<Thumb x:Name="PART_HeaderGripper" DragDelta="{x:Static usp:LadderViewHelper.ColumnHeaderThumb_DragDeltaHandler}">
|
||||
<Thumb.Style>
|
||||
<Style TargetType="{x:Type Thumb}">
|
||||
<Setter Property="Canvas.Right" Value="-9"/>
|
||||
<Setter Property="Width" Value="18"/>
|
||||
<Setter Property="Height" Value="{Binding ActualHeight, RelativeSource={RelativeSource TemplatedParent}}"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||
<Border Background="Transparent" Padding="{TemplateBinding Padding}">
|
||||
<Rectangle Fill="{TemplateBinding Background}" HorizontalAlignment="Center" Width="2"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{u:BTSColor header_text}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Thumb.Style>
|
||||
</Thumb>
|
||||
</Canvas>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<AdornerDecorator>
|
||||
<sp:LadderGridHost
|
||||
x:Name="ladder_grid_host"
|
||||
Command="{Binding send_order_command}"
|
||||
Orders="{Binding orders}"
|
||||
orders_column_width="{Binding Settings.orders_column.width, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
|
||||
bid_column_width="{Binding Settings.orders_column.width, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
|
||||
price_column_width="{Binding Settings.price_column.width, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
|
||||
ask_column_width="{Binding Settings.ask_column.width, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
|
||||
LastColumnWidth="{Binding Settings.last_column.width, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
|
||||
VolumeColumnWidth="{Binding Settings.volume_column.general.width, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
|
||||
CanUserSendOrders="{Binding CanUserSendOrders, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
|
||||
IsTabStop="False"
|
||||
>
|
||||
<sp:LadderGridHost.Resources>
|
||||
<Style TargetType="c:Badge">
|
||||
<Setter Property="Background" Value="{Binding background}"/>
|
||||
<Setter Property="BorderBrush" Value="{Binding border}"/>
|
||||
<Setter Property="FontFamily" Value="{Binding font_family}"/>
|
||||
<Setter Property="FontSize" Value="{Binding font_size}"/>
|
||||
<Setter Property="Foreground" Value="{Binding foreground}"/>
|
||||
<Setter Property="TextLines" Value="{Binding text_lines}"/>
|
||||
<Setter Property="Width" Value="{x:Static c:Badge.DefaultWidth}"/>
|
||||
<Setter Property="HasMultipleOrdersAtLevel" Value="{Binding has_multiple_orders_at_same_level}"/>
|
||||
<Setter Property="IsHedgeOrder" Value="{Binding is_hedge_order}"/>
|
||||
<Setter Property="AlgoType" Value="{Binding algo_type}"/>
|
||||
<Setter Property="Abbreviation" Value="{Binding algo_studio_abbreviation}" />
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{Binding hoverbackground}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</sp:LadderGridHost.Resources>
|
||||
</sp:LadderGridHost>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
@@ -1,29 +0,0 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
partial class MiniLadderView : UserControl {
|
||||
public MiniLadderView() =>
|
||||
InitializeComponent();
|
||||
|
||||
#region Settings
|
||||
public static readonly DependencyProperty SettingsProperty =
|
||||
WPFHelper.create_dp<mbts.spark.LadderSettings, MiniLadderView>(nameof(Settings));
|
||||
|
||||
public mbts.spark.LadderSettings Settings {
|
||||
get => GetValue(SettingsProperty) as mbts.spark.LadderSettings;
|
||||
set => SetValue(SettingsProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CanUserSendOrders
|
||||
public static readonly DependencyProperty CanUserSendOrdersProperty =
|
||||
WPFHelper.create_dp<bool, MiniLadderView>(nameof(CanUserSendOrders));
|
||||
|
||||
public bool CanUserSendOrders {
|
||||
get => (bool)GetValue(CanUserSendOrdersProperty);
|
||||
set => SetValue(CanUserSendOrdersProperty, value);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+7
-17
@@ -1,5 +1,4 @@
|
||||
using bts.api;
|
||||
using bts.md;
|
||||
using mbts;
|
||||
using mbts.config;
|
||||
using mbts.persist;
|
||||
@@ -22,26 +21,19 @@ namespace bts.shell {
|
||||
readonly LadderConfig _ladder_conf;
|
||||
readonly IBus _bus;
|
||||
readonly IWindowManager _wm;
|
||||
readonly MBOService _mbo;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly MarketDataAuxiliaryModel _md_auxiliary_model;
|
||||
IReadOnlyDictionary<uint, SecurityMD> _securities = new Dictionary<uint, SecurityMD>();
|
||||
|
||||
public LadderWindowFactory(
|
||||
IShellModule owner,
|
||||
IBus bus,
|
||||
IWindowManager wm,
|
||||
UserSettingsHub settings_hub,
|
||||
OrdersModel orders_model,
|
||||
MBOService mbo)
|
||||
|
||||
public LadderWindowFactory(IShellModule owner, IBus bus, IWindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model, MarketDataAuxiliaryModel md_auxiliary_model)
|
||||
: base(owner, "SparkLadder") {
|
||||
_bus = bus;
|
||||
_wm = wm;
|
||||
_settings_hub = settings_hub;
|
||||
_orders_model = orders_model;
|
||||
_ladder_conf = new LadderConfig(Configuration.get<bts.config.LadderKnobs>(), Configuration.get<bts.config.MultiThreadedWindows>().ladder);
|
||||
_mbo = mbo;
|
||||
_md_auxiliary_model = md_auxiliary_model;
|
||||
_ladder_conf = new LadderConfig(Configuration.get<bts.config.LadderKnobs>(), Configuration.get<bts.config.toggles.LaddersOnThreads>().enabled);
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(s => _securities = s),
|
||||
bus.subscribe(SparkTags.ShowLadderTag).OnDispatcher().Subscribe(s => show_windows(new[] { s })),
|
||||
@@ -64,16 +56,15 @@ namespace bts.shell {
|
||||
IShellWindow create_ladder_window(SecurityMD md, LadderSettings state_settings) {
|
||||
var id = Interlocked.Increment(ref _ladder_count) % Environment.ProcessorCount;
|
||||
var view = new Lazy<UIElement>(() => {
|
||||
var model = new LadderModel(md, _orders_model, _mbo);
|
||||
var model = new LadderModel(md, _orders_model, _md_auxiliary_model);
|
||||
var settings_manager = new WindowSettingsManager<LadderSettings>(
|
||||
_settings_hub, state_settings == null || state_settings.use_defaults ? null : state_settings,
|
||||
us => sanitize_ladder_settings(us.ladder),
|
||||
(us, v) => us.with(sanitize_ladder_settings(v)));
|
||||
var init = state_settings ?? settings_manager.default_settings;
|
||||
init.use_defaults |= state_settings == null;
|
||||
var vm = LadderVM.create(_bus, model, settings_manager, init);
|
||||
var vm = new LadderVM(_bus, model, settings_manager, init);
|
||||
vm.order_properties_vm.open_algo_popup_on_select = _ladder_conf.open_algo_popup_on_select;
|
||||
vm.order_properties_vm.use_algo_param_grid = _ladder_conf.use_algo_param_grid;
|
||||
var lv = new LadderView(vm);
|
||||
foreach (var kv in AlgoStudioModelUtils.get_dynamic_resources(vm.model.order_gateway.algo_model.ui_configuration))
|
||||
lv.Resources[kv.Key] = kv.Value;
|
||||
@@ -148,13 +139,12 @@ namespace bts.shell {
|
||||
|
||||
readonly struct LadderConfig {
|
||||
public readonly uint max_open_ladders;
|
||||
public readonly bool ladders_on_threads, open_algo_popup_on_select, use_algo_param_grid;
|
||||
public readonly bool ladders_on_threads, open_algo_popup_on_select;
|
||||
public readonly TimeSpan security_timeout;
|
||||
|
||||
public LadderConfig(bts.config.LadderKnobs knobs, bool ladders_on_threads) {
|
||||
max_open_ladders = knobs.max_open_ladders;
|
||||
open_algo_popup_on_select = knobs.open_algo_popup_on_select;
|
||||
use_algo_param_grid = knobs.use_algo_param_grid;
|
||||
security_timeout = TimeSpan.FromSeconds(Math.Max(15, knobs.security_timeout_secs));
|
||||
this.ladders_on_threads = ladders_on_threads;
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:uc="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
@@ -22,7 +22,6 @@
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
|
||||
<ResourceDictionary Source="/Modules/Trading/OrderBook/View/OrderParamsPanel.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<uspcv:TickDisplayConverter x:Key="TickDisplayConverter"/>
|
||||
<uspcv:OrderSubTypeDisplayTextConverter x:Key="OrderSubTypeLongDisplayTextConverter" UseLongName="True" />
|
||||
@@ -50,56 +49,16 @@
|
||||
|
||||
<Style x:Key="{x:Static sp:MiniOrderBookView.RowStyleKey}" TargetType="ListBoxItem" BasedOn="{StaticResource DIYGridRowStyle}">
|
||||
<Style.Triggers>
|
||||
<!-- Completed order background -->
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
|
||||
<Condition Binding="{Binding status}" Value="{u:Enum Completed, Type=order:OrderStatus}"/>
|
||||
<Condition Binding="{Binding status}" Value="{uc:Enum Working, Type=order:OrderStatus}"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.Setters>
|
||||
<Setter Property="Background" Value="{x:Static Member=completed_order_background_brush, MemberType={u:Nested u:BTSColors.Orders}}"/>
|
||||
</MultiDataTrigger.Setters>
|
||||
</MultiDataTrigger>
|
||||
<!-- Error, RejectedBySpark, Disconnected order background -->
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
|
||||
<Condition Binding="{Binding status}" Value="{u:Enum Error, Type=order:OrderStatus}"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.Setters>
|
||||
<Setter Property="Background" Value="{x:Static Member=error_order_background_brush, MemberType={u:Nested u:BTSColors.Orders}}"/>
|
||||
</MultiDataTrigger.Setters>
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
|
||||
<Condition Binding="{Binding status}" Value="{u:Enum RejectedBySpark, Type=order:OrderStatus}"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.Setters>
|
||||
<Setter Property="Background" Value="{x:Static Member=error_order_background_brush, MemberType={u:Nested u:BTSColors.Orders}}"/>
|
||||
</MultiDataTrigger.Setters>
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
|
||||
<Condition Binding="{Binding status}" Value="{u:Enum Disconnected, Type=order:OrderStatus}"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.Setters>
|
||||
<Setter Property="Background" Value="{x:Static Member=error_order_background_brush, MemberType={u:Nested u:BTSColors.Orders}}"/>
|
||||
</MultiDataTrigger.Setters>
|
||||
</MultiDataTrigger>
|
||||
<!-- Cancel order background -->
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
|
||||
<Condition Binding="{Binding status}" Value="{u:Enum Canceled, Type=order:OrderStatus}"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.Setters>
|
||||
<Setter Property="Background" Value="{x:Static Member=canceled_order_background_brush, MemberType={u:Nested u:BTSColors.Orders}}"/>
|
||||
<Setter Property="Background" Value="{x:Static Member=working_order_background_brush, MemberType={uc:Nested uc:BTSColors.Orders}}"/>
|
||||
</MultiDataTrigger.Setters>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
|
||||
</Style>
|
||||
<DataTemplate x:Key="{x:Static sp:MiniOrderBookView.RowTemplateKey}">
|
||||
<sp:MiniOrderBookRowView/>
|
||||
@@ -123,8 +82,7 @@
|
||||
RowTemplate="{StaticResource {x:Static sp:MiniOrderBookView.RowTemplateKey}}"
|
||||
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
|
||||
RowsHorizontalScrollBarVisibility="Collapsed"
|
||||
RowHeight="20"
|
||||
SelectionMode="Single"
|
||||
RowHeight="20"
|
||||
>
|
||||
<c:DIYGrid.Resources>
|
||||
<ResourceDictionary>
|
||||
@@ -137,7 +95,7 @@
|
||||
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
|
||||
<Style x:Key="{x:Static sp:OrderBookGridView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
|
||||
<Style x:Key="{x:Static sp:OrderBookView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Focusable" Value="False"/>
|
||||
<Setter Property="Width" Value="16"/>
|
||||
<Setter Property="Height" Value="16"/>
|
||||
@@ -191,8 +149,9 @@
|
||||
</ResourceDictionary>
|
||||
</c:DIYGrid.Resources>
|
||||
<c:DIYGrid.InputBindings>
|
||||
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
</c:DIYGrid.InputBindings>
|
||||
<!--<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/> -->
|
||||
</c:DIYGrid.InputBindings>
|
||||
<c:DIYGrid.CommandBindings>
|
||||
<CommandBinding
|
||||
Command="{x:Static c:DIYGrid.DefaultRowCommand}"
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using mbts.ui.controls;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal partial class MiniOrderBookView : UserControl {
|
||||
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
|
||||
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
|
||||
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Cancel, EditableQty, EditablePrice, Attach }
|
||||
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Cancel, EditableQty, EditablePrice }
|
||||
public const string
|
||||
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
|
||||
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
|
||||
@@ -21,10 +20,7 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
class MiniOrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookGridView.ColumnKeys> {
|
||||
public MiniOrderBookRowView() : base() {
|
||||
FocusManager.SetIsFocusScope(this, true);
|
||||
KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Cycle);
|
||||
}
|
||||
class MiniOrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookView.Columns> {
|
||||
public MiniOrderBookRowView() : base() { }
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,13 @@
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.shell.orderbook;
|
||||
using bts.utils;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using mbts.ui.utility.text;
|
||||
using mbts.ui.utilities;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -24,15 +20,15 @@ using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using Columns = mbts.spark.OrderBookGridView.ColumnKeys;
|
||||
using static bts.shell.OrderBookWindowFactory;
|
||||
using static mbts.spark.OrderBookView;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
using OrderType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
|
||||
using System.Windows.Input;
|
||||
using System.Reactive.Disposables;
|
||||
|
||||
namespace mbts.spark {
|
||||
// This interface is used to hide ColumnBase.SortDirection, and offer a SortDirection member that does NOT require a DependencyProperty lookup.
|
||||
interface ICVOrderBookColumn : IKeyedColumn<Columns>, IGridColumn {
|
||||
string sort_member_path { get; }
|
||||
ListSortDirection? SortDirection { get; set; }
|
||||
IDataGridColumnFilter<OrderSecurityAlgoMeta> filter { get; }
|
||||
IComparer<OrderBookRowVM> comparer { get; }
|
||||
@@ -65,7 +61,6 @@ namespace mbts.spark {
|
||||
partial class OrderBookVM {
|
||||
public class OrderBookColumnCollection : ObservableCollection<ColumnBase>, IValueFactory<(FilterStates filters, IReadOnlyList<SortingCriteria> sorting)>, IDisposable {
|
||||
readonly List<ICVOrderBookColumn> _active_comparers = new List<ICVOrderBookColumn>();
|
||||
CompositeDisposable _resources = new CompositeDisposable();
|
||||
|
||||
public OrderBookColumnCollection(IBus bus, OrdersModel model, IObservable<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)> order_securities) {
|
||||
// security
|
||||
@@ -83,6 +78,7 @@ namespace mbts.spark {
|
||||
Add(symbol_column = new SimpleTextColumn<string[]>(
|
||||
Columns.Symbol,
|
||||
DefaultColumnHeaders.Symbol,
|
||||
nameof(OrderBookRowVM.symbol),
|
||||
r => r.symbol,
|
||||
DefaultColumnWidths.Symbol,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, string>(
|
||||
@@ -96,6 +92,7 @@ namespace mbts.spark {
|
||||
Add(time_created_column = new SimpleTextColumn<TimeFilterTuple>(
|
||||
Columns.Time,
|
||||
"Time",
|
||||
nameof(OrderBookRowVM.time_created),
|
||||
r => r.time_created_text,
|
||||
DefaultColumnWidths.Time,
|
||||
new TimeFilterVM<OrderSecurityAlgoMeta>(
|
||||
@@ -107,6 +104,7 @@ namespace mbts.spark {
|
||||
Add(last_change_column = new SimpleTextColumn<TimeFilterTuple>(
|
||||
Columns.LastChange,
|
||||
"Last Change",
|
||||
nameof(OrderBookRowVM.last_change),
|
||||
r => r.last_change_text,
|
||||
DefaultColumnWidths.Time,
|
||||
new TimeFilterVM<OrderSecurityAlgoMeta>(
|
||||
@@ -118,6 +116,7 @@ namespace mbts.spark {
|
||||
Add(user_column = new SimpleTextColumn<string[]>(
|
||||
Columns.User,
|
||||
DefaultColumnHeaders.User,
|
||||
nameof(OrderBookRowVM.user),
|
||||
r => r.user,
|
||||
DefaultColumnWidths.User,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, string>(
|
||||
@@ -130,6 +129,7 @@ namespace mbts.spark {
|
||||
Add(account_column = new SimpleTextColumn<string[]>(
|
||||
Columns.Account,
|
||||
DefaultColumnHeaders.Account,
|
||||
nameof(OrderBookRowVM.account),
|
||||
r => r.account,
|
||||
DefaultColumnWidths.Account,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, string, string[]>(
|
||||
@@ -144,6 +144,7 @@ namespace mbts.spark {
|
||||
Add(exchange_column = new SimpleTextColumn<Venue[]>(
|
||||
Columns.Exchange,
|
||||
DefaultColumnHeaders.Exchange,
|
||||
nameof(OrderBookRowVM.exchange),
|
||||
r => r.exchange.ToString(),
|
||||
DefaultColumnWidths.Exchange,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, Venue>(
|
||||
@@ -156,6 +157,7 @@ namespace mbts.spark {
|
||||
Add(time_in_force_column = new SimpleTextColumn<TimeInForce[]>(
|
||||
Columns.TimeInForce,
|
||||
DefaultColumnHeaders.TimeInForce,
|
||||
nameof(OrderBookRowVM.time_in_force),
|
||||
r => r.time_in_force.ToString(),
|
||||
DefaultColumnWidths.TimeInForce,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, TimeInForce>(
|
||||
@@ -164,48 +166,24 @@ namespace mbts.spark {
|
||||
nameof(OrderBookRowVM.time_in_force)),
|
||||
DataGridFilterKnownTemplates.EnumValues,
|
||||
_wrap_comparer(r => r.time_in_force)));
|
||||
// (algo items)
|
||||
var algo_items_source = model.subscribe_all_algos().SelectKeyValue(AlgoDataVM.from_data, d => AlgoID.from_algo_key(d.data.key)).OnDispatcher();
|
||||
var algo_items_comp = AlgoCollectionHelper.algo_data_vm_comparer.to_non_generic();
|
||||
var algo_items_group_desc = new PropertyGroupDescription(nameof(AlgoDataVM.group));
|
||||
// order type
|
||||
Add(order_type_column = new OrderBookOrderTypeColumn(
|
||||
Columns.OrderType,
|
||||
nameof(OrderBookRowVM.badge_fill),
|
||||
nameof(OrderBookRowVM.display_name),
|
||||
_wrap_comparer(r => r.order_type),
|
||||
DiscreteValuesFilterVM.CreateTableUpdate<OrderSecurityAlgoMeta, AlgoDataVM, AlgoID>(
|
||||
o => new AlgoDataVM(o.algo.GetValueOrDefault()),
|
||||
o => AlgoID.from_order_def(o.order.Def),
|
||||
vm => AlgoID.from_algo_key(vm.data.key), out var order_type_filter_observer, out var order_type_items_view)));
|
||||
_resources.Add(algo_items_source.Subscribe(order_type_filter_observer));
|
||||
order_type_items_view.CustomSort = algo_items_comp;
|
||||
order_type_items_view.GroupDescriptions.Add(algo_items_group_desc);
|
||||
// root algo type
|
||||
Add(root_algo_type_column = new OrderBookOrderTypeColumn(
|
||||
Columns.RootAlgoType,
|
||||
nameof(OrderBookRowVM.root_algo_badge_fill),
|
||||
nameof(OrderBookRowVM.root_algo_display_name),
|
||||
_wrap_comparer(r => r.order_tuple.root_algo.value.CustomCase),
|
||||
DiscreteValuesFilterVM.CreateTableUpdate<OrderSecurityAlgoMeta, AlgoDataVM, AlgoID>(
|
||||
o => new AlgoDataVM(o.root_algo),
|
||||
o => AlgoID.from_algo_key(o.root_algo.key),
|
||||
vm => AlgoID.from_algo_key(vm.data.key), out var root_algo_type_observer, out var root_algo_type_items_view),
|
||||
DefaultColumnHeaders.RootAlgoType));
|
||||
_resources.Add(algo_items_source.Subscribe(root_algo_type_observer));
|
||||
root_algo_type_items_view.CustomSort = algo_items_comp;
|
||||
root_algo_type_items_view.GroupDescriptions.Add(algo_items_group_desc);
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, OrderType>(
|
||||
utilities.OrderDataHelper.all_order_types,
|
||||
ort => ort.order.order_type(),
|
||||
nameof(OrderBookRowVM.order_type))));
|
||||
// order sub type
|
||||
Add(sub_type_column = new SimpleTextColumn<OrderSubType[]>(
|
||||
Columns.SubType,
|
||||
"Sub Type",
|
||||
nameof(OrderBookRowVM.order_sub_type),
|
||||
r => r.order_sub_type_text,
|
||||
80,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, OrderSubType>(
|
||||
EnumHelper.get_values<OrderSubType>().ToList(),
|
||||
ort => ort.order.sub_type(),
|
||||
nameof(OrderBookRowVM.order_sub_type)),
|
||||
OrderBookGridView.SubTypeFilterTemplateKey,
|
||||
SubTypeFilterTemplateKey,
|
||||
_wrap_comparer(r => r.order_sub_type)));
|
||||
// side
|
||||
Add(side_column = new SideColumn(
|
||||
@@ -227,9 +205,10 @@ namespace mbts.spark {
|
||||
Add(price_column = new SimpleTextColumn<DoubleMinMaxState>(
|
||||
Columns.Price,
|
||||
"Price",
|
||||
nameof(OrderBookRowVM.price),
|
||||
r => r.price_text,
|
||||
DefaultColumnWidths.Price,
|
||||
new PriceMinMax<OrderSecurityAlgoMeta>(
|
||||
new DoubleMinMax<OrderSecurityAlgoMeta>(
|
||||
ort => ort.order.price(),
|
||||
nameof(OrderBookRowVM.price)),
|
||||
DataGridFilterKnownTemplates.DoubleMinMax,
|
||||
@@ -238,14 +217,14 @@ namespace mbts.spark {
|
||||
// not usually part of the main order book, but it's useful to add it
|
||||
// rather than debugging the popup window
|
||||
//Add(edit_price_column = new OrderBookVM.EditablePriceColumn());
|
||||
//Add(new OrderBookVM.AttachColumn("DataContext.attach_vm"));
|
||||
// avg filled price
|
||||
Add(avg_price_column = new SimpleTextColumn<DoubleMinMaxState>(
|
||||
Columns.AvgFilled,
|
||||
"Avg Price",
|
||||
nameof(OrderBookRowVM.avg_price),
|
||||
r => r.avg_price_text,
|
||||
DefaultColumnWidths.Price,
|
||||
new PriceMinMax<OrderSecurityAlgoMeta>(
|
||||
new DoubleMinMax<OrderSecurityAlgoMeta>(
|
||||
ort => ort.order.avg_price(),
|
||||
nameof(OrderBookRowVM.avg_price)),
|
||||
DataGridFilterKnownTemplates.DoubleMinMax,
|
||||
@@ -254,9 +233,10 @@ namespace mbts.spark {
|
||||
Add(last_price_column = new SimpleTextColumn<DoubleMinMaxState>(
|
||||
Columns.LastPrice,
|
||||
"Last Filled",
|
||||
nameof(OrderBookRowVM.last_price),
|
||||
r => r.last_price_text,
|
||||
DefaultColumnWidths.Price,
|
||||
new PriceMinMax<OrderSecurityAlgoMeta>(
|
||||
new DoubleMinMax<OrderSecurityAlgoMeta>(
|
||||
ort => ort.order.last_price(),
|
||||
nameof(OrderBookRowVM.last_price)),
|
||||
DataGridFilterKnownTemplates.DoubleMinMax,
|
||||
@@ -265,6 +245,7 @@ namespace mbts.spark {
|
||||
Add(status_column = new SimpleTextColumn<OrderStatus[]>(
|
||||
Columns.Status,
|
||||
"Status",
|
||||
nameof(OrderBookRowVM.status),
|
||||
r => r.status.ToString(),
|
||||
80,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, OrderStatus>(
|
||||
@@ -282,6 +263,7 @@ namespace mbts.spark {
|
||||
Add(security_key_column = new SimpleTextColumn<string>(
|
||||
Columns.SecurityKey,
|
||||
DefaultColumnHeaders.SecurityKey,
|
||||
nameof(OrderBookRowVM.security_key),
|
||||
r => r.security_key.ToString(),
|
||||
DefaultColumnWidths.SecurityKey,
|
||||
new RegexFilterVM<OrderSecurityAlgoMeta>(
|
||||
@@ -293,6 +275,7 @@ namespace mbts.spark {
|
||||
Add(order_key_column = new SimpleTextColumn<string>(
|
||||
Columns.OrderKey,
|
||||
DefaultColumnHeaders.OrderKey,
|
||||
nameof(OrderBookRowVM.order_key),
|
||||
r => r.order_key.ToString(),
|
||||
DefaultColumnWidths.OrderKey,
|
||||
new RegexFilterVM<OrderSecurityAlgoMeta>(
|
||||
@@ -304,6 +287,7 @@ namespace mbts.spark {
|
||||
Add(parent_key_column = new SimpleTextColumn<string>(
|
||||
Columns.ParentKey,
|
||||
"Parent Key",
|
||||
nameof(OrderBookRowVM.parent_order_key),
|
||||
r => r.parent_order_key.ToString(),
|
||||
DefaultColumnWidths.ParentKey,
|
||||
new RegexFilterVM<OrderSecurityAlgoMeta>(
|
||||
@@ -311,21 +295,11 @@ namespace mbts.spark {
|
||||
nameof(OrderBookRowVM.parent_order_key)),
|
||||
DataGridFilterKnownTemplates.Regex,
|
||||
_wrap_comparer(r => r.parent_order_key)));
|
||||
// attached target order key
|
||||
Add(attached_target_order_key_column = new SimpleTextColumn<string>(
|
||||
Columns.AttachedTargetOrderKey,
|
||||
DefaultColumnHeaders.AttachedTargetOrderKey,
|
||||
r => r.order.attached_target_order_key().ToString(),
|
||||
DefaultColumnWidths.AttachedTargetOrderKey,
|
||||
new RegexFilterVM<OrderSecurityAlgoMeta>(
|
||||
ort => ort.order.attached_target_order_key().ToString(),
|
||||
nameof(OrderBookRowVM.parent_order_key)),
|
||||
DataGridFilterKnownTemplates.Regex,
|
||||
_wrap_comparer(r => r.order.attached_target_order_key())));
|
||||
}
|
||||
|
||||
public event Action filter_changed;
|
||||
|
||||
public CancelColumn cancel_column { get; }
|
||||
public SecurityColumn security_column { get; }
|
||||
public SimpleTextColumn<string[]> symbol_column { get; }
|
||||
public SimpleTextColumn<TimeFilterTuple> time_created_column { get; }
|
||||
@@ -335,12 +309,12 @@ namespace mbts.spark {
|
||||
public SimpleTextColumn<Venue[]> exchange_column { get; }
|
||||
public SimpleTextColumn<TimeInForce[]> time_in_force_column { get; }
|
||||
public OrderBookOrderTypeColumn order_type_column { get; }
|
||||
public OrderBookOrderTypeColumn root_algo_type_column { get; }
|
||||
public SimpleTextColumn<OrderSubType[]> sub_type_column { get; }
|
||||
public SideColumn side_column { get; }
|
||||
public SizeColumn qty_column { get; }
|
||||
public EditableQtyColumn edit_qty_column { get; }
|
||||
public SimpleTextColumn<DoubleMinMaxState> price_column { get; }
|
||||
public EditablePriceColumn edit_price_column { get; }
|
||||
public FilledColumn filled_qty_column { get; }
|
||||
public SimpleTextColumn<DoubleMinMaxState> last_price_column { get; }
|
||||
public SimpleTextColumn<DoubleMinMaxState> avg_price_column { get; }
|
||||
@@ -349,20 +323,16 @@ namespace mbts.spark {
|
||||
public SimpleTextColumn<string> security_key_column { get; }
|
||||
public SimpleTextColumn<string> order_key_column { get; }
|
||||
public SimpleTextColumn<string> parent_key_column { get; }
|
||||
public SimpleTextColumn<string> attached_target_order_key_column { get; }
|
||||
|
||||
public FilterStates create_filter_states() =>
|
||||
public (FilterStates filters, IReadOnlyList<SortingCriteria> sorting) create() => (
|
||||
new FilterStates {
|
||||
order_key_regex = order_key_column.filter.get_state(),
|
||||
parent_key_regex = parent_key_column.filter.get_state(),
|
||||
attached_target_order_key_regex = attached_target_order_key_column.filter.get_state(),
|
||||
time_created = time_created_column.filter.get_state(),
|
||||
users = user_column.filter.get_state(),
|
||||
accounts = account_column.filter.get_state(),
|
||||
exchanges = exchange_column.filter.get_state(),
|
||||
tifs = time_in_force_column.filter.get_state(),
|
||||
order_types = order_type_column.filter.get_state(),
|
||||
root_algo_types = root_algo_type_column.filter.get_state(),
|
||||
order_sub_types = sub_type_column.filter.get_state(),
|
||||
sides = side_column.filter.get_state(),
|
||||
qty = qty_column.filter.get_state(),
|
||||
@@ -375,10 +345,8 @@ namespace mbts.spark {
|
||||
last_change = last_change_column.filter.get_state(),
|
||||
security_criteria = security_column.filter.get_state(),
|
||||
symbols = symbol_column.filter.get_state()
|
||||
};
|
||||
|
||||
public (FilterStates filters, IReadOnlyList<SortingCriteria> sorting) create() =>
|
||||
(create_filter_states(), get_sorting_data().sorting_criterion);
|
||||
},
|
||||
get_sorting_data().sorting_criterion);
|
||||
|
||||
public void load_from((FilterStates filters, IReadOnlyList<SortingCriteria> sorting) state) {
|
||||
if (state.filters is FilterStates fs) {
|
||||
@@ -392,13 +360,10 @@ namespace mbts.spark {
|
||||
filled_qty_column.filter.load_state(fs.qty_filled);
|
||||
side_column.filter.load_state(fs.sides);
|
||||
order_type_column.filter.load_state(fs.order_types);
|
||||
root_algo_type_column.filter.load_state(fs.root_algo_types);
|
||||
sub_type_column.filter.load_state(fs.order_sub_types);
|
||||
time_in_force_column.filter.load_state(fs.tifs);
|
||||
exchange_column.filter.load_state(fs.exchanges);
|
||||
order_key_column.filter.load_state(fs.order_key_regex);
|
||||
parent_key_column.filter.load_state(fs.parent_key_regex);
|
||||
attached_target_order_key_column.filter.load_state(fs.attached_target_order_key_regex);
|
||||
avg_price_column.filter.load_state(fs.avg_price);
|
||||
last_price_column.filter.load_state(fs.last_price);
|
||||
status_column.filter.load_state(fs.statuses);
|
||||
@@ -455,7 +420,6 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Disposable.dispose(ref _resources);
|
||||
foreach (var col in this.OfType<ICVOrderBookColumn>())
|
||||
(col.filter as IDisposable)?.Dispose();
|
||||
Clear();
|
||||
@@ -510,28 +474,19 @@ namespace mbts.spark {
|
||||
static IComparer<OrderBookRowVM> _wrap_comparer<T>(Func<OrderBookRowVM, T> get, IComparer<T> comparer) =>
|
||||
comparer.wrap(get);
|
||||
|
||||
static KeyBinding _create_keyinput_binding(Key k, string command_path) {
|
||||
var key_binding = new KeyBinding { Key = k };
|
||||
BindingOperations.SetBinding(
|
||||
key_binding,
|
||||
InputBinding.CommandProperty,
|
||||
new Binding { Path = new PropertyPath(command_path) }
|
||||
);
|
||||
return key_binding;
|
||||
}
|
||||
|
||||
public abstract class OrderbookColumnBase<T> : ColumnBase<Columns>, IKeyedColumn<Columns>, IFilterableColumn, ICVOrderBookColumn {
|
||||
ListSortDirection? _cached_sort_direction;
|
||||
readonly string _filter_template;
|
||||
protected readonly Func<OrderBookRowVM, string> _get_text;
|
||||
GlyphContext _root_glyph_context, _child_glyph_context;
|
||||
|
||||
protected OrderbookColumnBase(Columns col_key, string header, Func<OrderBookRowVM, string> get_text, double width,
|
||||
protected OrderbookColumnBase(Columns col_key, string header, Func<OrderBookRowVM, string> get_text, string sort_prop, double width,
|
||||
IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, T> filter, string filter_template, IComparer<OrderBookRowVM> comparer) : base(col_key) {
|
||||
id = key.ToString();
|
||||
Width = width;
|
||||
Header = header;
|
||||
_get_text = get_text;
|
||||
sort_member_path = sort_prop;
|
||||
this.filter = filter;
|
||||
_filter_template = filter_template;
|
||||
_cached_sort_direction = SortDirection;
|
||||
@@ -560,19 +515,19 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public class SimpleTextColumn<TFilterState> : OrderbookColumnBase<TFilterState>, IRenderColumn<OrderBookRowVM> {
|
||||
public SimpleTextColumn(Columns key, string header, Func<OrderBookRowVM, string> get_text, double width,
|
||||
public SimpleTextColumn(Columns key, string header, string sort_prop, Func<OrderBookRowVM, string> get_text, double width,
|
||||
IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, TFilterState> filter, string filter_template, IComparer<OrderBookRowVM> comparer)
|
||||
: base(key, header, get_text, width, filter, filter_template, comparer) { }
|
||||
: base(key, header, get_text, sort_prop, width, filter, filter_template, comparer) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, _get_text(row), position, row_height,
|
||||
RenderUtils.get_foreground(row, font_brush), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
|
||||
font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
|
||||
}
|
||||
|
||||
#region Explicit Column Defs
|
||||
public class SideColumn : OrderbookColumnBase<Side[]>, IRenderColumn<OrderBookRowVM> {
|
||||
public SideColumn(IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, Side[]> filter)
|
||||
: base(Columns.Side, "B/S", r => r.side.ToString(), DefaultColumnWidths.Side, filter, DataGridFilterKnownTemplates.EnumValues, _wrap_comparer(r => r.side)) { }
|
||||
: base(Columns.Side, "B/S", r => r.side.ToString(), nameof(OrderBookRowVM.side), DefaultColumnWidths.Side, filter, DataGridFilterKnownTemplates.EnumValues, _wrap_comparer(r => r.side)) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.side.ToString(), position, row_height,
|
||||
@@ -581,7 +536,9 @@ namespace mbts.spark {
|
||||
|
||||
public class SizeColumn : OrderbookColumnBase<UIntMinMaxState>, IRenderColumn<OrderBookRowVM> {
|
||||
public SizeColumn(IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, UIntMinMaxState> filter)
|
||||
: base(Columns.Size, "Qty", r => r.quantity.ToString(), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax, _wrap_comparer(r => r.quantity)) { }
|
||||
: base(Columns.Size, "Qty", r => r.quantity.ToString(), nameof(OrderBookRowVM.quantity), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax, _wrap_comparer(r => r.quantity)) {
|
||||
sort_member_path = nameof(OrderBookRowVM.quantity);
|
||||
}
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.quantity.ToString(), position, row_height,
|
||||
@@ -590,7 +547,9 @@ namespace mbts.spark {
|
||||
|
||||
public class FilledColumn : OrderbookColumnBase<UIntMinMaxState>, IRenderColumn<OrderBookRowVM> {
|
||||
public FilledColumn(IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, UIntMinMaxState> filter)
|
||||
: base(Columns.Filled, "Filled Qty", r => r.filled_quantity.ToString(), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax, _wrap_comparer(r => r.filled_quantity)) { }
|
||||
: base(Columns.Filled, "Filled Qty", r => r.filled_quantity.ToString(), nameof(OrderBookRowVM.filled_quantity), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax, _wrap_comparer(r => r.filled_quantity)) {
|
||||
sort_member_path = nameof(OrderBookRowVM.filled_quantity);
|
||||
}
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.filled_quantity.ToString(), position, row_height,
|
||||
@@ -598,32 +557,22 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public class OrderBookOrderTypeColumn : OrderTypeColumn<Columns>, ICVOrderBookColumn {
|
||||
readonly BindingBase _foreground_binding;
|
||||
ListSortDirection? _cached_sort_direction;
|
||||
|
||||
public OrderBookOrderTypeColumn(Columns key, string badge_fill_binding_path, string display_name_binding_path, IComparer<OrderBookRowVM> row_comparer, IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, AlgoID[]> filter, string header = DefaultColumnHeaders.OrderType)
|
||||
: base(key, new Binding(badge_fill_binding_path), new Binding(display_name_binding_path), filter, header) {
|
||||
public OrderBookOrderTypeColumn(IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, OrderType[]> filter)
|
||||
: base(Columns.OrderType, new Binding(nameof(OrderBookRowVM.badge_fill)), new Binding(nameof(OrderBookRowVM.display_name)), filter) {
|
||||
id = key.ToString();
|
||||
this.filter = filter;
|
||||
sort_member_path = nameof(OrderBookRowVM.order_type);
|
||||
font_weight_binding = RenderUtils.get_font_weight_binding();
|
||||
_foreground_binding = RenderUtils.get_foreground_binding(this);
|
||||
_cached_sort_direction = SortDirection;
|
||||
this.comparer = row_comparer;
|
||||
}
|
||||
|
||||
public new IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, AlgoID[]> filter { get; }
|
||||
public override string filter_template => DataGridFilterKnownTemplates.AlgoType;
|
||||
public IComparer<OrderBookRowVM> comparer { get; }
|
||||
public new IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, OrderType[]> filter { get; }
|
||||
public IComparer<OrderBookRowVM> comparer => _wrap_comparer(r => r.order_type);
|
||||
|
||||
IDataGridColumnFilter<OrderSecurityAlgoMeta> ICVOrderBookColumn.filter => filter;
|
||||
ListSortDirection? ICVOrderBookColumn.SortDirection { get => _cached_sort_direction; set => SortDirection = value; }
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var view = base.view_factory();
|
||||
view.SetBinding(OrderTypeItem.ForegroundProperty, _foreground_binding);
|
||||
return view;
|
||||
}
|
||||
|
||||
protected override void OnSortDirectionChanged(ListSortDirection? new_value) {
|
||||
_cached_sort_direction = new_value;
|
||||
base.OnSortDirectionChanged(new_value);
|
||||
@@ -638,7 +587,8 @@ namespace mbts.spark {
|
||||
id = key.ToString();
|
||||
this.filter = filter;
|
||||
Header = "Last Message";
|
||||
Width = DefaultColumnWidths.LastMessage;
|
||||
Width = DIYGrid.DefaultColumnWidths.LastMessage;
|
||||
sort_member_path = nameof(OrderBookRowVM.last_msg);
|
||||
tooltip_binding = new Binding(nameof(OrderBookRowVM.last_msg));
|
||||
}
|
||||
|
||||
@@ -655,7 +605,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public class SecurityColumn : FrameworkElementColumnBase<Columns>, ICVOrderBookColumn, IFilterableColumn {
|
||||
readonly BindingBase _font_weight_binding = RenderUtils.get_font_weight_binding(), _foreground_binding;
|
||||
readonly BindingBase _font_weight_binding = RenderUtils.get_font_weight_binding();
|
||||
readonly IValueConverter _expander_visiblilty_converter = new ui.converters.BooleanToVisibilityConverter { VisibilityForFalse = Visibility.Hidden };
|
||||
readonly IValueConverter _margin_converter = new AnonymousValueConverter<int, Thickness>(level => new Thickness(10 * level, 0, 0, 0));
|
||||
|
||||
@@ -664,9 +614,9 @@ namespace mbts.spark {
|
||||
public SecurityColumn(IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, SecurityCriteria> filter) : base(Columns.Security) {
|
||||
id = key.ToString();
|
||||
this.filter = filter;
|
||||
_foreground_binding = RenderUtils.get_foreground_binding(this);
|
||||
Header = DefaultColumnHeaders.Security;
|
||||
Width = DefaultColumnWidths.Security;
|
||||
sort_member_path = nameof(OrderBookRowVM.security);
|
||||
_cached_sort_direction = SortDirection;
|
||||
}
|
||||
|
||||
@@ -684,25 +634,29 @@ namespace mbts.spark {
|
||||
toggle_button.SetBinding(ToggleButton.IsCheckedProperty, new Binding(nameof(OrderBookRowVM.is_expanded)));
|
||||
toggle_button.SetBinding(ToggleButton.MarginProperty, new Binding(nameof(OrderBookRowVM.depth_level)) { Converter = _margin_converter });
|
||||
toggle_button.SetBinding(ToggleButton.VisibilityProperty, new Binding(nameof(OrderBookRowVM.is_parent)) { Converter = _expander_visiblilty_converter });
|
||||
toggle_button.SetResourceReference(ToggleButton.StyleProperty, OrderBookGridView.ExpandCollapseToggleStyleKey);
|
||||
toggle_button.SetResourceReference(ToggleButton.StyleProperty, ExpandCollapseToggleStyleKey);
|
||||
//
|
||||
var security_view = new TextBlock { Foreground = font_brush };
|
||||
security_view.SetBinding(TextBlock.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this });
|
||||
security_view.SetBinding(TextBlock.FontSizeProperty, new Binding(nameof(font_size)) { Source = this });
|
||||
security_view.SetBinding(TextBlock.TextProperty, nameof(OrderBookRowVM.security_text));
|
||||
security_view.SetBinding(TextBlock.HorizontalAlignmentProperty, new Binding(nameof(horizontal_alignment)) { Source = this });
|
||||
security_view.SetBinding(TextBlock.ForegroundProperty, _foreground_binding);
|
||||
security_view.SetBinding(TextBlock.ForegroundProperty, new Binding(nameof(font_brush)) { Source = this });
|
||||
security_view.SetBinding(TextBlock.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this });
|
||||
security_view.SetBinding(TextBlock.FontWeightProperty, _font_weight_binding);
|
||||
//
|
||||
var grid = new Grid { VerticalAlignment = VerticalAlignment.Center };
|
||||
var grid = new Grid();
|
||||
Grid.SetColumn(security_view, 1);
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
grid.Children.Add(toggle_button);
|
||||
grid.Children.Add(security_view);
|
||||
grid.VerticalAlignment = VerticalAlignment.Center;
|
||||
//
|
||||
return new Border { Child = grid, Margin = new Thickness(10, 0, 0, 0) };
|
||||
var border = new Border { Child = grid };
|
||||
border.Margin = new Thickness(10, 0, 0, 0);
|
||||
CellRender.set_background_animation_bindings(border, nameof(OrderBookRowVM.security));
|
||||
return border;
|
||||
}
|
||||
|
||||
protected override void OnSortDirectionChanged(ListSortDirection? new_value) {
|
||||
@@ -751,8 +705,7 @@ namespace mbts.spark {
|
||||
Margin = new Thickness(2),
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Height = 14,
|
||||
Width = 14,
|
||||
Focusable = false
|
||||
Width = 14
|
||||
};
|
||||
|
||||
button.SetBinding(Button.CommandProperty, _command_binding);
|
||||
@@ -760,345 +713,18 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public class AttachDetachColumn : FrameworkElementColumnBase<Columns> {
|
||||
readonly Type _mini_or_full_ob_type;
|
||||
|
||||
public AttachDetachColumn(Type mini_or_full_ob_type) : base(Columns.Attach) {
|
||||
if (!(mini_or_full_ob_type == typeof(MiniOrderBookView) || mini_or_full_ob_type == typeof(OrderBookView))) {
|
||||
throw new ArgumentException();
|
||||
}
|
||||
id = key.ToString();
|
||||
Header = DefaultColumnHeaders.AttachDetach;
|
||||
Width = 45;
|
||||
|
||||
_mini_or_full_ob_type = mini_or_full_ob_type;
|
||||
}
|
||||
|
||||
// General DataBinding trouble shooting tip:
|
||||
// This is how one can get extra debug statements during databinding
|
||||
//PresentationTraceSources.SetTraceLevel(_datacontext_binding, PresentationTraceLevel.High);
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var popup = new System.Windows.Controls.Primitives.Popup();
|
||||
|
||||
#region Attach Button
|
||||
|
||||
// create the attach button and assign the menu to it
|
||||
var attach_button = new Button() {
|
||||
Name = "attach_button",
|
||||
Content = new Image() { Source = Icons.AttachAlgos },
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = VerticalAlignment.Stretch,
|
||||
Focusable = false,
|
||||
ToolTip = "Attach"
|
||||
};
|
||||
var attach_grid = new Grid() {
|
||||
Width = 20,
|
||||
Height = 20,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
};
|
||||
attach_grid.Children.Add(attach_button);
|
||||
attach_button.Click += (s, e) => { popup.IsOpen = !popup.IsOpen; };
|
||||
|
||||
var orderbook_vm_datacontext_binding = new Binding("DataContext");
|
||||
orderbook_vm_datacontext_binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, _mini_or_full_ob_type, 1);
|
||||
|
||||
// setting the button binding, easy, same visual tree as the MiniOrderBookView
|
||||
// this has button has to be bound to the orderbook_vm b/c of the popup
|
||||
// that will be assigned to the PlacementTarget and the DataContext will be
|
||||
// kept in sync between those controls via a PlacementTarget.DataContext binding
|
||||
attach_button.SetBinding(Button.DataContextProperty, orderbook_vm_datacontext_binding);
|
||||
|
||||
var row_vm_attach_to_visibility = new Binding(WPFHelper.join_binding_paths("DataContext", nameof(OrderBookRowVM.is_attachable))) { Converter = new mbts.ui.converters.BooleanToVisibilityConverter() };
|
||||
row_vm_attach_to_visibility.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(MiniOrderBookRowView), 1);
|
||||
attach_button.SetBinding(Button.VisibilityProperty, row_vm_attach_to_visibility);
|
||||
|
||||
var row_vm_attach_command_binding = new Binding(WPFHelper.join_binding_paths("DataContext", nameof(OrderBookRowVM.attach_command)));
|
||||
row_vm_attach_command_binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(MiniOrderBookRowView), 1);
|
||||
attach_button.SetBinding(Button.CommandProperty, row_vm_attach_command_binding);
|
||||
|
||||
#region OrderParamsPanel
|
||||
// finally, set the order_params DataContext to that of the ContextMenu
|
||||
var order_params = AlgoEditorGridView.create_attach_view();
|
||||
var order_params_binding = new Binding(WPFHelper.join_binding_paths("DataContext", nameof(OrderBookVM.attach_vm)));
|
||||
order_params_binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Popup), 1);
|
||||
order_params.SetBinding(FrameworkElement.DataContextProperty, order_params_binding);
|
||||
order_params.SetBinding(AlgoEditorGridView.AlgoRootsProperty, new Binding(WPFHelper.join_binding_paths(nameof(OrderParamsVM.algo_vm), nameof(AlgoDefinitionVM.algo_roots))));
|
||||
order_params.SetBinding(AlgoEditorGridView.SelectedAlgoRootProperty, new Binding(WPFHelper.join_binding_paths(nameof(OrderParamsVM.algo_vm), nameof(AlgoDefinitionVM.algo_root))));
|
||||
order_params.SetBinding(AlgoEditorGridView.AlgoParamRowsProperty, new Binding(nameof(OrderParamsVM.param_rows)));
|
||||
#endregion
|
||||
|
||||
#region Dialog Skirt
|
||||
var dialog_skirt_button_style =
|
||||
Application.Current.TryFindResource(DialogSkirt.DialogSkirtButtonStyleKey) as Style ??
|
||||
Application.Current.TryFindResource("CommonButton") as Style ??
|
||||
Application.Current.TryFindResource(typeof(Button)) as Style;
|
||||
|
||||
// go find the OBVM on the ContextMenu to get the command
|
||||
// The button for which this will server as a Binding to needs to both perform the send_attached_orders
|
||||
// command *AND* close the algo param popup. Previously this was accomplished by setting both the Command
|
||||
// and Click event on a Button appropriately, but since the Button that will perform this
|
||||
// ultimately lives inside of the DialogSkirt, we don't have direct access to it. Ergo,
|
||||
// we make use of this addition trick from John that allows for both of these things to happen.
|
||||
// We take our original ICommand (send_attached) and wrap it in another Command by way of a Converter on the binding,
|
||||
// pure genious!
|
||||
var send_attach_button_binding = new Binding(WPFHelper.join_binding_paths("DataContext", nameof(OrderBookVM.send_attached_orders_cmd))) {
|
||||
Converter = CommandWithActionWrapper.mk_wrap_converter(() => popup.IsOpen = false)
|
||||
};
|
||||
send_attach_button_binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Popup), 1);
|
||||
|
||||
// Our ultimate goal is to have the Ok button (DialogSkirtItem) run the send attached orders command.
|
||||
// Since we're only dealing with binding paths in here and not actual objects, we don't have access
|
||||
// to an actual Command, which is what the DialogSkirtItem expects. John came up with a sneaky
|
||||
// way of setting the Button.CommandProperty via a Style backdoor, but it's also necessary to
|
||||
// create a KeyedStyleSelector here I guess.
|
||||
// Also, the DialogSkirt Ok button seems to magically close the popup, cool!
|
||||
var send_attached_button_style = new Style(typeof(Button), dialog_skirt_button_style);
|
||||
send_attached_button_style.Setters.Add(new Setter(Button.CommandProperty, send_attach_button_binding));
|
||||
var awesome_command_style_key = "SendAttachButtonStyleKey";
|
||||
var cancel_style_key = "CancelButtonStyleKey";
|
||||
|
||||
var dialog_skirt = new DialogSkirt {
|
||||
RightItems = new[] {
|
||||
new DialogSkirtItem("OK", null, awesome_command_style_key) { is_default = true },
|
||||
new DialogSkirtItem("Cancel", new DelegateCommand(() => popup.IsOpen = false), cancel_style_key) { is_cancel = true }
|
||||
}};
|
||||
|
||||
var kss = new KeyedStyleSelector();
|
||||
kss.Map.Add(new StyleSelectorKeyValue {
|
||||
Key = awesome_command_style_key,
|
||||
Value = send_attached_button_style
|
||||
});
|
||||
kss.Map.Add(
|
||||
new StyleSelectorKeyValue {
|
||||
Key = cancel_style_key,
|
||||
Value = new Style(typeof(Button), dialog_skirt_button_style)
|
||||
});
|
||||
dialog_skirt.RightItemContainerStyleSelector = kss;
|
||||
|
||||
Grid.SetRow(dialog_skirt, 1);
|
||||
var grid = new Grid();
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
||||
grid.Children.Add(order_params);
|
||||
grid.Children.Add(dialog_skirt);
|
||||
|
||||
var border = new Border() {
|
||||
Background = BTSColors.common_background_brush,
|
||||
BorderThickness = new Thickness(1),
|
||||
Name = "attach_border",
|
||||
Child = grid,
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
popup.Child = border;
|
||||
popup.StaysOpen = false;
|
||||
popup.PlacementTarget = attach_button;
|
||||
popup.Placement = PlacementMode.Bottom;
|
||||
// assign the Popup the DataContext of PlacementTarget, which will always be kept in sync with our parent button
|
||||
popup.SetBinding(Popup.DataContextProperty, new Binding("PlacementTarget.DataContext") {
|
||||
RelativeSource = new RelativeSource(RelativeSourceMode.Self)
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
#region Detach Button
|
||||
|
||||
var detach_button = new Button() {
|
||||
Name = "detach_button",
|
||||
Content = new Image() { Source = Icons.DetachAlgos },
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = VerticalAlignment.Stretch,
|
||||
Focusable = false,
|
||||
ToolTip = "Detach"
|
||||
};
|
||||
|
||||
//var orderbook_vm_datacontext_binding = new Binding(_detach_command_binding_path);
|
||||
//orderbook_vm_datacontext_binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, _mini_or_full_ob_type, 1);
|
||||
detach_button.SetBinding(Button.CommandProperty, new Binding(nameof(OrderBookRowVM.detach_command)));
|
||||
detach_button.SetBinding(Button.VisibilityProperty, new Binding(nameof(OrderBookRowVM.is_detachable)) { Converter = new mbts.ui.converters.BooleanToVisibilityConverter() });
|
||||
|
||||
|
||||
var detach_grid = new Grid() {
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Width = 20,
|
||||
Height = 20,
|
||||
};
|
||||
detach_grid.Children.Add(detach_button);
|
||||
|
||||
#endregion
|
||||
|
||||
var the_whole_thing = new Grid();
|
||||
the_whole_thing.Children.Add(attach_grid);
|
||||
the_whole_thing.Children.Add(detach_grid);
|
||||
the_whole_thing.Children.Add(popup);
|
||||
|
||||
return the_whole_thing;
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateColumn : FrameworkElementColumnBase<Columns> {
|
||||
readonly Type _mini_or_full_ob_type;
|
||||
|
||||
public UpdateColumn(Type mini_or_full_ob_type) : base(Columns.Update) {
|
||||
if (!(mini_or_full_ob_type == typeof(MiniOrderBookView) || mini_or_full_ob_type == typeof(OrderBookView))) {
|
||||
throw new ArgumentException();
|
||||
}
|
||||
id = key.ToString();
|
||||
Header = DefaultColumnHeaders.Update;
|
||||
Width = 45;
|
||||
|
||||
_mini_or_full_ob_type = mini_or_full_ob_type;
|
||||
}
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var popup = new System.Windows.Controls.Primitives.Popup();
|
||||
|
||||
#region Attach Button
|
||||
|
||||
// create the attach button and assign the menu to it
|
||||
var update_button = new Button() {
|
||||
Name = "update_button",
|
||||
Content = "···",
|
||||
Margin = new Thickness(2),
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Focusable = false
|
||||
};
|
||||
update_button.Click += (s, e) => { popup.IsOpen = !popup.IsOpen; };
|
||||
|
||||
var orderbook_vm_datacontext_binding = new Binding("DataContext");
|
||||
orderbook_vm_datacontext_binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, _mini_or_full_ob_type, 1);
|
||||
|
||||
// setting the button binding, easy, same visual tree as the MiniOrderBookView
|
||||
// this has button has to be bound to the orderbook_vm b/c of the popup
|
||||
// that will be assigned to the PlacementTarget and the DataContext will be
|
||||
// kept in sync between those controls via a PlacementTarget.DataContext binding
|
||||
update_button.SetBinding(Button.DataContextProperty, orderbook_vm_datacontext_binding);
|
||||
|
||||
var row_vm_update_command_binding = new Binding(WPFHelper.join_binding_paths("DataContext", nameof(OrderBookRowVM.update_command)));
|
||||
row_vm_update_command_binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(MiniOrderBookRowView), 1);
|
||||
update_button.SetBinding(Button.CommandProperty, row_vm_update_command_binding);
|
||||
|
||||
#endregion
|
||||
|
||||
#region OrderParamsPanel
|
||||
// finally, set the order_params DataContext to that of the ContextMenu
|
||||
var order_params = new AlgoEditorGridView {
|
||||
Margin = new Thickness(6),
|
||||
CanUserChangeAlgoRoot = false,
|
||||
};
|
||||
;
|
||||
var order_params_binding = new Binding(WPFHelper.join_binding_paths("DataContext", nameof(OrderBookVM.order_mod_vm)));
|
||||
order_params_binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Popup), 1);
|
||||
order_params.SetBinding(FrameworkElement.DataContextProperty, order_params_binding);
|
||||
order_params.SetBinding(AlgoEditorGridView.AlgoRootsProperty, new Binding(WPFHelper.join_binding_paths(nameof(OrderParamsVM.algo_vm), nameof(AlgoDefinitionVM.algo_roots))));
|
||||
order_params.SetBinding(AlgoEditorGridView.SelectedAlgoRootProperty, new Binding(WPFHelper.join_binding_paths(nameof(OrderParamsVM.algo_vm), nameof(AlgoDefinitionVM.algo_root))));
|
||||
order_params.SetBinding(AlgoEditorGridView.AlgoParamRowsProperty, new Binding(nameof(OrderParamsVM.param_rows)));
|
||||
#endregion
|
||||
|
||||
#region Dialog Skirt
|
||||
var dialog_skirt_button_style =
|
||||
Application.Current.TryFindResource(DialogSkirt.DialogSkirtButtonStyleKey) as Style ??
|
||||
Application.Current.TryFindResource("CommonButton") as Style ??
|
||||
Application.Current.TryFindResource(typeof(Button)) as Style;
|
||||
|
||||
// go find the OBVM on the ContextMenu to get the command
|
||||
// The button for which this will server as a Binding to needs to both perform the send_attached_orders
|
||||
// command *AND* close the algo param popup. Previously this was accomplished by setting both the Command
|
||||
// and Click event on a Button appropriately, but since the Button that will perform this
|
||||
// ultimately lives inside of the DialogSkirt, we don't have direct access to it. Ergo,
|
||||
// we make use of this addition trick from John that allows for both of these things to happen.
|
||||
// We take our original ICommand (send_attached) and wrap it in another Command by way of a Converter on the binding,
|
||||
// pure genious!
|
||||
var modify_selected_button_binding = new Binding(WPFHelper.join_binding_paths("DataContext", nameof(OrderBookVM.modify_selected_orders_cmd))) {
|
||||
Converter = CommandWithActionWrapper.mk_wrap_converter(() => popup.IsOpen = false)
|
||||
};
|
||||
modify_selected_button_binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Popup), 1);
|
||||
|
||||
// Our ultimate goal is to have the Ok button (DialogSkirtItem) run the send attached orders command.
|
||||
// Since we're only dealing with binding paths in here and not actual objects, we don't have access
|
||||
// to an actual Command, which is what the DialogSkirtItem expects. John came up with a sneaky
|
||||
// way of setting the Button.CommandProperty via a Style backdoor, but it's also necessary to
|
||||
// create a KeyedStyleSelector here I guess.
|
||||
// Also, the DialogSkirt Ok button seems to magically close the popup, cool!
|
||||
var send_attached_button_style = new Style(typeof(Button), dialog_skirt_button_style);
|
||||
send_attached_button_style.Setters.Add(new Setter(Button.CommandProperty, modify_selected_button_binding));
|
||||
var awesome_command_style_key = "SendAttachButtonStyleKey";
|
||||
var cancel_style_key = "CancelButtonStyleKey";
|
||||
|
||||
var dialog_skirt = new DialogSkirt {
|
||||
RightItems = new[] {
|
||||
new DialogSkirtItem("OK", null, awesome_command_style_key) { is_default = true },
|
||||
new DialogSkirtItem("Cancel", new DelegateCommand(() => popup.IsOpen = false), cancel_style_key) { is_cancel = true }
|
||||
}
|
||||
};
|
||||
|
||||
var kss = new KeyedStyleSelector();
|
||||
kss.Map.Add(new StyleSelectorKeyValue {
|
||||
Key = awesome_command_style_key,
|
||||
Value = send_attached_button_style
|
||||
});
|
||||
kss.Map.Add(
|
||||
new StyleSelectorKeyValue {
|
||||
Key = cancel_style_key,
|
||||
Value = new Style(typeof(Button), dialog_skirt_button_style)
|
||||
});
|
||||
dialog_skirt.RightItemContainerStyleSelector = kss;
|
||||
|
||||
Grid.SetRow(dialog_skirt, 1);
|
||||
var grid = new Grid();
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
||||
grid.Children.Add(order_params);
|
||||
grid.Children.Add(dialog_skirt);
|
||||
|
||||
var border = new Border() {
|
||||
Background = BTSColors.common_background_brush,
|
||||
BorderThickness = new Thickness(1),
|
||||
Name = "update_border",
|
||||
Child = grid,
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
popup.Child = border;
|
||||
popup.StaysOpen = false;
|
||||
popup.PlacementTarget = update_button;
|
||||
popup.Placement = PlacementMode.Bottom;
|
||||
// assign the Popup the DataContext of PlacementTarget, which will always be kept in sync with our parent button
|
||||
popup.SetBinding(Popup.DataContextProperty, new Binding("PlacementTarget.DataContext") {
|
||||
RelativeSource = new RelativeSource(RelativeSourceMode.Self)
|
||||
});
|
||||
popup.Closed += (s, e) => {
|
||||
if (popup.try_find_ancestor_of_type(out MiniOrderBookView mini_ob) && mini_ob.DataContext is OrderBookVM ob_vm)
|
||||
ob_vm.is_attaching_orders = false;
|
||||
};
|
||||
|
||||
var the_whole_thing = new Grid();
|
||||
the_whole_thing.Children.Add(update_button);
|
||||
the_whole_thing.Children.Add(popup);
|
||||
|
||||
return the_whole_thing;
|
||||
}
|
||||
}
|
||||
|
||||
public class EditableQtyColumn:FrameworkElementColumnBase<Columns> {
|
||||
|
||||
public EditableQtyColumn() : base(Columns.EditableQty) {
|
||||
id = key.ToString();
|
||||
Header = "Qty";
|
||||
Width = 40;
|
||||
Width = 64;
|
||||
}
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var tick_up_down = new TickUpDownNullableUIntCommit();
|
||||
tick_up_down.SetBinding(TickUpDownNullableUIntCommit.SourceValueProperty, nameof(OrderBookRowVM.prospective_quantity));
|
||||
tick_up_down.SetBinding(TickUpDownNullableUIntCommit.OnCommitProperty, nameof(OrderBookRowVM.modify_order_quantity_andor_price));
|
||||
tick_up_down.InputBindings.Add(_create_keyinput_binding(Key.Enter, nameof(OrderBookRowVM.modify_order_quantity_andor_price)));
|
||||
tick_up_down.InputBindings.Add(_create_keyinput_binding(Key.Escape, nameof(OrderBookRowVM.revert_prospective_order_quantity_andor_price)));
|
||||
tick_up_down.SetBinding(TickUpDownNullableUIntCommit.OnCommitProperty, nameof(OrderBookRowVM.modify_order_quantity));
|
||||
return tick_up_down;
|
||||
}
|
||||
}
|
||||
@@ -1108,54 +734,16 @@ namespace mbts.spark {
|
||||
public EditablePriceColumn() : base(Columns.EditablePrice) {
|
||||
id = key.ToString();
|
||||
Header = "Price";
|
||||
Width = 64;
|
||||
Width = 96;
|
||||
}
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var price_up_down = new PriceEntryCommitControl();
|
||||
price_up_down.SetBinding(PriceEntryCommitControl.SourceValueProperty, nameof(OrderBookRowVM.prospective_price));
|
||||
price_up_down.SetBinding(PriceEntryCommitControl.SourceContractSpecProperty, nameof(OrderBookRowVM.contract_spec));
|
||||
price_up_down.SetBinding(PriceEntryCommitControl.OnCommitProperty, nameof(OrderBookRowVM.modify_order_quantity_andor_price));
|
||||
price_up_down.InputBindings.Add(_create_keyinput_binding(Key.Enter, nameof(OrderBookRowVM.modify_order_quantity_andor_price)));
|
||||
price_up_down.InputBindings.Add(_create_keyinput_binding(Key.Escape, nameof(OrderBookRowVM.revert_prospective_order_quantity_andor_price)));
|
||||
price_up_down.SetBinding(PriceEntryCommitControl.OnCommitProperty, nameof(OrderBookRowVM.modify_order_price));
|
||||
return price_up_down;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class CommitColumn : FrameworkElementColumnBase<Columns> {
|
||||
public CommitColumn() : base(Columns.Commit) {
|
||||
id = key.ToString();
|
||||
Header = "";
|
||||
Width = 20;
|
||||
}
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var commit_button = new Button() {
|
||||
Name = "commit_button",
|
||||
Content = "✔",
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = VerticalAlignment.Stretch,
|
||||
};
|
||||
|
||||
var is_enabled_trigger = new Trigger() { Property = Button.IsEnabledProperty, Value = true };
|
||||
is_enabled_trigger.Setters.Add(new Setter(Button.BackgroundProperty, new SolidColorBrush(Colors.Green).frozen()));
|
||||
is_enabled_trigger.Setters.Add(new Setter(Button.ForegroundProperty, new SolidColorBrush(Colors.White).frozen()));
|
||||
var s = new Style(typeof(Button));
|
||||
s.Triggers.Add(is_enabled_trigger);
|
||||
commit_button.Style = s;
|
||||
|
||||
commit_button.SetBinding(Button.CommandProperty, nameof(OrderBookRowVM.modify_order_quantity_andor_price));
|
||||
|
||||
var grid = new Grid() {
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Width = 20,
|
||||
Height = 20,
|
||||
};
|
||||
grid.Children.Add(commit_button);
|
||||
return grid;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1163,24 +751,19 @@ namespace mbts.spark {
|
||||
static class RenderUtils {
|
||||
public static FontWeight
|
||||
root_font_weight = FontWeights.Normal,
|
||||
child_font_weight = FontWeights.Normal;
|
||||
static readonly IMultiValueConverter _foreground_converter =
|
||||
ValueConverter.create<bool, Brush, Brush>(_get_foreground);
|
||||
child_font_weight = FontWeights.Light;
|
||||
|
||||
public static double font_size = FontSizes.Small;
|
||||
|
||||
public static readonly IValueConverter
|
||||
_sub_type_text_converter = new ui.spark.converters.OrderSubTypeDisplayTextConverter(),
|
||||
_side_brush_converter = new AnonymousValueConverter<Side, Brush>(s => s == Side.Buy ? BTSColors.MarketData.bid : BTSColors.MarketData.ask),
|
||||
_font_weight_converter = new AnonymousValueConverter<bool, FontWeight>(is_child => is_child ? child_font_weight : root_font_weight);
|
||||
|
||||
_font_weight_converter = new AnonymousValueConverter<bool, FontWeight>(is_child => is_child ? FontWeights.Light : FontWeights.Normal);
|
||||
public static readonly IMultiValueConverter
|
||||
_price_text_converter = new ui.spark.converters.TickDisplayConverter(),
|
||||
_background_converter = new AnonymousMultiValueConverter<bool, OrderStatus, Brush>(
|
||||
(selected, status) => selected || status != OrderStatus.Working ? Brushes.Transparent : BTSColors.Orders.working_order_background_brush);
|
||||
|
||||
static readonly bool _animate_child_order_rows = mbts.config.Configuration.get<bts.config.OrderBookOptions>().animate_child_order_rows;
|
||||
|
||||
public static BindingBase _get_background_binding() {
|
||||
var mb = new MultiBinding { Converter = _background_converter };
|
||||
mb.Bindings.Add(new Binding(nameof(ListBoxItem.IsSelected)) { RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ListBoxItem), 1) });
|
||||
@@ -1188,29 +771,8 @@ namespace mbts.spark {
|
||||
return mb;
|
||||
}
|
||||
|
||||
public static BindingBase get_foreground_binding(ColumnBase col) {
|
||||
var col_font_brush_binding = new Binding(nameof(ColumnBase.font_brush)) { Source = col };
|
||||
if (!_animate_child_order_rows)
|
||||
return col_font_brush_binding;
|
||||
var mb = new MultiBinding { Converter = _foreground_converter };
|
||||
mb.Bindings.Add(new Binding(nameof(OrderBookRowVM.is_child)));
|
||||
mb.Bindings.Add(col_font_brush_binding);
|
||||
return mb;
|
||||
}
|
||||
|
||||
public static Brush get_side_brush(OrderBookRowVM r) => get_foreground(r, r.side == Side.Buy ? BTSColors.MarketData.bid : BTSColors.MarketData.ask);
|
||||
public static Brush get_side_brush(OrderBookRowVM r) => r.side == Side.Buy ? BTSColors.MarketData.bid : BTSColors.MarketData.ask;
|
||||
public static BindingBase get_font_weight_binding() => new Binding(nameof(OrderBookRowVM.is_child)) { Converter = _font_weight_converter };
|
||||
|
||||
public static Brush get_foreground(OrderBookRowVM row, Brush brush) =>
|
||||
_animate_child_order_rows ? _get_foreground(row.is_child, brush) : brush;
|
||||
|
||||
static Brush _get_foreground(bool is_child, Brush brush) {
|
||||
if (is_child) {
|
||||
brush = brush.Clone();
|
||||
brush.Opacity = 0.75;
|
||||
}
|
||||
return brush;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using bts.utils;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
class OrderBookRowCollection : ObservableCollection<OrderBookRowVM> {
|
||||
static readonly IEqualityComparer<INode<OrderBookRowVM>> _node_equality_comparer = EqualityComparer.Create<INode<OrderBookRowVM>, uint>(n => n.value.order_key);
|
||||
readonly Func<OrderSecurityAlgoMeta, OrderBookRowVM> _row_factory;
|
||||
/// Critical: Any and all row mutations <see cref="OrderBookRowVM"/> must be executed through <see cref="SortedTree{T, TKey}.TryReplace(TKey, Func{T, T})"/>.
|
||||
/// Mutations can change the sort-position of a row, and <see cref="_tree"/> will be broken if its elements aren't properly sorted (it relies heavily on binary-search logic).
|
||||
SortedTree<OrderBookRowVM, uint> _tree;
|
||||
IComparer<OrderBookRowVM> _comparer;
|
||||
Func<OrderSecurityAlgoMeta, bool> _filter;
|
||||
bool _is_refresh_pending;
|
||||
int _deferment_count = 0;
|
||||
/// When processing changed-items, the <see cref="SortedTree{T, TKey}.TryReplace(TKey, Func{T, T})"/> method is used to update the changed row. It is not expected to fail (return false),
|
||||
/// but if it does, we don't want to overwhelm the logs with a bunch of useless noise in case it starts happening repeatedly, hence the int fields below.
|
||||
/// <see cref="_max_replace_misses_logged"/>, <see cref="_n_replace_misses_logged"/>, <see cref="update(IIncrementalTableUpdateTransform{OrderSecurity, uint})"/>.
|
||||
const int _max_replace_misses_logged = 10;
|
||||
int _n_replace_misses_logged;
|
||||
|
||||
public OrderBookRowCollection(Func<OrderSecurityAlgoMeta, OrderBookRowVM> row_factory, IComparer<OrderBookRowVM> comparer) {
|
||||
_row_factory = row_factory;
|
||||
_tree = _mk_tree(_comparer = comparer);
|
||||
}
|
||||
|
||||
public Func<OrderSecurityAlgoMeta, bool> filter {
|
||||
get => _filter;
|
||||
set {
|
||||
_filter = value;
|
||||
_refresh_branches(_tree.RootNodes);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
public IComparer<OrderBookRowVM> comparer {
|
||||
get => _comparer;
|
||||
set {
|
||||
_tree = _tree.Resort(_comparer = value);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
public int total_row_count { get; private set; }
|
||||
IEnumerable<OrderBookRowVM> _visible_rows => _tree.Where(r => r.is_visible);
|
||||
|
||||
bool _is_refresh_deferred => _deferment_count > 0;
|
||||
|
||||
public IEnumerable<INode<OrderBookRowVM>> get_inner_nodes(OrderBookRowVM row) =>
|
||||
_tree.EnumerateInnerNodes(row);
|
||||
|
||||
public IEnumerable<OrderBookRowVM> get_descendent_rows(OrderBookRowVM row) =>
|
||||
_tree.EnumerateInnerValues(row);
|
||||
|
||||
public void collapse_all() {
|
||||
foreach (var n in _tree.RootNodes) {
|
||||
collapse_node(n);
|
||||
}
|
||||
if (_is_refresh_deferred) {
|
||||
_is_refresh_pending = true;
|
||||
} else {
|
||||
Clear();
|
||||
foreach (var n in _tree.RootNodes) {
|
||||
if (n.value.is_visible)
|
||||
Add(n.value);
|
||||
}
|
||||
}
|
||||
// local methods
|
||||
void collapse_node(INode<OrderBookRowVM> node) {
|
||||
node.value.collapse_internal();
|
||||
foreach (var cn in node.inner) {
|
||||
cn.value.is_visible = false;
|
||||
collapse_node(cn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void expand_all() {
|
||||
foreach (var n in _tree.RootNodes) {
|
||||
expand_node(n);
|
||||
}
|
||||
if (_is_refresh_deferred) {
|
||||
_is_refresh_pending = true;
|
||||
} else {
|
||||
Clear();
|
||||
foreach (var n in _tree.RootNodes) {
|
||||
if (n.value.is_visible) {
|
||||
Add(n.value);
|
||||
foreach (var r in n.inner_values())
|
||||
Add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
// local methods
|
||||
void expand_node(INode<OrderBookRowVM> node) {
|
||||
node.value.expand_internal();
|
||||
foreach (var cn in node.inner) {
|
||||
cn.value.is_visible = node.value.is_visible;
|
||||
expand_node(cn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the collection by calling <see cref="GlobalExtensions.sync_with{T}(IList{T}, IEnumerable{T})"/> with all visible rows.
|
||||
/// </summary>
|
||||
public void refresh() {
|
||||
if (_is_refresh_deferred) {
|
||||
_is_refresh_pending = true;
|
||||
} else {
|
||||
_is_refresh_pending = false;
|
||||
this.sync_with(_visible_rows);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the collection by calling <see cref="Collection{T}.Clear"/>, then adding all visible rows.
|
||||
/// </summary>
|
||||
public void reset(bool force = false) {
|
||||
if (_is_refresh_deferred && !force) {
|
||||
_is_refresh_pending = true;
|
||||
} else {
|
||||
Clear();
|
||||
this.AddRange(_visible_rows);
|
||||
_is_refresh_pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable defer_refresh(bool force_refresh_after = false) {
|
||||
// use a counter instead of a simple bool flag to gracefully handle recursive calls to defer_refresh(),
|
||||
// by only calling refresh() after all IDisposables returned by defer_refresh() have been disposed.
|
||||
_deferment_count++;
|
||||
_is_refresh_pending |= force_refresh_after;
|
||||
return Disposable.Create(() => {
|
||||
_deferment_count--;
|
||||
if (_is_refresh_pending)
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
|
||||
public void update(TableUpdate<OrderSecurityAlgoMeta, uint> table) {
|
||||
IEnumerable<INode<OrderBookRowVM>> impacted_branch_roots;
|
||||
int n_removed = table.removed.Count();
|
||||
if (n_removed == total_row_count) {
|
||||
// full refresh (most likely this is the first update or a server restart)
|
||||
_tree = _mk_tree(_comparer, table.added.Select(x => _row_factory(x)));
|
||||
impacted_branch_roots = _tree.RootNodes;
|
||||
} else {
|
||||
foreach (OrderSecurityAlgoMeta ort in table.removed)
|
||||
_tree.Remove(ort.order.Key);
|
||||
foreach (var change in table.changes) {
|
||||
if (!_tree.TryReplace(change.New.order.Key, r => { r.update(change.New); return r; })) {
|
||||
// uh oh, replace failed! Let's log this, but not a million times (this seems like the kind of error that, if it happens once, could happen a lot).
|
||||
if (_n_replace_misses_logged++ < _max_replace_misses_logged)
|
||||
Log.Ui.error($"{nameof(OrderBookRowCollection)} failed to process Changed {nameof(OrderSecurity)} in update (old:{change.Old.order.Key}, new:{change.New.order.Key}). This is the '{_n_replace_misses_logged}' time this has happened in this OrderBook.");
|
||||
}
|
||||
}
|
||||
foreach (OrderSecurityAlgoMeta ort in table.added)
|
||||
_tree.Insert(_row_factory(ort));
|
||||
impacted_branch_roots =
|
||||
table.added.Concat(table.changed).Select(ort => ort.order.Key).Select(_tree.GetRootNode)
|
||||
.Concat(
|
||||
table.removed
|
||||
.Where(ort => ort.order.has_parent())
|
||||
.Select(ort => ort.order.ParentKey)
|
||||
.Distinct() // since these are ParentKeys, there could be duplicates.
|
||||
.Select(_tree.GetRootNode)
|
||||
.Where(n => n != null)
|
||||
).Distinct(_node_equality_comparer);
|
||||
}
|
||||
_refresh_branches(impacted_branch_roots);
|
||||
refresh();
|
||||
int old_row_count = total_row_count;
|
||||
total_row_count += table.added.Count() - n_removed;
|
||||
if (old_row_count != total_row_count)
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(total_row_count)));
|
||||
}
|
||||
|
||||
static SortedTree<OrderBookRowVM, uint> _mk_tree(IComparer<OrderBookRowVM> comp, IEnumerable<OrderBookRowVM> init = null) =>
|
||||
new SortedTree<OrderBookRowVM, uint>(comp, r => r.order_key, r => r.parent_order_key, init);
|
||||
|
||||
void _refresh_branches(IEnumerable<INode<OrderBookRowVM>> roots) {
|
||||
if (filter == null) {
|
||||
foreach (var n in roots)
|
||||
refresh_tree_properties(0, true, n);
|
||||
} else {
|
||||
foreach (var n in roots)
|
||||
refresh_tree_properties(0, get_branch_passes_filter(n), n);
|
||||
}
|
||||
// local methods
|
||||
void refresh_tree_properties(int depth_level, bool is_visible, INode<OrderBookRowVM> node) {
|
||||
node.value.depth_level = depth_level;
|
||||
node.value.is_visible = is_visible;
|
||||
using (var en = node.inner.GetEnumerator()) {
|
||||
if (en.MoveNext()) {
|
||||
node.value.is_parent = true;
|
||||
depth_level++;
|
||||
do refresh_tree_properties(depth_level, node.value.is_visible && node.value.is_expanded, en.Current);
|
||||
while (en.MoveNext());
|
||||
}
|
||||
}
|
||||
}
|
||||
bool get_branch_passes_filter(INode<OrderBookRowVM> n) =>
|
||||
filter(n.value.order_tuple) || n.inner.Any(get_branch_passes_filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,36 @@
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using bts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Reactive;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using Columns = mbts.spark.OrderBookGridView.ColumnKeys;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class OrderBookRowVM : IEquatable<OrderBookRowVM>, DIYGrid.IRowVM, IHierarchicalRow<OrderSecurityAlgoMeta>, INotifyPropertyChanged {
|
||||
partial class OrderBookRowVM : DisposableViewModelBase, IEquatable<OrderBookRowVM>, DIYGrid.IRowVM, DIYGrid.IActionableRowVM {
|
||||
const string _unknown_security_price_format_string = "0.#########";
|
||||
const int _unknown_security_price_num_fractional_digits = 9;
|
||||
readonly OrderBookVM _owner;
|
||||
Color? _root_algo_badge_color_backing;
|
||||
Brush _badge_fill, _root_algo_badge_fill;
|
||||
IRaiseCanExecuteChangedCommand _show_order_modify_cmd, _toggle_expanded_cmd, _cancel_cmd, _detach_command, _attach_command, _update_command, _modify_order_quantity_andor_price, _revert_prospective_order_quantity_andor_price;
|
||||
bool _is_parent, _is_expanded, _is_selected, _is_attachable, _is_detachable;
|
||||
Brush _badge_fill;
|
||||
IRaiseCanExecuteChangedCommand _show_order_modify_cmd, _toggle_expanded_cmd, _cancel_cmd, _modify_order_quantity, _modify_order_price;
|
||||
bool _is_parent, _is_expanded, _is_selected;
|
||||
int _depth_level;
|
||||
SecurityImmutable? _security => order_tuple.security;
|
||||
|
||||
|
||||
// for mini-ob
|
||||
uint _prospective_quantity;
|
||||
double _prospective_price;
|
||||
|
||||
|
||||
public OrderBookRowVM(OrderBookVM owner, OrderSecurityAlgoMeta order_tuple, bool is_expanded) {
|
||||
_owner = owner;
|
||||
this.order_tuple = order_tuple;
|
||||
@@ -35,6 +38,12 @@ namespace mbts.spark {
|
||||
_is_expanded = is_expanded;
|
||||
prospective_quantity = this.quantity;
|
||||
prospective_price = this.price;
|
||||
this.invalidated += OrderBookRowVM_invalidated;
|
||||
}
|
||||
|
||||
private void OrderBookRowVM_invalidated() {
|
||||
modify_order_quantity?.TryRaiseCanExecuteChanged();
|
||||
modify_order_price?.TryRaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
//// This takes care of the case where the OB has all rows visible
|
||||
@@ -45,20 +54,23 @@ namespace mbts.spark {
|
||||
//// handler
|
||||
public IObservable<OrderSecurityAlgoMeta> subscribe_data() {
|
||||
return Observable.FromEvent<Action, OrderSecurityAlgoMeta>(
|
||||
cb => () => cb(order_tuple),
|
||||
cb => invalidated += cb,
|
||||
cb => () => cb(order_tuple),
|
||||
cb => invalidated += cb,
|
||||
cb => invalidated -= cb);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
this.invalidated -= OrderBookRowVM_invalidated;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public event Action invalidated;
|
||||
public event Action<(OrderSecurityAlgoMeta oldval, OrderSecurityAlgoMeta newval)> updated;
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
// Core
|
||||
public OrderSecurityAlgoMeta order_tuple { get; private set; }
|
||||
public OrderRecordImmutable order => order_tuple.order;
|
||||
public OrderMetadata order_metadata => _owner.get_metadata(this);
|
||||
public Brush badge_fill => _badge_fill ?? (_badge_fill = OrderUtils.badge_fill(order_tuple.meta, order_metadata));
|
||||
public Brush badge_fill => _badge_fill ?? (_badge_fill = order_tuple.badge_fill(order_metadata));
|
||||
public uint order_key => order.Key;
|
||||
public uint parent_order_key => order.ParentKey;
|
||||
public uint security_key => order.security_key();
|
||||
@@ -91,17 +103,6 @@ namespace mbts.spark {
|
||||
public uint quantity => order.quantity();
|
||||
public uint filled_quantity => order.filled();
|
||||
public string last_msg => order.last_msg();
|
||||
public bool is_non_terminal => order.is_non_terminal();
|
||||
public bool is_attachable { get => _is_attachable; set => SetVisual(ref _is_attachable, value); }
|
||||
public bool is_detachable { get => _is_detachable; set => SetVisual(ref _is_detachable, value); }
|
||||
public string filled_status_desc => order.filled_status_description(security.spec_or_default());
|
||||
|
||||
// Root Algo
|
||||
public CustomOrderDefinition.CustomOneofCase root_algo_order_type => order_tuple.root_algo.value.CustomCase;
|
||||
public OrderMetadata root_algo_order_metadata => _owner.get_metadata(root_algo_order_type);
|
||||
public Brush root_algo_badge_fill => _root_algo_badge_fill ?? (_root_algo_badge_fill = new SolidColorBrush(_root_algo_badge_color));
|
||||
public string root_algo_display_name => order_tuple.root_algo.name;
|
||||
|
||||
// Tree
|
||||
public bool is_parent { get => _is_parent; set => Set(ref _is_parent, value); }
|
||||
public bool is_child => order.has_parent();
|
||||
@@ -141,182 +142,93 @@ namespace mbts.spark {
|
||||
public ICommand toggle_command => _toggle_expanded_cmd ?? (
|
||||
_toggle_expanded_cmd = new DelegateCommand(() => is_expanded = !is_expanded));
|
||||
|
||||
public ICommand show_order_modify_cmd =>
|
||||
_show_order_modify_cmd ?? (_show_order_modify_cmd = new DelegateCommand(() => _owner.show_order_modify(this), () => status == OrderStatus.Working));
|
||||
ICommand DIYGrid.IActionableRowVM.default_command => _show_order_modify_cmd ??
|
||||
(_show_order_modify_cmd = new DelegateCommand(() => _owner.show_order_modify(this), () => status == OrderStatus.Working));
|
||||
|
||||
public ICommand cancel_command => _cancel_cmd ??
|
||||
(_cancel_cmd = new DelegateCommand(() => _owner.cancel_order(this), () => status == OrderStatus.Working || status == OrderStatus.Disconnected));
|
||||
(_cancel_cmd = new DelegateCommand(() => _owner.cancel_order(this), () => status == OrderStatus.Working));
|
||||
|
||||
public ICommand modify_order_quantity_andor_price {
|
||||
get {
|
||||
if (_modify_order_quantity_andor_price == null) {
|
||||
_modify_order_quantity_andor_price = new DelegateCommand(execute, can_execute);
|
||||
invalidated += _modify_order_quantity_andor_price.RaiseCanExecuteChanged;
|
||||
// impl
|
||||
void execute() {
|
||||
if (is_qty_dirty && is_price_dirty) {
|
||||
_owner.modify_order_price_and_quantity(this, prospective_price, prospective_quantity);
|
||||
} else if (is_qty_dirty) {
|
||||
_owner.modify_order_quantity(this, prospective_quantity);
|
||||
} else if (is_price_dirty) {
|
||||
_owner.modify_order_price(this, this.prospective_price);
|
||||
}
|
||||
_unselect_me();
|
||||
}
|
||||
bool can_execute() => status == OrderStatus.Working && (is_price_dirty || is_qty_dirty);
|
||||
}
|
||||
return _modify_order_quantity_andor_price;
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand revert_prospective_order_quantity_andor_price => _revert_prospective_order_quantity_andor_price ??
|
||||
(_revert_prospective_order_quantity_andor_price = new DelegateCommand(
|
||||
() => {
|
||||
prospective_price = price;
|
||||
prospective_quantity = quantity;
|
||||
public ICommand modify_order_quantity => _modify_order_quantity ??
|
||||
(_modify_order_quantity = new DelegateCommand(
|
||||
(desired_quantity) => {
|
||||
_owner.modify_order_quantity(this, (uint)desired_quantity);
|
||||
_unselect_me();
|
||||
},
|
||||
() => this.status == OrderStatus.Working && (is_price_dirty || is_qty_dirty)
|
||||
)
|
||||
(desired_quantity) =>
|
||||
(desired_quantity != null && this.status == OrderStatus.Working && (uint)desired_quantity != this.quantity)
|
||||
|| is_qty_dirty
|
||||
)
|
||||
);
|
||||
|
||||
public ICommand modify_order_price => _modify_order_price ??
|
||||
(_modify_order_price = new DelegateCommand(
|
||||
(desired_price) => {
|
||||
_owner.modify_order_price(this, (double)desired_price);
|
||||
_unselect_me();
|
||||
},
|
||||
(desired_price) =>
|
||||
(desired_price != null && this.status == OrderStatus.Working && (double)desired_price != this.price)
|
||||
|| is_price_dirty
|
||||
)
|
||||
);
|
||||
|
||||
public ICommand detach_command => _detach_command ??
|
||||
(_detach_command = new DelegateCommand(() => {
|
||||
_owner.detach_orders(this);
|
||||
})
|
||||
);
|
||||
|
||||
public ICommand attach_command => _attach_command ?? (
|
||||
_attach_command = new DelegateCommand(() => {
|
||||
// the _select me will trigger the OBVM.attach_vm to get primed
|
||||
_select_me();
|
||||
_owner.begin_attach_algos();
|
||||
})
|
||||
);
|
||||
|
||||
public ICommand update_command => _update_command ?? (
|
||||
_update_command = new DelegateCommand(() => {
|
||||
_select_me();
|
||||
})
|
||||
);
|
||||
|
||||
// private props
|
||||
Color _root_algo_badge_color {
|
||||
get => _root_algo_badge_color_backing ?? (_root_algo_badge_color_backing = OrderUtils.badge_color(order_tuple.root_meta, root_algo_order_metadata)).Value;
|
||||
set {
|
||||
if (_root_algo_badge_color_backing != value) {
|
||||
_root_algo_badge_color_backing = value;
|
||||
_root_algo_badge_fill = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region IHierarchicalRow
|
||||
bool IHierarchicalRow.is_parent => is_parent;
|
||||
bool IHierarchicalRow.is_expanded => is_expanded;
|
||||
int IHierarchicalRow.depth_level => depth_level;
|
||||
OrderSecurityAlgoMeta IHierarchicalRow<OrderSecurityAlgoMeta>.value => order_tuple;
|
||||
|
||||
void IHierarchicalRow.on_is_parent_changed(bool is_parent) => this.is_parent = is_parent;
|
||||
/// Do NOT actually call the <see cref="is_expanded"/> setter (it has side effects) - the row collection calls this method when the row is expanded/collapsed by some **external means, outside of this class.
|
||||
/// It should not trigger any functional response beyond modifying the value returned by <see cref="is_expanded"/>.
|
||||
/// **external means are:
|
||||
/// <see cref="OperableHierarchicalCollection{TRow, T, TKey}.expand_all"/>,
|
||||
/// <see cref="OperableHierarchicalCollection{TRow, T, TKey}.expand_row(TKey)"/>
|
||||
/// <see cref="OperableHierarchicalCollection{TRow, T, TKey}.collapse_all"/>
|
||||
/// <see cref="OperableHierarchicalCollection{TRow, T, TKey}.collapse_row(TKey)"/>
|
||||
void IHierarchicalRow.on_is_expanded_changed(bool is_expanded) => Set(ref _is_expanded, is_expanded, nameof(is_expanded));
|
||||
void IHierarchicalRow.on_depth_level_changed(int depth_level) => this.depth_level = depth_level;
|
||||
void IHierarchicalRow<OrderSecurityAlgoMeta>.on_value_changed(OrderSecurityAlgoMeta value) => update(value);
|
||||
#endregion
|
||||
|
||||
private void _unselect_me() {
|
||||
this.is_selected = false;
|
||||
_owner.current_row = null;
|
||||
}
|
||||
|
||||
private void _select_me() {
|
||||
this.is_selected = true;
|
||||
_owner.current_row = this;
|
||||
}
|
||||
|
||||
// These fns present a way for the orderbook to avoid noisy, intermittent collection changes during "Expand/Collapse All" ops,
|
||||
// when incremental changes are ignored in favor of a full reset, after 'is_expanded' is updated for all rows.
|
||||
internal void expand_internal() => Set(ref _is_expanded, true, nameof(is_expanded));
|
||||
internal void collapse_internal() => Set(ref _is_expanded, false, nameof(is_expanded));
|
||||
internal void expand_internal() => Set(ref _is_expanded, true);
|
||||
internal void collapse_internal() => Set(ref _is_expanded, false);
|
||||
|
||||
public void update(OrderSecurityAlgoMeta order_tuple) {
|
||||
Log.assert(order_tuple.order.Key == this.order.Key, "OrderBookRowVM: update_order was called with an order with the wrong key.");
|
||||
bool had_security = _security.HasValue;
|
||||
var old_meta = this.order_tuple.meta;
|
||||
// _show_order_modify_cmd is lazy (instantiated in the default_command getter); it might be null, don't not force instantiation
|
||||
bool? could_modify_execute = _show_order_modify_cmd?.CanExecute(null);
|
||||
var old_order_tuple = this.order_tuple;
|
||||
var dirty = ViewRefresh.get_dirty_cells(old_order_tuple, order_tuple);
|
||||
OrderRecordImmutable old_order = order;
|
||||
this.order_tuple = order_tuple;
|
||||
// order type
|
||||
if ((Columns.OrderType & dirty) != 0)
|
||||
// most cells are "render-cells," which don't use databindings - the only way to update such cells is to raise the invalidated event
|
||||
bool invalidate = !(
|
||||
old_order.last_change() == order.last_change() &&
|
||||
old_order.filled() == order.filled() &&
|
||||
old_order.last_price() == order.last_price() &&
|
||||
old_order.avg_price().Equals(order.avg_price()) &&
|
||||
old_order.price().Equals(order.price()) &&
|
||||
old_order.quantity() == order.quantity() &&
|
||||
old_order.last_msg() == order.last_msg());
|
||||
// however, the order-type and security cells DO use databindings - if these cells have changed, the PropertyChanged event must be raised regardless of whether or not invalidated is raised
|
||||
if (old_order.order_type() != order.order_type())
|
||||
RaisePropertyChanged(nameof(order_type), nameof(order_metadata));
|
||||
// quantity
|
||||
if ((Columns.Size & dirty) != 0)
|
||||
RaisePropertyChanged(nameof(quantity), nameof(is_qty_dirty));
|
||||
// we need to update the prospective_quantity here b/c an update could have come in while the row was in edit mode
|
||||
// via the Mini OB update column mechanism (vs the EditableQty column)
|
||||
// prospecive price does not need to be updated in the same manner b/c if the price is changed, the whole
|
||||
// row will no longer match the filter
|
||||
prospective_quantity = quantity;
|
||||
// price
|
||||
if ((Columns.Price & dirty) != 0) {
|
||||
if (old_order.quantity() != order.quantity()) {
|
||||
RaisePropertyChanged(nameof(quantity));
|
||||
RaisePropertyChanged(nameof(is_qty_dirty));
|
||||
}
|
||||
if (old_order.price() != order.price()) {
|
||||
RaisePropertyChanged(nameof(price));
|
||||
RaisePropertyChanged(nameof(is_price_dirty));
|
||||
}
|
||||
// security
|
||||
if ((Columns.Security & dirty) != 0)
|
||||
if (!had_security && _security.HasValue) {
|
||||
RaisePropertyChanged(nameof(security), nameof(security_text));
|
||||
// badge fill
|
||||
if (old_order_tuple.meta?.badge_color != order_tuple.meta?.badge_color) {
|
||||
// invalidate by setting to null (will be re-instantiated in the public getter)
|
||||
// need to raise invalidated to refresh the price cells (the security.ContractSpec determines how they are formatted - Ticks!)
|
||||
invalidate = true;
|
||||
}
|
||||
if (old_meta?.badge_color != order_tuple.meta?.badge_color) {
|
||||
_badge_fill = null;
|
||||
RaisePropertyChanged(nameof(badge_fill));
|
||||
}
|
||||
// display name
|
||||
if (old_order_tuple.display_name() != order_tuple.display_name())
|
||||
RaisePropertyChanged(nameof(display_name));
|
||||
// root badge fill
|
||||
if (_root_algo_badge_color_backing.HasValue) {
|
||||
// only need to invalidate it if it had a value (it is lazily instantiated)
|
||||
_root_algo_badge_color = OrderUtils.badge_color(order_tuple.root_meta, root_algo_order_metadata);
|
||||
RaisePropertyChanged(nameof(root_algo_badge_fill));
|
||||
}
|
||||
// root display name
|
||||
if (old_order_tuple.root_algo.name != order_tuple.root_algo.name)
|
||||
RaisePropertyChanged(nameof(root_algo_display_name));
|
||||
// status
|
||||
if ((Columns.Status & dirty) != 0)
|
||||
if (old_order.status() != order.status()) {
|
||||
invalidate = true;
|
||||
RaisePropertyChanged(nameof(status));
|
||||
// last message
|
||||
if ((Columns.LastMessage & dirty) != 0)
|
||||
}
|
||||
if (old_order.last_msg() != order.last_msg()) {
|
||||
RaisePropertyChanged(nameof(last_msg));
|
||||
// filled status desc
|
||||
if ((ViewRefresh.filled_status_desc_mask & dirty) != 0)
|
||||
RaisePropertyChanged(nameof(filled_status_desc));
|
||||
// show order modify cmd
|
||||
}
|
||||
if (could_modify_execute.HasValue && could_modify_execute.Value != _show_order_modify_cmd.CanExecute(null))
|
||||
_show_order_modify_cmd.RaiseCanExecuteChanged();
|
||||
// always raise updated
|
||||
updated?.Invoke((old_order_tuple, order_tuple));
|
||||
// terminal state
|
||||
if (old_order_tuple.order.is_non_terminal() != order_tuple.order.is_non_terminal()) {
|
||||
// while we call refresh_is_at/detachable in the OrderBookRowCollection.update
|
||||
// on all the children of a modified row, we used to not call it on the row istelf,
|
||||
// which should of course be done here. This manifested via the very top parent in any
|
||||
// length of attachable orders *still* showing an attach icon after some descendant was
|
||||
// detached
|
||||
RaisePropertyChanged(nameof(is_non_terminal), nameof(is_attachable), nameof(is_detachable));
|
||||
|
||||
// this will update the cancel column in the mini ob
|
||||
cancel_command.TryRaiseCanExecuteChanged();
|
||||
}
|
||||
// always refresh attach and detach b/c
|
||||
// conditionally raise invalidated
|
||||
if ((dirty & ViewRefresh.invalidate_mask) != 0)
|
||||
if (invalidate)
|
||||
raise_invalidated();
|
||||
}
|
||||
|
||||
@@ -338,74 +250,5 @@ namespace mbts.spark {
|
||||
string _fmt_price(double v) => security.HasValue ?
|
||||
security.Value.ContractSpec.format(v) :
|
||||
Math.Round(v, _unknown_security_price_num_fractional_digits, MidpointRounding.AwayFromZero).ToString(_unknown_security_price_format_string);
|
||||
|
||||
void RaisePropertyChanged(params string[] props) {
|
||||
var pc = PropertyChanged;
|
||||
if (pc == null)
|
||||
return;
|
||||
for (int i = 0; i < props.Length; i++)
|
||||
pc(this, new PropertyChangedEventArgs(props[i]));
|
||||
}
|
||||
|
||||
bool Set<T>(ref T backing, T newval, [CallerMemberName]string prop = "") where T : struct, IEquatable<T> {
|
||||
if (!backing.Equals(newval)) {
|
||||
backing = newval;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
bool SetVisual<T>(ref T backing, T newval, [CallerMemberName]string prop = "") where T : struct, IEquatable<T> {
|
||||
if (Set(ref backing, newval, prop)) {
|
||||
raise_invalidated();
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
static class ViewRefresh {
|
||||
public const Columns
|
||||
invalidate_mask =
|
||||
Columns.Security |
|
||||
Columns.LastChange |
|
||||
Columns.Filled |
|
||||
Columns.LastPrice |
|
||||
Columns.AvgFilled |
|
||||
Columns.Price |
|
||||
Columns.Size |
|
||||
Columns.Status |
|
||||
Columns.ParentKey |
|
||||
Columns.AttachedTargetOrderKey |
|
||||
Columns.LastMessage,
|
||||
filled_status_desc_mask =
|
||||
Columns.Filled |
|
||||
Columns.AvgFilled |
|
||||
Columns.Status;
|
||||
|
||||
public static Columns get_dirty_cells(OrderSecurityAlgoMeta oldval, OrderSecurityAlgoMeta newval) {
|
||||
var dirty = (Columns)0;
|
||||
if (oldval.security != newval.security)
|
||||
dirty |= Columns.Security;
|
||||
if (oldval.order.last_change() != newval.order.last_change())
|
||||
dirty |= Columns.LastChange;
|
||||
if (oldval.order.filled() != newval.order.filled())
|
||||
dirty |= Columns.Filled;
|
||||
if (oldval.order.last_price() != newval.order.last_price())
|
||||
dirty |= Columns.LastPrice;
|
||||
if (oldval.order.avg_price() != newval.order.avg_price())
|
||||
dirty |= Columns.AvgFilled;
|
||||
if (oldval.order.price() != newval.order.price())
|
||||
dirty |= Columns.Price;
|
||||
if (oldval.order.quantity() != newval.order.quantity())
|
||||
dirty |= Columns.Size;
|
||||
if (oldval.order.ParentKey != newval.order.ParentKey)
|
||||
dirty |= Columns.ParentKey;
|
||||
if (oldval.order.attached_target_order_key() != newval.order.attached_target_order_key())
|
||||
dirty |= Columns.AttachedTargetOrderKey;
|
||||
if (oldval.order.status() != newval.order.status())
|
||||
dirty |= Columns.Status;
|
||||
if (oldval.order.last_msg() != newval.order.last_msg())
|
||||
dirty |= Columns.LastMessage;
|
||||
return dirty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using bts.spark;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using mbts.collections.extensions;
|
||||
using bts.collections;
|
||||
using bts.api;
|
||||
|
||||
namespace mbts.spark.tests {
|
||||
namespace mbts.spark.tests
|
||||
{
|
||||
static internal class MockOrderRecords {
|
||||
public static OrderRecordImmutable mock_order(uint key, DateTime d, uint parent_key = 0) {
|
||||
OrderRecord o = new OrderRecord();
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,168 @@
|
||||
<UserControl x:Class="mbts.spark.OrderBookView" x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:uc="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:order="clr-namespace:bts.api.orderman;assembly=spark_api"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600" d:DesignWidth="1080"
|
||||
FocusManager.IsFocusScope="True"
|
||||
Background="{StaticResource CommonBackground}">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<uspcv:TickDisplayConverter x:Key="TickDisplayConverter"/>
|
||||
<uspcv:OrderSubTypeDisplayTextConverter x:Key="OrderSubTypeLongDisplayTextConverter" UseLongName="True" />
|
||||
<DataTemplate x:Key="{x:Static sp:OrderBookView.SubTypeFilterTemplateKey}">
|
||||
<usp:FilterContentControl
|
||||
ApplyCommandParameter="{Binding}"
|
||||
ApplyCommand="{x:Static c:DataGridFilterProperties.CommitCommand}"
|
||||
ClearCommandParameter="{Binding}"
|
||||
ClearCommand="{x:Static c:DataGridFilterProperties.ClearCommand}"
|
||||
>
|
||||
<c:ListBoxEx
|
||||
ItemsSource="{Binding items}"
|
||||
Selection="{Binding selected_items}"
|
||||
SelectionMode="Multiple"
|
||||
ItemContainerStyle="{StaticResource ListBoxItemCheckBoxStyle}"
|
||||
>
|
||||
<c:ListBoxEx.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={StaticResource OrderSubTypeLongDisplayTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
</c:ListBoxEx.ItemTemplate>
|
||||
</c:ListBoxEx>
|
||||
</usp:FilterContentControl>
|
||||
</DataTemplate>
|
||||
|
||||
<Style x:Key="{x:Static sp:OrderBookView.RowStyleKey}" TargetType="ListBoxItem" BasedOn="{StaticResource DIYGridRowStyle}">
|
||||
<Style.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
|
||||
<Condition Binding="{Binding status}" Value="{uc:Enum Working, Type=order:OrderStatus}"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.Setters>
|
||||
<Setter Property="Background" Value="{x:Static Member=working_order_background_brush, MemberType={uc:Nested uc:BTSColors.Orders}}"/>
|
||||
</MultiDataTrigger.Setters>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<DataTemplate x:Key="{x:Static sp:OrderBookView.RowTemplateKey}">
|
||||
<sp:OrderBookRowView/>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<AdornerDecorator>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<c:DIYGrid
|
||||
x:Name="_grid"
|
||||
Grid.Row="0"
|
||||
Columns="{Binding columns}"
|
||||
CurrentRow="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Rows="{Binding rows}"
|
||||
RowStyle="{StaticResource {x:Static sp:OrderBookView.RowStyleKey}}"
|
||||
RowTemplate="{StaticResource {x:Static sp:OrderBookView.RowTemplateKey}}"
|
||||
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
|
||||
>
|
||||
<c:DIYGrid.Resources>
|
||||
<ResourceDictionary>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF27C7F7"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="#FFCCEEFB"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF1CC4F7"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF82DFFB"/>
|
||||
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
|
||||
<Style x:Key="{x:Static sp:OrderBookView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Focusable" Value="False"/>
|
||||
<Setter Property="Width" Value="16"/>
|
||||
<Setter Property="Height" Value="16"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Border Background="Transparent" Height="16" Padding="0,5,5,5" Width="16">
|
||||
<Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="{StaticResource TreeViewItem.TreeArrow.Static.Fill}" Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="135" CenterY="3" CenterX="3"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="RenderTransform" TargetName="ExpandPath">
|
||||
<Setter.Value>
|
||||
<RotateTransform Angle="180" CenterY="3" CenterX="3"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True"/>
|
||||
<Condition Property="IsChecked" Value="True"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ToolTip}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="Content" Value="{x:Static sys:String.Empty}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
<Trigger Property="Content" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</c:DIYGrid.Resources>
|
||||
<c:DIYGrid.InputBindings>
|
||||
<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
</c:DIYGrid.InputBindings>
|
||||
<c:DIYGrid.CommandBindings>
|
||||
<CommandBinding
|
||||
Command="{x:Static c:DIYGrid.DefaultRowCommand}"
|
||||
Executed="{x:Static c:DIYGrid.DefaultRowActionExecuted}"
|
||||
CanExecute="{x:Static c:DIYGrid.DefaultRowActionCanExecute}"
|
||||
/>
|
||||
</c:DIYGrid.CommandBindings>
|
||||
</c:DIYGrid>
|
||||
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
|
||||
<StatusBarItem HorizontalAlignment="Right">
|
||||
<TextBlock>
|
||||
<Run Text="Orders:"/>
|
||||
<Run Text="{Binding rows.Count, Mode=OneWay}" />
|
||||
<Run Text="of"/>
|
||||
<Run Text="{Binding rows.total_row_count, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,26 @@
|
||||
using mbts.ui.controls;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal partial class OrderBookView : UserControl {
|
||||
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
|
||||
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
|
||||
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Cancel, EditableQty, EditablePrice }
|
||||
public const string
|
||||
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
|
||||
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
|
||||
RowStyleKey = "RowStyle",
|
||||
RowTemplateKey = "RowTemplateKey",
|
||||
CVRowStyleKey = "CVRowStyle",
|
||||
CVRowTemplateKey = "CVRowTemplateKey";
|
||||
|
||||
public OrderBookView(OrderBookVM vm) {
|
||||
InitializeComponent();
|
||||
DataContext = vm;
|
||||
}
|
||||
}
|
||||
|
||||
class OrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookView.Columns> {
|
||||
public OrderBookRowView() : base() { }
|
||||
}
|
||||
}
|
||||
@@ -1,65 +1,70 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.shell;
|
||||
using bts.shell.orderbook;
|
||||
using bts.spark;
|
||||
using bts.view;
|
||||
using mbts.persist;
|
||||
using mbts.ui;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.extensions;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using OrderBookGridSettingsVM = mbts.spark.GridSettingsVM<bts.api.orderman.OrderSecurity, mbts.OrderRecordTuplePredicate, bts.shell.orderbook.OrderBookGridSettings>;
|
||||
//using IOrderBookWSM = mbts.persist.IWindowSettingsManager<mbts.spark.GridSettingsWithNotifications<mbts.spark.OrderRecordTuple, mbts.OrderRecordTuplePredicate>>;
|
||||
using OrderBookGridSettings = bts.shell.OrderBookWindowFactory.OrderBookGridSettings;
|
||||
using OrderBookGridSettingsVM = mbts.spark.GridSettingsVM<bts.api.orderman.OrderSecurity, mbts.OrderRecordTuplePredicate, bts.shell.OrderBookWindowFactory.OrderBookGridSettings>;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal class OrderBookWindow : SparkWindow<OrderBookStartupData>, ISharedWindow {
|
||||
const string _window_type = "OrderBook", _default_title = "Order Book";
|
||||
internal class OrderBookWindow : SparkWindow<OrderBookWindowFactory.OrderBookStartupData>, ISharedWindow {
|
||||
const string _window_type = "OrderBook", ORDERBOOK_THREAD_ID = "OrderBook", _default_title = "Order Book";
|
||||
readonly INotificationService _notification_service;
|
||||
readonly IWindowSettingsManager<OrderBookGridSettings> _settings_manager;
|
||||
IDisposable _attachment_exit_sub;
|
||||
OrderBookStartupData _state;
|
||||
OrderBookWindowFactory.OrderBookStartupData _state;
|
||||
OrdersModel _model;
|
||||
OrderBookVM _vm;
|
||||
OrderBookView _view;
|
||||
OrderBookWindowFactory _factory;
|
||||
SparkCommand[] _commands;
|
||||
|
||||
SparkCommand _show_fills_command
|
||||
, _modify_order_command
|
||||
, _cancel_buy_orders_command
|
||||
, _cancel_sell_orders_command
|
||||
, _cancel_all_orders_command
|
||||
, _cancel_selected_orders_command
|
||||
, _recreate_selected_orders_command
|
||||
, _toggle_collapse_all_command
|
||||
, _toggle_expand_all_command
|
||||
, _show_grid_settings_command;
|
||||
|
||||
public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, INotificationService notification_service,
|
||||
OrderBookStartupData state, ImageSource icon, IWindowSettingsManager<OrderBookGridSettings> settings_manager)
|
||||
OrderBookWindowFactory.OrderBookStartupData state, ImageSource icon, IWindowSettingsManager<OrderBookGridSettings> settings_manager)
|
||||
: base(_default_title, bus, factory, SparkCommands.ShowOrders.icon) {
|
||||
_model = model;
|
||||
_factory = factory;
|
||||
_notification_service = notification_service;
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().order_book ? "OrderBook" : null;
|
||||
}
|
||||
|
||||
public override OrderBookStartupData SaveState => _state = _vm?.create();
|
||||
public override OrderBookWindowFactory.OrderBookStartupData SaveState => _state = _vm?.create();
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override UIElement Content => _view;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public override double MinWidth =>
|
||||
OrderBookStartupData.DefaultGrid.general.columns.Sum(gcs => gcs.width)
|
||||
OrderBookWindowFactory.OrderBookStartupData.DefaultGrid.general.columns.Sum(gcs => gcs.width)
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
public string thread_id { get; }
|
||||
public string thread_id => ORDERBOOK_THREAD_ID;
|
||||
|
||||
public override void Dispose() {
|
||||
_commands.OfType<IDisposable>().DisposeAll();
|
||||
Disposable.dispose(ref _attachment_exit_sub);
|
||||
Disposable.dispose(ref _vm);
|
||||
}
|
||||
|
||||
@@ -68,18 +73,19 @@ namespace mbts.spark {
|
||||
_vm.load_from(_state);
|
||||
_view = new OrderBookView(_vm);
|
||||
|
||||
var show_fills_command = SparkCommands.ShowFills(SparkCommands.KeyGestures.ShowFills).complete(() => new FillsWindowFactory.StartupData(_vm.selected_orders.Select(r => r.Key).ToArray()));
|
||||
_show_fills_command = SparkCommands.ShowFills.complete(() => new FillsWindowFactory.StartupData(_vm.selected_orders.Select(r => r.Key).ToArray()));
|
||||
|
||||
var toggle_show_right_panel_command = new SparkCommandCommand(
|
||||
get_toggle_show_right_panel_cmd_title(),
|
||||
Icons.ModifyOrder,
|
||||
_vm.toggle_show_right_panel_cmd,
|
||||
OrderBookView.KeyboardShortcuts.ToggleShowRightPanel) {
|
||||
group = CommandGroups.Modify,
|
||||
show_in_toolbar = false
|
||||
};
|
||||
_modify_order_command = SparkCommands.ShowModifyOrder.complete(
|
||||
() => _vm.current_order.Value.Key,
|
||||
can_execute: () => {
|
||||
if (_vm.current_order.HasValue) {
|
||||
return _vm.current_order.Value.State.Status == bts.api.orderman.OrderStatus.Working;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
var cancel_buy_orders_command = SparkCommands.CancelBuyOrders.complete(
|
||||
_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(),
|
||||
() => {
|
||||
@@ -91,9 +97,8 @@ namespace mbts.spark {
|
||||
return resp == BTSMessageBoxButtons.Yes;
|
||||
}
|
||||
);
|
||||
cancel_buy_orders_command.group = CommandGroups.Cancel;
|
||||
|
||||
var cancel_sell_orders_command = SparkCommands.CancelSellOrders.complete(
|
||||
_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(),
|
||||
() => {
|
||||
@@ -105,9 +110,8 @@ namespace mbts.spark {
|
||||
return resp == BTSMessageBoxButtons.Yes;
|
||||
}
|
||||
);
|
||||
cancel_sell_orders_command.group = CommandGroups.Cancel;
|
||||
|
||||
var cancel_all_orders_command = SparkCommands.CancelOrders.complete(
|
||||
_cancel_all_orders_command = SparkCommands.CancelOrders.complete(
|
||||
_model.working_orders.Select(os => os.Select(o => o.Key)),
|
||||
worders => worders.Any(),
|
||||
() => {
|
||||
@@ -119,58 +123,41 @@ namespace mbts.spark {
|
||||
return resp == BTSMessageBoxButtons.Yes;
|
||||
}
|
||||
);
|
||||
cancel_all_orders_command.group = CommandGroups.Cancel;
|
||||
|
||||
var cancel_selected_orders_command = SparkCommands.CancelSelectedOrders.complete(
|
||||
_cancel_selected_orders_command = SparkCommands.CancelSelectedOrders.complete(
|
||||
() => _vm.selected_orders.Where(x => x.status() == OrderStatus.Working).Select(x => x.Key),
|
||||
can_execute: () => _vm.selected_orders.Any(x => x.status() == OrderStatus.Working)
|
||||
);
|
||||
cancel_selected_orders_command.group = CommandGroups.Cancel;
|
||||
|
||||
var recreate_selected_orders_command = SparkCommands.RecreateSelectedOrders.complete(
|
||||
_recreate_selected_orders_command = SparkCommands.RecreateSelectedOrders.complete(
|
||||
() =>
|
||||
_vm.selected_orders.Where(x => passes_recreate_search_criteria(x))
|
||||
_vm.selected_orders.Where(x => _passes_recreate_search_criteria(x))
|
||||
.Select(x => (x.Def, x.State.Price, x.State.Quantity - x.State.FilledQuantity)),
|
||||
can_execute: () => _vm.selected_orders.Any(x => passes_recreate_search_criteria(x))
|
||||
can_execute: () => _vm.selected_orders.Any(x => _passes_recreate_search_criteria(x))
|
||||
);
|
||||
recreate_selected_orders_command.group = CommandGroups.Modify;
|
||||
|
||||
var attach_selected_orders_command = new SparkCommandCommand(
|
||||
"Attach To Selected",
|
||||
Icons.AttachAlgos,
|
||||
_vm.begin_attach_cmd,
|
||||
new KeyGesture(Key.A, ModifierKeys.Control | ModifierKeys.Shift));
|
||||
attach_selected_orders_command.group = CommandGroups.Modify;
|
||||
|
||||
var detach_selected_orders_command = new SparkCommandCommand(
|
||||
"Detach Selected",
|
||||
Icons.DetachAlgos,
|
||||
_vm.detach_selected_orders_cmd,
|
||||
new KeyGesture(Key.D, ModifierKeys.Control | ModifierKeys.Shift));
|
||||
detach_selected_orders_command.group = CommandGroups.Modify;
|
||||
|
||||
var toggle_collapse_all_command = new SparkCommandCommand("Collapse All",
|
||||
_toggle_collapse_all_command = new SparkCommandCommand("Collapse All",
|
||||
Icons.RightArrow,
|
||||
_vm.collapse_all_rows_command);
|
||||
toggle_collapse_all_command.group = CommandGroups.Auxiliary;
|
||||
|
||||
var toggle_expand_all_command = new SparkCommandCommand("Expand All",
|
||||
_toggle_expand_all_command = new SparkCommandCommand("Expand All",
|
||||
Icons.DownArrow,
|
||||
_vm.expand_all_rows_command);
|
||||
toggle_expand_all_command.group = CommandGroups.Auxiliary;
|
||||
|
||||
var show_grid_settings_command = SparkCommands.window_settings_command(new DialogCommand(() => {
|
||||
var gs_vm = new WindowSettingsDialogVM<OrderBookGridSettings, OrderBookSettingsVM>(
|
||||
_show_grid_settings_command = SparkCommands.window_settings_command(new DialogCommand(() => {
|
||||
var gs_vm = new WindowSettingsDialogVM<OrderBookGridSettings, OrderBookGridSettingsVM>(
|
||||
_settings_manager,
|
||||
us => us.order_book_grid,
|
||||
UserSettings.FileExtensions.GRID,
|
||||
s => {
|
||||
if (s.general is null)
|
||||
s = OrderBookStartupData.DefaultGrid;
|
||||
// we need to get the most up to date GridSettings when launching b/c users may
|
||||
// have changed some settings on the grid directly, like column widths.
|
||||
s.general = s.general.with(_vm.core_columns.Select(c => c.create()));
|
||||
return _vm.grid_settings_manager.create_vm(s, this.observe_title());
|
||||
s = OrderBookWindowFactory.OrderBookStartupData.DefaultGrid;
|
||||
return _vm.grid_settings_manager.create_vm(
|
||||
// we need to get the most up to date GridSettings when launching b/c users may
|
||||
// have changed some settings on the grid directly, like column widths.
|
||||
s.general.with(_vm.core_columns.Select(c => c.create())),
|
||||
this.observe_title()
|
||||
);
|
||||
}
|
||||
);
|
||||
// TODO: bind to main window's Title
|
||||
@@ -181,61 +168,32 @@ namespace mbts.spark {
|
||||
gs_vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
|
||||
return dialog;
|
||||
}));
|
||||
show_grid_settings_command.group = CommandGroups.Auxiliary;
|
||||
|
||||
_commands = new SparkCommand[] {
|
||||
toggle_show_right_panel_command,
|
||||
attach_selected_orders_command,
|
||||
detach_selected_orders_command,
|
||||
recreate_selected_orders_command,
|
||||
cancel_selected_orders_command,
|
||||
cancel_buy_orders_command,
|
||||
cancel_sell_orders_command,
|
||||
cancel_all_orders_command,
|
||||
toggle_collapse_all_command,
|
||||
toggle_expand_all_command,
|
||||
show_grid_settings_command,
|
||||
// hidden cmds (i.e., keyboard shortcuts at window level)
|
||||
hidden_cmd(new SparkCommandCommand(OrderBookView.CommandDescriptions.ModifyOrders, null, _vm.modify_selected_orders_cmd, OrderBookView.KeyboardShortcuts.ModifyOrders)),
|
||||
hidden_cmd(new SparkCommandCommand(OrderBookView.CommandDescriptions.RevertOrders, null, _vm.revert_selected_orders_cmd, OrderBookView.KeyboardShortcuts.RevertOrders))
|
||||
_commands = new SparkCommand[] {
|
||||
_show_fills_command,
|
||||
_modify_order_command,
|
||||
_cancel_selected_orders_command,
|
||||
_cancel_buy_orders_command,
|
||||
_cancel_sell_orders_command,
|
||||
_cancel_all_orders_command,
|
||||
_recreate_selected_orders_command,
|
||||
_toggle_collapse_all_command,
|
||||
_toggle_expand_all_command,
|
||||
_show_grid_settings_command
|
||||
};
|
||||
_vm.as_demuxer().subscribe<bool>(nameof(OrderBookVM.show_right_panel))
|
||||
.Subscribe(_ => toggle_show_right_panel_command.title = get_toggle_show_right_panel_cmd_title());
|
||||
_vm.selected_order_changed += vm_selected_order_changed;
|
||||
// local methods
|
||||
string get_toggle_show_right_panel_cmd_title() =>
|
||||
_vm.show_right_panel ? OrderBookView.CommandDescriptions.HideRightPanel : OrderBookView.CommandDescriptions.ShowRightPanel;
|
||||
|
||||
SparkCommand hidden_cmd(SparkCommand cmd) {
|
||||
cmd.show_in_context_menu = false;
|
||||
cmd.show_in_toolbar = false;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
bool passes_recreate_search_criteria(OrderRecordImmutable record) =>
|
||||
(record.status() == OrderStatus.Canceled || record.status() == OrderStatus.Error) &&
|
||||
!record.has_parent() &&
|
||||
record.State.Quantity > record.State.FilledQuantity;
|
||||
|
||||
void vm_selected_order_changed(OrderRecordImmutable? e) {
|
||||
cancel_selected_orders_command.raiseCanExecuteChanged();
|
||||
recreate_selected_orders_command.raiseCanExecuteChanged();
|
||||
attach_selected_orders_command.raiseCanExecuteChanged();
|
||||
detach_selected_orders_command.raiseCanExecuteChanged();
|
||||
}
|
||||
_vm.selected_order_changed += _vm_selected_order_changed;
|
||||
}
|
||||
|
||||
protected override void OnParentViewCreated(SparkWindowView parent_view) =>
|
||||
Disposable.replace(ref _attachment_exit_sub, parent_view.SubscribePreviewKeyDown((s, e) => {
|
||||
if (e.Key == Key.Escape || (e.Key == Key.Z && KeyboardExtensions.IsCtrlDown()))
|
||||
_vm.is_attaching_orders = false;
|
||||
}));
|
||||
private bool _passes_recreate_search_criteria(OrderRecordImmutable record) {
|
||||
return (record.status() == OrderStatus.Canceled || record.status() == OrderStatus.Error)
|
||||
&& !record.has_parent()
|
||||
&& record.State.Quantity > record.State.FilledQuantity;
|
||||
}
|
||||
|
||||
static class CommandGroups {
|
||||
public const string
|
||||
Modify = nameof(Modify),
|
||||
Cancel = nameof(Cancel),
|
||||
Auxiliary = nameof(Auxiliary);
|
||||
private void _vm_selected_order_changed(OrderRecordImmutable? e) {
|
||||
_modify_order_command.raiseCanExecuteChanged();
|
||||
_cancel_selected_orders_command.raiseCanExecuteChanged();
|
||||
_recreate_selected_orders_command.raiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,24 @@
|
||||
using bts.api;
|
||||
using bts.shell.orderbook;
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
using mbts;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using static mbts.spark.OrderBookView;
|
||||
|
||||
namespace bts.shell {
|
||||
internal class OrderBookWindowFactory : ShellWindowFactory<OrderBookStartupData> {
|
||||
internal class OrderBookWindowFactory : ShellWindowFactory<OrderBookWindowFactory.OrderBookStartupData> {
|
||||
readonly IBus _bus;
|
||||
readonly INotificationService _notification_service;
|
||||
readonly INotificationServiceDataCache _notification_service_data_cache;
|
||||
@@ -37,5 +46,223 @@ namespace bts.shell {
|
||||
var w = await load(startup_data);
|
||||
_bus.post(SparkTags.ShowWindow, w);
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class OrderBookStartupData {
|
||||
//[ProtoMember(5)]
|
||||
//public readonly List<SortingCriteria> sort_criteria;
|
||||
[ProtoMember(10)]
|
||||
public FilterStates filters;
|
||||
[ProtoMember(15)]
|
||||
[Obsolete]
|
||||
public GridSettings grid;
|
||||
[ProtoMember(20)]
|
||||
[Obsolete]
|
||||
List<OrderRecordTuplePredicateAnimations> _predicate_animations;
|
||||
[ProtoMember(25)]
|
||||
public bool use_defaults;
|
||||
|
||||
public OrderBookStartupData() { }
|
||||
|
||||
public OrderBookStartupData(FilterStates filters, GridSettings grid = default, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null, bool use_defaults = false) {
|
||||
this.filters = filters;
|
||||
this.use_defaults = use_defaults;
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
this.grid = grid;
|
||||
_predicate_animations = new List<OrderRecordTuplePredicateAnimations>();
|
||||
if (predicate_animations != null)
|
||||
_predicate_animations.AddRange(predicate_animations);
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
}
|
||||
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
[Obsolete]
|
||||
public IReadOnlyList<OrderRecordTuplePredicateAnimations> predicate_animations => _predicate_animations ?? (_predicate_animations = new List<OrderRecordTuplePredicateAnimations>());
|
||||
public OrderBookGridSettings grid_settings_wrapper() => use_defaults ? null : new OrderBookGridSettings(grid, predicate_animations);
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
|
||||
public static OrderBookStartupData Default =>
|
||||
new OrderBookStartupData(
|
||||
new FilterStates {
|
||||
statuses = new[] { OrderStatus.Working }
|
||||
}, _default_general_grid, use_defaults: true);
|
||||
|
||||
public static OrderBookGridSettings DefaultGrid => new OrderBookGridSettings(_default_general_grid);
|
||||
|
||||
static GridSettings _default_general_grid => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion);
|
||||
public static GridSettings default_mini_grid => GridSettings.Default.with(DefaultColumns.mini, DefaultColumns.sorting_criterion);
|
||||
|
||||
|
||||
static class DefaultColumns {
|
||||
public static GridColumnSettings security => new GridColumnSettings(Columns.Security.ToString(), DIYGrid.DefaultColumnHeaders.Security, width: DIYGrid.DefaultColumnWidths.Security);
|
||||
public static GridColumnSettings symbol => new GridColumnSettings(Columns.Symbol.ToString(), DIYGrid.DefaultColumnHeaders.Symbol, width: DIYGrid.DefaultColumnWidths.Symbol);
|
||||
public static GridColumnSettings time_created => new GridColumnSettings(Columns.Time.ToString(), "Time", width: DIYGrid.DefaultColumnWidths.Time);
|
||||
public static GridColumnSettings last_change => new GridColumnSettings(Columns.LastChange.ToString(), "Last Change", width: DIYGrid.DefaultColumnWidths.Time);
|
||||
public static GridColumnSettings user => new GridColumnSettings(Columns.User.ToString(), DIYGrid.DefaultColumnHeaders.User, width: DIYGrid.DefaultColumnWidths.User);
|
||||
public static GridColumnSettings account => new GridColumnSettings(Columns.Account.ToString(), DIYGrid.DefaultColumnHeaders.Account, width: DIYGrid.DefaultColumnWidths.Account);
|
||||
public static GridColumnSettings exchange => new GridColumnSettings(Columns.Exchange.ToString(), DIYGrid.DefaultColumnHeaders.Exchange, width: DIYGrid.DefaultColumnWidths.Exchange);
|
||||
public static GridColumnSettings time_in_force => new GridColumnSettings(Columns.TimeInForce.ToString(), DIYGrid.DefaultColumnHeaders.TimeInForce, width: DIYGrid.DefaultColumnWidths.TimeInForce);
|
||||
public static GridColumnSettings order_type => new GridColumnSettings(Columns.OrderType.ToString(), DIYGrid.DefaultColumnHeaders.OrderType, width: DIYGrid.DefaultColumnWidths.OrderType);
|
||||
public static GridColumnSettings sub_type => new GridColumnSettings(Columns.SubType.ToString(), "Sub Type", width: 80);
|
||||
public static GridColumnSettings side => new GridColumnSettings(Columns.Side.ToString(), "B/S", width: DIYGrid.DefaultColumnWidths.Side);
|
||||
public static GridColumnSettings quantity => new GridColumnSettings(Columns.Size.ToString(), "Qty", width: DIYGrid.DefaultColumnWidths.Size, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings price => new GridColumnSettings(Columns.Price.ToString(), DIYGrid.DefaultColumnHeaders.Price, width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings filled_quantity => new GridColumnSettings(Columns.Filled.ToString(), "Filled Qty", width: DIYGrid.DefaultColumnWidths.Filled, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings avg_price => new GridColumnSettings(Columns.AvgFilled.ToString(), "Avg Filled", width: DIYGrid.DefaultColumnWidths.AveragePrice, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings last_price => new GridColumnSettings(Columns.LastPrice.ToString(), "Last Filled", width: DIYGrid.DefaultColumnWidths.LastPrice, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings status => new GridColumnSettings(Columns.Status.ToString(), "Status", width: 80);
|
||||
public static GridColumnSettings last_message => new GridColumnSettings(Columns.LastMessage.ToString(), "Last Message", width: DIYGrid.DefaultColumnWidths.LastMessage);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, is_visible: false, width: DIYGrid.DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings order_key => new GridColumnSettings(Columns.OrderKey.ToString(), DIYGrid.DefaultColumnHeaders.OrderKey, width: DIYGrid.DefaultColumnWidths.OrderKey);
|
||||
public static GridColumnSettings parent_key => new GridColumnSettings(Columns.ParentKey.ToString(), "Parent Key", width: DIYGrid.DefaultColumnWidths.ParentKey);
|
||||
|
||||
public static IEnumerable<SortingCriteria> sorting_criterion {
|
||||
get {
|
||||
yield return new SortingCriteria(time_created.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GridColumnSettings> all {
|
||||
get {
|
||||
yield return security;
|
||||
yield return symbol;
|
||||
yield return time_created;
|
||||
yield return last_change;
|
||||
yield return user;
|
||||
yield return account;
|
||||
yield return exchange;
|
||||
yield return time_in_force;
|
||||
yield return order_type;
|
||||
yield return sub_type;
|
||||
yield return side;
|
||||
yield return quantity;
|
||||
yield return price;
|
||||
yield return filled_quantity;
|
||||
yield return avg_price;
|
||||
yield return last_price;
|
||||
yield return status;
|
||||
yield return last_message;
|
||||
yield return security_key;
|
||||
yield return order_key;
|
||||
yield return parent_key;
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<GridColumnSettings> _mini_mode_columns_to_hide = new HashSet<GridColumnSettings> {
|
||||
time_created, exchange, sub_type, quantity, price, avg_price, last_price, status, last_message, security_key, order_key, parent_key
|
||||
};
|
||||
|
||||
public static IEnumerable<GridColumnSettings> mini {
|
||||
get {
|
||||
foreach (var col_setting in all) {
|
||||
if (_mini_mode_columns_to_hide.Contains(col_setting)) {
|
||||
yield return col_setting.with(is_visible: false);
|
||||
} else {
|
||||
yield return col_setting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class FilterStates : IEquatable<FilterStates> {
|
||||
[ProtoMember(1)]
|
||||
public SecurityCriteria security_criteria;
|
||||
[ProtoMember(2)]
|
||||
public string order_key_regex;
|
||||
[ProtoMember(3)]
|
||||
public TimeFilterTuple time_created;
|
||||
[ProtoMember(4)]
|
||||
public string[] users;
|
||||
[ProtoMember(5)]
|
||||
public string[] accounts;
|
||||
[ProtoMember(6)]
|
||||
public Venue[] exchanges;
|
||||
[ProtoMember(7)]
|
||||
public api.orderman.TimeInForce[] tifs;
|
||||
[ProtoMember(8)]
|
||||
public api.orderman.CustomOrderDefinition.CustomOneofCase[] order_types;
|
||||
[ProtoMember(9)]
|
||||
public Side[] sides;
|
||||
[ProtoMember(10)]
|
||||
public UIntMinMaxState qty;
|
||||
[ProtoMember(11)]
|
||||
public DoubleMinMaxState price;
|
||||
[ProtoMember(12)]
|
||||
public UIntMinMaxState qty_filled;
|
||||
[ProtoMember(13)]
|
||||
public DoubleMinMaxState avg_price;
|
||||
[ProtoMember(14)]
|
||||
public DoubleMinMaxState last_price;
|
||||
[ProtoMember(15)]
|
||||
public api.orderman.OrderStatus[] statuses;
|
||||
[ProtoMember(16)]
|
||||
public string exchange_message_regex;
|
||||
[ProtoMember(17)]
|
||||
public TimeFilterTuple last_change;
|
||||
//[ProtoMember(18)]
|
||||
//public string security_regex;
|
||||
[ProtoMember(19)]
|
||||
public api.orderman.OrderSubType[] order_sub_types;
|
||||
[ProtoMember(20)]
|
||||
public string[] symbols;
|
||||
|
||||
public bool Equals(FilterStates other) =>
|
||||
other != null &&
|
||||
security_criteria.safe_equals(other.security_criteria) &&
|
||||
order_key_regex == other.order_key_regex &&
|
||||
time_created.Equals(other.time_created) &&
|
||||
EqualityComparer.safe_sequence_equals(users, other.users) &&
|
||||
EqualityComparer.safe_sequence_equals(accounts, other.accounts) &&
|
||||
EqualityComparer.safe_sequence_equals(exchanges, other.exchanges) &&
|
||||
EqualityComparer.safe_sequence_equals(tifs, other.tifs) &&
|
||||
EqualityComparer.safe_sequence_equals(order_types, other.order_types) &&
|
||||
EqualityComparer.safe_sequence_equals(sides, other.sides) &&
|
||||
qty.safe_equals(other.qty) &&
|
||||
price.safe_equals(other.price) &&
|
||||
qty_filled.safe_equals(other.qty_filled) &&
|
||||
avg_price.safe_equals(other.avg_price) &&
|
||||
last_price.safe_equals(other.last_price) &&
|
||||
EqualityComparer.safe_sequence_equals(statuses, other.statuses) &&
|
||||
exchange_message_regex == other.exchange_message_regex &&
|
||||
last_change.Equals(other.last_change) &&
|
||||
EqualityComparer.safe_sequence_equals(order_sub_types, other.order_sub_types) &&
|
||||
EqualityComparer.safe_sequence_equals(symbols, other.symbols);
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class OrderBookGridSettings : IGridSettingsWithNotifications<OrderSecurity, OrderRecordTuplePredicate>, IEquatable<OrderBookGridSettings> {
|
||||
[ProtoMember(1)]
|
||||
public GridSettings general { get; set; }
|
||||
[ProtoMember(2)]
|
||||
List<OrderRecordTuplePredicateAnimations> _predicate_animations;
|
||||
|
||||
public OrderBookGridSettings() { }
|
||||
public OrderBookGridSettings(GridSettings general = default, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null) {
|
||||
this.general = general;
|
||||
_predicate_animations = new List<OrderRecordTuplePredicateAnimations>();
|
||||
if (predicate_animations != null)
|
||||
_predicate_animations.AddRange(predicate_animations);
|
||||
}
|
||||
|
||||
public IReadOnlyList<OrderRecordTuplePredicateAnimations> predicate_animations => _predicate_animations ?? (_predicate_animations = new List<OrderRecordTuplePredicateAnimations>());
|
||||
IEnumerable<IPredicateAnimationsData<OrderSecurity, OrderRecordTuplePredicate>> IGridSettingsWithNotifications<OrderSecurity, OrderRecordTuplePredicate>.predicate_animations => predicate_animations;
|
||||
|
||||
public OrderBookGridSettings with(GridSettings general) =>
|
||||
new OrderBookGridSettings(general ?? this.general, predicate_animations);
|
||||
|
||||
public bool Equals(OrderBookGridSettings other) =>
|
||||
other != null &&
|
||||
EqualityComparer.null_check_equals(general, other.general) &&
|
||||
EqualityComparer.safe_sequence_equals(predicate_animations, other.predicate_animations);
|
||||
|
||||
public override bool Equals(object obj) => Equals(obj as OrderBookGridSettings);
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
EqualityComparer.safe_get_hash_code(general),
|
||||
EqualityComparer.safe_aggregate_hash(predicate_animations));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using mbts.ui;
|
||||
using ProtoBuf;
|
||||
using System.Collections.Generic;
|
||||
using GridSettings = mbts.ui.settings.GridSettings;
|
||||
|
||||
namespace bts.shell.orderbook {
|
||||
[ProtoContract]
|
||||
class OrderBookFillsGridSettings {
|
||||
[ProtoMember(1)]
|
||||
public GridSettings general { get; set; }
|
||||
[ProtoMember(2)]
|
||||
public FillsWindowFactory.FilterStates filters { get; set; }
|
||||
[ProtoMember(3)]
|
||||
public List<SortingCriteria> sorting_criterion { get; set; }
|
||||
|
||||
public FillsWindowFactory.StartupData to_startup_data() =>
|
||||
new FillsWindowFactory.StartupData(null, sorting_criterion, filters, general);
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
using mbts;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace bts.shell.orderbook {
|
||||
[ProtoContract]
|
||||
class FilterStates : IEquatable<FilterStates> {
|
||||
[ProtoMember(1)]
|
||||
public SecurityCriteria security_criteria;
|
||||
[ProtoMember(2)]
|
||||
public string order_key_regex;
|
||||
[ProtoMember(3)]
|
||||
public TimeFilterTuple time_created;
|
||||
[ProtoMember(4)]
|
||||
public string[] users;
|
||||
[ProtoMember(5)]
|
||||
public string[] accounts;
|
||||
[ProtoMember(6)]
|
||||
public Venue[] exchanges;
|
||||
[ProtoMember(7)]
|
||||
public TimeInForce[] tifs;
|
||||
//[ProtoMember(8)]
|
||||
//public CustomOrderDefinition.CustomOneofCase[] order_types;
|
||||
[ProtoMember(23)]
|
||||
public AlgoID[] order_types;
|
||||
[ProtoMember(24)]
|
||||
public AlgoID[] root_algo_types;
|
||||
[ProtoMember(9)]
|
||||
public Side[] sides;
|
||||
[ProtoMember(10)]
|
||||
public UIntMinMaxState qty;
|
||||
[ProtoMember(11)]
|
||||
public DoubleMinMaxState price;
|
||||
[ProtoMember(12)]
|
||||
public UIntMinMaxState qty_filled;
|
||||
[ProtoMember(13)]
|
||||
public DoubleMinMaxState avg_price;
|
||||
[ProtoMember(14)]
|
||||
public DoubleMinMaxState last_price;
|
||||
[ProtoMember(15)]
|
||||
public OrderStatus[] statuses;
|
||||
[ProtoMember(16)]
|
||||
public string exchange_message_regex;
|
||||
[ProtoMember(17)]
|
||||
public TimeFilterTuple last_change;
|
||||
//[ProtoMember(18)]
|
||||
//public string security_regex;
|
||||
[ProtoMember(19)]
|
||||
public api.orderman.OrderSubType[] order_sub_types;
|
||||
[ProtoMember(20)]
|
||||
public string[] symbols;
|
||||
[ProtoMember(21)]
|
||||
public string parent_key_regex;
|
||||
[ProtoMember(22)]
|
||||
public string attached_target_order_key_regex;
|
||||
|
||||
public bool Equals(FilterStates other) =>
|
||||
other != null &&
|
||||
security_criteria.safe_equals(other.security_criteria) &&
|
||||
order_key_regex == other.order_key_regex &&
|
||||
parent_key_regex == other.parent_key_regex &&
|
||||
attached_target_order_key_regex == other.attached_target_order_key_regex &&
|
||||
time_created.Equals(other.time_created) &&
|
||||
EqualityComparer.safe_sequence_equals(users, other.users) &&
|
||||
EqualityComparer.safe_sequence_equals(accounts, other.accounts) &&
|
||||
EqualityComparer.safe_sequence_equals(exchanges, other.exchanges) &&
|
||||
EqualityComparer.safe_sequence_equals(tifs, other.tifs) &&
|
||||
EqualityComparer.safe_sequence_equals(order_types, other.order_types) &&
|
||||
EqualityComparer.safe_sequence_equals(root_algo_types, other.root_algo_types) &&
|
||||
EqualityComparer.safe_sequence_equals(sides, other.sides) &&
|
||||
qty.safe_equals(other.qty) &&
|
||||
price.safe_equals(other.price) &&
|
||||
qty_filled.safe_equals(other.qty_filled) &&
|
||||
avg_price.safe_equals(other.avg_price) &&
|
||||
last_price.safe_equals(other.last_price) &&
|
||||
EqualityComparer.safe_sequence_equals(statuses, other.statuses) &&
|
||||
exchange_message_regex == other.exchange_message_regex &&
|
||||
last_change.Equals(other.last_change) &&
|
||||
EqualityComparer.safe_sequence_equals(order_sub_types, other.order_sub_types) &&
|
||||
EqualityComparer.safe_sequence_equals(symbols, other.symbols);
|
||||
|
||||
public override bool Equals(object obj) => Equals(obj as FilterStates);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
security_criteria?.GetHashCode() ?? 0,
|
||||
order_key_regex?.GetHashCode() ?? 0,
|
||||
parent_key_regex?.GetHashCode() ?? 0,
|
||||
attached_target_order_key_regex?.GetHashCode() ?? 0,
|
||||
time_created.GetHashCode(),
|
||||
EqualityComparer.safe_aggregate_hash(users),
|
||||
EqualityComparer.safe_aggregate_hash(accounts),
|
||||
EqualityComparer.safe_aggregate_hash(exchanges),
|
||||
EqualityComparer.safe_aggregate_hash(tifs),
|
||||
EqualityComparer.safe_aggregate_hash(order_types),
|
||||
EqualityComparer.safe_aggregate_hash(root_algo_types),
|
||||
EqualityComparer.safe_aggregate_hash(sides),
|
||||
qty?.GetHashCode() ?? 0,
|
||||
price?.GetHashCode() ?? 0,
|
||||
qty_filled?.GetHashCode() ?? 0,
|
||||
avg_price?.GetHashCode() ?? 0,
|
||||
last_price?.GetHashCode() ?? 0,
|
||||
EqualityComparer.safe_aggregate_hash(statuses),
|
||||
exchange_message_regex?.GetHashCode() ?? 0,
|
||||
last_change.GetHashCode(),
|
||||
EqualityComparer.safe_aggregate_hash(order_sub_types),
|
||||
EqualityComparer.safe_aggregate_hash(symbols));
|
||||
}
|
||||
|
||||
static class FilterStatesExtensions {
|
||||
public static Func<OrderSecurityAlgoMeta, bool> build_predicate(this FilterStates fs) {
|
||||
var predicates = new List<Func<OrderSecurityAlgoMeta, bool>>();
|
||||
// security
|
||||
if (fs.security_criteria?.is_defined() ?? false)
|
||||
predicates.Add(o => o.security.HasValue && ContractPickerModel.security_in_criteria(fs.security_criteria, o.security.Value));
|
||||
// regex
|
||||
add_regex(fs.order_key_regex, o => o.order.Key.ToString());
|
||||
add_regex(fs.exchange_message_regex, o => o.order.State.LastMsg);
|
||||
add_regex(fs.attached_target_order_key_regex, o => o.order.attached_target_order_key().ToString());
|
||||
add_regex(fs.parent_key_regex, o => o.order.ParentKey.ToString());
|
||||
// time
|
||||
add_time_filter(fs.time_created, o => o.order.created());
|
||||
add_time_filter(fs.last_change, o => o.order.last_change());
|
||||
// discrete values
|
||||
add_discrete_values(fs.users, o => o.order.user());
|
||||
add_discrete_values(fs.accounts, o => o.order.account());
|
||||
add_discrete_values(fs.exchanges, o => o.security?.Exchange ?? Venue.Unset);
|
||||
add_discrete_values(fs.tifs, o => o.order.time_in_force());
|
||||
add_discrete_values(fs.order_types, o => AlgoID.from_order_def(o.order.Def));
|
||||
add_discrete_values(fs.root_algo_types, o => AlgoID.from_algo_key(o.root_algo.key));
|
||||
add_discrete_values(fs.order_sub_types, o => o.order.sub_type());
|
||||
add_discrete_values(fs.sides, o => o.order.side());
|
||||
add_discrete_values(fs.statuses, o => o.order.status());
|
||||
add_discrete_values(fs.symbols, o => o.security?.Symbol);
|
||||
// numeric
|
||||
add_numeric(fs.qty, o => o.order.quantity());
|
||||
add_numeric(fs.price, o => o.order.price());
|
||||
add_numeric(fs.qty_filled, o => o.order.filled());
|
||||
add_numeric(fs.avg_price, o => o.order.avg_price());
|
||||
add_numeric(fs.last_price, o => o.order.last_price());
|
||||
return predicates.Count == 0 ? null : new Func<OrderSecurityAlgoMeta, bool>(o => predicates.All(p => p(o)));
|
||||
// local methods (impl)
|
||||
void add_regex(string str, Func<OrderSecurityAlgoMeta, string> selector) {
|
||||
var regex = RegexFilterUtils.build_regex(str);
|
||||
if (regex == null)
|
||||
return;
|
||||
predicates.Add(impl);
|
||||
bool impl(OrderSecurityAlgoMeta o) => regex.IsMatch(selector(o) ?? string.Empty);
|
||||
}
|
||||
void add_time_filter(TimeFilterTuple t, Func<OrderSecurityAlgoMeta, DateTime> selector) {
|
||||
if (!t.is_active)
|
||||
return;
|
||||
var dt_pred = TimeFilterUtils.build_predicate(t);
|
||||
predicates.Add(impl);
|
||||
bool impl(OrderSecurityAlgoMeta o) => dt_pred(selector(o));
|
||||
}
|
||||
void add_discrete_values<T>(T[] values, Func<OrderSecurityAlgoMeta, T> selector) {
|
||||
if (values.is_null_or_empty())
|
||||
return;
|
||||
if (values.Length < 16) {
|
||||
predicates.Add(impl);
|
||||
bool impl(OrderSecurityAlgoMeta o) => values.Contains(selector(o));
|
||||
} else {
|
||||
var set = new HashSet<T>(values);
|
||||
predicates.Add(impl);
|
||||
bool impl(OrderSecurityAlgoMeta o) => set.Contains(selector(o));
|
||||
}
|
||||
}
|
||||
void add_numeric<T>(IMinMaxState<T> value, Func<OrderSecurityAlgoMeta, T> selector) where T : struct, IComparable<T> {
|
||||
if (value == null)
|
||||
return;
|
||||
if (value.minimum.active) {
|
||||
var min = value.minimum.value.Value;
|
||||
if (value.maximum.active) {
|
||||
var max = value.maximum.value.Value;
|
||||
predicates.Add(impl);
|
||||
bool impl(OrderSecurityAlgoMeta o) {
|
||||
var v = selector(o);
|
||||
return min.CompareTo(v) <= 0 && max.CompareTo(v) >= 0;
|
||||
}
|
||||
} else {
|
||||
predicates.Add(impl);
|
||||
bool impl(OrderSecurityAlgoMeta o) => min.CompareTo(selector(o)) <= 0;
|
||||
}
|
||||
} else if (value.maximum.active) {
|
||||
var max = value.maximum.value.Value;
|
||||
predicates.Add(impl);
|
||||
bool impl(OrderSecurityAlgoMeta o) => max.CompareTo(selector(o)) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
using mbts;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using GridSettings = mbts.ui.settings.GridSettings;
|
||||
|
||||
namespace bts.shell.orderbook {
|
||||
enum AlgoTreeDisplayMode {
|
||||
FullTree,
|
||||
RootAndLeafNodes
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class OrderBookGridSettings : IGridSettingsWithNotifications<OrderSecurity, OrderRecordTuplePredicate>, IEquatable<OrderBookGridSettings> {
|
||||
const AlgoTreeDisplayMode _default_algo_tree_display_mode = AlgoTreeDisplayMode.FullTree;
|
||||
// general
|
||||
[ProtoMember(1)]
|
||||
public GridSettings general { get; set; }
|
||||
// predicate animations
|
||||
[ProtoMember(2)]
|
||||
List<OrderRecordTuplePredicateAnimations> _predicate_animations;
|
||||
// algo tree display mode
|
||||
[ProtoMember(3)]
|
||||
[DefaultValue(_default_algo_tree_display_mode)]
|
||||
public AlgoTreeDisplayMode algo_tree_display_mode { get; set; }
|
||||
|
||||
public OrderBookGridSettings() =>
|
||||
algo_tree_display_mode = _default_algo_tree_display_mode;
|
||||
|
||||
public OrderBookGridSettings(GridSettings general = default, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null) {
|
||||
this.general = general;
|
||||
_predicate_animations = new List<OrderRecordTuplePredicateAnimations>();
|
||||
if (predicate_animations != null)
|
||||
_predicate_animations.AddRange(predicate_animations);
|
||||
}
|
||||
|
||||
public IReadOnlyList<OrderRecordTuplePredicateAnimations> predicate_animations => _predicate_animations ?? (_predicate_animations = new List<OrderRecordTuplePredicateAnimations>());
|
||||
IEnumerable<IPredicateAnimationsData<OrderSecurity, OrderRecordTuplePredicate>> IGridSettingsWithNotifications<OrderSecurity, OrderRecordTuplePredicate>.predicate_animations => predicate_animations;
|
||||
|
||||
public OrderBookGridSettings with(GridSettings general) =>
|
||||
new OrderBookGridSettings(general ?? this.general, predicate_animations);
|
||||
|
||||
public bool Equals(OrderBookGridSettings other) =>
|
||||
other != null &&
|
||||
algo_tree_display_mode == other.algo_tree_display_mode &&
|
||||
EqualityComparer.null_check_equals(general, other.general) &&
|
||||
EqualityComparer.safe_sequence_equals(predicate_animations, other.predicate_animations);
|
||||
|
||||
public override bool Equals(object obj) => Equals(obj as OrderBookGridSettings);
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
EqualityComparer.safe_get_hash_code(general),
|
||||
EqualityComparer.safe_aggregate_hash(predicate_animations));
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Columns = mbts.spark.OrderBookGridView.ColumnKeys;
|
||||
|
||||
namespace bts.shell.orderbook {
|
||||
class OrderBookGridSettingsManager : IDisposable, IObserver<OrderBookGridSettings> {
|
||||
readonly IObserver<OrderBookGridSettings> _core_man_as_observer;
|
||||
AlgoTreeDisplayMode _algo_tree_display_mode;
|
||||
|
||||
public OrderBookGridSettingsManager(
|
||||
IObservable<string> owner_title,
|
||||
IBus bus,
|
||||
INotificationService notification_service,
|
||||
OrdersModel model,
|
||||
IEnumerable<OrderBookRowVM> rows,
|
||||
ObservableCollection<DIYGrid.ColumnBase> columns) {
|
||||
// make font-weight immutable for all columns (the font-weight rule is hard-coded: Regular for root-orders, Light for all others)
|
||||
// TODO: Remove the immutable font-weight flags when cell animation is ready
|
||||
var col_settings_flags = EnumHelper.get_values<Columns>().ToDictionary(
|
||||
c => c.ToString(), c => GridColumnSettingsFeatureFlags.ImmutableFontWeight);
|
||||
// make font-color immutable for Side, Size, and Filled columns (the font-color rule is hard-coded: MarketData.bid for Buy orders, MarketData.ask for Sells)
|
||||
// TODO: Remove the immutable font-color flags when cell animation is ready
|
||||
foreach (var c in new[] { Columns.Side, Columns.Size, Columns.Filled }) {
|
||||
col_settings_flags[c.ToString()] = GridColumnSettingsFeatureFlags.ImmutableFontColor | GridColumnSettingsFeatureFlags.ImmutableFontWeight;
|
||||
}
|
||||
core_manager = new GridSettingsManager<OrderBookRowVM, OrderSecurity, OrderRecordTuplePredicate, OrderBookGridSettings>(
|
||||
owner_title, bus, model, rows, columns,
|
||||
() => new OrderRecordTuplePredicate(),
|
||||
() => {
|
||||
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 = 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_data, a.cell_animations, a.row_animations, a.sound_notifications))) { algo_tree_display_mode = _algo_tree_display_mode },
|
||||
p => p.create_predicate(notification_service.data_cache),
|
||||
p => p.create_notification_predicate(notification_service.data_cache),
|
||||
notification_service == null ? new Func<IPredicate<OrderSecurity>, SoundNotification, IDisposable>((p, sn) => Disposable.empty) : notification_service.link_to_order,
|
||||
notification_service == null ? new Func<INotificationPredicate<OrderSecurity>, IDisposable>(t => Disposable.empty) : notification_service.link_to_order,
|
||||
col_settings_flags);
|
||||
_core_man_as_observer = core_manager;
|
||||
}
|
||||
|
||||
public event Action algo_tree_display_mode_changed;
|
||||
|
||||
public GridSettingsManager<OrderBookRowVM, OrderSecurity, OrderRecordTuplePredicate, OrderBookGridSettings> core_manager { get; }
|
||||
|
||||
public AlgoTreeDisplayMode algo_tree_display_mode {
|
||||
get => _algo_tree_display_mode;
|
||||
set {
|
||||
if (_algo_tree_display_mode != value) {
|
||||
_algo_tree_display_mode = value;
|
||||
algo_tree_display_mode_changed?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void load_from(OrderBookGridSettings gs) {
|
||||
core_manager.load_from(gs);
|
||||
algo_tree_display_mode = gs.algo_tree_display_mode;
|
||||
}
|
||||
|
||||
public OrderBookGridSettings create(IEnumerable<SortingCriteria> sorting_criterion) {
|
||||
var gs = core_manager.create();
|
||||
gs.general = gs.general.with(sorting_criterion: sorting_criterion);
|
||||
gs.algo_tree_display_mode = _algo_tree_display_mode;
|
||||
return gs;
|
||||
}
|
||||
|
||||
public OrderBookSettingsVM create_vm(OrderBookGridSettings gs, IObservable<string> owner_title) =>
|
||||
new OrderBookSettingsVM(core_manager.create_vm(gs.general, owner_title)) {
|
||||
algo_tree_display_mode = gs.algo_tree_display_mode
|
||||
};
|
||||
|
||||
public void Dispose() => core_manager.Dispose();
|
||||
|
||||
static OrderRecordTuplePredicate.PredicateTypeOneofCase _corresponding_predicate_type(Columns col) {
|
||||
switch (col) {
|
||||
case Columns.Security: return OrderRecordTuplePredicate.PredicateTypeOneofCase.Security;
|
||||
case Columns.Time: return OrderRecordTuplePredicate.PredicateTypeOneofCase.Time;
|
||||
case Columns.LastChange: return OrderRecordTuplePredicate.PredicateTypeOneofCase.LastChange;
|
||||
case Columns.User: return OrderRecordTuplePredicate.PredicateTypeOneofCase.User;
|
||||
case Columns.Account: return OrderRecordTuplePredicate.PredicateTypeOneofCase.Account;
|
||||
case Columns.Exchange: return OrderRecordTuplePredicate.PredicateTypeOneofCase.Exchange;
|
||||
case Columns.TimeInForce: return OrderRecordTuplePredicate.PredicateTypeOneofCase.TimeInForce;
|
||||
case Columns.OrderType: return OrderRecordTuplePredicate.PredicateTypeOneofCase.OrderType;
|
||||
case Columns.SubType: return OrderRecordTuplePredicate.PredicateTypeOneofCase.SubType;
|
||||
case Columns.Side: return OrderRecordTuplePredicate.PredicateTypeOneofCase.Side;
|
||||
case Columns.Size: return OrderRecordTuplePredicate.PredicateTypeOneofCase.Size;
|
||||
case Columns.Price: return OrderRecordTuplePredicate.PredicateTypeOneofCase.Price;
|
||||
case Columns.Filled: return OrderRecordTuplePredicate.PredicateTypeOneofCase.Filled;
|
||||
case Columns.AvgFilled: return OrderRecordTuplePredicate.PredicateTypeOneofCase.AvgFilled;
|
||||
case Columns.LastPrice: return OrderRecordTuplePredicate.PredicateTypeOneofCase.LastPrice;
|
||||
case Columns.Status: return OrderRecordTuplePredicate.PredicateTypeOneofCase.Status;
|
||||
case Columns.LastMessage: return OrderRecordTuplePredicate.PredicateTypeOneofCase.LastMessage;
|
||||
case Columns.SecurityKey: return OrderRecordTuplePredicate.PredicateTypeOneofCase.SecurityKey;
|
||||
case Columns.OrderKey: return OrderRecordTuplePredicate.PredicateTypeOneofCase.OrderKey;
|
||||
case Columns.ParentKey: return OrderRecordTuplePredicate.PredicateTypeOneofCase.ParentKey;
|
||||
case Columns.AttachedTargetOrderKey: return OrderRecordTuplePredicate.PredicateTypeOneofCase.AttachedTargetOrderKey;
|
||||
default: return default;
|
||||
}
|
||||
}
|
||||
|
||||
void IObserver<OrderBookGridSettings>.OnNext(OrderBookGridSettings value) {
|
||||
_core_man_as_observer.OnNext(value);
|
||||
algo_tree_display_mode = value.algo_tree_display_mode;
|
||||
}
|
||||
void IObserver<OrderBookGridSettings>.OnError(Exception error) => _core_man_as_observer.OnError(error);
|
||||
void IObserver<OrderBookGridSettings>.OnCompleted() => _core_man_as_observer.OnCompleted();
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
using mbts;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace bts.shell.orderbook {
|
||||
[ProtoContract]
|
||||
class OrderBookRightPanelSettings : IEquatable<OrderBookRightPanelSettings> {
|
||||
public OrderBookRightPanelSettings() {
|
||||
mode = Defaults.mode;
|
||||
stack = new StackSettings();
|
||||
tab = Defaults.tab;
|
||||
show = Defaults.show;
|
||||
width = Defaults.width;
|
||||
}
|
||||
|
||||
public enum ViewMode {
|
||||
[Description("Stack Mode")]
|
||||
Stack = 1,
|
||||
[Description("Tab Mode")]
|
||||
Tab = 1 << 1,
|
||||
}
|
||||
|
||||
public enum Components {
|
||||
None = 0,
|
||||
Params = 1,
|
||||
Ladder = 1 << 1,
|
||||
Fills = 1 << 2
|
||||
}
|
||||
|
||||
// show
|
||||
[ProtoMember(1)]
|
||||
[DefaultValue(Defaults.show)]
|
||||
public bool show { get; set; }
|
||||
// width
|
||||
[ProtoMember(2)]
|
||||
[DefaultValue(Defaults.width)]
|
||||
public double width { get; set; }
|
||||
// view mode
|
||||
[ProtoMember(3)]
|
||||
[DefaultValue(Defaults.mode)]
|
||||
public ViewMode mode { get; set; }
|
||||
// stack
|
||||
[ProtoMember(4)]
|
||||
public StackSettings stack { get; set; }
|
||||
// tab
|
||||
[ProtoMember(5)]
|
||||
[DefaultValue(Defaults.tab)]
|
||||
public Components tab { get; set; }
|
||||
|
||||
public bool Equals(OrderBookRightPanelSettings other) =>
|
||||
other != null &&
|
||||
show == other.show &&
|
||||
width == other.width &&
|
||||
mode == other.mode &&
|
||||
tab == other.tab &&
|
||||
stack.safe_equals(other.stack);
|
||||
|
||||
[ProtoContract]
|
||||
public class StackSettings : IEquatable<StackSettings> {
|
||||
// show order params
|
||||
[ProtoMember(1)]
|
||||
[DefaultValue(Defaults.show_order_params)]
|
||||
public bool show_order_params { get; set; }
|
||||
// order params height
|
||||
[ProtoMember(2)]
|
||||
[DefaultValue(Defaults.order_params_height)]
|
||||
public double order_params_height { get; set; }
|
||||
// show ladder
|
||||
[ProtoMember(3)]
|
||||
[DefaultValue(Defaults.show_fills)]
|
||||
public bool show_ladder { get; set; }
|
||||
// ladder height
|
||||
[ProtoMember(4)]
|
||||
[DefaultValue(Defaults.ladder_height)]
|
||||
public double ladder_height { get; set; }
|
||||
// show fills
|
||||
[ProtoMember(5)]
|
||||
[DefaultValue(Defaults.show_fills)]
|
||||
public bool show_fills { get; set; }
|
||||
// fills height
|
||||
[ProtoMember(6)]
|
||||
[DefaultValue(Defaults.fills_height)]
|
||||
public double fills_height { get; set; }
|
||||
// fills grid
|
||||
[ProtoMember(7)]
|
||||
public OrderBookFillsGridSettings fills_grid { get; set; }
|
||||
|
||||
public StackSettings() {
|
||||
show_order_params = Defaults.show_order_params;
|
||||
order_params_height = Defaults.order_params_height;
|
||||
show_fills = Defaults.show_fills;
|
||||
fills_height = Defaults.fills_height;
|
||||
show_ladder = Defaults.show_ladder;
|
||||
ladder_height = Defaults.ladder_height;
|
||||
}
|
||||
|
||||
public bool Equals(StackSettings other) =>
|
||||
other != null &&
|
||||
show_ladder == other.show_ladder &&
|
||||
show_order_params == other.show_order_params &&
|
||||
show_fills == other.show_fills;
|
||||
|
||||
static class Defaults {
|
||||
public const bool
|
||||
show_order_params = true,
|
||||
show_ladder = true,
|
||||
show_fills = true;
|
||||
public const double
|
||||
right_panel_width = 400,
|
||||
order_params_height = 250,
|
||||
fills_height = 300,
|
||||
ladder_height = 350;
|
||||
}
|
||||
}
|
||||
|
||||
static class Defaults {
|
||||
public const ViewMode mode = ViewMode.Tab;
|
||||
public const Components tab = Components.Params;
|
||||
public const bool show = true;
|
||||
public const double width = 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
using bts.api.orderman;
|
||||
using mbts;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using Columns = mbts.spark.OrderBookGridView.ColumnKeys;
|
||||
using GridSettings = mbts.ui.settings.GridSettings;
|
||||
|
||||
namespace bts.shell.orderbook {
|
||||
[ProtoContract]
|
||||
class OrderBookStartupData {
|
||||
const bool
|
||||
_default_show_right_panel = true,
|
||||
_default_show_order_params = true;
|
||||
const double
|
||||
_default_right_panel_width = 400,
|
||||
_default_order_params_height = 250,
|
||||
_default_fills_height = 300,
|
||||
_default_ladder_height = 350;
|
||||
[ProtoMember(16)]
|
||||
OrderBookRightPanelSettings _right_panel;
|
||||
// ALREADY USED TAGS
|
||||
// [ProtoMember(5)]
|
||||
// public readonly List<SortingCriteria> sort_criteria;
|
||||
|
||||
public OrderBookRightPanelSettings right_panel => _right_panel ?? (_right_panel = new OrderBookRightPanelSettings());
|
||||
/// TODO: remove properties below, and redirect callers to <see cref="right_panel"/>
|
||||
// right panel (host)
|
||||
public bool show_right_panel { get => right_panel.show; set => right_panel.show = value; }
|
||||
public double right_panel_width { get => right_panel.width; set => right_panel.width = value; }
|
||||
// order params
|
||||
public bool show_order_params { get => right_panel.stack.show_order_params; set => right_panel.stack.show_order_params = value; }
|
||||
public double order_params_height { get; set; }
|
||||
// ladder
|
||||
public bool show_ladder { get; set; }
|
||||
public double ladder_height { get => right_panel.stack.ladder_height; set => right_panel.stack.ladder_height = value; }
|
||||
// fills
|
||||
public bool show_fills { get => right_panel.stack.show_fills; set => right_panel.stack.show_fills = value; }
|
||||
public double fills_height { get => right_panel.stack.fills_height; set => right_panel.stack.fills_height = value; }
|
||||
|
||||
// filters
|
||||
[ProtoMember(10)]
|
||||
public FilterStates filters;
|
||||
// grid
|
||||
[ProtoMember(15)]
|
||||
[Obsolete]
|
||||
public GridSettings grid { get; set; }
|
||||
// orderbook grid
|
||||
[ProtoMember(17)]
|
||||
public OrderBookGridSettings orderbook_grid { get; set; }
|
||||
// fills settings
|
||||
[ProtoMember(26)]
|
||||
public OrderBookFillsGridSettings fills_settings { get; set; }
|
||||
// predicate animations
|
||||
[ProtoMember(20)]
|
||||
[Obsolete]
|
||||
List<OrderRecordTuplePredicateAnimations> _predicate_animations;
|
||||
// use defaults
|
||||
[ProtoMember(25)]
|
||||
public bool use_defaults;
|
||||
|
||||
/// <summary>
|
||||
/// Please use <see cref="Default"/> to get a new instance (the only reason the default constructor still exists is for ProtoBuf deserialization).
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public OrderBookStartupData() {
|
||||
show_right_panel = _default_show_right_panel;
|
||||
show_order_params = _default_show_order_params;
|
||||
right_panel_width = _default_right_panel_width;
|
||||
order_params_height = _default_order_params_height;
|
||||
fills_height = _default_fills_height;
|
||||
ladder_height = _default_ladder_height;
|
||||
}
|
||||
|
||||
public OrderBookStartupData(FilterStates filters, OrderBookGridSettings orderbook_grid = default, bool show_right_panel = false, double right_panel_width = 0, double fills_height = 0, bool show_fills = false, bool use_defaults = false) {
|
||||
this.filters = filters;
|
||||
this.use_defaults = use_defaults;
|
||||
this.show_right_panel = show_right_panel;
|
||||
this.right_panel_width = right_panel_width;
|
||||
this.fills_height = fills_height;
|
||||
this.show_fills = show_fills;
|
||||
this.orderbook_grid = orderbook_grid;
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
_predicate_animations = new List<OrderRecordTuplePredicateAnimations>();
|
||||
if (predicate_animations != null)
|
||||
_predicate_animations.AddRange(predicate_animations);
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
}
|
||||
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
[Obsolete]
|
||||
public IReadOnlyList<OrderRecordTuplePredicateAnimations> predicate_animations => _predicate_animations ?? (_predicate_animations = new List<OrderRecordTuplePredicateAnimations>());
|
||||
public OrderBookGridSettings grid_settings_wrapper() {
|
||||
if (use_defaults)
|
||||
return null;
|
||||
if (orderbook_grid == null) {
|
||||
if (grid == null)
|
||||
grid = _default_general_grid;
|
||||
orderbook_grid = new OrderBookGridSettings(grid.clone(), _predicate_animations);
|
||||
}
|
||||
return orderbook_grid;
|
||||
}
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
|
||||
public static OrderBookStartupData Default =>
|
||||
new OrderBookStartupData(
|
||||
new FilterStates { statuses = new[] { OrderStatus.Working } },
|
||||
DefaultGrid,
|
||||
_default_show_right_panel,
|
||||
_default_right_panel_width,
|
||||
_default_fills_height,
|
||||
use_defaults: true) {
|
||||
show_order_params = _default_show_order_params,
|
||||
order_params_height = _default_order_params_height,
|
||||
fills_settings = DefaultFillsGrid
|
||||
};
|
||||
|
||||
public static OrderBookGridSettings DefaultGrid => new OrderBookGridSettings(_default_general_grid) {
|
||||
algo_tree_display_mode = AlgoTreeDisplayMode.RootAndLeafNodes
|
||||
};
|
||||
|
||||
public static OrderBookFillsGridSettings DefaultFillsGrid {
|
||||
get {
|
||||
var default_fills = FillsWindowFactory.StartupData.Default();
|
||||
var rv = new OrderBookFillsGridSettings {
|
||||
sorting_criterion = default_fills.sorting_criterion.ToList(),
|
||||
filters = default_fills.filters,
|
||||
general = FillsWindowFactory.StartupData.MiniGridSettings
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
static GridSettings _default_general_grid => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion);
|
||||
public static OrderBookGridSettings default_mini_grid => new OrderBookGridSettings(GridSettings.Default.with(DefaultColumns.mini, DefaultColumns.sorting_criterion)) {
|
||||
algo_tree_display_mode = AlgoTreeDisplayMode.RootAndLeafNodes
|
||||
};
|
||||
|
||||
static class DefaultColumns {
|
||||
public static GridColumnSettings security => new GridColumnSettings(Columns.Security.ToString(), DIYGrid.DefaultColumnHeaders.Security, width: DIYGrid.DefaultColumnWidths.Security);
|
||||
public static GridColumnSettings symbol => new GridColumnSettings(Columns.Symbol.ToString(), DIYGrid.DefaultColumnHeaders.Symbol, width: DIYGrid.DefaultColumnWidths.Symbol, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings time_created => new GridColumnSettings(Columns.Time.ToString(), "Time", width: DIYGrid.DefaultColumnWidths.Time, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings last_change => new GridColumnSettings(Columns.LastChange.ToString(), "Last Change", width: DIYGrid.DefaultColumnWidths.Time, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings user => new GridColumnSettings(Columns.User.ToString(), DIYGrid.DefaultColumnHeaders.User, width: DIYGrid.DefaultColumnWidths.User, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings account => new GridColumnSettings(Columns.Account.ToString(), DIYGrid.DefaultColumnHeaders.Account, width: DIYGrid.DefaultColumnWidths.Account, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings exchange => new GridColumnSettings(Columns.Exchange.ToString(), DIYGrid.DefaultColumnHeaders.Exchange, width: DIYGrid.DefaultColumnWidths.Exchange, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings time_in_force => new GridColumnSettings(Columns.TimeInForce.ToString(), DIYGrid.DefaultColumnHeaders.TimeInForce, width: DIYGrid.DefaultColumnWidths.TimeInForce);
|
||||
public static GridColumnSettings order_type => new GridColumnSettings(Columns.OrderType.ToString(), DIYGrid.DefaultColumnHeaders.OrderType, width: DIYGrid.DefaultColumnWidths.OrderType);
|
||||
public static GridColumnSettings root_algo_type => new GridColumnSettings(Columns.RootAlgoType.ToString(), DIYGrid.DefaultColumnHeaders.RootAlgoType, width: DIYGrid.DefaultColumnWidths.OrderType);
|
||||
public static GridColumnSettings sub_type => new GridColumnSettings(Columns.SubType.ToString(), "Sub Type", width: 80, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings side => new GridColumnSettings(Columns.Side.ToString(), "B/S", width: DIYGrid.DefaultColumnWidths.Side, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings quantity => new GridColumnSettings(Columns.Size.ToString(), "Qty", width: DIYGrid.DefaultColumnWidths.Size, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings price => new GridColumnSettings(Columns.Price.ToString(), DIYGrid.DefaultColumnHeaders.Price, width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings filled_quantity => new GridColumnSettings(Columns.Filled.ToString(), "Filled Qty", width: DIYGrid.DefaultColumnWidths.Filled, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings avg_price => new GridColumnSettings(Columns.AvgFilled.ToString(), "Avg Filled", width: DIYGrid.DefaultColumnWidths.AveragePrice, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings last_price => new GridColumnSettings(Columns.LastPrice.ToString(), "Last Filled", width: DIYGrid.DefaultColumnWidths.LastPrice, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings status => new GridColumnSettings(Columns.Status.ToString(), "Status", width: 80, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings last_message => new GridColumnSettings(Columns.LastMessage.ToString(), "Last Message", width: DIYGrid.DefaultColumnWidths.LastMessage, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, is_visible: false, width: DIYGrid.DefaultColumnWidths.SecurityKey, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings order_key => new GridColumnSettings(Columns.OrderKey.ToString(), DIYGrid.DefaultColumnHeaders.OrderKey, width: DIYGrid.DefaultColumnWidths.OrderKey, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings parent_key => new GridColumnSettings(Columns.ParentKey.ToString(), "Parent Key", width: DIYGrid.DefaultColumnWidths.ParentKey, horizontal_alignment: HorizontalAlignment.Left);
|
||||
public static GridColumnSettings attached_target_order_key => new GridColumnSettings(Columns.AttachedTargetOrderKey.ToString(), DIYGrid.DefaultColumnHeaders.AttachedTargetOrderKey, width: DIYGrid.DefaultColumnWidths.ParentKey, horizontal_alignment: HorizontalAlignment.Left);
|
||||
|
||||
public static IEnumerable<SortingCriteria> sorting_criterion {
|
||||
get {
|
||||
yield return new SortingCriteria(time_created.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GridColumnSettings> all {
|
||||
get {
|
||||
yield return security;
|
||||
yield return symbol;
|
||||
yield return time_created;
|
||||
yield return last_change;
|
||||
yield return user;
|
||||
yield return account;
|
||||
yield return exchange;
|
||||
yield return time_in_force;
|
||||
yield return order_type;
|
||||
yield return root_algo_type;
|
||||
yield return sub_type;
|
||||
yield return side;
|
||||
yield return quantity;
|
||||
yield return price;
|
||||
yield return filled_quantity;
|
||||
yield return avg_price;
|
||||
yield return last_price;
|
||||
yield return status;
|
||||
yield return last_message;
|
||||
yield return security_key;
|
||||
yield return order_key;
|
||||
yield return parent_key;
|
||||
yield return attached_target_order_key;
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<GridColumnSettings> _mini_mode_columns_to_hide = new HashSet<GridColumnSettings> {
|
||||
symbol, last_change, exchange, sub_type, quantity, price, avg_price, last_price, status, last_message, security_key, order_key, parent_key, attached_target_order_key
|
||||
};
|
||||
|
||||
public static IEnumerable<GridColumnSettings> mini {
|
||||
get {
|
||||
foreach (var col_setting in all) {
|
||||
if (_mini_mode_columns_to_hide.Contains(col_setting)) {
|
||||
yield return col_setting.with(is_visible: false);
|
||||
} else {
|
||||
yield return col_setting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.spark;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace bts.shell.orderbook {
|
||||
class OrderBookSettingsVM : DisposableViewModelBase, IWindowSettingsVM<OrderBookGridSettings>, ITemplateSelectorKey {
|
||||
AlgoTreeDisplayMode _algo_tree_display_mode;
|
||||
|
||||
public OrderBookSettingsVM(GridSettingsVM<OrderSecurity, OrderRecordTuplePredicate, OrderBookGridSettings> core_settings_vm) =>
|
||||
this.core_settings_vm = core_settings_vm;
|
||||
|
||||
public OrderBookSettingsVM(
|
||||
IObservable<string> owner_title, IBus bus, OrdersModel orders_model,
|
||||
Func<OrderRecordTuplePredicate> predicate_data_factory,
|
||||
Func<OrderRecordTuplePredicate, INotificationPredicate<OrderSecurity>> notification_predicate_factory,
|
||||
Func<IPropertyWrapperVM<OrderRecordTuplePredicate>> create_predicate_vm,
|
||||
Func<string, IPropertyWrapperVM<OrderRecordTuplePredicate>> create_column_predicate_vm,
|
||||
Func<GridSettings, IEnumerable<IPredicateAnimationsData<OrderSecurity, OrderRecordTuplePredicate>>, OrderBookGridSettings> create_settings_obj,
|
||||
IReadOnlyDictionary<string, GridColumnSettingsFeatureFlags> column_flags = null) {
|
||||
core_settings_vm = new GridSettingsVM<OrderSecurity, OrderRecordTuplePredicate, OrderBookGridSettings>(
|
||||
owner_title, bus, orders_model, predicate_data_factory, notification_predicate_factory, create_predicate_vm, create_column_predicate_vm, create_settings_obj, column_flags);
|
||||
}
|
||||
|
||||
public GridSettingsVM<OrderSecurity, OrderRecordTuplePredicate, OrderBookGridSettings> core_settings_vm { get; }
|
||||
public AlgoTreeDisplayMode algo_tree_display_mode { get => _algo_tree_display_mode; set => Set(ref _algo_tree_display_mode, value); }
|
||||
string ITemplateSelectorKey.key => GridSettingsTemplateKeys.OrderBook;
|
||||
|
||||
public void load(OrderBookGridSettings grid_settings) {
|
||||
core_settings_vm.load(grid_settings);
|
||||
algo_tree_display_mode = grid_settings.algo_tree_display_mode;
|
||||
}
|
||||
public OrderBookGridSettings get_result() {
|
||||
var gs = core_settings_vm.get_result();
|
||||
gs.algo_tree_display_mode = algo_tree_display_mode;
|
||||
return gs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.controls.adorners;
|
||||
using mbts.ui.extensions;
|
||||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class OrderBookView {
|
||||
public class OrderBookAttachmentTargetAdorner : DIYGridRowHighlighterAdorner, IDisposable {
|
||||
IDisposable _attach_op;
|
||||
bool _is_active, _is_disposed;
|
||||
|
||||
#region IsActive
|
||||
public static readonly DependencyProperty IsActiveProperty =
|
||||
WPFHelper.create_dp<bool, OrderBookAttachmentTargetAdorner>(
|
||||
nameof(IsActive),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, v) => {
|
||||
o._is_active = v;
|
||||
Disposable.dispose(ref o._attach_op);
|
||||
if (v && !o._is_disposed) {
|
||||
o.IsHitTestVisible = true;
|
||||
o._attach_op = Disposable.Create(
|
||||
o.SubscribePreviewMouseDown((s, e) => {
|
||||
if (e.ChangedButton == MouseButton.Left) {
|
||||
if (e.ClickCount > 1 && o.TargetRow is OrderBookRowVM r) {
|
||||
e.Handled = true;
|
||||
o._raise_target_row_selected(r);
|
||||
}
|
||||
}
|
||||
}),
|
||||
o.AdornedElement.SubscribeMouseHoverRow<OrderBookRowVM>().Subscribe(row => o.TargetRow = row));
|
||||
} else {
|
||||
o.IsHitTestVisible = false;
|
||||
o.TargetRow = null;
|
||||
}
|
||||
},
|
||||
false);
|
||||
|
||||
public bool IsActive {
|
||||
get => (bool)GetValue(IsActiveProperty);
|
||||
set => SetValue(IsActiveProperty, value);
|
||||
}
|
||||
|
||||
public event Action<OrderBookRowVM> TargetRowSelected;
|
||||
#endregion
|
||||
|
||||
public OrderBookAttachmentTargetAdorner(DIYGrid grid) : base(grid) { }
|
||||
|
||||
public void Dispose() {
|
||||
if (!_is_disposed) {
|
||||
_is_disposed = true;
|
||||
Disposable.dispose(ref _attach_op);
|
||||
}
|
||||
}
|
||||
|
||||
void _raise_target_row_selected(OrderBookRowVM row) => TargetRowSelected?.Invoke(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
<c:DIYGrid
|
||||
x:Class="mbts.spark.OrderBookGridView"
|
||||
x:ClassModifier="internal"
|
||||
x:Name="_grid"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
InactiveSelectionOpacity="0.3"
|
||||
>
|
||||
<c:DIYGrid.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!--Primitive Resources (Brushes, Colors, etc.)-->
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF27C7F7"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="#FFCCEEFB"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF1CC4F7"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF82DFFB"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
|
||||
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
|
||||
<!--Converters-->
|
||||
<uspcv:OrderSubTypeDisplayTextConverter x:Key="OrderSubTypeLongDisplayTextConverter" UseLongName="True" />
|
||||
<!--DataTemplates-->
|
||||
<DataTemplate x:Key="{x:Static sp:OrderBookGridView.SubTypeFilterTemplateKey}">
|
||||
<usp:FilterContentControl
|
||||
ApplyCommandParameter="{Binding}"
|
||||
ApplyCommand="{x:Static c:DataGridFilterProperties.CommitCommand}"
|
||||
ClearCommandParameter="{Binding}"
|
||||
ClearCommand="{x:Static c:DataGridFilterProperties.ClearCommand}"
|
||||
>
|
||||
<c:ListBoxEx
|
||||
ItemsSource="{Binding items}"
|
||||
Selection="{Binding selected_items}"
|
||||
SelectionMode="Multiple"
|
||||
ItemContainerStyle="{StaticResource ListBoxItemCheckBoxStyle}"
|
||||
>
|
||||
<c:ListBoxEx.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={StaticResource OrderSubTypeLongDisplayTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
</c:ListBoxEx.ItemTemplate>
|
||||
</c:ListBoxEx>
|
||||
</usp:FilterContentControl>
|
||||
</DataTemplate>
|
||||
<!--Styles-->
|
||||
<Style x:Key="{x:Static sp:OrderBookGridView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Focusable" Value="False"/>
|
||||
<Setter Property="Width" Value="16"/>
|
||||
<Setter Property="Height" Value="16"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Border Background="Transparent" Height="16" Padding="0,5,5,5" Width="16">
|
||||
<Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="{StaticResource TreeViewItem.TreeArrow.Static.Fill}" Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="135" CenterY="3" CenterX="3"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="RenderTransform" TargetName="ExpandPath">
|
||||
<Setter.Value>
|
||||
<RotateTransform Angle="180" CenterY="3" CenterX="3"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True"/>
|
||||
<Condition Property="IsChecked" Value="True"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ToolTip}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="Content" Value="{x:Static sys:String.Empty}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
<Trigger Property="Content" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</c:DIYGrid.Resources>
|
||||
<c:DIYGrid.InputBindings>
|
||||
<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
</c:DIYGrid.InputBindings>
|
||||
</c:DIYGrid>
|
||||
@@ -1,103 +0,0 @@
|
||||
using bts.api.orderman;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.converters;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal partial class OrderBookGridView : DIYGrid {
|
||||
public const string
|
||||
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
|
||||
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
|
||||
RowStyleKey = "RowStyle",
|
||||
RowTemplateKey = "RowTemplateKey",
|
||||
CVRowStyleKey = "CVRowStyle",
|
||||
CVRowTemplateKey = "CVRowTemplateKey";
|
||||
|
||||
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
|
||||
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
|
||||
public enum ColumnKeys {
|
||||
Security = 1,
|
||||
Symbol = 1 << 1,
|
||||
Time = 1 << 2,
|
||||
LastChange = 1 << 3,
|
||||
User = 1 << 4,
|
||||
Account = 1 << 5,
|
||||
Exchange = 1 << 6,
|
||||
TimeInForce = 1 << 7,
|
||||
OrderType = 1 << 8,
|
||||
SubType = 1 << 9,
|
||||
Side = 1 << 10,
|
||||
Size = 1 << 11,
|
||||
Price = 1 << 12,
|
||||
Filled = 1 << 13,
|
||||
AvgFilled = 1 << 14,
|
||||
LastPrice = 1 << 15,
|
||||
Status = 1 << 16,
|
||||
LastMessage = 1 << 17,
|
||||
SecurityKey = 1 << 18,
|
||||
OrderKey = 1 << 19,
|
||||
ParentKey = 1 << 20,
|
||||
AttachedTargetOrderKey = 1 << 21,
|
||||
Cancel = 1 << 22,
|
||||
EditableQty = 1 << 23,
|
||||
EditablePrice = 1 << 24,
|
||||
Attach = 1 << 25,
|
||||
Detach = 1 << 26,
|
||||
Update = 1 << 27,
|
||||
Commit = 1 << 28,
|
||||
RootAlgoType = 1 << 29
|
||||
}
|
||||
|
||||
public OrderBookGridView() {
|
||||
InitializeComponent();
|
||||
var row_template_factory = new FrameworkElementFactory(typeof(OrderBookRowView));
|
||||
row_template_factory.SetValue(OrderBookRowView.BackgroundProperty, RowBackgroundHelper.row_background_binding);
|
||||
RowTemplate = new DataTemplate { VisualTree = row_template_factory };
|
||||
}
|
||||
|
||||
static class RowBackgroundHelper {
|
||||
public static readonly BindingBase row_background_binding;
|
||||
|
||||
static RowBackgroundHelper() {
|
||||
var bg_binding = new MultiBinding();
|
||||
bg_binding.Bindings.Add(new Binding(nameof(OrderBookRowVM.status)));
|
||||
bg_binding.Bindings.Add(new Binding(nameof(ListBoxItem.IsSelected)) { RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ListBoxItem), 1) });
|
||||
double overlay_opacity = config.Configuration.get<bts.config.OrderBookOptions>().status_color_opacity_when_selected;
|
||||
if (overlay_opacity > 0.1 && overlay_opacity < 0.9) {
|
||||
bg_binding.Converter = ValueConverter.create<OrderStatus, bool, Brush>((status, is_selected) => {
|
||||
var color = _status_color(status);
|
||||
return color.HasValue ? new SolidColorBrush(color.Value) { Opacity = is_selected ? overlay_opacity : 1.0 } : null;
|
||||
});
|
||||
} else {
|
||||
bg_binding.Converter = ValueConverter.create<OrderStatus, bool, Brush>((status, is_selected) => {
|
||||
if (is_selected)
|
||||
return null;
|
||||
var color = _status_color(status);
|
||||
return color.HasValue ? new SolidColorBrush(color.Value) : null;
|
||||
});
|
||||
}
|
||||
row_background_binding = bg_binding;
|
||||
}
|
||||
|
||||
static Color? _status_color(OrderStatus status) {
|
||||
switch (status) {
|
||||
case OrderStatus.Canceled: return BTSColors.Orders.canceled_order_background;
|
||||
case OrderStatus.Completed: return BTSColors.Orders.completed_order_background;
|
||||
case OrderStatus.Disconnected:
|
||||
case OrderStatus.RejectedBySpark:
|
||||
case OrderStatus.Error:
|
||||
return BTSColors.Orders.error_order_background;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookGridView.ColumnKeys> {
|
||||
public OrderBookRowView() : base() { }
|
||||
}
|
||||
}
|
||||
@@ -1,492 +0,0 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:ie="http://schemas.microsoft.com/expression/2010/interactions"
|
||||
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ob="clr-namespace:bts.shell.orderbook"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
>
|
||||
<!--Control colors.-->
|
||||
<Color x:Key="WindowColor">#FFE8EDF9</Color>
|
||||
|
||||
<Color x:Key="DisabledControlLightColor">#FFE8EDF9</Color>
|
||||
<Color x:Key="DisabledControlDarkColor">#FFC5CBF9</Color>
|
||||
<Color x:Key="DisabledForegroundColor">#FF888888</Color>
|
||||
|
||||
<Color x:Key="SelectedBackgroundColor">#FFC5CBF9</Color>
|
||||
<Color x:Key="SelectedUnfocusedColor">#FFDDDDDD</Color>
|
||||
|
||||
<Color x:Key="ControlLightColor">White</Color>
|
||||
<Color x:Key="ControlMediumColor">#FFCCCCCC</Color>
|
||||
<Color x:Key="ControlDarkColor">#FFAAAAAA</Color>
|
||||
<Color x:Key="ControlMouseOverColor">#200080FF</Color>
|
||||
<Color x:Key="ControlPressedColor">#800080FF</Color>
|
||||
<Color x:Key="TabLightColor">#FFE5E5E5</Color>
|
||||
<Color x:Key="TabMediumColor">#FFF0F0F0</Color>
|
||||
|
||||
<!--Border colors-->
|
||||
<Color x:Key="BorderMediumColor">#FF888888</Color>
|
||||
<Color x:Key="PressedBorderLightColor">#FF888888</Color>
|
||||
<Color x:Key="MouseOverBorderMediumColor">#90006CD9</Color>
|
||||
<Color x:Key="DisabledBorderLightColor">#FFAAAAAA</Color>
|
||||
<Color x:Key="DefaultBorderBrushDarkColor">Black</Color>
|
||||
<Color x:Key="TabBorderColor">#FFACACAC</Color>
|
||||
<Color x:Key="TabMouseOverBorderMediumColor">#FF7EB4EA</Color>
|
||||
|
||||
<!--Control-specific resources.-->
|
||||
<Color x:Key="HeaderTopColor">#FFC5CBF9</Color>
|
||||
<Color x:Key="DatagridCurrentCellBorderColor">Black</Color>
|
||||
<Color x:Key="SliderTrackDarkColor">#FFC5CBF9</Color>
|
||||
|
||||
<Color x:Key="NavButtonFrameColor">#FF3843C4</Color>
|
||||
|
||||
<Color x:Key="TabMouseOverBackgroundLightColor">#FFECF4FC</Color>
|
||||
<Color x:Key="TabMouseOverBackgroundMediumColor">#FFDCECFC</Color>
|
||||
|
||||
<LinearGradientBrush x:Key="MenuPopupBrush" EndPoint="0.5,1" StartPoint="0.5,0">
|
||||
<GradientStop Color="{DynamicResource ControlLightColor}" Offset="0"/>
|
||||
<GradientStop Color="{DynamicResource ControlMediumColor}" Offset="0.5"/>
|
||||
<GradientStop Color="{DynamicResource ControlLightColor}" Offset="1"/>
|
||||
</LinearGradientBrush>
|
||||
|
||||
<!--Converters-->
|
||||
<cv:CastConverter x:Key="CastConverter"/>
|
||||
|
||||
<Style x:Key="ToolBarButtonBaseStyle" TargetType="{x:Type ButtonBase}">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="1,0"/>
|
||||
<Setter Property="SnapsToDevicePixels" Value="True"/>
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ButtonBase}">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<EasingColorKeyFrame KeyTime="0" Value="{StaticResource ControlPressedColor}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="MouseOver">
|
||||
<Storyboard>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<EasingColorKeyFrame KeyTime="0" Value="{StaticResource ControlMouseOverColor}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<EasingColorKeyFrame KeyTime="0" Value="{StaticResource DisabledControlDarkColor}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="CheckStates">
|
||||
<VisualState x:Name="Checked">
|
||||
<Storyboard>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<EasingColorKeyFrame KeyTime="0" Value="{StaticResource ControlPressedColor}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Unchecked"/>
|
||||
<VisualState x:Name="Indeterminate"/>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="{x:Static ToolBar.ToggleButtonStyleKey}" BasedOn="{StaticResource ToolBarButtonBaseStyle}" TargetType="{x:Type ToggleButton}"/>
|
||||
|
||||
<Style x:Key="ToolBarThumbStyle" TargetType="{x:Type Thumb}">
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
<Setter Property="Cursor" Value="SizeAll"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||
<Border Background="Transparent" SnapsToDevicePixels="True">
|
||||
<Rectangle Margin="0,2">
|
||||
<Rectangle.Fill>
|
||||
<DrawingBrush
|
||||
Viewport="0,0,4,4"
|
||||
ViewportUnits="Absolute"
|
||||
Viewbox="0,0,8,8"
|
||||
ViewboxUnits="Absolute"
|
||||
TileMode="Tile"
|
||||
>
|
||||
<DrawingBrush.Drawing>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#AAA" Geometry="M 4 4 L 4 8 L 8 8 L 8 4 z"/>
|
||||
</DrawingGroup>
|
||||
</DrawingBrush.Drawing>
|
||||
</DrawingBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ToolBarOverflowButtonStyle" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
<Setter Property="ClickMode" Value="Press"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
BorderBrush="Transparent"
|
||||
Background="Transparent"
|
||||
BorderThickness="1,0"
|
||||
SnapsToDevicePixels="True"
|
||||
>
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager AttachToMembers="True">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:ModalStateDefinition Mode="{Binding Path='(u:StateDefinition.Group).(u:StateGroupsManager.Target).(Border.TemplatedParent).(ToggleButton.DataContext).(sp:OrderBookVM.right_panel_view_mode)', RelativeSource={RelativeSource Self}, Converter={StaticResource CastConverter}}">
|
||||
<u:ModalStateDefinition.Source>
|
||||
<u:MouseStateDefinition MouseSource="{Binding Path='(u:StateDefinition.Group).(u:StateGroupsManager.Target).(Border.TemplatedParent)', RelativeSource={RelativeSource Self}, Converter={StaticResource CastConverter}}"/>
|
||||
</u:ModalStateDefinition.Source>
|
||||
</u:ModalStateDefinition>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="CommonStates">
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="{StaticResource DisabledBorderLightColor}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup Name="MouseStates">
|
||||
<!--Stack-->
|
||||
<VisualState Name="StackNormal"/>
|
||||
<VisualState Name="StackMouseOver">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="{StaticResource ControlMouseOverColor}"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="{StaticResource MouseOverBorderMediumColor}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="StackPressed">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<!--Tab-->
|
||||
<VisualState Name="TabNormal"/>
|
||||
<VisualState Name="TabMouseOver">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="{StaticResource ControlMouseOverColor}"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="{StaticResource MouseOverBorderMediumColor}"/>
|
||||
<ThicknessAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.BorderThickness)" BeginTime="0" Duration="0" To="1"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Path
|
||||
x:Name="Arrow"
|
||||
Fill="Black"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="2,3"
|
||||
Data="M -0.5 3 L 5.5 3 L 2.5 6 Z"/>
|
||||
<ContentPresenter/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding right_panel_view_mode}" Value="{x:Static Tab, MemberType={u:Nested ob:OrderBookRightPanelSettings.ViewMode}}">
|
||||
<Setter TargetName="Border" Property="BorderThickness" Value="0,0,0,1"/>
|
||||
<Setter TargetName="Border" Property="BorderBrush">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="{StaticResource TabBorderColor}"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</DataTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!--MenuItem Storyboards-->
|
||||
<Storyboard x:Key="TabCheckedStoryboard">
|
||||
<!--CheckMark Opacity-->
|
||||
<DoubleAnimation Storyboard.TargetName="CheckMark" Storyboard.TargetProperty="(UIElement.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
<!--Background Opacity-->
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.Opacity)" BeginTime="0" Duration="0" To="0"/>
|
||||
<!--Border Thickness/Opacity/Color-->
|
||||
<ThicknessAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.BorderThickness)" BeginTime="0" Duration="0" To="1,1,1,0"/>
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="{StaticResource TabBorderColor}"/>
|
||||
<!--Padding-->
|
||||
<ThicknessAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.Padding)" BeginTime="0" Duration="0" To="6,4,6,5"/>
|
||||
</Storyboard>
|
||||
<Storyboard x:Key="TabMouseOverStoryboard">
|
||||
<!--Background Opacity/Color-->
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.GradientStops)[0].(GradientStop.Color)" BeginTime="0" Duration="0" To="{StaticResource TabMouseOverBackgroundLightColor}"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.GradientStops)[1].(GradientStop.Color)" BeginTime="0" Duration="0" To="{StaticResource TabMouseOverBackgroundMediumColor}"/>
|
||||
<!--Border Thickness/Opacity/Color-->
|
||||
<ThicknessAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.BorderThickness)" BeginTime="0" Duration="0" To="1"/>
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Opacity)" BeginTime="0" Duration="0" To="0.5625"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="{StaticResource TabMouseOverBorderMediumColor}"/>
|
||||
<!--Padding-->
|
||||
<ThicknessAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.Padding)" BeginTime="0" Duration="0" To="6,2"/>
|
||||
</Storyboard>
|
||||
|
||||
<Style x:Key="RightPanel.MenuItem" TargetType="MenuItem">
|
||||
<Setter Property="c:MenuItemEx.ClickMode" Value="Press"/>
|
||||
<Setter Property="BorderThickness" Value="1,0"/>
|
||||
<Setter Property="IsCheckable" Value="True"/>
|
||||
<Setter Property="Focusable" Value="False"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="MenuItem">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Padding="4,1,5,1"
|
||||
Height="{Binding Padding, RelativeSource={RelativeSource Self}, Converter={x:Static sp:OrderBookView.ToolBarMenuItemHeightConverter}}"
|
||||
MinHeight="{TemplateBinding MinHeight}"
|
||||
VerticalAlignment="Bottom"
|
||||
>
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager AttachToMembers="True">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:MouseStateDefinition MouseSource="{Binding Path='(u:StateDefinition.Group).(u:StateGroupsManager.Target).(Border.TemplatedParent)', RelativeSource={RelativeSource Self}, Converter={StaticResource CastConverter}}">
|
||||
<u:MouseStateDefinition.Mode>
|
||||
<MultiBinding>
|
||||
<MultiBinding.Converter>
|
||||
<cv:ConcatConverter/>
|
||||
</MultiBinding.Converter>
|
||||
<Binding Path="(u:StateDefinition.Group).(u:StateGroupsManager.Target).(Border.TemplatedParent).(MenuItem.DataContext).(sp:OrderBookVM.right_panel_view_mode)" RelativeSource="{RelativeSource Self}" Converter="{x:Static cv:ValueConverters.SimpleString}"/>
|
||||
<Binding Path="(u:StateDefinition.Group).(u:StateGroupsManager.Target).(Border.TemplatedParent).(MenuItem.IsChecked)" RelativeSource="{RelativeSource Self}">
|
||||
<Binding.Converter>
|
||||
<cv:BooleanToObjectConverter ValueForTrue="Checked" ValueForFalse="Unchecked"/>
|
||||
</Binding.Converter>
|
||||
</Binding>
|
||||
</MultiBinding>
|
||||
</u:MouseStateDefinition.Mode>
|
||||
</u:MouseStateDefinition>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<Border.BorderBrush>
|
||||
<SolidColorBrush Color="#006CD9" Opacity="0"/>
|
||||
</Border.BorderBrush>
|
||||
<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1" Opacity="0">
|
||||
<GradientStop Color="#400080FF" Offset="0.0"/>
|
||||
<GradientStop Color="#400080FF" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
</Border.Background>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<!--Stack-->
|
||||
<VisualState Name="StackUncheckedNormal"/>
|
||||
<VisualState Name="StackUncheckedMouseOver">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Opacity)" BeginTime="0" Duration="0" To="0.5625"/>
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.Opacity)" BeginTime="0" Duration="0" To="0.5"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="StackUncheckedPressed">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="StackCheckedNormal">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="CheckMark" Storyboard.TargetProperty="(UIElement.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.GradientStops)[0].(GradientStop.Color)" BeginTime="0" Duration="0" To="#800080FF"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.GradientStops)[1].(GradientStop.Color)" BeginTime="0" Duration="0" To="#800080FF"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="StackCheckedMouseOver">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="CheckMark" Storyboard.TargetProperty="(UIElement.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.GradientStops)[0].(GradientStop.Color)" BeginTime="0" Duration="0" To="#800080FF"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.GradientStops)[1].(GradientStop.Color)" BeginTime="0" Duration="0" To="#800080FF"/>
|
||||
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Opacity)" BeginTime="0" Duration="0" To="0.5625"/>
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.Opacity)" BeginTime="0" Duration="0" To="0.5"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="StackCheckedPressed">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="CheckMark" Storyboard.TargetProperty="(UIElement.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<!--Tab-->
|
||||
<VisualState Name="TabUncheckedNormal">
|
||||
<Storyboard>
|
||||
<!--Background Opacity/Color-->
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.GradientStops)[0].(GradientStop.Color)" BeginTime="0" Duration="0" To="{StaticResource TabMediumColor}"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.Background).(LinearGradientBrush.GradientStops)[1].(GradientStop.Color)" BeginTime="0" Duration="0" To="{StaticResource TabLightColor}"/>
|
||||
<!--Border Thickness/Opacity/Color-->
|
||||
<ThicknessAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.BorderThickness)" BeginTime="0" Duration="0" To="1"/>
|
||||
<DoubleAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Opacity)" BeginTime="0" Duration="0" To="1"/>
|
||||
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="{StaticResource TabBorderColor}"/>
|
||||
<ThicknessAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.Padding)" BeginTime="0" Duration="0" To="6,2"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="TabUncheckedMouseOver" Storyboard="{StaticResource TabMouseOverStoryboard}"/>
|
||||
<VisualState Name="TabUncheckedPressed" Storyboard="{StaticResource TabMouseOverStoryboard}"/>
|
||||
<VisualState Name="TabCheckedNormal" Storyboard="{StaticResource TabCheckedStoryboard}"/>
|
||||
<VisualState Name="TabCheckedMouseOver" Storyboard="{StaticResource TabCheckedStoryboard}"/>
|
||||
<VisualState Name="TabCheckedPressed" Storyboard="{StaticResource TabCheckedStoryboard}"/>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image x:Name="CheckMark" VerticalAlignment="Bottom" Height="9.5" Margin="0,0,0,3" Opacity="0">
|
||||
<Image.Source>
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<GeometryDrawing Brush="Black" Geometry="{Binding Source={StaticResource {x:Static u:Icons.CheckMarkGeometryKey}}}"/>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<Image Grid.Column="1" Margin="{DynamicResource IconPadding}" Source="{TemplateBinding Icon}"/>
|
||||
<ContentPresenter
|
||||
Grid.Column="2"
|
||||
TextElement.FontWeight="SemiBold"
|
||||
Margin="4,0,0,0"
|
||||
ContentSource="Header"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="{x:Type ToolBar}" TargetType="{x:Type ToolBar}">
|
||||
<Setter Property="ToolBarTray.IsLocked" Value="True"/>
|
||||
<Setter Property="Background" Value="LightGray"/>
|
||||
<Setter Property="BorderBrush" Value="DarkGray"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="SnapsToDevicePixels" Value="True"/>
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
<Setter Property="UseLayoutRounding" Value="True"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToolBar}">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
>
|
||||
<DockPanel>
|
||||
<ToggleButton
|
||||
DockPanel.Dock="Right"
|
||||
IsChecked="{Binding IsOverflowOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Style="{StaticResource ToolBarOverflowButtonStyle}"
|
||||
>
|
||||
<Popup
|
||||
x:Name="OverflowPopup"
|
||||
AllowsTransparency="True"
|
||||
Placement="Bottom"
|
||||
StaysOpen="False"
|
||||
Focusable="False"
|
||||
PopupAnimation="Slide"
|
||||
IsOpen="{Binding IsOverflowOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
>
|
||||
<Border
|
||||
x:Name="DropDownBorder"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{u:BTSColor UI.Button.normal_border}"
|
||||
Background="{StaticResource ControlBackground}"
|
||||
>
|
||||
<i:Interaction.Triggers>
|
||||
<i:EventTrigger EventName="MouseLeave">
|
||||
<ie:ChangePropertyAction TargetName="OverflowPopup" PropertyName="IsOpen" Value="False"/>
|
||||
</i:EventTrigger>
|
||||
</i:Interaction.Triggers>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<MenuItem
|
||||
c:RadioButtonGroupManager.Manager="{Binding Path='(c:RadioButtonGroupManager.Manager)', RelativeSource={RelativeSource AncestorType=ToolBar}}"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsCheckable="True"
|
||||
DataContext="{u:Enum Tab, Type={u:Nested ob:OrderBookRightPanelSettings.ViewMode}}"
|
||||
Header="{Binding}"
|
||||
HeaderTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
/>
|
||||
<MenuItem
|
||||
c:RadioButtonGroupManager.Manager="{Binding Path='(c:RadioButtonGroupManager.Manager)', RelativeSource={RelativeSource AncestorType=ToolBar}}"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsCheckable="True"
|
||||
DataContext="{u:Enum Stack, Type={u:Nested ob:OrderBookRightPanelSettings.ViewMode}}"
|
||||
Header="{Binding}"
|
||||
HeaderTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Popup>
|
||||
</ToggleButton>
|
||||
<Thumb x:Name="ToolBarThumb" Style="{StaticResource ToolBarThumbStyle}" Width="10"/>
|
||||
<ToolBarPanel x:Name="PART_ToolBarPanel" IsItemsHost="True"/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsOverflowOpen" Value="True">
|
||||
<Setter TargetName="ToolBarThumb" Property="IsEnabled" Value="False"/>
|
||||
</Trigger>
|
||||
<Trigger Property="ToolBarTray.IsLocked" Value="True">
|
||||
<Setter TargetName="ToolBarThumb" Property="Visibility" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="{x:Type ToolBarTray}" TargetType="{x:Type ToolBarTray}">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
|
||||
<LinearGradientBrush.GradientStops>
|
||||
<GradientStopCollection>
|
||||
<GradientStop Color="#FFF" Offset="0.0"/>
|
||||
<GradientStop Color="{DynamicResource WindowColor}" Offset="1.0"/>
|
||||
</GradientStopCollection>
|
||||
</LinearGradientBrush.GradientStops>
|
||||
</LinearGradientBrush>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -1,594 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="mbts.spark.OrderBookView"
|
||||
x:ClassModifier="internal"
|
||||
x:Name="Root"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:api="clr-namespace:bts.api;assembly=spark_api"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:ut="clr-namespace:mbts.ui.utilities"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:ob="clr-namespace:bts.shell.orderbook"
|
||||
Grid.Row="0"
|
||||
FocusManager.IsFocusScope="True"
|
||||
Background="{StaticResource CommonBackground}"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
|
||||
<ResourceDictionary Source="/Modules/Trading/OrderBook/View/OrderParamsPanel.xaml"/>
|
||||
<ResourceDictionary Source="/Modules/Trading/OrderBook/View/OrderBookToolBar.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<sys:Double x:Key="GridSplitterWidth">5</sys:Double>
|
||||
<sys:Double x:Key="GridSplitterAndMenuExpanderWidth">10</sys:Double>
|
||||
<SolidColorBrush x:Key="AlgoAttachmentBackground" Color="#55009500"/>
|
||||
<cv:GridLengthConverter x:Key="GridLengthPixelConverter" UnitType="Pixel"/>
|
||||
<cv:GridLengthConverter x:Key="GridLengthStarConverter" UnitType="Star"/>
|
||||
<cv:IntSubtractionConverter x:Key="MinusOneConverter" Value="1"/>
|
||||
<cv:ValueConverterPipe x:Key="SelectedOrdersAndOthersSuffixVisibilityConverter">
|
||||
<cv:IntGreaterThanConverter Value="1"/>
|
||||
<BooleanToVisibilityConverter/>
|
||||
</cv:ValueConverterPipe>
|
||||
<cv:ValueMap x:Key="SideToBrushConverter" Default="{u:Brush Black}">
|
||||
<cv:KeyValue Key="{u:Enum Buy, Type=api:Side}" Value="{x:Static bid_brush, MemberType={u:Nested u:BTSColors.MarketData}}"/>
|
||||
<cv:KeyValue Key="{u:Enum Sell, Type=api:Side}" Value="{x:Static ask_brush, MemberType={u:Nested u:BTSColors.MarketData}}"/>
|
||||
</cv:ValueMap>
|
||||
<cv:ValueMap x:Key="OrderParamsVisibilityConverter" Default="{x:Static Visibility.Visible}">
|
||||
<cv:KeyValue Key="{u:Int 0}" Value="{x:Static Visibility.Hidden}"/>
|
||||
</cv:ValueMap>
|
||||
|
||||
<Style TargetType="GridSplitter">
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{StaticResource Control.MediumGrayBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Control.MediumGrayBrush}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="{x:Static usp:OrderParamsPanel.BestBidPriceButtonStyleKey}" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="ToolTip">
|
||||
<Setter.Value>
|
||||
<ToolTip DataContext="{Binding PlacementTarget.Parent.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
<TextBlock Text="{Binding security, StringFormat='Best Bid Price ({0})'}"/>
|
||||
</ToolTip>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="{x:Static usp:OrderParamsPanel.BestBidSizeButtonStyleKey}" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Command" Value="{Binding DataContext.size_calculator.set_value_command, RelativeSource={RelativeSource AncestorType=usp:OrderParamsPanel}}"/>
|
||||
<Setter Property="CommandParameter" Value="{Binding BestBidSize, RelativeSource={RelativeSource AncestorType=usp:OrderParamsPanel}}"/>
|
||||
<Setter Property="ToolTip">
|
||||
<Setter.Value>
|
||||
<ToolTip DataContext="{Binding PlacementTarget.Parent.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
<TextBlock Text="{Binding security, StringFormat='Best Bid Size ({0})'}"/>
|
||||
</ToolTip>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="{x:Static usp:OrderParamsPanel.BestAskPriceButtonStyleKey}" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="ToolTip">
|
||||
<Setter.Value>
|
||||
<ToolTip DataContext="{Binding PlacementTarget.Parent.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
<TextBlock Text="{Binding security, StringFormat='Best Ask Price ({0})'}"/>
|
||||
</ToolTip>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="{x:Static usp:OrderParamsPanel.BestAskSizeButtonStyleKey}" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Command" Value="{Binding DataContext.size_calculator.set_value_command, RelativeSource={RelativeSource AncestorType=usp:OrderParamsPanel}}"/>
|
||||
<Setter Property="CommandParameter" Value="{Binding BestAskSize, RelativeSource={RelativeSource AncestorType=usp:OrderParamsPanel}}"/>
|
||||
<Setter Property="ToolTip">
|
||||
<Setter.Value>
|
||||
<ToolTip DataContext="{Binding PlacementTarget.Parent.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
<TextBlock Text="{Binding security, StringFormat='Best Ask Size ({0})'}"/>
|
||||
</ToolTip>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="{x:Static usp:OrderParamsPanel.SizeCalculatorButtonStyleKey}" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Command" Value="{Binding DataContext.size_calculator.click, RelativeSource={RelativeSource AncestorType=usp:OrderParamsPanel}}"/>
|
||||
<Setter Property="CommandParameter" Value="{Binding}"/>
|
||||
</Style>
|
||||
<DataTemplate x:Key="OrderDescriptionTemplate">
|
||||
<TextBlock>
|
||||
<TextBlock.Inlines>
|
||||
<Run Text="{Binding security, Mode=OneWay}" FontWeight="Bold"/>
|
||||
<Run Text="{Binding side, Mode=OneWay}" Foreground="{Binding side, Converter={StaticResource SideToBrushConverter}}"/>
|
||||
<Run Text="{Binding quantity, Mode=OneWay}"/>
|
||||
<Run>@</Run>
|
||||
<Run>
|
||||
<Run.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="price" Mode="OneWay"/>
|
||||
<Binding Path="security.ContractSpec" Mode="OneWay"/>
|
||||
</MultiBinding>
|
||||
</Run.Text>
|
||||
</Run>
|
||||
<Run Text="{Binding filled_status_desc, Mode=OneWay, StringFormat='({0})'}"/>
|
||||
</TextBlock.Inlines>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<AdornerDecorator>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.show_right_panel, Source={u:DirectReference ContentRoot}, Converter={x:Static sp:OrderBookView.RightPanelVisualStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested sp:OrderBookView.VisualStateNames.RightPanel}}">
|
||||
<!--Right Panel Closed-->
|
||||
<VisualState Name="{x:Static Inactive, MemberType={u:Nested sp:OrderBookView.VisualStateNames.RightPanel}}"/>
|
||||
<!--Right Panel Open-->
|
||||
<VisualState Name="{x:Static Active, MemberType={u:Nested sp:OrderBookView.VisualStateNames.RightPanel}}">
|
||||
<Storyboard>
|
||||
<!--Orders Grid-->
|
||||
<Int32Animation Storyboard.TargetName="orders_grid" Storyboard.TargetProperty="(Grid.ColumnSpan)" BeginTime="0" Duration="0" To="1"/>
|
||||
<!--Right Panel-->
|
||||
<DoubleAnimation Storyboard.TargetName="right_panel_expander_toggle" Storyboard.TargetProperty="(FrameworkElement.Width)" BeginTime="0" Duration="0" To="{StaticResource GridSplitterWidth}"/>
|
||||
<Int32Animation Storyboard.TargetName="right_panel_expander_toggle" Storyboard.TargetProperty="(Grid.Column)" BeginTime="0" Duration="0" To="1"/>
|
||||
<ThicknessAnimation Storyboard.TargetName="right_panel_expander_toggle" Storyboard.TargetProperty="(FrameworkElement.Margin)" BeginTime="0" Duration="0">
|
||||
<ThicknessAnimation.To>
|
||||
<Thickness Left="{StaticResource GridSplitterWidth}"/>
|
||||
</ThicknessAnimation.To>
|
||||
</ThicknessAnimation>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="right_panel_expander_toggle" Storyboard.TargetProperty="(Control.HorizontalAlignment)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static HorizontalAlignment.Left}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="right_panel_content_root" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="right_panel_width_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="{Binding right_panel_width, Converter={StaticResource GridLengthPixelConverter}, Mode=TwoWay}"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<sp:OrderBookGridView
|
||||
x:Name="orders_grid"
|
||||
Grid.ColumnSpan="2"
|
||||
Background="Transparent"
|
||||
Columns="{Binding columns}"
|
||||
CurrentRow="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Rows="{Binding rows}"
|
||||
SelectionHighlight="{Binding grid_settings_manager.core_manager.selection_highlight, Mode=OneWay}"
|
||||
>
|
||||
<FrameworkElement.Margin>
|
||||
<Binding Path="show_right_panel">
|
||||
<Binding.Converter>
|
||||
<cv:ValueConverterPipe>
|
||||
<cv:BooleanToObjectConverter ValueForFalse="{u:Double 0}" ValueForTrue="{StaticResource GridSplitterWidth}"/>
|
||||
<cv:DoubleToThicknessConverter RightMultiplier="-1"/>
|
||||
</cv:ValueConverterPipe>
|
||||
</Binding.Converter>
|
||||
</Binding>
|
||||
</FrameworkElement.Margin>
|
||||
</sp:OrderBookGridView>
|
||||
<GridSplitter
|
||||
x:Name="right_panel_width_splitter"
|
||||
Grid.Column="1"
|
||||
Panel.ZIndex="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Stretch"
|
||||
Width="{StaticResource GridSplitterWidth}"
|
||||
BorderThickness="1,0,0,0"
|
||||
BorderBrush="{u:BTSColor UI.form_content_border}"
|
||||
Visibility="Collapsed"
|
||||
>
|
||||
<GridSplitter.Opacity>
|
||||
<Binding Path="IsMouseOver" RelativeSource="{RelativeSource Self}">
|
||||
<Binding.Converter>
|
||||
<cv:BooleanToObjectConverter ValueForFalse="{u:Double 0}" ValueForTrue="{u:Double 1}"/>
|
||||
</Binding.Converter>
|
||||
</Binding>
|
||||
</GridSplitter.Opacity>
|
||||
</GridSplitter>
|
||||
<ToggleButton
|
||||
x:Name="right_panel_expander_toggle"
|
||||
Grid.Column="2"
|
||||
Panel.ZIndex="1"
|
||||
HorizontalAlignment="Right"
|
||||
Width="7"
|
||||
BorderThickness="1"
|
||||
IsChecked="{Binding show_right_panel}"
|
||||
>
|
||||
<ToggleButton.Style>
|
||||
<Style TargetType="ToggleButton" BasedOn="{StaticResource MenuExpanderButtonBase}">
|
||||
<Setter Property="Opacity">
|
||||
<Setter.Value>
|
||||
<MultiBinding>
|
||||
<MultiBinding.Converter>
|
||||
<cv:MultiValueConverterPipe>
|
||||
<cv:BooleanOrConverter/>
|
||||
<cv:BooleanToObjectConverter ValueForFalse="{u:Double 0}" ValueForTrue="{u:Double 1}"/>
|
||||
</cv:MultiValueConverterPipe>
|
||||
</MultiBinding.Converter>
|
||||
<Binding Path="IsMouseOver" RelativeSource="{RelativeSource Self}"/>
|
||||
<Binding Path="show_right_panel"/>
|
||||
</MultiBinding>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ToolTip">
|
||||
<Setter.Value>
|
||||
<MultiBinding Converter="{x:Static ut:CommandHelper.DescriptionConverter}">
|
||||
<Binding Path="show_right_panel">
|
||||
<Binding.Converter>
|
||||
<cv:BooleanToObjectConverter
|
||||
ValueForFalse="{x:Static ShowRightPanel, MemberType={u:Nested sp:OrderBookView.CommandDescriptions}}"
|
||||
ValueForTrue="{x:Static HideRightPanel, MemberType={u:Nested sp:OrderBookView.CommandDescriptions}}"
|
||||
/>
|
||||
</Binding.Converter>
|
||||
</Binding>
|
||||
<Binding Source="{x:Static ToggleShowRightPanel, MemberType={u:Nested sp:OrderBookView.KeyboardShortcuts}}"/>
|
||||
</MultiBinding>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsChecked" Value="False">
|
||||
<Setter Property="Cursor" Value="ScrollW"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="Cursor" Value="ScrollE"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ToggleButton.Style>
|
||||
</ToggleButton>
|
||||
<Grid x:Name="right_panel_content_root" Grid.Column="1" Visibility="Collapsed">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference right_panel_content_root}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition>
|
||||
<MultiBinding Converter="{x:Static sp:OrderBookView.SlaveDataVisualStateConverter}">
|
||||
<Binding Path="DataContext.show_order_params" Source="{u:DirectReference right_panel_content_root}"/>
|
||||
<Binding Path="DataContext.show_fills" Source="{u:DirectReference right_panel_content_root}"/>
|
||||
<Binding Path="DataContext.show_ladder" Source="{u:DirectReference right_panel_content_root}"/>
|
||||
</MultiBinding>
|
||||
</u:StateDefinition>
|
||||
<u:StateDefinition State="{Binding DataContext.security_plurality, Source={u:DirectReference right_panel_content_root}, Converter={x:Static sp:OrderBookView.LadderVisualStateConverter}}"/>
|
||||
<u:StateDefinition State="{Binding DataContext.current_row, Source={u:DirectReference right_panel_content_root}, Converter={x:Static sp:OrderBookView.OrderParamsVisualStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested sp:OrderBookView.VisualStateNames.SlaveData}}">
|
||||
<VisualState Name="{x:Static None, MemberType={u:Nested sp:OrderBookView.VisualStateNames.SlaveData}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="order_params_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ladder_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{x:Static OrderParams, MemberType={u:Nested sp:OrderBookView.VisualStateNames.SlaveData}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="order_params_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ladder_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{x:Static OrderParamsLadder, MemberType={u:Nested sp:OrderBookView.VisualStateNames.SlaveData}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ladder_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{x:Static OrderParamsFills, MemberType={u:Nested sp:OrderBookView.VisualStateNames.SlaveData}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ladder_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{x:Static OrderParamsLadderFills, MemberType={u:Nested sp:OrderBookView.VisualStateNames.SlaveData}}"/>
|
||||
<VisualState Name="{x:Static Ladder, MemberType={u:Nested sp:OrderBookView.VisualStateNames.SlaveData}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="order_params_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ladder_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{x:Static LadderFills, MemberType={u:Nested sp:OrderBookView.VisualStateNames.SlaveData}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ladder_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{x:Static Fills, MemberType={u:Nested sp:OrderBookView.VisualStateNames.SlaveData}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="order_params_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ladder_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested sp:OrderBookView.VisualStateNames.Ladder}}">
|
||||
<VisualState Name="{x:Static OneSecuritySelected, MemberType={u:Nested sp:OrderBookView.VisualStateNames.Ladder}}"/>
|
||||
<VisualState Name="{x:Static NoSecuritySelected, MemberType={u:Nested sp:OrderBookView.VisualStateNames.Ladder}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ladder" Storyboard.TargetProperty="(FrameworkElement.ToolTip)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="No Orders Selected (select orders in the grid)"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{x:Static ManySecuritiesSelected, MemberType={u:Nested sp:OrderBookView.VisualStateNames.Ladder}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ladder" Storyboard.TargetProperty="(FrameworkElement.ToolTip)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Multiple Securities Selected (select orders with a common security)"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested sp:OrderBookView.VisualStateNames.OrderDescription}}">
|
||||
<VisualState Name="{x:Static Active, MemberType={u:Nested sp:OrderBookView.VisualStateNames.OrderDescription}}"/>
|
||||
<VisualState Name="{x:Static Inactive, MemberType={u:Nested sp:OrderBookView.VisualStateNames.OrderDescription}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="no_rows_selected_text_block" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="current_row_description_content_control" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="order_description_root" Storyboard.TargetProperty="(FrameworkElement.ToolTip)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Select orders in the grid to view corresponding details/parameters below."/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource GridSplitterAndMenuExpanderWidth}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding order_params_height, Converter={StaticResource GridLengthStarConverter}, Mode=TwoWay}"/>
|
||||
<RowDefinition Height="5" MaxHeight="5"/>
|
||||
<RowDefinition Height="{Binding ladder_height, Converter={StaticResource GridLengthStarConverter}, Mode=TwoWay}"/>
|
||||
<RowDefinition Height="5" MaxHeight="5"/>
|
||||
<RowDefinition Height="{Binding fills_height, Converter={StaticResource GridLengthStarConverter}, Mode=TwoWay}"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ToolBar
|
||||
x:Name="right_panel_tool_bar"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemContainerStyle="{StaticResource RightPanel.MenuItem}"
|
||||
BorderBrush="DarkGray"
|
||||
BorderThickness="1"
|
||||
>
|
||||
<ToolBar.Background>
|
||||
<SolidColorBrush Color="LightGray" Opacity="1"/>
|
||||
</ToolBar.Background>
|
||||
<u:StateManager.VisualState>
|
||||
<Binding Path="right_panel_view_mode">
|
||||
<Binding.Converter>
|
||||
<cv:ValueMap Default="Tab">
|
||||
<cv:KeyValue Key="{x:Static Tab, MemberType={u:Nested ob:OrderBookRightPanelSettings.ViewMode}}" Value="Tab"/>
|
||||
<cv:KeyValue Key="{x:Static Stack, MemberType={u:Nested ob:OrderBookRightPanelSettings.ViewMode}}" Value="Stack"/>
|
||||
</cv:ValueMap>
|
||||
</Binding.Converter>
|
||||
</Binding>
|
||||
</u:StateManager.VisualState>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState Name="Tab">
|
||||
<Storyboard>
|
||||
<ThicknessAnimation Storyboard.TargetName="right_panel_tool_bar" Storyboard.TargetProperty="(Control.BorderThickness)" BeginTime="0" Duration="0" To="0"/>
|
||||
<ColorAnimation Storyboard.TargetName="right_panel_tool_bar" Storyboard.TargetProperty="(Control.BorderBrush).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="#44000000"/>
|
||||
<DoubleAnimation Storyboard.TargetName="right_panel_tool_bar" Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Opacity)" BeginTime="0" Duration="0" To="0"/>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="tab_remainder_menu_item" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="Stack"/>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<c:RadioButtonGroupManager.Manager>
|
||||
<c:RadioButtonGroupManager SelectedItem="{Binding DataContext.right_panel_view_mode, Source={u:DirectReference right_panel_tool_bar}, Mode=TwoWay}"/>
|
||||
</c:RadioButtonGroupManager.Manager>
|
||||
<MenuItem x:Name="params_menu_item" ToolBar.OverflowMode="Never" Header="Params" IsChecked="{Binding show_order_params}" Icon="{x:Static u:Icons.ModifyOrder}" ToolTip="Show Order/Algo Parameters for Selected Orders">
|
||||
<MenuItem.Resources>
|
||||
<Thickness x:Key="IconPadding" Left="2" Top="3" Bottom="2"/>
|
||||
</MenuItem.Resources>
|
||||
</MenuItem>
|
||||
<MenuItem x:Name="ladder_menu_item" ToolBar.OverflowMode="Never" Header="Ladder" IsChecked="{Binding show_ladder}" Icon="{x:Static u:Icons.Ladder}" ToolTip="Show Selected Orders in Ladder">
|
||||
<MenuItem.Resources>
|
||||
<Thickness x:Key="IconPadding" Left="2" Top="3" Bottom="2"/>
|
||||
</MenuItem.Resources>
|
||||
</MenuItem>
|
||||
<MenuItem x:Name="fills_menu_item" ToolBar.OverflowMode="Never" Header="Fills" IsChecked="{Binding show_fills}" Icon="{x:Static u:Icons.Payment}" ToolTip="Show Fills for Selected Orders">
|
||||
<MenuItem.Resources>
|
||||
<Thickness x:Key="IconPadding" Left="2" Top="4.5" Bottom="4.5"/>
|
||||
</MenuItem.Resources>
|
||||
</MenuItem>
|
||||
<MenuItem x:Name="tab_remainder_menu_item" ToolBar.OverflowMode="Never" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" IsCheckable="False" Visibility="Collapsed">
|
||||
<MenuItem.Template>
|
||||
<ControlTemplate TargetType="MenuItem">
|
||||
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Height="1">
|
||||
<Rectangle.Fill>
|
||||
<SolidColorBrush Color="{StaticResource TabBorderColor}"/>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
</ControlTemplate>
|
||||
</MenuItem.Template>
|
||||
<MenuItem.Width>
|
||||
<MultiBinding>
|
||||
<MultiBinding.Converter>
|
||||
<cv:RemainingDimensionConverter K="1"/>
|
||||
</MultiBinding.Converter>
|
||||
<Binding Path="ActualWidth" ElementName="params_menu_item"/>
|
||||
<Binding Path="ActualWidth" ElementName="ladder_menu_item"/>
|
||||
<Binding Path="ActualWidth" ElementName="fills_menu_item"/>
|
||||
<Binding Path="ActualWidth" RelativeSource="{RelativeSource AncestorType=ToolBar}"/>
|
||||
</MultiBinding>
|
||||
</MenuItem.Width>
|
||||
</MenuItem>
|
||||
</ToolBar>
|
||||
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Vertical">
|
||||
<StackPanel.Margin>
|
||||
<Thickness Left="{StaticResource DialogSkirtPadding.Left}" Top="4"/>
|
||||
</StackPanel.Margin>
|
||||
<StackPanel x:Name="order_description_root" Margin="0,5,0,1" Orientation="Horizontal">
|
||||
<StackPanel.ToolTip>
|
||||
<ItemsControl ItemsSource="{Binding DataContext.selected_rows, Source={u:DirectReference right_panel_content_root}}" ItemTemplate="{StaticResource OrderDescriptionTemplate}"/>
|
||||
</StackPanel.ToolTip>
|
||||
<ContentControl x:Name="current_row_description_content_control" Content="{Binding current_row}" ContentTemplate="{StaticResource OrderDescriptionTemplate}" IsTabStop="False" Visibility="Visible"/>
|
||||
<TextBlock x:Name="no_rows_selected_text_block" Visibility="Collapsed">
|
||||
(No orders are selected)
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
Margin="3,0,0,0"
|
||||
FontStyle="Italic"
|
||||
Visibility="{Binding selected_rows.Count, Converter={StaticResource SelectedOrdersAndOthersSuffixVisibilityConverter}}"
|
||||
VerticalAlignment="Top"
|
||||
Text="{Binding selected_rows.Count, Converter={StaticResource MinusOneConverter}, Mode=OneWay, StringFormat='and {0} others...'}"
|
||||
/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<ScrollViewer x:Name="order_params_root" Grid.Column="1" Grid.Row="2" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<Border Padding="12,12,12,6">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<usp:AlgoEditorGridView
|
||||
x:Name="algo_editor"
|
||||
DataContext="{Binding order_mod_vm}"
|
||||
AlgoRoots="{Binding algo_vm.algo_roots}"
|
||||
SelectedAlgoRoot="{Binding algo_vm.algo_root}"
|
||||
AlgoParamRows="{Binding param_rows}"
|
||||
IsReadOnly="{Binding DataContext.can_user_modify_selected_orders, ElementName=Root, Converter={StaticResource NotConverter}}"
|
||||
/>
|
||||
<StackPanel Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,8,0,0" Orientation="Horizontal">
|
||||
<Button x:Name="modify_orders_button" Command="{Binding modify_selected_orders_cmd}">
|
||||
<Button.Margin>
|
||||
<Thickness Right="{StaticResource DialogSkirtButtonPadding.Right}"/>
|
||||
</Button.Margin>
|
||||
<Button.ToolTip>
|
||||
<TextBlock>
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{x:Static ut:CommandHelper.DescriptionConverter}">
|
||||
<Binding Source="{x:Static ModifyOrders, MemberType={u:Nested sp:OrderBookView.CommandDescriptions}}"/>
|
||||
<Binding Source="{x:Static ModifyOrders, MemberType={u:Nested sp:OrderBookView.KeyboardShortcuts}}"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Button.ToolTip>
|
||||
<TextBlock>
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Text" Value="{Binding selected_rows.Count, StringFormat='Modify Orders ({0})'}"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource AncestorType=Button}}" Value="False">
|
||||
<Setter Property="Text" Value="Modify Orders"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
<Button Command="{Binding revert_selected_orders_cmd}">
|
||||
<Button.Margin>
|
||||
<Thickness Left="{StaticResource DialogSkirtButtonPadding.Left}"/>
|
||||
</Button.Margin>
|
||||
<Button.ToolTip>
|
||||
<TextBlock>
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{x:Static ut:CommandHelper.DescriptionConverter}">
|
||||
<Binding Source="{x:Static RevertOrders, MemberType={u:Nested sp:OrderBookView.CommandDescriptions}}"/>
|
||||
<Binding Source="{x:Static RevertOrders, MemberType={u:Nested sp:OrderBookView.KeyboardShortcuts}}"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Button.ToolTip>
|
||||
Revert
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
<GridSplitter
|
||||
x:Name="order_params_splitter"
|
||||
Grid.Column="1"
|
||||
Grid.Row="3"
|
||||
Panel.ZIndex="1"
|
||||
Width="{StaticResource GridSplitterWidth}"
|
||||
VerticalAlignment="Stretch"
|
||||
Height="{x:Static sp:OrderBookView.GridSplitterHeight}"
|
||||
BorderThickness="0,1,0,0"
|
||||
ResizeBehavior="PreviousAndNext"
|
||||
/>
|
||||
<usp:MiniLadderView x:Name="ladder" Grid.Column="1" Grid.Row="4" DataContext="{Binding order_mod_vm.ladder_vm}" CanUserSendOrders="False" Settings="{x:Static sp:LadderSettings.Default}"/>
|
||||
<GridSplitter
|
||||
x:Name="ladder_splitter"
|
||||
Grid.Column="1"
|
||||
Grid.Row="5"
|
||||
Panel.ZIndex="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Height="{x:Static sp:OrderBookView.GridSplitterHeight}"
|
||||
BorderThickness="0,1,0,0"
|
||||
ResizeBehavior="PreviousAndNext"
|
||||
/>
|
||||
<c:DIYGrid x:Name="fills_grid" Grid.Column="1" Grid.Row="6" DataContext="{Binding fills_vm}" Columns="{Binding columns}" Rows="{Binding rows}" IsTabStop="False">
|
||||
<c:DIYGrid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Fills Settings" Command="{Binding Path='(sp:OrderBookView.DataContext).(sp:OrderBookVM.launch_fills_settings_cmd)', Source={u:DirectReference Root}}" Icon="{Binding Source={x:Static u:Icons.Admin}, Converter={StaticResource IconConverter}}"/>
|
||||
</ContextMenu>
|
||||
</c:DIYGrid.ContextMenu>
|
||||
<c:DIYGrid.RowTemplate>
|
||||
<DataTemplate>
|
||||
<usp:FillsRowView/>
|
||||
</DataTemplate>
|
||||
</c:DIYGrid.RowTemplate>
|
||||
</c:DIYGrid>
|
||||
</Grid>
|
||||
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom" Grid.ColumnSpan="2">
|
||||
<StatusBarItem HorizontalAlignment="Right">
|
||||
<TextBlock>
|
||||
<Run Text="Orders:"/>
|
||||
<Run Text="{Binding rows.Count, Mode=OneWay}" />
|
||||
<Run Text="of"/>
|
||||
<Run Text="{Binding rows.total_row_count, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
@@ -1,290 +0,0 @@
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.controls.adorners;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.extensions;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utilities;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using OrderParamsVM = mbts.spark.OrderBookVM.OrderParamsVM;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal partial class OrderBookView : UserControl {
|
||||
public const double
|
||||
GridSplitterWidth = 5,
|
||||
GridSplitterHeight = 5;
|
||||
|
||||
public static readonly IValueConverter
|
||||
RightPanelVisualStateConverter = ValueConverter.create<bool, string>(show_panel => show_panel ? VisualStateNames.RightPanel.Active : VisualStateNames.RightPanel.Inactive),
|
||||
OrderParamsVisualStateConverter = ValueConverter.create<OrderBookRowVM, string>(row => row != null ? VisualStateNames.OrderDescription.Active : VisualStateNames.OrderDescription.Inactive),
|
||||
ToolBarMenuItemHeightConverter = ValueConverter.create<Thickness, double>(padding => padding.Top + 22 + padding.Bottom),
|
||||
LadderVisualStateConverter = ValueConverter.create<Plurality, string>(security_plurality => {
|
||||
switch (security_plurality) {
|
||||
case Plurality.None: return VisualStateNames.Ladder.NoSecuritySelected;
|
||||
case Plurality.One: return VisualStateNames.Ladder.OneSecuritySelected;
|
||||
case Plurality.Many: return VisualStateNames.Ladder.ManySecuritiesSelected;
|
||||
default: return null;
|
||||
}
|
||||
});
|
||||
|
||||
public static readonly IMultiValueConverter
|
||||
RightPanelDialogSkirtLeftAnchorConverter = ValueConverter.create<double, double, double>((canvas_width, skirt_width) => canvas_width - skirt_width + GridSplitterWidth),
|
||||
SlaveDataVisualStateConverter = ValueConverter.create<bool, bool, bool, string>(
|
||||
(show_order_params, show_fills, show_ladder) => {
|
||||
VisualStateNames.SlaveData.Data d = (VisualStateNames.SlaveData.Data)0;
|
||||
if (show_order_params)
|
||||
d |= VisualStateNames.SlaveData.Data.OrderParams;
|
||||
if (show_fills)
|
||||
d |= VisualStateNames.SlaveData.Data.Fills;
|
||||
if (show_ladder)
|
||||
d |= VisualStateNames.SlaveData.Data.Ladder;
|
||||
return VisualStateNames.SlaveData.fmt_name(d);
|
||||
});
|
||||
static readonly IMultiValueConverter
|
||||
_right_panel_expander_margin_converter = ValueConverter.create<bool, Visibility, Thickness>(
|
||||
(is_expanded, scrollbar_vis) =>
|
||||
!is_expanded && scrollbar_vis == Visibility.Visible ?
|
||||
new Thickness(0, DIYGrid.DefaultColumnHeaderHeight, SystemParameters.VerticalScrollBarWidth, 0) :
|
||||
new Thickness(0));
|
||||
|
||||
IDisposable _attach_op;
|
||||
|
||||
public OrderBookView(OrderBookVM vm) {
|
||||
InitializeComponent();
|
||||
DataContext = vm;
|
||||
bind_right_panel_expander_margin();
|
||||
var algo_editor_cm = new ContextMenu();
|
||||
algo_editor_cm.Items.AddRange(order_mod_menu());
|
||||
algo_editor.ContextMenu = algo_editor_cm;
|
||||
vm.SubscribeDisposed(
|
||||
Disposable.Create(
|
||||
algo_editor._param_grid.SubscribeIsEditing().SubscribeEach(is_editing => is_editing ? orders_grid.SubscribeGotFocus(on_orders_grid_got_focus) : null),
|
||||
WPFHelper.connect_vertical_splitter_components(vm, right_panel_content_root.RowDefinitions, 2,
|
||||
/*order params*/(order_params_root, nameof(OrderBookVM.order_params_height), vm.subscribe_show_order_params()),
|
||||
/*mini ladder */(ladder, nameof(OrderBookVM.ladder_height), vm.subscribe_show_ladder()),
|
||||
/*fills grid */(fills_grid, nameof(OrderBookVM.fills_height), vm.subscribe_show_fills())))
|
||||
);
|
||||
// 'send attach orders' behavior
|
||||
vm.begin_attaching += begin_attach_orders;
|
||||
vm.end_attaching += () => Disposable.dispose(ref _attach_op);
|
||||
// 'scroll to current order' behavior (mini ladder)
|
||||
vm.SubscribeDisposed(vm.subscribe_show_ladder().SubscribeEach(show => show ? connect_mini_ladder_auto_scroll() : null));
|
||||
IDisposable connect_mini_ladder_auto_scroll() {
|
||||
bool is_busy = false;
|
||||
vm.selected_order_changed += on_selected_order_changed;
|
||||
return Disposable.Create(() => vm.selected_order_changed -= on_selected_order_changed);
|
||||
async void on_selected_order_changed(OrderRecordImmutable? order) {
|
||||
if (order.HasValue && !is_busy) {
|
||||
is_busy = true;
|
||||
await Task.Delay(200);
|
||||
if (vm.current_order.HasValue && vm.current_order.Value.security_key() == ladder.ladder_grid_host.security.Id && vm.current_order.Value.is_active())
|
||||
ladder.ladder_grid_host.scroll_to_price(vm.current_order.Value.price());
|
||||
is_busy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on_orders_grid_got_focus(object sender, RoutedEventArgs e) {
|
||||
if (e.OriginalSource is ListBoxItem && algo_editor._param_grid.TryGetCurrentRowView(out var row_view) && row_view.IsEditing)
|
||||
algo_editor._param_grid.CancelEdit();
|
||||
}
|
||||
|
||||
IEnumerable order_mod_menu() {
|
||||
var revert_cell_mi = new MenuItem();
|
||||
var revert_cell_cmd = new DelegateCommand(revert_impl, () => get_current_row()?.param?.is_dirty ?? false);
|
||||
revert_cell_mi.Command = revert_cell_cmd;
|
||||
IDisposable param_is_dirty_sub = null;
|
||||
algo_editor._param_grid.CurrentCellChanged += on_current_cell_changed;
|
||||
// menu items
|
||||
yield return revert_cell_mi;
|
||||
yield return new MenuItem {
|
||||
Header = CommandHelper.fmt("Revert All", KeyboardShortcuts.RevertOrders),
|
||||
Command = vm.revert_selected_orders_cmd
|
||||
};
|
||||
yield return new Separator();
|
||||
yield return new MenuItem {
|
||||
Header = CommandHelper.fmt("Modify Orders", KeyboardShortcuts.ModifyOrders),
|
||||
Command = CommandWithActionWrapper.wrap(vm.modify_selected_orders_cmd, algo_editor.commit_edit)
|
||||
};
|
||||
// local methods
|
||||
AlgoParamRowVM get_current_row() => algo_editor._param_grid.SelectedItem as AlgoParamRowVM;
|
||||
void on_current_cell_changed(object sender, EventArgs e) {
|
||||
Disposable.dispose(ref param_is_dirty_sub);
|
||||
var r = get_current_row();
|
||||
var header = new StringBuilderEx("Revert ");
|
||||
revert_cell_cmd.RaiseCanExecuteChanged();
|
||||
if (r != null) {
|
||||
header.Add("'", r.param.display_name, "'");
|
||||
r.param.is_dirty_changed += on_is_dirty_changed;
|
||||
param_is_dirty_sub = Disposable.Create(dispose);
|
||||
// local methods
|
||||
void on_is_dirty_changed(bool _) => revert_cell_cmd.RaiseCanExecuteChanged();
|
||||
void dispose() => r.param.is_dirty_changed -= on_is_dirty_changed;
|
||||
} else {
|
||||
header.Add("(Param)");
|
||||
}
|
||||
revert_cell_mi.Header = header.ToString();
|
||||
}
|
||||
void revert_impl() {
|
||||
if (get_current_row() is AlgoParamRowVM r) {
|
||||
algo_editor.cancel_edit();
|
||||
r.param.revert();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async void bind_right_panel_expander_margin() {
|
||||
if (!orders_grid.IsLoaded)
|
||||
await orders_grid.LoadedAsync();
|
||||
if (orders_grid.try_find_descendent_of_type(out DIYGridRowsPresenter rows_presenter)) {
|
||||
if (rows_presenter.try_find_descendent_of_type(out ScrollViewer scroll_viewer)) {
|
||||
var mb = new MultiBinding { Converter = _right_panel_expander_margin_converter };
|
||||
mb.Bindings.Add(new Binding(nameof(OrderBookVM.show_right_panel)) { Source = vm });
|
||||
mb.Bindings.Add(new Binding(nameof(ScrollViewer.ComputedVerticalScrollBarVisibility)) { Source = scroll_viewer });
|
||||
right_panel_expander_toggle.SetBinding(MarginProperty, mb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new OrderBookVM DataContext {
|
||||
get => base.DataContext as OrderBookVM;
|
||||
set => base.DataContext = value;
|
||||
}
|
||||
|
||||
public void begin_attach_orders() {
|
||||
Disposable.dispose(ref _attach_op);
|
||||
var vm = DataContext.attach_vm;
|
||||
// attach orders view
|
||||
var attach_orders_view = AlgoEditorGridView.create_attach_view();
|
||||
attach_orders_view.DataContext = vm;
|
||||
attach_orders_view.StretchValueColumn = false;
|
||||
attach_orders_view.SetBinding(AlgoEditorGridView.AlgoRootsProperty, new Binding(string.Join(".", nameof(OrderParamsVM.algo_vm), nameof(AlgoDefinitionVM.algo_roots))) { Source = vm });
|
||||
attach_orders_view.SetBinding(AlgoEditorGridView.SelectedAlgoRootProperty, new Binding(string.Join(".", nameof(OrderParamsVM.algo_vm), nameof(AlgoDefinitionVM.algo_root))) { Source = vm });
|
||||
attach_orders_view.SetBinding(AlgoEditorGridView.AlgoParamRowsProperty, new Binding(nameof(OrderParamsVM.param_rows)) { Source = vm });
|
||||
// dialog skirt
|
||||
var dialog_skirt = new DialogSkirt {
|
||||
RightItems = new[] {
|
||||
new DialogSkirtItem("Attach To Orders", DataContext.send_attached_orders_cmd) { is_default = true },
|
||||
new DialogSkirtItem("Cancel", new DelegateCommand(() => Disposable.dispose(ref _attach_op))) { is_cancel = true }
|
||||
}
|
||||
};
|
||||
Grid.SetRow(dialog_skirt, 1);
|
||||
// grid
|
||||
var grid = new Grid();
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
||||
grid.Children.Add(attach_orders_view);
|
||||
grid.Children.Add(dialog_skirt);
|
||||
// popup adorner
|
||||
var popup_adorner = new PopupAdorner(
|
||||
orders_grid, orders_grid,
|
||||
new Border {
|
||||
Child = grid,
|
||||
Background = BTSColors.common_background_brush,
|
||||
BorderBrush = Brushes.Black,
|
||||
BorderThickness = new Thickness(1)
|
||||
},
|
||||
HorizontalPosition.Middle,
|
||||
VerticalPosition.Middle,
|
||||
0,
|
||||
orders_grid.RowHeight) {
|
||||
OpenRelativeToMouse = true,
|
||||
ClipToBounds = true
|
||||
};
|
||||
popup_adorner.attach();
|
||||
popup_adorner.IsOpen = true;
|
||||
// attach op (dispose)
|
||||
_attach_op = new CompositeDisposable(orders_grid.SubscribePreviewMouseDown((s, e) => {
|
||||
if (!KeyboardExtensions.IsShiftDown() && !KeyboardExtensions.IsCtrlDown() && !popup_adorner.PopupElement.IsMouseOver)
|
||||
Disposable.dispose(ref _attach_op);
|
||||
})) {
|
||||
() => {
|
||||
popup_adorner.IsOpen = false;
|
||||
popup_adorner.detach();
|
||||
DataContext.is_attaching_orders = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class CommandDescriptions {
|
||||
public static readonly string
|
||||
ShowRightPanel = "Show Details Panel",
|
||||
HideRightPanel = "Hide Details Panel",
|
||||
ModifyOrders = "Modify selected orders",
|
||||
RevertOrders = "Revert modifications to selected orders";
|
||||
}
|
||||
|
||||
public static class KeyboardShortcuts {
|
||||
public static readonly KeyGesture
|
||||
ToggleShowRightPanel = new KeyGesture(Key.M, ModifierKeys.Control | ModifierKeys.Shift),
|
||||
ModifyOrders = new KeyGesture(Key.S, ModifierKeys.Control | ModifierKeys.Shift),
|
||||
RevertOrders = new KeyGesture(Key.R, ModifierKeys.Control);
|
||||
}
|
||||
|
||||
public static class VisualStateNames {
|
||||
public static class RightPanel {
|
||||
public const string GroupName = nameof(RightPanel);
|
||||
|
||||
public static readonly string
|
||||
Active = $"{GroupName}{VisualStates.Active}",
|
||||
Inactive = $"{GroupName}{VisualStates.Inactive}";
|
||||
}
|
||||
|
||||
public static class OrderDescription {
|
||||
public const string GroupName = nameof(OrderDescription);
|
||||
|
||||
public static readonly string
|
||||
Active = $"{GroupName}{VisualStates.Active}",
|
||||
Inactive = $"{GroupName}{VisualStates.Inactive}";
|
||||
}
|
||||
|
||||
public static class Ladder {
|
||||
public const string GroupName = nameof(Ladder);
|
||||
|
||||
public static readonly string
|
||||
NoSecuritySelected = $"{GroupName}{nameof(NoSecuritySelected)}",
|
||||
OneSecuritySelected = $"{GroupName}{nameof(OneSecuritySelected)}",
|
||||
ManySecuritiesSelected = $"{GroupName}{nameof(ManySecuritiesSelected)}";
|
||||
}
|
||||
|
||||
public static class SlaveData {
|
||||
public const string GroupName = nameof(SlaveData);
|
||||
|
||||
public static readonly string
|
||||
None = $"{GroupName}{nameof(None)}",
|
||||
OrderParams = $"{GroupName}{nameof(OrderParams)}",
|
||||
OrderParamsLadder = $"{GroupName}{nameof(OrderParamsLadder)}",
|
||||
OrderParamsFills = $"{GroupName}{nameof(OrderParamsFills)}",
|
||||
OrderParamsLadderFills = $"{GroupName}{nameof(OrderParamsLadderFills)}",
|
||||
LadderFills = $"{GroupName}{nameof(LadderFills)}",
|
||||
Ladder = $"{GroupName}{nameof(Ladder)}",
|
||||
Fills = $"{GroupName}{nameof(Fills)}";
|
||||
|
||||
public enum Data {
|
||||
OrderParams = 1,
|
||||
Ladder = 1 << 1,
|
||||
Fills = 1 << 2
|
||||
}
|
||||
|
||||
public static string fmt_name(Data d) {
|
||||
var vis_data = string.Join("", EnumHelper.split_flags(d));
|
||||
return vis_data == string.Empty ? None : GroupName + vis_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,285 +0,0 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
<cv:MultiValueConverterPipe x:Key="CalculatorButtonsVisualStateConverter">
|
||||
<cv:BooleanNoneConverter/>
|
||||
<cv:ValueMap Default="{x:Static Normal, MemberType={u:Nested usp:OrderParamsPanel.VisualStateNames.CalculatorButtons}}">
|
||||
<cv:KeyValue Key="{u:True}" Value="{x:Static Stretched, MemberType={u:Nested usp:OrderParamsPanel.VisualStateNames.CalculatorButtons}}"/>
|
||||
</cv:ValueMap>
|
||||
</cv:MultiValueConverterPipe>
|
||||
|
||||
<cv:MultiValueConverterPipe x:Key="CalculatorButtonsColumnsConverter">
|
||||
<cv:BooleanNoneConverter/>
|
||||
<cv:ValueMap Default="{u:Int 3}">
|
||||
<cv:KeyValue Key="{u:True}" Value="{u:Int 6}"/>
|
||||
</cv:ValueMap>
|
||||
</cv:MultiValueConverterPipe>
|
||||
|
||||
<ControlTemplate x:Key="OrderParamsPanelTemplate" TargetType="usp:OrderParamsPanel">
|
||||
<Border
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
>
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager AttachToMembers="True">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding Path='(u:StateDefinition.Group).(u:StateGroupsManager.Target).(Border.TemplatedParent).(usp:OrderParamsPanel.IsReadOnly)', RelativeSource={RelativeSource Self}, Converter={x:Static usp:OrderParamsPanel.GeneralVisualStateConverter}}"/>
|
||||
<u:StateDefinition State="{Binding Path='(u:StateDefinition.Group).(u:StateGroupsManager.Target).(Border.TemplatedParent).(usp:OrderParamsPanel.ShowBBO)', RelativeSource={RelativeSource Self}, Converter={x:Static usp:OrderParamsPanel.BBOVisualStateConverter}}"/>
|
||||
<u:StateDefinition>
|
||||
<MultiBinding Converter="{StaticResource CalculatorButtonsVisualStateConverter}">
|
||||
<Binding Path="(u:StateDefinition.Group).(u:StateGroupsManager.Target).(Border.TemplatedParent).(usp:OrderParamsPanel.ShowAccount)" RelativeSource="{RelativeSource Self}"/>
|
||||
<Binding Path="(u:StateDefinition.Group).(u:StateGroupsManager.Target).(Border.TemplatedParent).(usp:OrderParamsPanel.ShowTimeInForce)" RelativeSource="{RelativeSource Self}"/>
|
||||
</MultiBinding>
|
||||
</u:StateDefinition>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested usp:OrderParamsPanel.VisualStateNames.General}}">
|
||||
<VisualState Name="{x:Static Normal, MemberType={u:Nested usp:OrderParamsPanel.VisualStateNames.General}}"/>
|
||||
<VisualState Name="{x:Static ReadOnly, MemberType={u:Nested usp:OrderParamsPanel.VisualStateNames.General}}">
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="price_root" Storyboard.TargetProperty="(Control.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="size_and_auxiliary_root" Storyboard.TargetProperty="(Control.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="algo_editor" Storyboard.TargetProperty="(usp:AlgoEditorGridView.IsReadOnly)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="True"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested usp:OrderParamsPanel.VisualStateNames.BBO}}">
|
||||
<VisualState Name="{x:Static Active, MemberType={u:Nested usp:OrderParamsPanel.VisualStateNames.BBO}}"/>
|
||||
<VisualState Name="{x:Static Inactive, MemberType={u:Nested usp:OrderParamsPanel.VisualStateNames.BBO}}">
|
||||
<Storyboard>
|
||||
<!--Price-->
|
||||
<Int32Animation Storyboard.TargetName="price_entry_control" Storyboard.TargetProperty="(Grid.Column)" BeginTime="0" Duration="0" To="0"/>
|
||||
<Int32Animation Storyboard.TargetName="price_entry_control" Storyboard.TargetProperty="(Grid.ColumnSpan)" BeginTime="0" Duration="0" To="5"/>
|
||||
<ThicknessAnimation Storyboard.TargetName="price_entry_control" Storyboard.TargetProperty="(Control.Margin)" BeginTime="0" Duration="0" To="40,0,0,0"/>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="price_label" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<!--Size-->
|
||||
<Int32Animation Storyboard.TargetName="size_tick_up_down" Storyboard.TargetProperty="(Grid.Column)" BeginTime="0" Duration="0" To="0"/>
|
||||
<Int32Animation Storyboard.TargetName="size_tick_up_down" Storyboard.TargetProperty="(Grid.ColumnSpan)" BeginTime="0" Duration="0" To="5"/>
|
||||
<ThicknessAnimation Storyboard.TargetName="size_tick_up_down" Storyboard.TargetProperty="(Control.Margin)" BeginTime="0" Duration="0" To="40,0,0,0"/>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="size_label" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<!--BBO Buttons-->
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="best_bid_button" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="best_bid_size_button" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="best_ask_button" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="best_ask_size_button" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested usp:OrderParamsPanel.VisualStateNames.CalculatorButtons}}">
|
||||
<VisualState Name="{x:Static Normal, MemberType={u:Nested usp:OrderParamsPanel.VisualStateNames.CalculatorButtons}}"/>
|
||||
<VisualState Name="{x:Static Stretched, MemberType={u:Nested usp:OrderParamsPanel.VisualStateNames.CalculatorButtons}}">
|
||||
<Storyboard>
|
||||
<Int32Animation Storyboard.TargetName="calculator_buttons_items_control" Storyboard.TargetProperty="(Grid.Column)" BeginTime="0" Duration="0" To="0"/>
|
||||
<Int32Animation Storyboard.TargetName="calculator_buttons_items_control" Storyboard.TargetProperty="(Grid.ColumnSpan)" BeginTime="0" Duration="0" To="5"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid Grid.IsSharedSizeScope="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="price_root">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="price_label" Margin="8,0,0,0" Text="Price" Visibility="Collapsed"/>
|
||||
<usp:PriceEntryControl
|
||||
x:Name="price_entry_control"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
ContractSpec="{Binding ContractSpec, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Value="{Binding Price, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
|
||||
ToolTip="Price"
|
||||
IsTabStop="False"
|
||||
/>
|
||||
<Button
|
||||
x:Name="best_bid_button"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
Command="{x:Static usp:OrderParamsPanel.SetPriceCommand}"
|
||||
CommandParameter="{Binding BestBidPrice, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Foreground="{u:BTSColor MarketData.bid}"
|
||||
FontWeight="Bold"
|
||||
Style="{DynamicResource {x:Static usp:OrderParamsPanel.BestBidPriceButtonStyleKey}}"
|
||||
>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="BestBidPrice" RelativeSource="{RelativeSource TemplatedParent}"/>
|
||||
<Binding Path="ContractSpec" RelativeSource="{RelativeSource TemplatedParent}"/>
|
||||
</MultiBinding>
|
||||
</Button>
|
||||
<Button
|
||||
x:Name="best_ask_button"
|
||||
Grid.Column="4"
|
||||
HorizontalAlignment="Stretch"
|
||||
Command="{x:Static usp:OrderParamsPanel.SetPriceCommand}"
|
||||
CommandParameter="{Binding BestAskPrice, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Foreground="{u:BTSColor MarketData.ask}"
|
||||
FontWeight="Bold"
|
||||
Style="{DynamicResource {x:Static usp:OrderParamsPanel.BestAskPriceButtonStyleKey}}"
|
||||
>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="BestAskPrice" RelativeSource="{RelativeSource TemplatedParent}"/>
|
||||
<Binding Path="ContractSpec" RelativeSource="{RelativeSource TemplatedParent}"/>
|
||||
</MultiBinding>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid x:Name="size_and_auxiliary_root" Grid.Row="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock x:Name="size_label" Margin="8,0,0,0" Text="Size" Visibility="Collapsed"/>
|
||||
<c:TickUpDownNullableUInt x:Name="size_tick_up_down" Value="{Binding Size, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="Size"/>
|
||||
<Button
|
||||
x:Name="best_bid_size_button"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
Content="{Binding BestBidSize, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Foreground="{u:BTSColor MarketData.bid}"
|
||||
Style="{DynamicResource {x:Static usp:OrderParamsPanel.BestBidSizeButtonStyleKey}}"
|
||||
/>
|
||||
<Button
|
||||
x:Name="best_ask_size_button"
|
||||
Grid.Column="4"
|
||||
HorizontalAlignment="Stretch"
|
||||
Content="{Binding BestAskSize, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Foreground="{u:BTSColor MarketData.ask}"
|
||||
Style="{DynamicResource {x:Static usp:OrderParamsPanel.BestAskSizeButtonStyleKey}}"
|
||||
/>
|
||||
<ItemsControl
|
||||
x:Name="calculator_buttons_items_control"
|
||||
Grid.Row="2"
|
||||
Grid.RowSpan="2"
|
||||
Margin="-4,-4,0,0"
|
||||
ItemsSource="{Binding SizePresets, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
IsTabStop="False"
|
||||
>
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Focusable="False">
|
||||
<UniformGrid.Columns>
|
||||
<MultiBinding Converter="{StaticResource CalculatorButtonsColumnsConverter}">
|
||||
<Binding Path="ShowAccount" RelativeSource="{RelativeSource AncestorType=usp:OrderParamsPanel}"/>
|
||||
<Binding Path="ShowTimeInForce" RelativeSource="{RelativeSource AncestorType=usp:OrderParamsPanel}"/>
|
||||
</MultiBinding>
|
||||
</UniformGrid.Columns>
|
||||
</UniformGrid>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="4,4,0,0"
|
||||
Content="{Binding}"
|
||||
Style="{DynamicResource {x:Static usp:OrderParamsPanel.SizeCalculatorButtonStyleKey}}"
|
||||
/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<Grid Grid.Row="2" Grid.RowSpan="2" Grid.Column="2" Grid.ColumnSpan="3">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<ComboBox
|
||||
x:Name="account_combo_box"
|
||||
Margin="0,2,0,4"
|
||||
ItemsSource="{Binding Accounts, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
SelectedItem="{Binding SelectedAccount, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
ToolTip="Choose Account"
|
||||
/>
|
||||
<ComboBox x:Name="time_in_force_combo_box" Grid.Row="1">
|
||||
<ComboBox.Style>
|
||||
<Style TargetType="ComboBox" BasedOn="{StaticResource DisabledPlaceholderComboBox}">
|
||||
<Setter Property="ItemsSource">
|
||||
<Setter.Value>
|
||||
<x:ArrayExtension Type="{x:Type sys:String}">
|
||||
<sys:String>Time In Force (N/A)</sys:String>
|
||||
</x:ArrayExtension>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ComboBox.Style>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<usp:AlgoEditorGridView
|
||||
x:Name="algo_editor"
|
||||
Grid.Row="4"
|
||||
AlgoRoots="{Binding AlgoRoots, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
SelectedAlgoRoot="{Binding SelectedAlgoRoot, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
AlgoParamRows="{Binding AlgoParamRows, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
CanUserChangeAlgoRoot="{TemplateBinding CanUserChangeAlgoRoot}"
|
||||
/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="ShowAccount" Value="False">
|
||||
<Setter TargetName="account_combo_box" Property="Visibility" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
<Trigger Property="ShowTimeInForce" Value="False">
|
||||
<Setter TargetName="time_in_force_combo_box" Property="Visibility" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
<Trigger Property="ShowCalculatorButtons" Value="False">
|
||||
<Setter TargetName="calculator_buttons_items_control" Property="Visibility" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
|
||||
<Style TargetType="usp:OrderParamsPanel">
|
||||
<Setter Property="MinWidth" Value="300"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Template" Value="{StaticResource OrderParamsPanelTemplate}"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -1,353 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.collections;
|
||||
using mbts.spark;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.extensions;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
class OrderParamsPanel : Control {
|
||||
public const string
|
||||
BestBidPriceButtonStyleKey = nameof(BestBidPriceButtonStyleKey),
|
||||
BestBidSizeButtonStyleKey = nameof(BestBidSizeButtonStyleKey),
|
||||
BestAskPriceButtonStyleKey = nameof(BestAskPriceButtonStyleKey),
|
||||
BestAskSizeButtonStyleKey = nameof(BestAskSizeButtonStyleKey),
|
||||
SizeCalculatorButtonStyleKey = nameof(SizeCalculatorButtonStyleKey);
|
||||
|
||||
public static readonly RoutedCommand SetPriceCommand = new RoutedCommand();
|
||||
|
||||
public static readonly ExecutedRoutedEventHandler SetPriceExecuted =
|
||||
(s, e) => {
|
||||
if (s is OrderParamsPanel opp)
|
||||
opp.SetCurrentValue(PriceProperty, ConvertUtils.try_convert(e.Parameter, out double d) ? d : double.NaN);
|
||||
};
|
||||
|
||||
public static readonly CanExecuteRoutedEventHandler
|
||||
SetPriceCanExecute = (s, e) => e.CanExecute = e.Parameter is double,
|
||||
SetSizeCanExecute = (s, e) => e.CanExecute = e.Parameter is uint;
|
||||
|
||||
public static readonly IValueConverter
|
||||
GeneralVisualStateConverter = ValueConverter.create<bool, string>(is_read_only => is_read_only ? VisualStateNames.General.ReadOnly : VisualStateNames.General.Normal),
|
||||
BBOVisualStateConverter = ValueConverter.create<bool, string>(show_bbo => show_bbo ? VisualStateNames.BBO.Active : VisualStateNames.BBO.Inactive);
|
||||
|
||||
AlgoEditorView _algo_editor;
|
||||
IDisposable _bbo_button_navigation_hooks;
|
||||
|
||||
static OrderParamsPanel() =>
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(OrderParamsPanel), new FrameworkPropertyMetadata(typeof(OrderParamsPanel)));
|
||||
|
||||
#region IsReadOnly
|
||||
public static readonly DependencyProperty IsReadOnlyProperty =
|
||||
WPFHelper.create_dp<bool, OrderParamsPanel>(nameof(IsReadOnly));
|
||||
|
||||
public bool IsReadOnly {
|
||||
get => (bool)GetValue(IsReadOnlyProperty);
|
||||
set => SetValue(IsReadOnlyProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Price
|
||||
public static readonly DependencyProperty PriceProperty =
|
||||
WPFHelper.create_dp<double, OrderParamsPanel>(nameof(Price), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, double.NaN);
|
||||
|
||||
public double Price {
|
||||
get => (double)GetValue(PriceProperty);
|
||||
set => SetValue(PriceProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Size
|
||||
public static readonly DependencyProperty SizeProperty =
|
||||
WPFHelper.create_dp<uint?, OrderParamsPanel>(nameof(Size), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
|
||||
|
||||
public uint? Size {
|
||||
get => GetValue(SizeProperty) as uint?;
|
||||
set => SetValue(SizeProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SizePresets
|
||||
public static readonly DependencyProperty SizePresetsProperty =
|
||||
WPFHelper.create_dp<IEnumerable, OrderParamsPanel>(nameof(SizePresets), Enumerable.Empty<string>());
|
||||
|
||||
public IEnumerable SizePresets {
|
||||
get => GetValue(SizePresetsProperty) as IEnumerable;
|
||||
set => SetValue(SizePresetsProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BestBidPrice
|
||||
public static readonly DependencyProperty BestBidPriceProperty =
|
||||
WPFHelper.create_dp<double?, OrderParamsPanel>(nameof(BestBidPrice));
|
||||
|
||||
public double? BestBidPrice {
|
||||
get => GetValue(BestBidPriceProperty) as double?;
|
||||
set => SetValue(BestBidPriceProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BestBidSize
|
||||
public static readonly DependencyProperty BestBidSizeProperty =
|
||||
WPFHelper.create_dp<uint?, OrderParamsPanel>(nameof(BestBidSize));
|
||||
|
||||
public uint? BestBidSize {
|
||||
get => GetValue(BestBidSizeProperty) as uint?;
|
||||
set => SetValue(BestBidSizeProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BestAskPrice
|
||||
public static readonly DependencyProperty BestAskPriceProperty =
|
||||
WPFHelper.create_dp<double?, OrderParamsPanel>(nameof(BestAskPrice));
|
||||
|
||||
public double? BestAskPrice {
|
||||
get => GetValue(BestAskPriceProperty) as double?;
|
||||
set => SetValue(BestAskPriceProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BestAskSize
|
||||
public static readonly DependencyProperty BestAskSizeProperty =
|
||||
WPFHelper.create_dp<uint?, OrderParamsPanel>(nameof(BestAskSize));
|
||||
|
||||
public uint? BestAskSize {
|
||||
get => GetValue(BestAskSizeProperty) as uint?;
|
||||
set => SetValue(BestAskSizeProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ShowBBO
|
||||
public static readonly DependencyProperty ShowBBOProperty =
|
||||
WPFHelper.create_dp<bool, OrderParamsPanel>(nameof(ShowBBO), true);
|
||||
|
||||
public bool ShowBBO {
|
||||
get => (bool)GetValue(ShowBBOProperty);
|
||||
set => SetValue(ShowBBOProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ShowCalculatorButtons
|
||||
public static readonly DependencyProperty ShowCalculatorButtonsProperty =
|
||||
WPFHelper.create_dp<bool, OrderParamsPanel>(nameof(ShowCalculatorButtons), true);
|
||||
|
||||
public bool ShowCalculatorButtons {
|
||||
get => (bool)GetValue(ShowCalculatorButtonsProperty);
|
||||
set => SetValue(ShowCalculatorButtonsProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ShowTimeInForce
|
||||
public static readonly DependencyProperty ShowTimeInForceProperty =
|
||||
WPFHelper.create_dp<bool, OrderParamsPanel>(nameof(ShowTimeInForce), true);
|
||||
|
||||
public bool ShowTimeInForce {
|
||||
get => (bool)GetValue(ShowTimeInForceProperty);
|
||||
set => SetValue(ShowTimeInForceProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ShowAccount
|
||||
public static readonly DependencyProperty ShowAccountProperty =
|
||||
WPFHelper.create_dp<bool, OrderParamsPanel>(nameof(ShowAccount), true);
|
||||
|
||||
public bool ShowAccount {
|
||||
get => (bool)GetValue(ShowAccountProperty);
|
||||
set => SetValue(ShowAccountProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Accounts
|
||||
public static readonly DependencyProperty AccountsProperty =
|
||||
WPFHelper.create_dp<IEnumerable, OrderParamsPanel>(nameof(Accounts), Enumerable.Empty<string>());
|
||||
|
||||
public IEnumerable Accounts {
|
||||
get => GetValue(AccountsProperty) as IEnumerable;
|
||||
set => SetValue(AccountsProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SelectedAccount
|
||||
public static readonly DependencyProperty SelectedAccountProperty =
|
||||
WPFHelper.create_dp<object, OrderParamsPanel>(nameof(SelectedAccount));
|
||||
|
||||
public object SelectedAccount {
|
||||
get => GetValue(SelectedAccountProperty);
|
||||
set => SetValue(SelectedAccountProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TimeInForces
|
||||
public static readonly DependencyProperty TimeInForcesProperty =
|
||||
WPFHelper.create_dp<IEnumerable, OrderParamsPanel>(nameof(TimeInForces), Enumerable.Empty<string>());
|
||||
|
||||
public IEnumerable TimeInForces {
|
||||
get => GetValue(TimeInForcesProperty) as IEnumerable;
|
||||
set => SetValue(TimeInForcesProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SelectedTimeInForce
|
||||
public static readonly DependencyProperty SelectedTimeInForceProperty =
|
||||
WPFHelper.create_dp<object, OrderParamsPanel>(nameof(SelectedTimeInForce));
|
||||
|
||||
public object SelectedTimeInForce {
|
||||
get => GetValue(SelectedTimeInForceProperty);
|
||||
set => SetValue(SelectedTimeInForceProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Security
|
||||
public static readonly DependencyProperty SecurityProperty =
|
||||
WPFHelper.create_dp<SecurityImmutable?, OrderParamsPanel>(
|
||||
nameof(Security),
|
||||
(o, v) => o.CoerceValue(ContractSpecProperty));
|
||||
|
||||
public SecurityImmutable? Security {
|
||||
get => GetValue(SecurityProperty) as SecurityImmutable?;
|
||||
set => SetValue(SecurityProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ContractSpec
|
||||
public static readonly DependencyProperty ContractSpecProperty =
|
||||
WPFHelper.create_dp<ContractSpecificationImmutable?, OrderParamsPanel>(
|
||||
nameof(ContractSpec),
|
||||
null,
|
||||
(d, base_value) => d.Security?.ContractSpec ?? base_value ?? SecurityDataHelper.Defaults.ContractSpecificationImmutable);
|
||||
|
||||
public ContractSpecificationImmutable? ContractSpec {
|
||||
get => GetValue(ContractSpecProperty) as ContractSpecificationImmutable?;
|
||||
set => SetValue(ContractSpecProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AlgoRoots
|
||||
public static readonly DependencyProperty AlgoRootsProperty =
|
||||
WPFHelper.create_dp<IReadOnlyObservableCollection<IAlgoVM>, OrderParamsPanel>(nameof(AlgoRoots));
|
||||
|
||||
public IReadOnlyObservableCollection<IAlgoVM> AlgoRoots {
|
||||
get => GetValue(AlgoRootsProperty) as IReadOnlyObservableCollection<IAlgoVM>;
|
||||
set => SetValue(AlgoRootsProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SelectedAlgoRoot
|
||||
public static readonly DependencyProperty SelectedAlgoRootProperty =
|
||||
WPFHelper.create_dp<IAlgoVM, OrderParamsPanel>(nameof(SelectedAlgoRoot));
|
||||
|
||||
public IAlgoVM SelectedAlgoRoot {
|
||||
get => GetValue(SelectedAlgoRootProperty) as IAlgoVM;
|
||||
set => SetValue(SelectedAlgoRootProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AlgoParamRows
|
||||
public static readonly DependencyProperty AlgoParamRowsProperty =
|
||||
WPFHelper.create_dp<IReadOnlyObservableCollection<AlgoParamRowVM>, OrderParamsPanel>(nameof(AlgoParamRows));
|
||||
|
||||
public IReadOnlyObservableCollection<AlgoParamRowVM> AlgoParamRows {
|
||||
get => GetValue(AlgoParamRowsProperty) as IReadOnlyObservableCollection<AlgoParamRowVM>;
|
||||
set => SetValue(AlgoParamRowsProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CanUserChangeAlgoRoot
|
||||
public static readonly DependencyProperty CanUserChangeAlgoRootProperty =
|
||||
WPFHelper.create_dp<bool, OrderParamsPanel>(nameof(CanUserChangeAlgoRoot));
|
||||
|
||||
public bool CanUserChangeAlgoRoot {
|
||||
get => (bool)GetValue(CanUserChangeAlgoRootProperty);
|
||||
set => SetValue(CanUserChangeAlgoRootProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ShowAlgoTypeNodes
|
||||
public static readonly DependencyProperty ShowAlgoTypeNodesProperty =
|
||||
WPFHelper.create_dp<bool, OrderParamsPanel>(nameof(ShowAlgoTypeNodes));
|
||||
|
||||
public bool ShowAlgoTypeNodes {
|
||||
get => (bool)GetValue(ShowAlgoTypeNodesProperty);
|
||||
set => SetValue(ShowAlgoTypeNodesProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override void OnApplyTemplate() {
|
||||
Disposable.replace(ref _bbo_button_navigation_hooks, new CompositeDisposable());
|
||||
_algo_editor = GetTemplateChild("algo_editor") as AlgoEditorView;
|
||||
// default directional navigation is wrong, so diy!
|
||||
Disposable.replace(ref _bbo_button_navigation_hooks, _connect_bbo_button_navigation(
|
||||
GetTemplateChild("price_entry_control") as FrameworkElement,
|
||||
GetTemplateChild("size_tick_up_down") as FrameworkElement,
|
||||
GetTemplateChild("best_bid_button") as Button,
|
||||
GetTemplateChild("best_bid_size_button") as Button,
|
||||
GetTemplateChild("best_ask_button") as Button,
|
||||
GetTemplateChild("best_ask_size_button") as Button,
|
||||
GetTemplateChild("calculator_buttons_items_control") as ItemsControl));
|
||||
}
|
||||
|
||||
static IDisposable _connect_bbo_button_navigation(FrameworkElement price, FrameworkElement size, Button best_bid, Button best_bid_size, Button best_ask, Button best_ask_size, ItemsControl calculator_buttons) {
|
||||
// BBO Orientation is as follows:
|
||||
// Price Entry | Best Bid Price | Best Ask Price
|
||||
// Size Entry | Best Bid Size | Best Ask Size
|
||||
// Button | Button | Button | Button | Button | etc. (calculator buttons)
|
||||
return Disposable.Create(
|
||||
NavigationHelper.connect_navigation(price, null, null, best_bid, size, ModifierKeys.Control),
|
||||
NavigationHelper.connect_navigation(best_bid, price, null, best_ask, best_bid_size),
|
||||
NavigationHelper.connect_navigation(best_ask, best_bid, null, null, best_ask_size),
|
||||
NavigationHelper.connect_navigation(size, () => null, () => price, () => best_bid_size, () => calculator_buttons.ItemContainerGenerator.ContainerFromIndex(0) as FrameworkElement, ModifierKeys.Control),
|
||||
NavigationHelper.connect_navigation(best_bid_size, () => size, () => best_bid, () => best_ask_size, () => get_y_neighbor(best_bid_size)),
|
||||
NavigationHelper.connect_navigation(best_ask_size, () => best_bid_size, () => best_ask, () => null, () => get_y_neighbor(best_ask_size)),
|
||||
NavigationHelper.connect_navigation(calculator_buttons, e => e.OriginalSource as FrameworkElement, el => get_adjacent_calc_button(el, -1), el => get_y_neighbor(el, -1), el => get_adjacent_calc_button(el, 1), el => null));
|
||||
// local methods
|
||||
FrameworkElement get_y_neighbor(FrameworkElement control, int direction = 1) {
|
||||
if (control.try_find_ancestor_of_type(out Grid grid)) {
|
||||
var pt = control.TranslatePoint(new Point(control.ActualWidth / 2, control.ActualHeight / 2), grid);
|
||||
// this is a hack, but since it is only for keyboard directional navigation, it's not the end of the world if this breaks (should clean this up when time/priorities permit).
|
||||
// as long as the row heights are roughly uniform, and the controls are vertically stretched, this should be fine.
|
||||
return grid.ChildAtPoint<Button>(new Point(pt.X, pt.Y + (control.ActualHeight * direction)));
|
||||
} else return null;
|
||||
}
|
||||
FrameworkElement get_adjacent_calc_button(FrameworkElement item, int direction) {
|
||||
int i = calculator_buttons.ItemContainerGenerator.IndexFromContainer(item.find_ancestor_of_type<ContentPresenter>());
|
||||
if ((i + direction >= 0) && (i + direction) < calculator_buttons.Items.Count) {
|
||||
var target_el = calculator_buttons.ItemContainerGenerator.ContainerFromIndex(i + direction) as FrameworkElement;
|
||||
return target_el?.descendants_of_type<Button>()?.FirstOrDefault();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class VisualStateNames {
|
||||
public static class BBO {
|
||||
public const string GroupName = nameof(BBO);
|
||||
|
||||
public static readonly string
|
||||
Active = $"{GroupName}{VisualStates.Active}",
|
||||
Inactive = $"{GroupName}{VisualStates.Inactive}";
|
||||
}
|
||||
|
||||
public static class CalculatorButtons {
|
||||
public const string GroupName = nameof(CalculatorButtons);
|
||||
|
||||
public static readonly string
|
||||
Normal = $"{GroupName}{nameof(Normal)}",
|
||||
Stretched = $"{GroupName}{nameof(Stretched)}";
|
||||
}
|
||||
|
||||
public static class General {
|
||||
public const string GroupName = nameof(General);
|
||||
|
||||
public static readonly string
|
||||
Normal = $"{GroupName}{nameof(Normal)}",
|
||||
ReadOnly = $"{GroupName}{nameof(ReadOnly)}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using bts.api;
|
||||
using bts.api.md.auxiliary;
|
||||
using bts.spark;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
@@ -6,7 +7,6 @@ using mbts.ui;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using static mbts.ui.shell.ProtoBufSerializationHelper;
|
||||
@@ -21,17 +21,9 @@ namespace bts.shell {
|
||||
AlgoStudioWindowFactory _aswf;
|
||||
|
||||
[ImportingConstructor]
|
||||
public TradingShellModule(
|
||||
IBus bus,
|
||||
IWindowManager wm,
|
||||
INotificationService notification_service,
|
||||
INotificationServiceDataCache notification_service_data_cache,
|
||||
UserSettingsHub settings_hub,
|
||||
OrdersModel orders_model,
|
||||
PAndLModel pandl_model,
|
||||
md.MBOService mbo,
|
||||
ErrorReporter error_reporter) {
|
||||
_lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model, mbo);
|
||||
public TradingShellModule(IBus bus, IWindowManager wm, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache,
|
||||
UserSettingsHub settings_hub, OrdersModel orders_model, PAndLModel pandl_model, MarketDataAuxiliaryModel md_auxiliary_model, ErrorReporter error_reporter) {
|
||||
_lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model, md_auxiliary_model);
|
||||
_obwf = new OrderBookWindowFactory(this, bus, notification_service, notification_service_data_cache, orders_model, settings_hub);
|
||||
_fwf = new FillsWindowFactory(this, bus, settings_hub, orders_model);
|
||||
_plwf = new PAndLWindowFactory(this, bus, settings_hub, pandl_model);
|
||||
@@ -45,8 +37,8 @@ namespace bts.shell {
|
||||
public int priority => 20;
|
||||
|
||||
public IEnumerable<SparkCommand> actions => new [] {
|
||||
SparkCommands.ShowOrders.complete(() => bts.shell.orderbook.OrderBookStartupData.Default),
|
||||
SparkCommands.ShowFills(null).complete(() => new FillsWindowFactory.StartupData()),
|
||||
SparkCommands.ShowOrders.complete(() => OrderBookWindowFactory.OrderBookStartupData.Default),
|
||||
SparkCommands.ShowFills.complete(() => new FillsWindowFactory.StartupData()),
|
||||
SparkCommands.ShowPAndL.complete(() => new PAndLWindowFactory.PAndLStartupData()),
|
||||
SparkCommands.PickLadder,
|
||||
SparkCommands.PickOrderTicket,
|
||||
@@ -61,8 +53,9 @@ namespace bts.shell {
|
||||
yield return _aswf;
|
||||
}
|
||||
|
||||
public Task<IShellWindow> create(Stream load_stream) =>
|
||||
dispatch_to_factory(load_stream, window_factories().ToArray());
|
||||
public Task<IShellWindow> create(Stream load_stream) {
|
||||
return dispatch_to_factory(load_stream, window_factories().ToLArray());
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
foreach (var i in window_factories())
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using bts.spark;
|
||||
using mbts;
|
||||
using mbts.collections;
|
||||
|
||||
@@ -2,16 +2,19 @@
|
||||
using bts.api;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.config;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.ui.extensions;
|
||||
using mbts.ui.utility;
|
||||
using NAudio.Wave;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace mbts.spark {
|
||||
class AlgoParamVM<T> : IAlgoParamVM<T>, INotifyPropertyChanged {
|
||||
readonly IEqualityComparer<T> _comparer;
|
||||
AlgoParamPermissions _permissions;
|
||||
bool _is_enabled, _has_value;
|
||||
T _committed_value, _value;
|
||||
|
||||
public AlgoParamVM(string id, string display_name)
|
||||
: this(id, display_name, default, EqualityComparer<T>.Default) { }
|
||||
|
||||
public AlgoParamVM(string id, string display_name, T value)
|
||||
: this(id, display_name, value, EqualityComparer<T>.Default) { }
|
||||
|
||||
public AlgoParamVM(string id, string display_name, T value, IEqualityComparer<T> comparer) {
|
||||
this.id = id;
|
||||
this.display_name = display_name;
|
||||
_value = value;
|
||||
_committed_value = value;
|
||||
_comparer = comparer;
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
public event PropertyChange<T> value_changed;
|
||||
public event PropertyChange<AlgoParamPermissions> permissions_changed;
|
||||
public event Action<bool> is_dirty_changed;
|
||||
|
||||
public string id { get; }
|
||||
public string display_name { get; }
|
||||
public T value {
|
||||
get => _value;
|
||||
set {
|
||||
if (_set_value(value) && (is_dirty == _comparer.Equals(value, _committed_value)))
|
||||
_toggle_is_dirty();
|
||||
}
|
||||
}
|
||||
public AlgoParamPermissions permissions {
|
||||
get => _permissions;
|
||||
set {
|
||||
if (_set(ref _permissions, value, out var oldval))
|
||||
permissions_changed?.Invoke(oldval, value);
|
||||
}
|
||||
}
|
||||
public bool is_enabled { get => _is_enabled; set => _set(ref _is_enabled, value); }
|
||||
public bool has_value { get => _has_value; set => _set(ref _has_value, value); }
|
||||
public bool is_dirty { get; private set; }
|
||||
object IAlgoParamVM.value { get => value; set => this.value = (T)value; }
|
||||
|
||||
public void revert() {
|
||||
if (is_dirty) {
|
||||
_set_value_core(_committed_value);
|
||||
_toggle_is_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public IObservable<T> subscribe_value() {
|
||||
return Observable.Create<T>(o => {
|
||||
o.OnNext(_value);
|
||||
value_changed += on_value_changed;
|
||||
return Disposable.Create(() => value_changed -= on_value_changed);
|
||||
void on_value_changed(T oldval, T newval) =>
|
||||
o.OnNext(newval);
|
||||
});
|
||||
}
|
||||
|
||||
public void mark_committed() {
|
||||
_committed_value = _value;
|
||||
if (is_dirty)
|
||||
_toggle_is_dirty();
|
||||
}
|
||||
|
||||
bool _set<TValue>(ref TValue backing, TValue new_value, [CallerMemberName]string prop = "") =>
|
||||
_set(ref backing, new_value, EqualityComparer<TValue>.Default, prop);
|
||||
|
||||
bool _set<TValue>(ref TValue backing, TValue new_value, out TValue old_value, [CallerMemberName]string prop = "") =>
|
||||
_set(ref backing, new_value, out old_value, EqualityComparer<TValue>.Default, prop);
|
||||
|
||||
bool _set<TValue>(ref TValue backing, TValue new_value, out TValue old_value, IEqualityComparer<TValue> comparer, [CallerMemberName]string prop = "") {
|
||||
old_value = backing;
|
||||
return _set(ref backing, new_value, comparer, prop);
|
||||
}
|
||||
|
||||
bool _set<TValue>(ref TValue backing, TValue new_value, IEqualityComparer<TValue> comparer, [CallerMemberName]string prop = "") {
|
||||
if (comparer.Equals(backing, new_value))
|
||||
return false;
|
||||
backing = new_value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _set_value(T new_value) {
|
||||
if (_comparer.Equals(new_value, _value))
|
||||
return false;
|
||||
_set_value_core(new_value);
|
||||
return true;
|
||||
}
|
||||
|
||||
void _set_value_core(T new_value) {
|
||||
if (value_changed == null) {
|
||||
_value = new_value;
|
||||
} else {
|
||||
value_changed(_value, _value = new_value);
|
||||
}
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(value)));
|
||||
}
|
||||
|
||||
void _toggle_is_dirty() {
|
||||
is_dirty = !is_dirty;
|
||||
is_dirty_changed?.Invoke(is_dirty);
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(is_dirty)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,12 +79,10 @@ namespace mbts.spark {
|
||||
RequestClose?.Invoke(this, new EventArgs());
|
||||
}
|
||||
});
|
||||
bbo = new BBOVM();
|
||||
_dispose = Disposable.Create(
|
||||
size_calculator,
|
||||
properties_vm,
|
||||
security_md_source.OnDispatcher().Subscribe(smd => bbo.security_md = smd),
|
||||
bbo,
|
||||
bbo = new BBOVM(security_md_source),
|
||||
order_source.OnDispatcher().Subscribe(ort => {
|
||||
key = ort.order.Key;
|
||||
if (DoubleHelper.IsNaNOrInfinity(price))
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace mbts.spark {
|
||||
) {
|
||||
_security = backing(nameof(security), security_md_source.Select(smd => smd.security), SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
properties_vm = new OrderPropertiesEditorVM(model, security_md_source, props_source.Select(p => p.order_props));
|
||||
bbo = new BBOVM();
|
||||
bbo = new BBOVM(security_md_source);
|
||||
send_order = new DelegateCommand<Side>(side => {
|
||||
bool price_valid = _validate_price(), size_valid = size_calculator.validate();
|
||||
if (price_valid && size_valid && !HasErrors && !properties_vm.HasErrors) {
|
||||
@@ -59,8 +59,6 @@ namespace mbts.spark {
|
||||
});
|
||||
set_price_command = new DelegateCommand<double>(set_price);
|
||||
_dispose = Disposable.Create(
|
||||
bbo,
|
||||
security_md_source.OnDispatcher().Subscribe(smd => bbo.security_md = smd),
|
||||
size_calculator = new CalculatorLogic(0, calculator_buttons),
|
||||
props_source.OnDispatcher().Subscribe(p => on_props(p.price, p.size))
|
||||
);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.shell;
|
||||
using bts.utils;
|
||||
using mbts;
|
||||
using mbts.spark;
|
||||
using mbts.ui.controls;
|
||||
@@ -84,18 +83,25 @@ namespace bts.spark {
|
||||
public static string modified_description(OrderRecordImmutable old_order, OrderRecordImmutable new_order, ContractSpecificationImmutable? spec) {
|
||||
double old_price = old_order.State.Price, new_price = new_order.State.Price;
|
||||
uint old_size = old_order.State.Quantity, new_size = new_order.State.Quantity;
|
||||
uint old_attached_target_key = old_order.attached_target_order_key(), new_attached_target_key = new_order.attached_target_order_key();
|
||||
return StringBuilderEx.join(prefix, change_descs());
|
||||
//
|
||||
string prefix() => $"({old_order.Key}) Changed";
|
||||
IEnumerable<string> change_descs() {
|
||||
if (old_price != new_price)
|
||||
yield return $"price from {spec.format(old_price)} to {spec.format(new_price)}";
|
||||
if (old_size != new_size)
|
||||
yield return $"size from {old_size} to {new_size}, working {new_order.working_quantity()}";
|
||||
if (old_attached_target_key != new_attached_target_key)
|
||||
yield return $"attached target from {old_attached_target_key} to {new_attached_target_key}";
|
||||
string modified_size_partial_description(int working_size) {
|
||||
return $"size from {old_size} to {new_size}, working {working_size}";
|
||||
}
|
||||
string modified_price_partial_description() {
|
||||
return $"price from {spec.format(old_price)} to {spec.format(new_price)}";
|
||||
}
|
||||
if (old_price != new_price || old_size != new_size) {
|
||||
StringBuilder sb = new StringBuilder($"({old_order.Key}) Changed ");
|
||||
if (old_price != new_price) {
|
||||
sb.Append(modified_price_partial_description());
|
||||
if (old_size != new_size)
|
||||
sb.Append($", {modified_size_partial_description(new_order.working_quantity())}");
|
||||
}
|
||||
else
|
||||
sb.Append(modified_size_partial_description(new_order.working_quantity()));
|
||||
return sb.ToString();
|
||||
}
|
||||
else
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
static string _header(uint key) => $"({key})";
|
||||
@@ -178,31 +184,21 @@ namespace bts.spark {
|
||||
format_cancel_error(order, security?.ContractSpec, error));
|
||||
}
|
||||
|
||||
internal void update_order(OrderRecordImmutable order, double price, uint size, bool algo_changed) =>
|
||||
_log_order_update(order, price, size, algo_changed, "change from");
|
||||
|
||||
internal void update_order_failed(OrderRecordImmutable order, double price, uint size, bool algo_changed) =>
|
||||
_log_order_update(order, price, size, algo_changed, "failed to change from");
|
||||
|
||||
void _log_order_update(OrderRecordImmutable order, double price, uint size, bool algo_changed, string change_desc_prefix) {
|
||||
if (price == order.price() && size == order.working_quantity() && !algo_changed) {
|
||||
// nothing actually changed
|
||||
return;
|
||||
}
|
||||
const string algo_change_text = "change algo parameters";
|
||||
internal void update_order(OrderRecordImmutable order, double price, uint size) {
|
||||
var security = try_get_security(order.Def.SecurityKey);
|
||||
var spec = security?.ContractSpec;
|
||||
var desc_builder = new StringBuilderEx();
|
||||
if (price != order.price() || size != order.working_quantity()) {
|
||||
desc_builder.Add(order.State.Quantity.ToString(), "@", spec.format(order.State.Price), "to ", size.ToString(), "@", spec.format(price));
|
||||
if (algo_changed)
|
||||
desc_builder.Add("; ", algo_change_text);
|
||||
} else if (algo_changed) {
|
||||
desc_builder.Add(algo_change_text);
|
||||
}
|
||||
desc_builder.Insert(0, $"{change_desc_prefix} ");
|
||||
desc_builder.Insert(0, $"({order.Key}) ");
|
||||
log_status(ActivityLogCategory.modify, security, desc_builder.ToString());
|
||||
log_status(ActivityLogCategory.modify,
|
||||
security,
|
||||
$"({order.Key}) change from {order.State.Quantity}@{spec.format(order.State.Price)} to {size}@{spec.format(price)}");
|
||||
}
|
||||
|
||||
internal void update_order_failed(OrderRecordImmutable order, double price, uint size) {
|
||||
var security = try_get_security(order.Def.SecurityKey);
|
||||
var spec = security?.ContractSpec;
|
||||
log_status(ActivityLogCategory.modify,
|
||||
security,
|
||||
$"({order.Key}) failed to change from {order.State.Quantity}@{spec.format(order.State.Price)} to {size}@{spec.format(price)}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -57,18 +57,15 @@ namespace mbts.spark {
|
||||
readonly SelectorProperty<AccountTuple, AccountTuple?> _accounts;
|
||||
readonly SelectorProperty<TimeInForce> _time_in_force;
|
||||
readonly SelectionModel<AlgoDefinitionVM> _selected_algo_definition;
|
||||
readonly SetStack<AlgoKey> _selected_algo_stack = new SetStack<AlgoKey>();
|
||||
readonly ObservableKeyedCollection<AlgoDefinitionVM, AlgoKey> _algo_definitions = new ObservableKeyedCollection<AlgoDefinitionVM, AlgoKey>(d => d.key);
|
||||
readonly ListCollectionView<AlgoDefinitionVM> _algo_definitions_view, _favorite_algo_definitions_view;
|
||||
readonly TableUpdateBuffer<AlgoData, AlgoKey> _definitions_buffer = new TableUpdateBuffer<AlgoData, AlgoKey>(d => d.key);
|
||||
readonly EditableCollectionModel<AlgoDefinitionTemplateVM, AlgoDefinitionVM> _algo_template_collection_model;
|
||||
readonly ObservableKeyedCollection<AlgoDefinitionTemplateVM, AlgoKey> _favorite_templates = new ObservableKeyedCollection<AlgoDefinitionTemplateVM, AlgoKey>(tvm => tvm.key);
|
||||
readonly ObservableAggregateCollection<AlgoParamRowVM> _algo_param_rows = new ObservableAggregateCollection<AlgoParamRowVM>();
|
||||
IReadOnlyDictionary<AlgoKey, AlgoDefinitionMetadata> _algo_metadata = DictionaryHelper.empty<AlgoKey, AlgoDefinitionMetadata>();
|
||||
IReadOnlyDictionary<AlgoKey, int> _favorite_algos_index = DictionaryHelper.empty<AlgoKey, int>();
|
||||
IDisposable _algo_param_rows_hook;
|
||||
IReadOnlyDictionary<AlgoKey, int> _favorite_algos_index = DictionaryHelper.empty<AlgoKey, int>();
|
||||
int _favorite_algos_column_count;
|
||||
bool _open_algo_popup_on_select, _use_algo_param_grid;
|
||||
bool _open_algo_popup_on_select;
|
||||
|
||||
public OrderPropertiesEditorVM(OrdersModel model, IObservable<SecurityMD> security_md_source, IObservable<InlineOrderProperties> order_properties) {
|
||||
_model = model;
|
||||
@@ -92,9 +89,8 @@ namespace mbts.spark {
|
||||
return (d.selected_format & sec_fmt) == sec_fmt;
|
||||
};
|
||||
_algo_definitions_view.LiveFilteringProperties.Add(nameof(AlgoDefinitionVM.selected_format));
|
||||
_algo_definitions_view.LiveSortingProperties.Add(nameof(AlgoDefinitionVM.group));
|
||||
_algo_definitions_view.LiveSortingProperties.Add(nameof(AlgoDefinitionVM.name));
|
||||
algo_definitions_view.GroupDescriptions.Add(new PropertyGroupDescription(nameof(AlgoDefinitionVM.group)));
|
||||
algo_definitions_view.GroupDescriptions.Add(new PropertyGroupDescription(nameof(AlgoDefinitionVM.is_factory_definition)));
|
||||
_favorite_algo_definitions_view = new ListCollectionView<AlgoDefinitionVM>(_algo_definitions);
|
||||
_favorite_algo_definitions_view.SortDescriptions.Add(new SortDescription(nameof(AlgoDefinitionVM.favorite_index), ListSortDirection.Ascending));
|
||||
_favorite_algo_definitions_view.LiveSortingProperties.Add(nameof(AlgoDefinitionVM.favorite_index));
|
||||
@@ -153,21 +149,9 @@ namespace mbts.spark {
|
||||
d => d.load_committed(),
|
||||
d => d == null ? Observable.Return(false) : d.subscribe_is_definition_dirty(),
|
||||
out IObserver<AlgoDefinitionVM> revert_definition_observer);
|
||||
// algo params dialog skirt
|
||||
set_default_algo_params_cmd = ObservantCommand.create(() => {
|
||||
algo_param_rows.commit_all();
|
||||
_close_algo_popup();
|
||||
}, out IObserver<bool> set_default_algo_params_observer);
|
||||
revert_algo_params_cmd = ObservantCommand.create(() => {
|
||||
algo_param_rows.revert_all();
|
||||
_close_algo_popup();
|
||||
}, out IObserver<bool> revert_algo_params_observer);
|
||||
var algo_params_dirty_mon = new ReactiveThresholdMonitor<AlgoParamRowVM>(r => r.param.subscribe_is_dirty());
|
||||
var nullable_security_md_source = security_md_source.Select(smd => new SecurityMD?(smd)).RefCountReplay(1);
|
||||
//
|
||||
_dispose = new CompositeDisposable(
|
||||
algo_params_dirty_mon.Subscribe(set_default_algo_params_observer, revert_algo_params_observer),
|
||||
ObservableAutoWrapper.connect_item_hooks(_algo_param_rows, algo_params_dirty_mon.join),
|
||||
_selected_algo_definition.subscribe_changed(_selected_algo_definition_Changed),
|
||||
as_demuxer().subscribe<AlgoDefinitionTemplateVM>(nameof(selected_algo_template)).Subscribe(
|
||||
undo_observer,
|
||||
@@ -197,7 +181,7 @@ namespace mbts.spark {
|
||||
security_md_source.Select(smd => model.factory.default_algos(smd.security)).Table(s => s.key).Subscribe(factory_defs => {
|
||||
using (_selected_algo_definition.begin_update())
|
||||
AlgoCollectionHelper.update_persisted_definitions(_algo_definitions, factory_defs, fd => {
|
||||
var vm = AlgoDefinitionVM.create(_model, fd, init_format: OrderDataHelper.get_algo_format(security.ContractCase), editing_permissions: AlgoEditingPermissions.AllowEditValues);
|
||||
var vm = AlgoDefinitionVM.create(_model, fd, init_format: OrderDataHelper.get_algo_format(security.ContractCase));
|
||||
vm.selected_format = OrderDataHelper.get_algo_format(security.ContractCase);
|
||||
return vm;
|
||||
});
|
||||
@@ -205,7 +189,7 @@ namespace mbts.spark {
|
||||
model.algo_model.definitions.Subscribe(_definitions_buffer),
|
||||
_definitions_buffer.OnDispatcher().Subscribe(u => {
|
||||
AlgoCollectionHelper.update_persisted_definitions(_algo_definitions, u, def_data => {
|
||||
var def_vm = AlgoDefinitionVM.create(model, def_data, true, OrderDataHelper.get_algo_format(security.ContractCase), AlgoEditingPermissions.AllowEditValues);
|
||||
var def_vm = AlgoDefinitionVM.create(model, def_data, true, OrderDataHelper.get_algo_format(security.ContractCase));
|
||||
def_vm.selected_format = OrderDataHelper.get_algo_format(security.ContractCase);
|
||||
def_vm.favorite_index = _get_favorite_algo_index(def_vm.key);
|
||||
return def_vm;
|
||||
@@ -221,10 +205,7 @@ namespace mbts.spark {
|
||||
d.selected_format = fmt;
|
||||
}),
|
||||
vm), true)) {
|
||||
() => {
|
||||
Disposable.dispose(ref _template_collection_sync);
|
||||
Disposable.dispose(ref _algo_param_rows_hook);
|
||||
}
|
||||
() => Disposable.dispose(ref _template_collection_sync)
|
||||
};
|
||||
async Task save_template_async(AlgoDefinitionTemplateVM template) {
|
||||
using (_preserve_selection_by_index())
|
||||
@@ -248,6 +229,7 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,13 +237,8 @@ namespace mbts.spark {
|
||||
IDisposable _template_collection_sync;
|
||||
void _selected_algo_definition_Changed(AlgoDefinitionVM old_value, AlgoDefinitionVM new_value, SelectionReason reason) {
|
||||
Disposable.dispose(ref _template_collection_sync);
|
||||
Disposable.dispose(ref _algo_param_rows_hook);
|
||||
if (new_value != null) {
|
||||
_template_collection_sync = ObservableAutoWrapper.connect_two_way(new_value.templates_with_custom, algo_templates);
|
||||
if (use_algo_param_grid)
|
||||
_algo_param_rows_hook = _connect_algo_param_rows(new_value, _algo_param_rows);
|
||||
if (reason != SelectionReason.Explicit)
|
||||
_selected_algo_stack.Push(new_value.key);
|
||||
} else {
|
||||
algo_templates.Clear();
|
||||
}
|
||||
@@ -287,21 +264,9 @@ namespace mbts.spark {
|
||||
public TimeInForce selected_time_in_force { get => _time_in_force.selected_item; set => _time_in_force.selected_item = value; }
|
||||
public CustomOrderDefinitionImmutable? definition => selected_algo_template?.algo_root?.definition;
|
||||
public int favorite_algos_column_count { get => _favorite_algos_column_count; private set => Set(ref _favorite_algos_column_count, value); }
|
||||
public bool use_algo_param_grid {
|
||||
get => _use_algo_param_grid;
|
||||
set {
|
||||
if (_use_algo_param_grid != value) {
|
||||
_use_algo_param_grid = value;
|
||||
if (value) {
|
||||
Disposable.replace(ref _algo_param_rows_hook, _connect_algo_param_rows(selected_algo_definition, _algo_param_rows));
|
||||
} else Disposable.dispose(ref _algo_param_rows_hook);
|
||||
}
|
||||
}
|
||||
}
|
||||
// items sources
|
||||
public ObservableCollection<AlgoDefinitionVM> algo_definitions => _algo_definitions;
|
||||
public ObservableCollection<AlgoDefinitionTemplateVM> algo_templates { get; } = new ObservableCollection<AlgoDefinitionTemplateVM>();
|
||||
public IReadOnlyObservableCollection<AlgoParamRowVM> algo_param_rows => _algo_param_rows;
|
||||
public ICollectionView<AlgoDefinitionVM> algo_definitions_view => _algo_definitions_view;
|
||||
public ICollectionView<AlgoDefinitionVM> favorite_algo_definitions_view => _favorite_algo_definitions_view;
|
||||
public ICollectionView algo_templates_view { get; }
|
||||
@@ -344,12 +309,8 @@ namespace mbts.spark {
|
||||
public AlgoDefinitionVM selected_favorite_algo_definition {
|
||||
get => selected_algo_definition?.is_favorite ?? false ? selected_algo_definition : null;
|
||||
set {
|
||||
if (value != null) {
|
||||
if (value != null)
|
||||
_selected_algo_definition.select_explicit(value);
|
||||
} else if (_selected_algo_stack.TryPeek(out AlgoKey k)) {
|
||||
if (_algo_definitions.TryGetValue(k, out AlgoDefinitionVM vm))
|
||||
_selected_algo_definition.select_explicit(vm);
|
||||
}
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
@@ -370,8 +331,6 @@ namespace mbts.spark {
|
||||
public ICommand save_definition_cmd { get; }
|
||||
public ICommand save_template_cmd { get; }
|
||||
public ICommand revert_definition_cmd { get; }
|
||||
public ICommand set_default_algo_params_cmd { get; }
|
||||
public ICommand revert_algo_params_cmd { get; }
|
||||
public ICommand undo_cmd { get; }
|
||||
public ICommand redo_cmd { get; }
|
||||
public ICommand undo_definition_cmd { get; }
|
||||
@@ -387,9 +346,9 @@ namespace mbts.spark {
|
||||
_time_in_force.observe_current(start_immediately);
|
||||
|
||||
public IObservable<IAlgoVM> observe_order_type(bool start_immediately = false) =>
|
||||
_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);
|
||||
_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)
|
||||
@@ -400,22 +359,9 @@ namespace mbts.spark {
|
||||
if (_model.algo_model.templates_enabled()) {
|
||||
selected_algo_template.algo_root.assign_to(order.CustomDef);
|
||||
} else {
|
||||
var algo = new CustomOrderDefinition();
|
||||
selected_algo_definition.algo_root.assign_to(algo);
|
||||
switch (selected_algo_definition.key.mode) {
|
||||
case AlgoKey.Mode.Persisted:
|
||||
order.CustomDef = new CustomOrderDefinition {
|
||||
Composite = new CompositeOrderDefinition {
|
||||
AlgoTemplateKey = selected_algo_definition.key.persisted.id_uint,
|
||||
Definition = algo
|
||||
}
|
||||
};
|
||||
selected_algo_definition.algo_root.assign_to(order.CustomDef.Composite.Definition);
|
||||
break;
|
||||
default:
|
||||
order.CustomDef = algo;
|
||||
break;
|
||||
}
|
||||
selected_algo_definition.algo_root.assign_to(order.CustomDef);
|
||||
if (selected_algo_definition.key.mode == AlgoKey.Mode.Persisted)
|
||||
order.AlgoTemplateKey = selected_algo_definition.key.persisted.id_uint;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
@@ -435,14 +381,6 @@ namespace mbts.spark {
|
||||
favorite_algos_column_count = value.column_count;
|
||||
}
|
||||
|
||||
static IDisposable _connect_algo_param_rows(AlgoDefinitionVM def, ObservableAggregateCollection<AlgoParamRowVM> rows) =>
|
||||
AlgoParamRowsHelper.connect(def, rows, false);
|
||||
|
||||
void _close_algo_popup() {
|
||||
if (selected_algo_definition?.algo_root is IAlgoVM a)
|
||||
a.is_expanded = false;
|
||||
}
|
||||
|
||||
int? _get_favorite_algo_index(AlgoKey key) =>
|
||||
key.mode == AlgoKey.Mode.Persisted && _favorite_algos_index.TryGetValue(key, out int i) ? i : (int?)null;
|
||||
|
||||
|
||||
@@ -3,34 +3,21 @@ using mbts.spark;
|
||||
using System;
|
||||
|
||||
namespace bts.api.orderman {
|
||||
readonly struct OrderSecurity: IEquatable<OrderSecurity> {
|
||||
public readonly struct OrderSecurity: IEquatable<OrderSecurity> {
|
||||
public OrderSecurity(SecurityImmutable? sec, OrderRecordImmutable order) {
|
||||
this.security = sec;
|
||||
this.order = order;
|
||||
piq = null;
|
||||
algo = null;
|
||||
meta = null;
|
||||
}
|
||||
public OrderSecurity((SecurityImmutable? sec, OrderRecordImmutable order) t) {
|
||||
security = t.sec;
|
||||
order = t.order;
|
||||
piq = null;
|
||||
algo = null;
|
||||
meta = null;
|
||||
}
|
||||
public OrderSecurity(SecurityImmutable? sec, OrderRecordImmutable order, uint? piq) {
|
||||
this.security = sec;
|
||||
this.order = order;
|
||||
this.piq = piq;
|
||||
algo = null;
|
||||
meta = null;
|
||||
}
|
||||
public OrderSecurity(SecurityImmutable? sec, OrderRecordImmutable order, uint? piq, AlgoData? algo, AlgoDefinitionMetadata? meta) {
|
||||
this.security = sec;
|
||||
this.order = order;
|
||||
this.piq = piq;
|
||||
this.algo = algo;
|
||||
this.meta = meta;
|
||||
}
|
||||
//public OrderSecurity(OrderSecurity t) {
|
||||
// security = t.security;
|
||||
@@ -41,10 +28,6 @@ namespace bts.api.orderman {
|
||||
public SecurityImmutable? security { get; }
|
||||
public OrderRecordImmutable order { get; }
|
||||
public uint? piq { get; }
|
||||
public AlgoData? algo { get; }
|
||||
public AlgoDefinitionMetadata? meta { get; }
|
||||
|
||||
|
||||
|
||||
public override bool Equals(object obj) => (obj is OrderSecurity os) && Equals(os);
|
||||
public bool Equals(OrderSecurity other) => order.Equals(other.order) && security == other.security && piq == other.piq;
|
||||
@@ -58,39 +41,21 @@ namespace bts.api.orderman {
|
||||
struct OrderSecurityAlgoMeta : IEquatable<OrderSecurityAlgoMeta> {
|
||||
public readonly OrderRecordImmutable order;
|
||||
public readonly SecurityImmutable? security;
|
||||
/// TODO: refactor <see cref="algo"/> to 'studio_algo' or something that indicates it does not bear the same relation to this order, that <see cref="root_algo"/> does to the root order.
|
||||
/// The former is meant to hold studio algo information, and nothing else. Naturally, it indicates whether or not this is a studio order by being non-null (<see cref="is_studio_algo"/>).
|
||||
/// The latter, however, can be any kind of algo (studio, exchange, etc.); it holds information about the root-most algo in this order's branch.
|
||||
public readonly AlgoData? algo;
|
||||
public readonly AlgoDefinitionMetadata? meta;
|
||||
public readonly AlgoData root_algo;
|
||||
public readonly AlgoDefinitionMetadata? root_meta;
|
||||
|
||||
public OrderSecurityAlgoMeta(OrderRecordImmutable order, SecurityImmutable? security, AlgoData? algo, AlgoDefinitionMetadata? meta, AlgoData? root_algo = null, AlgoDefinitionMetadata? root_meta = null) {
|
||||
public OrderSecurityAlgoMeta(OrderRecordImmutable order, SecurityImmutable? security, AlgoData? algo, AlgoDefinitionMetadata? meta) {
|
||||
this.order = order;
|
||||
this.security = security;
|
||||
this.algo = algo;
|
||||
this.meta = meta;
|
||||
if (root_algo.HasValue) {
|
||||
this.root_algo = root_algo.Value;
|
||||
this.root_meta = root_meta;
|
||||
} else {
|
||||
this.root_algo = algo ?? new AlgoData(order.Def.CustomDef);
|
||||
this.root_meta = meta;
|
||||
}
|
||||
}
|
||||
|
||||
public bool is_studio_algo => algo.HasValue;
|
||||
|
||||
public OrderSecurityAlgoMeta with(AlgoData? root_algo, AlgoDefinitionMetadata? root_meta = null) =>
|
||||
new OrderSecurityAlgoMeta(order, security, algo, meta, root_algo, root_meta);
|
||||
|
||||
public bool Equals(OrderSecurityAlgoMeta other) =>
|
||||
order == other.order &&
|
||||
security == other.security &&
|
||||
algo == other.algo &&
|
||||
meta == other.meta &&
|
||||
root_algo == other.root_algo;
|
||||
meta == other.meta;
|
||||
|
||||
public override bool Equals(object obj) => EqualityComparer.equals_obj(this, obj);
|
||||
|
||||
@@ -99,10 +64,9 @@ namespace bts.api.orderman {
|
||||
order.GetHashCode(),
|
||||
security?.GetHashCode() ?? 0,
|
||||
algo?.GetHashCode() ?? 0,
|
||||
meta?.GetHashCode() ?? 0,
|
||||
root_algo.GetHashCode());
|
||||
meta?.GetHashCode() ?? 0);
|
||||
|
||||
public static bool operator ==(OrderSecurityAlgoMeta a, OrderSecurityAlgoMeta b) => a.Equals(b);
|
||||
public static bool operator !=(OrderSecurityAlgoMeta a, OrderSecurityAlgoMeta b) => !a.Equals(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,13 @@
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
class CoverOrderVM : OrderTypeVMBase<CoverOrderDefinition, CoverOrderDefinitionImmutable> {
|
||||
readonly Property<uint?> _hedge_ticks;
|
||||
readonly Property<int> _hedge_ticks;
|
||||
|
||||
public CoverOrderVM(AlgoKey key, string display_name, AlgoEditingPermissions editing_permissions)
|
||||
: base(key, display_name, editing_permissions) =>
|
||||
_hedge_ticks = add_nullable_property(nameof(hedge_ticks), CoverOrderDefinition.HedgeTicksFieldNumber, 1u, true);
|
||||
_hedge_ticks = add_property(nameof(hedge_ticks), CoverOrderDefinition.HedgeTicksFieldNumber, 1);
|
||||
|
||||
public uint? hedge_ticks { get => _hedge_ticks.Value; set => _hedge_ticks.set(value); }
|
||||
|
||||
protected override void validate() =>
|
||||
assert(hedge_ticks.HasValue, nameof(hedge_ticks), "Invalid hedge ticks.");
|
||||
public int hedge_ticks { get => _hedge_ticks.Value; set => _hedge_ticks.set(value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,9 @@
|
||||
using bts.collections;
|
||||
using bts.spark;
|
||||
using bts.spark;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using AlgoDef = bts.api.orderman.CustomOrderDefinition;
|
||||
@@ -15,9 +11,8 @@ using AlgoDefi = bts.api.orderman.CustomOrderDefinitionImmutable;
|
||||
using AlgoType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
|
||||
|
||||
namespace mbts.spark {
|
||||
class DefaultAlgoVM : DisposableViewModelBase, IAlgoVM, IDynamicAlgoVMImpl, IObserver<SecurityMD?>, IObserver<OrdersModel>, IObserver<AlgoEditingPermissions> {
|
||||
class DefaultAlgoVM : DisposableViewModelBase, IAlgoVM, IDynamicAlgoVMImpl, IObserver<SecurityMD?>, IObserver<OrdersModel> {
|
||||
static readonly AlgoDefi _empty = new AlgoDefi(new AlgoDef());
|
||||
readonly ObservableAggregateCollection<IAlgoParamVM> _algo_params = new ObservableAggregateCollection<IAlgoParamVM>();
|
||||
readonly VersionContainer<AlgoDefi> _definition;
|
||||
AlgoFormats _format;
|
||||
AlgoEditingPermissions _editing_permissions;
|
||||
@@ -32,6 +27,7 @@ namespace mbts.spark {
|
||||
public IAlgoVM impl { get; private set; }
|
||||
public AlgoKey id { get; } = new AlgoKey(AlgoType.None);
|
||||
public AlgoType order_type => _definition.value.CustomCase;
|
||||
public bool is_bts_type => false;
|
||||
public OrderMetadata meta => OrderDataHelper.get_metadata(order_type);
|
||||
public bool can_expand => impl?.can_expand ?? false;
|
||||
public bool is_expanded {
|
||||
@@ -41,33 +37,29 @@ namespace mbts.spark {
|
||||
impl.is_expanded = value;
|
||||
}
|
||||
}
|
||||
public AlgoEditingPermissions editing_permissions => _editing_permissions;
|
||||
public bool can_edit_values => impl?.can_edit_values ?? false;
|
||||
public bool can_edit_structure => impl?.can_edit_structure ?? false;
|
||||
public bool can_save_values => impl?.can_save_values ?? false;
|
||||
public bool can_save_structure => impl?.can_save_structure ?? false;
|
||||
public string display_name => impl?.display_name ?? "(Default)";
|
||||
public string abbreviation => display_name;
|
||||
public AlgoDisplayGroup group => impl?.group ?? AlgoDisplayGroup.BTS;
|
||||
public AlgoDefi definition => _definition.value;
|
||||
public ObservableCollection<IAlgoSelector> child_algo_selectors { get; } = new ObservableCollection<IAlgoSelector>();
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public ObservableCollection<IAlgoSelector> child_algo_selectors_view => child_algo_selectors;
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public dynamic children_vm => impl?.children_vm;
|
||||
|
||||
public IReadOnlyObservableCollection<IAlgoParamVM> algo_params => _algo_params;
|
||||
|
||||
public void assign_to(AlgoDef value) => value.MergeFrom(definition.CloneToMutable());
|
||||
public bool key_match(KeyEventArgs e) => false;
|
||||
public void load_from(AlgoDefi value) => impl?.load_from(value);
|
||||
public void load_many(IEnumerable<AlgoDefi> values) => impl?.load_many(values);
|
||||
public void set_permissions(AlgoEditingPermissions editing_permissions) {
|
||||
_editing_permissions = editing_permissions;
|
||||
impl?.set_permissions(editing_permissions);
|
||||
}
|
||||
public Task<AlgoDefi?> load_from_async(AlgoDefi definition) =>
|
||||
impl?.load_from_async(definition) ?? Task.FromResult<AlgoDefi?>(definition);
|
||||
void IAlgoVM.ensure_init() => impl?.ensure_init();
|
||||
|
||||
protected override void Dispose(bool disposing) =>
|
||||
_clear_impl();
|
||||
|
||||
void _clear_impl() {
|
||||
if (impl != null) {
|
||||
_algo_params.clear_collections();
|
||||
impl.definition_changed -= _on_impl_definition_changed;
|
||||
(impl as IDisposable)?.Dispose();
|
||||
impl = null;
|
||||
@@ -84,7 +76,6 @@ namespace mbts.spark {
|
||||
void IObserver<SecurityMD?>.OnCompleted() { }
|
||||
void IObserver<SecurityMD?>.OnError(Exception error) { }
|
||||
void IObserver<SecurityMD?>.OnNext(SecurityMD? security_md) {
|
||||
var orig_group = group;
|
||||
_security_md = security_md;
|
||||
if (security_md.HasValue)
|
||||
_format = OrderDataHelper.get_algo_format(security_md.Value.security.ContractCase);
|
||||
@@ -97,7 +88,6 @@ namespace mbts.spark {
|
||||
break;
|
||||
_clear_impl();
|
||||
impl = OrderTypeVMFactory.create(new AlgoKey(fmt_type), fmt_type.display_name(), fmt_type, is_expanded: true);
|
||||
_algo_params.add_collection(impl.algo_params);
|
||||
(impl as IObserver<OrdersModel>)?.OnNext(_model);
|
||||
break;
|
||||
}
|
||||
@@ -106,15 +96,6 @@ namespace mbts.spark {
|
||||
impl.definition_changed += _on_impl_definition_changed;
|
||||
}
|
||||
_on_impl_definition_changed();
|
||||
if (group != orig_group)
|
||||
RaisePropertyChanged(nameof(group));
|
||||
}
|
||||
|
||||
void IObserver<AlgoEditingPermissions>.OnCompleted() { }
|
||||
void IObserver<AlgoEditingPermissions>.OnError(Exception error) { }
|
||||
void IObserver<AlgoEditingPermissions>.OnNext(AlgoEditingPermissions editing_permissions) {
|
||||
Set(ref _editing_permissions, editing_permissions, nameof(this.editing_permissions));
|
||||
(impl as IObserver<AlgoEditingPermissions>)?.OnNext(editing_permissions);
|
||||
}
|
||||
|
||||
void _on_impl_definition_changed() {
|
||||
@@ -124,7 +105,6 @@ namespace mbts.spark {
|
||||
definition_changed?.Invoke();
|
||||
if (order_type != orig_def.CustomCase)
|
||||
RaisePropertyChanged(nameof(order_type), nameof(meta));
|
||||
RaisePropertyChanged(nameof(children_vm));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 651 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user