Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbf1fad2dd | ||
|
|
8a3a35a822 | ||
|
|
924b654f5f | ||
|
|
dac01b3004 | ||
|
|
508c542d56 | ||
|
|
9586b72653 | ||
|
|
ee21b4f597 | ||
|
|
964b516504 | ||
|
|
be6124e72f | ||
|
|
578b9e7570 | ||
|
|
fd66ef6993 | ||
|
|
1301130afb | ||
|
|
7daa96d246 | ||
|
|
49e5eab7d3 | ||
|
|
fee4541e38 | ||
|
|
1fbc1584e5 | ||
|
|
02defef93d | ||
|
|
9132b4a178 | ||
|
|
9390fb0639 | ||
|
|
82a8d007bf | ||
|
|
cff32ad09a | ||
|
|
7b9071a1b2 | ||
|
|
99f1d1fa01 | ||
|
|
03fe75cfd2 | ||
|
|
a7d71fe170 | ||
|
|
00ce3adde5 | ||
|
|
6d774d46c9 | ||
|
|
b39812aad0 | ||
|
|
fb059e5571 | ||
|
|
1864c27584 | ||
|
|
8b7f31ccd6 | ||
|
|
b91f998239 | ||
|
|
183916118e | ||
|
|
8d200ee7f3 | ||
|
|
d59d6fd095 | ||
|
|
c53f69bc53 | ||
|
|
e234a76199 | ||
|
|
cf432a82a0 | ||
|
|
5b40b4b4ca | ||
|
|
dd6b415ce7 | ||
|
|
f99717d880 | ||
|
|
b61cf170fe | ||
|
|
7915c84b6a | ||
|
|
3aece4fb97 | ||
|
|
f224b8be43 | ||
|
|
be4dd05ba6 | ||
|
|
570b362a7e | ||
|
|
944714adc5 | ||
|
|
fee7dcedb8 | ||
|
|
0883b91ef8 | ||
|
|
f43269f688 | ||
|
|
20728b7e8c | ||
|
|
b53060d665 | ||
|
|
01d94748a2 | ||
|
|
ee4ebd8ccf | ||
|
|
ef3e3d6edc | ||
|
|
6d4ff5707c | ||
|
|
158b8ac64d | ||
|
|
c11fc37781 | ||
|
|
e3ca2194d0 | ||
|
|
e49c8e32f2 | ||
|
|
63754199bf | ||
|
|
7af568a620 | ||
|
|
dcf509d21b | ||
|
|
b8c3b36a98 | ||
|
|
8e5afbfa40 | ||
|
|
906031d1c3 | ||
|
|
5eedc1a977 | ||
|
|
021c163b64 | ||
|
|
c6c71f0b04 | ||
|
|
bc8c01a789 | ||
|
|
b93f794116 | ||
|
|
5369e67c7a | ||
|
|
65c4b41c74 | ||
|
|
2ce9f7db34 | ||
|
|
ecad4471b3 | ||
|
|
9b4a99dcb7 | ||
|
|
8d805409d5 | ||
|
|
6a100bf3a6 | ||
|
|
4047e5bdee | ||
|
|
5aef712065 | ||
|
|
dd19ac4bce | ||
|
|
ff2102e0cd | ||
|
|
29762512ad | ||
|
|
289d40f24a | ||
|
|
947a905423 | ||
|
|
dfe9ea12c3 | ||
|
|
a9c2261671 | ||
|
|
b8766089f0 | ||
|
|
18d3c268de | ||
|
|
fdf255a7a3 | ||
|
|
995be6cfd0 | ||
|
|
52ac650c58 | ||
|
|
96c7cc1528 | ||
|
|
d81ef8668c | ||
|
|
dab95d7e91 | ||
|
|
88045ea432 | ||
|
|
01113e645a | ||
|
|
17c57878e1 | ||
|
|
4357fbc410 | ||
|
|
7d216978a3 | ||
|
|
fc3d84c02a | ||
|
|
f6fd9155b1 | ||
|
|
7723f1e004 | ||
|
|
041a7988c1 | ||
|
|
daa9800af2 | ||
|
|
dcb10a3484 | ||
|
|
70e826b212 | ||
|
|
af737564aa | ||
|
|
582f97d1ce | ||
|
|
a83c1eb633 | ||
|
|
1f893c73ad | ||
|
|
ef755ea6fb | ||
|
|
ebfc1bc485 | ||
|
|
a8646ebb3f | ||
|
|
28fd04293b | ||
|
|
111767522f | ||
|
|
429e16996a | ||
|
|
1ed85af40e | ||
|
|
f5457e5ce4 | ||
|
|
7465a38022 | ||
|
|
8afa03cc62 | ||
|
|
26dfd89d6c | ||
|
|
fe54d5b973 | ||
|
|
e7b9c3e228 | ||
|
|
ae5db799d4 | ||
|
|
5ba746fd59 | ||
|
|
ab5cb23062 | ||
|
|
4ebdc603c5 | ||
|
|
d573688064 | ||
|
|
14c601034a | ||
|
|
3de65fb027 | ||
|
|
9de0c4f530 | ||
|
|
ce5998c5dd | ||
|
|
dcbc6e756e | ||
|
|
4955c1a792 | ||
|
|
2990710b88 | ||
|
|
280ae81860 | ||
|
|
5e713d9008 | ||
|
|
763c2fa41a | ||
|
|
9d170e32f1 | ||
|
|
b4410f7412 | ||
|
|
b702ce9479 | ||
|
|
abc7b1e2e9 | ||
|
|
f71ff0e306 | ||
|
|
3c82327f1a | ||
|
|
f3ac92c434 | ||
|
|
a8694d6fb2 | ||
|
|
52e6536018 | ||
|
|
42274edbc4 | ||
|
|
6d438b4764 | ||
|
|
3688ec0739 | ||
|
|
ce8d27bb71 | ||
|
|
ee66ddf9a6 | ||
|
|
bea0e5ced7 | ||
|
|
b2d1d11826 | ||
|
|
d075f2ad0c | ||
|
|
409bbdc0f9 | ||
|
|
d4fd3900be | ||
|
|
e45736a692 | ||
|
|
bd8336e22b | ||
|
|
f3dedfc4e3 | ||
|
|
326bcb4544 | ||
|
|
884cdef97b | ||
|
|
5e3467e3b6 | ||
|
|
979c6e078a | ||
|
|
69f38daad7 | ||
|
|
a64d428a89 | ||
|
|
78c89c6677 | ||
|
|
8d0d678c01 | ||
|
|
8b9df305b4 | ||
|
|
02ee5cc6d3 | ||
|
|
207c93c972 | ||
|
|
14012a16da | ||
|
|
f4b7fbe6b5 | ||
|
|
9901ef54d4 | ||
|
|
293c13e11d | ||
|
|
dc264e1063 | ||
|
|
be4c69fad4 | ||
|
|
2e883808b6 | ||
|
|
2c4dc8fbe5 | ||
|
|
4ea93bc386 | ||
|
|
184a900eea | ||
|
|
3bf086c73a | ||
|
|
9838d123c7 | ||
|
|
9d5b80c990 | ||
|
|
040fc3af96 | ||
|
|
1c6333b07a | ||
|
|
cc07e28aca | ||
|
|
d2aec0c7f2 | ||
|
|
a71953b871 | ||
|
|
70e962000f | ||
|
|
f413938943 | ||
|
|
a7250b4734 | ||
|
|
060b88e4c9 | ||
|
|
26d543af82 | ||
|
|
99b301ad6f | ||
|
|
9cbe83e1a4 | ||
|
|
59369e1b2b | ||
|
|
7c42e75ddd | ||
|
|
ae200b374b | ||
|
|
64b94c58e7 | ||
|
|
8e907d7395 | ||
|
|
c567283d3f | ||
|
|
6b84dc433a | ||
|
|
a833944ec5 | ||
|
|
b453be40fe | ||
|
|
97d16115e8 | ||
|
|
96709c8121 | ||
|
|
2e3957a7ac | ||
|
|
b866157bff | ||
|
|
8e9c7383b6 | ||
|
|
5de9f63665 | ||
|
|
930f3b23b8 | ||
|
|
d3fb52246c | ||
|
|
e173fe5515 | ||
|
|
dbe02e44fd | ||
|
|
7453540140 | ||
|
|
9830dcbe31 | ||
|
|
69bc734b6a | ||
|
|
2e69966d44 | ||
|
|
c2070b8e52 | ||
|
|
64e2408b59 | ||
|
|
17a8d47452 | ||
|
|
b4eabf4eb8 | ||
|
|
f12c1d5e74 | ||
|
|
714dfd5038 | ||
|
|
6dfe9b3892 | ||
|
|
15e557a209 | ||
|
|
8075bafb86 | ||
|
|
e2f4a86fcf | ||
|
|
6089b209d1 | ||
|
|
d7a12d0dd9 | ||
|
|
95ae7b780e | ||
|
|
0644d301b2 | ||
|
|
7e68dc4ff7 | ||
|
|
879963b828 | ||
|
|
5320e16088 | ||
|
|
6116e24df0 | ||
|
|
6ba1de8c5c | ||
|
|
0d66054e8e | ||
|
|
daf39324fd | ||
|
|
ab9d3cede3 | ||
|
|
2f87fe31be | ||
|
|
7429c42de2 | ||
|
|
9147b4605a | ||
|
|
5a9749d551 | ||
|
|
ea950c8786 | ||
|
|
6fc8a6c7fe | ||
|
|
b537c0c5bd | ||
|
|
400ae269e3 | ||
|
|
30cd749d7c | ||
|
|
30891bfb49 | ||
|
|
efbf45167c | ||
|
|
913b81b8b6 | ||
|
|
606d37744f | ||
|
|
8a4d3d4c97 | ||
|
|
7cbf0dc683 | ||
|
|
e05ce79abf | ||
|
|
2ea95e7938 | ||
|
|
641141810e | ||
|
|
34319c4536 | ||
|
|
65c4c23b22 | ||
|
|
54dde449ce | ||
|
|
c206d38982 | ||
|
|
72a1bddbd2 | ||
|
|
8ee63d7b8e | ||
|
|
73cd8d3f26 | ||
|
|
86a87c501c | ||
|
|
4705368097 | ||
|
|
c3ac2957d2 | ||
|
|
196c518ac6 | ||
|
|
f04af5af9d | ||
|
|
950509e5d6 | ||
|
|
8f4da463a7 | ||
|
|
f3adf780d0 | ||
|
|
9002ca6183 | ||
|
|
b36715a6a0 | ||
|
|
2305c3af32 | ||
|
|
034aaa5b98 | ||
|
|
cbbd678112 | ||
|
|
d62a2ea9d4 | ||
|
|
6259f8b03f | ||
|
|
3866e63a70 | ||
|
|
877e0192a1 | ||
|
|
6d3b7305df | ||
|
|
aa975ea1c5 | ||
|
|
49445c6871 | ||
|
|
ef7c3c70da | ||
|
|
404a193068 | ||
|
|
42a59c9c92 | ||
|
|
5078b74f72 | ||
|
|
809bd16e99 | ||
|
|
04446df8cc | ||
|
|
6a1a2d5f0a | ||
|
|
d998d3ccbb | ||
|
|
664c1f28ed | ||
|
|
62892f1fb9 | ||
|
|
8e75c80e02 | ||
|
|
aae927d773 | ||
|
|
b90f6cb45c | ||
|
|
1cc5a77d7f | ||
|
|
fb14a9c3af | ||
|
|
1a2a82eda3 | ||
|
|
00dba92636 | ||
|
|
71147d038c | ||
|
|
e82db39f8c | ||
|
|
c3292d2f60 | ||
|
|
e749a24a77 | ||
|
|
5a5de65292 | ||
|
|
b706c36edb | ||
|
|
7823972bfd | ||
|
|
f958ee19ad | ||
|
|
dffb9c5320 | ||
|
|
63cbd74744 | ||
|
|
fc78475037 | ||
|
|
3ef42a453a | ||
|
|
469acd23a5 | ||
|
|
d2a0efebd3 | ||
|
|
5cde9d55a7 | ||
|
|
ddd08cb036 | ||
|
|
5bb064197f | ||
|
|
2417c6b57f | ||
|
|
a1addee1b3 | ||
|
|
2e1bd765f6 | ||
|
|
d108ff3151 | ||
|
|
0f7cae6de7 | ||
|
|
2be4eb1c5c | ||
|
|
1ff84f26d2 | ||
|
|
175204efdf | ||
|
|
dbd8a7d742 | ||
|
|
8894aea1ad | ||
|
|
e45123fd76 | ||
|
|
4924a873a2 | ||
|
|
f0ba6b72fc | ||
|
|
5e6a0799d2 | ||
|
|
bea84c03f7 | ||
|
|
869faa5c6c | ||
|
|
43e82fb8c4 | ||
|
|
141c28ed78 | ||
|
|
4d9bce82ab | ||
|
|
8428423ff9 | ||
|
|
fa18cf5aba | ||
|
|
de11e00d89 | ||
|
|
8feab27332 | ||
|
|
8c26ffd5d3 | ||
|
|
dad7d030bc | ||
|
|
7e7af8f606 | ||
|
|
d0647fb8d9 | ||
|
|
bb2564fb0a | ||
|
|
6b0e8b6600 | ||
|
|
9e768b7a24 | ||
|
|
a3774c8d01 | ||
|
|
59a88f76a8 | ||
|
|
24d8929e88 | ||
|
|
dcaae15e28 | ||
|
|
a94d753fdc | ||
|
|
bbd95db97b | ||
|
|
a048874734 | ||
|
|
6b24745ae8 | ||
|
|
a981965fd9 | ||
|
|
c4910f011f | ||
|
|
0c2593cb20 | ||
|
|
37b4657023 | ||
|
|
a608b5b99d | ||
|
|
b1fb6ed06a | ||
|
|
c88db07474 | ||
|
|
dbea159fa9 | ||
|
|
b33b671342 | ||
|
|
70570164c7 | ||
|
|
9338328b1f | ||
|
|
70db5ceb10 | ||
|
|
e07d602cf5 | ||
|
|
077505509a | ||
|
|
fd85f0507c | ||
|
|
24f0b48f9d | ||
|
|
30ed5b296e | ||
|
|
b7a620d30e | ||
|
|
dcee2930f5 | ||
|
|
1b2aeee069 | ||
|
|
c4393637e7 | ||
|
|
04dadc46d5 | ||
|
|
b64cd3b031 | ||
|
|
5e1c4143db | ||
|
|
ba4b6aa1ff | ||
|
|
ad8e803a8c | ||
|
|
601eec3ddb | ||
|
|
38edcb27f1 | ||
|
|
e1b638a4b6 | ||
|
|
b80fe1e748 | ||
|
|
0419d084f4 | ||
|
|
f26faa5676 | ||
|
|
09d4d1f907 | ||
|
|
bdbf7e53e8 | ||
|
|
9c5ddba7f7 | ||
|
|
91dab2c841 | ||
|
|
8cfca8e40e | ||
|
|
9cb619d374 | ||
|
|
fba8233c63 | ||
|
|
631158ebbc | ||
|
|
5f12e2f1b2 | ||
|
|
807f8b5876 | ||
|
|
e6a9c7e433 | ||
|
|
096787d710 | ||
|
|
0468fe4f4d | ||
|
|
69b75c5f5e | ||
|
|
5626614e2d | ||
|
|
67d0a0351a | ||
|
|
97925a82d4 | ||
|
|
0bdd2dfca0 | ||
|
|
a0c572ef2e | ||
|
|
b5c24880a1 | ||
|
|
31c8a50200 | ||
|
|
0219dd2013 | ||
|
|
91cb215b09 | ||
|
|
4446ef0f57 | ||
|
|
41d662b3ba | ||
|
|
05d0eee8c7 | ||
|
|
93abf315a3 | ||
|
|
0d706efb10 | ||
|
|
73fe142904 | ||
|
|
8668c54784 | ||
|
|
f29e04751d | ||
|
|
f9f64c23c1 | ||
|
|
d3e9f15e99 | ||
|
|
112a5703e0 | ||
|
|
6dba376e8b | ||
|
|
99b380a9f3 | ||
|
|
955b9d1fec | ||
|
|
d652646e93 | ||
|
|
86199755f5 | ||
|
|
d68156f214 | ||
|
|
895bf3be6a | ||
|
|
860132469f | ||
|
|
e1568c1e67 | ||
|
|
f4ee9434cc | ||
|
|
3bbef9f328 | ||
|
|
d3043e9a19 | ||
|
|
c6b494db16 | ||
|
|
e8f797739c | ||
|
|
fc5e4039b2 | ||
|
|
d6c1285ada | ||
|
|
a5c0c95e4e | ||
|
|
308c5e5579 | ||
|
|
67b5aa7f15 | ||
|
|
70bcf3ff0d | ||
|
|
5dfca66665 | ||
|
|
5e1fd41935 | ||
|
|
ef00559f1e |
No files matched your search
@@ -28,3 +28,5 @@ src/server/sparkd/orderman/perf_analysis/trace_data/
|
||||
*.fsx.lock
|
||||
src/lib/orderman/perf_analysis/logs/latency_data/
|
||||
trace_data/
|
||||
/src/lib/orderman/perf_analysis/last_pretty_log.csv
|
||||
/src/lib/orderman/perf_analysis/log.csv
|
||||
Vendored
+3
@@ -30,6 +30,7 @@
|
||||
"${workspaceRoot}/build/ninja_debug/src",
|
||||
"${workspaceRoot}/externals/windows/include",
|
||||
"${workspaceRoot}/externals/linux/include",
|
||||
"${workspaceRoot}/externals/linux/include/tensorflow/contrib/makefile/gen/proto",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
|
||||
@@ -50,6 +51,7 @@
|
||||
"${workspaceRoot}/build/ninja_debug/src",
|
||||
"${workspaceRoot}/externals/windows/include",
|
||||
"${workspaceRoot}/externals/linux/include",
|
||||
"${workspaceRoot}/externals/linux/include/tensorflow/contrib/makefile/gen/proto",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
|
||||
@@ -74,6 +76,7 @@
|
||||
"${workspaceRoot}/build/ninja_debug/src",
|
||||
"${workspaceRoot}/externals/windows/include",
|
||||
"${workspaceRoot}/externals/linux/include",
|
||||
"${workspaceRoot}/externals/linux/include/tensorflow/contrib/makefile/gen/proto",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
|
||||
|
||||
Vendored
+47
-7
@@ -6,7 +6,7 @@
|
||||
"name": "(gdb) Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"program": "${workspaceRoot}/build/ninja_debug/server/sparkd/sparkd",
|
||||
"args": ["run"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceRoot}/build/ninja_debug/src/server/sparkd",
|
||||
@@ -36,8 +36,8 @@
|
||||
"name": "SDK Launch",
|
||||
"type": "clr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
|
||||
"args": ["md","GLBX","Future","ZN", "ZNU8"],
|
||||
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
|
||||
"args": ["md","GLBX","Future","CL", "CLZ9", "--user=bts", "--pass="],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
@@ -48,7 +48,7 @@
|
||||
"type": "clr",
|
||||
"request": "launch",
|
||||
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
|
||||
"args": ["${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64.xll"],
|
||||
"args": ["${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
@@ -75,10 +75,10 @@
|
||||
"name": "SparkVM Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
|
||||
"args": ["run"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/server/",
|
||||
"environment": [],
|
||||
"externalConsole": true,
|
||||
"MIMode": "gdb",
|
||||
@@ -108,6 +108,46 @@
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "TFTest ",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/sdk/examples/tf_test",
|
||||
"args": [
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/sdk/examples/",
|
||||
"environment": [],
|
||||
"externalConsole": true,
|
||||
"MIMode": "gdb",
|
||||
"preLaunchTask": "",
|
||||
"logging": {
|
||||
"engineLogging": false
|
||||
},
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "enable pretty-printer",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"description": "Skip STL",
|
||||
"text": "skip -gfi /usr/include/c++/7/bits/*.h",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
],
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": [
|
||||
"bts@sparkvm"
|
||||
],
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Spreader Tests",
|
||||
"type": "cppdbg",
|
||||
@@ -209,7 +249,7 @@
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"args": ["test","Co2Tests"],
|
||||
"args": ["test","AuthToken"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
|
||||
"environment": [],
|
||||
|
||||
+6
-2
@@ -13,7 +13,7 @@ set (SPARK_BIN "${EXTERNALS_ROOT}/bin")
|
||||
set (SPARK_INCLUDE "${EXTERNALS_ROOT}/include")
|
||||
set (SPARK_LIB "${EXTERNALS_ROOT}/lib/${CMAKE_BUILD_TYPE}")
|
||||
|
||||
include_directories (BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
|
||||
include_directories (BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib ${TF_INCLUDE})
|
||||
link_directories (${SPARK_LIB})
|
||||
|
||||
if (WIN32)
|
||||
@@ -39,8 +39,9 @@ else ()
|
||||
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} \
|
||||
-fPIC -Wall -pedantic -Wextra -Werror \
|
||||
-Wno-missing-braces -Wno-ignored-qualifiers \
|
||||
--std=c++17 -fdiagnostics-color -Wl,--no-undefined")
|
||||
--std=c++17 -fdiagnostics-color")
|
||||
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -DNDEBUG -O3")
|
||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wno-undef")
|
||||
add_subdirectory(src/server/sparkd)
|
||||
add_subdirectory(src/lib)
|
||||
add_subdirectory(src/web)
|
||||
@@ -56,6 +57,9 @@ else ()
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/lib/libspark.so ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND mkdir ARGS -p ${CMAKE_BINARY_DIR}/server/plugins
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/sdk/examples/order_plugin_example.so ${CMAKE_BINARY_DIR}/server/plugins/
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/sdk/examples/ai_order_plugin_example.so ${CMAKE_BINARY_DIR}/server/plugins/
|
||||
COMMAND ln ARGS -sf ${EXTERNALS_ROOT}/lib/Release/libtensorflow.so ${CMAKE_BINARY_DIR}/server/plugins
|
||||
|
||||
COMMENT "Creating server runtime layout"
|
||||
DEPENDS sparkd data_files_dir)
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
# The BTS Development Process #
|
||||
|
||||
## Projects ##
|
||||
Our development work is organized in projects. A project is a
|
||||
non-trivial effort to specify, develop and validate a related set of
|
||||
additions to our Spark platform. A project can be assigned to one more
|
||||
developers and a developer might concurrently be assigned to multiple
|
||||
projects. Projects typically start out with specification work,
|
||||
proceed to implementation and finish with validation efforts. At any
|
||||
given time there will be multiple projects being worked on
|
||||
concurrently. A project is concluded when the features it implements
|
||||
are merged into the product and deployed to customers.
|
||||
|
||||
## The Details ##
|
||||
Typically project ideas come from sales interactions, opposition
|
||||
research and customer feedback, all tough on occasion development or
|
||||
operational requirements can also suggest valuable project
|
||||
ideas. Whatever the source of a project idea the final decision of
|
||||
which projects to pursue is always done by management. Once a project
|
||||
idea has been selected for implementation the corresponding project
|
||||
will go through several phases: inception, specification, development,
|
||||
validation and merge.
|
||||
|
||||
* Inception. In this phase the infrastructure for the project is
|
||||
created. Typically this means selecting a team, creating a project
|
||||
case in Jira (that is a case assigned to the team@bluetradesys.com
|
||||
Jira user) and possibly creating a new branch for the project. This
|
||||
phase is mostly mechanical and the responsibility of Pedro.
|
||||
|
||||
* Specification. The specification phase turns a usually vague and
|
||||
ill-considered idea project idea into a concrete, implementable
|
||||
specification that attempts to maximize the value/cost ratio of the
|
||||
project. It is hard to exaggerate the impact of a this phase on the
|
||||
rest of the project. *Nothing* has more impact on BTS's ability to
|
||||
quickly deliver value to customers than good specification work. A
|
||||
specification typically takes the form of a shared google docs using
|
||||
a mix of text and UI mock-ups to capture both user requirements and
|
||||
suggested implementation strategies. Taha and Kevin are responsible for this
|
||||
phase.
|
||||
|
||||
* Development. Once a specification is complete, a team of developers
|
||||
will use a project specific branch to implement the
|
||||
specification. Periodically, typically once a week, progress on the
|
||||
project is assessed by Pedro, possibly with the help of others. This
|
||||
assessment takes the form of code reviews, demos, or just one-on-one
|
||||
conversations. To ensure development does not go off on tangents,
|
||||
developers are expected to continuously merge master into the project
|
||||
branch as well as encouraged to create Pull Requests (PRs) early in
|
||||
the project implementation, so that their work is kept visible and
|
||||
integrated with that of the rest of the group. During the
|
||||
implementation phase developers should reach out to Pedro for UI
|
||||
reviews, architecture and design questions and specification trade off
|
||||
concerns.
|
||||
|
||||
* Verification. Once code is complete it is the project's team job to
|
||||
think carefully about the best way to verify that the work is solid
|
||||
enough to go to production. We do not have a dedicated QA cycle to
|
||||
flush out bugs so it is important for changes to be fully tested
|
||||
before being marked ready to merge (RTM). For any non-trivial change
|
||||
project teams should enlist a 'test buddy' from outside the team to
|
||||
help review the code and think of test cases that they may not have
|
||||
considered. Stability from release to release is especially critical
|
||||
right now as we develop a reputation in the futures
|
||||
world. Additionally, project teams are encouraged to make liberal use
|
||||
of other testing techniques such as
|
||||
- Unit tests
|
||||
- System tests
|
||||
- Manual tests
|
||||
|
||||
* Merge. Once a project is considered complete by developers, a PR
|
||||
that includes Pedro should be created (unless, as mentioned above, an
|
||||
early PR was already created in the development phase). Whether the PR
|
||||
already existed or not, its description should be changed to start
|
||||
with the RTM (Ready To Merge) acronym. This marks the project as ready
|
||||
to be merged into master. Once a week, typically on Mondays, Pedro
|
||||
will go through every PR marked as "Ready To Merge", build it, check
|
||||
it out and if everything looks good merge it to master. At the end of
|
||||
the day the resulting master will be tagged with a new version
|
||||
number. This version will be go through some additional testing by
|
||||
Brian and will then be deployed by Karl and Pete to customers
|
||||
throughout the rest of the week.
|
||||
|
||||
@@ -1,4 +1,94 @@
|
||||
# Release Notes
|
||||
|
||||
## 1.5.0
|
||||
### User Visible Changes
|
||||
#### User Interface Improvements
|
||||
- Removed security key field from most grids
|
||||
- Removed trade key field from time and sales grid
|
||||
- Fixed the "Since Open" time filter to use the exchange open time rather than 8:30 AM
|
||||
- Fixed the floating order book so that rows are no longer
|
||||
highlighted after an order is modified
|
||||
#### SDK
|
||||
- Improved SDK documentation and usability
|
||||
### Internal Changes
|
||||
- Improved robustness of limit system test
|
||||
- Extensive market data refactoring allow for security definition updates
|
||||
|
||||
|
||||
## 1.4.11
|
||||
### New Features
|
||||
- Floating Order Book
|
||||
- The floating order book provides a quick view of orders occupying a price level in the ladder
|
||||
- Launch the Floating Order Book by middle-clicking a ladder badge
|
||||
- Cancel orders by clicking on the red X in the Cancel column
|
||||
- Quantity and Price modification
|
||||
- Clicking on a row will select it, providing the option for changing your price and quantity
|
||||
- Enter in a new value or use the up/down buttons to set a desired value
|
||||
- Press the green checkbox to finalize your change and update the order
|
||||
### SDK
|
||||
- Improved SDK documentation and usability
|
||||
### User Interface Improvements
|
||||
- The following spreader quoting parameters have been deprecated:
|
||||
- Hidden parameter
|
||||
- Price Type setting of the Conditional parameter
|
||||
### Bug Fixes
|
||||
- Fixed a limit enforcement problem that could lead to 0 sized orders being sent to the exchange
|
||||
|
||||
## 1.4.10
|
||||
### Bug Fixes
|
||||
- Removed duplicate entries in performance log
|
||||
|
||||
## 1.4.9
|
||||
### SDK
|
||||
- Added ticker events to the order API
|
||||
### Performance Improvements
|
||||
- Significantly improved hedging speed
|
||||
### Bug Fixes
|
||||
- Fixed a visual oddity in the order book UI when using High DPI displays
|
||||
|
||||
## 1.4.8
|
||||
### Marketdata
|
||||
- Additional trade details for CME ticker
|
||||
|
||||
## 1.4.7
|
||||
### Bug Fixes
|
||||
- Corrected FIX tag 1028, ManualOrderIndicator, when orders are triggered by algos
|
||||
|
||||
## 1.4.6
|
||||
### Ladder Usability Improvements
|
||||
- The ladder can now be zoomed in/out according to a user's preference
|
||||
- The zoom slider can be enabled from Ladder Form -> Settings button -> Left Pane tab -> check Zoom Slider checkbox
|
||||
- The zoom functionality can also be performed via hotkey and mouse wheel combinations
|
||||
- Zoom In = CTRL + Mouse wheel up
|
||||
- Zoom Out = CTRL + Mouse wheel down
|
||||
- Reset zoom = CTRL + Mouse wheel click or double click on the zoom slider
|
||||
### Exchange simulator
|
||||
- Improved the exchange simulator to track real market data more closely
|
||||
### Bug Fixes
|
||||
- Fixed a UI crash in some cases when attempting to sort the order book by time
|
||||
|
||||
## 1.4.5
|
||||
- Fixed entitlement validation for ICS spreads
|
||||
- Tuned performance logging
|
||||
|
||||
## 1.4.4
|
||||
|
||||
### Performance Improvements
|
||||
- Reduced performance logging activity
|
||||
|
||||
## 1.4.3
|
||||
|
||||
### Ladder Usability Improvements
|
||||
- Controls found in Left Pane of the ladder can now be hidden via the checkboxes found under Ladder Form -> Settings button -> Left Pane tab
|
||||
- The Left Pane is now scrollable with the mouse wheel when there isn't enough vertical space to accomodate the entire form
|
||||
- The Left Pane can now grown and shrunk horizontally via the splitter between the left pane and the main ladder area
|
||||
### Performance Improvements
|
||||
- Requoting and hedging latency has been reduced by up to 30%
|
||||
### Software Development Kit (SDK) Improvements
|
||||
- The API now enforces authentication
|
||||
### Bug Fixes
|
||||
- The Market View form now honors filters when new securities are added
|
||||
|
||||
## 1.4.2
|
||||
|
||||
### User Interface Improvements
|
||||
@@ -6,9 +96,18 @@
|
||||
### Bug Fixes
|
||||
- Shell no longer crashes when removing a security from the synthetics window
|
||||
- It is now possible to create a new synthetic using the same name as a previously deleted synthetic
|
||||
### SDK Improvements
|
||||
- Improved Client and Server SDK clarity and documentation
|
||||
### Performance Improvements
|
||||
- Synthetic calculation speed was improved by 400%.
|
||||
|
||||
### Risk / Entitlement System
|
||||
- Risk Admin UI
|
||||
- Spark users now have separate Trading, RiskAdmin, and/or MDAdmin permissions
|
||||
- Spark users can now only add other users or accounts if permissioned to do so.
|
||||
- Market Data Entitlement
|
||||
- Automated market data reporting
|
||||
|
||||
|
||||
## 1.4.1
|
||||
|
||||
### Tabs
|
||||
|
||||
+1
-1
Submodule externals updated: 5f21fd13e7...c0941c5650.
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"uri": "sftp://bts:BT5u53r@sparkvm:22/home/bts/",
|
||||
"name": "sparkvm"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"files.associations": {
|
||||
"utility": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"list": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"vector": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xstring": "cpp",
|
||||
"system_error": "cpp",
|
||||
"array": "cpp",
|
||||
"string_view": "cpp",
|
||||
"xutility": "cpp",
|
||||
"*.ipp": "cpp"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"files.associations": {
|
||||
"utility": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"list": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"vector": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xstring": "cpp",
|
||||
"system_error": "cpp",
|
||||
"array": "cpp",
|
||||
"string_view": "cpp",
|
||||
"xutility": "cpp",
|
||||
"*.ipp": "cpp",
|
||||
"atomic": "cpp",
|
||||
"hash_map": "cpp",
|
||||
"hash_set": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"bitset": "cpp",
|
||||
"cctype": "cpp",
|
||||
"cfenv": "cpp",
|
||||
"chrono": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"complex": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"csignal": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"forward_list": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"exception": "cpp",
|
||||
"any": "cpp",
|
||||
"optional": "cpp",
|
||||
"fstream": "cpp",
|
||||
"functional": "cpp",
|
||||
"future": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"memory": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"ostream": "cpp",
|
||||
"numeric": "cpp",
|
||||
"ratio": "cpp",
|
||||
"scoped_allocator": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"thread": "cpp",
|
||||
"tuple": "cpp",
|
||||
"typeindex": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"valarray": "cpp",
|
||||
"variant": "cpp"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using bts.api.core;
|
||||
|
||||
namespace mbts.spark {
|
||||
[Export]
|
||||
|
||||
@@ -17,31 +17,22 @@ using bts.api.user;
|
||||
|
||||
namespace bts {
|
||||
[Export(typeof(IInitializable))]
|
||||
class MDSource : IObserver<List<SecurityAndHandle>>, IInitializable, IDisposable {
|
||||
class MDDaemon : bts.md.MDSource, IObserver<List<SecurityAndHandle>>, IInitializable, IDisposable {
|
||||
readonly Lazy<IBus> _bus;
|
||||
readonly Lazy<AuthToken> _token;
|
||||
readonly Lazy<AuthTokenImmutable> _token;
|
||||
readonly Lazy<SyntheticSecurityService> _ss;
|
||||
readonly MDConsumer _consumer = new MDConsumer();
|
||||
readonly Co2RelayControl _relay;
|
||||
readonly Dictionary<SecurityKey, SecurityMD> _deleted_synthetics = new Dictionary<SecurityKey, SecurityMD>();
|
||||
long _version = 0;
|
||||
readonly ConcurrentDictionary<SecurityKey, SecurityMD> _exchange_sec_map = new ConcurrentDictionary<SecurityKey, SecurityMD>();
|
||||
List<IDisposable> _dispose = new List<IDisposable>();
|
||||
|
||||
static uint get_naggle() => mbts.config.Configuration.get<bts.md.RelayOptions>().naggle_ms;
|
||||
|
||||
[ImportingConstructor]
|
||||
public MDSource(
|
||||
Lazy<IBus> bus,
|
||||
Lazy<AuthToken> token,
|
||||
Lazy<SyntheticSecurityService> ss) {
|
||||
public MDDaemon(Lazy<IBus> bus, Lazy<AuthTokenImmutable> token, Lazy<SyntheticSecurityService> ss) : base(get_naggle(), true) {
|
||||
_bus = bus;
|
||||
_token = token;
|
||||
_ss = ss;
|
||||
_relay = new Co2RelayControl(_consumer);
|
||||
_relay.preload_securities((co2.MDRequest.Types.Flags)0xff).ignore();
|
||||
_dispose.Add(_consumer.on_security.Subscribe(this));
|
||||
_ss = ss;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
override public void initialize() {
|
||||
_relay.set_token(_token.Value);
|
||||
_dispose.Add(
|
||||
_ss.Value.subscribei_metadata()
|
||||
@@ -66,6 +57,9 @@ namespace bts {
|
||||
// Post full dictionary of syntheics here
|
||||
bus.post(SparkTags.SyntheticstrategiesTag, t);
|
||||
}));
|
||||
_dispose.Add( this.on_securities.Subscribe( (md) => {
|
||||
bus.post(SparkTags.SecuritiesTag, md);
|
||||
}));
|
||||
Task.Run(run);
|
||||
}
|
||||
|
||||
@@ -74,37 +68,5 @@ namespace bts {
|
||||
|
||||
public int priority => 55;
|
||||
IBus bus => _bus.Value;
|
||||
|
||||
public void OnCompleted() { }
|
||||
public void OnError(Exception error) => Log.Md.error(error.ToString());
|
||||
public void OnNext(List<SecurityAndHandle> values) {
|
||||
if (values.Count > 0) {
|
||||
foreach (var v in values) {
|
||||
var smd = new SecurityMD(v.security, v.handle);
|
||||
_exchange_sec_map.AddOrUpdate(smd.security.Id, smd, (_, __) => smd);
|
||||
}
|
||||
_version += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() => Disposable.dispose(_dispose);
|
||||
async Task run() {
|
||||
long run_version = 0;
|
||||
while (true) {
|
||||
var exch = _exchange_sec_map;
|
||||
long current_version = _version;
|
||||
if (current_version > run_version) {
|
||||
run_version = current_version;
|
||||
try {
|
||||
Log.Md.info($"MDSource publishing {exch.Count} securities");
|
||||
bus.post(SparkTags.SecuritiesTag, exch);
|
||||
} catch (Exception e) {
|
||||
Log.Md.error($"Error in publish_securities: {e}");
|
||||
}
|
||||
}
|
||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,7 @@ using System.ComponentModel.Composition;
|
||||
namespace Spark {
|
||||
public class ShellServices {
|
||||
[Export]
|
||||
public static ServiceContext context { get; } = new ServiceContext();
|
||||
|
||||
[Export]
|
||||
public UserService user_service => context.locate<UserService>();
|
||||
public ServiceContext context { get; } = new ServiceContext();
|
||||
|
||||
[Export("settings")]
|
||||
public PersistenceService settings_service => context.locate<PersistenceService>("settings");
|
||||
|
||||
@@ -31,7 +31,9 @@ namespace mbts.ui {
|
||||
}
|
||||
|
||||
private static ImageSource load_resource_and_freeze(string resource_name) {
|
||||
return ((DrawingImage)_resources[resource_name]).frozen();
|
||||
lock (_resources) {
|
||||
return ((DrawingImage)_resources[resource_name]).frozen();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
@@ -152,7 +154,10 @@ namespace mbts.ui {
|
||||
}
|
||||
|
||||
static IEnumerable<string> _get_geometric_path(string geometric_path_key) {
|
||||
object obj = _resources[geometric_path_key];
|
||||
object obj;
|
||||
lock (_resources) {
|
||||
obj = _resources[geometric_path_key];
|
||||
}
|
||||
if (obj is string path) {
|
||||
if (string.IsNullOrEmpty(path)) {
|
||||
Log.Api.error($"Resource lookup of '{geometric_path_key}' did not return a valid string.", nameof(geometric_path_key));
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace mbts.spark {
|
||||
readonly CellProperty<string> _working_bids_text, _working_asks_text;
|
||||
readonly MyOrderSummarizer _order_summarizer = new MyOrderSummarizer();
|
||||
readonly MDDisplayHelper.SizeFormatter _size_formatter = MDDisplayHelper.get_formatter(MarketSizeDisplay.get_default());
|
||||
uint _bid_direct_size, _bid_merged_size, _ask_direct_size, _ask_merged_size;
|
||||
Property<uint> _bid_direct_size, _bid_merged_size, _ask_direct_size, _ask_merged_size;
|
||||
string _bid_merged_size_text, _ask_merged_size_text;
|
||||
ulong _bbo_ref, _last_ref;
|
||||
|
||||
@@ -45,28 +45,33 @@ namespace mbts.spark {
|
||||
_security_text = smd.security.ToString();
|
||||
_security_key_text = security_key.ToString();
|
||||
string format_price(double price) => security.ContractSpec.format(price);
|
||||
_position = new CellProperty<int>(0);
|
||||
_position = new CellProperty<int>(this, nameof(position), 0);
|
||||
_bid_price = backing(nameof(bid_price), double.NaN);
|
||||
_ask_price = backing(nameof(ask_price), double.NaN);
|
||||
_last_price = backing(nameof(last_price), double.NaN);
|
||||
_last_size = new CellProperty<uint>(0);
|
||||
_last_size = new CellProperty<uint>(this, nameof(last_size), 0);
|
||||
_day_change = backing(nameof(day_change), double.NaN);
|
||||
_session_low = backing(nameof(session_low), double.NaN);
|
||||
_session_high = backing(nameof(session_high), double.NaN);
|
||||
_settlement = backing(nameof(settlement), double.NaN);
|
||||
_volume = new CellProperty<ulong>(0);
|
||||
_inside_bids = new CellProperty<bool>();
|
||||
_inside_asks = new CellProperty<bool>();
|
||||
_working_bid_volume = new CellProperty<int>();
|
||||
_working_ask_volume = new CellProperty<int>();
|
||||
_volume = new CellProperty<ulong>(this, nameof(volume), 0);
|
||||
_inside_bids = new CellProperty<bool>(this, nameof(inside_bids));
|
||||
_inside_asks = new CellProperty<bool>(this, nameof(inside_asks));
|
||||
_working_bid_volume = new CellProperty<int>(this, nameof(working_bid_volume));
|
||||
_working_ask_volume = new CellProperty<int>(this, nameof(working_ask_volume));
|
||||
//
|
||||
_bid_direct_size = backing(nameof(bid_direct_size), default(uint));
|
||||
_bid_merged_size = backing(nameof(bid_merged_size), default(uint));
|
||||
_ask_direct_size = backing(nameof(ask_direct_size), default(uint));
|
||||
_ask_merged_size = backing(nameof(ask_merged_size), default(uint));
|
||||
//
|
||||
_working_orders = new ObservableFilterCollection<OrderRecordImmutable>(_orders, o => o.is_active());
|
||||
_working_bids = new ObservableFilterCollection<OrderRecordImmutable>(_working_orders, o => o.Def.Side == Side.Buy);
|
||||
_working_asks = new ObservableFilterCollection<OrderRecordImmutable>(_working_orders, o => o.Def.Side == Side.Sell);
|
||||
_working_best_bid = new CellProperty<double>(format_price, double.NaN);
|
||||
_working_best_ask = new CellProperty<double>(format_price, double.NaN);
|
||||
_working_bids_text = new CellProperty<string>(string.Empty);
|
||||
_working_asks_text = new CellProperty<string>(string.Empty);
|
||||
_working_best_bid = new CellProperty<double>(this, nameof(working_best_bid), format_price, double.NaN);
|
||||
_working_best_ask = new CellProperty<double>(this, nameof(working_best_ask), format_price, double.NaN);
|
||||
_working_bids_text = new CellProperty<string>(this, nameof(working_bids_text), string.Empty);
|
||||
_working_asks_text = new CellProperty<string>(this, nameof(working_asks_text), string.Empty);
|
||||
//
|
||||
default_command = new DelegateCommand(_show_ladder);
|
||||
bid_price_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.Bid));
|
||||
@@ -100,15 +105,15 @@ namespace mbts.spark {
|
||||
public double last_price => _last_price.Value;
|
||||
public uint last_size => _last_size.value;
|
||||
public double bid_price => _bid_price.Value;
|
||||
public uint bid_direct_size => _bid_direct_size;
|
||||
public uint bid_implied_size => _bid_merged_size - _bid_direct_size;
|
||||
public uint bid_merged_size => _bid_merged_size;
|
||||
public string bid_merged_size_text => _bid_merged_size_text ?? (_bid_merged_size_text = _size_formatter(_bid_merged_size, bid_implied_size));
|
||||
public uint bid_direct_size => _bid_direct_size.Value;
|
||||
public uint bid_implied_size => _bid_merged_size.Value - _bid_direct_size.Value;
|
||||
public uint bid_merged_size => _bid_merged_size.Value;
|
||||
public string bid_merged_size_text => _bid_merged_size_text ?? (_bid_merged_size_text = _size_formatter(_bid_merged_size.Value, bid_implied_size));
|
||||
public double ask_price => _ask_price.Value;
|
||||
public uint ask_direct_size => _ask_direct_size;
|
||||
public uint ask_implied_size => _ask_merged_size - _ask_direct_size;
|
||||
public uint ask_merged_size => _ask_merged_size;
|
||||
public string ask_merged_size_text => _ask_merged_size_text ?? (_ask_merged_size_text = _size_formatter(_ask_merged_size, ask_implied_size));
|
||||
public uint ask_direct_size => _ask_direct_size.Value;
|
||||
public uint ask_implied_size => _ask_merged_size.Value - _ask_direct_size.Value;
|
||||
public uint ask_merged_size => _ask_merged_size.Value;
|
||||
public string ask_merged_size_text => _ask_merged_size_text ?? (_ask_merged_size_text = _size_formatter(_ask_merged_size.Value, ask_implied_size));
|
||||
public ulong volume => _volume.value;
|
||||
public double settlement => _settlement.Value;
|
||||
public double day_change => _day_change.Value;
|
||||
@@ -144,9 +149,9 @@ namespace mbts.spark {
|
||||
bool dirty = false;
|
||||
if (md.md.get_bbo(ref _bbo_ref, out NativePriceSize b, out NativePriceSize a)) {
|
||||
_bid_price.set(b.price);
|
||||
refresh_size(ref b, ref _bid_direct_size, ref _bid_merged_size, ref _bid_merged_size_text, nameof(bid_merged_size_text));
|
||||
refresh_size(ref b, _bid_direct_size, _bid_merged_size, ref _bid_merged_size_text, nameof(bid_merged_size_text));
|
||||
_ask_price.set(a.price);
|
||||
refresh_size(ref a, ref _ask_direct_size, ref _ask_merged_size, ref _ask_merged_size_text, nameof(ask_merged_size_text));
|
||||
refresh_size(ref a, _ask_direct_size, _ask_merged_size, ref _ask_merged_size_text, nameof(ask_merged_size_text));
|
||||
_inside_bids.on_next_distinct(_bid_price.Value >= _working_best_bid.value, ref dirty);
|
||||
_inside_asks.on_next_distinct(_ask_price.Value <= _working_best_ask.value, ref dirty);
|
||||
}
|
||||
@@ -162,10 +167,10 @@ namespace mbts.spark {
|
||||
if (dirty)
|
||||
invalidated?.Invoke();
|
||||
|
||||
void refresh_size(ref NativePriceSize nps, ref uint direct, ref uint merged, ref string text, string text_prop) {
|
||||
if (nps.size != merged || nps.direct_size != direct) {
|
||||
merged = nps.size;
|
||||
direct = nps.direct_size;
|
||||
void refresh_size(ref NativePriceSize nps, Property<uint> direct, Property<uint> merged, ref string text, string text_prop) {
|
||||
if (nps.size != merged.Value || nps.direct_size != direct.Value) {
|
||||
merged.set(nps.size);
|
||||
direct.set(nps.direct_size);
|
||||
text = null;
|
||||
dirty = true;
|
||||
RaisePropertyChanged(text_prop);
|
||||
@@ -205,36 +210,28 @@ namespace mbts.spark {
|
||||
var order = _orders_model.factory.order(security);
|
||||
switch (col) {
|
||||
case MarketViewView.Columns.Bid:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, 0u));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, 0u));
|
||||
break;
|
||||
case MarketViewView.Columns.BidMergedSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_merged_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, bid_merged_size));
|
||||
break;
|
||||
case MarketViewView.Columns.BidDirectSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_direct_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, bid_direct_size));
|
||||
break;
|
||||
case MarketViewView.Columns.BidImpliedSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_implied_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, bid_implied_size));
|
||||
break;
|
||||
case MarketViewView.Columns.Ask:
|
||||
order.Side = Side.Buy;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, 0u));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Buy), ask_price, 0u));
|
||||
break;
|
||||
case MarketViewView.Columns.AskMergedSize:
|
||||
order.Side = Side.Buy;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_merged_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Buy), ask_price, ask_merged_size));
|
||||
break;
|
||||
case MarketViewView.Columns.AskDirectSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_direct_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), ask_price, ask_direct_size));
|
||||
break;
|
||||
case MarketViewView.Columns.AskImpliedSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_implied_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), ask_price, ask_implied_size));
|
||||
break;
|
||||
case MarketViewView.Columns.Last:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, last_price, 0u));
|
||||
@@ -266,7 +263,15 @@ namespace mbts.spark {
|
||||
_working_best_ask.on_next_distinct(_order_summarizer.best_working_ask, ref dirty);
|
||||
_inside_bids.on_next_distinct(_order_summarizer.best_working_bid >= bid_price, ref dirty);
|
||||
_inside_asks.on_next_distinct(_order_summarizer.best_working_ask <= ask_price, ref dirty);
|
||||
RaisePropertyChanged(nameof(working_bids_text), nameof(working_asks_text));
|
||||
|
||||
RaisePropertyChanged(
|
||||
// Implied might not be ever set, so it probably does not RaisePropertyChanged either
|
||||
// This might be part of the filter funk?
|
||||
//nameof(bid_implied_size),
|
||||
//nameof(ask_implied_size),
|
||||
nameof(working_bids_text),
|
||||
nameof(working_asks_text)
|
||||
);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace bts.shell {
|
||||
public static GridColumnSettings day_change => new GridColumnSettings(col_id(Columns.DayChange), DefaultColumnHeaders.DayChange, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings session_low => new GridColumnSettings(col_id(Columns.SessionLow), DefaultColumnHeaders.SessionLow, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings session_high => new GridColumnSettings(col_id(Columns.SessionHigh), DefaultColumnHeaders.SessionHigh, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -97,8 +97,11 @@ namespace mbts.spark {
|
||||
public IEnumerable<TradeInfo> read_trades() {
|
||||
var copy = _securities;
|
||||
foreach (var tm in _channel.read_trades(ref _cursor)) {
|
||||
if (copy.TryGetValue(tm.Key, out SecurityImmutable sec))
|
||||
yield return new TradeInfo(sec, tm);
|
||||
if (tm != null) {
|
||||
if (copy.TryGetValue(tm.Key, out SecurityImmutable sec)) {
|
||||
yield return new TradeInfo(sec, new TradeMessageImmutable(tm));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ namespace mbts.spark {
|
||||
price_filter.load_state(filters.price);
|
||||
size_filter.load_state(filters.size);
|
||||
aggressor_filter.load_state(filters.aggressor_sides);
|
||||
trade_key_filter.load_state(filters.trade_key);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
}
|
||||
// Columns
|
||||
@@ -47,7 +46,6 @@ namespace mbts.spark {
|
||||
add_column(TimeAndSalesView.Columns.Price, DefaultColumnHeaders.Price, DefaultColumnWidths.Price, nameof(Row.price), price_filter, DataGridFilterKnownTemplates.DoubleMinMax, System.Windows.HorizontalAlignment.Right);
|
||||
add_column(TimeAndSalesView.Columns.Size, DefaultColumnHeaders.Size, DefaultColumnWidths.Size, nameof(Row.size), size_filter, DataGridFilterKnownTemplates.UIntMinMax, System.Windows.HorizontalAlignment.Right);
|
||||
add_column(TimeAndSalesView.Columns.Aggressor, DefaultColumnHeaders.Aggressor, DefaultColumnWidths.Aggressor, nameof(Row.aggressor_side), aggressor_filter, DataGridFilterKnownTemplates.EnumValues);
|
||||
add_column(TimeAndSalesView.Columns.TradeKey, DefaultColumnHeaders.TradeKey, DefaultColumnWidths.TradeKey, nameof(Row.trade_key), trade_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_column(TimeAndSalesView.Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, DefaultColumnWidths.SecurityKey, nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
void add_column(TimeAndSalesView.Columns col_key, string header, double width, string sort_member_path, IDataGridColumnFilter filter, string filter_template, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
|
||||
columns.Add(new TimeAndSalesColumn(
|
||||
@@ -66,7 +64,7 @@ namespace mbts.spark {
|
||||
_resources.AddRange(
|
||||
show_ladder,
|
||||
DataGridFilterHelper.hook_filters(
|
||||
rows_view, time_filter, security_filter, price_filter, size_filter, aggressor_filter, trade_key_filter, security_key_filter),
|
||||
rows_view, time_filter, security_filter, price_filter, size_filter, aggressor_filter, security_key_filter),
|
||||
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager)
|
||||
);
|
||||
rows_view.set_sort_descriptions(startup_data.sorting_criterion, columns);
|
||||
@@ -103,7 +101,6 @@ namespace mbts.spark {
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> size_filter { get; } = new UIntMinMax<Row>(r => r.size, nameof(Row.size));
|
||||
public IDataGridColumnPersistentFilter<Row, AggressorSide[]> aggressor_filter { get; } = DiscreteValuesFilterVM.Create<Row, AggressorSide>(EnumHelper.get_values<AggressorSide>().ToArray(), r => r.aggressor_side, nameof(Row.aggressor_side));
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
|
||||
public IDataGridColumnPersistentFilter<Row, string> trade_key_filter { get; } = new RegexFilterVM<Row>(r => r.trade_key.ToString(), nameof(Row.trade_key));
|
||||
|
||||
FilterStates _get_filter_states() =>
|
||||
new FilterStates {
|
||||
@@ -111,7 +108,6 @@ namespace mbts.spark {
|
||||
security = security_filter.get_state(),
|
||||
price = price_filter.get_state(),
|
||||
size = size_filter.get_state(),
|
||||
trade_key = trade_key_filter.get_state(),
|
||||
security_key = security_key_filter.get_state(),
|
||||
aggressor_sides = aggressor_filter.get_state()
|
||||
};
|
||||
@@ -175,7 +171,6 @@ namespace mbts.spark {
|
||||
case TimeAndSalesView.Columns.Price: return _price_text;
|
||||
case TimeAndSalesView.Columns.Size: return _size_text;
|
||||
case TimeAndSalesView.Columns.Aggressor: return _aggressor_text;
|
||||
case TimeAndSalesView.Columns.TradeKey: return _trade_key_text;
|
||||
case TimeAndSalesView.Columns.SecurityKey: return _security_key_text;
|
||||
default: return null;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace mbts.ui.spark {
|
||||
/// Interaction logic for TimeAndSalesView.xaml
|
||||
/// </summary>
|
||||
partial class TimeAndSalesView : UserControl {
|
||||
public enum Columns { Time, Security, Price, Size, OrderType, TradeKey, SecurityKey, Aggressor }
|
||||
public enum Columns { Time, Security, Price, Size, OrderType, SecurityKey, Aggressor }
|
||||
|
||||
public TimeAndSalesView() =>
|
||||
InitializeComponent();
|
||||
|
||||
@@ -84,8 +84,7 @@ namespace bts.shell {
|
||||
public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings size => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings aggressor => new GridColumnSettings(col_id(Columns.Aggressor), DefaultColumnHeaders.Aggressor, width: DefaultColumnWidths.Aggressor);
|
||||
public static GridColumnSettings trade_key => new GridColumnSettings(col_id(Columns.TradeKey), DefaultColumnHeaders.TradeKey, width: DefaultColumnWidths.TradeKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible:false, width: DefaultColumnWidths.SecurityKey);
|
||||
|
||||
public static IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
get {
|
||||
@@ -100,7 +99,6 @@ namespace bts.shell {
|
||||
yield return price;
|
||||
yield return size;
|
||||
yield return aggressor;
|
||||
yield return trade_key;
|
||||
yield return security_key;
|
||||
}
|
||||
}
|
||||
@@ -117,8 +115,8 @@ namespace bts.shell {
|
||||
public DoubleMinMaxState price;
|
||||
[ProtoMember(4)]
|
||||
public UIntMinMaxState size;
|
||||
[ProtoMember(5)]
|
||||
public string trade_key;
|
||||
// [ProtoMember(5)]
|
||||
// ProtoMember 5 cannot be used further
|
||||
[ProtoMember(6)]
|
||||
public string security_key;
|
||||
[ProtoMember(7)]
|
||||
|
||||
@@ -53,8 +53,8 @@ namespace mbts.spark {
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<uint?> create_synthetic(SecurityImmutable synthetic) {
|
||||
var result = await _service.create_synthetic_async(synthetic);
|
||||
public async Task<uint?> create_synthetic(SecurityImmutable? synthetic) {
|
||||
var result = await _service.create_synthetic_async(synthetic.Value);
|
||||
return _error.handle_error(result, "Error creating synthetic: {0}", r => r.Error, e => e == CreateResult.Types.Error.Ok, r => new uint?(r.Key), () => null).result;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace bts.shell {
|
||||
public static GridColumnSettings description => new GridColumnSettings(col_id(Columns.Description), DefaultColumnHeaders.Description, width: DefaultColumnWidths.Description);
|
||||
public static GridColumnSettings n_legs => new GridColumnSettings(col_id(Columns.n_legs), DefaultColumnHeaders.N_Legs, width: DefaultColumnWidths.N_Legs);
|
||||
public static GridColumnSettings n_working_orders => new GridColumnSettings(col_id(Columns.n_working_orders), DefaultColumnHeaders.N_Working_Orders, width: DefaultColumnWidths.N_Working_Orders);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
|
||||
public static IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
get {
|
||||
|
||||
@@ -205,7 +205,7 @@ namespace mbts.spark {
|
||||
//new EnableHedgeModel(), //TAHA_SPREADER
|
||||
new HedgeRatioModel(),
|
||||
//new MinLeanQtyModel(), //TAHA_SPREADER
|
||||
new QuoteHiddenModel(),
|
||||
//new QuoteHiddenModel(),
|
||||
new MarketDataTypeModel(),
|
||||
new MarketDataCalculationModel(),
|
||||
new QuoteThrottleEnabledModel(quote_throttle_inside_ticks, quote_throttle_outside_ticks),
|
||||
@@ -213,7 +213,7 @@ namespace mbts.spark {
|
||||
quote_throttle_outside_ticks,
|
||||
new ConditionalQuoteEnabledModel(conditional_quoting_ticks, conditional_quoting_price_type),
|
||||
conditional_quoting_ticks,
|
||||
conditional_quoting_price_type,
|
||||
//conditional_quoting_price_type,
|
||||
//new QuoteInsideMarketThrottleEnabledModel(quote_inside_market_throttle_ticks, quote_inside_market_throttle_min_move),
|
||||
//quote_inside_market_throttle_ticks,
|
||||
//quote_inside_market_throttle_min_move,
|
||||
@@ -294,9 +294,9 @@ namespace mbts.spark {
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
static string _deduce_symbol(SecurityImmutable security) {
|
||||
if (!string.IsNullOrWhiteSpace(security.Symbol)) return security.Symbol;
|
||||
if (security.Synthetic.Legs.Count() > 0) return security.Synthetic.Legs.First().Security.Symbol;
|
||||
static string _deduce_symbol(SecurityImmutable? security) {
|
||||
if (!string.IsNullOrWhiteSpace(security?.Symbol)) return security?.Symbol;
|
||||
if (security?.Synthetic.Legs.Count() > 0) return security?.Synthetic.Legs.First().Security.Symbol;
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace mbts.spark {
|
||||
} else return model.on_fill_tuples;
|
||||
}
|
||||
|
||||
static IConnectableObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> _get_observable(IBus bus, OrdersModel model, IObservable<StartupData> state) =>
|
||||
static IConnectableObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> _get_observable(IBus bus, OrdersModel model, IObservable<StartupData> state) =>
|
||||
state.SelectManyInSeries(s => _get_observable(bus, model, s));
|
||||
|
||||
public class Row : SecurityRow, IEquatable<Row>, IRowVM {
|
||||
@@ -186,7 +186,7 @@ namespace mbts.spark {
|
||||
readonly FillImmutable _fill;
|
||||
readonly OrderRecordImmutable _order;
|
||||
|
||||
public Row(FillTuple fill_tuple, OrdersModel model) : base(fill_tuple.security_md.HasValue ? fill_tuple.security_md.Value.security : SecurityDataHelper.Defaults.Security) {
|
||||
public Row(FillTuple fill_tuple, OrdersModel model) : base(fill_tuple.security_md.HasValue ? fill_tuple.security_md.Value.security : SecurityDataHelper.Defaults.SecurityImmutable) {
|
||||
_fill = fill_tuple.fill;
|
||||
_order = fill_tuple.order ?? OrderDataHelper.Defaults.OrderRecordImmutable;
|
||||
_model = model;
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace bts.shell {
|
||||
public static GridColumnSettings fill_kind => new GridColumnSettings(col_id(Columns.FillKind), DefaultColumnHeaders.FillKind, width: DefaultColumnWidths.FillKind);
|
||||
public static GridColumnSettings fill_key => new GridColumnSettings(col_id(Columns.FillKey), DefaultColumnHeaders.FillKey, width: DefaultColumnWidths.FillKey);
|
||||
public static GridColumnSettings order_key => new GridColumnSettings(col_id(Columns.OrderKey), DefaultColumnHeaders.OrderKey, width: DefaultColumnWidths.OrderKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
|
||||
public static IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
get {
|
||||
yield return new SortingCriteria(fill_time.id, false);
|
||||
|
||||
@@ -61,10 +61,12 @@ namespace bts.shell {
|
||||
var view = new Lazy<UIElement>(() => {
|
||||
var model = new LadderModel(md, _orders_model, _md_auxiliary_model);
|
||||
var settings_manager = new WindowSettingsManager<LadderSettings>(
|
||||
_settings_hub, state_settings,
|
||||
_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 vm = new LadderVM(_bus, model, settings_manager);
|
||||
var init = state_settings ?? settings_manager.default_settings;
|
||||
init.use_defaults |= state_settings == null;
|
||||
var vm = new LadderVM(_bus, model, settings_manager, init);
|
||||
return new LadderView(vm);
|
||||
});
|
||||
string thread = Configuration.get<bts.config.toggles.LaddersOnThreads>().enabled ? $"Ladder{id:00}" : "";
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
<!-- MaxHeight will force only x rows before vertical scrollbar shows up -->
|
||||
<!-- MinWidth will give enough room beyond the last column so that the the verical scrollbar does not eclipse the column content -->
|
||||
<UserControl x:Class="mbts.spark.MiniOrderBookView" x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:uc="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:order="clr-namespace:bts.api.orderman;assembly=spark_api"
|
||||
mc:Ignorable="d"
|
||||
MaxHeight="240"
|
||||
MinWidth="875"
|
||||
d:DesignHeight="600" d:DesignWidth="1080"
|
||||
FocusManager.IsFocusScope="True"
|
||||
Background="{StaticResource CommonBackground}">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<uspcv:TickDisplayConverter x:Key="TickDisplayConverter"/>
|
||||
<uspcv:OrderSubTypeDisplayTextConverter x:Key="OrderSubTypeLongDisplayTextConverter" UseLongName="True" />
|
||||
<DataTemplate x:Key="{x:Static sp:MiniOrderBookView.SubTypeFilterTemplateKey}">
|
||||
<usp:FilterContentControl
|
||||
ApplyCommandParameter="{Binding}"
|
||||
ApplyCommand="{x:Static c:DataGridFilterProperties.CommitCommand}"
|
||||
ClearCommandParameter="{Binding}"
|
||||
ClearCommand="{x:Static c:DataGridFilterProperties.ClearCommand}"
|
||||
>
|
||||
<c:ListBoxEx
|
||||
ItemsSource="{Binding items}"
|
||||
Selection="{Binding selected_items}"
|
||||
SelectionMode="Multiple"
|
||||
ItemContainerStyle="{StaticResource ListBoxItemCheckBoxStyle}"
|
||||
>
|
||||
<c:ListBoxEx.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={StaticResource OrderSubTypeLongDisplayTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
</c:ListBoxEx.ItemTemplate>
|
||||
</c:ListBoxEx>
|
||||
</usp:FilterContentControl>
|
||||
</DataTemplate>
|
||||
|
||||
<Style x:Key="{x:Static sp:MiniOrderBookView.RowStyleKey}" TargetType="ListBoxItem" BasedOn="{StaticResource DIYGridRowStyle}">
|
||||
<Style.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
|
||||
<Condition Binding="{Binding status}" Value="{uc:Enum Working, Type=order:OrderStatus}"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.Setters>
|
||||
<Setter Property="Background" Value="{x:Static Member=working_order_background_brush, MemberType={uc:Nested uc:BTSColors.Orders}}"/>
|
||||
</MultiDataTrigger.Setters>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<DataTemplate x:Key="{x:Static sp:MiniOrderBookView.RowTemplateKey}">
|
||||
<sp:MiniOrderBookRowView/>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<AdornerDecorator>
|
||||
<Border BorderThickness="4" BorderBrush="LightGray">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<c:DIYGrid
|
||||
x:Name="_grid"
|
||||
Grid.Row="0"
|
||||
Columns="{Binding columns}"
|
||||
CurrentRow="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Rows="{Binding rows}"
|
||||
RowStyle="{StaticResource {x:Static sp:MiniOrderBookView.RowStyleKey}}"
|
||||
RowTemplate="{StaticResource {x:Static sp:MiniOrderBookView.RowTemplateKey}}"
|
||||
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
|
||||
RowsHorizontalScrollBarVisibility="Collapsed"
|
||||
RowHeight="20"
|
||||
>
|
||||
<c:DIYGrid.Resources>
|
||||
<ResourceDictionary>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF27C7F7"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="#FFCCEEFB"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF1CC4F7"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF82DFFB"/>
|
||||
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
|
||||
<Style x:Key="{x:Static sp:OrderBookView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Focusable" Value="False"/>
|
||||
<Setter Property="Width" Value="16"/>
|
||||
<Setter Property="Height" Value="16"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Border Background="Transparent" Height="16" Padding="0,5,5,5" Width="16">
|
||||
<Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="{StaticResource TreeViewItem.TreeArrow.Static.Fill}" Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="135" CenterY="3" CenterX="3"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="RenderTransform" TargetName="ExpandPath">
|
||||
<Setter.Value>
|
||||
<RotateTransform Angle="180" CenterY="3" CenterX="3"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True"/>
|
||||
<Condition Property="IsChecked" Value="True"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ToolTip}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="Content" Value="{x:Static sys:String.Empty}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
<Trigger Property="Content" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</c:DIYGrid.Resources>
|
||||
<c:DIYGrid.InputBindings>
|
||||
<!--<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/> -->
|
||||
</c:DIYGrid.InputBindings>
|
||||
<c:DIYGrid.CommandBindings>
|
||||
<CommandBinding
|
||||
Command="{x:Static c:DIYGrid.DefaultRowCommand}"
|
||||
Executed="{x:Static c:DIYGrid.DefaultRowActionExecuted}"
|
||||
CanExecute="{x:Static c:DIYGrid.DefaultRowActionCanExecute}"
|
||||
/>
|
||||
</c:DIYGrid.CommandBindings>
|
||||
</c:DIYGrid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,26 @@
|
||||
using mbts.ui.controls;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal partial class MiniOrderBookView : UserControl {
|
||||
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
|
||||
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
|
||||
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Cancel, EditableQty, EditablePrice }
|
||||
public const string
|
||||
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
|
||||
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
|
||||
RowStyleKey = "RowStyle",
|
||||
RowTemplateKey = "RowTemplateKey",
|
||||
CVRowStyleKey = "CVRowStyle",
|
||||
CVRowTemplateKey = "CVRowTemplateKey";
|
||||
|
||||
public MiniOrderBookView(OrderBookVM vm) {
|
||||
InitializeComponent();
|
||||
DataContext = vm;
|
||||
}
|
||||
}
|
||||
|
||||
class MiniOrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookView.Columns> {
|
||||
public MiniOrderBookRowView() : base() { }
|
||||
}
|
||||
}
|
||||
@@ -194,6 +194,10 @@ namespace mbts.spark {
|
||||
// quantity
|
||||
Add(qty_column = new SizeColumn(
|
||||
new UIntMinMax<OrderRecordTuple>(ort => ort.order.quantity(), nameof(OrderBookRowVM.quantity))));
|
||||
// edit quantity column
|
||||
// not usually part of the main order book, but it's useful to add it
|
||||
// rather than debugging the popup window
|
||||
//Add(edit_qty_column = new EditableQtyColumn());
|
||||
// filled quantity
|
||||
Add(filled_qty_column = new FilledColumn(
|
||||
new UIntMinMax<OrderRecordTuple>(ort => ort.order.filled(), nameof(OrderBookRowVM.filled_quantity))));
|
||||
@@ -209,6 +213,10 @@ namespace mbts.spark {
|
||||
nameof(OrderBookRowVM.price)),
|
||||
DataGridFilterKnownTemplates.DoubleMinMax,
|
||||
_wrap_comparer(r => r.price)));
|
||||
// edit price column
|
||||
// not usually part of the main order book, but it's useful to add it
|
||||
// rather than debugging the popup window
|
||||
//Add(edit_price_column = new OrderBookVM.EditablePriceColumn());
|
||||
// avg filled price
|
||||
Add(avg_price_column = new SimpleTextColumn<DoubleMinMaxState>(
|
||||
Columns.AvgFilled,
|
||||
@@ -291,6 +299,7 @@ namespace mbts.spark {
|
||||
|
||||
public event Action filter_changed;
|
||||
|
||||
public CancelColumn cancel_column { get; }
|
||||
public SecurityColumn security_column { get; }
|
||||
public SimpleTextColumn<string[]> symbol_column { get; }
|
||||
public SimpleTextColumn<TimeFilterTuple> time_created_column { get; }
|
||||
@@ -303,7 +312,9 @@ namespace mbts.spark {
|
||||
public SimpleTextColumn<OrderSubType[]> sub_type_column { get; }
|
||||
public SideColumn side_column { get; }
|
||||
public SizeColumn qty_column { get; }
|
||||
public EditableQtyColumn edit_qty_column { get; }
|
||||
public SimpleTextColumn<DoubleMinMaxState> price_column { get; }
|
||||
public EditablePriceColumn edit_price_column { get; }
|
||||
public FilledColumn filled_qty_column { get; }
|
||||
public SimpleTextColumn<DoubleMinMaxState> last_price_column { get; }
|
||||
public SimpleTextColumn<DoubleMinMaxState> avg_price_column { get; }
|
||||
@@ -509,7 +520,7 @@ namespace mbts.spark {
|
||||
: base(key, header, get_text, sort_prop, width, filter, filter_template, comparer) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, _get_text(row), position, RowHeight,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, _get_text(row), position, row_height,
|
||||
font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
|
||||
}
|
||||
|
||||
@@ -519,7 +530,7 @@ namespace mbts.spark {
|
||||
: base(Columns.Side, "B/S", r => r.side.ToString(), nameof(OrderBookRowVM.side), DefaultColumnWidths.Side, filter, DataGridFilterKnownTemplates.EnumValues, _wrap_comparer(r => r.side)) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.side.ToString(), position, RowHeight,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.side.ToString(), position, row_height,
|
||||
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
|
||||
}
|
||||
|
||||
@@ -530,7 +541,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.quantity.ToString(), position, RowHeight,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.quantity.ToString(), position, row_height,
|
||||
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
|
||||
}
|
||||
|
||||
@@ -541,7 +552,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.filled_quantity.ToString(), position, RowHeight,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.filled_quantity.ToString(), position, row_height,
|
||||
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
|
||||
}
|
||||
|
||||
@@ -640,6 +651,8 @@ namespace mbts.spark {
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
grid.Children.Add(toggle_button);
|
||||
grid.Children.Add(security_view);
|
||||
grid.HorizontalAlignment = HorizontalAlignment.Center;
|
||||
grid.VerticalAlignment = VerticalAlignment.Center;
|
||||
//
|
||||
var border = new Border { Child = grid };
|
||||
CellRender.set_background_animation_bindings(border, nameof(OrderBookRowVM.security));
|
||||
@@ -651,6 +664,88 @@ namespace mbts.spark {
|
||||
base.OnSortDirectionChanged(new_value);
|
||||
}
|
||||
}
|
||||
|
||||
public class CancelColumn : FrameworkElementColumnBase<Columns> {
|
||||
readonly BindingBase _command_binding;
|
||||
|
||||
public CancelColumn(string command_property) : base(Columns.Cancel) {
|
||||
id = key.ToString();
|
||||
Header = DefaultColumnHeaders.Cancel;
|
||||
_command_binding = new Binding(command_property);
|
||||
}
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var grid = new Grid() {
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
};
|
||||
var l1 = new System.Windows.Shapes.Line() {
|
||||
StrokeThickness = 1,
|
||||
Stroke = Brushes.White,
|
||||
X1 = 0.5,
|
||||
Y1 = 0.5,
|
||||
X2 = 7,
|
||||
Y2 = 7
|
||||
};
|
||||
var l2 = new System.Windows.Shapes.Line() {
|
||||
StrokeThickness = 1,
|
||||
Stroke = Brushes.White,
|
||||
X1 = 0.5,
|
||||
Y1 = 7,
|
||||
X2 = 7,
|
||||
Y2 = 0.5
|
||||
};
|
||||
grid.Children.Add(l1);
|
||||
grid.Children.Add(l2);
|
||||
|
||||
var button = new Button() {
|
||||
Content = grid,
|
||||
Background = Brushes.Red,
|
||||
Foreground = Brushes.White,
|
||||
Margin = new Thickness(2),
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Height = 14,
|
||||
Width = 14
|
||||
};
|
||||
|
||||
button.SetBinding(Button.CommandProperty, _command_binding);
|
||||
return button;
|
||||
}
|
||||
}
|
||||
|
||||
public class EditableQtyColumn:FrameworkElementColumnBase<Columns> {
|
||||
|
||||
public EditableQtyColumn() : base(Columns.EditableQty) {
|
||||
id = key.ToString();
|
||||
Header = "Qty";
|
||||
Width = 64;
|
||||
}
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var tick_up_down = new TickUpDownNullableUIntCommit();
|
||||
tick_up_down.SetBinding(TickUpDownNullableUIntCommit.SourceValueProperty, nameof(OrderBookRowVM.prospective_quantity));
|
||||
tick_up_down.SetBinding(TickUpDownNullableUIntCommit.OnCommitProperty, nameof(OrderBookRowVM.modify_order_quantity));
|
||||
return tick_up_down;
|
||||
}
|
||||
}
|
||||
|
||||
public class EditablePriceColumn : FrameworkElementColumnBase<Columns> {
|
||||
|
||||
public EditablePriceColumn() : base(Columns.EditablePrice) {
|
||||
id = key.ToString();
|
||||
Header = "Price";
|
||||
Width = 96;
|
||||
}
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var price_up_down = new PriceEntryCommitControl();
|
||||
price_up_down.SetBinding(PriceEntryCommitControl.SourceValueProperty, nameof(OrderBookRowVM.prospective_price));
|
||||
price_up_down.SetBinding(PriceEntryCommitControl.SourceContractSpecProperty, nameof(OrderBookRowVM.contract_spec));
|
||||
price_up_down.SetBinding(PriceEntryCommitControl.OnCommitProperty, nameof(OrderBookRowVM.modify_order_price));
|
||||
return price_up_down;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
static class RenderUtils {
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace mbts.spark {
|
||||
class OrderBookRowCollection : ObservableCollection<OrderBookRowVM> {
|
||||
static readonly IEqualityComparer<INode<OrderBookRowVM>> _node_equality_comparer = EqualityComparer.Create<INode<OrderBookRowVM>, uint>(n => n.value.order_key);
|
||||
readonly Func<OrderRecordTuple, OrderBookRowVM> _row_factory;
|
||||
/// Critical: Any and all row mutations <see cref="OrderBookRowVM"/> must be executed through <see cref="SortedTree{T, TKey}.TryReplace(TKey, Func{T, T})"/>.
|
||||
/// Mutations can change the sort-position of a row, and <see cref="_tree"/> will be broken if its elements aren't properly sorted (it relies heavily on binary-search logic).
|
||||
SortedTree<OrderBookRowVM, uint> _tree;
|
||||
IComparer<OrderBookRowVM> _comparer;
|
||||
Func<OrderRecordTuple, bool> _filter;
|
||||
|
||||
@@ -6,6 +6,9 @@ using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
@@ -13,15 +16,34 @@ namespace mbts.spark {
|
||||
const string _unknown_security_price_format_string = "0.#########";
|
||||
const int _unknown_security_price_num_fractional_digits = 9;
|
||||
readonly OrderBookVM _owner;
|
||||
IRaiseCanExecuteChangedCommand _show_order_modify_cmd, _toggle_expanded_cmd;
|
||||
bool _is_parent, _is_expanded;
|
||||
IRaiseCanExecuteChangedCommand _show_order_modify_cmd, _toggle_expanded_cmd, _cancel_cmd, _modify_order_quantity, _modify_order_price;
|
||||
bool _is_parent, _is_expanded, _is_selected;
|
||||
int _depth_level;
|
||||
SecurityMD? _security_md => order_tuple.security_md;
|
||||
|
||||
public OrderBookRowVM(OrderBookVM owner, OrderRecordTuple order_tuple) {
|
||||
|
||||
// for mini-ob
|
||||
uint _prospective_quantity;
|
||||
double _prospective_price;
|
||||
|
||||
|
||||
public OrderBookRowVM(OrderBookVM owner, OrderRecordTuple order_tuple, bool is_expanded) {
|
||||
_owner = owner;
|
||||
this.order_tuple = order_tuple;
|
||||
is_visible = !is_child;
|
||||
_is_expanded = is_expanded;
|
||||
prospective_quantity = this.quantity;
|
||||
prospective_price = this.price;
|
||||
this.invalidated += OrderBookRowVM_invalidated;
|
||||
}
|
||||
|
||||
private void OrderBookRowVM_invalidated() {
|
||||
modify_order_quantity?.TryRaiseCanExecuteChanged();
|
||||
modify_order_price?.TryRaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
this.invalidated -= OrderBookRowVM_invalidated;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public event Action invalidated;
|
||||
@@ -34,6 +56,7 @@ namespace mbts.spark {
|
||||
public uint parent_order_key => order.ParentKey;
|
||||
public uint security_key => order.security_key();
|
||||
public SecurityImmutable? security => _security_md.security();
|
||||
public ContractSpecificationImmutable? contract_spec => security.spec_or_default();
|
||||
public string security_text => security.display_name();
|
||||
public Venue exchange => _security_md.exchange();
|
||||
public string symbol => _security_md.symbol();
|
||||
@@ -72,6 +95,29 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
public int depth_level { get => _depth_level; set => Set(ref _depth_level, value); }
|
||||
public bool is_selected {
|
||||
get => _is_selected;
|
||||
set => Set(ref _is_selected, value);
|
||||
}
|
||||
public uint prospective_quantity {
|
||||
get => _prospective_quantity;
|
||||
set {
|
||||
if (Set(ref _prospective_quantity, value)) {
|
||||
RaisePropertyChanged(nameof(is_qty_dirty));
|
||||
}
|
||||
}
|
||||
}
|
||||
public double prospective_price {
|
||||
get => _prospective_price;
|
||||
set {
|
||||
if (Set(ref _prospective_price, value)) {
|
||||
RaisePropertyChanged(nameof(is_price_dirty));
|
||||
}
|
||||
}
|
||||
}
|
||||
public bool is_qty_dirty => this.prospective_quantity != this.quantity;
|
||||
public bool is_price_dirty => this.prospective_price != this.price;
|
||||
|
||||
// Commands
|
||||
public ICommand toggle_command => _toggle_expanded_cmd ?? (
|
||||
_toggle_expanded_cmd = new DelegateCommand(() => is_expanded = !is_expanded));
|
||||
@@ -79,6 +125,38 @@ namespace mbts.spark {
|
||||
ICommand DIYGrid.IActionableRowVM.default_command => _show_order_modify_cmd ??
|
||||
(_show_order_modify_cmd = new DelegateCommand(() => _owner.show_order_modify(this), () => status == OrderStatus.Working));
|
||||
|
||||
public ICommand cancel_command => _cancel_cmd ??
|
||||
(_cancel_cmd = new DelegateCommand(() => _owner.cancel_order(this), () => status == OrderStatus.Working));
|
||||
|
||||
public ICommand modify_order_quantity => _modify_order_quantity ??
|
||||
(_modify_order_quantity = new DelegateCommand(
|
||||
(desired_quantity) => {
|
||||
_owner.modify_order_quantity(this, (uint)desired_quantity);
|
||||
_unselect_me();
|
||||
},
|
||||
(desired_quantity) =>
|
||||
(desired_quantity != null && this.status == OrderStatus.Working && (uint)desired_quantity != this.quantity)
|
||||
|| is_qty_dirty
|
||||
)
|
||||
);
|
||||
|
||||
public ICommand modify_order_price => _modify_order_price ??
|
||||
(_modify_order_price = new DelegateCommand(
|
||||
(desired_price) => {
|
||||
_owner.modify_order_price(this, (double)desired_price);
|
||||
_unselect_me();
|
||||
},
|
||||
(desired_price) =>
|
||||
(desired_price != null && this.status == OrderStatus.Working && (double)desired_price != this.price)
|
||||
|| is_price_dirty
|
||||
)
|
||||
);
|
||||
|
||||
private void _unselect_me() {
|
||||
this.is_selected = false;
|
||||
_owner.current_row = null;
|
||||
}
|
||||
|
||||
// These fns present a way for the orderbook to avoid noisy, intermittent collection changes during "Expand/Collapse All" ops,
|
||||
// when incremental changes are ignored in favor of a full reset, after 'is_expanded' is updated for all rows.
|
||||
internal void expand_internal() => Set(ref _is_expanded, true);
|
||||
@@ -103,6 +181,14 @@ namespace mbts.spark {
|
||||
// however, the order-type and security cells DO use databindings - if these cells have changed, the PropertyChanged event must be raised regardless of whether or not invalidated is raised
|
||||
if (old_order.order_type() != order.order_type())
|
||||
RaisePropertyChanged(nameof(order_type), nameof(order_metadata));
|
||||
if (old_order.quantity() != order.quantity()) {
|
||||
RaisePropertyChanged(nameof(quantity));
|
||||
RaisePropertyChanged(nameof(is_qty_dirty));
|
||||
}
|
||||
if (old_order.price() != order.price()) {
|
||||
RaisePropertyChanged(nameof(price));
|
||||
RaisePropertyChanged(nameof(is_price_dirty));
|
||||
}
|
||||
if (!had_security && _security_md.HasValue) {
|
||||
RaisePropertyChanged(nameof(security), nameof(security_text));
|
||||
// need to raise invalidated to refresh the price cells (the security.ContractSpec determines how they are formatted - Ticks!)
|
||||
|
||||
@@ -16,6 +16,7 @@ using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using static bts.shell.OrderBookWindowFactory;
|
||||
@@ -33,44 +34,84 @@ namespace mbts.spark {
|
||||
readonly IWindowSettingsManager<OrderBookGridSettings> _settings_manager;
|
||||
readonly List<IDisposable> _resources;
|
||||
OrderBookStartupData _state;
|
||||
bool _is_row_collection_populated;
|
||||
|
||||
public OrderBookVM(IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model, IWindowSettingsManager<OrderBookGridSettings> settings_manager) {
|
||||
public OrderBookVM(IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model, IWindowSettingsManager<OrderBookGridSettings> settings_manager, bool rows_are_initially_expanded = false) {
|
||||
_bus = bus;
|
||||
_model = model;
|
||||
_settings_manager = settings_manager;
|
||||
_columns = new OrderBookColumnCollection(bus, model, model.on_order_securities.Select(u => (u.added.Values.Select(smd => smd.security), u.removed.Values.Select(smd => smd.security))));
|
||||
_row_collection = new OrderBookRowCollection(ort => new OrderBookRowVM(this, ort), _columns.get_sorting_data().comparer) { filter = _columns.get_filter() };
|
||||
// we use an ObservableAppendCollection b/c the mini-ob needs a cancel column at the front
|
||||
columns = new ObservableAppendCollection<DIYGrid.ColumnBase>(_columns);
|
||||
_row_collection = new OrderBookRowCollection(ort => new OrderBookRowVM(this, ort, rows_are_initially_expanded), _columns.get_sorting_data().comparer) { filter = _columns.get_filter() };
|
||||
_row_collection.CollectionChanged += _row_collection_CollectionChanged;
|
||||
_current_row = backing<OrderBookRowVM>(nameof(current_row));
|
||||
_columns.filter_changed += on_filter_changed;
|
||||
collapse_all_rows_command = new DelegateCommand(_row_collection.collapse_all);
|
||||
expand_all_rows_command = new DelegateCommand(_row_collection.expand_all);
|
||||
grid_settings_manager = _create_grid_settings_manager(owner_title, bus, notification_service, model, rows, columns);
|
||||
grid_settings_manager = _create_grid_settings_manager(owner_title, bus, notification_service, model, rows, core_columns);
|
||||
_resources = new List<IDisposable> {
|
||||
model.on_order_tuples_table.OnDispatcher().Subscribe(_row_collection.update),
|
||||
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
|
||||
grid_settings_manager,
|
||||
Disposable.Create(() => {
|
||||
_columns.filter_changed -= on_filter_changed;
|
||||
_row_collection.CollectionChanged -= _row_collection_CollectionChanged;
|
||||
Disposable.dispose(_row_collection);
|
||||
}),
|
||||
_columns
|
||||
};
|
||||
if (settings_manager != null) {
|
||||
_resources.Add(settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager));
|
||||
}
|
||||
void on_filter_changed() => _row_collection.filter = _columns.get_filter();
|
||||
}
|
||||
|
||||
private void _row_collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
|
||||
is_row_collection_populated = _row_collection.Count != 0;
|
||||
}
|
||||
|
||||
public event Action<OrderRecordImmutable?> selected_order_changed;
|
||||
public event Action<bool> OnDisposed;
|
||||
|
||||
public ObservableCollection<DIYGrid.ColumnBase> columns => _columns;
|
||||
public ObservableAppendCollection<DIYGrid.ColumnBase> columns { get; }
|
||||
public ObservableCollection<DIYGrid.ColumnBase> core_columns => _columns;
|
||||
public ObservableCollection<OrderBookRowVM> rows => _row_collection;
|
||||
public OrderBookRowVM current_row { get => _current_row.Value; set => _current_row.Value = value ?? _selected_rows.FirstOrDefault(); }
|
||||
public OrderBookRowVM current_row {
|
||||
get => _current_row.Value;
|
||||
set {
|
||||
_current_row.Value = value;
|
||||
// this is so that when the current_row is set to null from
|
||||
// the row_vm, we propagate the change up to the OrderBookWindow
|
||||
// so that the cancel and modify commands can_execute is updated.
|
||||
// Note that this is really only for if/when the edit_xxx columns
|
||||
// from the mini-ob are present in regular orderbook, else this is
|
||||
// a no-op.
|
||||
selected_order_changed?.Invoke(current_order);
|
||||
}
|
||||
}
|
||||
public OrderRecordImmutable? current_order => current_row?.order;
|
||||
public IEnumerable<OrderRecordImmutable> selected_orders => _selected_rows.Select(r => r.order);
|
||||
public _GridSettingsManager grid_settings_manager { get; }
|
||||
public ICommand collapse_all_rows_command { get; }
|
||||
public ICommand expand_all_rows_command { get; }
|
||||
|
||||
public bool is_row_collection_populated {
|
||||
get => _is_row_collection_populated;
|
||||
set => Set(ref _is_row_collection_populated, value);
|
||||
}
|
||||
|
||||
public void row_selection_changed(SelectionChangedEventArgs e) {
|
||||
if (e.RemovedItems != null) {
|
||||
foreach (var row_vm in e.RemovedItems.Cast<OrderBookRowVM>()) {
|
||||
row_vm.is_selected = false;
|
||||
}
|
||||
}
|
||||
if (e.AddedItems != null) {
|
||||
foreach (var row_vm in e.AddedItems.Cast<OrderBookRowVM>()) {
|
||||
row_vm.is_selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
RowSelectionHelper.apply_changes(_selected_rows, e);
|
||||
selected_order_changed?.Invoke(current_order);
|
||||
}
|
||||
@@ -131,6 +172,15 @@ namespace mbts.spark {
|
||||
internal void show_order_modify(OrderBookRowVM row) =>
|
||||
_bus.post(SparkTags.ShowModifyOrderTag, row.order_key);
|
||||
|
||||
internal void cancel_order(OrderBookRowVM row) =>
|
||||
_bus.post(SparkTags.CancelOrders, new List<uint> { row.order_key });
|
||||
|
||||
internal void modify_order_quantity(OrderBookRowVM row, uint size) =>
|
||||
_bus.post(SparkTags.UpdateOrder, (row.order_key, row.price, size));
|
||||
|
||||
internal void modify_order_price(OrderBookRowVM row, double price) =>
|
||||
_bus.post(SparkTags.UpdateOrder, (row.order_key, price, row.quantity));
|
||||
|
||||
internal void on_expanded_changed(OrderBookRowVM row) {
|
||||
using (_row_collection.defer_refresh(true)) {
|
||||
if (row.is_expanded) {
|
||||
@@ -187,7 +237,8 @@ namespace mbts.spark {
|
||||
return pvm;
|
||||
},
|
||||
(gen, anims) => new OrderBookGridSettings(gen, anims.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_builder, a.cell_animations, a.row_animations, a.sound_notifications))),
|
||||
notification_service.data_cache, col_settings_flags);
|
||||
(notification_service == null) ? null : notification_service.data_cache,
|
||||
col_settings_flags);
|
||||
}
|
||||
|
||||
static OrderRecordTuplePredicate.PredicateTypeOneofCase _corresponding_predicate_type(Columns col) {
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace mbts.spark {
|
||||
internal partial class OrderBookView : UserControl {
|
||||
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
|
||||
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
|
||||
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey }
|
||||
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Cancel, EditableQty, EditablePrice }
|
||||
public const string
|
||||
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
|
||||
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
|
||||
@@ -14,8 +14,6 @@ namespace mbts.spark {
|
||||
CVRowStyleKey = "CVRowStyle",
|
||||
CVRowTemplateKey = "CVRowTemplateKey";
|
||||
|
||||
public const double RowHeight = 17;
|
||||
|
||||
public OrderBookView(OrderBookVM vm) {
|
||||
InitializeComponent();
|
||||
DataContext = vm;
|
||||
@@ -23,6 +21,6 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
class OrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookView.Columns> {
|
||||
public OrderBookRowView() : base(OrderBookView.RowHeight) { }
|
||||
public OrderBookRowView() : base() { }
|
||||
}
|
||||
}
|
||||
@@ -131,7 +131,7 @@ namespace mbts.spark {
|
||||
return _vm.grid_settings_manager.create_vm(
|
||||
// we need to get the most up to date GridSettings when launching b/c users may
|
||||
// have changed some settings on the grid directly, like column widths.
|
||||
s.general.with(_vm.columns.Select(c => c.create())),
|
||||
s.general.with(_vm.core_columns.Select(c => c.create())),
|
||||
this.observe_title()
|
||||
);
|
||||
}
|
||||
@@ -167,11 +167,9 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
private void _vm_selected_order_changed(OrderRecordImmutable? e) {
|
||||
if (e.HasValue) {
|
||||
_modify_order_command.raiseCanExecuteChanged();
|
||||
_cancel_selected_orders_command.raiseCanExecuteChanged();
|
||||
_recreate_selected_orders_command.raiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,8 @@ namespace bts.shell {
|
||||
public static OrderBookGridSettings DefaultGrid => new OrderBookGridSettings(_default_general_grid);
|
||||
|
||||
static GridSettings _default_general_grid => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion);
|
||||
public static GridSettings default_mini_grid => GridSettings.Default.with(DefaultColumns.mini, DefaultColumns.sorting_criterion);
|
||||
|
||||
|
||||
static class DefaultColumns {
|
||||
public static GridColumnSettings security => new GridColumnSettings(Columns.Security.ToString(), DIYGrid.DefaultColumnHeaders.Security, width: DIYGrid.DefaultColumnWidths.Security);
|
||||
@@ -110,7 +112,7 @@ namespace bts.shell {
|
||||
public static GridColumnSettings last_price => new GridColumnSettings(Columns.LastPrice.ToString(), "Last Filled", width: DIYGrid.DefaultColumnWidths.LastPrice, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings status => new GridColumnSettings(Columns.Status.ToString(), "Status", width: 80);
|
||||
public static GridColumnSettings last_message => new GridColumnSettings(Columns.LastMessage.ToString(), "Last Message", width: DIYGrid.DefaultColumnWidths.LastMessage);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, width: DIYGrid.DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, is_visible: false, width: DIYGrid.DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings order_key => new GridColumnSettings(Columns.OrderKey.ToString(), DIYGrid.DefaultColumnHeaders.OrderKey, width: DIYGrid.DefaultColumnWidths.OrderKey);
|
||||
public static GridColumnSettings parent_key => new GridColumnSettings(Columns.ParentKey.ToString(), "Parent Key", width: DIYGrid.DefaultColumnWidths.ParentKey);
|
||||
|
||||
@@ -145,7 +147,23 @@ namespace bts.shell {
|
||||
yield return parent_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<GridColumnSettings> _mini_mode_columns_to_hide = new HashSet<GridColumnSettings> {
|
||||
time_created, exchange, sub_type, quantity, price, avg_price, last_price, status, last_message, security_key, order_key, parent_key
|
||||
};
|
||||
|
||||
public static IEnumerable<GridColumnSettings> mini {
|
||||
get {
|
||||
foreach (var col_setting in all) {
|
||||
if (_mini_mode_columns_to_hide.Contains(col_setting)) {
|
||||
yield return col_setting.with(is_visible: false);
|
||||
} else {
|
||||
yield return col_setting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace mbts.ui.spark {
|
||||
public DataTemplate JoinOrderTemplate { get; set; }
|
||||
public DataTemplate TopOrderTemplate { get; set; }
|
||||
public DataTemplate CoverOrderTemplate { get; set; }
|
||||
public DataTemplate OCOOrderTemplate { get; set; }
|
||||
public DataTemplate PricerOrderTemplate { get; set; }
|
||||
public DataTemplate OffsetOrderTemplate { get; set; }
|
||||
public DataTemplate TriggerOrderTemplate { get; set; }
|
||||
public DataTemplate IcebergOrderTemplate { get; set; }
|
||||
@@ -43,6 +45,8 @@ namespace mbts.ui.spark {
|
||||
case CustomOrderDefinition.CustomOneofCase.Join: return JoinOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Top: return TopOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Cover: return CoverOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Oco : return OCOOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Pricer: return PricerOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Offset: return OffsetOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Trigger: return TriggerOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Iceberg: return IcebergOrderTemplate;
|
||||
|
||||
@@ -139,13 +139,13 @@ namespace mbts.spark {
|
||||
tif => time_in_forces.Contains(tif));
|
||||
_dispose = Disposable.Create(() => _order_type.Changed -= on_selected_order_type_changed);
|
||||
// local methods
|
||||
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd, string id, string display_name) {
|
||||
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable? cd, string id, string display_name) {
|
||||
IOrderTypeVM vm = null;
|
||||
if(cd.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic)
|
||||
vm = OrderTypeVMFactory.create(cd.CustomCase, id, display_name, model, security_md_source, cd.Dynamic.DynamicOrderType);
|
||||
if(cd?.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic)
|
||||
vm = OrderTypeVMFactory.create(cd.Value.CustomCase, id, display_name, model, security_md_source, cd.Value.Dynamic.DynamicOrderType);
|
||||
else
|
||||
vm = OrderTypeVMFactory.create(cd.CustomCase, id, display_name, model, security_md_source);
|
||||
vm.load_from(cd);
|
||||
vm = OrderTypeVMFactory.create(cd.Value.CustomCase, id, display_name, model, security_md_source);
|
||||
vm.load_from(cd.Value);
|
||||
return vm;
|
||||
}
|
||||
InlineUserProperty<IOrderTypeVM> get_order_type_vm_prop(InlineOrderProperties p) {
|
||||
|
||||
@@ -126,6 +126,8 @@ namespace mbts.spark
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public bool has_child => _order_def_tags.Any();
|
||||
|
||||
public IOrderTypeVM child_order
|
||||
{
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.ui;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using static bts.api.orderman.CustomOrderDefinition;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
static partial class OrderTypeVMFactory
|
||||
{
|
||||
class InlineOCOOrderProperties
|
||||
{
|
||||
public InlineOCOOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
|
||||
var othertype = CustomOneofCase.Limit;
|
||||
this.selected_other_order_type = new InlineUserProperty<OrderTemplateHeader>(new OrderTemplateHeader(othertype), true);
|
||||
this.other_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
|
||||
other_ticks = new InlineUserProperty<int>(1, false);
|
||||
other_side = new InlineUserProperty<bts.api.RelativeSide>(bts.api.RelativeSide.Same, false);
|
||||
}
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_other_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> other_order_types;
|
||||
|
||||
public readonly InlineUserProperty<int> other_ticks;
|
||||
public readonly InlineUserProperty<bts.api.RelativeSide> other_side;
|
||||
|
||||
}
|
||||
|
||||
class OCOOrderVM : OrderTypeVMBase<OCOOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineOCOOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _other_order_choices;
|
||||
IOrderTypeVM _other_order;
|
||||
IDisposable _other_order_errors_sub;
|
||||
|
||||
public int other_ticks { get; set; } = 1;
|
||||
public bts.api.RelativeSide other_side { get; set; } = bts.api.RelativeSide.Same;
|
||||
|
||||
public OCOOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineOCOOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name)
|
||||
{ }
|
||||
|
||||
public OCOOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineOCOOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
{
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p =>
|
||||
{
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
{
|
||||
_order_types.SetItems(p.child_order_types.value);
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
if (_other_order_choices != null && (_other_order == null || p.selected_other_order_type.force_override))
|
||||
{
|
||||
_select_other_order(p.selected_other_order_type.value.key.type);
|
||||
}
|
||||
if (p.other_ticks.force_override)
|
||||
{
|
||||
other_ticks = p.other_ticks.value;
|
||||
RaisePropertyChanged(nameof(other_ticks));
|
||||
}
|
||||
if (p.other_side.force_override)
|
||||
{
|
||||
other_side = p.other_side.value;
|
||||
RaisePropertyChanged(nameof(other_side));
|
||||
}
|
||||
_inline_props = p;
|
||||
_validate();
|
||||
})
|
||||
);
|
||||
_validate();
|
||||
}
|
||||
|
||||
public override void assign_to(CustomOrderDefinition value)
|
||||
{
|
||||
if (_child_order == null)
|
||||
_select_child_order(model.factory.default_order_type(_security.Value, true));
|
||||
if (_other_order == null )
|
||||
_select_other_order(model.factory.default_order_type(_security.Value, true));
|
||||
_validate();
|
||||
if (!HasErrors)
|
||||
{
|
||||
var i = factory.oco_order();
|
||||
i.OtherTicks = other_ticks;
|
||||
i.OtherSide = other_side;
|
||||
child_order.assign_to(i.Child);
|
||||
if (other_order != null)
|
||||
other_order.assign_to(i.Other);
|
||||
value.Oco = i;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<bts.api.RelativeSide> other_side_types => EnumHelper.get_values_except<bts.api.RelativeSide>(bts.api.RelativeSide.Undefined);
|
||||
|
||||
public ICollection<IOrderTypeVM> child_order_types
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<IOrderTypeVM> other_order_types
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_other_order_choices == null)
|
||||
_other_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
return _other_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public IOrderTypeVM child_order
|
||||
{
|
||||
get
|
||||
{
|
||||
init();
|
||||
return _child_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
Disposable.dispose(ref _child_order_errors_sub);
|
||||
_child_order = value;
|
||||
_child_order?.init();
|
||||
if (_child_order is INotifyDataErrorInfo ndei)
|
||||
{
|
||||
ndei.ErrorsChanged += on_errors_changed;
|
||||
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
|
||||
_validate();
|
||||
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
|
||||
}
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
}
|
||||
|
||||
public IOrderTypeVM other_order
|
||||
{
|
||||
get
|
||||
{
|
||||
init();
|
||||
return _other_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
Disposable.dispose(ref _other_order_errors_sub);
|
||||
_other_order = value;
|
||||
_other_order?.init();
|
||||
if (_other_order is INotifyDataErrorInfo ndei)
|
||||
{
|
||||
ndei.ErrorsChanged += on_errors_changed;
|
||||
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
|
||||
_validate();
|
||||
_other_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
|
||||
}
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(other_order));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposable.dispose(ref _dispose);
|
||||
if (_child_order_choices != null)
|
||||
_child_order_choices.OfType<IDisposable>().DisposeAll();
|
||||
if (_other_order_choices != null)
|
||||
_other_order_choices.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
|
||||
public override void init()
|
||||
{
|
||||
if (_child_order == null && _security.HasValue)
|
||||
_select_child_order();
|
||||
if (_other_order == null && _security.HasValue)
|
||||
_select_other_order();
|
||||
_validate();
|
||||
}
|
||||
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
void _select_child_order(CustomOneofCase ot)
|
||||
{
|
||||
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
|
||||
if (_child_order == null)
|
||||
_child_order = child_order_types.FirstOrDefault();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
|
||||
void _select_other_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_other_order_type.has_value ?
|
||||
_inline_props.selected_other_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_other_order(ot);
|
||||
}
|
||||
|
||||
void _select_other_order(CustomOneofCase ot)
|
||||
{
|
||||
_other_order = other_order_types.FirstOrDefault(vm => vm.order_type == ot);
|
||||
if (_other_order == null)
|
||||
_other_order = other_order_types.FirstOrDefault();
|
||||
RaisePropertyChanged(nameof(other_order));
|
||||
}
|
||||
|
||||
|
||||
void _validate()
|
||||
{
|
||||
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
|
||||
set_error(nameof(child_order), string.Empty, true);
|
||||
else clear_error(nameof(child_order));
|
||||
|
||||
if ((_other_order == null ) || (_other_order is INotifyDataErrorInfo hdei && hdei.HasErrors))
|
||||
set_error(nameof(other_order), string.Empty, true);
|
||||
else clear_error(nameof(other_order));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,12 @@ namespace mbts.spark {
|
||||
public static IOrderTypeVM stop(OrdersModel model, string id, string display_name) => new StopOrderVM(model, id, display_name);
|
||||
public static IOrderTypeVM stop_limit(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new StopLimitOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TimeBoxOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM tick(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TickOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new LeanOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new JoinOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TopOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM cover(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new CoverOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM oco(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new OCOOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM pricer(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new PricerOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM offset(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new OffsetOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM trigger(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TriggerOrderVM(model, security_md_source, id, display_name);
|
||||
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new IcebergOrderVM(model, security_md_source, id, display_name);
|
||||
@@ -34,11 +35,12 @@ namespace mbts.spark {
|
||||
case CustomOneofCase.Stop: return stop(model, id, display_name);
|
||||
case CustomOneofCase.StopLimit: return stop_limit(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.TimeBox: return time_box(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Tick: return tick(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Lean: return lean(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Join: return join(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Top: return top(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Cover: return cover(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Oco : return oco(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Pricer: return pricer(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Offset: return offset(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Trigger: return trigger(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Iceberg: return iceberg(model, security_md_source, id, display_name);
|
||||
|
||||
+25
-53
@@ -15,52 +15,42 @@ namespace mbts.spark
|
||||
{
|
||||
static partial class OrderTypeVMFactory
|
||||
{
|
||||
class InlineTickOrderProperties
|
||||
class InlinePricerOrderProperties
|
||||
{
|
||||
public InlineTickOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
|
||||
public InlinePricerOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
tick_offset_on_deploy = new InlineUserProperty<uint>(0, false);
|
||||
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
|
||||
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
|
||||
cross_ticks = new InlineUserProperty<uint>(0, false);
|
||||
max_chase_ticks = new InlineUserProperty<uint>(10, false);
|
||||
detach_on_toward = new InlineUserProperty<bool>(true, false);
|
||||
price_offset_ticks = new InlineUserProperty<int>(0, false);
|
||||
price_factor = new InlineUserProperty<double>(0, false);
|
||||
}
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
|
||||
public readonly InlineUserProperty<uint> tick_offset_on_deploy;
|
||||
public readonly InlineUserProperty<bts.spark.QuantityMeasure> cross_threshold;
|
||||
public readonly InlineUserProperty<uint> cross_ticks;
|
||||
public readonly InlineUserProperty<uint> max_chase_ticks;
|
||||
public readonly InlineUserProperty<bool> detach_on_toward;
|
||||
public readonly InlineUserProperty<int> price_offset_ticks;
|
||||
public readonly InlineUserProperty<double> price_factor;
|
||||
}
|
||||
|
||||
class TickOrderVM : OrderTypeVMBase<TickOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
class PricerOrderVM : OrderTypeVMBase<PricerOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineTickOrderProperties _inline_props;
|
||||
InlinePricerOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
public uint tick_offset_on_deploy { get; set; } = 0;
|
||||
public bts.spark.QuantityMeasure cross_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
|
||||
public uint cross_ticks { get; set; } = 0;
|
||||
public uint max_chase_ticks { get; set; } = 10;
|
||||
public bool detach_on_toward { get; set; } = true;
|
||||
public int price_offset_ticks { get; set; } = 0;
|
||||
public double price_factor { get; set; } = 1.0;
|
||||
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
public PricerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTickOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { }
|
||||
new InlinePricerOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { }
|
||||
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTickOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
public PricerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlinePricerOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
{
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
@@ -76,31 +66,16 @@ namespace mbts.spark
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
if (p.tick_offset_on_deploy.force_override)
|
||||
if (p.price_offset_ticks.force_override)
|
||||
{
|
||||
tick_offset_on_deploy = p.tick_offset_on_deploy.value;
|
||||
RaisePropertyChanged(nameof(tick_offset_on_deploy));
|
||||
price_offset_ticks = p.price_offset_ticks.value;
|
||||
RaisePropertyChanged(nameof(price_offset_ticks));
|
||||
}
|
||||
if (p.cross_threshold.force_override)
|
||||
if (p.price_factor.force_override)
|
||||
{
|
||||
cross_threshold = p.cross_threshold.value;
|
||||
RaisePropertyChanged(nameof(cross_threshold));
|
||||
}
|
||||
if (p.cross_ticks.force_override)
|
||||
{
|
||||
cross_ticks = p.cross_ticks.value;
|
||||
RaisePropertyChanged(nameof(cross_ticks));
|
||||
}
|
||||
if (p.max_chase_ticks.force_override)
|
||||
{
|
||||
max_chase_ticks = p.max_chase_ticks.value;
|
||||
RaisePropertyChanged(nameof(max_chase_ticks));
|
||||
}
|
||||
if (p.detach_on_toward.force_override)
|
||||
{
|
||||
detach_on_toward = p.detach_on_toward.value;
|
||||
RaisePropertyChanged(nameof(detach_on_toward));
|
||||
}
|
||||
price_factor = p.price_factor.value;
|
||||
RaisePropertyChanged(nameof(price_factor));
|
||||
}
|
||||
_inline_props = p;
|
||||
_validate();
|
||||
})
|
||||
@@ -115,14 +90,11 @@ namespace mbts.spark
|
||||
_validate();
|
||||
if (!HasErrors)
|
||||
{
|
||||
var i = factory.tick_order();
|
||||
i.TickOffsetOnDeploy = tick_offset_on_deploy;
|
||||
i.CrossThreshold = cross_threshold;
|
||||
i.CrossTicks = cross_ticks;
|
||||
i.MaxChaseTicks = max_chase_ticks;
|
||||
i.DetachOnToward = detach_on_toward;
|
||||
var i = factory.pricer_order();
|
||||
i.PriceOffsetTicks = price_offset_ticks;
|
||||
i.PriceFactor = price_factor;
|
||||
child_order.assign_to(i.Child);
|
||||
value.Tick = i;
|
||||
value.Pricer = i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace mbts.spark {
|
||||
var so = model.factory.spreader_order();
|
||||
so.Security = s.CloneToMutable();
|
||||
so.ExecutionStrategy = strategy.CloneToMutable();
|
||||
return new InlineUserProperty<SpreaderOrderDefinitionImmutable>(so, true);
|
||||
var soi = new SpreaderOrderDefinitionImmutable(so);
|
||||
return new InlineUserProperty<SpreaderOrderDefinitionImmutable>(soi, true);
|
||||
})), id, display_name) { }
|
||||
|
||||
public SpreaderOrderVM(
|
||||
|
||||
@@ -44,8 +44,8 @@ namespace mbts.spark {
|
||||
_dynamic_helper = dynamic_helper;
|
||||
var scheduler = ThreadPoolScheduler.Instance;
|
||||
var interval = TimeSpan.FromMilliseconds(100);
|
||||
on_orders = _service.subscribei_orders().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache();
|
||||
on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache();
|
||||
on_orders = _service.subscribei_orders().Naggle(interval, scheduler).PermissionFilter(admin_model.on_admin_data).Table().Cache();
|
||||
on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).PermissionFilter(admin_model.on_admin_data).Table().Cache();
|
||||
working_orders =
|
||||
on_orders.Scan(
|
||||
ImmutableDictionary<uint,OrderRecordImmutable>.Empty,
|
||||
@@ -55,7 +55,7 @@ namespace mbts.spark {
|
||||
on_order_tuples = order_record_tuple_source.subscribe_incremental_update();
|
||||
on_order_tuples_table = order_record_tuple_source.subscribe_incremental_table_update();
|
||||
on_order_securities = order_record_tuple_source.subscribe_order_securities();
|
||||
on_fills = _service.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache();
|
||||
on_fills = _service.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(admin_model.on_admin_data).Table().Cache();
|
||||
_activity = new OrderActivitySummarizer(bus, on_orders);
|
||||
on_fill_tuples = FillTupleSource.create_observable(bus.subscribe(SparkTags.SecuritiesTag), on_orders, on_fills);
|
||||
_security_orders_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
|
||||
@@ -93,10 +93,13 @@ namespace mbts.spark {
|
||||
#endregion
|
||||
|
||||
#region Input
|
||||
public async Task<uint?> send_order(OrderDefinitionImmutable order, double price, uint size) {
|
||||
var result = (await send_order_get_error(order, price, size));
|
||||
_activity.on_send_order(order, price, size, result.key, result.error);
|
||||
return result.key;
|
||||
public async Task<uint?> send_order(OrderDefinitionImmutable? order, double price, uint size) {
|
||||
if (order.HasValue) {
|
||||
var result = (await send_order_get_error(order.Value, price, size));
|
||||
_activity.on_send_order(order.Value, price, size, result.key, result.error);
|
||||
return result.key;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
async Task<(uint? key, string error)> send_order_get_error(OrderDefinitionImmutable order, double price, uint size) {
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.4.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
|
||||
<PackageReference Include="IronPython" Version="2.7.7" />
|
||||
<PackageReference Include="NAudio" Version="1.8.4" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
@@ -54,6 +54,7 @@
|
||||
<ItemGroup>
|
||||
<None Remove="Sounds\game_sound_correct.wav" />
|
||||
<None Remove="Sounds\whistle.wav" />
|
||||
<None Remove="view\Controls\TickUpDown\TickUpDownNullableUIntCommit.xaml" />
|
||||
<None Remove="view\Shell\Workspaces\WorkspacesView.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -71,11 +72,20 @@
|
||||
<Folder Include="view\Extensions\ItemsControl\ObservableItemsSource\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="view\Controls\TickUpDown\TickUpDownNullableUIntCommit.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="view\Shell\Workspaces\WorkspacesDialogView.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Modules\Trading\OrderBook\MiniOrderBook\MiniOrderBookView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</None>
|
||||
<None Update="Modules\Trading\OrderBook\MiniOrderBook\OrderBookView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</None>
|
||||
<None Update="view\Controls\DIYGrid\Settings\SimpleGridSettingsDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</None>
|
||||
|
||||
@@ -3,6 +3,7 @@ using bts.api.orderman;
|
||||
using bts.shell;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
@@ -211,6 +211,8 @@ namespace mbts.ui {
|
||||
public static BTSColor lean_order { get { return new BTSColor(255, 170, 210); } }
|
||||
public static BTSColor join_order { get { return new BTSColor(200, 171, 232); } }
|
||||
public static BTSColor cover_order { get { return new BTSColor(134, 232, 224); } }
|
||||
public static BTSColor oco_order { get { return new BTSColor(110, 94, 11); } }
|
||||
public static BTSColor pricer_order { get { return new BTSColor(170, 132, 150); } }
|
||||
public static BTSColor trigger_order { get { return new BTSColor(172, 255, 179); } }
|
||||
public static BTSColor off_order { get { return new BTSColor(0x8a, 0x00, 0x5f); } }//8a005f
|
||||
public static BTSColor top_order { get { return new BTSColor(0xdd, 0x7a, 0x27); } }//dd7a27
|
||||
|
||||
@@ -218,9 +218,7 @@ namespace mbts
|
||||
private Func<T, T, int> _comparer_func;
|
||||
public DelegateComparer(Func<T, T, int> comparer_func) { _comparer_func = comparer_func; }
|
||||
public int Compare(T x, T y) { return _comparer_func(x, y); }
|
||||
}
|
||||
|
||||
public static void ignore(this Task t) { }
|
||||
}
|
||||
|
||||
public static Google.Protobuf.WellKnownTypes.Timestamp ToGoogleTime(this DateTime self) {
|
||||
return Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(self);
|
||||
|
||||
@@ -22,12 +22,12 @@ namespace mbts.ui.settings
|
||||
readonly PersistenceService _persist;
|
||||
|
||||
[ImportingConstructor]
|
||||
public UISettingsFactory(UserService login, [Import("settings")] PersistenceService persist) {
|
||||
public UISettingsFactory(AuthService login, [Import("settings")] PersistenceService persist) {
|
||||
_persist = persist;
|
||||
_users = get_users(login);
|
||||
}
|
||||
|
||||
async Task<User[]> get_users(UserService login) {
|
||||
async Task<User[]> get_users(AuthService login) {
|
||||
var users = await login.get_users_async(null);
|
||||
if (users.success) return users.value.Users.ToArray();
|
||||
else return new User[0];
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace mbts.persist
|
||||
public IObservable<IEnumerable<PersistResult<T>>> subscribe<T>(string ns, string pattern) {
|
||||
var re = new Regex(pattern);
|
||||
return _persist
|
||||
.subscribe_ns(new NamespaceSubArgs() { Ns = uns(ns) })
|
||||
.subscribe_ns(uns(ns))
|
||||
.Select(update =>
|
||||
update.added
|
||||
.Where(item => re.IsMatch(item.Key))
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace bts.proto {
|
||||
static class DateTimeProviderExtensions {
|
||||
public static DateTime get_date_time(this DateTimeProvider dtp) {
|
||||
switch (dtp.mode) {
|
||||
case DateTimeProviderMode.CMEOpen: return MarketSchedule.get_cme_gen_open();
|
||||
case DateTimeProviderMode.CMEOpen: return MarketSchedule.get_start_of_trading_day();
|
||||
case DateTimeProviderMode.Today: return DateTime.Today;
|
||||
case DateTimeProviderMode.Explicit:
|
||||
default: return dtp.explicit_date.GetValueOrDefault();
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using FontFamily = System.Windows.Media.FontFamily;
|
||||
@@ -38,6 +39,7 @@ namespace mbts.ui.controls {
|
||||
CustomOrderDefinition.CustomOneofCase _parent_algo_type;
|
||||
(Point p1, Point p2, Point p3) _current_triangle_vertices;
|
||||
StreamGeometry _triangle_geometry;
|
||||
Popup _popup;
|
||||
|
||||
|
||||
static Badge() {
|
||||
@@ -181,6 +183,7 @@ namespace mbts.ui.controls {
|
||||
_has_multiple_orders_at_level = HasMultipleOrdersAtLevel;
|
||||
_is_hedge_order = IsHedgeOrder;
|
||||
_gcr.glyph_context = GlyphContext.create(FontFamily, FontWeight);
|
||||
_popup = Popup;
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty TextLinesProperty =
|
||||
@@ -264,6 +267,19 @@ namespace mbts.ui.controls {
|
||||
(b, v) => b._is_hedge_order = v,
|
||||
false);
|
||||
|
||||
public Popup Popup {
|
||||
get { return (Popup)GetValue(PopupProperty); }
|
||||
set { SetValue(PopupProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PopupProperty =
|
||||
WPFHelper.create_dp<Popup, Badge>(
|
||||
nameof(Popup),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._popup = v,
|
||||
null);
|
||||
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize) => finalSize;
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize) {
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
using System;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace mbts.spark {
|
||||
class CellProperty<T> : ISubject<T, (T oldval, T newval)> {
|
||||
readonly Func<T, string> _format;
|
||||
readonly IRaiseNotifyPropertyChanged _owner;
|
||||
readonly string _prop_name;
|
||||
string _text;
|
||||
|
||||
public CellProperty(T initial_value = default)
|
||||
: this(t => t.ToString(), initial_value) { }
|
||||
public CellProperty(IRaiseNotifyPropertyChanged owner, string prop_name, T initial_value = default)
|
||||
: this(owner, prop_name, t => t.ToString(), initial_value) { }
|
||||
|
||||
public CellProperty(Func<T, string> format, T initial_value = default) {
|
||||
public CellProperty(IRaiseNotifyPropertyChanged owner, string prop_name, Func<T, string> format, T initial_value = default) {
|
||||
_owner = owner;
|
||||
_prop_name = prop_name;
|
||||
_format = format;
|
||||
value = initial_value;
|
||||
}
|
||||
@@ -24,6 +29,7 @@ namespace mbts.spark {
|
||||
if (!value.Equals(this.value)) {
|
||||
OnNext(value);
|
||||
dirty = true;
|
||||
_owner.raise_notify_property_changed(_prop_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace mbts.ui.controls {
|
||||
Symbol = 78,
|
||||
Time = 112,
|
||||
TimeInForce = 47,
|
||||
TradeKey = 76,
|
||||
User = 53,
|
||||
Aggressor = 80,
|
||||
Volume = 74,
|
||||
@@ -71,7 +70,6 @@ namespace mbts.ui.controls {
|
||||
ImpliedSize = "Qty (I)",
|
||||
Symbol = "Symbol",
|
||||
TimeInForce = "TIF",
|
||||
TradeKey = "Trade Key",
|
||||
User = "User",
|
||||
Time = "Time",
|
||||
Aggressor = "Aggressor",
|
||||
@@ -97,7 +95,8 @@ namespace mbts.ui.controls {
|
||||
DayPnL = "Day PnL",
|
||||
TotalNetPos = "Total Net Pos",
|
||||
TotalPnL = "Total PnL",
|
||||
PnLPrice = "PnL Price";
|
||||
PnLPrice = "PnL Price",
|
||||
Cancel = "Cancel";
|
||||
}
|
||||
|
||||
public interface IKeyedColumn<TColKey> {
|
||||
|
||||
@@ -139,6 +139,7 @@ namespace mbts.ui.controls {
|
||||
public static readonly Thickness
|
||||
DefaultCellPadding = new Thickness(4, 2, 4, 2),
|
||||
AnimatableCellPadding = new Thickness(8, 2, 4, 2);
|
||||
public static readonly Visibility DefaultRowsHorizontalScrollBarVisibility = Visibility.Visible;
|
||||
|
||||
#region RoutedCommands
|
||||
public static readonly RoutedCommand SortCommand = new RoutedCommand(), DefaultRowCommand = new RoutedCommand();
|
||||
@@ -695,6 +696,14 @@ namespace mbts.ui.controls {
|
||||
|
||||
public static readonly DependencyProperty SelectionModeProperty = ListBox.SelectionModeProperty.AddOwner(typeof(DIYGrid));
|
||||
|
||||
public Visibility RowsHorizontalScrollBarVisibility {
|
||||
get { return (Visibility)GetValue(RowsHorizontalScrollBarVisibilityProperty); }
|
||||
set { SetValue(RowsHorizontalScrollBarVisibilityProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty RowsHorizontalScrollBarVisibilityProperty = DependencyProperty.Register(
|
||||
nameof(RowsHorizontalScrollBarVisibility), typeof(Visibility), typeof(DIYGrid), new PropertyMetadata(DefaultRowsHorizontalScrollBarVisibility));
|
||||
|
||||
#endregion
|
||||
|
||||
// Internal Methods
|
||||
|
||||
@@ -62,7 +62,8 @@
|
||||
<Setter Property="ColumnHeaderStyle">
|
||||
<Setter.Value>
|
||||
<Style TargetType="GridViewColumnHeader">
|
||||
<Setter Property="Background" Value="{mbts:BTSColor header}"/>
|
||||
<!-- Background - note that we're settings this property on the containing stackpanel in which the GridViewHeaderRowPresenter resides -->
|
||||
<Setter Property="Background" Value="{x:Null}"/>
|
||||
<Setter Property="Foreground" Value="{mbts:BTSColor header_text}"/>
|
||||
<Setter Property="BorderBrush" Value="{mbts:BTSColor header}"/>
|
||||
<Setter Property="BorderThickness" Value="0,1,1,0"/>
|
||||
@@ -301,7 +302,9 @@
|
||||
HorizontalScrollBarVisibility="Hidden"
|
||||
VerticalScrollBarVisibility="Hidden"
|
||||
x:Name="_header_scroll_viewer">
|
||||
<StackPanel>
|
||||
<!-- setting the bg color for this stackpanel is a work-around for the vertical white lines that appear on high-dpi displays between some columns -->
|
||||
<!-- in reality, there is some high dpi rounding thing going on, but that's a battle for another day -->
|
||||
<StackPanel Background="{mbts:BTSColor Name=header}">
|
||||
<!-- We don't need super headers over the fixed columns -->
|
||||
<StackPanel Orientation="Horizontal" x:Name="_super_headers" />
|
||||
<GridViewHeaderRowPresenter x:Name="_header_scroll" AllowsColumnReorder="False" ColumnHeaderContainerStyle="{TemplateBinding ColumnHeaderStyle}" UseLayoutRounding="True" />
|
||||
@@ -332,7 +335,7 @@
|
||||
</ControlTemplate>
|
||||
</bts:DIYGridRowsPresenter.Template>
|
||||
</bts:DIYGridRowsPresenter>
|
||||
<ScrollBar Grid.Row="3" Grid.Column="1" Orientation="Horizontal" LargeChange="10" x:Name="_scroll_horiz" />
|
||||
<ScrollBar Grid.Row="3" Grid.Column="1" Orientation="Horizontal" LargeChange="10" x:Name="_scroll_horiz" Visibility="{TemplateBinding RowsHorizontalScrollBarVisibility}" />
|
||||
|
||||
<!-- todo: All edit controls go in a canvas - edit api is called with an edit type. Hide all controls -->
|
||||
<!-- This stole hours of my life. The Text binding down there used to be 'value' and I was encountering -->
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using bts.proto;
|
||||
using bts.utils;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
@@ -18,7 +19,7 @@ namespace mbts.ui.settings {
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class GridColumnSettings {
|
||||
class GridColumnSettings : IEquatable<GridColumnSettings> {
|
||||
const bool
|
||||
_default_is_visible = true,
|
||||
_default_is_collapsed = false;
|
||||
@@ -103,5 +104,43 @@ namespace mbts.ui.settings {
|
||||
width ?? this.width,
|
||||
horizontal_alignment ?? this.horizontal_alignment,
|
||||
horizontal_offset ?? this.horizontal_offset);
|
||||
|
||||
public bool Equals(GridColumnSettings other) =>
|
||||
other != null &&
|
||||
id == other.id &&
|
||||
display_name == other.display_name &&
|
||||
is_visible == other.is_visible &&
|
||||
is_collapsed == other.is_collapsed &&
|
||||
font_weight_override.Equals(other.font_weight_override) &&
|
||||
font_color_override.Equals(other.font_color_override) &&
|
||||
width == other.width &&
|
||||
horizontal_alignment == other.horizontal_alignment &&
|
||||
horizontal_offset == other.horizontal_offset &&
|
||||
display_name == other.display_name;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as GridColumnSettings);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
id.GetHashCode(),
|
||||
display_name.GetHashCode(),
|
||||
is_visible.GetHashCode(),
|
||||
is_collapsed.GetHashCode(),
|
||||
font_weight_override.GetHashCode(),
|
||||
font_color_override.GetHashCode(),
|
||||
width.GetHashCode(),
|
||||
horizontal_alignment.GetHashCode(),
|
||||
horizontal_offset.GetHashCode(),
|
||||
display_name.GetHashCode()
|
||||
);
|
||||
|
||||
public override string ToString() {
|
||||
return $"id: {id} display_name: {display_name}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -126,13 +126,15 @@ namespace mbts.ui {
|
||||
col.set_fallback_font_params(new FontFamily(grid.font_family), grid.font_size, grid.font_color.rgb_color(), grid.font_weight);
|
||||
_predicate_animations_data = value.predicate_animations;
|
||||
_animation_defs.clear();
|
||||
foreach (var pa in _predicate_animations_data) {
|
||||
var predicate = pa.predicate_builder.create_predicate(_arg);
|
||||
foreach (var s in pa.sound_notifications) {
|
||||
_notification_subs.Add(_notification_service.link(predicate, s));
|
||||
}
|
||||
if (pa.predicate_builder is INotificationPredicateBuilder<TRowData, TPredicateBuilderArg> tpb && tpb.is_toast_enabled) {
|
||||
_notification_subs.Add(_notification_service.link(tpb.create_notification_predicate(_arg)));
|
||||
if (_notification_service != null && _arg != null) {
|
||||
foreach (var pa in _predicate_animations_data) {
|
||||
var predicate = pa.predicate_builder.create_predicate(_arg);
|
||||
foreach (var s in pa.sound_notifications) {
|
||||
_notification_subs.Add(_notification_service.link(predicate, s));
|
||||
}
|
||||
if (pa.predicate_builder is INotificationPredicateBuilder<TRowData, TPredicateBuilderArg> tpb && tpb.is_toast_enabled) {
|
||||
_notification_subs.Add(_notification_service.link(tpb.create_notification_predicate(_arg)));
|
||||
}
|
||||
}
|
||||
}
|
||||
// ** in the unfortunate event that we botch backwards compatability, and the columns don't bear a 1:1 relationship with the column-settings
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace mbts.spark.filters {
|
||||
TimeFilterMode _mode;
|
||||
TimeTextTuple _begin, _end;
|
||||
DateTime? _begin_dt, _end_dt;
|
||||
readonly DateTime _cme_open = MarketSchedule.get_cme_gen_open();
|
||||
readonly DateTime _cme_open = MarketSchedule.get_start_of_trading_day();
|
||||
|
||||
public TimeFilterVM(Func<TRow, DateTime> get_row_val, string prop_name) : base(get_row_val, prop_name) =>
|
||||
_set_default_values();
|
||||
|
||||
@@ -34,9 +34,11 @@ namespace bts.view.controls {
|
||||
private void any_button_click(object sender, RoutedEventArgs e) {
|
||||
Close();
|
||||
}
|
||||
internal static void Show(ErrorReportService srv, ErrorReport report) {
|
||||
var window = new SupportRequestWindow(new SupportRequestVM(srv, report));
|
||||
window.Show();
|
||||
internal static void Show(ErrorReportService srv, ErrorReportImmutable? report) {
|
||||
if (report.HasValue) {
|
||||
var window = new SupportRequestWindow(new SupportRequestVM(srv, report.Value));
|
||||
window.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,51 +166,6 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.TimeBoxOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.TickOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="1" VerticalAlignment="Center">Cross Thresh</TextBlock>
|
||||
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding cross_threshold}"/>
|
||||
|
||||
<TextBlock Grid.Row="3" VerticalAlignment="Center">Cross Ticks</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="3" Grid.Column="2" Value="{Binding cross_ticks}"/>
|
||||
<TextBlock Grid.Row="5" VerticalAlignment="Center">Max Chase Ticks</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="5" Grid.Column="2" Value="{Binding max_chase_ticks}"/>
|
||||
<CheckBox Grid.Row="7" Grid.Column="2" VerticalAlignment="Center" Margin="0,0,0,0" IsChecked="{Binding detach_on_toward}">Detach on Toward</CheckBox>
|
||||
<TextBlock Grid.Row="9" VerticalAlignment="Center">Initial Tick Offset</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="9" Grid.Column="2" Value="{Binding tick_offset_on_deploy}"/>
|
||||
<TextBlock Grid.Row="11">Child</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="11" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding child_order_types}"
|
||||
SelectedItem="{Binding child_order}"
|
||||
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.TickOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.LeanOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -379,16 +334,16 @@
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="1"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center" >Hedge Ticks</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the number of ticks to offset the hedge." />
|
||||
<TextBlock VerticalAlignment="Center" >Profit Ticks</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the number of ticks in your favor to offset the profit/hedge order." />
|
||||
</StackPanel>
|
||||
<c:TickUpDownInt Grid.Row="1" Grid.Column="2" Value="{Binding hedge_ticks}"/>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="3"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Hedge</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of the hedge order to send for each fill of Child Order." />
|
||||
<TextBlock VerticalAlignment="Center">Profit Order</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of the profit/hedge order to send for each fill of Child Order." />
|
||||
</StackPanel>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="3" Grid.Column="2"
|
||||
@@ -415,6 +370,68 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.CoverOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.OCOOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="1" >
|
||||
<TextBlock VerticalAlignment="Center" >Stop Ticks</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the number of ticks away from the main order to start the 'stop' order." />
|
||||
</StackPanel>
|
||||
<c:TickUpDownInt Grid.Row="1" Grid.Column="2" Value="{Binding other_ticks}"/>
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="3" >
|
||||
<TextBlock VerticalAlignment="Center" >Stop Side</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="The Side of the 'stop' order" />
|
||||
</StackPanel>
|
||||
<ComboBox Grid.Row="3" Grid.Column="2" ItemsSource="{Binding other_side_types}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding other_side}"/>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="5"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Stop Order</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of the 'stop' order to send." />
|
||||
</StackPanel>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="5" Grid.Column="2"
|
||||
IsOpen="{Binding other_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding other_order_types}"
|
||||
SelectedItem="{Binding other_order}"
|
||||
HasEditableProperties="{Binding other_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="7"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Child</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of order to be placed when the algo is triggered." />
|
||||
</StackPanel>
|
||||
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="7" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding child_order_types}"
|
||||
SelectedItem="{Binding child_order}"
|
||||
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.OCOOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.OffsetOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -773,9 +790,11 @@
|
||||
</usp:DynamicOrderParamTemplateSelector>
|
||||
</ItemsControl.ItemTemplateSelector>
|
||||
</ItemsControl>
|
||||
<TextBlock Grid.Row="3">Child</TextBlock>
|
||||
<TextBlock Grid.Row="3"
|
||||
Visibility="{Binding has_child, Converter={StaticResource BooleanToVisibilityConverter}}" >Child</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="3" Grid.Column="2"
|
||||
Grid.Row="3" Grid.Column="2"
|
||||
Visibility="{Binding has_child, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding child_order_types}"
|
||||
SelectedItem="{Binding child_order}"
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<UserControl x:Class="mbts.ui.controls.PriceEntryCommitControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
d:DesignHeight="100" d:DesignWidth="800" x:Name="_me_price">
|
||||
<UserControl.Resources>
|
||||
<cv:NullableDoubleConverter x:Key="TickDefaultPriceConverter" DefaultValue="0"/>
|
||||
<uspcv:TickDisplayConverter x:Key="TickDisplayConverter" x:Shared="False"/>
|
||||
<ControlTemplate x:Key="ReadTemplate">
|
||||
<Grid HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.ColumnSpan="2">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="SourceValue" ElementName="_me_price"/>
|
||||
<Binding Path="SourceContractSpec" ElementName="_me_price"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
<ControlTemplate x:Key="EditTemplate">
|
||||
<Grid HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<usp:PriceEntryControl x:Name="price_ticker"
|
||||
Margin="0,0,2,0"
|
||||
TickDefaultValue="{Binding SourceValue, ElementName=_me_price, Converter={StaticResource TickDefaultPriceConverter}, FallbackValue={u:Double 0}}"
|
||||
ContractSpec="{Binding SourceContractSpec, ElementName=_me_price}"
|
||||
Value="{Binding SourceValue, ElementName=_me_price}" />
|
||||
<Button Grid.Column="2" x:Name="commit_button" CommandParameter="{Binding Value, ElementName=price_ticker}" Command="{Binding OnCommit, ElementName=_me_price}" Content="✔" >
|
||||
<Button.Style>
|
||||
<Style TargetType="Button">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="True">
|
||||
<Setter Property="Background" Value="Green" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</UserControl.Resources>
|
||||
|
||||
<UserControl.Style>
|
||||
<Style TargetType="UserControl">
|
||||
<Setter Property="Template" Value="{StaticResource ReadTemplate}"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding is_selected}" Value="True">
|
||||
<Setter Property="Template" Value="{StaticResource EditTemplate}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding is_price_dirty}" Value="True">
|
||||
<Setter Property="Template" Value="{StaticResource EditTemplate}"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</UserControl.Style>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,73 @@
|
||||
using bts.api;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
/// <summary>
|
||||
/// Interaction logic for PriceEntryCommitControl.xaml
|
||||
/// </summary>
|
||||
public partial class PriceEntryCommitControl : UserControl {
|
||||
|
||||
|
||||
public static readonly DependencyProperty SourceValueProperty =
|
||||
DependencyProperty.Register("SourceValue",
|
||||
typeof(double),
|
||||
typeof(PriceEntryCommitControl),
|
||||
new FrameworkPropertyMetadata {
|
||||
BindsTwoWayByDefault = true,
|
||||
PropertyChangedCallback = source_value_changed
|
||||
}
|
||||
);
|
||||
|
||||
public double SourceValue {
|
||||
get => (double)GetValue(SourceValueProperty);
|
||||
set => SetValue(SourceValueProperty, value);
|
||||
}
|
||||
private static void source_value_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
if (d is PriceEntryCommitControl _instance_of_me) {
|
||||
_instance_of_me.Dispatcher.BeginInvoke(
|
||||
(Action)(() => _instance_of_me.OnCommit?.TryRaiseCanExecuteChanged())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SourceContractSpecProperty =
|
||||
DependencyProperty.Register("SourceContractSpec", typeof(ContractSpecificationImmutable?), typeof(PriceEntryCommitControl));
|
||||
public ContractSpecificationImmutable? SourceContractSpec {
|
||||
get => (ContractSpecificationImmutable?)GetValue(SourceContractSpecProperty);
|
||||
set => SetValue(SourceContractSpecProperty, value);
|
||||
}
|
||||
|
||||
// public static readonly DependencyProperty PriceTextProperty =
|
||||
//DependencyProperty.Register("PricText", typeof(string), typeof(PriceEntryCommitControl));
|
||||
// public string PriceText {
|
||||
// get => (string)GetValue(PriceTextProperty);
|
||||
// set => SetValue(PriceTextProperty, value);
|
||||
// }
|
||||
|
||||
public static readonly DependencyProperty OnCommitProperty =
|
||||
DependencyProperty.Register("OnCommit", typeof(ICommand), typeof(PriceEntryCommitControl));
|
||||
public ICommand OnCommit {
|
||||
get => (ICommand)GetValue(OnCommitProperty);
|
||||
set => SetValue(OnCommitProperty, value);
|
||||
}
|
||||
|
||||
public PriceEntryCommitControl() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<UserControl x:Class="mbts.ui.controls.TickUpDownNullableUIntCommit"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
d:DesignHeight="100" d:DesignWidth="800" x:Name="_me">
|
||||
<UserControl.Resources>
|
||||
<ControlTemplate x:Key="ReadTemplate">
|
||||
<Grid HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.ColumnSpan="2" Text="{Binding SourceValue, ElementName=_me}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
<ControlTemplate x:Key="EditTemplate">
|
||||
<Grid HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<c:TickUpDownNullableUInt x:Name="qty_ticker" Margin="0,0,2,0" Value="{Binding SourceValue, ElementName=_me}" />
|
||||
<Button Grid.Column="2" x:Name="commit_button" CommandParameter="{Binding Value, ElementName=qty_ticker}" Command="{Binding OnCommit, ElementName=_me}" Content="✔" >
|
||||
<Button.Style>
|
||||
<Style TargetType="Button">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="True">
|
||||
<Setter Property="Background" Value="Green" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</UserControl.Resources>
|
||||
|
||||
<UserControl.Style>
|
||||
<Style TargetType="UserControl">
|
||||
<Setter Property="Template" Value="{StaticResource ReadTemplate}"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding is_selected}" Value="True">
|
||||
<Setter Property="Template" Value="{StaticResource EditTemplate}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding is_qty_dirty}" Value="True">
|
||||
<Setter Property="Template" Value="{StaticResource EditTemplate}"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</UserControl.Style>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
/// <summary>
|
||||
/// Interaction logic for TickUpDownNullableUIntCommit.xaml
|
||||
/// </summary>
|
||||
public partial class TickUpDownNullableUIntCommit : UserControl {
|
||||
|
||||
|
||||
public static readonly DependencyProperty SourceValueProperty =
|
||||
DependencyProperty.Register("SourceValue",
|
||||
typeof(uint?),
|
||||
typeof(TickUpDownNullableUIntCommit),
|
||||
new FrameworkPropertyMetadata {
|
||||
BindsTwoWayByDefault = true,
|
||||
PropertyChangedCallback = source_value_changed
|
||||
}
|
||||
);
|
||||
|
||||
private static void source_value_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
if (d is TickUpDownNullableUIntCommit _instance_of_me) {
|
||||
_instance_of_me.Dispatcher.BeginInvoke(
|
||||
(Action)(() => _instance_of_me.OnCommit?.TryRaiseCanExecuteChanged())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public uint? SourceValue {
|
||||
get => GetValue(SourceValueProperty) as uint?;
|
||||
set => SetValue(SourceValueProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty OnCommitProperty =
|
||||
DependencyProperty.Register("OnCommit", typeof(ICommand), typeof(TickUpDownNullableUIntCommit));
|
||||
public ICommand OnCommit {
|
||||
get => (ICommand)GetValue(OnCommitProperty);
|
||||
set => SetValue(OnCommitProperty, value);
|
||||
}
|
||||
|
||||
public TickUpDownNullableUIntCommit() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace mbts.ui.converters {
|
||||
class ResponsiveTextConverter : IValueConverter {
|
||||
|
||||
public string ShortText { get; set; }
|
||||
public string LongText { get; set; }
|
||||
public double Threshold { get; set; }
|
||||
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
if (value is double _input) {
|
||||
if (_input < Threshold) {
|
||||
return ShortText;
|
||||
} else {
|
||||
return LongText;
|
||||
}
|
||||
}
|
||||
return LongText;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using mbts.config;
|
||||
using mbts.ui.native;
|
||||
using mbts.utils;
|
||||
using bts.co2;
|
||||
using bts.api.user;
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.persistence;
|
||||
using bts;
|
||||
using bts.api.user;
|
||||
using mbts.config;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace mbts.ui.shell
|
||||
{
|
||||
namespace mbts.ui.shell {
|
||||
// The LoginContainer participates in the startup Import/Export dependency system
|
||||
// Since the login form should not depend on *anything* it will be shown before this is created
|
||||
// Once this is created it access the global LoginModel and retrieves the username
|
||||
internal class LoginContainer
|
||||
{
|
||||
[ImportingConstructor]
|
||||
public LoginContainer(UserService user, [Import("settings")] PersistenceService persist) {
|
||||
public LoginContainer(ServiceContext context) {
|
||||
var model = LoginModel.instance;
|
||||
model.provide_services(user, persist);
|
||||
model.provide_services(context);
|
||||
// await_login pumps the dispatcher but doesn't allow control to return to MEF until the username and workspace are available
|
||||
model.await_login();
|
||||
var u = model.get_user();
|
||||
UserName = u.name;
|
||||
UserId = u.id;
|
||||
Token = u.token;
|
||||
(UserName,UserId,Token) = model.get_user();
|
||||
Workspace = model.get_workspace();
|
||||
model.Dispose();
|
||||
}
|
||||
@@ -44,21 +34,19 @@ namespace mbts.ui.shell
|
||||
[Export("userid")]
|
||||
public uint UserId { get; private set; }
|
||||
[Export]
|
||||
public AuthToken Token { get; private set; }
|
||||
public AuthTokenImmutable Token { get; private set; }
|
||||
|
||||
[Export("workspace")]
|
||||
public string Workspace { get; private set; }
|
||||
}
|
||||
|
||||
public struct User {
|
||||
public User(string name, uint id, AuthToken token) {
|
||||
public User(string name, uint id) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
this.token = token;
|
||||
}
|
||||
public readonly string name;
|
||||
public readonly UInt32 id;
|
||||
public readonly AuthToken token;
|
||||
}
|
||||
|
||||
public class LoginModel : IDisposable
|
||||
@@ -66,11 +54,10 @@ namespace mbts.ui.shell
|
||||
// Singleton to help bootstrap the system
|
||||
private static LoginModel _instance = new LoginModel();
|
||||
public static LoginModel instance { get { return _instance; } }
|
||||
private User _user;
|
||||
private string _workspace;
|
||||
private PreloginWorkspaceEnumerator _enum_workspace;
|
||||
|
||||
UserService _logon;
|
||||
|
||||
ServiceContext _ctx;
|
||||
DispatcherFrame _frame = new DispatcherFrame();
|
||||
|
||||
private LoginModel() {
|
||||
@@ -82,13 +69,14 @@ namespace mbts.ui.shell
|
||||
public string ErrorMessage { get; private set; }
|
||||
public bool HasFatalError { get; set; }
|
||||
public User[] Users { get; private set; }
|
||||
|
||||
|
||||
|
||||
|
||||
// The final username
|
||||
public User get_user() {
|
||||
Log.assert_d(!string.IsNullOrEmpty(_user.name));
|
||||
return _user;
|
||||
public (string, uint, AuthTokenImmutable) get_user() {
|
||||
Log.assert_d(!string.IsNullOrEmpty(_ctx.user));
|
||||
return (_ctx.user, _ctx.user_id, _ctx.token.Value);
|
||||
}
|
||||
|
||||
public string get_workspace() {
|
||||
return _workspace ?? "";
|
||||
}
|
||||
@@ -112,13 +100,13 @@ namespace mbts.ui.shell
|
||||
}
|
||||
|
||||
// Wait for the hub to create the login doodad
|
||||
(var users, var persist) = await _hub_src.Task;
|
||||
_logon = users;
|
||||
_ctx = await _hub_src.Task;
|
||||
// Populate the UI
|
||||
_enum_workspace = new PreloginWorkspaceEnumerator(persist);
|
||||
var response = await _logon.get_users_async(new GetUsersRequest());
|
||||
_enum_workspace = new PreloginWorkspaceEnumerator(_ctx.locate<PersistenceService>("settings"));
|
||||
var auth = _ctx.locate<AuthService>();
|
||||
var response = await auth.get_users_async(new GetUsersRequest());
|
||||
if (response.success) {
|
||||
Users = response.value.Users.Select(x => new User(x.Login,x.Id, null)).OrderBy(u => u.name).ToArray();
|
||||
Users = response.value.Users.Select(x => new User(x.Login,x.Id)).OrderBy(u => u.name).ToArray();
|
||||
}
|
||||
else {
|
||||
ErrorMessage = "Logon: " + response.error_message;
|
||||
@@ -129,49 +117,37 @@ namespace mbts.ui.shell
|
||||
|
||||
#endregion
|
||||
|
||||
public async Task<(bool success,string info)> do_login(string username, string workspace, string password) {
|
||||
public async Task<LoginResult> do_login(string username, string workspace, string password) {
|
||||
if (Users.Select(u => u.name).Contains(username)) {
|
||||
var response = await _logon.authenticate_login_async(new AuthenticateLoginRequest() {
|
||||
Login = username,
|
||||
Password = password ??""
|
||||
});
|
||||
if (response.success) {
|
||||
if (response.value.IsValid) {
|
||||
_workspace = workspace;
|
||||
set_authenticated_user(new User(username, response.value.UserId, response.value.Token));
|
||||
return (true, "");
|
||||
}
|
||||
else {
|
||||
return (false, response.value.Reason);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return (false, response.error_message);
|
||||
var result = await _ctx.login(username, password);
|
||||
if (result.success) {
|
||||
_workspace = workspace;
|
||||
on_user_authenticated(_ctx.user, _ctx.token);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return (false, "Invalid username");
|
||||
return new LoginResult("Invalid username");
|
||||
}
|
||||
|
||||
private void set_authenticated_user(User user) {
|
||||
Log.assert(!string.IsNullOrEmpty(user.name));
|
||||
Log.assert(Users.Select(u => u.name).Contains(user.name));
|
||||
_user = user;
|
||||
Environment.SetEnvironmentVariable("BTS_USER", user.name);
|
||||
private void on_user_authenticated(string username, AuthTokenImmutable? token) {
|
||||
Log.assert(!string.IsNullOrEmpty(username));
|
||||
Log.assert(Users.Select(u => u.name).Contains(username));
|
||||
Environment.SetEnvironmentVariable("BTS_USER", username);
|
||||
_frame.Continue = false;
|
||||
}
|
||||
|
||||
public void await_login() {
|
||||
if (string.IsNullOrEmpty(_user.name)) {
|
||||
if (!_ctx.token.HasValue) {
|
||||
Dispatcher.PushFrame(_frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region provide hub
|
||||
public void provide_services(UserService u, PersistenceService p) {
|
||||
_hub_src.TrySetResult((u,p));
|
||||
internal void provide_services(ServiceContext ctx) {
|
||||
_hub_src.TrySetResult(ctx);
|
||||
}
|
||||
private TaskCompletionSource<(UserService, PersistenceService)> _hub_src = new TaskCompletionSource<(UserService, PersistenceService)>();
|
||||
private TaskCompletionSource<ServiceContext> _hub_src = new TaskCompletionSource<ServiceContext>();
|
||||
#endregion
|
||||
|
||||
#region Dispose
|
||||
|
||||
@@ -138,14 +138,15 @@ namespace mbts.ui.shell
|
||||
if (selected_workspace == "New workspace...") {
|
||||
selected_workspace = null;
|
||||
}
|
||||
(bool success, string message) = await _model.do_login(Name, selected_workspace, Password);
|
||||
if (success) { VisualState = "Loading"; }
|
||||
if (!success && !string.IsNullOrEmpty(message)) {
|
||||
WarningMessage = message;
|
||||
var result = await _model.do_login(Name, selected_workspace, Password);
|
||||
if (result.success) { VisualState = "Loading"; }
|
||||
if (!result.success && !string.IsNullOrEmpty(result.reason)) {
|
||||
WarningMessage = result.reason;
|
||||
VisualState = "BadPassword";
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(message)) {
|
||||
BTSMessageBox.Show(message, "Login Message", BTSMessageBoxButtons.OK, MessageBoxImage.Information);
|
||||
else if (!string.IsNullOrEmpty(result.reason)) {
|
||||
// Login was successful but we have a message for the user
|
||||
BTSMessageBox.Show(result.reason, "Login Message", BTSMessageBoxButtons.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
private void OnCancel(object unused) {
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace bts.settings {
|
||||
public class SettingsManagerTool {
|
||||
public static void run() {
|
||||
var sc = new ServiceContext();
|
||||
var users = sc.locate<UserService>();
|
||||
var users = sc.locate<AuthService>();
|
||||
var persist = sc.locate<PersistenceService>("settings");
|
||||
var user_list = users.get_users(new GetUsersRequest());
|
||||
var user_names = user_list.Users.Select(u => u.Login);
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace mbts.spark {
|
||||
return intpart + floatpart;
|
||||
}
|
||||
|
||||
public static RegionalTickFactors get_regional_ticks(ContractSpecificationImmutable spec) =>
|
||||
public static RegionalTickFactors get_regional_ticks(ContractSpecificationImmutable? spec) =>
|
||||
TickStore.get(spec);
|
||||
|
||||
public static RegionalTickFactors get_regional_ticks(SecurityImmutable security) =>
|
||||
@@ -177,8 +177,14 @@ namespace mbts.spark {
|
||||
public static RegionalTickFactors get(ContractSpecification spec) =>
|
||||
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
|
||||
|
||||
public static RegionalTickFactors get(ContractSpecificationImmutable spec) =>
|
||||
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
|
||||
public static RegionalTickFactors get(ContractSpecificationImmutable? spec) {
|
||||
if (spec.HasValue) {
|
||||
return _exchange_ticks.GetOrAdd(new SpecKey(spec.Value), _create_rtf);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static RegionalTickFactors get(SecurityImmutable security) =>
|
||||
security.HasContractSpec ? get(security.ContractSpec) : default_rtf;
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace mbts.spark {
|
||||
{
|
||||
var lgh = source as LadderGridHost;
|
||||
double width_adjustment = (double)(uint) e.NewValue - (double)(uint) e.OldValue;
|
||||
lgh._adjust_column_width(lgh.columns.First(c => c.column_type == ColumnType.Order), width_adjustment);
|
||||
lgh._adjust_column_width(lgh.columns.FirstOrDefault(c => c.column_type == ColumnType.Order), width_adjustment);
|
||||
}
|
||||
|
||||
public uint bid_column_width
|
||||
@@ -126,7 +126,7 @@ namespace mbts.spark {
|
||||
{
|
||||
var lgh = source as LadderGridHost;
|
||||
double width_adjustment = (double)(uint) e.NewValue - (double)(uint) e.OldValue;
|
||||
lgh._adjust_column_width(lgh.columns.First(c => c.column_type == ColumnType.Bid), width_adjustment);
|
||||
lgh._adjust_column_width(lgh.columns.FirstOrDefault(c => c.column_type == ColumnType.Bid), width_adjustment);
|
||||
}
|
||||
|
||||
public uint price_column_width
|
||||
@@ -142,7 +142,7 @@ namespace mbts.spark {
|
||||
{
|
||||
var lgh = source as LadderGridHost;
|
||||
double width_adjustment = (double)(uint) e.NewValue - (double)(uint) e.OldValue;
|
||||
lgh._adjust_column_width(lgh.columns.First(c => c.column_type == ColumnType.Price), width_adjustment);
|
||||
lgh._adjust_column_width(lgh.columns.FirstOrDefault(c => c.column_type == ColumnType.Price), width_adjustment);
|
||||
}
|
||||
|
||||
|
||||
@@ -159,14 +159,14 @@ namespace mbts.spark {
|
||||
{
|
||||
var lgh = source as LadderGridHost;
|
||||
double width_adjustment = (double)(uint) e.NewValue - (double)(uint) e.OldValue;
|
||||
lgh._adjust_column_width(lgh.columns.First(c => c.column_type == ColumnType.Ask), width_adjustment);
|
||||
lgh._adjust_column_width(lgh.columns.FirstOrDefault(c => c.column_type == ColumnType.Ask), width_adjustment);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty VolumeColumnWidthProperty =
|
||||
WPFHelper.create_dp<uint, LadderGridHost>(
|
||||
nameof(VolumeColumnWidth),
|
||||
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, oldval, newval) => o._adjust_column_width(o.columns.First(c => c.column_type == ColumnType.Volume), (double)newval - (double)oldval),
|
||||
(o, oldval, newval) => o._adjust_column_width(o.columns.FirstOrDefault(c => c.column_type == ColumnType.Volume), (double)newval - (double)oldval),
|
||||
50u);
|
||||
|
||||
public uint VolumeColumnWidth {
|
||||
@@ -178,7 +178,7 @@ namespace mbts.spark {
|
||||
WPFHelper.create_dp<uint, LadderGridHost>(
|
||||
nameof(LastColumnWidth),
|
||||
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, oldval, newval) => o._adjust_column_width(o.columns.First(c => c.column_type == ColumnType.Last), (double)newval - (double)oldval),
|
||||
(o, oldval, newval) => o._adjust_column_width(o.columns.FirstOrDefault(c => c.column_type == ColumnType.Last), (double)newval - (double)oldval),
|
||||
50u);
|
||||
|
||||
public uint LastColumnWidth {
|
||||
@@ -327,6 +327,8 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
void _adjust_column_width(Column column, double adjustment) {
|
||||
if (column == null)
|
||||
return;
|
||||
int new_col_width;
|
||||
switch (column.column_type) {
|
||||
case ColumnType.Order: new_col_width = (int)orders_column_width; break;
|
||||
@@ -662,22 +664,10 @@ namespace mbts.spark {
|
||||
} else {
|
||||
init = false;
|
||||
prev_row_height = _settings.row_height;
|
||||
// This is a general issue with default-settings:
|
||||
// "Not every property in the settings object should be shared across windows."
|
||||
// Column widths are one such property (they are changed from the actual Ladder window, not the settings dialog).
|
||||
// Below is a hack which prevents overwriting current column widths; until we come up with a better solution this will suffice.
|
||||
// TODO: fix this by moving such properties from the settings proto into a separate proto (or come up with a better workaround/solution)
|
||||
copy_col_width(v => v.orders_column);
|
||||
copy_col_width(v => v.bid_column);
|
||||
copy_col_width(v => v.price_column);
|
||||
copy_col_width(v => v.ask_column);
|
||||
copy_col_width(v => v.volume_column.general);
|
||||
copy_col_width(v => v.last_column);
|
||||
void copy_col_width(Func<LadderSettings, LadderColumnSettings> select_col) =>
|
||||
select_col(s).width = select_col(_settings).width;
|
||||
}
|
||||
//
|
||||
_settings = s;
|
||||
/// The viewmodel will always have the most up-to-date settings, so ignore <see cref="s"/> and just get it from <see cref="_vm"/>.
|
||||
/// This is a workaround for the global settings-defaults issue: when defaults are updated, some properties are inadvertantly overwritten, such as column-widths, etc.
|
||||
_settings = _vm.get_save_state();
|
||||
_bid_vis_tup.glyph_context = glyph_context(s.bid_column);
|
||||
_ask_vis_tup.glyph_context = glyph_context(s.ask_column);
|
||||
_price_vis_tup.glyph_context = glyph_context(s.price_column);
|
||||
@@ -698,18 +688,19 @@ namespace mbts.spark {
|
||||
// Renders the grid lines and column backgrounds
|
||||
_visualHost.AddVisualChild(_gridvis);
|
||||
// Orders column
|
||||
orders_column_width = s.orders_column.width;
|
||||
SetCurrentValue(orders_column_widthProperty, s.orders_column.width);
|
||||
_ordershost.update_settings(_price_list, _price_comparer, s.row_height, s.orders_column);
|
||||
foreach (var col in columns) {
|
||||
if (col.visual_tup != null) {
|
||||
if (col.settings.show) {
|
||||
_visualHost.AddVisualChild(col.visual_tup.visual);
|
||||
switch (col.column_type) {
|
||||
case ColumnType.Bid: bid_column_width = col.settings.width; break;
|
||||
case ColumnType.Price: price_column_width = col.settings.width; break;
|
||||
case ColumnType.Ask: ask_column_width = col.settings.width; break;
|
||||
case ColumnType.Bid: SetCurrentValue(bid_column_widthProperty, col.settings.width); break;
|
||||
case ColumnType.Price: SetCurrentValue(price_column_widthProperty, col.settings.width); break;
|
||||
case ColumnType.Ask: SetCurrentValue(ask_column_widthProperty, col.settings.width); break;
|
||||
case ColumnType.Last: SetCurrentValue(LastColumnWidthProperty, col.settings.width); break;
|
||||
case ColumnType.Volume:
|
||||
VolumeColumnWidth = col.settings.width;
|
||||
SetCurrentValue(VolumeColumnWidthProperty, col.settings.width);
|
||||
_volume_container.mark_dirty();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace mbts.spark {
|
||||
readonly Property<Brush> _foreground, _background, _hoverbackground, _border, _parent_algo_type_brush;
|
||||
readonly Property<bool> _is_exchange_order, _has_multiple_orders_at_same_level, _is_hedge_order;
|
||||
readonly Property<CustomOrderDefinition.CustomOneofCase> _parent_algo_type;
|
||||
readonly Property<string> _summary, _description;
|
||||
readonly Property<string> _summary;
|
||||
IObservable<LadderSettings.BadgeStyle> _bs;
|
||||
LadderSettings.BadgeStyle _current_badge_style;
|
||||
int _n_active_price_change_ops = 0;
|
||||
@@ -63,7 +63,6 @@ namespace mbts.spark {
|
||||
_hoverbackground = backing(nameof(hoverbackground), _color_bg_brush);
|
||||
_border = backing(nameof(border), _color_bg_brush);
|
||||
_summary = backing(nameof(summary), string.Empty);
|
||||
_description = backing(nameof(description), string.Empty);
|
||||
_is_exchange_order = backing(nameof(is_exchange_order), false);
|
||||
_has_multiple_orders_at_same_level = backing(nameof(has_multiple_orders_at_same_level), false);
|
||||
_is_hedge_order = backing(nameof(is_hedge_order), false);
|
||||
@@ -75,6 +74,7 @@ namespace mbts.spark {
|
||||
Brush get_foreground(LadderSettings s) => s.orders_column.foreground.rgb_brush();
|
||||
//
|
||||
add_order(o);
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
_bs.Subscribe(badge => {
|
||||
_current_badge_style = badge;
|
||||
@@ -122,7 +122,6 @@ namespace mbts.spark {
|
||||
|
||||
if (_orders.Any()) {
|
||||
summary = _orders.Values.Select(order => order.short_description(_parent.security.ContractSpec)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
|
||||
description = _orders.Values.Select(order => order.full_description(_parent.security)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
|
||||
}
|
||||
_set_badge_text();
|
||||
|
||||
@@ -185,11 +184,10 @@ namespace mbts.spark {
|
||||
public Brush hoverbackground { get => _hoverbackground.Value; private set => _hoverbackground.Value = value; }
|
||||
public Brush border { get => _border.Value; private set => _border.Value = value; }
|
||||
public string summary { get => _summary.Value; private set => _summary.Value = value; }
|
||||
public string description { get => _description.Value; private set => _description.Value = value; }
|
||||
public bool is_exchange_order { get => _is_exchange_order.Value; private set => _is_exchange_order.Value = value; }
|
||||
public bool has_multiple_orders_at_same_level { get => _has_multiple_orders_at_same_level.Value; private set => _has_multiple_orders_at_same_level.Value = value; }
|
||||
public bool is_hedge_order { get => _is_hedge_order.Value; private set => _is_hedge_order.Value = value; }
|
||||
public Brush parent_algo_type_brush { get => _parent_algo_type_brush.Value; private set => _parent_algo_type_brush.Value = value; }
|
||||
public Brush parent_algo_type_brush { get => _parent_algo_type_brush.Value; private set => _parent_algo_type_brush.Value = value; }
|
||||
// the setter is public so that the LadderVM can set this field
|
||||
public CustomOrderDefinition.CustomOneofCase parent_algo_type { get => _parent_algo_type.Value; set => _parent_algo_type.Value = value; }
|
||||
|
||||
@@ -224,6 +222,56 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public OrderBookVM create_orderbook_vm() {
|
||||
// We are returning a new OrderBookVM each time b/c after
|
||||
// successive launches of the mini-ob from the same badge, we get an exception
|
||||
// System.InvalidOperationException: Sharing GridViewColumn among multiple GridViewColumnCollections or adding the same GridViewColumn into one GridViewColumnCollection more than once is not allowed.
|
||||
// I checked with Eric and he guesses that what is happening is that the Popup is throwing away the grid, but the binding doesn't remove the columns from that grid
|
||||
//
|
||||
// I think returning a new OrderbookVM is okay at the moment since this Popup window will not be saving any state at this time
|
||||
var price_filter = new filters.DoubleMinMaxState();
|
||||
price_filter.minimum.value = real_price;
|
||||
price_filter.maximum.value = real_price;
|
||||
|
||||
var obvm = new OrderBookVM(null, _parent.bus, null, _parent.model.order_gateway, null, true);
|
||||
var state = new bts.shell.OrderBookWindowFactory.OrderBookStartupData(
|
||||
new bts.shell.OrderBookWindowFactory.FilterStates {
|
||||
security_criteria = new SecurityCriteria(_parent.security),
|
||||
price = price_filter,
|
||||
statuses = new[] { OrderStatus.Working }
|
||||
},
|
||||
bts.shell.OrderBookWindowFactory.OrderBookStartupData.default_mini_grid,
|
||||
use_defaults: false
|
||||
);
|
||||
// NOTE:
|
||||
// we might need the ObservableAppend to InsertAt
|
||||
// if these EditXXX ever support settings.
|
||||
// insert the cancel column
|
||||
obvm.columns.Insert(0, new OrderBookVM.CancelColumn(nameof(OrderBookRowVM.cancel_command)));
|
||||
var qty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.Size.ToString());
|
||||
var qty_index = obvm.columns.indexOf(qty_col);
|
||||
if (qty_index > -1) {
|
||||
// insert the edit column in the same position that the hidden column occupies
|
||||
var existing_editqty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.EditableQty.ToString());
|
||||
var existing_editqty_index = obvm.columns.indexOf(existing_editqty_col);
|
||||
if (existing_editqty_index == -1) {
|
||||
obvm.columns.Insert(qty_index, new OrderBookVM.EditableQtyColumn());
|
||||
}
|
||||
}
|
||||
var price_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.Price.ToString());
|
||||
var price_index = obvm.columns.indexOf(price_col);
|
||||
if (price_index > -1) {
|
||||
var existing_editprice_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.EditablePrice.ToString());
|
||||
var existing_editprice_index = obvm.columns.indexOf(existing_editprice_col);
|
||||
if (existing_editprice_index == -1) {
|
||||
// insert the edit price column immediately after the edit qty
|
||||
obvm.columns.Insert(qty_index + 1, new OrderBookVM.EditablePriceColumn());
|
||||
}
|
||||
}
|
||||
obvm.load_from(state);
|
||||
return obvm;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return $"Price: {this.price} Order Count: {this.orders.Count()}";
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
@@ -91,15 +92,26 @@ namespace mbts.spark {
|
||||
order.PropertyChanged += _order_PropertyChanged;
|
||||
// get the stackpanel it belongs in
|
||||
Panel panel = get_panel(price);
|
||||
//var coords = GetAbsolutePlacement(panel, true);
|
||||
// insert Badge and wire up DataContext
|
||||
if (panel != null) {
|
||||
panel.Children.Add(mkbadge(order));
|
||||
panel.Children.Add(mkbadge(order, panel));
|
||||
}
|
||||
} //else {
|
||||
//Log.Ui.warn($"Failed to add order ({order.id}): order already in {nameof(LadderGridHost)}'s {nameof(_orders)}.");
|
||||
//}
|
||||
}
|
||||
|
||||
public static Rect GetAbsolutePlacement(FrameworkElement element, bool relativeToScreen = false) {
|
||||
var absolutePos = element.PointToScreen(new System.Windows.Point(0, 0));
|
||||
if (relativeToScreen) {
|
||||
return new Rect(absolutePos.X, absolutePos.Y, element.ActualWidth, element.ActualHeight);
|
||||
}
|
||||
var posMW = Application.Current.MainWindow.PointToScreen(new System.Windows.Point(0, 0));
|
||||
absolutePos = new System.Windows.Point(absolutePos.X - posMW.X, absolutePos.Y - posMW.Y);
|
||||
return new Rect(absolutePos.X, absolutePos.Y, element.ActualWidth, element.ActualHeight);
|
||||
}
|
||||
|
||||
void _order_PropertyChanged(object sender, PropertyChangedEventArgs e) {
|
||||
if (e.PropertyName == nameof(LadderOrderVM.price)) {
|
||||
var order = (LadderOrderVM)sender;
|
||||
@@ -108,35 +120,38 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
Badge mkbadge(LadderOrderVM order) {
|
||||
Badge mkbadge(LadderOrderVM order, UIElement popup_placement_target) {
|
||||
Badge b = new Badge();
|
||||
b.DataContext = order;
|
||||
// Hook up the style from xaml
|
||||
b.Style = this.TryFindResource(typeof(Badge)) as Style;
|
||||
b.MouseUp += (s, e) => {
|
||||
b.MouseUp += async (s, e) => {
|
||||
var curr_order = b.DataContext as LadderOrderVM;
|
||||
if (e.ChangedButton == MouseButton.Middle) {
|
||||
b.ContextMenu.DataContext = b.DataContext;
|
||||
b.ContextMenu.IsOpen = true;
|
||||
// close context menu after command executed
|
||||
if (curr_order != null) {
|
||||
Action command_executed = null;
|
||||
command_executed = () => {
|
||||
try {
|
||||
b.ContextMenu.IsOpen = false;
|
||||
curr_order.command_executed -= command_executed;
|
||||
} catch (Exception ex) {
|
||||
Log.Ui.warn($"Error in {typeof(LadderGridHost)}'s {typeof(Badge)} {nameof(LadderOrderVM.command_executed)} local handler: {ex}");
|
||||
}
|
||||
};
|
||||
curr_order.command_executed += command_executed;
|
||||
}
|
||||
// create the new popup window in here
|
||||
var obvm = order.create_orderbook_vm();
|
||||
await Task.Delay(100); // Wait for obvm to load rows collection before showing UI to avoid a flash
|
||||
var view = new MiniOrderBookView(obvm);
|
||||
var popup = new System.Windows.Controls.Primitives.Popup {
|
||||
// we only keep the obvm around assigned to the DataContext so that we can
|
||||
// properly Dispose of it later when the popup is closed.
|
||||
DataContext = obvm,
|
||||
Child = view,
|
||||
StaysOpen = false,
|
||||
PlacementTarget = popup_placement_target,
|
||||
Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom,
|
||||
VerticalOffset = 5
|
||||
};
|
||||
popup.SetBinding(System.Windows.Controls.Primitives.Popup.IsOpenProperty,
|
||||
nameof(OrderBookVM.is_row_collection_populated)
|
||||
);
|
||||
popup.Closed += (sender, event_args) => (popup.DataContext as IDisposable)?.Dispose();
|
||||
b.Popup = popup;
|
||||
} else if (e.ChangedButton == MouseButton.Left) {
|
||||
if (curr_order?.cancel_command?.CanExecute(null) == true)
|
||||
curr_order.cancel_command.Execute(null);
|
||||
}
|
||||
};
|
||||
b.ContextMenuOpening += (s, e) => e.Handled = true;
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Windows;
|
||||
using bts.proto;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace mbts.spark {
|
||||
// IMPORTANT: BE SURE TO ADD ANY NEW PROPERTIES TO THE EQUALS FUNCTION!
|
||||
@@ -179,6 +180,34 @@ namespace mbts.spark {
|
||||
// Settings are liable to change in the near future, so an internal IEqualityComparer simply won't do as it risks becoming out of date.
|
||||
[ProtoContract]
|
||||
class LadderSettings : IEquatable<LadderSettings> {
|
||||
const double _default_order_entry_panel_width = 110.0;
|
||||
const double _default_zoom_factor = 1.0;
|
||||
|
||||
// Default proto constructor
|
||||
// Added this so that when clients upgrade to a version with new settings,
|
||||
// the default values are applied correctly; thx to John for this tip
|
||||
public LadderSettings() {
|
||||
day_visible = true;
|
||||
high_visible = true;
|
||||
low_visible = true;
|
||||
open_visible = true;
|
||||
settle_visible = true;
|
||||
volume_visible = true;
|
||||
account_visible = true;
|
||||
tif_visible = true;
|
||||
order_type_visible = true;
|
||||
account_position_visible = true;
|
||||
size_presets_visible = true;
|
||||
cancel_buys_visible = true;
|
||||
cancel_sells_visible = true;
|
||||
cancel_all_visible = true;
|
||||
auto_clear_visible = true;
|
||||
clear_visible = true;
|
||||
order_entry_panel_initial_width = _default_order_entry_panel_width;
|
||||
zoom_factor = _default_zoom_factor;
|
||||
use_defaults = true;
|
||||
}
|
||||
|
||||
public static LadderSettings Default {
|
||||
get {
|
||||
LadderColumnSettings
|
||||
@@ -216,7 +245,26 @@ namespace mbts.spark {
|
||||
session_high_line = new LadderMarketBreakLine { color = BTSColors.MarketData.session_high, show = true },
|
||||
session_range_fill = new LadderMarketBreakLine { color = BTSColors.MarketData.session_range_fill, show = true },
|
||||
alternate_order_size = 0,
|
||||
size_display = MarketSizeDisplay.get_default()
|
||||
size_display = MarketSizeDisplay.get_default(),
|
||||
day_visible = true,
|
||||
high_visible = true,
|
||||
low_visible = true,
|
||||
open_visible = true,
|
||||
settle_visible = true,
|
||||
volume_visible = true,
|
||||
account_visible = true,
|
||||
tif_visible = true,
|
||||
order_type_visible = true,
|
||||
account_position_visible = true,
|
||||
size_presets_visible = true,
|
||||
cancel_buys_visible = true,
|
||||
cancel_sells_visible = true,
|
||||
cancel_all_visible = true,
|
||||
auto_clear_visible = true,
|
||||
clear_visible = true,
|
||||
order_entry_panel_initial_width = _default_order_entry_panel_width,
|
||||
zoom_slider_visible = false,
|
||||
zoom_factor = _default_zoom_factor,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -278,6 +326,54 @@ namespace mbts.spark {
|
||||
[ProtoMember(26)]
|
||||
public MarketSizeDisplay size_display { get; set; }
|
||||
|
||||
// order entry panel items show/hide
|
||||
[ProtoMember(27), DefaultValue(true)]
|
||||
public bool day_visible { get; set; }
|
||||
[ProtoMember(28), DefaultValue(true)]
|
||||
public bool high_visible { get; set; }
|
||||
[ProtoMember(29), DefaultValue(true)]
|
||||
public bool low_visible { get; set; }
|
||||
[ProtoMember(30), DefaultValue(true)]
|
||||
public bool open_visible { get; set; }
|
||||
[ProtoMember(31), DefaultValue(true)]
|
||||
public bool settle_visible { get; set; }
|
||||
[ProtoMember(32), DefaultValue(true)]
|
||||
public bool volume_visible { get; set; }
|
||||
[ProtoMember(33), DefaultValue(true)]
|
||||
public bool account_visible { get; set; }
|
||||
[ProtoMember(34), DefaultValue(true)]
|
||||
public bool tif_visible { get; set; }
|
||||
[ProtoMember(35), DefaultValue(true)]
|
||||
public bool order_type_visible { get; set; }
|
||||
[ProtoMember(36), DefaultValue(true)]
|
||||
public bool account_position_visible { get; set; }
|
||||
[ProtoMember(37), DefaultValue(true)]
|
||||
public bool size_presets_visible { get; set; }
|
||||
[ProtoMember(38), DefaultValue(true)]
|
||||
public bool cancel_buys_visible { get; set; }
|
||||
[ProtoMember(39), DefaultValue(true)]
|
||||
public bool cancel_sells_visible { get; set; }
|
||||
[ProtoMember(40), DefaultValue(true)]
|
||||
public bool cancel_all_visible { get; set; }
|
||||
[ProtoMember(41), DefaultValue(true)]
|
||||
public bool auto_clear_visible { get; set; }
|
||||
[ProtoMember(42), DefaultValue(true)]
|
||||
public bool clear_visible { get; set; }
|
||||
//
|
||||
[ProtoMember(43), DefaultValue(_default_order_entry_panel_width)]
|
||||
public double order_entry_panel_initial_width { get; set; }
|
||||
|
||||
[ProtoMember(44), DefaultValue(true)]
|
||||
public bool use_defaults { get; set; }
|
||||
|
||||
// the zoom slider is the first new widget on the panel
|
||||
// since our initial general release, so its enablement
|
||||
// will be opt-in (default not visible)
|
||||
[ProtoMember(45)]
|
||||
public bool zoom_slider_visible { get; set; }
|
||||
[ProtoMember(46), DefaultValue(_default_zoom_factor)]
|
||||
public double zoom_factor { get; set; }
|
||||
|
||||
public bool Equals(LadderSettings other) =>
|
||||
other != null &&
|
||||
row_height == other.row_height &&
|
||||
@@ -302,7 +398,27 @@ namespace mbts.spark {
|
||||
session_high_line.safe_equals(other.session_high_line) &&
|
||||
session_range_fill.safe_equals(other.session_range_fill) &&
|
||||
volume_column.safe_equals(other.volume_column) &&
|
||||
size_display.safe_equals(other.size_display);
|
||||
size_display.safe_equals(other.size_display) &&
|
||||
day_visible == other.day_visible &&
|
||||
high_visible == other.high_visible &&
|
||||
low_visible == other.low_visible &&
|
||||
open_visible == other.open_visible &&
|
||||
settle_visible == other.settle_visible &&
|
||||
volume_visible == other.volume_visible &&
|
||||
account_visible == other.account_visible &&
|
||||
tif_visible == other.tif_visible &&
|
||||
order_type_visible == other.order_type_visible &&
|
||||
account_position_visible == other.account_position_visible &&
|
||||
size_presets_visible == other.size_presets_visible &&
|
||||
cancel_buys_visible == other.cancel_buys_visible &&
|
||||
cancel_sells_visible == other.cancel_sells_visible &&
|
||||
cancel_all_visible == other.cancel_all_visible &&
|
||||
auto_clear_visible == other.auto_clear_visible &&
|
||||
clear_visible == other.clear_visible &&
|
||||
order_entry_panel_initial_width == other.order_entry_panel_initial_width &&
|
||||
zoom_slider_visible == other.zoom_slider_visible &&
|
||||
zoom_factor == other.zoom_factor &&
|
||||
use_defaults == other.use_defaults;
|
||||
|
||||
public override bool Equals(object obj) => Equals(obj as LadderSettings);
|
||||
|
||||
@@ -330,7 +446,46 @@ namespace mbts.spark {
|
||||
EqualityComparer.safe_get_hash_code(session_high_line),
|
||||
EqualityComparer.safe_get_hash_code(session_range_fill),
|
||||
EqualityComparer.safe_get_hash_code(volume_column),
|
||||
EqualityComparer.safe_get_hash_code(size_display));
|
||||
EqualityComparer.safe_get_hash_code(size_display),
|
||||
day_visible.GetHashCode(),
|
||||
high_visible.GetHashCode(),
|
||||
low_visible.GetHashCode(),
|
||||
open_visible.GetHashCode(),
|
||||
settle_visible.GetHashCode(),
|
||||
volume_visible.GetHashCode(),
|
||||
account_visible.GetHashCode(),
|
||||
tif_visible.GetHashCode(),
|
||||
order_type_visible.GetHashCode(),
|
||||
account_position_visible.GetHashCode(),
|
||||
size_presets_visible.GetHashCode(),
|
||||
cancel_buys_visible.GetHashCode(),
|
||||
cancel_sells_visible.GetHashCode(),
|
||||
cancel_all_visible.GetHashCode(),
|
||||
auto_clear_visible.GetHashCode(),
|
||||
clear_visible.GetHashCode(),
|
||||
order_entry_panel_initial_width.GetHashCode(),
|
||||
zoom_slider_visible.GetHashCode(),
|
||||
zoom_factor.GetHashCode(),
|
||||
use_defaults.GetHashCode());
|
||||
|
||||
public struct WindowSpecifics {
|
||||
public readonly double order_entry_panel_width, zoom_factor;
|
||||
public readonly uint orders_column_width, bid_column_width, price_column_width, ask_column_width, last_column_width, volume_column_width;
|
||||
|
||||
public WindowSpecifics(LadderSettings s)
|
||||
: this(s.order_entry_panel_initial_width, s.orders_column.width, s.bid_column.width, s.price_column.width, s.ask_column.width, s.last_column.width, s.volume_column.general.width, s.zoom_factor) { }
|
||||
|
||||
public WindowSpecifics(double order_entry_panel_width, uint orders_column_width, uint bid_column_width, uint price_column_width, uint ask_column_width, uint last_column_width, uint volume_column_width, double zoom_factor) {
|
||||
this.order_entry_panel_width = order_entry_panel_width;
|
||||
this.orders_column_width = orders_column_width;
|
||||
this.bid_column_width = bid_column_width;
|
||||
this.price_column_width = price_column_width;
|
||||
this.ask_column_width = ask_column_width;
|
||||
this.last_column_width = last_column_width;
|
||||
this.volume_column_width = volume_column_width;
|
||||
this.zoom_factor = zoom_factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class LadderSettingsExtensions {
|
||||
@@ -345,5 +500,22 @@ namespace mbts.spark {
|
||||
|
||||
public static IEnumerable<LadderColumnSettings> visible_columns(this LadderSettings settings) =>
|
||||
settings.all_columns().Where(c => c.show);
|
||||
|
||||
public static LadderSettings.WindowSpecifics get_window_specifics(this LadderSettings settings) =>
|
||||
new LadderSettings.WindowSpecifics(settings);
|
||||
|
||||
public static LadderSettings with_window_specifics(this LadderSettings source, LadderSettings.WindowSpecifics specifics) {
|
||||
// must use clone bc we are applying settings that are specific to each individual LadderWindow, and should not be shared among all ladders (even if it is default settings).
|
||||
source = SparkUtils.clone_proto(source);
|
||||
source.order_entry_panel_initial_width = specifics.order_entry_panel_width;
|
||||
source.orders_column.width = specifics.orders_column_width;
|
||||
source.bid_column.width = specifics.bid_column_width;
|
||||
source.price_column.width = specifics.price_column_width;
|
||||
source.ask_column.width = specifics.ask_column_width;
|
||||
source.last_column.width = specifics.last_column_width;
|
||||
source.volume_column.general.width = specifics.volume_column_width;
|
||||
source.zoom_factor = specifics.zoom_factor;
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ namespace mbts.spark {
|
||||
readonly Property<LadderSettings.BadgeStyle> _badge_style;
|
||||
readonly Property<uint> _alternate_order_size;
|
||||
readonly DelegateProperty<double> _row_height;
|
||||
Func<double> _get_order_entry_panel_width, _get_zoom_factor;
|
||||
LadderSettings _current;
|
||||
LadderColumnSettingsVM
|
||||
_orders = new OrdersColumnSettingsVM(),
|
||||
@@ -32,14 +33,35 @@ namespace mbts.spark {
|
||||
_session_low_mkt_line = new LadderMarketBreakLineVM("Session Low", 2, 1),
|
||||
_session_high_mkt_line = new LadderMarketBreakLineVM("Session High", 2, 1, false),
|
||||
_session_range_fill = new LadderMarketBreakLineVM("Session Range", 0, 0, false);
|
||||
OrderEntryPanelItemVisibilityVM
|
||||
_zoom_slider_visbility = new OrderEntryPanelItemVisibilityVM("Zoom Slider"),
|
||||
_day_visibility = new OrderEntryPanelItemVisibilityVM("Day Change"),
|
||||
_high_visibility = new OrderEntryPanelItemVisibilityVM("High"),
|
||||
_low_visibility = new OrderEntryPanelItemVisibilityVM("Low"),
|
||||
_open_visibility = new OrderEntryPanelItemVisibilityVM("Open"),
|
||||
_settle_visibility = new OrderEntryPanelItemVisibilityVM("Settle"),
|
||||
_volume_visibility = new OrderEntryPanelItemVisibilityVM("Volume"),
|
||||
_account_visibility = new OrderEntryPanelItemVisibilityVM("Account"),
|
||||
_tif_visibility = new OrderEntryPanelItemVisibilityVM("Time In Force"),
|
||||
_order_type_visibility = new OrderEntryPanelItemVisibilityVM("Order Type"),
|
||||
_account_position_visibility = new OrderEntryPanelItemVisibilityVM("Account Position"),
|
||||
_size_preset_visibility = new OrderEntryPanelItemVisibilityVM("Size Presets"),
|
||||
_auto_clear_visibility = new OrderEntryPanelItemVisibilityVM("Auto Clear"),
|
||||
_clear_visibility = new OrderEntryPanelItemVisibilityVM("Clear"),
|
||||
_cancel_buys_visibility = new OrderEntryPanelItemVisibilityVM("Cancel Buys"),
|
||||
_cancel_sells_visibility = new OrderEntryPanelItemVisibilityVM("Cancel Sells"),
|
||||
_cancel_all_visibility = new OrderEntryPanelItemVisibilityVM("Cancel All");
|
||||
|
||||
public LadderSettingsVM(bool is_synthetic) {
|
||||
public LadderSettingsVM(bool is_synthetic, Func<double> get_left_panel_width, Func<double> get_zoom_factor) {
|
||||
this.is_synthetic = is_synthetic;
|
||||
size_presets = new ObservableEditableCollection<IMutableValueWrapperVM<uint>>(_create_size_preset_vm);
|
||||
size_presets.CollectionChanged += (s, e) => RaisePropertyChanged(nameof(size_presets_text));
|
||||
_badge_style = backing<LadderSettings.BadgeStyle>(nameof(badge_style));
|
||||
_badge_style.Changed += _badge_style_Changed;
|
||||
_alternate_order_size = backing<uint>(nameof(alternate_order_size));
|
||||
_get_order_entry_panel_width = get_left_panel_width;
|
||||
_get_zoom_factor = get_zoom_factor;
|
||||
// be sure to initialize backings before this measure_row_height call
|
||||
_row_height = backing(nameof(row_height), () => LadderRender.measure_row_height(get_result()));
|
||||
_orders.font_size_changed += on_font_size_changed;
|
||||
_bid.font_size_changed += on_font_size_changed;
|
||||
@@ -108,6 +130,28 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<OrderEntryPanelItemVisibilityVM> order_entry_panel_items {
|
||||
get {
|
||||
yield return _zoom_slider_visbility;
|
||||
yield return _day_visibility;
|
||||
yield return _high_visibility;
|
||||
yield return _low_visibility;
|
||||
yield return _open_visibility;
|
||||
yield return _settle_visibility;
|
||||
yield return _volume_visibility;
|
||||
yield return _account_visibility;
|
||||
yield return _tif_visibility;
|
||||
yield return _order_type_visibility;
|
||||
yield return _account_position_visibility;
|
||||
yield return _size_preset_visibility;
|
||||
yield return _clear_visibility;
|
||||
yield return _auto_clear_visibility;
|
||||
yield return _cancel_buys_visibility;
|
||||
yield return _cancel_sells_visibility;
|
||||
yield return _cancel_all_visibility;
|
||||
}
|
||||
}
|
||||
|
||||
public string key => GridOrLadderSettingsSelectorKeys.Ladder;
|
||||
|
||||
public void load(LadderSettings settings) {
|
||||
@@ -128,6 +172,27 @@ namespace mbts.spark {
|
||||
_session_low_mkt_line.assign_from(_current.session_low_line);
|
||||
_session_high_mkt_line.assign_from(_current.session_high_line);
|
||||
_session_range_fill.assign_from(_current.session_range_fill);
|
||||
_zoom_slider_visbility.show = settings.zoom_slider_visible;
|
||||
_day_visibility.show = settings.day_visible;
|
||||
_high_visibility.show = settings.high_visible;
|
||||
_low_visibility.show = settings.low_visible;
|
||||
_open_visibility.show = settings.open_visible;
|
||||
_settle_visibility.show = settings.settle_visible;
|
||||
_volume_visibility.show = settings.volume_visible;
|
||||
_account_visibility.show = settings.account_visible;
|
||||
_tif_visibility.show = settings.tif_visible;
|
||||
_order_type_visibility.show = settings.order_type_visible;
|
||||
_account_position_visibility.show = settings.account_position_visible;
|
||||
_size_preset_visibility.show = settings.size_presets_visible;
|
||||
_clear_visibility.show = settings.clear_visible;
|
||||
_auto_clear_visibility.show = settings.auto_clear_visible;
|
||||
_cancel_buys_visibility.show = settings.cancel_buys_visible;
|
||||
_cancel_sells_visibility.show = settings.cancel_sells_visible;
|
||||
_cancel_all_visibility.show = settings.cancel_all_visible;
|
||||
// do not read the order entry panel width here from settings
|
||||
// because it could have changed from the UI
|
||||
// there might be a better way of doing this, like how
|
||||
// does the widths of the ladder grid column pull this off?
|
||||
}
|
||||
|
||||
public LadderSettings get_result() {
|
||||
@@ -149,6 +214,25 @@ namespace mbts.spark {
|
||||
clone.session_high_line = _session_high_mkt_line.get_settings();
|
||||
clone.session_range_fill = _session_range_fill.get_settings();
|
||||
clone.row_height = LadderRender.measure_row_height(clone);
|
||||
clone.day_visible = _day_visibility.show;
|
||||
clone.high_visible = _high_visibility.show;
|
||||
clone.low_visible = _low_visibility.show;
|
||||
clone.open_visible = _open_visibility.show;
|
||||
clone.settle_visible = _settle_visibility.show;
|
||||
clone.volume_visible = _volume_visibility.show;
|
||||
clone.account_visible = _account_visibility.show;
|
||||
clone.tif_visible = _tif_visibility.show;
|
||||
clone.order_type_visible = _order_type_visibility.show;
|
||||
clone.account_position_visible = _account_position_visibility.show;
|
||||
clone.size_presets_visible = _size_preset_visibility.show;
|
||||
clone.auto_clear_visible = _auto_clear_visibility.show;
|
||||
clone.clear_visible = _clear_visibility.show;
|
||||
clone.cancel_buys_visible = _cancel_buys_visibility.show;
|
||||
clone.cancel_sells_visible = _cancel_sells_visibility.show;
|
||||
clone.cancel_all_visible = _cancel_all_visibility.show;
|
||||
clone.order_entry_panel_initial_width = _get_order_entry_panel_width();
|
||||
clone.zoom_slider_visible = _zoom_slider_visbility.show;
|
||||
clone.zoom_factor = _get_zoom_factor();
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -276,6 +360,19 @@ namespace mbts.spark {
|
||||
};
|
||||
}
|
||||
|
||||
public class OrderEntryPanelItemVisibilityVM : ViewModelBase {
|
||||
readonly Property<bool> _show;
|
||||
|
||||
public OrderEntryPanelItemVisibilityVM(string name) {
|
||||
this.name = name;
|
||||
_show = backing(nameof(show), true);
|
||||
}
|
||||
|
||||
public string name { get; }
|
||||
public bool show { get => _show.Value; set => _show.Value = value; }
|
||||
|
||||
}
|
||||
|
||||
public class VolumeColumnVM : ViewModelBase {
|
||||
readonly DelegateProperty<bool> _any_visible;
|
||||
public VolumeColumnVM() {
|
||||
|
||||
@@ -140,6 +140,7 @@
|
||||
<Setter Property="VerticalContentAlignment" Value="Top"/>
|
||||
</Style>
|
||||
</TabControl.ItemContainerStyle>
|
||||
<!-- Fonts and Colors Tab -->
|
||||
<TabItem Header="Fonts and Colors" VerticalAlignment="Stretch">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
@@ -232,6 +233,7 @@
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<!-- Market Break Lines Tab -->
|
||||
<TabItem Header="Market Break Lines" VerticalAlignment="Stretch">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
@@ -350,6 +352,7 @@
|
||||
</ItemsControl>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<!-- Market Size Tab -->
|
||||
<TabItem Header="Market Size Display">
|
||||
<Grid DataContext="{Binding size_display}">
|
||||
<Grid.RowDefinitions>
|
||||
@@ -440,6 +443,7 @@
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<!-- Volume Display Tab -->
|
||||
<TabItem Header="Volume Display" VerticalAlignment="Stretch" Visibility="{Binding is_synthetic, Converter={StaticResource FalseVisibilityConverter}}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
@@ -557,6 +561,7 @@
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<!-- Size Presets Tab-->
|
||||
<TabItem Header="Size Presets" VerticalAlignment="Stretch">
|
||||
<Grid HorizontalAlignment="Left">
|
||||
<Grid.RowDefinitions>
|
||||
@@ -592,6 +597,63 @@
|
||||
</Button>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<!-- Order Entry Panel Item Control Visbility -->
|
||||
<TabItem Header="Left Panel" VerticalAlignment="Stretch">
|
||||
<Grid HorizontalAlignment="Left" Grid.IsSharedSizeScope="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ItemsControl Grid.Row="0" ItemsSource="{Binding order_entry_panel_items}" Grid.IsSharedSizeScope="True">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="2" Focusable="False"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="A" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Margin="0,5,5,0">
|
||||
<CheckBox Content="{Binding name}" FlowDirection="RightToLeft" IsChecked="{Binding show}">
|
||||
<CheckBox.Resources>
|
||||
<Style TargetType="{x:Type Path}">
|
||||
<Setter Property="FlowDirection" Value="LeftToRight" />
|
||||
</Style>
|
||||
</CheckBox.Resources>
|
||||
</CheckBox>
|
||||
</Border>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<!--
|
||||
<ItemsControl ItemsSource="{Binding order_entry_panel_items}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="2" Focusable="False"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="52"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" VerticalAlignment="Center" Text="{Binding name}"/>
|
||||
<CheckBox Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding show}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
-->
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
</TabControl>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -20,18 +20,24 @@ using System.Windows.Media;
|
||||
|
||||
namespace mbts.spark {
|
||||
class LadderVM : DisposableViewModelBase, IWillLetYouKnowWhenIDispose, IKeyListener {
|
||||
static readonly Brush _transparent_status_brush = BTSColors.UI.transparent;
|
||||
|
||||
readonly IBus _bus;
|
||||
readonly LadderModel _model;
|
||||
readonly Property<double> _high, _low, _change, _open, _settlement, _position;
|
||||
readonly Property<double> _high, _low, _change, _open, _settlement, _position, _left_panel_width, _zoom_factor;
|
||||
//readonly Property<double> _window_width, _window_height;
|
||||
readonly Property<uint> _orders_column_width, _bid_column_width, _price_column_width, _ask_column_width, _last_column_width, _volume_column_width;
|
||||
readonly Property<ulong> _volume;
|
||||
readonly Property<bool> _auto_clear;
|
||||
readonly Property<bool> _auto_clear, _show_day, _show_high, _show_low, _show_open, _show_settle, _show_volume, _show_account,
|
||||
_show_tif, _show_order_type, _show_account_position, _show_size_preset, _show_auto_clear, _show_clear, _show_cancel_buys,
|
||||
_show_cancel_sells, _show_cancel_all, _show_zoom;
|
||||
readonly Property<Brush> _bid_background, _bid_foreground, _ask_background, _ask_foreground;
|
||||
IWindowSettingsManager<LadderSettings> _settings_manager;
|
||||
ulong _misc_time = 0;
|
||||
IEqualityComparer<double> _equality;
|
||||
Dictionary<double, LadderOrderVM> _slot_price_vs_orders;
|
||||
|
||||
public LadderVM(IBus bus, LadderModel model, IWindowSettingsManager<LadderSettings> settings_manager) {
|
||||
public LadderVM(IBus bus, LadderModel model, IWindowSettingsManager<LadderSettings> settings_manager, LadderSettings init) {
|
||||
_bus = bus;
|
||||
_model = model;
|
||||
_equality = TickUtils.tick_equality_comparer(_model.security.ContractSpec);
|
||||
@@ -44,13 +50,23 @@ namespace mbts.spark {
|
||||
_change = backing(nameof(change), double.NaN);
|
||||
_open = backing(nameof(open), double.NaN);
|
||||
_settlement = backing(nameof(settlement), double.NaN);
|
||||
// init window-specific settings: these properties should not observe settings updates; they are independent from the settings dialog, so it doesn't make sense that the settings dialog should cause them to change.
|
||||
var ws = init.get_window_specifics();
|
||||
_orders_column_width = backing(nameof(orders_column_width), ws.orders_column_width);
|
||||
_bid_column_width = backing(nameof(bid_column_width), ws.bid_column_width);
|
||||
_price_column_width = backing(nameof(price_column_width), ws.price_column_width);
|
||||
_ask_column_width = backing(nameof(ask_column_width), ws.ask_column_width);
|
||||
_last_column_width = backing(nameof(last_column_width), ws.last_column_width);
|
||||
_volume_column_width = backing(nameof(volume_column_width), ws.volume_column_width);
|
||||
_left_panel_width = backing(nameof(left_panel_width), ws.order_entry_panel_width);
|
||||
_zoom_factor = backing(nameof(zoom_factor), ws.zoom_factor);
|
||||
// remember: account_positions needs to subscribe to selected_account; both the sub and account_positions need to be disposed
|
||||
var account_positions = new KeyDrivenObservableSeries<string, double>(a => _model.order_gateway.security_total_position(security.Id, a));
|
||||
_position = backing(nameof(position), account_positions.OnDispatcher());
|
||||
_volume = backing(nameof(volume), 0ul);
|
||||
_auto_clear = backing(nameof(auto_clear), false);
|
||||
_status = backing(nameof(status_message), "");
|
||||
_status_brush = backing(nameof(status_background), (Brush)BTSColors.UI.spark_menu_background);
|
||||
_status_brush = backing(nameof(status_background), _transparent_status_brush);
|
||||
// brush props
|
||||
_bid_background = brush_prop(nameof(bid_background), s => s.bid_column.background.rgb_brush());
|
||||
_bid_foreground = brush_prop(nameof(bid_foreground), s => s.bid_column.foreground.rgb_brush());
|
||||
@@ -58,7 +74,31 @@ namespace mbts.spark {
|
||||
_ask_foreground = brush_prop(nameof(ask_foreground), s => s.ask_column.foreground.rgb_brush());
|
||||
Property<Brush> brush_prop(string name, Func<LadderSettings, Brush> get_brush) =>
|
||||
backing(name, settings_manager.subscribe_current().OnDispatcher(), get_brush);
|
||||
// bool visibility props
|
||||
_show_day = bool_prop(nameof(show_day), s => s.day_visible);
|
||||
_show_high = bool_prop(nameof(show_high), s => s.high_visible);
|
||||
_show_low = bool_prop(nameof(show_low), s => s.low_visible);
|
||||
_show_open = bool_prop(nameof(show_open), s => s.open_visible);
|
||||
_show_settle = bool_prop(nameof(show_settle), s => s.settle_visible);
|
||||
_show_volume = bool_prop(nameof(show_volume), s => s.volume_visible);
|
||||
_show_account = bool_prop(nameof(show_account), s => s.account_visible);
|
||||
_show_tif = bool_prop(nameof(show_tif), s => s.tif_visible);
|
||||
_show_order_type = bool_prop(nameof(show_order_type), s => s.order_type_visible);
|
||||
_show_account_position = bool_prop(nameof(show_account_position), s => s.account_position_visible);
|
||||
_show_size_preset = bool_prop(nameof(show_size_preset), s => s.size_presets_visible);
|
||||
_show_auto_clear = bool_prop(nameof(show_auto_clear), s => s.auto_clear_visible);
|
||||
_show_clear = bool_prop(nameof(show_clear), s => s.clear_visible);
|
||||
_show_cancel_buys = bool_prop(nameof(show_cancel_buys), s => s.cancel_buys_visible);
|
||||
_show_cancel_sells = bool_prop(nameof(show_cancel_sells), s => s.cancel_sells_visible);
|
||||
_show_cancel_all = bool_prop(nameof(show_cancel_all), s => s.cancel_all_visible);
|
||||
_show_zoom = bool_prop(nameof(show_zoom), s => s.zoom_slider_visible);
|
||||
|
||||
Property<bool> bool_prop(string name, Func<LadderSettings, bool> get_bool) =>
|
||||
backing(name, settings_manager.subscribe_current().OnDispatcher(), get_bool);
|
||||
|
||||
//
|
||||
//_window_height = backing(nameof(window_height), settings_manager.subscribe_current().OnDispatcher().Select(s => s.zoom_factor), (scale) => scale * 600);
|
||||
//_window_width = backing(nameof(window_width), settings_manager.subscribe_current().OnDispatcher().Select(s => s.zoom_factor), (scale) => scale * 420);
|
||||
position_click_command = new DelegateCommand(() => size_calculator.value = DoubleHelper.safe_convert_to_uint(Math.Abs(position)));
|
||||
send_order_command = new DelegateCommand<(double price, Side? side, bool size_from_alternate)>(send_order);
|
||||
cancel_command = new AsyncCommand<LadderModel.CancelType>(_model.cancel_orders, true);
|
||||
@@ -67,11 +107,11 @@ namespace mbts.spark {
|
||||
settings_command = new DialogCommand(() => {
|
||||
var vm = new WindowSettingsDialogVM<LadderSettings, LadderSettingsVM>(
|
||||
settings_manager, s => s.ladder, UserSettings.FileExtensions.LADDER,
|
||||
s => {
|
||||
var svm = new LadderSettingsVM(security.ContractCase == Security.ContractOneofCase.Synthetic);
|
||||
s => {
|
||||
var svm = new LadderSettingsVM(security.ContractCase == Security.ContractOneofCase.Synthetic, () => left_panel_width, () => zoom_factor);
|
||||
svm.load(s);
|
||||
return svm;
|
||||
});
|
||||
}, s => s.with_window_specifics(get_save_state().get_window_specifics()));
|
||||
var dialog = new ui.spark.SimpleGridSettingsDialog {
|
||||
DataContext = vm,
|
||||
Title = $"Ladder Settings - '{security}'"
|
||||
@@ -202,7 +242,7 @@ namespace mbts.spark {
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
if (_status.Value == a.message) {
|
||||
_status.set("");
|
||||
_status_brush.set(BTSColors.UI.spark_menu_background);
|
||||
_status_brush.set(_transparent_status_brush);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,16 +273,41 @@ namespace mbts.spark {
|
||||
public Brush ask_background => _ask_background.Value;
|
||||
public Brush ask_foreground => _ask_foreground.Value;
|
||||
public DialogCommand settings_command { get; }
|
||||
public bool show_day { get => _show_day.Value; set => _show_day.set(value); }
|
||||
public bool show_high { get => _show_high.Value; set => _show_high.set(value); }
|
||||
public bool show_low { get => _show_low.Value; set => _show_low.set(value); }
|
||||
public bool show_open { get => _show_open.Value; set => _show_open.set(value); }
|
||||
public bool show_settle { get => _show_settle.Value; set => _show_settle.set(value); }
|
||||
public bool show_volume { get => _show_volume.Value; set => _show_volume.set(value); }
|
||||
public bool show_account { get => _show_account.Value; set => _show_account.set(value); }
|
||||
public bool show_tif { get => _show_tif.Value; set => _show_tif.set(value); }
|
||||
public bool show_order_type { get => _show_order_type.Value; set => _show_order_type.set(value); }
|
||||
public bool show_account_position { get => _show_account_position.Value; set => _show_account_position.set(value); }
|
||||
public bool show_size_preset { get => _show_size_preset.Value; set => _show_size_preset.set(value); }
|
||||
public bool show_auto_clear { get => _show_auto_clear.Value; set => _show_auto_clear.set(value); }
|
||||
public bool show_clear { get => _show_clear.Value; set => _show_clear.set(value); }
|
||||
public bool show_cancel_buys { get => _show_cancel_buys.Value; set => _show_cancel_buys.set(value); }
|
||||
public bool show_cancel_sells { get => _show_cancel_sells.Value; set => _show_cancel_sells.set(value); }
|
||||
public bool show_cancel_all { get => _show_cancel_all.Value; set => _show_cancel_all.set(value); }
|
||||
public bool show_zoom { get => _show_zoom.Value; set => _show_zoom.set(value); }
|
||||
public double left_panel_width { get => _left_panel_width.Value; set => _left_panel_width.set(value); }
|
||||
public double zoom_factor { get => _zoom_factor.Value; set => _zoom_factor.set(value); }
|
||||
//public double window_height { get => _window_height.Value; set => _window_height.set(value); }
|
||||
//public double window_width { get => _window_width.Value; set => _window_width.set(value); }
|
||||
public uint orders_column_width { get => _orders_column_width.Value; set => _orders_column_width.set(value); }
|
||||
public uint bid_column_width { get => _bid_column_width.Value; set => _bid_column_width.set(value); }
|
||||
public uint price_column_width { get => _price_column_width.Value; set => _price_column_width.set(value); }
|
||||
public uint ask_column_width { get => _ask_column_width.Value; set => _ask_column_width.set(value); }
|
||||
public uint last_column_width { get => _last_column_width.Value; set => _last_column_width.set(value); }
|
||||
public uint volume_column_width { get => _volume_column_width.Value; set => _volume_column_width.set(value); }
|
||||
internal LadderModel model => _model;
|
||||
internal IObservable<LadderSettings> settings => _settings_manager.subscribe_current();
|
||||
|
||||
public LadderSettings get_save_state() {
|
||||
// TODO: save an actual OrderDefinition to preserve order params.
|
||||
if (_settings_manager.instance_settings == null)
|
||||
return null;
|
||||
var settings_clone = SparkUtils.clone_proto(_settings_manager.instance_settings);
|
||||
settings_clone.default_order_type = order_properties_vm.selected_order_type?.order_type ?? settings_clone.default_order_type;
|
||||
return settings_clone;
|
||||
var state = _settings_manager.current_settings();
|
||||
state = state.with_window_specifics(new LadderSettings.WindowSpecifics(left_panel_width, orders_column_width, bid_column_width, price_column_width, ask_column_width, last_column_width, volume_column_width, zoom_factor));
|
||||
state.use_defaults = _settings_manager.instance_settings == null;
|
||||
return state;
|
||||
}
|
||||
|
||||
public void key_down(KeyEventArgs e) {
|
||||
@@ -299,6 +364,7 @@ namespace mbts.spark {
|
||||
_change.set(mid - md.settlement_price);
|
||||
}
|
||||
_volume.set(volume);
|
||||
_misc_time = _model.md.misc_timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
<UserControl x:Class="mbts.spark.LadderView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mbts="clr-namespace:mbts.ui"
|
||||
xmlns:controls="clr-namespace:mbts.ui.controls"
|
||||
xmlns:spark="clr-namespace:mbts.spark"
|
||||
xmlns:uag="clr-namespace:mbts.ui.spark.algo"
|
||||
xmlns:converters="clr-namespace:mbts.ui.converters"
|
||||
xmlns:txt="clr-namespace:mbts.ui.utility.text"
|
||||
Height="600"
|
||||
Width="420"
|
||||
Background="{x:Static mbts:BTSColors.common_background_brush}"
|
||||
TextElement.FontSize="{x:Static mbts:FontSizes.Small}">
|
||||
<UserControl
|
||||
x:Class="mbts.spark.LadderView"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mbts="clr-namespace:mbts.ui"
|
||||
xmlns:controls="clr-namespace:mbts.ui.controls"
|
||||
xmlns:spark="clr-namespace:mbts.spark"
|
||||
xmlns:uag="clr-namespace:mbts.ui.spark.algo"
|
||||
xmlns:converters="clr-namespace:mbts.ui.converters"
|
||||
xmlns:txt="clr-namespace:mbts.ui.utility.text"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
Height="600"
|
||||
Width="420"
|
||||
Background="{x:Static mbts:BTSColors.common_background_brush}"
|
||||
TextElement.FontSize="{x:Static mbts:FontSizes.Small}"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
@@ -27,6 +31,13 @@
|
||||
</converters:BooleanToObjectConverter>
|
||||
<converters:EnumCharacterCasingConverter x:Key="casing_converter"/>
|
||||
<converters:DoubleTextConverter x:Key="PositionTextConverter" Format="0" NaNText="-"/>
|
||||
<converters:GridLengthConverter x:Key="GridLengthPixelConverter" UnitType="Pixel" />
|
||||
<converters:ResponsiveTextConverter x:Key="DayTextConverter" LongText="Day:" ShortText="D:" Threshold="99" />
|
||||
<converters:ResponsiveTextConverter x:Key="HighTextConverter" LongText="High:" ShortText="H:" Threshold="99" />
|
||||
<converters:ResponsiveTextConverter x:Key="LowTextConverter" LongText="Low:" ShortText="L:" Threshold="99" />
|
||||
<converters:ResponsiveTextConverter x:Key="OpenTextConverter" LongText="Open:" ShortText="O:" Threshold="99" />
|
||||
<converters:ResponsiveTextConverter x:Key="SettleTextConverter" LongText="Settle:" ShortText="S:" Threshold="99" />
|
||||
<converters:ResponsiveTextConverter x:Key="VolumeTextConverter" LongText="Volume:" ShortText="V:" Threshold="99" />
|
||||
<Style TargetType="GridViewColumnHeader">
|
||||
<Setter Property="Background" Value="{mbts:BTSColor header}"/>
|
||||
<Setter Property="Foreground" Value="{mbts:BTSColor header_text}"/>
|
||||
@@ -34,6 +45,7 @@
|
||||
<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}">
|
||||
@@ -57,8 +69,8 @@
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Canvas>
|
||||
<Thumb x:Name="PART_HeaderGripper">
|
||||
<Canvas MinWidth="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=MinWidth, Mode=OneTime}">
|
||||
<Thumb x:Name="PART_HeaderGripper" DragDelta="Thumb_DragDelta">
|
||||
<Thumb.Style>
|
||||
<Style TargetType="{x:Type Thumb}">
|
||||
<Setter Property="Canvas.Right" Value="-9"/>
|
||||
@@ -123,7 +135,7 @@
|
||||
<Style x:Key="LadderMenuButton" TargetType="Button">
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="Margin" Value="5,0,5,5"/>
|
||||
<Setter Property="Margin" Value="2,0,2,5"/>
|
||||
<Setter Property="Padding" Value="2"/>
|
||||
</Style>
|
||||
<Style x:Key="CancelBuys" TargetType="Button" BasedOn="{StaticResource LadderMenuButton}">
|
||||
@@ -244,8 +256,7 @@
|
||||
Background="{mbts:BTSColor UI.Button.normal_background}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
SnapsToDevicePixels="True"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="0,0,0,12">
|
||||
Margin="2,0,2,10">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
@@ -263,108 +274,166 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<sys:Double x:Key="MaxZoomFactor">1.5</sys:Double>
|
||||
<sys:Double x:Key="MinZoomFactor">0.5</sys:Double>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<AdornerDecorator>
|
||||
<Grid x:Name="_root">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="112"/>
|
||||
<ColumnDefinition Width="2"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid RowSpan="2" Background="{mbts:BTSColor UI.spark_menu_background}">
|
||||
<Grid.LayoutTransform>
|
||||
<ScaleTransform CenterX="0" CenterY="0"
|
||||
ScaleX="{Binding ElementName=_ui_scaler, Path=Value}"
|
||||
ScaleY="{Binding ElementName=_ui_scaler, Path=Value}"
|
||||
/>
|
||||
</Grid.LayoutTransform>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{Binding left_panel_width, Mode=TwoWay, Converter={StaticResource GridLengthPixelConverter}}"
|
||||
MinWidth="60" MaxWidth="150"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Margin="5,0">
|
||||
<StackPanel.Resources>
|
||||
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</StackPanel.Resources>
|
||||
<Grid Margin="8,4,0,0">
|
||||
<ScrollViewer Grid.Row="0" Grid.RowSpan="2" VerticalScrollBarVisibility="Hidden">
|
||||
<Grid RowSpan="2" Background="{mbts:BTSColor UI.spark_menu_background}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="6"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Grid.Row="0" Text="Day"/>
|
||||
<TextBlock Grid.Column="0" Grid.Row="2" Text="High"/>
|
||||
<TextBlock Grid.Column="0" Grid.Row="4" Text="Low"/>
|
||||
<TextBlock Grid.Column="0" Grid.Row="6" Text="Open"/>
|
||||
<TextBlock Grid.Column="0" Grid.Row="8" Text="Settle"/>
|
||||
<TextBlock Grid.Column="0" Grid.Row="10" Text="Volume"/>
|
||||
|
||||
<TextBlock Grid.Column="2" Grid.Row="0" Foreground="{Binding change, Converter={StaticResource NumericSignBrushConverter}}" FontWeight="Bold">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="change"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="2" Grid.Row="2" Foreground="{mbts:BTSColor UI.default_text}" FontWeight="Bold">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="high"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="2" Grid.Row="4" Foreground="{mbts:BTSColor UI.default_text}" FontWeight="Bold">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="low"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="2" Grid.Row="6" FontWeight="Bold">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="open"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="2" Grid.Row="8" FontWeight="Bold">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="settlement"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="2" Grid.Row="10" Text="{Binding volume, Mode=OneWay}" FontWeight="Bold" />
|
||||
</Grid>
|
||||
<ComboBox DataContext="{Binding order_properties_vm}" Margin="2,10,2,2" ItemsSource="{Binding accounts}" SelectedItem="{Binding selected_account}"/>
|
||||
<ComboBox DataContext="{Binding order_properties_vm}" ItemsSource="{Binding time_in_forces}" SelectedItem="{Binding selected_time_in_force}" Margin="2">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={StaticResource casing_converter}, ConverterParameter=Upper}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<uag:OrderDetailsPopupControl
|
||||
<StackPanel Margin="2,0,5,0">
|
||||
<StackPanel.Resources>
|
||||
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</StackPanel.Resources>
|
||||
<StackPanel x:Name="_slider_stack_panel" Margin="5,5,5,0">
|
||||
<Slider x:Name="_ui_scaler" Value="{Binding zoom_factor}"
|
||||
Minimum="{StaticResource MinZoomFactor}"
|
||||
Maximum="{StaticResource MaxZoomFactor}"
|
||||
Width="{Binding Path=ActualWidth, ElementName=_slider_stack_panel}"
|
||||
Visibility="{Binding show_zoom, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
</StackPanel>
|
||||
<Grid Margin="2,4,2,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="6"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Day -->
|
||||
<TextBlock Grid.Column="0" Grid.Row="0"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource DayTextConverter}}"
|
||||
Margin="0,0,0,4"
|
||||
Visibility="{Binding show_day, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
<TextBlock Grid.Column="2" Grid.Row="0" FontWeight="Bold"
|
||||
Margin="0,0,0,4"
|
||||
Foreground="{Binding change, Converter={StaticResource NumericSignBrushConverter}}"
|
||||
Visibility="{Binding show_day, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="change"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
<!-- High -->
|
||||
<TextBlock Grid.Column="0" Grid.Row="1"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource HighTextConverter}}"
|
||||
Margin="0,0,0,4"
|
||||
Visibility="{Binding show_high, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
<TextBlock Grid.Column="2" Grid.Row="1" Foreground="{mbts:BTSColor UI.default_text}" FontWeight="Bold"
|
||||
Margin="0,0,0,4"
|
||||
Visibility="{Binding show_high, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="high"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
<!-- Low -->
|
||||
<TextBlock Grid.Column="0" Grid.Row="2"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource LowTextConverter}}"
|
||||
Margin="0,0,0,4"
|
||||
Visibility="{Binding show_low, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
<TextBlock Grid.Column="2" Grid.Row="2" Foreground="{mbts:BTSColor UI.default_text}" FontWeight="Bold"
|
||||
Margin="0,0,0,4"
|
||||
Visibility="{Binding show_low, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="low"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
<!-- Open -->
|
||||
<TextBlock Grid.Column="0" Grid.Row="3"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource OpenTextConverter}}"
|
||||
Margin="0,0,0,4"
|
||||
Visibility="{Binding show_open, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
<TextBlock Grid.Column="2" Grid.Row="3" FontWeight="Bold"
|
||||
Margin="0,0,0,4"
|
||||
Visibility="{Binding show_open, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="open"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
<!-- Settle -->
|
||||
<TextBlock Grid.Column="0" Grid.Row="4"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource SettleTextConverter}}"
|
||||
Margin="0,0,0,4"
|
||||
Visibility="{Binding show_settle, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
<TextBlock Grid.Column="2" Grid.Row="4" FontWeight="Bold"
|
||||
Margin="0,0,0,4"
|
||||
Visibility="{Binding show_settle, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
|
||||
<Binding Path="settlement"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
<!-- Volume -->
|
||||
<TextBlock Grid.Column="0" Grid.Row="5"
|
||||
Text="{Binding left_panel_width, Converter={StaticResource VolumeTextConverter}}"
|
||||
Margin="0,0,0,4"
|
||||
Visibility="{Binding show_volume, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
<TextBlock Grid.Column="2" Grid.Row="5" Text="{Binding volume, Mode=OneWay}" FontWeight="Bold"
|
||||
Margin="0,0,0,4"
|
||||
Visibility="{Binding show_volume, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
</Grid>
|
||||
<!-- Account combobox -->
|
||||
<ComboBox DataContext="{Binding order_properties_vm}" Margin="2,10,2,2"
|
||||
ItemsSource="{Binding accounts}" SelectedItem="{Binding selected_account}"
|
||||
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
|
||||
Path = DataContext.show_account,
|
||||
Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
<!-- TIF combobox-->
|
||||
<ComboBox DataContext="{Binding order_properties_vm}" ItemsSource="{Binding time_in_forces}"
|
||||
SelectedItem="{Binding selected_time_in_force}" Margin="2"
|
||||
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
|
||||
Path = DataContext.show_tif,
|
||||
Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={StaticResource casing_converter}, ConverterParameter=Upper}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<!-- Order Type combobox -->
|
||||
<uag:OrderDetailsPopupControl
|
||||
DataContext="{Binding order_properties_vm}"
|
||||
Margin="2"
|
||||
ExpandDirection="Left"
|
||||
@@ -372,19 +441,25 @@
|
||||
IsOpen="{Binding selected_order_type.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding order_types}"
|
||||
SelectedItem="{Binding selected_order_type}"
|
||||
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
|
||||
Path = DataContext.show_order_type,
|
||||
Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
/>
|
||||
<Button
|
||||
<!-- Position into qty button -->
|
||||
<Button
|
||||
Margin="2"
|
||||
Command="{Binding position_click_command}"
|
||||
Content="{Binding position, Converter={StaticResource PositionTextConverter}}"
|
||||
FontWeight="Bold"
|
||||
Foreground="{Binding position, Converter={StaticResource NumericSignBrushConverter}}"
|
||||
Visibility="{Binding show_account_position, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
>
|
||||
<Button.ToolTip>
|
||||
<TextBlock Text="{Binding order_properties_vm.selected_account, StringFormat='{}{0} total position (click to copy to size)'}"/>
|
||||
</Button.ToolTip>
|
||||
</Button>
|
||||
<TextBox
|
||||
<Button.ToolTip>
|
||||
<TextBlock Text="{Binding order_properties_vm.selected_account, StringFormat='{}{0} total position (click to copy to size)'}"/>
|
||||
</Button.ToolTip>
|
||||
</Button>
|
||||
<!-- Qty textbox -->
|
||||
<TextBox
|
||||
mbts:TextBoxBehavior.InputFilter="{mbts:RegexInputFilter Regex={x:Static Member=Integer, MemberType={mbts:Nested txt:TextUtils.RegexFilters}}}"
|
||||
mbts:TextBoxBehavior.IncrementDecrementOnMouseClick="True"
|
||||
HorizontalContentAlignment="Right"
|
||||
@@ -392,42 +467,64 @@
|
||||
Text="{Binding size_calculator.value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
ContextMenu="{x:Null}"
|
||||
/>
|
||||
<ItemsControl ItemsSource="{Binding size_calculator.buttons}" Focusable="False">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="2" Focusable="False"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Margin="2" Command="{Binding Path=DataContext.size_calculator.click, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding}" Content="{Binding}" Focusable="False"/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<Button Margin="2" HorizontalAlignment="Stretch" Command="{Binding size_calculator.clear}">Clear</Button>
|
||||
<!-- reset to defaults after order -->
|
||||
<CheckBox Margin="2" IsChecked="{Binding auto_clear}">Auto Clear</CheckBox>
|
||||
</StackPanel>
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Margin="0,17,0,0" VerticalAlignment="Top">
|
||||
<Button Command="{Binding cancel_command}" CommandParameter="Bids" Style="{StaticResource CancelBuys}"/>
|
||||
<Button Command="{Binding cancel_command}" CommandParameter="Asks" Style="{StaticResource CancelSells}"/>
|
||||
<Button Command="{Binding cancel_command}" CommandParameter="All" Style="{StaticResource CancelAll}"/>
|
||||
</StackPanel>
|
||||
<Button Grid.Row="1" HorizontalContentAlignment="Stretch" Command="{Binding settings_command}" Style="{StaticResource SettingsButton}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Border Grid.Column="2" HorizontalAlignment="Stretch">
|
||||
<GridViewHeaderRowPresenter x:Name="_column_headers_presenter" AllowsColumnReorder="False"/>
|
||||
</Border>
|
||||
<spark:LadderGridHost x:Name="_ladder" Grid.Column="2" Grid.Row="1"
|
||||
Command="{Binding send_order_command}"
|
||||
Orders="{Binding orders}">
|
||||
<spark:LadderGridHost.Resources>
|
||||
<!-- Qty quick buttons -->
|
||||
<ItemsControl ItemsSource="{Binding size_calculator.buttons}" Focusable="False"
|
||||
Visibility="{Binding show_size_preset, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
>
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="2" Focusable="False"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Margin="2" Command="{Binding Path=DataContext.size_calculator.click, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding}" Content="{Binding}" Focusable="False"/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<!-- Clear button -->
|
||||
<Button Margin="2" HorizontalAlignment="Stretch" Command="{Binding size_calculator.clear}"
|
||||
Visibility="{Binding show_clear, Converter={StaticResource BooleanToVisibilityConverter}}">Clear</Button>
|
||||
<!-- Auto Clear button - reset to defaults after order -->
|
||||
<CheckBox Margin="2" IsChecked="{Binding auto_clear}"
|
||||
Visibility="{Binding show_auto_clear, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
Auto Clear
|
||||
</CheckBox>
|
||||
<StackPanel Margin="0,17,0,0" VerticalAlignment="Top">
|
||||
<!-- Cancel Buttons -->
|
||||
<Button Command="{Binding cancel_command}" CommandParameter="Bids" Style="{StaticResource CancelBuys}"
|
||||
Visibility="{Binding show_cancel_buys, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
<Button Command="{Binding cancel_command}" CommandParameter="Asks" Style="{StaticResource CancelSells}"
|
||||
Visibility="{Binding show_cancel_sells, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
<Button Command="{Binding cancel_command}" CommandParameter="All" Style="{StaticResource CancelAll}"
|
||||
Visibility="{Binding show_cancel_all, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="1" Margin="0,17,0,0" VerticalAlignment="Bottom">
|
||||
<!-- Settings Button -->
|
||||
<Button Command="{Binding settings_command}" Style="{StaticResource SettingsButton}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
<!-- Ladder Column Headers -->
|
||||
<Border Grid.Column="2" HorizontalAlignment="Stretch">
|
||||
<GridViewHeaderRowPresenter x:Name="_column_headers_presenter" AllowsColumnReorder="False" />
|
||||
</Border>
|
||||
<!-- LadderGridHost -->
|
||||
<spark:LadderGridHost
|
||||
x:Name="_ladder"
|
||||
Grid.Column="2"
|
||||
Grid.Row="1"
|
||||
Command="{Binding send_order_command}"
|
||||
Orders="{Binding orders}"
|
||||
orders_column_width="{Binding orders_column_width, Mode=TwoWay}"
|
||||
bid_column_width="{Binding bid_column_width, Mode=TwoWay}"
|
||||
price_column_width="{Binding price_column_width, Mode=TwoWay}"
|
||||
ask_column_width="{Binding ask_column_width, Mode=TwoWay}"
|
||||
LastColumnWidth="{Binding last_column_width, Mode=TwoWay}"
|
||||
VolumeColumnWidth="{Binding volume_column_width, Mode=TwoWay}"
|
||||
>
|
||||
<spark:LadderGridHost.Resources>
|
||||
<Style TargetType="controls:Badge">
|
||||
<Setter Property="Background" Value="{Binding background}" />
|
||||
<Setter Property="BorderBrush" Value="{Binding border}" />
|
||||
@@ -436,29 +533,12 @@
|
||||
<Setter Property="Foreground" Value="{Binding foreground}"/>
|
||||
<Setter Property="TextLines" Value="{Binding text_lines}" />
|
||||
<Setter Property="Width" Value="{x:Static controls:Badge.DefaultWidth}"/>
|
||||
<Setter Property="ToolTip" Value="{Binding description}" />
|
||||
<Setter Property="IsExchangeOrder" Value="{Binding is_exchange_order}" />
|
||||
<Setter Property="HasMultipleOrdersAtLevel" Value="{Binding has_multiple_orders_at_same_level}" />
|
||||
<Setter Property="IsHedgeOrder" Value="{Binding is_hedge_order}" />
|
||||
<Setter Property="ParentAlgoType" Value="{Binding parent_algo_type}" />
|
||||
<Setter Property="ParentAlgoTypeBrush" Value="{Binding parent_algo_type_brush}" />
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="ContextMenu">
|
||||
<Setter.Value>
|
||||
<ContextMenu>
|
||||
<MenuItem Command="{Binding cancel_command}" Header="Cancel">
|
||||
<MenuItem.Icon>
|
||||
<Image Source="{StaticResource CancelSelected}" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Command="{Binding detach_command}" Header="Detach">
|
||||
<MenuItem.Icon>
|
||||
<Image Source="{StaticResource ModifyOrder}" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{Binding hoverbackground}" />
|
||||
@@ -467,7 +547,7 @@
|
||||
</Style>
|
||||
</spark:LadderGridHost.Resources>
|
||||
</spark:LadderGridHost>
|
||||
<spark:RecenterCountdown Grid.Column="2" Grid.Row="1" Margin="0,0,18,0"
|
||||
<spark:RecenterCountdown Grid.Column="2" Grid.Row="1" Margin="0,0,18,0"
|
||||
VerticalAlignment="Bottom" HorizontalAlignment="Right"
|
||||
Width="30" Height="30"
|
||||
Mode="Pie" Delay="0:0:10"
|
||||
@@ -483,14 +563,19 @@
|
||||
<Pen Thickness="0" Brush="MediumPurple" />
|
||||
</spark:RecenterCountdown.PieStroke>
|
||||
</spark:RecenterCountdown>
|
||||
|
||||
<Button x:Name="_status" Grid.ColumnSpan="4" Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Command="{Binding status_click_command}">
|
||||
<Button.Template>
|
||||
<ControlTemplate>
|
||||
<TextBlock Text="{Binding status_message}" Background="{Binding status_background}" Padding="5,0" />
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
</Grid>
|
||||
<!-- Since the GridSplitter will share a cell with other elements,
|
||||
we add it in toward the end, but we still want the ladder status
|
||||
message to have the topmost Z order
|
||||
-->
|
||||
<GridSplitter Grid.Column="0" Grid.RowSpan="2" Width="5" />
|
||||
<!-- Ladder Status message -->
|
||||
<Button x:Name="_status" Grid.ColumnSpan="4" Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Command="{Binding status_click_command}">
|
||||
<Button.Template>
|
||||
<ControlTemplate>
|
||||
<TextBlock Text="{Binding status_message}" Background="{Binding status_background}" Padding="5,0" />
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
</Grid>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
@@ -3,10 +3,14 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
public partial class LadderView : UserControl {
|
||||
partial class LadderView : UserControl {
|
||||
readonly double _max_zoom, _min_zoom;
|
||||
|
||||
internal LadderView(LadderVM vm) {
|
||||
InitializeComponent();
|
||||
_column_headers_presenter.Columns = new GridViewColumnCollection();
|
||||
@@ -15,12 +19,87 @@ namespace mbts.spark {
|
||||
this.vm = vm;
|
||||
DataContext = vm;
|
||||
DataContextChanged += datacontext_changed;
|
||||
if (TryFindResource("MaxZoomFactor") is double found_max) {
|
||||
_max_zoom = found_max;
|
||||
} else {
|
||||
// is there a better way of not specifying the constant twice ?
|
||||
// once in the resource dictionary here and again in the xaml, seems like there has to be a better way
|
||||
_max_zoom = 1.5;
|
||||
}
|
||||
// ditto
|
||||
if (TryFindResource("MinZoomFactor") is double found_min) {
|
||||
_min_zoom = found_min;
|
||||
} else {
|
||||
_min_zoom = 0.5;
|
||||
}
|
||||
_ui_scaler.MouseDoubleClick += _ui_scaler_MouseDoubleClick;
|
||||
this.PreviewMouseWheel += LadderView_PreviewMouseWheel;
|
||||
this.PreviewMouseDown += LadderView_PreviewMouseDown;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// (Left|Right) CTRL + Wheel to zoom
|
||||
// This is neat b/c the user doesn't even need to have the slider visible to zoom
|
||||
private void LadderView_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e) {
|
||||
base.OnPreviewMouseWheel(e);
|
||||
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) {
|
||||
var delta = (e.Delta > 0) ? 0.1 : -0.1;
|
||||
var proposed_scaler_value = _ui_scaler.Value + delta;
|
||||
// Here we enforce the same contraints that are on the gui slider regarding min and max
|
||||
if (_min_zoom < proposed_scaler_value && proposed_scaler_value < _max_zoom) {
|
||||
_ui_scaler.Value = proposed_scaler_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (Left|Right) CTRL + Wheel Click to reset
|
||||
private void LadderView_PreviewMouseDown(object sender, MouseButtonEventArgs e) {
|
||||
base.OnPreviewMouseDown(e);
|
||||
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) {
|
||||
if (e.MiddleButton == MouseButtonState.Pressed) {
|
||||
_ui_scaler_MouseDoubleClick(_ui_scaler, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Double click on scaler itself to reset
|
||||
private void _ui_scaler_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e) {
|
||||
if (sender is Slider _the_slider) {
|
||||
_the_slider.Value = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void datacontext_changed(object sender, DependencyPropertyChangedEventArgs e) {
|
||||
this.vm = DataContext as LadderVM;
|
||||
}
|
||||
|
||||
// I found this on StackOverflow (and the XAML) and thought it was a neat
|
||||
// way of enforcing a MinWidth on the columns. This is so that the columns
|
||||
// cannot be resized to nothing with the mouse.
|
||||
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e) {
|
||||
if (sender != null) {
|
||||
DependencyObject parent = sender as DependencyObject;
|
||||
if (parent != null) {
|
||||
try {
|
||||
do {
|
||||
parent = System.Windows.Media.VisualTreeHelper.GetParent(parent as DependencyObject);
|
||||
} while (parent.GetType() != typeof(Canvas));
|
||||
|
||||
Canvas canvas = parent as Canvas;
|
||||
if (canvas != null) {
|
||||
if (canvas.ActualWidth + e.HorizontalChange < canvas.MinWidth) {
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal LadderVM vm { get; private set; }
|
||||
|
||||
protected override void OnDragOver(DragEventArgs e) {
|
||||
@@ -40,31 +119,31 @@ namespace mbts.spark {
|
||||
switch (type) {
|
||||
case LadderGridHost.ColumnType.Order:
|
||||
header = "Orders";
|
||||
width_prop = nameof(LadderGridHost.orders_column_width);
|
||||
width_prop = nameof(LadderVM.orders_column_width);
|
||||
break;
|
||||
case LadderGridHost.ColumnType.Bid:
|
||||
header = "Bid";
|
||||
width_prop = nameof(LadderGridHost.bid_column_width);
|
||||
width_prop = nameof(LadderVM.bid_column_width);
|
||||
break;
|
||||
case LadderGridHost.ColumnType.Price:
|
||||
header = "Price";
|
||||
width_prop = nameof(LadderGridHost.price_column_width);
|
||||
width_prop = nameof(LadderVM.price_column_width);
|
||||
break;
|
||||
case LadderGridHost.ColumnType.Ask:
|
||||
header = "Ask";
|
||||
width_prop = nameof(LadderGridHost.ask_column_width);
|
||||
width_prop = nameof(LadderVM.ask_column_width);
|
||||
break;
|
||||
case LadderGridHost.ColumnType.Volume:
|
||||
header = "Volume";
|
||||
width_prop = nameof(LadderGridHost.VolumeColumnWidth);
|
||||
width_prop = nameof(LadderVM.volume_column_width);
|
||||
break;
|
||||
case LadderGridHost.ColumnType.Last:
|
||||
header = "Last";
|
||||
width_prop = nameof(LadderGridHost.LastColumnWidth);
|
||||
width_prop = nameof(LadderVM.last_column_width);
|
||||
break;
|
||||
default: throw ExceptionBuilder.Argument.unexpected_case(type, nameof(type));
|
||||
}
|
||||
return _create(host, header, width_prop);
|
||||
return _create(host, header, $"DataContext.{width_prop}");
|
||||
}
|
||||
|
||||
static GridViewColumn _create(LadderGridHost host, string header, string width_prop) {
|
||||
@@ -77,5 +156,6 @@ namespace mbts.spark {
|
||||
return gvc;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -28,31 +28,10 @@ namespace mbts.spark {
|
||||
static readonly int sod_hh = (int)config.Configuration.get<bts.config.SODTime>().sod_hh;
|
||||
static readonly int sod_mm = (int)config.Configuration.get<bts.config.SODTime>().sod_mm;
|
||||
static readonly TimeZoneInfo central_tzi = TimeZoneInfo.FindSystemTimeZoneById(cst_id);
|
||||
static readonly DateTime _start_of_trading_day_utc;
|
||||
static readonly DateTime _adjusted_central_sod;
|
||||
|
||||
|
||||
static readonly DateTime unadjusted_central_sod = TimeZoneInfo.ConvertTimeFromUtc(
|
||||
DateTime.UtcNow.Date.Add(new TimeSpan(sod_hh, sod_mm, 0)),
|
||||
central_tzi
|
||||
);
|
||||
|
||||
static MarketSchedule() {
|
||||
var cme_schedule = config.Configuration.get<bts.md.CMETradingSchedule>();
|
||||
_cme_gen_open_hrs = cme_schedule?.gen_open_hour ?? 8.5;
|
||||
|
||||
_adjusted_central_sod = !central_tzi.IsDaylightSavingTime(unadjusted_central_sod) ? unadjusted_central_sod.AddHours(1) : unadjusted_central_sod;
|
||||
|
||||
// Note that this isn't the acutal start of the trading day
|
||||
// but it's close enough for pnl purposes.
|
||||
// In reality, instruments have different schedules
|
||||
|
||||
// if now is less than the current sod, then we me move the sod back a day to account for the trading day start
|
||||
if (_adjusted_central_sod >= TimeZoneInfo.ConvertTime(DateTime.Now, central_tzi)) {
|
||||
_adjusted_central_sod = _adjusted_central_sod.AddDays(-1);
|
||||
}
|
||||
var local_time_zone_sod = TimeZoneInfo.ConvertTime(_adjusted_central_sod, central_tzi, TimeZoneInfo.Local);
|
||||
_start_of_trading_day_utc = local_time_zone_sod.ToUniversalTime();
|
||||
}
|
||||
// for object initializers
|
||||
public MarketSchedule() { }
|
||||
@@ -73,6 +52,27 @@ namespace mbts.spark {
|
||||
return last_open;
|
||||
}
|
||||
|
||||
// DateTime? used here b/c we can't set a default parameter with DateTime.Now
|
||||
// since it's not a compile-time constant
|
||||
public static DateTime get_start_of_trading_day(DateTime? target_datetime = null, DateTimeKind dtk = DateTimeKind.Local ) {
|
||||
if (target_datetime == null) target_datetime = DateTime.Now;
|
||||
var dst_adjusted_central_sod = new DateTimeOffset(
|
||||
new DateTime(target_datetime.Value.Date.Add(new TimeSpan(sod_hh, sod_mm, 0)).Ticks, DateTimeKind.Unspecified),
|
||||
central_tzi.BaseUtcOffset
|
||||
);
|
||||
|
||||
// if we're not currently past the start of the trading day, then the start must be yesterday
|
||||
var temp = dst_adjusted_central_sod;
|
||||
if (temp >= TimeZoneInfo.ConvertTime(target_datetime.Value, central_tzi)) {
|
||||
temp = dst_adjusted_central_sod.AddDays(-1);
|
||||
}
|
||||
var local_time_zone_sod = TimeZoneInfo.ConvertTime(temp.DateTime, central_tzi, TimeZoneInfo.Local);
|
||||
if (dtk == DateTimeKind.Utc) {
|
||||
return local_time_zone_sod.ToUniversalTime();
|
||||
}
|
||||
return local_time_zone_sod;
|
||||
}
|
||||
|
||||
public static DateTime get_utctimestamp_as_localtime(string s) {
|
||||
return DateTimeOffset.ParseExact(s, "yyyyMMddHHmmssffffff", null, System.Globalization.DateTimeStyles.AssumeUniversal).ToLocalTime().LocalDateTime;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace mbts.spark {
|
||||
class PnLSummaryRow : ViewModelBase, IEquatable<PnLSummaryRow>, DIYGrid.IRowVM {
|
||||
static readonly SignedDataStore<Brush> _brushes;
|
||||
readonly PnLDoubleBrushCell _start_pnl, _day_pnl, _total_pnl;
|
||||
readonly PnLPriceCell _pnl_price, _weighted_average_buy, _weighted_average_sell;
|
||||
readonly CellProperty<double> _pnl_price, _weighted_average_buy, _weighted_average_sell;
|
||||
(string account, Venue? exchange, string symbol, SecurityImmutable? security, SecurityMD? sec_md, SecurityType? sec_type) key;
|
||||
IEnumerable<FillImmutable> _fills;
|
||||
double _driver;
|
||||
@@ -38,9 +38,9 @@ namespace mbts.spark {
|
||||
day_short_quantity = day_sell_quant;
|
||||
total_net_position = net_pos;
|
||||
start_net_position = sod_pos;
|
||||
_pnl_price = new PnLPriceCell(this, nameof(pnl_price));
|
||||
_weighted_average_buy = new PnLPriceCell(this, nameof(weighted_average_buy), wavg_buy);
|
||||
_weighted_average_sell = new PnLPriceCell(this, nameof(weighted_average_sell), wavg_sell);
|
||||
_pnl_price = new CellProperty<double>(this, nameof(pnl_price), price => this.security.spec_or_default().format(price), 0);
|
||||
_weighted_average_buy = new CellProperty<double>(this, nameof(weighted_average_buy), price => this.security.spec_or_default().format(price), wavg_buy);
|
||||
_weighted_average_sell = new CellProperty<double>(this, nameof(weighted_average_sell), price => this.security.spec_or_default().format(price), wavg_sell);
|
||||
_fills = fills;
|
||||
|
||||
// set the initial pnl and pnl_price when there has been a
|
||||
@@ -193,19 +193,13 @@ namespace mbts.spark {
|
||||
class PnLDoubleBrushCell : CellProperty<double> {
|
||||
Brush _background;
|
||||
|
||||
public PnLDoubleBrushCell(PnLSummaryRow owner, string prop) : base(d => double.IsNaN(d) ? string.Empty : d.ToString("N2"), double.NaN) =>
|
||||
public PnLDoubleBrushCell(PnLSummaryRow owner, string prop) : base(owner, prop, d => double.IsNaN(d) ? string.Empty : d.ToString("N2"), double.NaN) =>
|
||||
_value_set += _ => {
|
||||
_background = null;
|
||||
owner.RaisePropertyChanged(prop);
|
||||
};
|
||||
|
||||
public Brush background => _background ?? (_background = _brushes.get(value));
|
||||
}
|
||||
|
||||
class PnLPriceCell : CellProperty<double> {
|
||||
public PnLPriceCell(PnLSummaryRow owner, string prop, double init = 0)
|
||||
: base(price => owner.security.spec_or_default().format(price), init) =>
|
||||
_value_set += _ => owner.RaisePropertyChanged(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ namespace mbts.spark.utilities {
|
||||
static readonly OrderMetadata _default_order_metadata = new OrderMetadata { KeyBinding = "", Color = BTSColors.Orders.spreader_order };
|
||||
static readonly CustomOrderDefinition.CustomOneofCase[]
|
||||
// The following standard types are not yet ready for production use:
|
||||
// CustomOrderDefinition.CustomOneofCase.Tick,
|
||||
// CustomOrderDefinition.CustomOneofCase.Dynamic,
|
||||
_standard_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Market,
|
||||
@@ -37,6 +36,7 @@ namespace mbts.spark.utilities {
|
||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
||||
CustomOrderDefinition.CustomOneofCase.Join,
|
||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
||||
CustomOrderDefinition.CustomOneofCase.Oco,
|
||||
CustomOrderDefinition.CustomOneofCase.Offset,
|
||||
CustomOrderDefinition.CustomOneofCase.Top,
|
||||
CustomOrderDefinition.CustomOneofCase.Trigger,
|
||||
@@ -165,7 +165,8 @@ namespace mbts.spark.utilities {
|
||||
}
|
||||
#endregion
|
||||
|
||||
public OrderDefinition order(SecurityImmutable security) => order(security, _admin_model.get_default_account());
|
||||
public OrderDefinitionImmutable order(SecurityImmutable security) =>
|
||||
new OrderDefinitionImmutable(order(security, _admin_model.get_default_account()));
|
||||
public OrderDefinition order(SecurityImmutable security, AccountTuple? account) {
|
||||
var rv = new OrderDefinition {
|
||||
SecurityKey = security.Id,
|
||||
@@ -227,6 +228,15 @@ namespace mbts.spark.utilities {
|
||||
Child = custom_order(),
|
||||
Hedge = custom_order(),
|
||||
};
|
||||
public OCOOrderDefinition oco_order() => new OCOOrderDefinition
|
||||
{
|
||||
Child = custom_order(),
|
||||
Other = custom_order(),
|
||||
};
|
||||
public PricerOrderDefinition pricer_order() => new PricerOrderDefinition
|
||||
{
|
||||
Child = custom_order(),
|
||||
};
|
||||
public OffsetOrderDefinition offset_order() => new OffsetOrderDefinition
|
||||
{
|
||||
Child = custom_order(),
|
||||
@@ -261,6 +271,8 @@ namespace mbts.spark.utilities {
|
||||
case CustomOrderDefinition.CustomOneofCase.Join: rv.Join = join_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Top: rv.Top = top_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Cover: rv.Cover = cover_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Oco: rv.Oco = oco_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Pricer: rv.Pricer = pricer_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Offset: rv.Offset = offset_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Trigger: rv.Trigger = trigger_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: rv.Spreader = spreader_order(); break;
|
||||
@@ -284,6 +296,8 @@ namespace mbts.spark.utilities {
|
||||
case CustomOrderDefinition.CustomOneofCase.Join: return new OrderMetadata { KeyBinding = "J", Color = BTSColors.Orders.join_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Top: return new OrderMetadata { KeyBinding = "D", Color = BTSColors.Orders.top_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Cover: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.cover_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Oco: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.oco_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Pricer: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.pricer_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Offset: return new OrderMetadata { KeyBinding = "O", Color = BTSColors.Orders.tick_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Trigger: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.trigger_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: goto default;
|
||||
@@ -305,6 +319,8 @@ namespace mbts.spark.utilities {
|
||||
case OrderState.CustomOneofCase.Join: return new OrderMetadata { KeyBinding = "J", Color = BTSColors.Orders.join_order };
|
||||
case OrderState.CustomOneofCase.Top: return new OrderMetadata { KeyBinding = "D", Color = BTSColors.Orders.top_order };
|
||||
case OrderState.CustomOneofCase.Cover: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.cover_order };
|
||||
case OrderState.CustomOneofCase.Oco: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.oco_order };
|
||||
case OrderState.CustomOneofCase.Pricer: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.pricer_order };
|
||||
case OrderState.CustomOneofCase.Offset: return new OrderMetadata { KeyBinding = "O", Color = BTSColors.Orders.tick_order };
|
||||
case OrderState.CustomOneofCase.Trigger: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.trigger_order };
|
||||
case OrderState.CustomOneofCase.Spreader: goto default;
|
||||
@@ -344,6 +360,8 @@ namespace mbts.spark.utilities {
|
||||
case nameof(JoinOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Join;
|
||||
case nameof(TopOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Top;
|
||||
case nameof(CoverOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Cover;
|
||||
case nameof(OCOOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Oco;
|
||||
case nameof(PricerOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Pricer;
|
||||
case nameof(OffsetOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Offset;
|
||||
case nameof(TriggerOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Trigger;
|
||||
case nameof(DynamicOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Dynamic;
|
||||
|
||||
@@ -17,7 +17,9 @@ namespace mbts.spark {
|
||||
readonly DelegateProperty<IEnumerable<DialogSkirtItem>> _left_skirt_items;
|
||||
readonly List<IDisposable> _resources = new List<IDisposable>(2); // initialize with 2 capacity bc the only potential disposables are the instance/default vms
|
||||
|
||||
public WindowSettingsDialogVM(IWindowSettingsManager<T> settings_manager, Func<UserSettings, T> get_factory_settings, string settings_file_ext, Func<T, TVM> vm_factory) {
|
||||
public WindowSettingsDialogVM(IWindowSettingsManager<T> settings_manager, Func<UserSettings, T> get_factory_settings, string settings_file_ext, Func<T, TVM> vm_factory, Func<T, T> merge_with_current = null) {
|
||||
if (merge_with_current == null)
|
||||
merge_with_current = s => s;
|
||||
// by default, each new window will use their own instance settings
|
||||
_use_default = backing(nameof(use_default), settings_manager.instance_settings == null);
|
||||
default_vm = vm_factory(settings_manager.default_settings);
|
||||
@@ -59,15 +61,14 @@ namespace mbts.spark {
|
||||
_resources.Add(dispose_dvm);
|
||||
// local methods
|
||||
void apply() {
|
||||
var result = merge_with_current(instance_vm.get_result());
|
||||
if (!use_default) {
|
||||
settings_manager.instance_settings = instance_vm.get_result();
|
||||
settings_manager.instance_settings = result;
|
||||
} else {
|
||||
settings_manager.instance_settings = null;
|
||||
|
||||
// copy the current instance settings and persist
|
||||
var my_default = instance_vm.get_result();
|
||||
if (!my_default.safe_equals(settings_manager.default_settings))
|
||||
settings_manager.save_default_settings(my_default).ignore();
|
||||
if (!result.safe_equals(settings_manager.default_settings))
|
||||
settings_manager.save_default_settings(result).ignore();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File renamed without changes.
+4
-9
@@ -13,14 +13,9 @@ namespace mbts.co2
|
||||
{
|
||||
internal static unsafe T deserialize<T>(byte* buffer, ulong length) where T : new() {
|
||||
var stream = new System.IO.UnmanagedMemoryStream(buffer, (long)length);
|
||||
if (typeof(IMessage).IsAssignableFrom(typeof(T))) {
|
||||
IMessage result = (IMessage)new T();
|
||||
result.MergeFrom(stream);
|
||||
return (T)result;
|
||||
}
|
||||
else {
|
||||
return ProtoBuf.Serializer.Deserialize<T>(stream);
|
||||
}
|
||||
IMessage result = (IMessage)new T();
|
||||
result.MergeFrom(stream);
|
||||
return (T)result;
|
||||
}
|
||||
|
||||
[DllImport("libspark")]
|
||||
@@ -81,7 +76,7 @@ namespace mbts.co2
|
||||
[DllImport("libspark")]
|
||||
internal unsafe static extern IntPtr add_relay_symbol(IntPtr relay, UserSymbol symbol, bts.co2.MDRequest.Types.Flags flags);
|
||||
[DllImport("libspark")]
|
||||
internal unsafe static extern IntPtr set_relay_token(IntPtr relay, string user_name);
|
||||
internal unsafe static extern IntPtr set_relay_token(IntPtr relay, byte[] token, long token_size);
|
||||
|
||||
[DllImport("libspark")]
|
||||
internal unsafe static extern void post_on_scan_security_thread(Action callback);
|
||||
+5
-6
@@ -14,17 +14,16 @@ namespace mbts.co2
|
||||
{
|
||||
MDConsumer _consumer;
|
||||
IntPtr _relay;
|
||||
public Co2RelayControl(MDConsumer consumer) {
|
||||
public Co2RelayControl(MDConsumer consumer, uint naggle_ms) {
|
||||
_consumer = consumer;
|
||||
Log.assert(consumer.native_pointer() != IntPtr.Zero);
|
||||
_relay =
|
||||
Co2NativeMethods.start_private_relay(consumer.native_pointer(),
|
||||
config.Configuration.get<bts.md.RelayOptions>().naggle_ms);
|
||||
_relay = Co2NativeMethods.start_private_relay(consumer.native_pointer(), naggle_ms);
|
||||
Log.assert(_relay != IntPtr.Zero);
|
||||
}
|
||||
|
||||
public void set_token(AuthToken token) {
|
||||
Co2NativeMethods.set_relay_token(_relay, token.Token);
|
||||
public void set_token(AuthTokenImmutable token) {
|
||||
var buffer = token.Token.ToByteArray();
|
||||
Co2NativeMethods.set_relay_token(_relay, buffer, buffer.LongLength);
|
||||
}
|
||||
|
||||
public async Task preload_securities(MDRequest.Types.Flags flags=MDRequest.Types.Flags.Security) {
|
||||
File renamed without changes.
@@ -9,6 +9,7 @@ using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using security_key = System.UInt32;
|
||||
using bts;
|
||||
|
||||
namespace mbts.co2
|
||||
{
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -64,7 +64,7 @@ namespace bts.api.core {
|
||||
internal static extern void api_client_send(IntPtr nchannel, byte[] buffer, int length, raw_pb_callback p, IntPtr extra);
|
||||
|
||||
[DllImport("libspark")]
|
||||
internal static extern IntPtr api_client_subscribe(IntPtr client, string method_and_qualifier, raw_subscription_callback cb, IntPtr extra);
|
||||
internal static extern IntPtr api_client_subscribe(IntPtr client, string method, string qualifier, raw_subscription_callback cb, IntPtr extra);
|
||||
[DllImport("libspark")]
|
||||
internal static extern void api_client_unsubscribe(IntPtr client, IntPtr subscription);
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace bts.api.core {
|
||||
}
|
||||
|
||||
[DllImport("libspark")]
|
||||
internal static extern IntPtr api_server_create_table(IntPtr server, string method_and_qualifier);
|
||||
internal static extern IntPtr api_server_create_table(IntPtr server, string method, string qualifier);
|
||||
[DllImport("libspark")]
|
||||
internal static extern void api_server_update_table(IntPtr table, [In] StreamUpdate[] changes, long change_count);
|
||||
[DllImport("libspark")]
|
||||
|
||||
@@ -17,15 +17,6 @@ namespace bts.api.core
|
||||
|
||||
internal abstract class ApiServer : IApiServer
|
||||
{
|
||||
static JsonFormatter _formatter = new JsonFormatter(new JsonFormatter.Settings(true));
|
||||
static Regex _fmt_cleanup = new Regex(@"[\s_+]", RegexOptions.Compiled | RegexOptions.Multiline);
|
||||
public static string format_qualifier<T>(string method, T arg) where T : IMessage {
|
||||
if (arg != null) {
|
||||
string arg_fmt = _fmt_cleanup.Replace(_formatter.Format(arg), "");
|
||||
return method + arg_fmt;
|
||||
}
|
||||
return method;
|
||||
}
|
||||
public abstract void dispatch(RequestHeader header, Stream request, Action<byte[]> on_response);
|
||||
|
||||
protected void respond<T>(int request_id, Task<T> task, Action<byte[]> on_response) where T : IMessage, IImmutableMessage {
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
using Google.Protobuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bts.api {
|
||||
internal static class ApiUtils {
|
||||
|
||||
|
||||
public static byte[] serialize<T>(this RequestHeader header, T body) where T : IImmutableMessage {
|
||||
int body_size = body?.CalculateSize() ?? 0;
|
||||
int header_size = header.CalculateSize() + 1; // 1 byte header length
|
||||
var buffer = new byte[header_size+body_size];
|
||||
using bts.api.core;
|
||||
using Google.Protobuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bts.api {
|
||||
internal static class ApiUtils {
|
||||
|
||||
public static byte[] serialize<T>(this RequestHeader header, T body) where T : IImmutableMessage {
|
||||
int body_size = body?.CalculateSize() ?? 0;
|
||||
int header_size = header.CalculateSize() + 1; // 1 byte header length
|
||||
var buffer = new byte[header_size + body_size];
|
||||
using (var stream = new MemoryStream(buffer)) {
|
||||
checked { stream.WriteByte((byte)header_size); }
|
||||
header.WriteTo(stream);
|
||||
if (body != null) body.WriteTo(stream);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public struct DeserializeResult<THeader,TBody> {
|
||||
public THeader header;
|
||||
public TBody body;
|
||||
}
|
||||
|
||||
private static DeserializeResult<THeader, TBody> mkresult<THeader,TBody>(THeader header, TBody body) {
|
||||
return new DeserializeResult<THeader, TBody>() { header = header, body = body }; }
|
||||
|
||||
public static unsafe DeserializeResult<THeader, TBody> deserialize<THeader,TBody>(byte* buffer, long buffer_length) where THeader : IMessage,new() where TBody : class,IMessage,new() {
|
||||
var header_size = buffer[0];
|
||||
Debug.Assert(header_size <= buffer_length);
|
||||
THeader header = new THeader();
|
||||
header.MergeFrom(new UnmanagedMemoryStream(buffer+1, header_size - 1));
|
||||
TBody body = new TBody();
|
||||
if (buffer_length > header_size) {
|
||||
body.MergeFrom(new UnmanagedMemoryStream(buffer + header_size, buffer_length - header_size));
|
||||
}
|
||||
return mkresult(header, body);
|
||||
}
|
||||
|
||||
public static byte[] serialize<T>(this ResponseHeader header, T body) where T : IImmutableMessage {
|
||||
int header_size = 1 + header.CalculateSize();
|
||||
int body_size = body == null ? 0 : body.CalculateSize();
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public struct DeserializeResult<THeader, TBody> {
|
||||
public THeader header;
|
||||
public TBody body;
|
||||
}
|
||||
|
||||
private static DeserializeResult<THeader, TBody> mkresult<THeader, TBody>(THeader header, TBody body) {
|
||||
return new DeserializeResult<THeader, TBody>() { header = header, body = body }; }
|
||||
|
||||
public static unsafe DeserializeResult<THeader, TBody> deserialize<THeader, TBody>(byte* buffer, long buffer_length) where THeader : IMessage, new() where TBody : class, IMessage, new() {
|
||||
var header_size = buffer[0];
|
||||
Debug.Assert(header_size <= buffer_length);
|
||||
THeader header = new THeader();
|
||||
header.MergeFrom(new UnmanagedMemoryStream(buffer + 1, header_size - 1));
|
||||
TBody body = new TBody();
|
||||
if (buffer_length > header_size) {
|
||||
body.MergeFrom(new UnmanagedMemoryStream(buffer + header_size, buffer_length - header_size));
|
||||
}
|
||||
return mkresult(header, body);
|
||||
}
|
||||
|
||||
public static byte[] serialize<T>(this ResponseHeader header, T body) where T : IImmutableMessage {
|
||||
int header_size = 1 + header.CalculateSize();
|
||||
int body_size = body == null ? 0 : body.CalculateSize();
|
||||
var buffer = new byte[header_size + body_size];
|
||||
using (var stream = new MemoryStream(buffer)) {
|
||||
checked { stream.WriteByte((byte)header_size); }
|
||||
header.WriteTo(stream);
|
||||
if (body != null) body.WriteTo(stream);
|
||||
using (var stream = new MemoryStream(buffer)) {
|
||||
checked { stream.WriteByte((byte)header_size); }
|
||||
header.WriteTo(stream);
|
||||
if (body != null) body.WriteTo(stream);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
public static byte[] serialize(this ResponseHeader header) {
|
||||
int header_size = 1 + header.CalculateSize();
|
||||
var buffer = new byte[header_size];
|
||||
using (var stream = new MemoryStream(buffer)) {
|
||||
checked { stream.WriteByte((byte)header_size); }
|
||||
header.WriteTo(stream);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public static byte[] serialize(this ResponseHeader header) {
|
||||
int header_size = 1 + header.CalculateSize();
|
||||
var buffer = new byte[header_size];
|
||||
using (var stream = new MemoryStream(buffer)) {
|
||||
checked { stream.WriteByte((byte)header_size); }
|
||||
header.WriteTo(stream);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ namespace bts.api.core {
|
||||
var header = new RequestHeader() {
|
||||
Method = method_id,
|
||||
RequestId = id,
|
||||
Token = _context.token.HasValue ? new AuthToken() { Token = _context.token?.Token } : null
|
||||
};
|
||||
var buffer = header.serialize(request);
|
||||
var tcs = new TaskCompletionSource<Result<Response>>();
|
||||
@@ -56,60 +57,99 @@ namespace bts.api.core {
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
public unsafe override IObservable<RawUpdate<TOut>> subscribe<TParse,TOut>(string method_and_qualifier, MessageParser<TParse> parser, Func<TParse,TOut> converter) {
|
||||
var result = Observable.Create<RawUpdate<TOut>>(observer => {
|
||||
bool first_update = true;
|
||||
// Memory optimization. There is only one update per subscription.
|
||||
RawUpdate<TOut> the_update = new RawUpdate<TOut>();
|
||||
ApiNativeMethods.raw_subscription_callback cb = (event_type, changes, change_count, unused) => {
|
||||
the_update.Clear();
|
||||
try {
|
||||
if (event_type == ApiNativeMethods.EventType.TABLE_UPDATED) {
|
||||
foreach (var change in changes) {
|
||||
switch (change.type) {
|
||||
case ApiNativeMethods.ChangeType.REMOVE:
|
||||
the_update.removed_keys.Add(change.key);
|
||||
break;
|
||||
case ApiNativeMethods.ChangeType.REPLACE:
|
||||
the_update.changed_items.Add(converter(parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
|
||||
the_update.changed_keys.Add(change.key);
|
||||
break;
|
||||
case ApiNativeMethods.ChangeType.CREATE:
|
||||
the_update.added_items.Add(converter(parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
|
||||
the_update.added_keys.Add(change.key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
observer.OnNext(the_update);
|
||||
public override IObservable<RawUpdate<TOut>> subscribe<TParse,TOut>(int method_id, string method, string qualifier, MessageParser<TParse> parser, Func<TParse,TOut> converter) {
|
||||
var result = Observable.Create<RawUpdate<TOut>>(async observer => {
|
||||
IntPtr temp_subscription = IntPtr.Zero;
|
||||
void unsubscribe_temp_sub() {
|
||||
// Unsubscribe late to keep qb refcount > 0 continuously
|
||||
if (temp_subscription != IntPtr.Zero) ApiNativeMethods.api_client_unsubscribe(_client, temp_subscription);
|
||||
}
|
||||
while (true) {
|
||||
var response = await check_stream_access(method_id, qualifier);
|
||||
if (response.success) {
|
||||
if (response.value.Authorized) {
|
||||
// success. Break loop and subscribe
|
||||
break;
|
||||
}
|
||||
else if (event_type == ApiNativeMethods.EventType.TABLE_CREATED) {
|
||||
if (!first_update) {
|
||||
observer.OnNext(the_update.MarkAsReset());
|
||||
}
|
||||
first_update = false;
|
||||
else {
|
||||
// Not authorized. return empty stream
|
||||
unsubscribe_temp_sub();
|
||||
observer.OnCompleted();
|
||||
return System.Reactive.Disposables.Disposable.Empty;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
ApiNativeMethods.api_client_log_error($"Exception in subscription callback for sub: '{method_and_qualifier}' \n--\n '{e}'");
|
||||
// Sleep to let the log message get asyncronously written to disk
|
||||
Thread.Sleep(100);
|
||||
Environment.Exit(-1);
|
||||
else {
|
||||
// Wait for stream to fire once before trying again
|
||||
unsubscribe_temp_sub();
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
ApiNativeMethods.raw_subscription_callback cb = (event_type, changes, change_count, unused) => {
|
||||
tcs.TrySetResult(true);
|
||||
};
|
||||
temp_subscription = ApiNativeMethods.api_client_subscribe(_client, method, qualifier, cb, IntPtr.Zero);
|
||||
await tcs.Task;
|
||||
GC.KeepAlive(cb);
|
||||
}
|
||||
};
|
||||
var subscription = ApiNativeMethods.api_client_subscribe(_client, method_and_qualifier, cb, IntPtr.Zero);
|
||||
return FinalizeableAnonymousDisposable.Create((disposing) => {
|
||||
ApiNativeMethods.api_client_unsubscribe(_client, subscription);
|
||||
subscription = IntPtr.Zero;
|
||||
GC.KeepAlive(cb);
|
||||
cb = null;
|
||||
// Cannot (ever) call logging from the finalizer thread
|
||||
// if (!disposing) {
|
||||
// ApiNativeMethods.api_client_log_error($"Subscription has been terminated because the returned IDisposable has been finalized. Subscription: {method_and_qualifier}");
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
// Authorized. Stream events
|
||||
unsubscribe_temp_sub();
|
||||
return SubscribeNative(method, qualifier, parser, converter, observer);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
private Task<Result<StreamAccessResponse>> check_stream_access(int method_id, string qualifier) {
|
||||
var request = new StreamAccessRequest() { Qualifier = qualifier ?? "" };
|
||||
return send<StreamAccessRequest, StreamAccessResponse>(method_id, request);
|
||||
}
|
||||
|
||||
private unsafe IDisposable SubscribeNative<TParse, TOut>(string method, string qualifier, MessageParser<TParse> parser, Func<TParse, TOut> converter, IObserver<RawUpdate<TOut>> observer) where TParse : IMessage<TParse> {
|
||||
bool first_update = true;
|
||||
// Memory optimization. There is only one update per subscription.
|
||||
RawUpdate<TOut> the_update = new RawUpdate<TOut>();
|
||||
ApiNativeMethods.raw_subscription_callback cb = (event_type, changes, change_count, unused) => {
|
||||
the_update.Clear();
|
||||
try {
|
||||
if (event_type == ApiNativeMethods.EventType.TABLE_UPDATED) {
|
||||
foreach (var change in changes) {
|
||||
switch (change.type) {
|
||||
case ApiNativeMethods.ChangeType.REMOVE:
|
||||
the_update.removed_keys.Add(change.key);
|
||||
break;
|
||||
case ApiNativeMethods.ChangeType.REPLACE:
|
||||
the_update.changed_items.Add(converter(parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
|
||||
the_update.changed_keys.Add(change.key);
|
||||
break;
|
||||
case ApiNativeMethods.ChangeType.CREATE:
|
||||
the_update.added_items.Add(converter(parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
|
||||
the_update.added_keys.Add(change.key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
observer.OnNext(the_update);
|
||||
}
|
||||
else if (event_type == ApiNativeMethods.EventType.TABLE_CREATED) {
|
||||
if (!first_update) {
|
||||
observer.OnNext(the_update.MarkAsReset());
|
||||
}
|
||||
first_update = false;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
ApiNativeMethods.api_client_log_error($"Exception in subscription callback for sub: '{method}/{qualifier}' \n--\n '{e}'");
|
||||
// Sleep to let the log message get asyncronously written to disk
|
||||
Thread.Sleep(100);
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
};
|
||||
var subscription = ApiNativeMethods.api_client_subscribe(_client, method, qualifier, cb, IntPtr.Zero);
|
||||
return FinalizeableAnonymousDisposable.Create((disposing) => {
|
||||
ApiNativeMethods.api_client_unsubscribe(_client, subscription);
|
||||
subscription = IntPtr.Zero;
|
||||
GC.KeepAlive(cb);
|
||||
cb = null;
|
||||
});
|
||||
}
|
||||
|
||||
private class FinalizeableAnonymousDisposable : IDisposable {
|
||||
private Action<bool> _action;
|
||||
public FinalizeableAnonymousDisposable(Action<bool> dispose) {
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
||||
namespace bts.api.core {
|
||||
internal abstract class ClientChannel : IDisposable
|
||||
{
|
||||
readonly ServiceContext _context;
|
||||
protected readonly ServiceContext _context;
|
||||
|
||||
public ClientChannel(ServiceContext context) {
|
||||
_context = context;
|
||||
@@ -13,7 +13,7 @@ namespace bts.api.core {
|
||||
public abstract void Dispose();
|
||||
public abstract Task<Result<Response>> send<Request, Response>(int method_id, Request request)
|
||||
where Request: IImmutableMessage where Response : class,IMessage,new();
|
||||
public abstract IObservable<RawUpdate<TOut>> subscribe<TParse,TOut>(string method_and_qualifier, MessageParser<TParse> parser, Func<TParse,TOut> converter=null)
|
||||
public abstract IObservable<RawUpdate<TOut>> subscribe<TParse,TOut>(int method_id, string method, string qualifier, MessageParser<TParse> parser, Func<TParse,TOut> converter=null)
|
||||
where TParse : IMessage<TParse>;
|
||||
|
||||
public abstract bool is_sync_call_ok();
|
||||
|
||||
@@ -37,7 +37,12 @@ namespace bts.api.core
|
||||
public T value {
|
||||
get {
|
||||
if (success) return _result;
|
||||
else throw new ApiException(error_message);
|
||||
else {
|
||||
if (typeof(T).IsValueType) { throw new ApiException(error_message); }
|
||||
else {
|
||||
return default(T);// always null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public bool success { get { return _code == ApiError.Success; } }
|
||||
|
||||
@@ -34,8 +34,8 @@ namespace bts.api.core {
|
||||
}
|
||||
public Action<RequestHeader, Stream, Action<byte[]>> on_request;
|
||||
|
||||
public IntPtr create_table(string method_and_qualifier) {
|
||||
return ApiNativeMethods.api_server_create_table(_server, method_and_qualifier);
|
||||
public IntPtr create_table(string method, string qualifier) {
|
||||
return ApiNativeMethods.api_server_create_table(_server, method, qualifier);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
|
||||
@@ -4,12 +4,14 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using bts.api.user;
|
||||
|
||||
namespace bts.api.core
|
||||
{
|
||||
namespace bts.api.core {
|
||||
public class ServiceContext : IDisposable
|
||||
{
|
||||
IntPtr _qbus;
|
||||
AuthToken _token;
|
||||
|
||||
public ServiceContext() {
|
||||
_qbus = ApiNativeMethods.api_create_quickbus(null, 0);
|
||||
}
|
||||
@@ -22,7 +24,6 @@ namespace bts.api.core
|
||||
Type[] arg_types = new[] { typeof(ClientChannel) };
|
||||
ConstructorInfo ctor = typeof(T).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).First(ci => ci.GetParameters().SingleOrDefault()?.ParameterType == typeof(ClientChannel));
|
||||
return (T)ctor.Invoke(new object[] { channel });
|
||||
//return (T)Activator.CreateInstance(typeof(T), channel);
|
||||
}
|
||||
|
||||
internal ServerChannel publish<T>(T service, string service_type, string qualifier) where T : IApiServer {
|
||||
@@ -31,6 +32,38 @@ namespace bts.api.core
|
||||
return ch;
|
||||
}
|
||||
|
||||
public AuthTokenImmutable? token => _token ?? (AuthTokenImmutable?)null;
|
||||
public string user { get; private set; }
|
||||
public uint user_id { get; private set; }
|
||||
public bool is_authenticated => token.HasValue;
|
||||
|
||||
public async Task<LoginResult> login(string username, string password) {
|
||||
var auth_service = locate<AuthService>();
|
||||
var result = await auth_service.authenticate_login_async(new AuthenticateLoginRequest() {
|
||||
Login = username ?? "",
|
||||
Password = password ?? ""
|
||||
});
|
||||
if (result.success) {
|
||||
if (result.value.IsValid) {
|
||||
_token = result.value.Token;
|
||||
user = username;
|
||||
user_id = result.value.UserId;
|
||||
return new LoginResult();
|
||||
}
|
||||
else {
|
||||
return new LoginResult(result.value.Reason);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return new LoginResult(result.error_message);
|
||||
}
|
||||
}
|
||||
public void login(AuthTokenImmutable token) {
|
||||
this.user = "";
|
||||
this.user_id = 0;
|
||||
this._token = token.CloneToMutable();
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
|
||||
protected virtual void Dispose(bool disposing) {
|
||||
@@ -52,4 +85,11 @@ namespace bts.api.core
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class LoginResult {
|
||||
public LoginResult() { success = true; }
|
||||
public LoginResult(string reason) { success = false; this.reason = reason; }
|
||||
public bool success { get; }
|
||||
public string reason { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
|
||||
namespace bts.api
|
||||
{
|
||||
public class Authenticator
|
||||
{
|
||||
readonly static Lazy<user.UserService> _auth = new Lazy<user.UserService>(() => Locator.locate<user.UserService>(), true);
|
||||
user.AuthToken _token = null;
|
||||
public Authenticator() { }
|
||||
|
||||
public user.AuthToken last_token => _token;
|
||||
public async Task<user.AuthToken> login(string login, string password)
|
||||
{
|
||||
var res = await _auth.Value.authenticate_login_async(new user.AuthenticateLoginRequest { Login = login, Password = password });
|
||||
if (res.success)
|
||||
_token = res.value.Token;
|
||||
return _token;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using bts.api;
|
||||
using mbts;
|
||||
using mbts.co2;
|
||||
|
||||
namespace bts.md
|
||||
{
|
||||
interface IMDUpdate
|
||||
{
|
||||
bool update(SecurityMD smd, ref ulong last_update_time);
|
||||
}
|
||||
|
||||
public interface IMDData
|
||||
{
|
||||
SecurityImmutable security { get; }
|
||||
}
|
||||
|
||||
public interface IMDBook : IMDData
|
||||
{
|
||||
List<PriceSize> bids { get; }
|
||||
List<PriceSize> asks { get; }
|
||||
}
|
||||
|
||||
class MDBook : IMDBook, IMDUpdate
|
||||
{
|
||||
public MDBook()
|
||||
{
|
||||
}
|
||||
public SecurityImmutable security { get; private set; }
|
||||
|
||||
public List<PriceSize> bids { get; private set; }
|
||||
public List<PriceSize> asks { get; private set; }
|
||||
|
||||
bool IMDUpdate.update(SecurityMD smd, ref ulong last_update_time)
|
||||
{
|
||||
this.security = smd.security;
|
||||
NativePriceSize[] bids = null, asks = null;
|
||||
if (smd.md.get_depth(ref last_update_time, ref bids, ref asks, NativePriceHelpers.COCONUT_MAX_DEPTH))
|
||||
{
|
||||
this.bids = bids.Select(x => new PriceSize(x.size, x.direct_size, x.implied_size, x.price)).ToList();
|
||||
this.asks = asks.Select(x => new PriceSize(x.size, x.direct_size, x.implied_size, x.price)).ToList();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return security.ToString() + Environment.NewLine +
|
||||
"Bid BidSz BidISz" + Environment.NewLine +
|
||||
string.Join("\n", bids.Select(x => x.ToString())) + Environment.NewLine +
|
||||
"Ask AskSz AskISz" + Environment.NewLine +
|
||||
string.Join("\n", asks.Select(x => x.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 456 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user